From ef368d15cab213276326712b2ce50509a35f284b Mon Sep 17 00:00:00 2001 From: "Dr.Lt.Data" Date: Sat, 13 Apr 2024 23:23:38 +0900 Subject: [PATCH] wip --- tools/resolve_requirements.py | 120 +++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 30 deletions(-) diff --git a/tools/resolve_requirements.py b/tools/resolve_requirements.py index cee6685..1b28348 100644 --- a/tools/resolve_requirements.py +++ b/tools/resolve_requirements.py @@ -4,14 +4,25 @@ import re from typing import Tuple, Optional from packaging import version, specifiers import re +import traceback + + +import re +from typing import Tuple, Optional def parse_pip_spec(spec: str) -> Tuple[str, Optional[str], Optional[str]]: - match = re.match(r'([^=<>!~\[\]]+)(\[.*\])?(.*)', spec) + match = re.match(r'([A-Za-z0-9_-]+)(\[[^\]]+\])?(.*)', spec) if match: package_name = match.group(1).strip() - extras = match.group(2).strip()[1:-1] if match.group(2) else None - version_spec = match.group(3).strip() or None + extras = match.group(2)[1:-1] if match.group(2) else None + + version_spec = None + if match.group(3): + version_match = re.match(r'\(?([^\)]+)\)?', match.group(3).strip()) + if version_match: + version_spec = version_match.group(1).strip() + return package_name, extras, version_spec else: raise ValueError(f"Invalid package spec: {spec}") @@ -24,14 +35,14 @@ def fetch_package_versions(package_name): response.raise_for_status() data = response.json() versions = data["releases"].keys() - print(f"Available versions of {package_name}:") + print(f"Available versions of '{package_name}':") return list(versions) except requests.RequestException as e: print(f"Error fetching package versions: {e}") return [] -def fetch_required_dist(package_name, ver, extras): +def fetch_required_dist(package_name, ver): url = f"https://pypi.org/pypi/{package_name}/{ver}/json" try: response = requests.get(url) @@ -47,26 +58,23 @@ def fetch_required_dist(package_name, ver, extras): dependencies = [] for dist in requires_dist: dist = dist.split(";") - match = re.match(r'([^=<>!~]+)(.*)', dist[0].strip()) - pkg_name = None - ver_spec = None dist_extra = None + if len(dist) > 1: + match = re.match(r'extra\s*==\s*"([^"]*)"', dist[1].strip()) + if match: + dist_extra = match.group(1).strip() + + match = re.match(r'([^=<>!~]+)(.*)', dist[0].strip()) if match: pkg_name = match.group(1).strip() ver_spec = match.group(2).strip() or None - if len(dist) > 1: - match = re.match(r'extra\s*==\s*"([^"]*)"', dist[1].strip()) - if match: - dist_extra = match.group(1).strip() + if ver_spec is not None and ver_spec.endswith(')'): + pass - if pkg_name is not None: - if dist_extra is None: - dependencies.append((pkg_name, ver_spec)) - elif dist_extra == extras: - dependencies.append((pkg_name, ver_spec)) + dependencies.append((pkg_name, dist_extra, ver_spec)) # print(f"Processed dependencies for {package_name} version {ver}: {dependencies}") return dependencies @@ -76,27 +84,79 @@ def fetch_required_dist(package_name, ver, extras): def filter_versions(versions, spec): - specifier = specifiers.SpecifierSet(spec) if spec else specifiers.SpecifierSet() - return [v for v in versions if v in specifier] + try: + specifier = specifiers.SpecifierSet(spec) if spec else specifiers.SpecifierSet() + res = [] + for v in versions: + match = re.match(r'^([0-9.]+[0-9])', v) + if match: + if match.group(1) in specifier: + res.append(v) -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Usage: python script.py ") - sys.exit(1) - - pkg_name, extras, version_spec = parse_pip_spec(sys.argv[1]) + return res + except Exception as e: + print(f"ERROR: {e}") + traceback.print_exc() + + +def filter_extras(required_dist, extras): + return [x for x in required_dist if x[1] is None or x[1] == extras] + + +resolved = {} + + +def resolve_pip_spec(pkg_name, extras, ver): + global resolved + required_dist = fetch_required_dist(pkg_name, ver) + resolved[(pkg_name, extras, ver)] = required_dist + return filter_extras(required_dist, extras) + + +def best_match(pkg_name, version_spec): + global resolved candidate_versions = fetch_package_versions(pkg_name) if version_spec: candidate_versions = filter_versions(candidate_versions, version_spec) - + if candidate_versions: - best_version = max(candidate_versions, key=version.parse) - print(f"Best matching version: {best_version}") - required_dist = fetch_required_dist(pkg_name, best_version, extras) - print(f"Required_dist: {required_dist}") + return max(candidate_versions, key=version.parse) else: print("No matching versions found.") + return None + + +def resolve_recursively(items): + worklist = items + + while worklist: + pkg_name, extras, version_spec = worklist[0] + worklist = worklist[1:] + ver = best_match(pkg_name, version_spec) + if (pkg_name, extras, ver) in resolved: + continue + if ver is not None: + nexts = resolve_pip_spec(pkg_name, extras, ver) + worklist += nexts + + +def main(): + global resolved + if len(sys.argv) < 2: + print("Usage: python script.py ") + sys.exit(1) + + pkg_name, extras, version_spec = parse_pip_spec(sys.argv[1]) + ver = best_match(pkg_name, version_spec) + required_dist = resolve_pip_spec(pkg_name, extras, ver) + resolve_recursively(required_dist) + + print(f"Requirements: {resolved}") + + +if __name__ == "__main__": + main() \ No newline at end of file