Server IP : 85.214.239.14 / Your IP : 18.226.52.26 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /usr/share/doc/python3-resolvelib/examples/ |
Upload File : |
import sys from email.message import EmailMessage from email.parser import BytesParser from io import BytesIO from operator import attrgetter from platform import python_version from urllib.parse import urlparse from zipfile import ZipFile import html5lib import requests from packaging.requirements import Requirement from packaging.specifiers import SpecifierSet from packaging.utils import canonicalize_name from packaging.version import InvalidVersion, Version from resolvelib import BaseReporter, Resolver from .extras_provider import ExtrasProvider PYTHON_VERSION = Version(python_version()) class Candidate: def __init__(self, name, version, url=None, extras=None): self.name = canonicalize_name(name) self.version = version self.url = url self.extras = extras self._metadata = None self._dependencies = None def __repr__(self): if not self.extras: return f"<{self.name}=={self.version}>" return f"<{self.name}[{','.join(self.extras)}]=={self.version}>" @property def metadata(self): if self._metadata is None: self._metadata = get_metadata_for_wheel(self.url) return self._metadata @property def requires_python(self): return self.metadata.get("Requires-Python") def _get_dependencies(self): deps = self.metadata.get_all("Requires-Dist", []) extras = self.extras if self.extras else [""] for d in deps: r = Requirement(d) if r.marker is None: yield r else: for e in extras: if r.marker.evaluate({"extra": e}): yield r @property def dependencies(self): if self._dependencies is None: self._dependencies = list(self._get_dependencies()) return self._dependencies def get_project_from_pypi(project, extras): """Return candidates created from the project name and extras.""" url = "https://pypi.org/simple/{}".format(project) data = requests.get(url).content doc = html5lib.parse(data, namespaceHTMLElements=False) for i in doc.findall(".//a"): url = i.attrib["href"] py_req = i.attrib.get("data-requires-python") # Skip items that need a different Python version if py_req: spec = SpecifierSet(py_req) if PYTHON_VERSION not in spec: continue path = urlparse(url).path filename = path.rpartition("/")[-1] # We only handle wheels if not filename.endswith(".whl"): continue # TODO: Handle compatibility tags? # Very primitive wheel filename parsing name, version = filename[:-4].split("-")[:2] try: version = Version(version) except InvalidVersion: # Ignore files with invalid versions continue yield Candidate(name, version, url=url, extras=extras) def get_metadata_for_wheel(url): data = requests.get(url).content with ZipFile(BytesIO(data)) as z: for n in z.namelist(): if n.endswith(".dist-info/METADATA"): p = BytesParser() return p.parse(z.open(n), headersonly=True) # If we didn't find the metadata, return an empty dict return EmailMessage() class PyPIProvider(ExtrasProvider): def identify(self, requirement_or_candidate): return canonicalize_name(requirement_or_candidate.name) def get_extras_for(self, requirement_or_candidate): # Extras is a set, which is not hashable return tuple(sorted(requirement_or_candidate.extras)) def get_base_requirement(self, candidate): return Requirement("{}=={}".format(candidate.name, candidate.version)) def get_preference(self, identifier, resolutions, candidates, information): return sum(1 for _ in candidates[identifier]) def find_matches(self, identifier, requirements, incompatibilities): requirements = list(requirements[identifier]) assert not any( r.extras for r in requirements ), "extras not supported in this example" bad_versions = {c.version for c in incompatibilities[identifier]} # Need to pass the extras to the search, so they # are added to the candidate at creation - we # treat candidates as immutable once created. candidates = ( candidate for candidate in get_project_from_pypi(identifier, set()) if candidate.version not in bad_versions and all(candidate.version in r.specifier for r in requirements) ) return sorted(candidates, key=attrgetter("version"), reverse=True) def is_satisfied_by(self, requirement, candidate): if canonicalize_name(requirement.name) != candidate.name: return False return candidate.version in requirement.specifier def get_dependencies(self, candidate): return candidate.dependencies def display_resolution(result): """Print pinned candidates and dependency graph to stdout.""" print("\n--- Pinned Candidates ---") for name, candidate in result.mapping.items(): print(f"{name}: {candidate.name} {candidate.version}") print("\n--- Dependency Graph ---") for name in result.graph: targets = ", ".join(result.graph.iter_children(name)) print(f"{name} -> {targets}") def main(): """Resolve requirements as project names on PyPI. The requirements are taken as command-line arguments and the resolution result will be printed to stdout. """ if len(sys.argv) == 1: print("Usage:", sys.argv[0], "<PyPI project name(s)>") return # Things I want to resolve. reqs = sys.argv[1:] requirements = [Requirement(r) for r in reqs] # Create the (reusable) resolver. provider = PyPIProvider() reporter = BaseReporter() resolver = Resolver(provider, reporter) # Kick off the resolution process, and get the final result. print("Resolving", ", ".join(reqs)) result = resolver.resolve(requirements) display_resolution(result) if __name__ == "__main__": try: main() except KeyboardInterrupt: print()