From 55eb236a135fd02eec97c6e438217f78ed8222cc Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 7 Apr 2022 10:17:31 +0200 Subject: [PATCH] Workaround for setuptools distribution autodiscovery This introduces a ``setuptools.finalize_distribution_options`` that sets the *name* and *py_modules* attributes of the distribution in a way that is compatible with py2app's regular calculation of these attributes. This avoid problems with setuptools 61.0.0 or later that use autodiscovery to set these attributes to values that may not be compatible with py2app. Issue #414 --- doc/changelog.rst | 25 +++++++++++++++---------- py2app/build_app.py | 38 ++++++++++++++++++++++++++++++++++++-- setup.py | 1 + 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 9fe0b08..244f6c5 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -4,20 +4,25 @@ Release history py2app 0.28 ----------- -.. warning:: incompatibility with setuptools 61.0 +* #414 Workaround for autodiscovery in setuptools 61.0 - Setuptools 61.0 introduces autodiscovery of distribution - attributes, and this might affect users of py2app. Two - related error scenario's are: + Setuptools 61.0 introduces autodiscovery of distribution + attributes, and that broke py2app. This version introduces + a ``setuptools.finalize_distribution_options`` entrypoint + in py2app that will set the distributions's *name* and + *py_modules* attributes in a way that is compatible with + the main code of py2app when they are not yet set (before + autodiscovery kicks in). - - The name of the generated application is not based on - the script name, but some other value. + In older versions of py2app buildin an app can fail in two + ways with setuptools 61.0 or later: - - Calling ``python setup.py py2app`` results in an error - mentioning ``Multiple top-level modules discovered``. + - The name of the generated application is not based on + the script name, but some other value. + + - Calling ``python setup.py py2app`` results in an error + mentioning ``Multiple top-level modules discovered``. - To disable autodiscovery by setuptools update the setup.py - file and add ``name=`` and ``py_modules=[]`` to the call to setup(). * PR #418: Add recipe for black diff --git a/py2app/build_app.py b/py2app/build_app.py index 3a5081d..f57199d 100644 --- a/py2app/build_app.py +++ b/py2app/build_app.py @@ -90,6 +90,36 @@ else: sys_base_prefix = sys.prefix +def finalize_distribution_options(dist): + """ + setuptools.finalize_distribution_options extension + point for py2app, to deail with autodiscovery in + setuptools 61. + + This addin will set the name and py_modules attributes + 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): + return + + if getattr(dist.metadata, "py_modules", None) is None: + dist.py_modules = [] + + name = getattr(dist.metadata, "name", None) + if name is None or name == "UNKNOWN": + if dist.app: + targets = FixupTargets(dist.app, "script") + else: + targets = FixupTargets(dist.plugin, "script") + + base = targets[0].get_dest_base() + name = os.path.basename(base) + + dist.metadata.name = name + def loader_paths(sourcefn, destfn): # Yield (sourcefn, destfn) pairs for all @@ -107,7 +137,6 @@ def loader_paths(sourcefn, destfn): def rewrite_tkinter_load_commands(tkinter_path): - print("rewrite_tk", tkinter_path) m = macholib.MachO.MachO(tkinter_path) tcl_path = None tk_path = None @@ -280,6 +309,9 @@ def __init__(self, **kw): if m and isinstance(m, basestring): self.modules = [m] + def __repr__(self): + return ""%(self.__dict__,) + def get_dest_base(self): dest_base = getattr(self, "dest_base", None) if dest_base: @@ -1154,6 +1186,7 @@ def process_recipes(self, mf, filters, flatpackages, loader_files): else: break + def _run(self): try: if self.alias: @@ -1533,6 +1566,7 @@ def packagefilter(mod, pkgdirs=pkgdirs): if dist_info_path is None and (fn.endswith(".pyc") or fn.endswith(".pyo")): dist_info_path = metadata_infos.get(fn[:-1], None) if dist_info_path is not None: + print("ADD INFO", dist_info_path) included_metadata.add(dist_info_path) def files_in_dir(toplevel): @@ -1592,11 +1626,11 @@ def files_in_dir(toplevel): continue src = os.path.join(pkg_info_path, fn) dst = os.path.join(base, fn) + if os.path.isdir(src): self.copy_tree(src, dst, preserve_symlinks=False) else: self.copy_file(src, dst) - self.lib_files = [] self.app_files = [] diff --git a/setup.py b/setup.py index 88bb7b0..548f046 100644 --- a/setup.py +++ b/setup.py @@ -351,6 +351,7 @@ def run(self): ], }, entry_points={ + "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",