|
11 | 11 | import operator |
12 | 12 | import os |
13 | 13 | from typing import List |
| 14 | +from typing import Sequence |
14 | 15 |
|
15 | 16 | import packaging.requirements |
16 | 17 | import packaging.utils |
@@ -51,9 +52,9 @@ def is_valid_version(parsed_version, requirements, identifier, bad_versions): |
51 | 52 |
|
52 | 53 |
|
53 | 54 | class PythonInputProvider(AbstractProvider): |
54 | | - def __init__(self, environment=None, repos=[]): |
| 55 | + def __init__(self, environment=None, repos=tuple()): |
55 | 56 | self.environment = environment |
56 | | - self.repos = repos |
| 57 | + self.repos = repos or [] |
57 | 58 | self.versions_by_package = {} |
58 | 59 | self.dependencies_by_purl = {} |
59 | 60 |
|
@@ -82,54 +83,75 @@ def get_versions_for_package(self, name, repo=None): |
82 | 83 | """ |
83 | 84 | Return a list of versions for a package. |
84 | 85 | """ |
85 | | - versions = [] |
86 | 86 | if repo and self.environment: |
87 | | - for version, package in repo._get_package_versions_map(name).items(): |
88 | | - wheels = package.get_supported_wheels(environment=self.environment) |
89 | | - if list(wheels): |
90 | | - versions.append(version) |
| 87 | + return self.get_versions_for_package_from_repo(name, repo) |
91 | 88 | else: |
92 | | - if name not in self.versions_by_package: |
93 | | - api_url = f"https://pypi.org/pypi/{name}/json" |
94 | | - resp = get_response(api_url) |
95 | | - if not resp: |
96 | | - self.versions_by_package[name] = [] |
97 | | - releases = resp.get("releases") or {} |
98 | | - self.versions_by_package[name] = releases.keys() or [] |
99 | | - versions = self.versions_by_package[name] |
| 89 | + return self.get_versions_for_package_from_pypi_json_api(name) |
| 90 | + |
| 91 | + def get_versions_for_package_from_repo(self, name, repo): |
| 92 | + """ |
| 93 | + Return a list of versions for a package name from a repo |
| 94 | + """ |
| 95 | + versions = [] |
| 96 | + for version, package in repo._get_package_versions_map(name).items(): |
| 97 | + wheels = package.get_supported_wheels(environment=self.environment) |
| 98 | + if list(wheels): |
| 99 | + versions.append(version) |
| 100 | + return versions |
| 101 | + |
| 102 | + def get_versions_for_package_from_pypi_json_api(self, name): |
| 103 | + """ |
| 104 | + Return a list of versions for a package name from the PyPI.org JSON API |
| 105 | + """ |
| 106 | + if name not in self.versions_by_package: |
| 107 | + api_url = f"https://pypi.org/pypi/{name}/json" |
| 108 | + resp = get_response(api_url) |
| 109 | + if not resp: |
| 110 | + self.versions_by_package[name] = [] |
| 111 | + releases = resp.get("releases") or {} |
| 112 | + self.versions_by_package[name] = releases.keys() or [] |
| 113 | + versions = self.versions_by_package[name] |
100 | 114 | return versions |
101 | 115 |
|
102 | 116 | def get_requirements_for_package(self, purl, candidate): |
103 | 117 | """ |
104 | 118 | Yield requirements for a package. |
105 | 119 | """ |
106 | 120 | if self.repos and self.environment: |
107 | | - wheels = utils_pypi.download_wheel( |
108 | | - name=candidate.name, |
109 | | - version=str(candidate.version), |
110 | | - environment=self.environment, |
111 | | - repos=self.repos, |
112 | | - ) |
113 | | - for wheel in wheels: |
114 | | - deps = list( |
115 | | - PypiWheelHandler.parse(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, wheel)) |
116 | | - ) |
117 | | - assert len(deps) == 1 |
118 | | - deps = deps[0].dependencies |
119 | | - for dep in deps: |
120 | | - if dep.scope == "install": |
121 | | - yield packaging.requirements.Requirement(str(dep.extracted_requirement)) |
| 121 | + return self.get_requirements_for_package_from_pypi_simple(candidate) |
122 | 122 | else: |
123 | | - if str(purl) not in self.dependencies_by_purl: |
124 | | - api_url = f"https://pypi.org/pypi/{purl.name}/{purl.version}/json" |
125 | | - resp = get_response(api_url) |
126 | | - if not resp: |
127 | | - self.dependencies_by_purl[str(purl)] = [] |
128 | | - info = resp.get("info") or {} |
129 | | - requires_dist = info.get("requires_dist") or [] |
130 | | - self.dependencies_by_purl[str(purl)] = requires_dist |
131 | | - for dependency in self.dependencies_by_purl[str(purl)]: |
132 | | - yield packaging.requirements.Requirement(dependency) |
| 123 | + return self.get_requirements_for_package_from_pypi_json_api(purl) |
| 124 | + |
| 125 | + def get_requirements_for_package_from_pypi_simple(self, candidate): |
| 126 | + wheels = utils_pypi.download_wheel( |
| 127 | + name=candidate.name, |
| 128 | + version=str(candidate.version), |
| 129 | + environment=self.environment, |
| 130 | + repos=self.repos, |
| 131 | + ) |
| 132 | + for wheel in wheels: |
| 133 | + deps = list( |
| 134 | + PypiWheelHandler.parse(os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, wheel)) |
| 135 | + ) |
| 136 | + assert len(deps) == 1 |
| 137 | + deps = deps[0].dependencies |
| 138 | + for dep in deps: |
| 139 | + if dep.scope == "install": |
| 140 | + yield packaging.requirements.Requirement(str(dep.extracted_requirement)) |
| 141 | + |
| 142 | + def get_requirements_for_package_from_pypi_json_api(self, purl): |
| 143 | + |
| 144 | + # if no repos are provided use the incorrect but fast JSON API |
| 145 | + if str(purl) not in self.dependencies_by_purl: |
| 146 | + api_url = f"https://pypi.org/pypi/{purl.name}/{purl.version}/json" |
| 147 | + resp = get_response(api_url) |
| 148 | + if not resp: |
| 149 | + self.dependencies_by_purl[str(purl)] = [] |
| 150 | + info = resp.get("info") or {} |
| 151 | + requires_dist = info.get("requires_dist") or [] |
| 152 | + self.dependencies_by_purl[str(purl)] = requires_dist |
| 153 | + for dependency in self.dependencies_by_purl[str(purl)]: |
| 154 | + yield packaging.requirements.Requirement(dependency) |
133 | 155 |
|
134 | 156 | def get_candidates(self, all_versions, requirements, identifier, bad_versions, name, extras): |
135 | 157 | """ |
@@ -267,29 +289,22 @@ def format_resolution(results, as_tree=False): |
267 | 289 | return dependencies |
268 | 290 |
|
269 | 291 |
|
270 | | -def pypi_simple_repo_in_repos(repos: utils_pypi.PypiSimpleRepository): |
271 | | - """ |
272 | | - Return True if simple pypi index_url is present in any of the repos |
273 | | - """ |
274 | | - return any(repo.index_url == utils_pypi.PYPI_SIMPLE_URL for repo in repos) |
275 | | - |
276 | | - |
277 | 292 | def get_resolved_dependencies( |
278 | 293 | requirements: List[Requirement], |
279 | 294 | environment: utils_pypi.Environment = None, |
280 | | - repos: List[utils_pypi.PypiSimpleRepository] = [], |
| 295 | + repos: Sequence[utils_pypi.PypiSimpleRepository] = tuple(), |
281 | 296 | as_tree: bool = False, |
282 | 297 | ): |
283 | 298 | """ |
284 | 299 | Return resolved dependencies of a ``requirements`` list of Requirement for |
285 | 300 | an ``enviroment`` Environment. The resolved dependencies are formatted as |
286 | | - parent/children or a nested tree if ``as_tree`` is True |
287 | | - """ |
288 | | - if repos and not pypi_simple_repo_in_repos(repos): |
289 | | - repos.append(utils_pypi.PYPI_PUBLIC_REPO) |
| 301 | + parent/children or a nested tree if ``as_tree`` is True. |
290 | 302 |
|
| 303 | + Used the provided ``repos`` list of PypiSimpleRepository. |
| 304 | + If empty, use instead the PyPI.org JSON API exclusively instead |
| 305 | + """ |
291 | 306 | resolver = Resolver( |
292 | | - provider=PythonInputProvider(environment, repos), |
| 307 | + provider=PythonInputProvider(environment=environment, repos=repos), |
293 | 308 | reporter=BaseReporter(), |
294 | 309 | ) |
295 | 310 | results = resolver.resolve(requirements=requirements) |
|
0 commit comments