def test_package_authors(): package = Package("foo", "0.1.0") package.authors.append("Sébastien Eustace <*****@*****.**>") assert package.author_name == "Sébastien Eustace" assert package.author_email == "*****@*****.**" package.authors.insert(0, "John Doe") assert package.author_name == "John Doe" assert package.author_email is None
def package_git(): package = Package( "demo", "1.0.0", source_type="git", source_url="[email protected]:demo/demo.git", source_reference="master", ) return package
def find_packages(self, dependency: Dependency) -> list[Package]: """ Find packages on the remote server. """ constraint, allow_prereleases = self._get_constraints_from_dependency( dependency ) try: info = self.get_package_info(dependency.name) except PackageNotFound: self._log( f"No packages found for {dependency.name} {constraint!s}", level="debug", ) return [] packages = [] ignored_pre_release_packages = [] for version, release in info["releases"].items(): if not release: # Bad release self._log( f"No release information found for {dependency.name}-{version}," " skipping", level="debug", ) continue try: package = Package(info["info"]["name"], version) except InvalidVersion: self._log( f'Unable to parse version "{version}" for the' f" {dependency.name} package, skipping", level="debug", ) continue if package.is_prerelease() and not allow_prereleases: if constraint.is_any(): # we need this when all versions of the package are pre-releases ignored_pre_release_packages.append(package) continue if constraint.allows(package.version): packages.append(package) self._log( f"{len(packages)} packages found for {dependency.name} {constraint!s}", level="debug", ) return packages or ignored_pre_release_packages
def test_adding_a_plugin_can_update_poetry_dependencies_if_needed( app, repo, tester, env, installed): poetry_package = Package("poetry", "1.2.0") poetry_package.add_dependency( Factory.create_dependency("tomlkit", "^0.7.0")) plugin_package = Package("poetry-plugin", "1.2.3") plugin_package.add_dependency( Factory.create_dependency("tomlkit", "^0.7.2")) installed.add_package(poetry_package) installed.add_package(Package("tomlkit", "0.7.1")) repo.add_package(plugin_package) repo.add_package(Package("tomlkit", "0.7.1")) repo.add_package(Package("tomlkit", "0.7.2")) tester.execute("poetry-plugin") expected = """\ Using version ^1.2.3 for poetry-plugin Updating dependencies Resolving dependencies... Writing lock file Package operations: 1 install, 1 update, 0 removals • Updating tomlkit (0.7.1 -> 0.7.2) • Installing poetry-plugin (1.2.3) """ assert tester.io.fetch_output() == expected update_command = app.find("update") assert update_command.poetry.file.parent == env.path assert update_command.poetry.locker.lock.parent == env.path assert update_command.poetry.locker.lock.exists() content = update_command.poetry.file.read()["tool"]["poetry"] assert "poetry-plugin" in content["dependencies"] assert content["dependencies"]["poetry-plugin"] == "^1.2.3"
def test_remove_does_not_live_empty_groups( tester: CommandTester, app: PoetryTestApplication, repo: TestRepository, command_tester_factory: CommandTesterFactory, installed: Repository, ): """ Empty groups are automatically discarded after package removal. """ installed.add_package(Package("foo", "2.0.0")) repo.add_package(Package("foo", "2.0.0")) repo.add_package(Package("baz", "1.0.0")) content = app.poetry.file.read() groups_content = tomlkit.parse("""\ [tool.poetry.group.bar.dependencies] foo = "^2.0.0" baz = "^1.0.0" """) content["tool"]["poetry"]["dependencies"]["foo"] = "^2.0.0" content["tool"]["poetry"].value._insert_after( "dependencies", "group", groups_content["tool"]["poetry"]["group"]) app.poetry.file.write(content) app.poetry.package.add_dependency( Factory.create_dependency("foo", "^2.0.0")) app.poetry.package.add_dependency( Factory.create_dependency("foo", "^2.0.0", groups=["bar"])) app.poetry.package.add_dependency( Factory.create_dependency("baz", "^1.0.0", groups=["bar"])) tester.execute("foo baz --group bar") content = app.poetry.file.read()["tool"]["poetry"] assert "foo" in content["dependencies"] assert "foo" not in content["group"]["bar"]["dependencies"] assert "baz" not in content["group"]["bar"]["dependencies"] assert "[tool.poetry.group.bar]" not in content.as_string() assert "[tool.poetry.group]" not in content.as_string()
def test_requirement_source_type_url(): installer = PipInstaller(NullEnv(), NullIO(), Pool()) foo = Package("foo", "0.0.0") foo.source_type = "url" foo.source_url = "https://somehwere.com/releases/foo-1.0.0.tar.gz" result = installer.requirement(foo, formatted=True) expected = "{}#egg={}".format(foo.source_url, foo.name) assert expected == result
def add_to_repo(repository, name, version, deps=None, python=None): package = Package(name, version) if python: package.python_versions = python if deps: for dep_name, dep_constraint in deps.items(): package.add_dependency( Factory.create_dependency(dep_name, dep_constraint)) repository.add_package(package)
def test_package_authors_invalid(): package = Package("foo", "0.1.0") package.authors.insert(0, "<John Doe") with pytest.raises(ValueError) as e: package.author_name assert ( str(e.value) == "Invalid author string. Must be in the format: John Smith <*****@*****.**>" )
def test_package_add_dependency_vcs_groups(groups: list[str], f: Factory) -> None: package = Package("foo", "0.1.0") dependency = package.add_dependency( f.create_dependency( "poetry", {"git": "https://github.com/python-poetry/poetry.git"}, groups=groups, )) assert dependency.groups == frozenset(groups)
def test_execute_executes_a_batch_of_operations(config, pool, io, tmp_dir, mock_file_downloads): config = Config() config.merge({"cache-dir": tmp_dir}) env = MockEnv(path=Path(tmp_dir)) executor = Executor(env, pool, config, io) file_package = Package("demo", "0.1.0") file_package.source_type = "file" file_package.source_url = str( Path(__file__).parent.parent.joinpath( "fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl").resolve( )) directory_package = Package("simple-project", "1.2.3") directory_package.source_type = "directory" directory_package.source_url = str( Path(__file__).parent.parent.joinpath( "fixtures/simple_project").resolve()) git_package = Package("demo", "0.1.0") git_package.source_type = "git" git_package.source_reference = "master" git_package.source_url = "https://github.com/demo/demo.git" assert 0 == executor.execute([ Install(Package("pytest", "3.5.2")), Uninstall(Package("attrs", "17.4.0")), Update(Package("requests", "2.18.3"), Package("requests", "2.18.4")), Uninstall(Package("clikit", "0.2.3")).skip("Not currently installed"), Install(file_package), Install(directory_package), Install(git_package), ]) expected = """ Package operations: 4 installs, 1 update, 1 removal • Installing pytest (3.5.2) • Removing attrs (17.4.0) • Updating requests (2.18.3 -> 2.18.4) • Installing demo (0.1.0 {}) • Installing simple-project (1.2.3 {}) • Installing demo (0.1.0 master) """.format(file_package.source_url, directory_package.source_url) expected = set(expected.splitlines()) output = set(io.fetch_output().splitlines()) assert expected == output assert 5 == len(env.executed)
def test_chooser_chooses_system_specific_wheel_link_if_available( mock_pypi, mock_legacy, source_type, pool ): env = MockEnv( supported_tags=[Tag("cp37", "cp37m", "win32"), Tag("py3", "none", "any")] ) chooser = Chooser(pool, env) package = Package("pyyaml", "3.13.0") if source_type == "legacy": package = Package( package.name, package.version.text, source_type="legacy", source_reference="foo", source_url="https://foo.bar/simple/", ) link = chooser.choose_for(package) assert "PyYAML-3.13-cp37-cp37m-win32.whl" == link.filename
def package_with_groups(): package = Package("foo", "1.2.3") optional_group = DependencyGroup("optional", optional=True) optional_group.add_dependency(Factory.create_dependency("bam", "^3.0.0")) package.add_dependency(Factory.create_dependency("bar", "^1.0.0")) package.add_dependency(Factory.create_dependency("baz", "^1.1.0")) package.add_dependency(Factory.create_dependency("bim", "^2.0.0", groups=["dev"])) package.add_dependency_group(optional_group) return package
def test_add_with_constraint( tester: CommandTester, repo: TestRepository, ): repo.add_package(Package("poetry-plugin", "0.1.0")) repo.add_package(Package("poetry-plugin", "0.2.0")) tester.execute("poetry-plugin@^0.2.0") expected = """ Updating dependencies Resolving dependencies... Writing lock file Package operations: 1 install, 0 updates, 0 removals • Installing poetry-plugin (0.2.0) """ assert_plugin_add_result(tester, expected, "^0.2.0")
def find_packages(self, dependency: Dependency) -> list[Package]: packages = [] constraint, allow_prereleases = self._get_constraints_from_dependency( dependency) key = dependency.name if not constraint.is_any(): key = f"{key}:{constraint!s}" ignored_pre_release_versions = [] if self._cache.store("matches").has(key): versions = self._cache.store("matches").get(key) else: page = self._get_page(f"/{dependency.name.replace('.', '-')}/") if page is None: return [] versions = [] for version in page.versions(dependency.name): if version.is_unstable() and not allow_prereleases: if constraint.is_any(): # we need this when all versions of the package are pre-releases ignored_pre_release_versions.append(version) continue if constraint.allows(version): versions.append(version) self._cache.store("matches").put(key, versions, 5) for package_versions in (versions, ignored_pre_release_versions): for version in package_versions: package = Package( dependency.name, version, source_type="legacy", source_reference=self.name, source_url=self._url, ) packages.append(package) self._log( f"{len(packages)} packages found for {dependency.name} {constraint!s}", level="debug", ) if packages or not constraint.is_any(): # we have matching packages, or constraint is not (*) break return packages
def test_package_resolved_reference_is_relevant_for_equality_only_if_present_for_both_packages( ): a1 = Package( "a", "0.1.0", source_type="git", source_url="https://foo.bar", source_reference="master", source_resolved_reference="c01b317af582501c5ba07b23d5bef3fbada2d4ef", ) a2 = Package( a1.name, a1.version, source_type="git", source_url="https://foo.bar", source_reference="master", source_resolved_reference="a444731cd243cb5cd04e4d5fb81f86e1fecf8a00", ) a3 = Package( a1.name, a1.version, source_type="git", source_url="https://foo.bar", source_reference="master", source_resolved_reference="c01b317af582501c5ba07b23d5bef3fbada2d4ef", ) a4 = Package( a1.name, a1.version, source_type="git", source_url="https://foo.bar", source_reference="master", ) assert a1 == a1 assert a1 == a3 assert a1 != a2 assert a2 != a3 assert a1 == a4 assert a2 == a4
def test_add_existing_plugin_warns_about_no_operation(app, repo, tester, env, installed): env.path.joinpath("pyproject.toml").write_text( """\ [tool.poetry] name = "poetry" version = "1.2.0" description = "Python dependency management and packaging made easy." authors = [ "Sébastien Eustace <*****@*****.**>" ] [tool.poetry.dependencies] python = "^3.6" poetry-plugin = "^1.2.3" """, encoding="utf-8", ) installed.add_package(Package("poetry-plugin", "1.2.3")) repo.add_package(Package("poetry-plugin", "1.2.3")) tester.execute("poetry-plugin") expected = """\ The following plugins are already present in the pyproject.toml file and will be skipped: • poetry-plugin If you want to update it to the latest compatible version, you can use `poetry plugin update package`. If you prefer to upgrade it to the latest available version, you can use `poetry plugin add package@latest`. """ assert tester.io.fetch_output() == expected update_command = app.find("update") # The update command should not have been called assert update_command.poetry.file.parent != env.path
def test_it_should_calculate_operations_in_correct_order(): transaction = Transaction( [Package("a", "1.0.0"), Package("b", "2.0.0"), Package("c", "3.0.0")], [ (Package("a", "1.0.0"), 1), (Package("b", "2.1.0"), 2), (Package("d", "4.0.0"), 0), ], ) check_operations( transaction.calculate_operations(), [ {"job": "install", "package": Package("b", "2.1.0")}, {"job": "install", "package": Package("a", "1.0.0")}, {"job": "install", "package": Package("d", "4.0.0")}, ], )
def test_remove_command_should_not_write_changes_upon_installer_errors( tester, app, repo, command_tester_factory, mocker): repo.add_package(Package("foo", "2.0.0")) command_tester_factory("add").execute("foo") mocker.patch("poetry.installation.installer.Installer.run", return_value=1) original_content = app.poetry.file.read().as_string() tester.execute("foo") assert app.poetry.file.read().as_string() == original_content
def test_add_existing_plugin_updates_if_requested(app, repo, tester, env, installed, mocker): env.path.joinpath("pyproject.toml").write_text( """\ [tool.poetry] name = "poetry" version = "1.2.0" description = "Python dependency management and packaging made easy." authors = [ "Sébastien Eustace <*****@*****.**>" ] [tool.poetry.dependencies] python = "^3.6" poetry-plugin = "^1.2.3" """, encoding="utf-8", ) installed.add_package(Package("poetry-plugin", "1.2.3")) repo.add_package(Package("poetry-plugin", "1.2.3")) repo.add_package(Package("poetry-plugin", "2.3.4")) tester.execute("poetry-plugin@latest") expected = """\ Using version ^2.3.4 for poetry-plugin Updating dependencies Resolving dependencies... Writing lock file Package operations: 0 installs, 1 update, 0 removals • Updating poetry-plugin (1.2.3 -> 2.3.4) """ assert_plugin_add_result(tester, app, env, expected, "^2.3.4")
def test_chooser_chooses_sdist_if_no_compatible_wheel_link_is_available( env, mock_pypi, mock_legacy, source_type, pool, ): chooser = Chooser(pool, env) package = Package("pyyaml", "3.13.0") if source_type == "legacy": package = Package( package.name, package.version.text, source_type="legacy", source_reference="foo", source_url="https://foo.bar/simple/", ) link = chooser.choose_for(package) assert "PyYAML-3.13.tar.gz" == link.filename
def test_chooser_chooses_universal_wheel_link_if_available( env, mock_pypi, mock_legacy, source_type, pool): chooser = Chooser(pool, env) package = Package("pytest", "3.5.0") if source_type == "legacy": package.source_type = "legacy" package.source_reference = "foo" package.source_url = "https://foo.bar/simple/" link = chooser.choose_for(package) assert "pytest-3.5.0-py2.py3-none-any.whl" == link.filename
def test_executor_should_write_pep610_url_references_for_directories( tmp_venv, pool, config, io ): url = Path(__file__).parent.parent.joinpath("fixtures/simple_project").resolve() package = Package( "simple-project", "1.2.3", source_type="directory", source_url=url.as_posix() ) executor = Executor(tmp_venv, pool, config, io) executor.execute([Install(package)]) verify_installed_distribution( tmp_venv, package, {"dir_info": {}, "url": url.as_uri()} )
def create_package_from_pep610(cls, distribution: metadata.Distribution) -> Package: path = Path(str(distribution._path)) # type: ignore[attr-defined] source_type = None source_url = None source_reference = None source_resolved_reference = None develop = False url_reference = json.loads( path.joinpath("direct_url.json").read_text(encoding="utf-8") ) if "archive_info" in url_reference: # File or URL distribution if url_reference["url"].startswith("file:"): # File distribution source_type = "file" source_url = url_to_path(url_reference["url"]).as_posix() else: # URL distribution source_type = "url" source_url = url_reference["url"] elif "dir_info" in url_reference: # Directory distribution source_type = "directory" source_url = url_to_path(url_reference["url"]).as_posix() develop = url_reference["dir_info"].get("editable", False) elif "vcs_info" in url_reference: # VCS distribution source_type = url_reference["vcs_info"]["vcs"] source_url = url_reference["url"] source_resolved_reference = url_reference["vcs_info"]["commit_id"] source_reference = url_reference["vcs_info"].get( "requested_revision", source_resolved_reference ) package = Package( distribution.metadata["name"], distribution.metadata["version"], source_type=source_type, source_url=source_url, source_reference=source_reference, source_resolved_reference=source_resolved_reference, develop=develop, ) package.description = distribution.metadata.get( # type: ignore[attr-defined] "summary", "", ) return package
def test_adding_a_plugin_can_update_poetry_dependencies_if_needed( app: "PoetryTestApplication", repo: "TestRepository", tester: "CommandTester", env: "MockEnv", installed: "Repository", ): poetry_package = Package("poetry", "1.2.0") poetry_package.add_dependency( Factory.create_dependency("tomlkit", "^0.7.0")) plugin_package = Package("poetry-plugin", "1.2.3") plugin_package.add_dependency( Factory.create_dependency("tomlkit", "^0.7.2")) installed.add_package(poetry_package) installed.add_package(Package("tomlkit", "0.7.1")) repo.add_package(plugin_package) repo.add_package(Package("tomlkit", "0.7.1")) repo.add_package(Package("tomlkit", "0.7.2")) tester.execute("poetry-plugin") expected = """\ Using version ^1.2.3 for poetry-plugin Updating dependencies Resolving dependencies... Writing lock file Package operations: 1 install, 1 update, 0 removals • Updating tomlkit (0.7.1 -> 0.7.2) • Installing poetry-plugin (1.2.3) """ assert_plugin_add_result(tester, app, env, expected, "^1.2.3")
def test_self_update_can_update_from_recommended_installation( tester: CommandTester, repo: TestRepository, installed: TestRepository, ): new_version = Version.parse(__version__).next_minor().text old_poetry = Package("poetry", __version__) old_poetry.add_dependency(Factory.create_dependency("cleo", "^0.8.2")) new_poetry = Package("poetry", new_version) new_poetry.add_dependency(Factory.create_dependency("cleo", "^1.0.0")) installed.add_package(old_poetry) installed.add_package(Package("cleo", "0.8.2")) repo.add_package(new_poetry) repo.add_package(Package("cleo", "1.0.0")) tester.execute() expected_output = f"""\ Updating Poetry version ... Using version ^{new_version} for poetry Updating dependencies Resolving dependencies... Writing lock file Package operations: 0 installs, 2 updates, 0 removals • Updating cleo (0.8.2 -> 1.0.0) • Updating poetry ({__version__} -> {new_version}) """ assert tester.io.fetch_output() == expected_output
def test_add_existing_plugin_warns_about_no_operation( tester: CommandTester, repo: TestRepository, installed: TestRepository, ): SelfCommand.get_default_system_pyproject_file().write_text( f"""\ [tool.poetry] name = "poetry-instance" version = "1.2.0" description = "Python dependency management and packaging made easy." authors = [] [tool.poetry.dependencies] python = "^3.6" [tool.poetry.group.{SelfCommand.ADDITIONAL_PACKAGE_GROUP}.dependencies] poetry-plugin = "^1.2.3" """, encoding="utf-8", ) installed.add_package(Package("poetry-plugin", "1.2.3")) repo.add_package(Package("poetry-plugin", "1.2.3")) tester.execute("poetry-plugin") expected = f"""\ The following packages are already present in the pyproject.toml and will be\ skipped: • poetry-plugin {tester.command._hint_update_packages} Nothing to add. """ assert tester.io.fetch_output() == expected
def test_requirement_source_type_url(): installer = PipInstaller(NullEnv(), NullIO(), Pool()) foo = Package( "foo", "0.0.0", source_type="url", source_url="https://somewhere.com/releases/foo-1.0.0.tar.gz", ) result = installer.requirement(foo, formatted=True) expected = f"{foo.source_url}#egg={foo.name}" assert result == expected
def test_install_with_non_pypi_default_repository(pool, installer): default = LegacyRepository("default", "https://default.com") another = LegacyRepository("another", "https://another.com") pool.add_repository(default, default=True) pool.add_repository(another) foo = Package( "foo", "0.0.0", source_type="legacy", source_reference=default.name, source_url=default.url, ) bar = Package( "bar", "0.1.0", source_type="legacy", source_reference=another.name, source_url=another.url, ) installer.install(foo) installer.install(bar)
def test_executor_should_write_pep610_url_references_for_files( tmp_venv, pool, config, io): url = (Path(__file__).parent.parent.joinpath( "fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl").resolve()) package = Package("demo", "0.1.0", source_type="file", source_url=url.as_posix()) executor = Executor(tmp_venv, pool, config, io) executor.execute([Install(package)]) verify_installed_distribution(tmp_venv, package, { "archive_info": {}, "url": url.as_uri() })
def test_throw_on_invalid_package(): invalid = next( filter( lambda e: str(e._path).endswith("invalid-0.0.1.dist-info"), sorted( metadata.distributions(path=[str(SITE_PURELIB)]), key=lambda d: str(d._path), ), ) ) name = invalid.metadata["name"] version = invalid.metadata["version"] with pytest.raises(TypeError) as error: Package(name, version, version) assert str(error.value) == "expected string or bytes-like object"