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",