def __init__(self, name, version, pretty_version=None): """ Creates a new in memory package. """ self._pretty_name = name self._name = canonicalize_name(name) if not isinstance(version, Version): self._version = Version.parse(version) self._pretty_version = pretty_version or version else: self._version = version self._pretty_version = pretty_version or self._version.text self.description = "" self._authors = [] self._maintainers = [] self.homepage = None self.repository_url = None self.documentation_url = None self.keywords = [] self._license = None self.readme = None self.source_name = "" self.source_type = "" self.source_reference = "" self.source_url = "" self.requires = [] self.dev_requires = [] self.extras = {} self.requires_extras = [] self.category = "main" self.hashes = [] self.optional = False self.classifiers = [] self._python_versions = "*" self._python_constraint = parse_constraint("*") self._python_marker = AnyMarker() self.platform = None self.marker = AnyMarker() self.root_dir = None self.develop = True
def __init__( self, name, # type: str constraint, # type: str optional=False, # type: bool category="main", # type: str allows_prereleases=False, # type: bool source_name=None, # type: Optional[str] global_options=[], # type: list[str] ): self._name = canonicalize_name(name) self._pretty_name = name try: if not isinstance(constraint, VersionConstraint): self._constraint = parse_constraint(constraint) else: self._constraint = constraint except ValueError: self._constraint = parse_constraint("*") self._pretty_constraint = str(constraint) self._optional = optional self._category = category if isinstance(self._constraint, VersionRange) and self._constraint.min: allows_prereleases = (allows_prereleases or self._constraint.min.is_prerelease()) self._allows_prereleases = allows_prereleases self._source_name = source_name self._python_versions = "*" self._python_constraint = parse_constraint("*") self._transitive_python_versions = None self._transitive_python_constraint = None self._transitive_marker = None self._extras = [] self._in_extras = [] self.global_opts = list(global_options) self._activated = not self._optional self.is_root = False self.marker = AnyMarker()
def _solve(self, use_latest=None): self._branches.append(self._package.python_versions) locked = {} for package in self._locked.packages: locked[package.name] = DependencyPackage(package.to_dependency(), package) try: result = resolve_version(self._package, self._provider, locked=locked, use_latest=use_latest) packages = result.packages except CompatibilityError as e: return self.solve_in_compatibility_mode(e.constraints, use_latest=use_latest) except SolveFailure as e: raise SolverProblemError(e) graph = self._build_graph(self._package, packages) depths = [] final_packages = [] for package in packages: category, optional, marker, depth = self._get_tags_for_package( package, graph) if marker is None: marker = AnyMarker() if marker.is_empty(): continue package.category = category package.optional = optional package.marker = marker depths.append(depth) final_packages.append(package) return final_packages, depths
class Package(object): AVAILABLE_PYTHONS = {"2", "2.7", "3", "3.4", "3.5", "3.6", "3.7"} def __init__(self, name, version, pretty_version=None): """ Creates a new in memory package. """ self._pretty_name = name self._name = canonicalize_name(name) if not isinstance(version, Version): self._version = Version.parse(version) self._pretty_version = pretty_version or version else: self._version = version self._pretty_version = pretty_version or self._version.text self.description = "" self._authors = [] self._maintainers = [] self.homepage = None self.repository_url = None self.documentation_url = None self.keywords = [] self._license = None self.readme = None self.source_name = "" self.source_type = "" self.source_reference = "" self.source_url = "" self.requires = [] self.dev_requires = [] self.extras = {} self.requires_extras = [] self.category = "main" self.hashes = [] self.optional = False self.classifiers = [] self._python_versions = "*" self._python_constraint = parse_constraint("*") self._python_marker = AnyMarker() self.platform = None self.marker = AnyMarker() self.root_dir = None self.develop = True @property def name(self): return self._name @property def pretty_name(self): return self._pretty_name @property def version(self): return self._version @property def pretty_version(self): return self._pretty_version @property def unique_name(self): if self.is_root(): return self._name return self.name + "-" + self._version.text @property def pretty_string(self): return self.pretty_name + " " + self.pretty_version @property def full_pretty_version(self): if self.source_type in ["file", "directory"]: return "{} {}".format(self._pretty_version, self.source_url) if self.source_type not in ["hg", "git"]: return self._pretty_version # if source reference is a sha1 hash -- truncate if len(self.source_reference) == 40: return "{} {}".format(self._pretty_version, self.source_reference[0:7]) return "{} {}".format(self._pretty_version, self.source_reference) @property def authors(self): # type: () -> list return self._authors @property def author_name(self): # type: () -> str return self._get_author()["name"] @property def author_email(self): # type: () -> str return self._get_author()["email"] @property def maintainers(self): # type: () -> list return self._maintainers @property def maintainer_name(self): # type: () -> str return self._get_maintainer()["name"] @property def maintainer_email(self): # type: () -> str return self._get_maintainer()["email"] @property def all_requires(self): return self.requires + self.dev_requires def _get_author(self): # type: () -> dict if not self._authors: return {"name": None, "email": None} m = AUTHOR_REGEX.match(self._authors[0]) name = m.group("name") email = m.group("email") return {"name": name, "email": email} def _get_maintainer(self): # type: () -> dict if not self._maintainers: return {"name": None, "email": None} m = AUTHOR_REGEX.match(self._maintainers[0]) name = m.group("name") email = m.group("email") return {"name": name, "email": email} @property def python_versions(self): return self._python_versions @python_versions.setter def python_versions(self, value): self._python_versions = value self._python_constraint = parse_constraint(value) self._python_marker = parse_marker( create_nested_marker("python_version", self._python_constraint) ) @property def python_constraint(self): return self._python_constraint @property def python_marker(self): return self._python_marker @property def license(self): return self._license @license.setter def license(self, value): if value is None: self._license = value elif isinstance(value, License): self._license = value else: self._license = license_by_id(value) @property def all_classifiers(self): classifiers = copy.copy(self.classifiers) # Automatically set python classifiers if self.python_versions == "*": python_constraint = parse_constraint("~2.7 || ^3.4") else: python_constraint = self.python_constraint for version in sorted(self.AVAILABLE_PYTHONS): if len(version) == 1: constraint = parse_constraint(version + ".*") else: constraint = Version.parse(version) if python_constraint.allows_any(constraint): classifiers.append( "Programming Language :: Python :: {}".format(version) ) # Automatically set license classifiers if self.license: classifiers.append(self.license.classifier) classifiers = set(classifiers) return sorted(classifiers) @property def urls(self): urls = {} if self.homepage: urls["Homepage"] = self.homepage if self.repository_url: urls["Repository"] = self.repository_url if self.documentation_url: urls["Documentation"] = self.documentation_url return urls def is_prerelease(self): return self._version.is_prerelease() def is_root(self): return False def add_dependency( self, name, # type: str constraint=None, # type: Union[str, dict, None] category="main", # type: str ): # type: (...) -> Dependency if constraint is None: constraint = "*" if isinstance(constraint, dict): optional = constraint.get("optional", False) python_versions = constraint.get("python") platform = constraint.get("platform") markers = constraint.get("markers") allows_prereleases = constraint.get("allows-prereleases", False) if "git" in constraint: # VCS dependency dependency = VCSDependency( name, "git", constraint["git"], branch=constraint.get("branch", None), tag=constraint.get("tag", None), rev=constraint.get("rev", None), optional=optional, ) elif "file" in constraint: file_path = Path(constraint["file"]) dependency = FileDependency( name, file_path, category=category, base=self.root_dir ) elif "path" in constraint: path = Path(constraint["path"]) if self.root_dir: is_file = (self.root_dir / path).is_file() else: is_file = path.is_file() if is_file: dependency = FileDependency( name, path, category=category, optional=optional, base=self.root_dir, ) else: dependency = DirectoryDependency( name, path, category=category, optional=optional, base=self.root_dir, develop=constraint.get("develop", True), ) else: version = constraint["version"] dependency = Dependency( name, version, optional=optional, category=category, allows_prereleases=allows_prereleases, source_name=constraint.get("source"), ) if not markers: marker = AnyMarker() if python_versions: dependency.python_versions = python_versions marker = marker.intersect( parse_marker( create_nested_marker( "python_version", dependency.python_constraint ) ) ) if platform: marker = marker.intersect( parse_marker( create_nested_marker( "sys_platform", parse_generic_constraint(platform) ) ) ) else: marker = parse_marker(markers) if not marker.is_any(): dependency.marker = marker if "extras" in constraint: for extra in constraint["extras"]: dependency.extras.append(extra) else: dependency = Dependency(name, constraint, category=category) if category == "dev": self.dev_requires.append(dependency) else: self.requires.append(dependency) return dependency def to_dependency(self): from . import dependency_from_pep_508 name = "{} (=={})".format(self._name, self._version) if not self.marker.is_any(): name += " ; {}".format(str(self.marker)) return dependency_from_pep_508(name) @contextmanager def with_python_versions(self, python_versions): original_python_versions = self.python_versions self.python_versions = python_versions yield self.python_versions = original_python_versions def clone(self): # type: () -> Package clone = self.__class__(self.pretty_name, self.version) clone.category = self.category clone.optional = self.optional clone.python_versions = self.python_versions clone.marker = self.marker clone.extras = self.extras clone.source_type = self.source_type clone.source_url = self.source_url clone.source_reference = self.source_reference for dep in self.requires: clone.requires.append(dep) for dep in self.dev_requires: clone.dev_requires.append(dep) return clone def __hash__(self): return hash((self._name, self._version)) def __eq__(self, other): if not isinstance(other, Package): return NotImplemented return self._name == other.name and self._version == other.version def __str__(self): return self.unique_name def __repr__(self): return "<Package {}>".format(self.unique_name)
def add_dependency( self, name, # type: str constraint=None, # type: Union[str, dict, None] category="main", # type: str ): # type: (...) -> Dependency if constraint is None: constraint = "*" if isinstance(constraint, dict): optional = constraint.get("optional", False) python_versions = constraint.get("python") platform = constraint.get("platform") markers = constraint.get("markers") allows_prereleases = constraint.get("allows-prereleases", False) if "git" in constraint: # VCS dependency dependency = VCSDependency( name, "git", constraint["git"], branch=constraint.get("branch", None), tag=constraint.get("tag", None), rev=constraint.get("rev", None), optional=optional, ) elif "file" in constraint: file_path = Path(constraint["file"]) dependency = FileDependency( name, file_path, category=category, base=self.root_dir ) elif "path" in constraint: path = Path(constraint["path"]) if self.root_dir: is_file = (self.root_dir / path).is_file() else: is_file = path.is_file() if is_file: dependency = FileDependency( name, path, category=category, optional=optional, base=self.root_dir, ) else: dependency = DirectoryDependency( name, path, category=category, optional=optional, base=self.root_dir, develop=constraint.get("develop", True), ) else: version = constraint["version"] dependency = Dependency( name, version, optional=optional, category=category, allows_prereleases=allows_prereleases, source_name=constraint.get("source"), ) if not markers: marker = AnyMarker() if python_versions: dependency.python_versions = python_versions marker = marker.intersect( parse_marker( create_nested_marker( "python_version", dependency.python_constraint ) ) ) if platform: marker = marker.intersect( parse_marker( create_nested_marker( "sys_platform", parse_generic_constraint(platform) ) ) ) else: marker = parse_marker(markers) if not marker.is_any(): dependency.marker = marker if "extras" in constraint: for extra in constraint["extras"]: dependency.extras.append(extra) else: dependency = Dependency(name, constraint, category=category) if category == "dev": self.dev_requires.append(dependency) else: self.requires.append(dependency) return dependency
class Dependency(object): def __init__( self, name, # type: str constraint, # type: str optional=False, # type: bool category="main", # type: str allows_prereleases=False, # type: bool ): self._name = canonicalize_name(name) self._pretty_name = name try: if not isinstance(constraint, VersionConstraint): self._constraint = parse_constraint(constraint) else: self._constraint = constraint except ValueError: self._constraint = parse_constraint("*") self._pretty_constraint = str(constraint) self._optional = optional self._category = category if isinstance(self._constraint, VersionRange) and self._constraint.min: allows_prereleases = (allows_prereleases or self._constraint.min.is_prerelease()) self._allows_prereleases = allows_prereleases self._python_versions = "*" self._python_constraint = parse_constraint("*") self._transitive_python_versions = None self._transitive_python_constraint = None self._extras = [] self._in_extras = [] self._activated = not self._optional self.is_root = False self.marker = AnyMarker() @property def name(self): return self._name @property def constraint(self): return self._constraint @property def pretty_constraint(self): return self._pretty_constraint @property def pretty_name(self): return self._pretty_name @property def category(self): return self._category @property def python_versions(self): return self._python_versions @python_versions.setter def python_versions(self, value): self._python_versions = value self._python_constraint = parse_constraint(value) if not self._python_constraint.is_any(): self.marker = self.marker.intersect( parse_marker( self._create_nested_marker("python_version", self._python_constraint))) @property def transitive_python_versions(self): if self._transitive_python_versions is None: return self._python_versions return self._transitive_python_versions @transitive_python_versions.setter def transitive_python_versions(self, value): self._transitive_python_versions = value self._transitive_python_constraint = parse_constraint(value) @property def python_constraint(self): return self._python_constraint @property def transitive_python_constraint(self): if self._transitive_python_constraint is None: return self._python_constraint return self._transitive_python_constraint @property def extras(self): # type: () -> list return self._extras @property def in_extras(self): # type: () -> list return self._in_extras @property def base_pep_508_name(self): # type: () -> str requirement = self.pretty_name if self.extras: requirement += "[{}]".format(",".join(self.extras)) if isinstance(self.constraint, VersionUnion): requirement += " ({})".format(",".join( [str(c).replace(" ", "") for c in self.constraint.ranges])) elif isinstance(self.constraint, Version): requirement += " (=={})".format(self.constraint.text) elif not self.constraint.is_any(): requirement += " ({})".format( str(self.constraint).replace(" ", "")) return requirement def allows_prereleases(self): return self._allows_prereleases def is_optional(self): return self._optional def is_activated(self): return self._activated def is_vcs(self): return False def is_file(self): return False def is_directory(self): return False def accepts(self, package): # type: (poetry.packages.Package) -> bool """ Determines if the given package matches this dependency. """ return (self._name == package.name and self._constraint.allows(package.version) and (not package.is_prerelease() or self.allows_prereleases())) def to_pep_508(self, with_extras=True): # type: (bool) -> str requirement = self.base_pep_508_name markers = [] if not self.marker.is_any(): marker = self.marker if not with_extras: marker = marker.without_extras() if not marker.is_empty(): markers.append(str(marker)) else: # Python marker if self.python_versions != "*": python_constraint = self.python_constraint markers.append( self._create_nested_marker("python_version", python_constraint)) in_extras = " || ".join(self._in_extras) if in_extras and with_extras: markers.append( self._create_nested_marker( "extra", parse_generic_constraint(in_extras))) if markers: if self.is_vcs(): requirement += " " if len(markers) > 1: markers = ["({})".format(m) for m in markers] requirement += "; {}".format(" and ".join(markers)) else: requirement += "; {}".format(markers[0]) return requirement def _create_nested_marker(self, name, constraint): if isinstance(constraint, (MultiConstraint, UnionConstraint)): parts = [] for c in constraint.constraints: multi = False if isinstance(c, (MultiConstraint, UnionConstraint)): multi = True parts.append((multi, self._create_nested_marker(name, c))) glue = " and " if isinstance(constraint, UnionConstraint): parts = [ "({})".format(part[1]) if part[0] else part[1] for part in parts ] glue = " or " else: parts = [part[1] for part in parts] marker = glue.join(parts) elif isinstance(constraint, Constraint): marker = '{} {} "{}"'.format(name, constraint.operator, constraint.version) elif isinstance(constraint, VersionUnion): parts = [] for c in constraint.ranges: parts.append(self._create_nested_marker(name, c)) glue = " or " parts = ["({})".format(part) for part in parts] marker = glue.join(parts) elif isinstance(constraint, Version): marker = '{} == "{}"'.format(name, constraint.text) else: if constraint.min is not None: op = ">=" if not constraint.include_min: op = ">" version = constraint.min.text if constraint.max is not None: text = '{} {} "{}"'.format(name, op, version) op = "<=" if not constraint.include_max: op = "<" version = constraint.max text += ' and {} {} "{}"'.format(name, op, version) return text elif constraint.max is not None: op = "<=" if not constraint.include_max: op = "<" version = constraint.max else: return "" marker = '{} {} "{}"'.format(name, op, version) return marker def activate(self): """ Set the dependency as mandatory. """ self._activated = True def deactivate(self): """ Set the dependency as optional. """ if not self._optional: self._optional = True self._activated = False def with_constraint(self, constraint): new = Dependency( self.pretty_name, constraint, optional=self.is_optional(), category=self.category, allows_prereleases=self.allows_prereleases(), ) new.is_root = self.is_root new.python_versions = self.python_versions for extra in self.extras: new.extras.append(extra) for in_extra in self.in_extras: new.in_extras.append(in_extra) return new def __eq__(self, other): if not isinstance(other, Dependency): return NotImplemented return self._name == other.name and self._constraint == other.constraint def __ne__(self, other): return not self == other def __hash__(self): return hash((self._name, self._pretty_constraint)) def __str__(self): if self.is_root: return self._pretty_name return "{} ({})".format(self._pretty_name, self._pretty_constraint) def __repr__(self): return "<{} {}>".format(self.__class__.__name__, str(self))