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)
def format_python_constraint(constraint): """ This helper will help in transforming disjunctive constraint into proper constraint. """ if isinstance(constraint, Version) and constraint.precision < 3: # Transform 3.6 or 3 if constraint.precision == 2: # 3.6 constraint = parse_constraint("~{}.{}".format( constraint.major, constraint.minor)) else: constraint = parse_constraint("^{}.0".format(constraint.major)) if not isinstance(constraint, VersionUnion): return str(constraint) formatted = [] accepted = [] for version in PYTHON_VERSION: version_constraint = parse_constraint(version) matches = constraint.allows_any(version_constraint) if not matches: formatted.append("!=" + version) else: accepted.append(version) # Checking lower bound low = accepted[0] formatted.insert(0, ">=" + ".".join(low.split(".")[:2])) return ", ".join(formatted)
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)
def format_python_constraint(constraint): """ This helper will help in transforming disjunctive constraint into proper constraint. """ if isinstance(constraint, Version) and constraint.precision < 3: # Transform 3.6 or 3 if constraint.precision == 2: # 3.6 constraint = parse_constraint( "~{}.{}".format(constraint.major, constraint.minor) ) else: constraint = parse_constraint("^{}.0".format(constraint.major)) if not isinstance(constraint, VersionUnion): return str(constraint) formatted = [] accepted = [] for version in PYTHON_VERSION: version_constraint = parse_constraint(version) matches = constraint.allows_any(version_constraint) if not matches: formatted.append("!=" + version) else: accepted.append(version) # Checking lower bound low = accepted[0] formatted.insert(0, ">=" + ".".join(low.split(".")[:2])) return ", ".join(formatted)
def find_best_candidate( self, package_name, # type: str target_package_version=None, # type: Union[str, None] allow_prereleases=False, # type: bool ): # type: (...) -> Union[Package, bool] """ Given a package name and optional version, returns the latest Package that matches """ if target_package_version: constraint = parse_constraint(target_package_version) else: constraint = parse_constraint("*") candidates = self._pool.find_packages( package_name, constraint, allow_prereleases=allow_prereleases ) if not candidates: return False dependency = Dependency(package_name, constraint) # Select highest version if we have many package = candidates[0] for candidate in candidates: if candidate.is_prerelease() and not dependency.allows_prereleases(): continue # Select highest version of the two if package.version < candidate.version: package = candidate return package
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 self._allows_prereleases = allows_prereleases self._python_versions = "*" self._python_constraint = parse_constraint("*") self._platform = "*" self._platform_constraint = EmptyConstraint() self._extras = [] self._in_extras = [] self._activated = not self._optional self.is_root = False
def find_best_candidate( self, package_name, # type: str target_package_version=None, # type: Union[str, None] allow_prereleases=False, # type: bool ): # type: (...) -> Union[Package, bool] """ Given a package name and optional version, returns the latest Package that matches """ if target_package_version: constraint = parse_constraint(target_package_version) else: constraint = parse_constraint("*") candidates = self._pool.find_packages( package_name, constraint, allow_prereleases=allow_prereleases) if not candidates: return False dependency = Dependency(package_name, constraint) # Select highest version if we have many package = candidates[0] for candidate in candidates: if candidate.is_prerelease( ) and not dependency.allows_prereleases(): continue # Select highest version of the two if package.version < candidate.version: package = candidate return package
def handle(self): from poetry.packages import Dependency from poetry.packages import ProjectPackage from poetry.puzzle import Solver from poetry.repositories.repository import Repository from poetry.semver import parse_constraint packages = self.argument("package") if not packages: package = self.poetry.package else: requirements = self._determine_requirements(packages) requirements = self._format_requirements(requirements) # validate requirements format for constraint in requirements.values(): parse_constraint(constraint) dependencies = [] for name, constraint in requirements.items(): dep = Dependency(name, constraint) extras = [] for extra in self.option("extras"): if " " in extra: extras += [e.strip() for e in extra.split(" ")] else: extras.append(extra) for ex in extras: dep.extras.append(ex) dependencies.append(dep) package = ProjectPackage(self.poetry.package.name, self.poetry.package.version) package.python_versions = (self.option("python") or self.poetry.package.python_versions) for dep in dependencies: package.requires.append(dep) solver = Solver(package, self.poetry.pool, Repository(), Repository(), self.output) ops = solver.solve() self.line("") self.line("Resolution results:") self.line("") for op in ops: package = op.package self.line(" - <info>{}</info> (<comment>{}</comment>)".format( package.name, package.version)) if package.requirements: for req_name, req_value in package.requirements.items(): self.line(" - {}: {}".format(req_name, req_value))
def handle(self): from poetry.layouts import layout from poetry.semver import parse_constraint from poetry.utils._compat import Path from poetry.utils.env import SystemEnv from poetry.vcs.git import GitConfig if self.option("src"): layout_ = layout("src") else: layout_ = layout("standard") path = Path.cwd() / Path(self.argument("path")) name = self.option("name") if not name: name = path.name if path.exists(): if list(path.glob("*")): # Directory is not empty. Aborting. raise RuntimeError("Destination <fg=yellow>{}</> " "exists and is not empty".format(path)) readme_format = "rst" config = GitConfig() author = None if config.get("user.name"): author = config["user.name"] author_email = config.get("user.email") if author_email: author += " <{}>".format(author_email) current_env = SystemEnv(Path(sys.executable)) default_python = "^{}".format(".".join( str(v) for v in current_env.version_info[:2])) dev_dependencies = {} python_constraint = parse_constraint(default_python) if parse_constraint("<3.5").allows_any(python_constraint): dev_dependencies["pytest"] = "^4.6" if parse_constraint(">=3.5").allows_all(python_constraint): dev_dependencies["pytest"] = "^5.2" layout_ = layout_( name, "0.1.0", author=author, readme_format=readme_format, python=default_python, dev_dependencies=dev_dependencies, ) layout_.create(path) self.line("Created package <info>{}</> in <fg=blue>{}</>".format( module_name(name), path.relative_to(Path.cwd())))
def test_format_python_constraint_single_version(): constraint = parse_constraint("3.6") result = format_python_constraint(constraint) assert result == ">=3.6,<3.7" constraint = parse_constraint("3") result = format_python_constraint(constraint) assert result == ">=3.0,<4.0"
def _determine_requirements(self, requires): # type: (List[str]) -> List[str] from poetry.semver import parse_constraint if not requires: return [] requires = self._parse_name_version_pairs(requires) for requirement in requires: if "version" in requirement: parse_constraint(requirement["version"]) return requires
def format_python_constraint(constraint): """ This helper will help in transforming disjunctive constraint into proper constraint. """ if not isinstance(constraint, VersionUnion): return str(constraint) formatted = [] accepted = [] for version in PYTHON_VERSION: version_constraint = parse_constraint(version) matches = constraint.allows_any(version_constraint) if not matches: formatted.append('!=' + version) else: accepted.append(version) # Checking lower bound low = accepted[0] formatted.insert(0, '>=' + '.'.join(low.split('.')[:2])) return ', '.join(formatted)
def set_env(self, event, event_name, _): # type: (PreHandleEvent, str, _) -> None from poetry.semver import parse_constraint from poetry.utils.env import EnvManager command = event.command.config.handler # type: EnvCommand if not isinstance(command, EnvCommand): return io = event.io poetry = command.poetry env_manager = EnvManager(poetry.config) # Checking compatibility of the current environment with # the python dependency specified in pyproject.toml current_env = env_manager.get(poetry.file.parent) supported_python = poetry.package.python_constraint current_python = parse_constraint( ".".join(str(v) for v in current_env.version_info[:3]) ) if not supported_python.allows(current_python): raise RuntimeError( "The current Python version ({}) is not supported by the project ({})\n" "Please activate a compatible Python version.".format( current_python, poetry.package.python_versions ) ) env = env_manager.create_venv(poetry.file.parent, io, poetry.package.name) if env.is_venv() and io.is_verbose(): io.write_line("Using virtualenv: <comment>{}</>".format(env.path)) command.set_env(env)
def get_release_info(self, name, version): # type: (str, str) -> dict """ Return the release information given a package name and a version. The information is returned from the cache if it exists or retrieved from the remote server. """ if self._disable_cache: return self._get_release_info(name, version) cached = self._cache.remember_forever( "{}:{}".format(name, version), lambda: self._get_release_info(name, version)) cache_version = cached.get("_cache_version", "0.0.0") if parse_constraint(cache_version) != self.CACHE_VERSION: # The cache must be updated self._log( "The cache for {} {} is outdated. Refreshing.".format( name, version), level="debug", ) cached = self._get_release_info(name, version) self._cache.forever("{}:{}".format(name, version), cached) return cached
def get_aiida_version_poetry(pyproject): """Get AiiDA version that this plugin is compatible with from a pyproject.toml. """ from poetry.semver import parse_constraint # pylint: disable=import-outside-toplevel try: deps = pyproject['tool']['poetry']['dependencies'] except KeyError: return None for name, data in deps.items(): if name not in ['aiida-core', 'aiida_core', 'aiida']: continue try: # data is either a dict {"version": ..., "extras": ["..", ], } version = data['version'] except TypeError: # or directly the version string version = data break else: print(' >> WARNING! AiiDA version not specified') return None try: return str(parse_constraint(version)) except ValueError: print( ' >> WARNING: Invalid version encountered in Poetry pyproject.toml for aiida-core' ) return None
def get_aiida_version_poetry(pyproject): """Get AiiDA version that this plugin is compatible with from a pyproject.toml. """ try: deps = pyproject["tool"]["poetry"]["dependencies"] except KeyError: return None for name, data in deps.items(): if name not in ["aiida-core", "aiida_core", "aiida"]: continue try: # data is either a dict {"version": ..., "extras": ["..", ], } version = data["version"] except TypeError: # or directly the version string version = data break else: print(" >> WARNING! AiiDA version not specified") return None from poetry.semver import parse_constraint try: return str(parse_constraint(version)) except ValueError: print( " >> WARNING: Invalid version encountered in Poetry pyproject.toml for aiida-core" ) return None
def solve_in_compatibility_mode(self, constraints, use_latest=None): locked = {} for package in self._locked.packages: locked[package.name] = DependencyPackage(package.to_dependency(), package) packages = [] depths = [] for constraint in constraints: constraint = parse_constraint(constraint) intersection = constraint.intersect( self._package.python_constraint) self._provider.debug( "<comment>Retrying dependency resolution " "for Python ({}).</comment>".format(intersection)) with self._package.with_python_versions(str(intersection)): _packages, _depths = self._solve(use_latest=use_latest) for index, package in enumerate(_packages): if package not in packages: packages.append(package) depths.append(_depths[index]) continue else: idx = packages.index(package) pkg = packages[idx] depths[idx] = max(depths[idx], _depths[index]) pkg.marker = pkg.marker.union(package.marker) for dep in package.requires: if dep not in pkg.requires: pkg.requires.append(dep) return packages, depths
def solve_in_compatibility_mode(self, constraints, use_latest=None): locked = {} for package in self._locked.packages: locked[package.name] = package packages = [] depths = [] for constraint in constraints: constraint = parse_constraint(constraint) intersection = constraint.intersect(self._package.python_constraint) self._provider.debug( "<comment>Retrying dependency resolution " "for Python ({}).</comment>".format(intersection) ) with self._package.with_python_versions(str(intersection)): _packages, _depths = self._solve(use_latest=use_latest) for index, package in enumerate(_packages): if package not in packages: packages.append(package) depths.append(_depths[index]) continue current_package = packages[packages.index(package)] for dep in package.requires: if dep not in current_package.requires: current_package.requires.append(dep) return packages, depths
def _get_lock_data(self): # type: () -> dict if not self._lock.exists(): raise RuntimeError("No lockfile found. Unable to read locked packages") try: lock_data = self._lock.read() except TOMLKitError as e: raise RuntimeError("Unable to read the lock file ({}).".format(e)) lock_version = Version.parse(lock_data["metadata"].get("lock-version", "1.0")) current_version = Version.parse(self._VERSION) accepted_versions = parse_constraint( "^{}".format(Version(current_version.major, current_version.minor)) ) lock_version_allowed = accepted_versions.allows(lock_version) if lock_version_allowed and current_version < lock_version: logger.warning( "The lock file might not be compatible with the current version of Poetry.\n" "Upgrade Poetry to ensure the lock file is read properly or, alternatively, " "regenerate the lock file with the `poetry lock` command." ) elif not lock_version_allowed: raise RuntimeError( "The lock file is not compatible with the current version of Poetry.\n" "Upgrade Poetry to be able to read the lock file or, alternatively, " "regenerate the lock file with the `poetry lock` command." ) return lock_data
def _filter_operations( self, ops, repo): # type: (List[Operation], Repository) -> None extra_packages = [p.name for p in self._get_extra_packages(repo)] for op in ops: if isinstance(op, Update): package = op.target_package else: package = op.package if op.job_type == "uninstall": continue if package.name in self._develop and package.source_type == "directory": package.develop = True if op.skipped: op.unskip() python = Version.parse(".".join( [str(i) for i in self._venv.version_info[:3]])) if "python" in package.requirements: python_constraint = parse_constraint( package.requirements["python"]) if not python_constraint.allows(python): # Incompatible python versions op.skip("Not needed for the current python version") continue if not package.python_constraint.allows(python): op.skip("Not needed for the current python version") continue if "platform" in package.requirements: platform_constraint = GenericConstraint.parse( package.requirements["platform"]) if not platform_constraint.matches( GenericConstraint("=", sys.platform)): # Incompatible systems op.skip("Not needed for the current platform") continue if self._update: extras = {} for extra, deps in self._package.extras.items(): extras[extra] = [dep.name for dep in deps] else: extras = {} for extra, deps in self._locker.lock_data.get("extras", {}).items(): extras[extra] = [dep.lower() for dep in deps] # If a package is optional and not requested # in any extra we skip it if package.optional: if package.name not in extra_packages: op.skip("Not required") # If the package is a dev package and dev packages # are not requests, we skip it if package.category == "dev" and not self.is_dev_mode(): op.skip("Dev dependencies not requested")
def __init__(self, requirement_string): try: req = REQUIREMENT.parseString(requirement_string) except ParseException as e: raise InvalidRequirement( 'Invalid requirement, parse error at "{0!r}"'.format( requirement_string[e.loc : e.loc + 8] ) ) self.name = req.name if req.url: parsed_url = urlparse.urlparse(req.url) if not (parsed_url.scheme and parsed_url.netloc) or ( not parsed_url.scheme and not parsed_url.netloc ): raise InvalidRequirement("Invalid URL given") self.url = req.url else: self.url = None self.extras = set(req.extras.asList() if req.extras else []) constraint = req.specifier if not constraint: constraint = "*" self.constraint = parse_constraint(constraint) self.pretty_constraint = constraint self.marker = req.marker if req.marker else None
def __init__(self, requirement_string): try: req = REQUIREMENT.parseString(requirement_string) except ParseException as e: raise InvalidRequirement( 'Invalid requirement, parse error at "{0!r}"'.format( requirement_string[e.loc : e.loc + 8] ) ) self.name = req.name if req.url: parsed_url = urlparse.urlparse(req.url) if not (parsed_url.scheme and parsed_url.netloc) or ( not parsed_url.scheme and not parsed_url.netloc ): raise InvalidRequirement("Invalid URL given") self.url = req.url else: self.url = None self.extras = set(req.extras.asList() if req.extras else []) constraint = req.specifier if not constraint: constraint = "*" self.constraint = parse_constraint(constraint) self.pretty_constraint = constraint self.marker = req.marker if req.marker else None
def get_python_constraint_from_marker( marker, ): # type: (BaseMarker) -> VersionConstraint python_marker = marker.only("python_version") if python_marker.is_any(): return VersionRange() if python_marker.is_empty(): return EmptyConstraint() markers = convert_markers(marker) ors = [] for or_ in markers["python_version"]: ands = [] for op, version in or_: # Expand python version if op == "==": version = "~" + version op = "" elif op == "!=": version += ".*" elif op in ("<=", ">"): parsed_version = Version.parse(version) if parsed_version.precision == 1: if op == "<=": op = "<" version = parsed_version.next_major.text elif op == ">": op = ">=" version = parsed_version.next_major.text elif parsed_version.precision == 2: if op == "<=": op = "<" version = parsed_version.next_minor.text elif op == ">": op = ">=" version = parsed_version.next_minor.text elif op in ("in", "not in"): versions = [] for v in re.split("[ ,]+", version): split = v.split(".") if len(split) in [1, 2]: split.append("*") op_ = "" if op == "in" else "!=" else: op_ = "==" if op == "in" else "!=" versions.append(op_ + ".".join(split)) glue = " || " if op == "in" else ", " if versions: ands.append(glue.join(versions)) continue ands.append("{}{}".format(op, version)) ors.append(" ".join(ands)) return parse_constraint(" || ".join(ors))
def initialize(self, i, o): from poetry.semver import parse_constraint from poetry.utils.env import Env super(EnvCommand, self).initialize(i, o) # Checking compatibility of the current environment with # the python dependency specified in pyproject.toml current_env = Env.get() supported_python = self.poetry.package.python_constraint current_python = parse_constraint(".".join( str(v) for v in current_env.version_info[:3])) if not supported_python.allows(current_python): raise RuntimeError( "The current Python version ({}) is not supported by the project ({})\n" "Please activate a compatible Python version.".format( current_python, self.poetry.package.python_versions)) self._env = Env.create_venv(o, self.poetry.package.name, cwd=self.poetry.file.parent) if self._env.is_venv() and o.is_verbose(): o.writeln("Using virtualenv: <comment>{}</>".format( self._env.path))
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 find_packages(self, name, constraint=None, extras=None, allow_prereleases=False): packages = [] if constraint is None: constraint = "*" if not isinstance(constraint, VersionConstraint): constraint = parse_constraint(constraint) if isinstance(constraint, VersionRange): if (constraint.max is not None and constraint.max.is_prerelease() or constraint.min is not None and constraint.min.is_prerelease()): allow_prereleases = True key = name if not constraint.is_any(): key = "{}:{}".format(key, str(constraint)) if self._cache.store("matches").has(key): versions = self._cache.store("matches").get(key) else: page = self._get("/{}/".format( canonicalize_name(name).replace(".", "-"))) if page is None: return [] versions = [] for version in page.versions: if version.is_prerelease() and not allow_prereleases: continue if constraint.allows(version): versions.append(version) self._cache.store("matches").put(key, versions, 5) for version in versions: package = Package(name, version) package.source_type = "legacy" package.source_reference = self.name package.source_url = self._url if extras is not None: package.requires_extras = extras packages.append(package) self._log( "{} packages found for {} {}".format(len(packages), name, str(constraint)), level="debug", ) return packages
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)))
def python_versions(self, value): self._python_versions = value if value == "*" or value == VersionRange(): value = "~2.7 || >=3.4" self._python_constraint = parse_constraint(value) self._python_marker = parse_marker( create_nested_marker("python_version", self._python_constraint))
def __init__(self, name, version, pretty_version=None): super(ProjectPackage, self).__init__(name, version, pretty_version) self.build = None self.packages = [] self.include = [] self.exclude = [] if self._python_versions == "*": self._python_constraint = parse_constraint("~2.7 || >=3.4")
def find_packages( self, name, # type: str constraint=None, # type: Union[VersionConstraint, str, None] extras=None, # type: Union[list, None] allow_prereleases=False, # type: bool ): # type: (...) -> List[Package] """ Find packages on the remote server. """ if constraint is None: constraint = "*" if not isinstance(constraint, VersionConstraint): constraint = parse_constraint(constraint) if isinstance(constraint, VersionRange): if (constraint.max is not None and constraint.max.is_prerelease() or constraint.min is not None and constraint.min.is_prerelease()): allow_prereleases = True info = self.get_package_info(name) packages = [] for version, release in info["releases"].items(): if not release: # Bad release self._log( "No release information found for {}-{}, skipping".format( name, version), level="debug", ) continue package = Package(name, version) if package.is_prerelease() and not allow_prereleases: continue if not constraint or (constraint and constraint.allows(package.version)): if extras is not None: package.requires_extras = extras packages.append(package) self._log( "{} packages found for {} {}".format(len(packages), name, str(constraint)), level="debug", ) return packages
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.homepage = None self.repository_url = None self.keywords = [] self._license = None self.readme = None 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 # Requirements for making it mandatory self.requirements = {} self.build = None self.include = [] self.exclude = [] self.classifiers = [] self._python_versions = "*" self._python_constraint = parse_constraint("*") self._platform = "*" self._platform_constraint = EmptyConstraint() self.root_dir = None self.develop = False
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._platform = "*" self._platform_constraint = EmptyConstraint() self._extras = [] self._in_extras = [] self._activated = not self._optional self.is_root = False
def find_packages( self, name, # type: str constraint=None, # type: Union[VersionConstraint, str, None] extras=None, # type: Union[list, None] allow_prereleases=False, # type: bool ): # type: (...) -> List[Package] """ Find packages on the remote server. """ if constraint is None: constraint = "*" if not isinstance(constraint, VersionConstraint): constraint = parse_constraint(constraint) info = self.get_package_info(name) packages = [] for version, release in info["releases"].items(): if not release: # Bad release self._log( "No release information found for {}-{}, skipping".format( name, version ), level="debug", ) continue package = Package(name, version) if ( package.is_prerelease() and not allow_prereleases and not constraint.allows(package.version) ): continue if not constraint or (constraint and constraint.allows(package.version)): if extras is not None: package.requires_extras = extras packages.append(package) self._log( "{} packages found for {} {}".format(len(packages), name, str(constraint)), level="debug", ) return packages
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 check_version(self, poet, python_version): python_constraint = self.poet.package.python_constraint wished_constraint = parse_constraint(python_version) if python_constraint.allows(wished_constraint): return True else: raise RhymException( "The specified python version {} doesn't feet with pytproject.toml constraint : {}".format( python_version, python_constraint ) )
def find_packages( self, name, constraint=None, extras=None, allow_prereleases=False ): name = name.lower() packages = [] if extras is None: extras = [] if constraint is None: constraint = "*" if not isinstance(constraint, VersionConstraint): constraint = parse_constraint(constraint) if isinstance(constraint, VersionRange): if ( constraint.max is not None and constraint.max.is_prerelease() or constraint.min is not None and constraint.min.is_prerelease() ): allow_prereleases = True for package in self.packages: if name == package.name: if ( package.is_prerelease() and not allow_prereleases and not package.source_type ): # If prereleases are not allowed and the package is a prerelease # and is a standard package then we skip it continue if constraint.allows(package.version): for dep in package.requires: for extra in extras: if extra not in package.extras: continue reqs = package.extras[extra] for req in reqs: if req.name == dep.name: dep.activate() if extras: package.requires_extras = extras packages.append(package) return packages
def get_update_status(self, latest, package): from poetry.semver import parse_constraint if latest.full_pretty_version == package.full_pretty_version: return "up-to-date" constraint = parse_constraint("^" + package.pretty_version) if latest.version and constraint.allows(latest.version): # It needs an immediate semver-compliant upgrade return "semver-safe-update" # it needs an upgrade but has potential BC breaks so is not urgent return "update-possible"
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.homepage = None self.repository_url = None self.keywords = [] self._license = None self.readme = None 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 # Requirements for making it mandatory self.requirements = {} self.classifiers = [] self._python_versions = "*" self._python_constraint = parse_constraint("*") self._platform = "*" self._platform_constraint = EmptyConstraint() self.root_dir = None self.develop = False
def get_update_status(self, latest, package): from poetry.semver import parse_constraint if latest.full_pretty_version == package.full_pretty_version: return "up-to-date" constraint = parse_constraint("^" + package.pretty_version) if latest.version and constraint.allows(latest.version): # It needs an immediate semver-compliant upgrade return "semver-safe-update" # it needs an upgrade but has potential BC breaks so is not urgent return "update-possible"
def find_packages( self, name, constraint=None, extras=None, allow_prereleases=False ): packages = [] if constraint is not None and not isinstance(constraint, VersionConstraint): constraint = parse_constraint(constraint) key = name if constraint: key = "{}:{}".format(key, str(constraint)) if self._cache.store("matches").has(key): versions = self._cache.store("matches").get(key) else: page = self._get("/{}/".format(canonicalize_name(name).replace(".", "-"))) if page is None: return [] versions = [] for version in page.versions: if not constraint or (constraint and constraint.allows(version)): versions.append(version) self._cache.store("matches").put(key, versions, 5) for version in versions: package = Package(name, version) package.source_type = "legacy" package.source_url = self._url if extras is not None: package.requires_extras = extras packages.append(package) self._log( "{} packages found for {} {}".format(len(packages), name, str(constraint)), level="debug", ) return packages
def find_packages( self, name, constraint=None, extras=None, allow_prereleases=False ): name = name.lower() packages = [] if extras is None: extras = [] if constraint is None: constraint = "*" if not isinstance(constraint, VersionConstraint): constraint = parse_constraint(constraint) for package in self.packages: if name == package.name: if ( package.is_prerelease() and not allow_prereleases and not constraint.allows(package.version) ): continue if constraint is None or constraint.allows(package.version): for dep in package.requires: for extra in extras: if extra not in package.extras: continue reqs = package.extras[extra] for req in reqs: if req.name == dep.name: dep.activate() packages.append(package) return packages
def test_parse_constraint_tilde(input, constraint): assert parse_constraint(input) == constraint
def test_format_python_constraint(): constraint = parse_constraint("~2.7 || ^3.6") result = format_python_constraint(constraint) assert result == ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
def _filter_operations( self, ops, repo ): # type: (List[Operation], Repository) -> None extra_packages = [p.name for p in self._get_extra_packages(repo)] for op in ops: if isinstance(op, Update): package = op.target_package else: package = op.package if op.job_type == "uninstall": continue if package.name in self._develop and package.source_type == "directory": package.develop = True if op.skipped: op.unskip() python = Version.parse( ".".join([str(i) for i in self._venv.version_info[:3]]) ) if "python" in package.requirements: python_constraint = parse_constraint(package.requirements["python"]) if not python_constraint.allows(python): # Incompatible python versions op.skip("Not needed for the current python version") continue if not package.python_constraint.allows(python): op.skip("Not needed for the current python version") continue if "platform" in package.requirements: platform_constraint = GenericConstraint.parse( package.requirements["platform"] ) if not platform_constraint.matches( GenericConstraint("=", sys.platform) ): # Incompatible systems op.skip("Not needed for the current platform") continue if self._update: extras = {} for extra, deps in self._package.extras.items(): extras[extra] = [dep.name for dep in deps] else: extras = {} for extra, deps in self._locker.lock_data.get("extras", {}).items(): extras[extra] = [dep.lower() for dep in deps] # If a package is optional and not requested # in any extra we skip it if package.optional: if package.name not in extra_packages: op.skip("Not required") # If the package is a dev package and dev packages # are not requests, we skip it if package.category == "dev" and not self.is_dev_mode(): op.skip("Dev dependencies not requested")
def handle(self): from poetry.installation import Installer from poetry.semver import parse_constraint from tomlkit import inline_table packages = self.argument("name") is_dev = self.option("dev") if (self.option("git") or self.option("path") or self.option("extras")) and len( packages ) > 1: raise ValueError( "You can only specify one package " "when using the --git or --path options" ) if self.option("git") and self.option("path"): raise RuntimeError("--git and --path cannot be used at the same time") section = "dependencies" if is_dev: section = "dev-dependencies" original_content = self.poetry.file.read() content = self.poetry.file.read() poetry_content = content["tool"]["poetry"] if section not in poetry_content: poetry_content[section] = {} for name in packages: for key in poetry_content[section]: if key.lower() == name.lower(): raise ValueError("Package {} is already present".format(name)) if self.option("git") or self.option("path"): requirements = {packages[0]: ""} else: requirements = self._determine_requirements( packages, allow_prereleases=self.option("allow-prereleases") ) requirements = self._format_requirements(requirements) # validate requirements format for constraint in requirements.values(): parse_constraint(constraint) for name, _constraint in requirements.items(): constraint = inline_table() constraint["version"] = _constraint if self.option("git"): del constraint["version"] constraint["git"] = self.option("git") elif self.option("path"): del constraint["version"] constraint["path"] = self.option("path") if self.option("optional"): constraint["optional"] = True if self.option("allow-prereleases"): constraint["allows-prereleases"] = True if self.option("extras"): extras = [] for extra in self.option("extras"): if " " in extra: extras += [e.strip() for e in extra.split(" ")] else: extras.append(extra) constraint["extras"] = self.option("extras") if self.option("python"): constraint["python"] = self.option("python") if self.option("platform"): constraint["platform"] = self.option("platform") if len(constraint) == 1 and "version" in constraint: constraint = constraint["version"] poetry_content[section][name] = constraint # Write new content self.poetry.file.write(content) # Cosmetic new line self.line("") # Update packages self.reset_poetry() installer = Installer( self.output, self.venv, self.poetry.package, self.poetry.locker, self.poetry.pool, ) installer.dry_run(self.option("dry-run")) installer.update(True) installer.whitelist(requirements) try: status = installer.run() except Exception: self.poetry.file.write(original_content) raise if status != 0 or self.option("dry-run"): # Revert changes if not self.option("dry-run"): self.error( "\n" "Addition failed, reverting pyproject.toml " "to its original content." ) self.poetry.file.write(original_content) return status
def handle(self): from poetry.packages.constraints.generic_constraint import GenericConstraint from poetry.repositories.installed_repository import InstalledRepository from poetry.semver import Version from poetry.semver import parse_constraint package = self.argument("package") if self.option("tree"): self.init_styles() if self.option("outdated"): self.input.set_option("latest", True) locked_repo = self.poetry.locker.locked_repository(True) # Show tree view if requested if self.option("tree") and not package: requires = self.poetry.package.requires + self.poetry.package.dev_requires packages = locked_repo.packages for package in packages: for require in requires: if package.name == require.name: self.display_package_tree(package, locked_repo) break return 0 table = self.table(style="compact") table.get_style().set_vertical_border_char("") locked_packages = locked_repo.packages if package: pkg = None for locked in locked_packages: if package.lower() == locked.name: pkg = locked break if not pkg: raise ValueError("Package {} not found".format(package)) if self.option("tree"): self.display_package_tree(pkg, locked_repo) return 0 rows = [ ["<info>name</>", " : <fg=cyan>{}</>".format(pkg.pretty_name)], ["<info>version</>", " : <comment>{}</>".format(pkg.pretty_version)], ["<info>description</>", " : {}".format(pkg.description)], ] table.add_rows(rows) table.render() if pkg.requires: self.line("") self.line("<info>dependencies</info>") for dependency in pkg.requires: self.line( " - {} <comment>{}</>".format( dependency.pretty_name, dependency.pretty_constraint ) ) return 0 show_latest = self.option("latest") show_all = self.option("all") terminal = self.get_application().terminal width = terminal.width name_length = version_length = latest_length = 0 latest_packages = {} installed_repo = InstalledRepository.load(self.venv) skipped = [] platform = sys.platform python = Version.parse(".".join([str(i) for i in self._venv.version_info[:3]])) # Computing widths for locked in locked_packages: python_constraint = parse_constraint(locked.requirements.get("python", "*")) platform_constraint = GenericConstraint.parse( locked.requirements.get("platform", "*") ) if not python_constraint.allows(python) or not platform_constraint.matches( GenericConstraint("=", platform) ): skipped.append(locked) if not show_all: continue current_length = len(locked.pretty_name) if not self.output.is_decorated(): installed_status = self.get_installed_status(locked, installed_repo) if installed_status == "not-installed": current_length += 4 name_length = max(name_length, current_length) version_length = max(version_length, len(locked.full_pretty_version)) if show_latest: latest = self.find_latest_package(locked) if not latest: latest = locked latest_packages[locked.pretty_name] = latest latest_length = max(latest_length, len(latest.full_pretty_version)) write_version = name_length + version_length + 3 <= width write_latest = name_length + version_length + latest_length + 3 <= width write_description = name_length + version_length + latest_length + 24 <= width for locked in locked_packages: color = "green" name = locked.pretty_name install_marker = "" if locked in skipped: if not show_all: continue color = "black;options=bold" else: installed_status = self.get_installed_status(locked, installed_repo) if installed_status == "not-installed": color = "red" if not self.output.is_decorated(): # Non installed in non decorated mode install_marker = " (!)" line = "<fg={}>{:{}}{}</>".format( color, name, name_length - len(install_marker), install_marker ) if write_version: line += " <comment>{:{}}</comment>".format( locked.full_pretty_version, version_length ) if show_latest and write_latest: latest = latest_packages[locked.pretty_name] update_status = self.get_update_status(latest, locked) color = "green" if update_status == "semver-safe-update": color = "red" elif update_status == "update-possible": color = "yellow" line += " <fg={}>{:{}}</>".format( color, latest.pretty_version, latest_length ) if self.option("outdated") and update_status == "up-to-date": continue if write_description: description = locked.description remaining = width - name_length - version_length - 4 if show_latest: remaining -= latest_length if len(locked.description) > remaining: description = description[: remaining - 3] + "..." line += " " + description self.line(line)
def supports_python2(self): return self._package.python_constraint.allows_any( parse_constraint(">=2.0.0 <3.0.0") )
def _get_tags_for_package(self, package, graph, depth=0): categories = ["dev"] optionals = [True] python_versions = [] platforms = [] _depths = [0] children = graph["children"] found = False for child in children: if child["name"] == package.name: category = child["category"] optional = child["optional"] python_version = child["python_version"] platform = child["platform"] _depths.append(depth) else: ( category, optional, python_version, platform, _depth, ) = self._get_tags_for_package(package, child, depth=depth + 1) _depths.append(_depth) categories.append(category) optionals.append(optional) if python_version is not None: python_versions.append(python_version) if platform is not None: platforms.append(platform) if "main" in categories: category = "main" else: category = "dev" optional = all(optionals) if not python_versions: python_version = None else: # Find the least restrictive constraint python_version = python_versions[0] for constraint in python_versions[1:]: previous = parse_constraint(python_version) current = parse_constraint(constraint) if python_version == "*": continue elif constraint == "*": python_version = constraint elif current.allows_all(previous): python_version = constraint if not platforms: platform = None else: platform = platforms[0] for constraint in platforms[1:]: previous = GenericConstraint.parse(platform) current = GenericConstraint.parse(constraint) if platform == "*": continue elif constraint == "*": platform = constraint elif current.matches(previous): platform = constraint depth = max(*(_depths + [0])) return category, optional, python_version, platform, depth
def test_parse_constraint_multi(input): assert parse_constraint(input) == VersionRange( Version(2, 0, 0), Version(3, 0, 0), include_min=False, include_max=True )
def test_parse_constraint_wildcard(input, constraint): assert parse_constraint(input) == constraint
def test_versions_are_sortable(unsorted, sorted_): unsorted = [parse_constraint(u) for u in unsorted] sorted_ = [parse_constraint(s) for s in sorted_] assert sorted(unsorted) == sorted_
def test_constraints_keep_version_precision(input, expected): assert str(parse_constraint(input)) == expected
def test_parse_constraints_negative_wildcard(input, constraint): assert parse_constraint(input) == constraint
def test_parse_constraint_multi_wilcard(input): assert parse_constraint(input) == VersionUnion( VersionRange(Version(2, 7, 0), Version(3, 0, 0), True, False), VersionRange(Version(3, 2, 0), None, True, False), )
def handle(self): from poetry.packages import Dependency from poetry.packages import ProjectPackage from poetry.puzzle import Solver from poetry.repositories.repository import Repository from poetry.semver import parse_constraint packages = self.argument("package") if not packages: package = self.poetry.package else: requirements = self._determine_requirements(packages) requirements = self._format_requirements(requirements) # validate requirements format for constraint in requirements.values(): parse_constraint(constraint) dependencies = [] for name, constraint in requirements.items(): dep = Dependency(name, constraint) extras = [] for extra in self.option("extras"): if " " in extra: extras += [e.strip() for e in extra.split(" ")] else: extras.append(extra) for ex in extras: dep.extras.append(ex) dependencies.append(dep) package = ProjectPackage( self.poetry.package.name, self.poetry.package.version ) package.python_versions = ( self.option("python") or self.poetry.package.python_versions ) for dep in dependencies: package.requires.append(dep) solver = Solver( package, self.poetry.pool, Repository(), Repository(), self.output ) ops = solver.solve() self.line("") self.line("Resolution results:") self.line("") for op in ops: package = op.package self.line( " - <info>{}</info> (<comment>{}</comment>)".format( package.name, package.version ) ) if package.requirements: for req_name, req_value in package.requirements.items(): self.line(" - {}: {}".format(req_name, req_value))
def test_parse_constraint_caret(input, constraint): assert parse_constraint(input) == constraint
def python_versions(self, value): self._python_versions = value self._python_constraint = parse_constraint(value)
def _build_graph( self, package, packages, previous=None, previous_dep=None, dep=None ): if not previous: category = "dev" optional = True python_version = "*" platform = "*" else: category = dep.category optional = dep.is_optional() and not dep.is_activated() python_version = str( parse_constraint(previous["python_version"]).intersect( previous_dep.python_constraint ) ) platform = str( previous_dep.platform if GenericConstraint.parse(previous["platform"]).matches( previous_dep.platform_constraint ) and previous_dep.platform != "*" else previous["platform"] ) graph = { "name": package.name, "category": category, "optional": optional, "python_version": python_version, "platform": platform, "children": [], } if previous_dep and previous_dep is not dep and previous_dep.name == dep.name: return graph for dependency in package.all_requires: if dependency.is_optional(): if not package.is_root() and ( not previous_dep or not previous_dep.extras ): continue is_activated = False for group, extras in package.extras.items(): if dep: extras = previous_dep.extras elif package.is_root(): extras = package.extras else: extras = [] if group in extras: is_activated = True break if not is_activated: continue for pkg in packages: if pkg.name == dependency.name: # If there is already a child with this name # we merge the requirements existing = None for child in graph["children"]: if ( child["name"] == pkg.name and child["category"] == dependency.category ): existing = child continue child_graph = self._build_graph( pkg, packages, graph, dependency, dep or dependency ) if existing: existing["python_version"] = str( parse_constraint(existing["python_version"]).union( parse_constraint(child_graph["python_version"]) ) ) continue graph["children"].append(child_graph) return graph