From e241419490c80cb0b758dc017ffe8f7b95533890 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Sat, 25 Nov 2023 00:34:18 -0700 Subject: [PATCH] utils/scanpypi: add flit package support These packages don't have a setup.py so we instead need to parse their pyproject.toml file. It is possible that such a package does not define a project_urls config item, so ensure we do not choke on it (None.get() would raise). Note that this currently doesn't handle flit package dependency resolution. Signed-off-by: James Hilliard Reviewed-by: Yegor Yefremov [yann.morin.1998@free.fr: - add sentence about the project_urls rewrite - fix flake8 errors ] Signed-off-by: Yann E. MORIN --- utils/scanpypi | 94 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/utils/scanpypi b/utils/scanpypi index 3c98bb4bcc..021c99a172 100755 --- a/utils/scanpypi +++ b/utils/scanpypi @@ -43,6 +43,56 @@ except ImportError: liclookup = None +def toml_load(f): + with open(f, 'rb') as fh: + ex = None + + # Try standard library tomllib first + try: + from tomllib import load + return load(fh) + except ImportError: + pass + + # Try regular tomli next + try: + from tomli import load + return load(fh) + except ImportError as e: + ex = e + + # Try pip's vendored tomli + try: + from pip._vendor.tomli import load + try: + return load(fh) + except TypeError: + # Fallback to handle older version + try: + fh.seek(0) + w = io.TextIOWrapper(fh, encoding="utf8", newline="") + return load(w) + finally: + w.detach() + except ImportError: + pass + + # Try regular toml last + try: + from toml import load + fh.seek(0) + w = io.TextIOWrapper(fh, encoding="utf8", newline="") + try: + return load(w) + finally: + w.detach() + except ImportError: + pass + + print('This package needs tomli') + raise ex + + def setup_decorator(func, method): """ Decorator for distutils.core.setup and setuptools.setup. @@ -316,6 +366,35 @@ class BuildrootPackage(): os.chdir(current_dir) sys.path.remove(self.tmp_extract) + def load_pyproject(self): + """ + Loads the corresponding pyproject.toml and store its metadata + """ + current_dir = os.getcwd() + os.chdir(self.tmp_extract) + sys.path.insert(0, self.tmp_extract) + try: + pyproject_data = toml_load('pyproject.toml') + try: + self.setup_metadata = pyproject_data.get('project', {}) + 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' + elif build_system.get('backend-path', None): + self.setup_metadata['method'] = 'pep517' + else: + self.setup_metadata['method'] = 'unknown' + 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) + def get_requirements(self, pkg_folder): """ Retrieve dependencies from the metadata found in the setup.py script of @@ -620,8 +699,12 @@ class BuildrootPackage(): if help_lines[-1][-1] != '.': help_lines[-1] += '.' - home_page = md_info.get('home_page', None) or \ - md_info.get('project_urls', {}).get('Homepage', None) # noqa: E127 + home_page = md_info.get('home_page', None) + + if not home_page: + project_urls = md_info.get('project_urls', None) + if project_urls: + home_page = project_urls.get('Homepage', None) if home_page: # \t + two spaces is 3 char long @@ -699,9 +782,12 @@ def main(): except ImportError as err: if 'buildutils' in str(err): print('This package needs buildutils') + continue else: - raise - continue + try: + package.load_pyproject() + except Exception: + raise except (AttributeError, KeyError) as error: print('Error: Could not install package {pkg}: {error}'.format( pkg=package.real_name, error=error))