def _do_install(self, local_repo): from poetry.puzzle import Solver locked_repository = Repository() if self._update: if self._locker.is_locked() and not self._lock: locked_repository = self._locker.locked_repository(True) # If no packages have been whitelisted (The ones we want to update), # we whitelist every package in the lock file. if not self._whitelist: for pkg in locked_repository.packages: self._whitelist.append(pkg.name) # Checking extras for extra in self._extras: if extra not in self._package.extras: raise ValueError( "Extra [{}] is not specified.".format(extra)) self._io.write_line("<info>Updating dependencies</>") solver = Solver( self._package, self._pool, self._installed_repository, locked_repository, self._io, remove_untracked=self._remove_untracked, ) ops = solver.solve(use_latest=self._whitelist) else: self._io.write_line( "<info>Installing dependencies from lock file</>") locked_repository = self._locker.locked_repository(True) if not self._locker.is_fresh(): self._io.write_line( "<warning>" "Warning: The lock file is not up to date with " "the latest changes in pyproject.toml. " "You may be getting outdated dependencies. " "Run update to update them." "</warning>") for extra in self._extras: if extra not in self._locker.lock_data.get("extras", {}): raise ValueError( "Extra [{}] is not specified.".format(extra)) # If we are installing from lock # Filter the operations by comparing it with what is # currently installed ops = self._get_operations_from_lock(locked_repository) self._populate_local_repo(local_repo, ops) if self._update: self._write_lock_file(local_repo) if self._lock: # If we are only in lock mode, no need to go any further return 0 root = self._package if not self.is_dev_mode(): root = root.clone() del root.dev_requires[:] elif self.is_dev_only(): root = root.clone() del root.requires[:] if self._io.is_verbose(): self._io.write_line("") self._io.write_line( "<info>Finding the necessary packages for the current system</>" ) # We resolve again by only using the lock file pool = Pool(ignore_repository_names=True) # Making a new repo containing the packages # newly resolved and the ones from the current lock file repo = Repository() for package in local_repo.packages + locked_repository.packages: if not repo.has_package(package): repo.add_package(package) pool.add_repository(repo) # We whitelist all packages to be sure # that the latest ones are picked up whitelist = [] for pkg in locked_repository.packages: whitelist.append(pkg.name) solver = Solver( root, pool, self._installed_repository, locked_repository, NullIO(), remove_untracked=self._remove_untracked, ) with solver.use_environment(self._env): ops = solver.solve(use_latest=whitelist) # We need to filter operations so that packages # not compatible with the current system, # or optional and not requested, are dropped self._filter_operations(ops, local_repo) # Execute operations return self._execute(ops)
def handle(self): from poetry.core.packages.project_package import ProjectPackage from poetry.io.null_io import NullIO from poetry.puzzle import Solver from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository from poetry.utils.env import EnvManager packages = self.argument("package") if not packages: package = self.poetry.package else: # Using current pool for determine_requirements() self._pool = self.poetry.pool package = ProjectPackage( self.poetry.package.name, self.poetry.package.version ) # Silencing output is_quiet = self.io.output.is_quiet() if not is_quiet: self.io.output.set_quiet(True) requirements = self._determine_requirements(packages) if not is_quiet: self.io.output.set_quiet(False) for constraint in requirements: name = constraint.pop("name") dep = package.add_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) package.python_versions = self.option("python") or ( self.poetry.package.python_versions ) pool = self.poetry.pool solver = Solver(package, pool, Repository(), Repository(), self._io) ops = solver.solve() self.line("") self.line("Resolution results:") self.line("") if self.option("tree"): show_command = self.application.find("show") show_command.init_styles(self.io) packages = [op.package for op in ops] repo = Repository(packages) requires = package.requires + package.dev_requires for pkg in repo.packages: for require in requires: if pkg.name == require.name: show_command.display_package_tree(self.io, pkg, repo) break return 0 table = self.table([], style="borderless") rows = [] if self.option("install"): env = EnvManager(self.poetry).get() pool = Pool() locked_repository = Repository() for op in ops: locked_repository.add_package(op.package) pool.add_repository(locked_repository) solver = Solver(package, pool, Repository(), Repository(), NullIO()) with solver.use_environment(env): ops = solver.solve() for op in ops: if self.option("install") and op.skipped: continue pkg = op.package row = [ "<c1>{}</c1>".format(pkg.name), "<b>{}</b>".format(pkg.version), "", ] if not pkg.marker.is_any(): row[2] = str(pkg.marker) rows.append(row) table.set_rows(rows) table.render(self.io)
def _do_install(self, local_repo: Repository) -> int: from poetry.puzzle import Solver locked_repository = Repository() if self._update: if self._locker.is_locked() and not self._lock: locked_repository = self._locker.locked_repository() # If no packages have been whitelisted (The ones we want to update), # we whitelist every package in the lock file. if not self._whitelist: for pkg in locked_repository.packages: self._whitelist.append(pkg.name) # Checking extras for extra in self._extras: if extra not in self._package.extras: raise ValueError(f"Extra [{extra}] is not specified.") self._io.write_line("<info>Updating dependencies</>") solver = Solver( self._package, self._pool, self._installed_repository, locked_repository, self._io, ) with solver.provider.use_source_root( source_root=self._env.path.joinpath("src") ): ops = solver.solve(use_latest=self._whitelist).calculate_operations() else: self._io.write_line("<info>Installing dependencies from lock file</>") locked_repository = self._locker.locked_repository() if not self._locker.is_fresh(): self._io.write_error_line( "<warning>" "Warning: poetry.lock is not consistent with pyproject.toml. " "You may be getting improper dependencies. " "Run `poetry lock [--no-update]` to fix it." "</warning>" ) for extra in self._extras: if extra not in self._locker.lock_data.get("extras", {}): raise ValueError(f"Extra [{extra}] is not specified.") # If we are installing from lock # Filter the operations by comparing it with what is # currently installed ops = self._get_operations_from_lock(locked_repository) self._populate_local_repo(local_repo, ops) if self._update: self._write_lock_file(local_repo) if self._lock: # If we are only in lock mode, no need to go any further return 0 if self._groups is not None: root = self._package.with_dependency_groups(list(self._groups), only=True) else: root = self._package.without_optional_dependency_groups() if self._io.is_verbose(): self._io.write_line("") self._io.write_line( "<info>Finding the necessary packages for the current system</>" ) # We resolve again by only using the lock file pool = Pool(ignore_repository_names=True) # Making a new repo containing the packages # newly resolved and the ones from the current lock file repo = Repository() for package in local_repo.packages + locked_repository.packages: if not repo.has_package(package): repo.add_package(package) pool.add_repository(repo) solver = Solver( root, pool, self._installed_repository, locked_repository, NullIO() ) # Everything is resolved at this point, so we no longer need # to load deferred dependencies (i.e. VCS, URL and path dependencies) solver.provider.load_deferred(False) with solver.use_environment(self._env): ops = solver.solve(use_latest=self._whitelist).calculate_operations( with_uninstalls=self._requires_synchronization, synchronize=self._requires_synchronization, ) if not self._requires_synchronization: # If no packages synchronisation has been requested we need # to calculate the uninstall operations from poetry.puzzle.transaction import Transaction transaction = Transaction( locked_repository.packages, [(package, 0) for package in local_repo.packages], installed_packages=self._installed_repository.packages, root_package=root, ) ops = [ op for op in transaction.calculate_operations(with_uninstalls=True) if op.job_type == "uninstall" ] + ops # We need to filter operations so that packages # not compatible with the current system, # or optional and not requested, are dropped self._filter_operations(ops, local_repo) # Execute operations return self._execute(ops)
def handle(self) -> Optional[int]: from cleo.io.null_io import NullIO from poetry.core.packages.project_package import ProjectPackage from poetry.factory import Factory from poetry.puzzle import Solver from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository from poetry.utils.env import EnvManager packages = self.argument("package") if not packages: package = self.poetry.package else: # Using current pool for determine_requirements() self._pool = self.poetry.pool package = ProjectPackage(self.poetry.package.name, self.poetry.package.version) # Silencing output verbosity = self.io.output.verbosity self.io.output.set_verbosity(Verbosity.QUIET) requirements = self._determine_requirements(packages) self.io.output.set_verbosity(verbosity) for constraint in requirements: name = constraint.pop("name") extras = [] for extra in self.option("extras"): if " " in extra: extras += [e.strip() for e in extra.split(" ")] else: extras.append(extra) constraint["extras"] = extras package.add_dependency( Factory.create_dependency(name, constraint)) package.python_versions = self.option("python") or ( self.poetry.package.python_versions) pool = self.poetry.pool solver = Solver(package, pool, Repository(), Repository(), self._io) ops = solver.solve().calculate_operations() self.line("") self.line("Resolution results:") self.line("") if self.option("tree"): show_command: ShowCommand = self.application.find("show") show_command.init_styles(self.io) packages = [op.package for op in ops] repo = Repository(packages) requires = package.all_requires for pkg in repo.packages: for require in requires: if pkg.name == require.name: show_command.display_package_tree(self.io, pkg, repo) break return 0 table = self.table(style="compact") table.style.set_vertical_border_chars("", " ") rows = [] if self.option("install"): env = EnvManager(self.poetry).get() pool = Pool() locked_repository = Repository() for op in ops: locked_repository.add_package(op.package) pool.add_repository(locked_repository) solver = Solver(package, pool, Repository(), Repository(), NullIO()) with solver.use_environment(env): ops = solver.solve().calculate_operations() for op in ops: if self.option("install") and op.skipped: continue pkg = op.package row = [ "<c1>{}</c1>".format(pkg.complete_name), "<b>{}</b>".format(pkg.version), ] if not pkg.marker.is_any(): row[2] = str(pkg.marker) rows.append(row) table.set_rows(rows) table.render() return None
def _do_install(self, local_repo: Repository) -> int: from poetry.puzzle import Solver locked_repository = Repository() if self._update: if self._locker.is_locked() and not self._lock: locked_repository = self._locker.locked_repository(True) # If no packages have been whitelisted (The ones we want to update), # we whitelist every package in the lock file. if not self._whitelist: for pkg in locked_repository.packages: self._whitelist.append(pkg.name) # Checking extras for extra in self._extras: if extra not in self._package.extras: raise ValueError(f"Extra [{extra}] is not specified.") self._io.write_line("<info>Updating dependencies</>") solver = Solver( self._package, self._pool, self._installed_repository, locked_repository, self._io, remove_untracked=self._remove_untracked, ) ops = solver.solve(use_latest=self._whitelist) else: self._io.write_line( "<info>Installing dependencies from lock file</>") locked_repository = self._locker.locked_repository(True) if not self._locker.is_fresh(): self._io.write_line( "<warning>" "Warning: The lock file is not up to date with " "the latest changes in pyproject.toml. " "You may be getting outdated dependencies. " "Run update to update them." "</warning>") for extra in self._extras: if extra not in self._locker.lock_data.get("extras", {}): raise ValueError(f"Extra [{extra}] is not specified.") # If we are installing from lock # Filter the operations by comparing it with what is # currently installed ops = self._get_operations_from_lock(locked_repository) self._populate_local_repo(local_repo, ops) if self._update: self._write_lock_file(local_repo) if self._lock: # If we are only in lock mode, no need to go any further return 0 if self._without_groups or self._with_groups or self._only_groups: if self._with_groups: # Default dependencies and opted-in optional dependencies root = self._package.with_dependency_groups(self._with_groups) elif self._without_groups: # Default dependencies without selected groups root = self._package.without_dependency_groups( self._without_groups) else: # Only selected groups root = self._package.with_dependency_groups(self._only_groups, only=True) else: root = self._package.without_optional_dependency_groups() if self._io.is_verbose(): self._io.write_line("") self._io.write_line( "<info>Finding the necessary packages for the current system</>" ) # We resolve again by only using the lock file pool = Pool(ignore_repository_names=True) # Making a new repo containing the packages # newly resolved and the ones from the current lock file repo = Repository() for package in local_repo.packages + locked_repository.packages: if not repo.has_package(package): repo.add_package(package) pool.add_repository(repo) solver = Solver( root, pool, self._installed_repository, locked_repository, NullIO(), remove_untracked=self._remove_untracked, ) # Everything is resolved at this point, so we no longer need # to load deferred dependencies (i.e. VCS, URL and path dependencies) solver.provider.load_deferred(False) with solver.use_environment(self._env): ops = solver.solve(use_latest=self._whitelist) # We need to filter operations so that packages # not compatible with the current system, # or optional and not requested, are dropped self._filter_operations(ops, local_repo) # Execute operations return self._execute(ops)
def solve_pypi( pip_specs: Dict[str, src_parser.Dependency], use_latest: List[str], pip_locked: Dict[str, src_parser.LockedDependency], conda_locked: Dict[str, src_parser.LockedDependency], python_version: str, platform: str, verbose: bool = False, ) -> Dict[str, src_parser.LockedDependency]: """ Solve pip dependencies for the given platform Parameters ---------- conda : Path to conda, mamba, or micromamba use_latest : Names of packages to update to the latest version compatible with pip_specs pip_specs : PEP440 package specifications pip_locked : Previous solution for the given platform (pip packages only) conda_locked : Current solution of conda-only specs for the given platform python_version : Version of Python in conda_locked platform : Target platform verbose : Print chatter from solver """ dummy_package = ProjectPackage("_dummy_package_", "0.0.0") dependencies = [get_dependency(spec) for spec in pip_specs.values()] for dep in dependencies: dummy_package.add_dependency(dep) pypi = PyPiRepository() pool = Pool(repositories=[pypi]) installed = Repository() locked = Repository() python_packages = dict() for dep in conda_locked.values(): if dep.name.startswith("__"): continue try: pypi_name = conda_name_to_pypi_name(dep.name).lower() except KeyError: continue # Prefer the Python package when its name collides with the Conda package # for the underlying library, e.g. python-xxhash (pypi: xxhash) over xxhash # (pypi: no equivalent) if pypi_name not in python_packages or pypi_name != dep.name: python_packages[pypi_name] = dep.version # treat conda packages as both locked and installed for name, version in python_packages.items(): for repo in (locked, installed): repo.add_package(Package(name=name, version=version)) # treat pip packages as locked only for spec in pip_locked.values(): locked.add_package(get_package(spec)) if verbose: io = ConsoleIO() io.set_verbosity(VERY_VERBOSE) else: io = NullIO() s = Solver( dummy_package, pool=pool, installed=installed, locked=locked, io=io, ) to_update = list({spec.name for spec in pip_locked.values() }.intersection(use_latest)) env = PlatformEnv(python_version, platform) # find platform-specific solution (e.g. dependencies conditioned on markers) with s.use_environment(env): result = s.solve(use_latest=to_update) chooser = Chooser(pool, env=env) # Extract distributions from Poetry package plan, ignoring uninstalls # (usually: conda package with no pypi equivalent) and skipped ops # (already installed) requirements: List[src_parser.LockedDependency] = [] for op in result: if not isinstance(op, Uninstall) and not op.skipped: # Take direct references verbatim source: Optional[src_parser.DependencySource] = None if op.package.source_type == "url": url, fragment = urldefrag(op.package.source_url) hash_type, hash = fragment.split("=") hash = src_parser.HashModel(**{hash_type: hash}) source = src_parser.DependencySource(type="url", url=op.package.source_url) # Choose the most specific distribution for the target else: link = chooser.choose_for(op.package) url = link.url_without_fragment hash = src_parser.HashModel(**{link.hash_name: link.hash}) requirements.append( src_parser.LockedDependency( name=op.package.name, version=str(op.package.version), manager="pip", source=source, platform=platform, dependencies={ dep.name: str(dep.constraint) for dep in op.package.requires }, url=url, hash=hash, )) # use PyPI names of conda packages to walking the dependency tree and propagate # categories from explicit to transitive dependencies planned = { **{dep.name: dep for dep in requirements}, # prefer conda packages so add them afterwards } for conda_name, dep in conda_locked.items(): try: pypi_name = conda_name_to_pypi_name(conda_name).lower() except KeyError: # no conda-name found, assuming conda packages do NOT intersect with the pip package continue planned[pypi_name] = dep src_parser._apply_categories(requested=pip_specs, planned=planned) return {dep.name: dep for dep in requirements}