[Buildroot] [PATCH 1/1] utils/scanpypi: refactor setuptools handling to not use imp

James Hilliard james.hilliard1 at gmail.com
Thu Nov 30 19:32:34 UTC 2023


The imp module is deprecated as of python verison 3.12.

Refactor setuptools handling to remove monkeypatching hack and
instead do pep517 metadata generation and dependency resolution.

Invert setuptools/pyproject fallback ordering so that we try
parsing pyproject.toml files first.

Use a set comprehension instead of a list comprehension for
generating pkg_req so that we don't get duplicates.

Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
 utils/scanpypi | 92 +++++++++++++++++---------------------------------
 1 file changed, 31 insertions(+), 61 deletions(-)

diff --git a/utils/scanpypi b/utils/scanpypi
index 021c99a172..a1c2c2bd52 100755
--- a/utils/scanpypi
+++ b/utils/scanpypi
@@ -18,8 +18,8 @@ import hashlib
 import re
 import textwrap
 import tempfile
-import imp
-from functools import wraps
+import importlib
+from setuptools.build_meta import prepare_metadata_for_build_wheel
 import six.moves.urllib.request
 import six.moves.urllib.error
 import six.moves.urllib.parse
@@ -93,32 +93,6 @@ def toml_load(f):
         raise ex
 
 
-def setup_decorator(func, method):
-    """
-    Decorator for distutils.core.setup and setuptools.setup.
-    Puts the arguments with which setup is called as a dict
-    Add key 'method' which should be either 'setuptools' or 'distutils'.
-
-    Keyword arguments:
-    func -- either setuptools.setup or distutils.core.setup
-    method -- either 'setuptools' or 'distutils'
-    """
-
-    @wraps(func)
-    def closure(*args, **kwargs):
-        # Any python packages calls its setup function to be installed.
-        # Argument 'name' of this setup function is the package's name
-        BuildrootPackage.setup_args[kwargs['name']] = kwargs
-        BuildrootPackage.setup_args[kwargs['name']]['method'] = method
-    return closure
-
-# monkey patch
-import setuptools  # noqa E402
-setuptools.setup = setup_decorator(setuptools.setup, 'setuptools')
-import distutils   # noqa E402
-distutils.core.setup = setup_decorator(setuptools.setup, 'distutils')
-
-
 def find_file_upper_case(filenames, path='./'):
     """
     List generator:
@@ -345,26 +319,18 @@ class BuildrootPackage():
         """
         current_dir = os.getcwd()
         os.chdir(self.tmp_extract)
-        sys.path.insert(0, self.tmp_extract)
         try:
-            s_file, s_path, s_desc = imp.find_module('setup', [self.tmp_extract])
-            imp.load_module('__main__', s_file, s_path, s_desc)
-            if self.metadata_name in self.setup_args:
-                pass
-            elif self.metadata_name.replace('_', '-') in self.setup_args:
-                self.metadata_name = self.metadata_name.replace('_', '-')
-            elif self.metadata_name.replace('-', '_') in self.setup_args:
-                self.metadata_name = self.metadata_name.replace('-', '_')
+            metadata = prepare_metadata_for_build_wheel(self.tmp_extract)
             try:
-                self.setup_metadata = self.setup_args[self.metadata_name]
-            except KeyError:
-                # This means setup was not called
-                print('ERROR: Could not determine package metadata for {pkg}.\n'
-                      .format(pkg=self.real_name))
-                raise
+                dist = importlib.metadata.Distribution.at(metadata)
+                self.metadata_name = dist.name
+                self.setup_metadata = {'method': 'setuptools'}
+                if dist.requires:
+                    self.setup_metadata['install_requires'] = dist.requires
+            finally:
+                shutil.rmtree(metadata)
         finally:
             os.chdir(current_dir)
-            sys.path.remove(self.tmp_extract)
 
     def load_pyproject(self):
         """
@@ -372,7 +338,6 @@ class BuildrootPackage():
         """
         current_dir = os.getcwd()
         os.chdir(self.tmp_extract)
-        sys.path.insert(0, self.tmp_extract)
         try:
             pyproject_data = toml_load('pyproject.toml')
             try:
@@ -380,20 +345,25 @@ class BuildrootPackage():
                 self.metadata_name = self.setup_metadata.get('name', self.real_name)
                 build_system = pyproject_data.get('build-system', {})
                 build_backend = build_system.get('build-backend', None)
-                if build_backend and build_backend == 'flit_core.buildapi':
-                    self.setup_metadata['method'] = 'flit'
+                if build_backend:
+                    if build_backend == 'flit_core.buildapi':
+                        self.setup_metadata['method'] = 'flit'
+                    elif build_backend == 'setuptools.build_meta':
+                        raise Exception("handle setuptools")
+                    else:
+                        self.setup_metadata['method'] = 'unknown'
                 elif build_system.get('backend-path', None):
                     self.setup_metadata['method'] = 'pep517'
                 else:
-                    self.setup_metadata['method'] = 'unknown'
+                    raise Exception("handle setuptools")
             except KeyError:
                 print('ERROR: Could not determine package metadata for {pkg}.\n'
                       .format(pkg=self.real_name))
                 raise
         except FileNotFoundError:
             raise
-        os.chdir(current_dir)
-        sys.path.remove(self.tmp_extract)
+        finally:
+            os.chdir(current_dir)
 
     def get_requirements(self, pkg_folder):
         """
@@ -411,8 +381,8 @@ class BuildrootPackage():
                         for req in self.pkg_req]
 
         # get rid of commented lines and also strip the package strings
-        self.pkg_req = [item.strip() for item in self.pkg_req
-                        if len(item) > 0 and item[0] != '#']
+        self.pkg_req = {item.strip() for item in self.pkg_req
+                        if len(item) > 0 and item[0] != '#'}
 
         req_not_found = self.pkg_req
         self.pkg_req = list(map(pkg_buildroot_name, self.pkg_req))
@@ -778,15 +748,15 @@ def main():
 
             # Loading the package install info from the package
             try:
-                package.load_setup()
-            except ImportError as err:
-                if 'buildutils' in str(err):
-                    print('This package needs buildutils')
-                    continue
-                else:
-                    try:
-                        package.load_pyproject()
-                    except Exception:
+                package.load_pyproject()
+            except Exception:
+                try:
+                    package.load_setup()
+                except ImportError as err:
+                    if 'buildutils' in str(err):
+                        print('This package needs buildutils')
+                        continue
+                    else:
                         raise
             except (AttributeError, KeyError) as error:
                 print('Error: Could not install package {pkg}: {error}'.format(
-- 
2.34.1




More information about the buildroot mailing list