diff --git a/README.rst b/README.rst index 5b22333..a9037b1 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ and plugins from Python scripts. py2app is similar in purpose and design to py2exe for Windows. -NOTE: py2app must be used on OSX to build applications, +NOTE: py2app must be used on macOS to build applications, it cannot create Mac applications on other platforms. Project links diff --git a/doc/changelog.rst b/doc/changelog.rst index fdf653e..d9b8b82 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -4,6 +4,11 @@ Release history py2app 0.28 ----------- +.. note:: + + This is the last version of py2app with compatibility with + Python 2.7. Future versions will require Python 3.6 or later. + * PR #410: Fix typo in NamedTemporyFile call PR by MAKOMO diff --git a/py2app/_pkg_meta.py b/py2app/_pkg_meta.py index 4bf0122..1bd2e7a 100644 --- a/py2app/_pkg_meta.py +++ b/py2app/_pkg_meta.py @@ -40,6 +40,7 @@ def update_metadata_cache_distinfo(infos, dist_info_path): ) ] = dist_info_path + def update_metadata_cache_distlink(infos, dist_link_path): """ Update mapping from filename to dist_info directory @@ -52,7 +53,6 @@ def update_metadata_cache_distlink(infos, dist_link_path): with open(dist_link_path, "r") as fp: dn = fp.readline()[:-1] - # The list of files and directories that would have been # in the list of installed items. to_include = [] @@ -81,16 +81,22 @@ def update_metadata_cache_distlink(infos, dist_link_path): # the cache. add_recursive(infos, dist_info_path, to_include) + def add_recursive(infos, dist_info_path, to_include): - """ Add items from to_include to infos, recursively - walking into directories """ + """Add items from to_include to infos, recursively + walking into directories""" for item in to_include: if os.path.isdir(item): - add_recursive(infos, dist_info_path, [os.path.join(item, fn) for fn in os.listdir(item)]) + add_recursive( + infos, + dist_info_path, + [os.path.join(item, fn) for fn in os.listdir(item)], + ) else: infos[item] = dist_info_path + def scan_for_metadata(path): """ Scan the importlib search path *path* for dist-info/egg-info @@ -103,7 +109,7 @@ def scan_for_metadata(path): continue for nm in os.listdir(dirname): if nm.endswith(".egg-link"): - # Editable install, these will be found later on in + # Editable install, these will be found later on in # *path* as well, but contain metadata without a list # of installed files. update_metadata_cache_distlink(infos, os.path.join(dirname, nm)) diff --git a/py2app/apptemplate/lib/site.py b/py2app/apptemplate/lib/site.py index c765f9e..ede88ff 100644 --- a/py2app/apptemplate/lib/site.py +++ b/py2app/apptemplate/lib/site.py @@ -193,11 +193,12 @@ def getusersitepackages(): if sys.version_info[0] == 3: import builtins # noqa: E402 + import _sitebuiltins # noqa: E402 builtins.help = _sitebuiltins._Helper() - builtins.quit = _sitebuiltins.Quitter('quit', 'Ctrl-D (i.e. EOF)') - builtins.exit = _sitebuiltins.Quitter('exit', 'Ctrl-D (i.e. EOF)') + builtins.quit = _sitebuiltins.Quitter("quit", "Ctrl-D (i.e. EOF)") + builtins.exit = _sitebuiltins.Quitter("exit", "Ctrl-D (i.e. EOF)") # Prefixes for site-packages; add additional prefixes like /usr/local here PREFIXES = [sys.prefix, sys.exec_prefix] diff --git a/py2app/bootstrap/boot_aliasapp.py b/py2app/bootstrap/boot_aliasapp.py index 81996b0..d54b864 100644 --- a/py2app/bootstrap/boot_aliasapp.py +++ b/py2app/bootstrap/boot_aliasapp.py @@ -1,7 +1,7 @@ import re import sys -cookie_re = re.compile(br"coding[:=]\s*([-\w.]+)") +cookie_re = re.compile(rb"coding[:=]\s*([-\w.]+)") if sys.version_info[0] == 2: default_encoding = "ascii" else: diff --git a/py2app/bootstrap/boot_aliasplugin.py b/py2app/bootstrap/boot_aliasplugin.py index f7ecafa..479376f 100644 --- a/py2app/bootstrap/boot_aliasplugin.py +++ b/py2app/bootstrap/boot_aliasplugin.py @@ -1,7 +1,7 @@ import re import sys -cookie_re = re.compile(br"coding[:=]\s*([-\w.]+)") +cookie_re = re.compile(rb"coding[:=]\s*([-\w.]+)") if sys.version_info[0] == 2: default_encoding = "ascii" else: diff --git a/py2app/bootstrap/boot_app.py b/py2app/bootstrap/boot_app.py index 484336d..cc30975 100644 --- a/py2app/bootstrap/boot_app.py +++ b/py2app/bootstrap/boot_app.py @@ -1,7 +1,7 @@ import re import sys -cookie_re = re.compile(br"coding[:=]\s*([-\w.]+)") +cookie_re = re.compile(rb"coding[:=]\s*([-\w.]+)") if sys.version_info[0] == 2: default_encoding = "ascii" else: diff --git a/py2app/bootstrap/boot_plugin.py b/py2app/bootstrap/boot_plugin.py index 0f2581d..42e7280 100644 --- a/py2app/bootstrap/boot_plugin.py +++ b/py2app/bootstrap/boot_plugin.py @@ -1,7 +1,7 @@ import re import sys -cookie_re = re.compile(br"coding[:=]\s*([-\w.]+)") +cookie_re = re.compile(rb"coding[:=]\s*([-\w.]+)") if sys.version_info[0] == 2: default_encoding = "ascii" else: diff --git a/py2app/build_app.py b/py2app/build_app.py index f57199d..62fb7d9 100644 --- a/py2app/build_app.py +++ b/py2app/build_app.py @@ -90,6 +90,7 @@ else: sys_base_prefix = sys.prefix + def finalize_distribution_options(dist): """ setuptools.finalize_distribution_options extension @@ -97,12 +98,11 @@ def finalize_distribution_options(dist): setuptools 61. This addin will set the name and py_modules attributes - when a py2app distribution is detected that does not + when a py2app distribution is detected that does not yet have these attributes. are not already set """ - if (getattr(dist, "app", None) is None and - getattr(dist, "plugin", None) is None): + if getattr(dist, "app", None) is None and getattr(dist, "plugin", None) is None: return if getattr(dist.metadata, "py_modules", None) is None: @@ -310,7 +310,7 @@ def __init__(self, **kw): self.modules = [m] def __repr__(self): - return ""%(self.__dict__,) + return "" % (self.__dict__,) def get_dest_base(self): dest_base = getattr(self, "dest_base", None) @@ -1186,7 +1186,6 @@ def process_recipes(self, mf, filters, flatpackages, loader_files): else: break - def _run(self): try: if self.alias: diff --git a/py2app/bundletemplate/lib/site.py b/py2app/bundletemplate/lib/site.py index d867838..0bd152a 100644 --- a/py2app/bundletemplate/lib/site.py +++ b/py2app/bundletemplate/lib/site.py @@ -198,8 +198,9 @@ def getusersitepackages(): del sys.setdefaultencoding import builtins # noqa: E402 + import _sitebuiltins # noqa: E402 builtins.help = _sitebuiltins._Helper() -builtins.quit = _sitebuiltins.Quitter('quit', 'Ctrl-D (i.e. EOF)') -builtins.exit = _sitebuiltins.Quitter('exit', 'Ctrl-D (i.e. EOF)') +builtins.quit = _sitebuiltins.Quitter("quit", "Ctrl-D (i.e. EOF)") +builtins.exit = _sitebuiltins.Quitter("exit", "Ctrl-D (i.e. EOF)") diff --git a/py2app/recipes/PIL/__init__.py b/py2app/recipes/PIL/__init__.py index bf00c27..2fc8919 100644 --- a/py2app/recipes/PIL/__init__.py +++ b/py2app/recipes/PIL/__init__.py @@ -56,7 +56,7 @@ def check(cmd, mf): s = StringIO("_recipes_pil_prescript(%r)\n" % list(plugins)) print(plugins) plugins = set() - #sys.exit(1) + # sys.exit(1) for plugin in plugins: if have_PIL: mf.implyNodeReference(m, "PIL." + plugin) diff --git a/py2app/recipes/black.py b/py2app/recipes/black.py index 1fb86df..484b1ef 100644 --- a/py2app/recipes/black.py +++ b/py2app/recipes/black.py @@ -1,4 +1,5 @@ import os + from pkg_resources import get_distribution @@ -7,8 +8,8 @@ def check(cmd, mf): if m is None or m.filename is None: return None - egg = get_distribution('black').egg_info - top = os.path.join(egg, 'top_level.txt') + egg = get_distribution("black").egg_info + top = os.path.join(egg, "top_level.txt") # These cannot be in zip packages = ["black", "blib2to3"] @@ -16,11 +17,11 @@ def check(cmd, mf): # black may include optimized platform specific C extension which has # unusual name, e.g. 610faff656c4cfcbb4a3__mypyc; best to determine it from # the egg-info/top_level.txt - with open(top, 'r') as f: - includes = set(f.read().strip().split('\n')) + with open(top, "r") as f: + includes = set(f.read().strip().split("\n")) includes = list(includes.difference(packages)) # Missed dependency - includes.append('pathspec') + includes.append("pathspec") return {"includes": includes, "packages": packages} diff --git a/py2app/recipes/qt5.py b/py2app/recipes/qt5.py index 639f594..1dfb4e8 100644 --- a/py2app/recipes/qt5.py +++ b/py2app/recipes/qt5.py @@ -34,7 +34,6 @@ def check(cmd, mf): except ImportError: mf.import_hook("sip", m, level=1) - qtdir = QLibraryInfo.location(QLibraryInfo.LibrariesPath) if os.path.relpath(qtdir, os.path.dirname(PyQt5.__file__)).startswith("../"): # Qt5's prefix is not the PyQt5 package, which means diff --git a/py2app/recipes/qt6.py b/py2app/recipes/qt6.py index cc80bb4..b98086f 100644 --- a/py2app/recipes/qt6.py +++ b/py2app/recipes/qt6.py @@ -35,7 +35,9 @@ def check(cmd, mf): # Ensure that the Qt plugins are copied into the "Contents/plugins" folder, # that's where the bundles Qt expects them to be extra = { - "resources": [("..", [QLibraryInfo.path(QLibraryInfo.LibraryPath.PluginsPath)])] + "resources": [ + ("..", [QLibraryInfo.path(QLibraryInfo.LibraryPath.PluginsPath)]) + ] } else: @@ -51,7 +53,7 @@ def check(cmd, mf): except ImportError: mf.import_hook("sip", m, level=1) - reslt = {"packages": ["PyQt6"] } + reslt = {"packages": ["PyQt6"]} result.update(extra) return result diff --git a/py2app/recipes/setuptools.py b/py2app/recipes/setuptools.py index 5de491f..8610054 100644 --- a/py2app/recipes/setuptools.py +++ b/py2app/recipes/setuptools.py @@ -1,13 +1,14 @@ +import os import sys import textwrap -import os if sys.version_info[0] == 2: from cStringIO import StringIO else: from io import StringIO -PRESCRIPT=textwrap.dedent("""\ +PRESCRIPT = textwrap.dedent( + """\ import pkg_resources, zipimport, os def find_eggs_in_zip(importer, path_item, only=False): @@ -37,7 +38,8 @@ def _fixup_pkg_resources(): list(map(pkg_resources.working_set.add_entry, sys.path)) _fixup_pkg_resources() -""") +""" +) def check(cmd, mf): @@ -57,7 +59,8 @@ def check(cmd, mf): if os.path.exists(vendor_dir): for topdir, dirs, files in os.walk(vendor_dir): for fn in files: - if fn in ("__pycache__", "__init__.py"): continue + if fn in ("__pycache__", "__init__.py"): + continue relnm = os.path.relpath(os.path.join(topdir, fn), vendor_dir) if relnm.endswith(".py"): @@ -81,4 +84,7 @@ def check(cmd, mf): if sys.version[0] != 2: expected_missing_imports.add("__builtin__") - return {"expected_missing_imports": expected_missing_imports, "prescripts": [StringIO(PRESCRIPT)]} + return { + "expected_missing_imports": expected_missing_imports, + "prescripts": [StringIO(PRESCRIPT)], + } diff --git a/py2app/util.py b/py2app/util.py index 2b557b0..b6d852f 100644 --- a/py2app/util.py +++ b/py2app/util.py @@ -276,7 +276,6 @@ def in_system_path(filename): def fsencoding(s, encoding=sys.getfilesystemencoding()): # noqa: M511,B008 return macholib.util.fsencoding(s, encoding=encoding) - else: def fsencoding(s, encoding=sys.getfilesystemencoding()): # noqa: M511,B008 @@ -393,8 +392,9 @@ def byte_compile( if verbose: print("writing byte-compilation script") if not dry_run: - with NamedTemporaryFile(suffix=".py", delete=False, mode="w", - encoding="utf-8") as script: + with NamedTemporaryFile( + suffix=".py", delete=False, mode="w", encoding="utf-8" + ) as script: script_name = script.name script.write( """ diff --git a/setup.py b/setup.py index 548f046..9eabc7c 100644 --- a/setup.py +++ b/setup.py @@ -4,11 +4,11 @@ import os import sys import unittest -from distutils import log from fnmatch import fnmatch from setuptools import Command, find_packages, setup from setuptools.command import egg_info +from distutils import log fp = open("README.rst") try: @@ -23,7 +23,7 @@ fp.close() CLASSIFIERS = [ - "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: MacOS X :: Cocoa", "Intended Audience :: Developers", @@ -291,7 +291,8 @@ def run(self): long_description_content_type="text/x-rst; charset=UTF-8", classifiers=CLASSIFIERS, keywords=[".app", "standalone"], - install_requires=["altgraph>=0.16", "modulegraph>=0.17", "macholib>=1.11"], + install_requires=["altgraph>=0.16", "modulegraph>=0.17", "macholib>=1.16"], + setup_requires=["altgraph>=0.16", "modulegraph>=0.17", "macholib>=1.16"], tests_require=["pyobjc"], cmdclass=cmdclass, packages=find_packages(exclude=["py2app_tests"]), @@ -351,7 +352,9 @@ def run(self): ], }, entry_points={ - "setuptools.finalize_distribution_options": ["py2app = py2app.build_app:finalize_distribution_options"], + "setuptools.finalize_distribution_options": [ + "py2app = py2app.build_app:finalize_distribution_options" + ], "distutils.commands": ["py2app = py2app.build_app:py2app"], "distutils.setup_keywords": [ "app = py2app.build_app:validate_target", diff --git a/tox.ini b/tox.ini index 3c362ed..396d12e 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,8 @@ envlist = isort,black,py37,py38,py39,py310,flake8 # commands = {envbindir}/python -mcoverage run --parallel setup.py test --verbosity=3 commands = {envbindir}/python setup.py test --verbosity=3 deps = + macholib + modulegraph coverage pyobjc