def test_to_dependency_for_file(): path = Path(__file__).parent.parent.joinpath( "fixtures/distributions/demo-0.1.0.tar.gz") package = Package( "foo", "1.2.3", source_type="file", source_url=path.as_posix(), features=["baz", "bar"], ) dep = package.to_dependency() assert "foo" == dep.name assert package.version == dep.constraint assert frozenset({"bar", "baz"}) == dep.features assert dep.is_file() assert path == dep.path assert "file" == dep.source_type assert path.as_posix() == dep.source_url
def install_git(self, package: Package) -> None: from poetry.core.packages.package import Package from poetry.vcs.git import Git source = Git.clone( url=package.source_url, source_root=self._env.path / "src", revision=package.source_resolved_reference or package.source_reference, ) # Now we just need to install from the source directory pkg = Package(package.name, package.version) pkg._source_type = "directory" pkg._source_url = str(source.path) pkg.develop = package.develop self.install_directory(pkg)
def test_to_dependency_for_directory() -> None: path = Path(__file__).parent.parent.joinpath("fixtures/simple_project") package = Package( "foo", "1.2.3", source_type="directory", source_url=path.as_posix(), features=["baz", "bar"], ) dep = package.to_dependency() assert dep.name == "foo" assert dep.constraint == package.version assert dep.features == frozenset({"bar", "baz"}) assert dep.is_directory() dep = cast(DirectoryDependency, dep) assert dep.path == path assert dep.source_type == "directory" assert dep.source_url == path.as_posix()
def test_add_existing_plugin_updates_if_requested( 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")) 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, expected, "^2.3.4")
def package_git() -> Package: package = Package( "demo", "1.0.0", source_type="git", source_url="[email protected]:demo/demo.git", source_reference="master", ) return package
def test_requirement(installer: PipInstaller): package = Package("ipython", "7.5.0") package.files = [ {"file": "foo-0.1.0.tar.gz", "hash": "md5:dbdc53e3918f28fa335a173432402a00"}, { "file": "foo.0.1.0.whl", "hash": "e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26", }, ] result = installer.requirement(package, formatted=True) expected = ( "ipython==7.5.0 " "--hash md5:dbdc53e3918f28fa335a173432402a00 " "--hash sha256:e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26" "\n" ) assert result == expected
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_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 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_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_to_dependency_for_file() -> None: path = Path(__file__).parent.parent.joinpath( "fixtures/distributions/demo-0.1.0.tar.gz") package = Package( "foo", "1.2.3", source_type="file", source_url=path.as_posix(), features=["baz", "bar"], ) dep = package.to_dependency() assert dep.name == "foo" assert dep.constraint == package.version assert dep.features == frozenset({"bar", "baz"}) assert dep.is_file() dep = cast(FileDependency, dep) assert dep.path == path assert dep.source_type == "file" assert dep.source_url == path.as_posix()
def install_git(self, package: "Package") -> None: from poetry.core.packages.package import Package from poetry.core.vcs.git import Git src_dir = self._env.path / "src" / package.name if src_dir.exists(): safe_rmtree(str(src_dir)) src_dir.parent.mkdir(exist_ok=True) git = Git() git.clone(package.source_url, src_dir) git.checkout(package.source_reference, src_dir) # Now we just need to install from the source directory pkg = Package(package.name, package.version) pkg._source_type = "directory" pkg._source_url = str(src_dir) pkg.develop = package.develop self.install_directory(pkg)
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 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 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_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_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_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_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 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 search(self, query: str) -> list[Package]: results = [] search = {"q": query} response = requests.session().get(self._base_url + "search", params=search, timeout=REQUESTS_TIMEOUT) content = parse(response.content, namespaceHTMLElements=False) for result in content.findall(".//*[@class='package-snippet']"): name_element = result.find("h3/*[@class='package-snippet__name']") version_element = result.find( "h3/*[@class='package-snippet__version']") if (name_element is None or version_element is None or not name_element.text or not version_element.text): continue name = name_element.text version = version_element.text description_element = result.find( "p[@class='package-snippet__description']") description = (description_element.text if description_element is not None and description_element.text else "") try: package = Package(name, version) package.description = to_str(description.strip()) results.append(package) except InvalidVersion: self._log( f'Unable to parse version "{version}" for the {name} package,' " skipping", level="debug", ) return results
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_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_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_executor_should_check_every_possible_hash_types_before_failing( config, io, pool, mocker, fixture_dir, tmp_dir ): mocker.patch.object( Chef, "get_cached_archive_for_link", side_effect=lambda link: link, ) mocker.patch.object( Executor, "_download_archive", return_value=fixture_dir("distributions").joinpath( "demo-0.1.0-py2.py3-none-any.whl" ), ) env = MockEnv(path=Path(tmp_dir)) executor = Executor(env, pool, config, io) package = Package("demo", "0.1.0") package.files = [ {"file": "demo-0.1.0-py2.py3-none-any.whl", "hash": "md5:123456"}, {"file": "demo-0.1.0-py2.py3-none-any.whl", "hash": "sha256:123456"}, ] expected_message = ( "Invalid hashes " "(" "md5:15507846fd4299596661d0197bfb4f90, " "sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a" ") " "for demo (0.1.0) using archive demo-0.1.0-py2.py3-none-any.whl. " "Expected one of md5:123456, sha256:123456." ) with pytest.raises(RuntimeError, match=re.escape(expected_message)): executor._download_link( Install(package), Link("https://example.com/demo-0.1.0-py2.py3-none-any.whl"), )
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_requirement_source_type_url(): installer = PipInstaller(NullEnv(), NullIO(), Pool()) foo = Package( "foo", "0.0.0", source_type="url", 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 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"