|
|
|
@ -154,8 +154,8 @@ def add_lang_to_module(paths, module_name, path):
|
|
|
|
|
Returns True if the language code detection was successful
|
|
|
|
|
"""
|
|
|
|
|
for i, parent in enumerate(path.parents):
|
|
|
|
|
if i > 0 and parent.name == 'locale':
|
|
|
|
|
lang_country_code = path.parents[i-1].name
|
|
|
|
|
if parent.name == 'LC_MESSAGES':
|
|
|
|
|
lang_country_code = path.parents[i+1].name
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
return False
|
|
|
|
@ -286,6 +286,36 @@ def module_names_from_path(path):
|
|
|
|
|
return {'.'.join(parts[:x+1]) for x in range(len(parts))}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_license_file(path, license_files, license_directories):
|
|
|
|
|
"""
|
|
|
|
|
Check if the given BuildrootPath path matches any of the "License-File" entries.
|
|
|
|
|
The path is considered matched when resolved from any of the license_directories
|
|
|
|
|
matches string-wise what is stored in any "License-File" entry (license_files).
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
>>> site_packages = BuildrootPath('/usr/lib/python3.12/site-packages')
|
|
|
|
|
>>> distinfo = site_packages / 'foo-1.0.dist-info'
|
|
|
|
|
>>> license_directories = [distinfo / 'licenses', distinfo]
|
|
|
|
|
>>> license_files = ['LICENSE.txt', 'AUTHORS.md']
|
|
|
|
|
>>> is_license_file(distinfo / 'AUTHORS.md', license_files, license_directories)
|
|
|
|
|
True
|
|
|
|
|
>>> is_license_file(distinfo / 'licenses/LICENSE.txt', license_files, license_directories)
|
|
|
|
|
True
|
|
|
|
|
>>> # we don't match based on directory only
|
|
|
|
|
>>> is_license_file(distinfo / 'licenses/COPYING', license_files, license_directories)
|
|
|
|
|
False
|
|
|
|
|
>>> is_license_file(site_packages / 'foo/LICENSE.txt', license_files, license_directories)
|
|
|
|
|
False
|
|
|
|
|
"""
|
|
|
|
|
if not license_files or not license_directories:
|
|
|
|
|
return False
|
|
|
|
|
for license_dir in license_directories:
|
|
|
|
|
if (path.is_relative_to(license_dir) and
|
|
|
|
|
str(path.relative_to(license_dir)) in license_files):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def classify_paths(
|
|
|
|
|
record_path, parsed_record_content, metadata, sitedirs, python_version, prefix
|
|
|
|
|
):
|
|
|
|
@ -311,10 +341,17 @@ def classify_paths(
|
|
|
|
|
"other": {"files": []}, # regular %file entries we could not parse :(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
license_files = metadata.get_all('License-File')
|
|
|
|
|
license_directory = distinfo / 'licenses' # See PEP 369 "Root License Directory"
|
|
|
|
|
# setuptools was the first known build backend to implement License-File.
|
|
|
|
|
# Unfortunately they don't put licenses to the license directory (yet):
|
|
|
|
|
# https://github.com/pypa/setuptools/issues/3596
|
|
|
|
|
# Hence, we check licenses in both licenses and dist-info
|
|
|
|
|
license_directories = (license_directory, distinfo)
|
|
|
|
|
|
|
|
|
|
# In RECORDs generated by pip, there are no directories, only files.
|
|
|
|
|
# The example RECORD from PEP 376 does not contain directories either.
|
|
|
|
|
# Hence, we'll only assume files, but TODO get it officially documented.
|
|
|
|
|
license_files = metadata.get_all('License-File')
|
|
|
|
|
for path in parsed_record_content:
|
|
|
|
|
if path.suffix == ".pyc":
|
|
|
|
|
# we handle bytecode separately
|
|
|
|
@ -325,7 +362,7 @@ def classify_paths(
|
|
|
|
|
# RECORD and REQUESTED files are removed in %pyproject_install
|
|
|
|
|
# See PEP 627
|
|
|
|
|
continue
|
|
|
|
|
if license_files and str(path.relative_to(distinfo)) in license_files:
|
|
|
|
|
if is_license_file(path, license_files, license_directories):
|
|
|
|
|
paths["metadata"]["licenses"].append(path)
|
|
|
|
|
else:
|
|
|
|
|
paths["metadata"]["files"].append(path)
|
|
|
|
@ -499,6 +536,50 @@ def generate_file_list(paths_dict, module_globs, include_others=False):
|
|
|
|
|
return sorted(files)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_module_list(paths_dict, module_globs):
|
|
|
|
|
"""
|
|
|
|
|
This function takes the paths_dict created by the classify_paths() function and
|
|
|
|
|
reads the modules names from it.
|
|
|
|
|
It filters those whose top-level module names match any of the provided module_globs.
|
|
|
|
|
|
|
|
|
|
Returns list with matching qualified module names.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
>>> generate_module_list({'module_names': {'foo', 'foo.bar', 'baz'}}, {'foo'})
|
|
|
|
|
['foo', 'foo.bar']
|
|
|
|
|
|
|
|
|
|
>>> generate_module_list({'module_names': {'foo', 'foo.bar', 'baz'}}, {'*foo'})
|
|
|
|
|
['foo', 'foo.bar']
|
|
|
|
|
|
|
|
|
|
>>> generate_module_list({'module_names': {'foo', 'foo.bar', 'baz'}}, {'foo', 'baz'})
|
|
|
|
|
['baz', 'foo', 'foo.bar']
|
|
|
|
|
|
|
|
|
|
>>> generate_module_list({'module_names': {'foo', 'foo.bar', 'baz'}}, {'*'})
|
|
|
|
|
['baz', 'foo', 'foo.bar']
|
|
|
|
|
|
|
|
|
|
>>> generate_module_list({'module_names': {'foo', 'foo.bar', 'baz'}}, {'bar'})
|
|
|
|
|
[]
|
|
|
|
|
|
|
|
|
|
Submodules aren't discovered:
|
|
|
|
|
|
|
|
|
|
>>> generate_module_list({'module_names': {'foo', 'foo.bar', 'baz'}}, {'*bar'})
|
|
|
|
|
[]
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
module_names = paths_dict['module_names']
|
|
|
|
|
filtered_module_names = set()
|
|
|
|
|
|
|
|
|
|
for glob in module_globs:
|
|
|
|
|
for name in module_names:
|
|
|
|
|
# Match the top-level part of the qualified name, eg. 'foo.bar.baz' -> 'foo'
|
|
|
|
|
top_level_name = name.split('.')[0]
|
|
|
|
|
if fnmatch.fnmatchcase(top_level_name, glob):
|
|
|
|
|
filtered_module_names.add(name)
|
|
|
|
|
|
|
|
|
|
return sorted(filtered_module_names)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_varargs(varargs):
|
|
|
|
|
"""
|
|
|
|
|
Parse varargs from the %pyproject_save_files macro
|
|
|
|
@ -627,7 +708,7 @@ def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_versio
|
|
|
|
|
parsed_records = load_parsed_record(pyproject_record)
|
|
|
|
|
|
|
|
|
|
final_file_list = []
|
|
|
|
|
all_module_names = set()
|
|
|
|
|
final_module_list = []
|
|
|
|
|
|
|
|
|
|
for record_path, files in parsed_records.items():
|
|
|
|
|
metadata = dist_metadata(buildroot, record_path)
|
|
|
|
@ -638,12 +719,11 @@ def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_versio
|
|
|
|
|
final_file_list.extend(
|
|
|
|
|
generate_file_list(paths_dict, globs, include_auto)
|
|
|
|
|
)
|
|
|
|
|
all_module_names.update(paths_dict["module_names"])
|
|
|
|
|
|
|
|
|
|
# Sort values, so they are always checked in the same order
|
|
|
|
|
all_module_names = sorted(all_module_names)
|
|
|
|
|
final_module_list.extend(
|
|
|
|
|
generate_module_list(paths_dict, globs)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return final_file_list, all_module_names
|
|
|
|
|
return final_file_list, final_module_list
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(cli_args):
|
|
|
|
@ -662,17 +742,31 @@ def main(cli_args):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def argparser():
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
|
description="Create %{pyproject_files} for a Python project.",
|
|
|
|
|
prog="%pyproject_save_files",
|
|
|
|
|
add_help=False,
|
|
|
|
|
# custom usage to add +auto
|
|
|
|
|
usage="%(prog)s MODULE_GLOB [MODULE_GLOB ...] [+auto]",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
'--help', action='help',
|
|
|
|
|
default=argparse.SUPPRESS,
|
|
|
|
|
help=argparse.SUPPRESS,
|
|
|
|
|
)
|
|
|
|
|
r = parser.add_argument_group("required arguments")
|
|
|
|
|
r.add_argument("--output-files", type=PosixPath, required=True)
|
|
|
|
|
r.add_argument("--output-modules", type=PosixPath, required=True)
|
|
|
|
|
r.add_argument("--buildroot", type=PosixPath, required=True)
|
|
|
|
|
r.add_argument("--sitelib", type=BuildrootPath, required=True)
|
|
|
|
|
r.add_argument("--sitearch", type=BuildrootPath, required=True)
|
|
|
|
|
r.add_argument("--python-version", type=str, required=True)
|
|
|
|
|
r.add_argument("--pyproject-record", type=PosixPath, required=True)
|
|
|
|
|
r.add_argument("--prefix", type=PosixPath, required=True)
|
|
|
|
|
parser.add_argument("varargs", nargs="+")
|
|
|
|
|
r.add_argument("--output-files", type=PosixPath, required=True, help=argparse.SUPPRESS)
|
|
|
|
|
r.add_argument("--output-modules", type=PosixPath, required=True, help=argparse.SUPPRESS)
|
|
|
|
|
r.add_argument("--buildroot", type=PosixPath, required=True, help=argparse.SUPPRESS)
|
|
|
|
|
r.add_argument("--sitelib", type=BuildrootPath, required=True, help=argparse.SUPPRESS)
|
|
|
|
|
r.add_argument("--sitearch", type=BuildrootPath, required=True, help=argparse.SUPPRESS)
|
|
|
|
|
r.add_argument("--python-version", type=str, required=True, help=argparse.SUPPRESS)
|
|
|
|
|
r.add_argument("--pyproject-record", type=PosixPath, required=True, help=argparse.SUPPRESS)
|
|
|
|
|
r.add_argument("--prefix", type=PosixPath, required=True, help=argparse.SUPPRESS)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"varargs", nargs="+", metavar="MODULE_GLOB",
|
|
|
|
|
help="Shell-like glob matching top-level module names to save into %%{pyproject_files}",
|
|
|
|
|
)
|
|
|
|
|
return parser
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|