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 <james.hilliard1@gmail.com>
Reviewed-by: Yegor Yefremov <yegorslists@googlemail.com>
[yann.morin.1998@free.fr:
  - add sentence about the project_urls rewrite
  - fix flake8 errors
]
Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
This commit is contained in:
James Hilliard 2023-11-25 00:34:18 -07:00 committed by Yann E. MORIN
parent efb5d3fbd8
commit e241419490

View File

@ -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))