def repository(mocker, env): # type: (MockFixture, MockEnv) -> InstalledRepository mocker.patch( "poetry.utils._compat.metadata.Distribution.discover", return_value=INSTALLED_RESULTS, ) mocker.patch( "poetry.core.vcs.git.Git.rev_parse", return_value="bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6", ) mocker.patch( "poetry.core.vcs.git.Git.remote_urls", side_effect=[ { "remote.origin.url": "https://github.com/sdispater/pendulum.git" }, { "remote.origin.url": "[email protected]:sdispater/pendulum.git" }, ], ) mocker.patch("poetry.repositories.installed_repository._VENDORS", str(VENDOR_DIR)) return InstalledRepository.load(env)
def _get_operations_from_lock( self, locked_repository: Repository) -> List[Operation]: installed_repo = InstalledRepository.load(self._venv) ops = [] extra_packages = [ p.name for p in self._get_extra_packages(locked_repository) ] for locked in locked_repository.packages: is_installed = False for installed in installed_repo.packages: if locked.name == installed.name: is_installed = True if locked.category == 'dev' and not self.is_dev_mode(): ops.append(Uninstall(locked)) elif locked.optional and locked.name not in extra_packages: # Installed but optional and not requested in extras ops.append(Uninstall(locked)) elif locked.version != installed.version: ops.append(Update(installed, locked)) if not is_installed: # If it's optional and not in required extras # we do not install if locked.optional and locked.name not in extra_packages: continue ops.append(Install(locked)) return ops
def _update_with_new_method(self, version): from poetry.config.config import Config from poetry.core.packages.dependency import Dependency from poetry.core.packages.project_package import ProjectPackage from poetry.installation.installer import Installer from poetry.packages.locker import NullLocker from poetry.repositories.installed_repository import InstalledRepository from poetry.utils.env import EnvManager env = EnvManager.get_system_env() installed = InstalledRepository.load(env) root = ProjectPackage("poetry-updater", "0.0.0") root.python_versions = ".".join(str(c) for c in env.version_info[:3]) root.add_dependency(Dependency("poetry", version.text)) installer = Installer( self.io, env, root, NullLocker(self.data_dir.joinpath("poetry.lock"), {}), self.pool, Config(), installed=installed, ) installer.update(True) installer.run()
def test_load(mocker): mocker.patch( "poetry.utils._compat.metadata.Distribution.discover", return_value=INSTALLED_RESULTS, ) mocker.patch( "poetry.vcs.git.Git.rev_parse", return_value="bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6", ) mocker.patch( "poetry.vcs.git.Git.remote_urls", side_effect=[ { "remote.origin.url": "https://github.com/sdispater/pendulum.git" }, { "remote.origin.url": "[email protected]:sdispater/pendulum.git" }, ], ) mocker.patch("poetry.repositories.installed_repository._CURRENT_VENDOR", str(VENDOR_DIR)) repository = InstalledRepository.load(MockEnv(path=ENV_DIR)) assert len(repository.packages) == 3 cleo = repository.packages[0] assert cleo.name == "cleo" assert cleo.version.text == "0.7.6" assert ( cleo.description == "Cleo allows you to create beautiful and testable command-line interfaces." ) foo = repository.packages[1] assert foo.name == "foo" assert foo.version.text == "0.1.0" pendulum = repository.packages[2] assert pendulum.name == "pendulum" assert pendulum.version.text == "2.0.5" assert pendulum.description == "Python datetimes made easy" assert pendulum.source_type == "git" assert pendulum.source_url == "https://github.com/sdispater/pendulum.git" assert pendulum.source_reference == "bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6" for pkg in repository.packages: assert pkg.name != "attrs"
def test_load(): repository = InstalledRepository.load(MockEnv()) assert len(repository.packages) == 2 cleo = repository.packages[0] assert cleo.name == "cleo" assert cleo.version.text == "0.6.8" pendulum = repository.packages[1] assert pendulum.name == "pendulum" assert pendulum.version.text == "0.0.0" assert pendulum.source_type == "git" assert pendulum.source_url == "https://github.com/sdispater/pendulum.git" assert pendulum.source_reference == "bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6"
def test_env_no_pip(tmp_path: Path, poetry: Poetry, flags: dict[str, bool], packages: set[str]): venv_path = tmp_path / "venv" EnvManager(poetry).build_venv(path=venv_path, flags=flags) env = VirtualEnv(venv_path) installed_repository = InstalledRepository.load(env=env, with_dependencies=True) installed_packages = { package.name for package in installed_repository.packages # workaround for BSD test environments if package.name != "sqlite3" } assert installed_packages == packages
def repository(mocker: MockerFixture, env: MockEnv) -> InstalledRepository: mocker.patch( "poetry.utils._compat.metadata.Distribution.discover", return_value=INSTALLED_RESULTS, ) mocker.patch( "poetry.vcs.git.Git.info", return_value=namedtuple("GitRepoLocalInfo", "origin revision")( origin="https://github.com/sdispater/pendulum.git", revision="bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6", ), ) mocker.patch("poetry.repositories.installed_repository._VENDORS", str(VENDOR_DIR)) return InstalledRepository.load(env)
def test_load_successful_with_invalid_distribution( caplog: LogCaptureFixture, mocker: MockerFixture, env: MockEnv, tmp_dir: str ) -> None: invalid_dist_info = Path(tmp_dir) / "site-packages" / "invalid-0.1.0.dist-info" invalid_dist_info.mkdir(parents=True) mocker.patch( "poetry.utils._compat.metadata.Distribution.discover", return_value=INSTALLED_RESULTS + [metadata.PathDistribution(invalid_dist_info)], ) repository_with_invalid_distribution = InstalledRepository.load(env) assert ( len(repository_with_invalid_distribution.packages) == len(INSTALLED_RESULTS) - 1 ) assert len(caplog.messages) == 1 message = caplog.messages[0] assert message.startswith("Project environment contains an invalid distribution") assert str(invalid_dist_info) in message
def handle(self): from clikit.utils.terminal import Terminal from poetry.io.null_io import NullIO from poetry.puzzle.solver import Solver from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository from poetry.utils.helpers import get_package_version_display_string package = self.argument("package") if self.option("tree"): self.init_styles(self.io) if self.option("outdated"): self._args.set_option("latest", True) include_dev = not self.option("no-dev") 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(self._io, package, locked_repo) break return 0 table = self.table(style="compact") # table.style.line_vc_char = "" locked_packages = locked_repo.packages pool = Pool(ignore_repository_names=True) pool.add_repository(locked_repo) solver = Solver( self.poetry.package, pool=pool, installed=Repository(), locked=locked_repo, io=NullIO(), ) solver.provider.load_deferred(False) with solver.use_environment(self.env): ops = solver.solve() required_locked_packages = set( [op.package for op in ops if not op.skipped]) if self.option("no-dev"): required_locked_packages = [ p for p in locked_packages if p.category == "main" ] 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(self.io, pkg, locked_repo) return 0 rows = [ ["<info>name</>", " : <c1>{}</>".format(pkg.pretty_name)], [ "<info>version</>", " : <b>{}</b>".format(pkg.pretty_version) ], ["<info>description</>", " : {}".format(pkg.description)], ] table.add_rows(rows) table.render(self.io) if pkg.requires: self.line("") self.line("<info>dependencies</info>") for dependency in pkg.requires: self.line(" - <c1>{}</c1> <b>{}</b>".format( dependency.pretty_name, dependency.pretty_constraint)) return 0 show_latest = self.option("latest") show_all = self.option("all") terminal = Terminal() width = terminal.width name_length = version_length = latest_length = 0 latest_packages = {} latest_statuses = {} installed_repo = InstalledRepository.load(self.env) # Computing widths for locked in locked_packages: if locked not in required_locked_packages and not show_all: continue current_length = len(locked.pretty_name) if not self._io.output.supports_ansi(): installed_status = self.get_installed_status( locked, installed_repo) if installed_status == "not-installed": current_length += 4 if show_latest: latest = self.find_latest_package(locked, include_dev) if not latest: latest = locked latest_packages[locked.pretty_name] = latest update_status = latest_statuses[ locked.pretty_name] = self.get_update_status( latest, locked) if not self.option( "outdated") or update_status != "up-to-date": name_length = max(name_length, current_length) version_length = max( version_length, len( get_package_version_display_string( locked, root=self.poetry.file.parent)), ) latest_length = max( latest_length, len( get_package_version_display_string( latest, root=self.poetry.file.parent)), ) else: name_length = max(name_length, current_length) version_length = max( version_length, len( get_package_version_display_string( locked, root=self.poetry.file.parent)), ) 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 = "cyan" name = locked.pretty_name install_marker = "" if locked not in required_locked_packages: 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._io.output.supports_ansi(): # Non installed in non decorated mode install_marker = " (!)" if (show_latest and self.option("outdated") and latest_statuses[locked.pretty_name] == "up-to-date"): continue line = "<fg={}>{:{}}{}</>".format( color, name, name_length - len(install_marker), install_marker) if write_version: line += " <b>{:{}}</b>".format( get_package_version_display_string( locked, root=self.poetry.file.parent), version_length, ) if show_latest: latest = latest_packages[locked.pretty_name] update_status = latest_statuses[locked.pretty_name] if write_latest: color = "green" if update_status == "semver-safe-update": color = "red" elif update_status == "update-possible": color = "yellow" line += " <fg={}>{:{}}</>".format( color, get_package_version_display_string( latest, root=self.poetry.file.parent), latest_length, ) 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 handle(self): from poetry.packages.constraints import ( parse_constraint as parse_generic_constraint, ) 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( not self.option("no-dev")) # 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.env) skipped = [] python = Version.parse(".".join( [str(i) for i in self.env.version_info[:3]])) # Computing widths for locked in locked_packages: python_constraint = locked.python_constraint if not python_constraint.allows( python) or not self.env.is_valid_for_marker(locked.marker): 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 _system_project_handle(self) -> int: from poetry.plugins.application_plugin import ApplicationPlugin from poetry.plugins.plugin import Plugin from poetry.plugins.plugin_manager import PluginManager from poetry.repositories.installed_repository import InstalledRepository from poetry.utils.env import EnvManager from poetry.utils.helpers import canonicalize_name from poetry.utils.helpers import pluralize plugins: dict[str, PluginPackage] = {} system_env = EnvManager.get_system_env(naive=True) entry_points = PluginManager( ApplicationPlugin.group).get_plugin_entry_points( env=system_env) + PluginManager( Plugin.group).get_plugin_entry_points(env=system_env) installed_repository = InstalledRepository.load(system_env, with_dependencies=True) packages_by_name: dict[str, Package] = { pkg.name: pkg for pkg in installed_repository.packages } for entry_point in entry_points: plugin = entry_point.load() assert entry_point.distro is not None package = packages_by_name[canonicalize_name( entry_point.distro.name)] name = package.pretty_name info = plugins.get(name) or PluginPackage(package=package) if issubclass(plugin, ApplicationPlugin): info.application_plugins.append(entry_point) else: info.plugins.append(entry_point) plugins[name] = info for name, info in plugins.items(): package = info.package description = " " + package.description if package.description else "" self.line("") self.line( f" • <c1>{name}</c1> (<c2>{package.version}</c2>){description}" ) provide_line = " " if info.plugins: count = len(info.plugins) provide_line += f" <info>{count}</info> plugin{pluralize(count)}" if info.application_plugins: if info.plugins: provide_line += " and" count = len(info.application_plugins) provide_line += ( f" <info>{count}</info> application plugin{pluralize(count)}" ) self.line(provide_line) if package.requires: self.line("") self.line(" <info>Dependencies</info>") for dependency in package.requires: self.line(f" - {dependency.pretty_name}" f" (<c2>{dependency.pretty_constraint}</c2>)") return 0
def handle(self) -> Optional[int]: from cleo.io.null_io import NullIO from cleo.terminal import Terminal from poetry.puzzle.solver import Solver from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository from poetry.utils.helpers import get_package_version_display_string package = self.argument("package") if self.option("tree"): self.init_styles(self.io) if self.option("outdated"): self._io.input.set_option("latest", True) excluded_groups = [] included_groups = [] only_groups = [] if self.option("no-dev"): self.line_error( "<warning>The `<fg=yellow;options=bold>--no-dev</>` option is" " deprecated, use the `<fg=yellow;options=bold>--without dev</>`" " notation instead.</warning>" ) excluded_groups.append("dev") excluded_groups.extend( [ group.strip() for groups in self.option("without") for group in groups.split(",") ] ) included_groups.extend( [ group.strip() for groups in self.option("with") for group in groups.split(",") ] ) only_groups.extend( [ group.strip() for groups in self.option("only") for group in groups.split(",") ] ) if self.option("default"): only_groups.append("default") locked_repo = self.poetry.locker.locked_repository(True) if only_groups: root = self.poetry.package.with_dependency_groups(only_groups, only=True) else: root = self.poetry.package.with_dependency_groups( included_groups ).without_dependency_groups(excluded_groups) # Show tree view if requested if self.option("tree") and not package: requires = root.all_requires packages = locked_repo.packages for pkg in packages: for require in requires: if pkg.name == require.name: self.display_package_tree(self._io, pkg, locked_repo) break return 0 table = self.table(style="compact") locked_packages = locked_repo.packages pool = Pool(ignore_repository_names=True) pool.add_repository(locked_repo) solver = Solver( root, pool=pool, installed=Repository(), locked=locked_repo, io=NullIO(), ) solver.provider.load_deferred(False) with solver.use_environment(self.env): ops = solver.solve().calculate_operations() required_locked_packages = {op.package for op in ops if not op.skipped} if package: pkg = None for locked in locked_packages: if package.lower() == locked.name: pkg = locked break if not pkg: raise ValueError(f"Package {package} not found") if self.option("tree"): self.display_package_tree(self.io, pkg, locked_repo) return 0 required_by = {} for locked in locked_packages: dependencies = {d.name: d.pretty_constraint for d in locked.requires} if pkg.name in dependencies: required_by[locked.pretty_name] = dependencies[pkg.name] rows = [ ["<info>name</>", f" : <c1>{pkg.pretty_name}</>"], ["<info>version</>", f" : <b>{pkg.pretty_version}</b>"], ["<info>description</>", f" : {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( f" - <c1>{dependency.pretty_name}</c1>" f" <b>{dependency.pretty_constraint}</b>" ) if required_by: self.line("") self.line("<info>required by</info>") for parent, requires_version in required_by.items(): self.line(f" - <c1>{parent}</c1> <b>{requires_version}</b>") return 0 show_latest = self.option("latest") show_all = self.option("all") terminal = Terminal() width = terminal.width name_length = version_length = latest_length = 0 latest_packages = {} latest_statuses = {} installed_repo = InstalledRepository.load(self.env) # Computing widths for locked in locked_packages: if locked not in required_locked_packages and not show_all: continue current_length = len(locked.pretty_name) if not self._io.output.is_decorated(): installed_status = self.get_installed_status(locked, installed_repo) if installed_status == "not-installed": current_length += 4 if show_latest: latest = self.find_latest_package(locked, root) if not latest: latest = locked latest_packages[locked.pretty_name] = latest update_status = latest_statuses[ locked.pretty_name ] = self.get_update_status(latest, locked) if not self.option("outdated") or update_status != "up-to-date": name_length = max(name_length, current_length) version_length = max( version_length, len( get_package_version_display_string( locked, root=self.poetry.file.parent ) ), ) latest_length = max( latest_length, len( get_package_version_display_string( latest, root=self.poetry.file.parent ) ), ) else: name_length = max(name_length, current_length) version_length = max( version_length, len( get_package_version_display_string( locked, root=self.poetry.file.parent ) ), ) 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 = "cyan" name = locked.pretty_name install_marker = "" if locked not in required_locked_packages: 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._io.output.is_decorated(): # Non installed in non decorated mode install_marker = " (!)" if ( show_latest and self.option("outdated") and latest_statuses[locked.pretty_name] == "up-to-date" ): continue line = ( f"<fg={color}>" f"{name:{name_length - len(install_marker)}}{install_marker}</>" ) if write_version: version = get_package_version_display_string( locked, root=self.poetry.file.parent ) line += f" <b>{version:{version_length}}</b>" if show_latest: latest = latest_packages[locked.pretty_name] update_status = latest_statuses[locked.pretty_name] if write_latest: color = "green" if update_status == "semver-safe-update": color = "red" elif update_status == "update-possible": color = "yellow" version = get_package_version_display_string( latest, root=self.poetry.file.parent ) line += f" <fg={color}>{version:{latest_length}}</>" 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) return None
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 handle(self) -> int: from pathlib import Path import tomlkit from cleo.io.inputs.string_input import StringInput from cleo.io.io import IO from poetry.core.pyproject.toml import PyProjectTOML from poetry.core.semver.helpers import parse_constraint from poetry.factory import Factory from poetry.packages.project_package import ProjectPackage from poetry.repositories.installed_repository import InstalledRepository from poetry.utils.env import EnvManager plugins = self.argument("plugins") # Plugins should be installed in the system env to be globally available system_env = EnvManager.get_system_env(naive=True) env_dir = Path(os.getenv("POETRY_HOME") or system_env.path) # We check for the plugins existence first. if env_dir.joinpath("pyproject.toml").exists(): pyproject = tomlkit.loads( env_dir.joinpath("pyproject.toml").read_text(encoding="utf-8")) poetry_content = pyproject["tool"]["poetry"] existing_packages = self.get_existing_packages_from_input( plugins, poetry_content, "dependencies") if existing_packages: self.notify_about_existing_packages(existing_packages) plugins = [ plugin for plugin in plugins if plugin not in existing_packages ] if not plugins: return 0 plugins = self._determine_requirements(plugins) # We retrieve the packages installed in the system environment. # We assume that this environment will be a self contained virtual environment # built by the official installer or by pipx. # If not, it might lead to side effects since other installed packages # might not be required by Poetry but still taken into account when resolving dependencies. installed_repository = InstalledRepository.load(system_env, with_dependencies=True) root_package = None for package in installed_repository.packages: if package.name == "poetry": root_package = ProjectPackage(package.name, package.version) for dependency in package.requires: root_package.add_dependency(dependency) break root_package.python_versions = ".".join( str(v) for v in system_env.version_info[:3]) # We create a `pyproject.toml` file based on all the information # we have about the current environment. if not env_dir.joinpath("pyproject.toml").exists(): Factory.create_pyproject_from_package(root_package, env_dir) # We add the plugins to the dependencies section of the previously # created `pyproject.toml` file pyproject = PyProjectTOML(env_dir.joinpath("pyproject.toml")) poetry_content = pyproject.poetry_config poetry_dependency_section = poetry_content["dependencies"] plugin_names = [] for plugin in plugins: if "version" in plugin: # Validate version constraint parse_constraint(plugin["version"]) constraint = tomlkit.inline_table() for name, value in plugin.items(): if name == "name": continue constraint[name] = value if len(constraint) == 1 and "version" in constraint: constraint = constraint["version"] poetry_dependency_section[plugin["name"]] = constraint plugin_names.append(plugin["name"]) pyproject.save() # From this point forward, all the logic will be deferred to # the update command, by using the previously created `pyproject.toml` # file. application = cast(Application, self.application) update_command: UpdateCommand = cast(UpdateCommand, application.find("update")) # We won't go through the event dispatching done by the application # so we need to configure the command manually update_command.set_poetry(Factory().create_poetry(env_dir)) update_command.set_env(system_env) application._configure_installer(update_command, self._io) argv = ["update"] + plugin_names if self.option("dry-run"): argv.append("--dry-run") return update_command.run( IO( StringInput(" ".join(argv)), self._io.output, self._io.error_output, ))
def _get_installed(self): # type: () -> InstalledRepository return InstalledRepository.load(self._venv)
def _get_installed(self): # type: () -> InstalledRepository return InstalledRepository.load(self._env)
def handle(self) -> int: from poetry.plugins.application_plugin import ApplicationPlugin from poetry.plugins.plugin_manager import PluginManager from poetry.repositories.installed_repository import InstalledRepository from poetry.utils.env import EnvManager from poetry.utils.helpers import canonicalize_name plugins: DefaultDict[str, Dict[str, Union[ "Package", List[str]]]] = defaultdict(lambda: { "package": None, "plugins": [], "application_plugins": [], }) entry_points = ( PluginManager("application.plugin").get_plugin_entry_points() + PluginManager("plugin").get_plugin_entry_points()) system_env = EnvManager.get_system_env(naive=True) installed_repository = InstalledRepository.load(system_env, with_dependencies=True) packages_by_name = { pkg.name: pkg for pkg in installed_repository.packages } for entry_point in entry_points: plugin = entry_point.load() category = "plugins" if issubclass(plugin, ApplicationPlugin): category = "application_plugins" package = packages_by_name[canonicalize_name(entry_point.name)] plugins[package.pretty_name]["package"] = package plugins[package.pretty_name][category].append(entry_point) for name, info in plugins.items(): package = info["package"] self.line("") self.line(" • <c1>{}</c1> (<c2>{}</c2>){}".format( name, package.version, " " + package.description if package.description else "", )) provide_line = " " if info["plugins"]: provide_line += " <info>{}</info> plugin{}".format( len(info["plugins"]), "s" if len(info["plugins"]) > 1 else "") if info["application_plugins"]: if info["plugins"]: provide_line += " and" provide_line += " <info>{}</info> application plugin{}".format( len(info["application_plugins"]), "s" if len(info["application_plugins"]) > 1 else "", ) self.line(provide_line) if package.requires: self.line("") self.line(" <info>Dependencies</info>") for dependency in package.requires: self.line(" - {} (<c2>{}</c2>)".format( dependency.pretty_name, dependency.pretty_constraint)) return 0
def handle(self) -> int: from cleo.io.null_io import NullIO from cleo.terminal import Terminal from poetry.puzzle.solver import Solver from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.pool import Pool from poetry.utils.helpers import get_package_version_display_string package = self.argument("package") if self.option("tree"): self.init_styles(self.io) if self.option("why"): if self.option("tree") and package is None: self.line_error( "<error>Error: --why requires a package when combined with" " --tree.</error>") return 1 if not self.option("tree") and package: self.line_error( "<error>Error: --why cannot be used without --tree when displaying" " a single package.</error>") return 1 if self.option("outdated"): self.io.input.set_option("latest", True) if not self.poetry.locker.is_locked(): self.line_error( "<error>Error: poetry.lock not found. Run `poetry lock` to create" " it.</error>") return 1 locked_repo = self.poetry.locker.locked_repository() root = self.project_with_activated_groups_only() # Show tree view if requested if self.option("tree") and package is None: requires = root.all_requires packages = locked_repo.packages for p in packages: for require in requires: if p.name == require.name: self.display_package_tree(self.io, p, packages) break return 0 table = self.table(style="compact") locked_packages = locked_repo.packages pool = Pool(ignore_repository_names=True) pool.add_repository(locked_repo) solver = Solver( root, pool=pool, installed=[], locked=locked_packages, io=NullIO(), ) solver.provider.load_deferred(False) with solver.use_environment(self.env): ops = solver.solve().calculate_operations() required_locked_packages = {op.package for op in ops if not op.skipped} if package: pkg = None for locked in locked_packages: if canonicalize_name(package) == locked.name: pkg = locked break if not pkg: raise ValueError(f"Package {package} not found") required_by = reverse_deps(pkg, locked_repo) if self.option("tree"): if self.option("why"): # The default case if there's no reverse dependencies is to query # the subtree for pkg but if any rev-deps exist we'll query for each # of them in turn packages = [pkg] if required_by: packages = [ p for p in locked_packages for r in required_by.keys() if p.name == r ] else: # if no rev-deps exist we'll make this clear as it can otherwise # look very odd for packages that also have no or few direct # dependencies self.io.write_line( f"Package {package} is a direct dependency.") for p in packages: self.display_package_tree(self.io, p, locked_packages, why_package=pkg) else: self.display_package_tree(self.io, pkg, locked_packages) return 0 rows = [ ["<info>name</>", f" : <c1>{pkg.pretty_name}</>"], ["<info>version</>", f" : <b>{pkg.pretty_version}</b>"], ["<info>description</>", f" : {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(f" - <c1>{dependency.pretty_name}</c1>" f" <b>{dependency.pretty_constraint}</b>") if required_by: self.line("") self.line("<info>required by</info>") for parent, requires_version in required_by.items(): self.line( f" - <c1>{parent}</c1> <b>{requires_version}</b>") return 0 show_latest = self.option("latest") show_all = self.option("all") terminal = Terminal() width = terminal.width name_length = version_length = latest_length = required_by_length = 0 latest_packages = {} latest_statuses = {} installed_repo = InstalledRepository.load(self.env) # Computing widths for locked in locked_packages: if locked not in required_locked_packages and not show_all: continue current_length = len(locked.pretty_name) if not self.io.output.is_decorated(): installed_status = self.get_installed_status( locked, installed_repo.packages) if installed_status == "not-installed": current_length += 4 if show_latest: latest = self.find_latest_package(locked, root) if not latest: latest = locked latest_packages[locked.pretty_name] = latest update_status = latest_statuses[ locked.pretty_name] = self.get_update_status( latest, locked) if not self.option( "outdated") or update_status != "up-to-date": name_length = max(name_length, current_length) version_length = max( version_length, len( get_package_version_display_string( locked, root=self.poetry.file.parent)), ) latest_length = max( latest_length, len( get_package_version_display_string( latest, root=self.poetry.file.parent)), ) if self.option("why"): required_by = reverse_deps(locked, locked_repo) required_by_length = max( required_by_length, len(" from " + ",".join(required_by.keys())), ) else: name_length = max(name_length, current_length) version_length = max( version_length, len( get_package_version_display_string( locked, root=self.poetry.file.parent)), ) if self.option("why"): required_by = reverse_deps(locked, locked_repo) required_by_length = max( required_by_length, len(" from " + ",".join(required_by.keys()))) write_version = name_length + version_length + 3 <= width write_latest = name_length + version_length + latest_length + 3 <= width why_end_column = (name_length + version_length + latest_length + required_by_length) write_why = self.option("why") and (why_end_column + 3) <= width write_description = (why_end_column + 24) <= width for locked in locked_packages: color = "cyan" name = locked.pretty_name install_marker = "" if locked not in required_locked_packages: if not show_all: continue color = "black;options=bold" else: installed_status = self.get_installed_status( locked, installed_repo.packages) if installed_status == "not-installed": color = "red" if not self.io.output.is_decorated(): # Non installed in non decorated mode install_marker = " (!)" if (show_latest and self.option("outdated") and latest_statuses[locked.pretty_name] == "up-to-date"): continue line = ( f"<fg={color}>" f"{name:{name_length - len(install_marker)}}{install_marker}</>" ) if write_version: version = get_package_version_display_string( locked, root=self.poetry.file.parent) line += f" <b>{version:{version_length}}</b>" if show_latest: latest = latest_packages[locked.pretty_name] update_status = latest_statuses[locked.pretty_name] if write_latest: color = "green" if update_status == "semver-safe-update": color = "red" elif update_status == "update-possible": color = "yellow" version = get_package_version_display_string( latest, root=self.poetry.file.parent) line += f" <fg={color}>{version:{latest_length}}</>" if write_why: required_by = reverse_deps(locked, locked_repo) if required_by: content = ",".join(required_by.keys()) # subtract 6 for ' from ' line += f" from {content:{required_by_length - 6}}" else: line += " " * required_by_length if write_description: description = locked.description remaining = (width - name_length - version_length - required_by_length - 4) if show_latest: remaining -= latest_length if len(locked.description) > remaining: description = description[:remaining - 3] + "..." line += " " + description self.line(line) return 0
def _get_installed(self) -> InstalledRepository: return InstalledRepository.load(self._env)
def repository(mocker: MockerFixture, env: MockEnv) -> InstalledRepository: mocker.patch( "poetry.utils._compat.metadata.Distribution.discover", return_value=INSTALLED_RESULTS, ) return InstalledRepository.load(env)
def handle(self) -> int: from poetry.plugins.application_plugin import ApplicationPlugin from poetry.plugins.plugin import Plugin from poetry.plugins.plugin_manager import PluginManager from poetry.repositories.installed_repository import InstalledRepository from poetry.utils.env import EnvManager from poetry.utils.helpers import canonicalize_name from poetry.utils.helpers import pluralize plugins: dict[str, dict[str, Any]] = defaultdict(lambda: { "package": None, "plugins": [], "application_plugins": [], }) entry_points = ( PluginManager(ApplicationPlugin.group).get_plugin_entry_points() + PluginManager(Plugin.group).get_plugin_entry_points()) system_env = EnvManager.get_system_env(naive=True) installed_repository = InstalledRepository.load(system_env, with_dependencies=True) packages_by_name = { pkg.name: pkg for pkg in installed_repository.packages } for entry_point in entry_points: plugin = entry_point.load() category = "plugins" if issubclass(plugin, ApplicationPlugin): category = "application_plugins" package = packages_by_name[canonicalize_name( entry_point.distro.name)] plugins[package.pretty_name]["package"] = package plugins[package.pretty_name][category].append(entry_point) for name, info in plugins.items(): package = info["package"] description = " " + package.description if package.description else "" self.line("") self.line( f" • <c1>{name}</c1> (<c2>{package.version}</c2>){description}" ) provide_line = " " if info["plugins"]: count = len(info["plugins"]) provide_line += f" <info>{count}</info> plugin{pluralize(count)}" if info["application_plugins"]: if info["plugins"]: provide_line += " and" count = len(info["application_plugins"]) provide_line += ( f" <info>{count}</info> application plugin{pluralize(count)}" ) self.line(provide_line) if package.requires: self.line("") self.line(" <info>Dependencies</info>") for dependency in package.requires: self.line(f" - {dependency.pretty_name}" f" (<c2>{dependency.pretty_constraint}</c2>)") return 0
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 += ' {:{}}'.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)