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 = VersionParser().parse_constraints(constraint) self.pretty_constraint = constraint self.marker = req.marker if req.marker else None
def find_packages(self, name: str, constraint: Union[Constraint, str, None] = None, extras: Union[list, None] = None) -> List[Package]: """ Find packages on the remote server. """ packages = [] if constraint is not None and not isinstance(constraint, BaseConstraint): version_parser = VersionParser() constraint = version_parser.parse_constraints(constraint) info = self.get_package_info(name) versions = [] for version, release in info['releases'].items(): if (not constraint or (constraint and constraint.matches(Constraint('=', version)))): versions.append(version) for version in versions: packages.append(self.package(name, version, extras=extras)) return packages
def find_packages(self, name, constraint=None, extras=None): packages = [] if constraint is not None and not isinstance(constraint, BaseConstraint): version_parser = VersionParser() constraint = version_parser.parse_constraints(constraint) key = name if constraint: key = f'{key}:{str(constraint)}' if self._cache.store('matches').has(key): versions = self._cache.store('matches').get(key) else: candidates = [str(c.version) for c in self._repository.find_all_candidates(name)] versions = [] for version in candidates: if version in versions: continue if ( not constraint or (constraint and constraint.matches(Constraint('=', version))) ): versions.append(version) self._cache.store('matches').put(key, versions, 5) for version in versions: packages.append(self.package(name, version, extras=extras)) 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, BaseConstraint): parser = VersionParser() constraint = parser.parse_constraints(constraint) for package in self.packages: if name == package.name: pkg_constraint = Constraint('==', package.version) if constraint is None or constraint.matches(pkg_constraint): 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 all_classifiers(self): classifiers = copy.copy(self.classifiers) # Automatically set python classifiers parser = VersionParser() if self.python_versions == '*': python_constraint = parser.parse_constraints('~2.7 || ^3.4') else: python_constraint = self.python_constraint for version in sorted(self.AVAILABLE_PYTHONS): if len(version) == 1: constraint = parser.parse_constraints(version + '.*') else: constraint = Constraint('=', version) if python_constraint.matches(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 not isinstance(constraint, MultiConstraint): return str(constraint) has_disjunctive = False for c in constraint.constraints: if isinstance(c, MultiConstraint) and c.is_disjunctive(): has_disjunctive = True break parser = VersionParser() formatted = [] accepted = [] if not constraint.is_disjunctive() and not has_disjunctive: return str(constraint) for version in PYTHON_VERSION: version_constraint = parser.parse_constraints(version) matches = constraint.matches(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 _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 parser = VersionParser() python = '.'.join([str(i) for i in self._venv.version_info[:3]]) if 'python' in package.requirements: python_constraint = parser.parse_constraints( package.requirements['python'] ) if not python_constraint.matches(Constraint('=', python)): # Incompatible python versions op.skip('Not needed for the current python version') continue if not package.python_constraint.matches(Constraint('=', 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, name: str, constraint: str, optional: bool = False, category: str = 'main', allows_prereleases: bool = False): self._name = name.lower() self._pretty_name = name self._parser = VersionParser() try: self._constraint = self._parser.parse_constraints(constraint) except ValueError: self._constraint = self._parser.parse_constraints('*') self._pretty_constraint = constraint self._optional = optional self._category = category self._allows_prereleases = allows_prereleases self._python_versions = '*' self._python_constraint = self._parser.parse_constraints('*') self._platform = '*' self._platform_constraint = self._parser.parse_constraints('*') self._extras = []
def test_format_python_constraint(): parser = VersionParser() constraint = parser.parse_constraints('~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 __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 self._parser = VersionParser() try: if not isinstance(constraint, BaseConstraint): self._constraint = self._parser.parse_constraints(constraint) else: self._constraint = constraint except ValueError: self._constraint = self._parser.parse_constraints('*') self._pretty_constraint = constraint self._optional = optional self._category = category self._allows_prereleases = allows_prereleases self._python_versions = '*' self._python_constraint = self._parser.parse_constraints('*') self._platform = '*' self._platform_constraint = EmptyConstraint() self._extras = [] self._in_extras = []
def find_packages(self, name, constraint=None, extras=None): name = name.lower() packages = [] if extras is None: extras = [] if not isinstance(constraint, BaseConstraint): parser = VersionParser() constraint = parser.parse_constraints(constraint) for package in self.packages: if name == package.name: pkg_constraint = Constraint('==', package.version) if constraint is None or constraint.matches(pkg_constraint): for extra in extras: if extra in package.extras: for dep in package.extras[extra]: dep.activate() package.requires += package.extras[extra] packages.append(package) 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) self._version = str(parse_version(version)) self._pretty_version = pretty_version or version self.description = '' self._stability = parse_stability(version) self._dev = self._stability == 'dev' 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._parser = VersionParser() 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 = self._parser.parse_constraints('*') self._platform = '*' self._platform_constraint = EmptyConstraint() self.cwd = None
def find_packages(self, name, # type: str constraint=None, # type: Union[Constraint, 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 not None and not isinstance(constraint, BaseConstraint): version_parser = VersionParser() constraint = version_parser.parse_constraints(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: continue if ( not constraint or (constraint and constraint.matches(Constraint('=', 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 get_classifers(self): classifiers = [] # Automatically set python classifiers parser = VersionParser() if self._package.python_versions == '*': python_constraint = parser.parse_constraints('~2.7 || ^3.4') else: python_constraint = self._package.python_constraint for version in sorted(self.AVAILABLE_PYTHONS): if python_constraint.matches(Constraint('=', version)): classifiers.append(f'Programming Language :: Python :: {version}') return classifiers
def _filter_operations(self, ops: List[Operation]): for op in ops: if isinstance(op, Update): package = op.target_package else: package = op.package if op.job_type == 'uninstall': continue parser = VersionParser() python = '.'.join([str(i) for i in sys.version_info[:3]]) if 'python' in package.requirements: python_constraint = parser.parse_constraints( package.requirements['python']) if not python_constraint.matches(Constraint('=', python)): # Incompatible python versions op.skip('Not needed for the current python version') 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', {}): 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: drop = True for extra in self._extras: if extra in extras and package.name in extras[extra]: drop = False continue if drop: op.skip('Not required')
def handle(self): packages = self.argument('package') if not packages: package = self.poetry.package dependencies = package.requires + package.dev_requires else: requirements = self._determine_requirements(packages) requirements = self._format_requirements(requirements) # validate requirements format parser = VersionParser() for constraint in requirements.values(): parser.parse_constraints(constraint) dependencies = [] for name, constraint in requirements.items(): dependencies.append( Dependency(name, constraint) ) solver = Solver( self.poetry.package, self.poetry.pool, Repository(), self.output ) ops = solver.solve(dependencies) self.line('') self.line('Resolution results:') self.line('') for op in ops: package = op.package self.line(f' - <info>{package.name}</info> ' f'(<comment>{package.version}</comment>)')
def handle(self): packages = self.argument('name') is_dev = self.option('dev') section = 'dependencies' if is_dev: section = 'dev-dependencies' original_content = self.poetry.file.read() content = self.poetry.file.read() poetry_content = content['tool']['poetry'] for name in packages: for key in poetry_content[section]: if key.lower() == name.lower(): raise ValueError(f'Package {name} is already present') requirements = self._determine_requirements(packages) requirements = self._format_requirements(requirements) # validate requirements format parser = VersionParser() for constraint in requirements.values(): parser.parse_constraints(constraint) for name, constraint in requirements.items(): if self.option('optional'): constraint = {'version': constraint, 'optional': True} 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 solve(self, requested, fixed=None) -> List[Operation]: resolver = Resolver(Provider(self._package, self._pool), UI(self._io)) base = None if fixed is not None: base = DependencyGraph() for fixed_req in fixed: base.add_vertex(fixed_req.name, fixed_req, True) try: graph = resolver.resolve(requested, base=base) except ResolverError as e: raise SolverProblemError(e) packages = [v.payload for v in graph.vertices.values()] # Setting info for vertex in graph.vertices.values(): tags = self._get_tags_for_vertex(vertex, requested) if 'main' in tags['category']: vertex.payload.category = 'main' else: vertex.payload.category = 'dev' if not tags['optional']: vertex.payload.optional = False else: vertex.payload.optional = True # Finding the less restrictive requirements requirements = {} parser = VersionParser() for req_name, reqs in tags['requirements'].items(): for req in reqs: if req_name == 'python': if 'python' not in requirements: requirements['python'] = req continue previous = parser.parse_constraints(requirements['python']) current = parser.parse_constraints(req) if current.matches(previous): requirements['python'] = req if req_name == 'platform': if 'platform' not in requirements: requirements['platform'] = req continue vertex.payload.requirements = requirements operations = [] for package in packages: installed = False for pkg in self._locked.packages: if package.name == pkg.name: installed = True # Checking version if package.version != pkg.version: operations.append(Update(pkg, package)) break if not installed: operations.append(Install(package)) # Checking for removals for pkg in self._locked.packages: remove = True for package in packages: if pkg.name == package.name: remove = False break if remove: operations.append(Uninstall(pkg)) return list(reversed(operations))
def __init__(self, pool, parser=VersionParser()): self._pool = pool self._parser = parser
def parser(): return VersionParser()
def handle(self): from poetry.installation import Installer from poetry.semver.version_parser import VersionParser 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'] 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 parser = VersionParser() for constraint in requirements.values(): parser.parse_constraints(constraint) for name, constraint in requirements.items(): 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'): constraint['extras'] = self.option('extras') 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 _get_tags_for_vertex(self, vertex, requested): category = 'dev' optional = True python_version = None platform = None if not vertex.incoming_edges: # Original dependency for req in requested: if vertex.payload.name == req.name: category = req.category optional = req.is_optional() python_version = str(req.python_constraint) platform = str(req.platform_constraint) break return category, optional, python_version, platform parser = VersionParser() python_versions = [] platforms = [] for edge in vertex.incoming_edges: python_version = None platform = None for req in edge.origin.payload.requires: if req.name == vertex.payload.name: python_version = req.python_versions platform = req.platform break (top_category, top_optional, top_python_version, top_platform) = self._get_tags_for_vertex( edge.origin, requested ) if top_category == 'main': category = top_category optional = optional and top_optional # Take the most restrictive constraints if top_python_version is not None: if python_version is not None: previous = parser.parse_constraints(python_version) current = parser.parse_constraints(top_python_version) if top_python_version != '*' and previous.matches(current): python_versions.append(top_python_version) else: python_versions.append(python_version) else: python_versions.append(top_python_version) elif python_version is not None: python_versions.append(python_version) if top_platform is not None: if platform is not None: previous = GenericConstraint.parse(platform) current = GenericConstraint.parse(top_platform) if top_platform != '*' and previous.matches(current): platforms.append(top_platform) else: platforms.append(platform) else: platforms.append(top_platform) elif platform is not None: platforms.append(platform) if not python_versions: python_version = None else: # Find the least restrictive constraint python_version = python_versions[0] previous = parser.parse_constraints(python_version) for constraint in python_versions[1:]: current = parser.parse_constraints(constraint) if python_version == '*': continue elif constraint == '*': python_version = constraint elif current.matches(previous): python_version = constraint if not platforms: platform = None else: platform = platforms[0] previous = GenericConstraint.parse(platform) for constraint in platforms[1:]: current = GenericConstraint.parse(constraint) if platform == '*': continue elif constraint == '*': platform = constraint elif current.matches(previous): platform = constraint return category, optional, python_version, platform
def increment_version(self, version, rule): from poetry.semver.version_parser import VersionParser parser = VersionParser() version_regex = ( 'v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?' ).format(parser._modifier_regex) m = re.match(version_regex, version) if not m: raise ValueError( 'The project\'s version doesn\'t seem to follow semver') if m.group(3): index = 2 elif m.group(2): index = 1 else: index = 0 matches = m.groups()[:index + 1] base = '.'.join(matches) extra_matches = list(g or '' for g in m.groups()[4:]) extras = version[len('.'.join(matches)):] increment = 1 is_prerelease = (extra_matches[0] or extra_matches[1]) != '' bump_prerelease = rule in { 'premajor', 'preminor', 'prepatch', 'prerelease' } position = -1 if rule in {'major', 'premajor'}: if m.group(1) != '0' or m.group(2) != '0' or not is_prerelease: position = 0 elif rule in {'minor', 'preminor'}: if m.group(2) != '0' or not is_prerelease: position = 1 elif rule in {'patch', 'prepatch'}: if not is_prerelease: position = 2 elif rule == 'prerelease' and not is_prerelease: position = 2 if position != -1: extra_matches[0] = None base = parser._manipulate_version_string(matches, position, increment=increment) if bump_prerelease: # We bump the prerelease part of the version sep = '' if not extra_matches[0]: extra_matches[0] = 'alpha' extra_matches[1] = '.0' sep = '-' else: if extras.startswith(('.', '_', '-')): sep = extras[0] prerelease = extra_matches[1] if not prerelease: prerelease = '.1' psep = '' if prerelease.startswith(('.', '-')): psep = prerelease[0] prerelease = prerelease[1:] new_prerelease = str(int(prerelease) + 1) extra_matches[1] = '{}{}'.format(psep, new_prerelease) extras = '{}{}{}{}'.format(sep, extra_matches[0], extra_matches[1], extra_matches[2]) else: extras = '' return '.'.join(base.split('.')[:max(index, position) + 1]) + extras