def test_lock_packages_with_null_description(locker: Locker, root: ProjectPackage): package_a = get_package("A", "1.0.0") package_a.description = None locker.set_lock_data(root, [package_a]) with locker.lock.open(encoding="utf-8") as f: content = f.read() expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [metadata] lock-version = "1.1" python-versions = "*" content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] """ assert content == expected
def test_extras_dependencies_are_ordered(locker: Locker, root: ProjectPackage): package_a = get_package("A", "1.0.0") package_a.add_dependency( Factory.create_dependency( "B", {"version": "^1.0.0", "optional": True, "extras": ["c", "a", "b"]} ) ) package_a.requires[-1].activate() locker.set_lock_data(root, [package_a]) expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] B = {version = "^1.0.0", extras = ["a", "b", "c"], optional = true} [metadata] lock-version = "1.1" python-versions = "*" content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] """ with locker.lock.open(encoding="utf-8") as f: content = f.read() assert content == expected
def test_extras_dependencies_are_ordered(locker: Locker, root: ProjectPackage): package_a = get_package("A", "1.0.0") package_a.add_dependency( Factory.create_dependency( "B", {"version": "^1.0.0", "optional": True, "extras": ["c", "a", "b"]} ) ) package_a.requires[-1].activate() locker.set_lock_data(root, [package_a]) expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] B = {version = "^1.0.0", extras = ["a", "b", "c"], optional = true} [metadata] lock-version = "1.1" python-versions = "*" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831" [metadata.files] A = [] """ with locker.lock.open(encoding="utf-8") as f: content = f.read() assert content == expected
def test_lock_packages_with_null_description(locker: Locker, root: ProjectPackage): package_a = get_package("A", "1.0.0") package_a.description = None locker.set_lock_data(root, [package_a]) with locker.lock.open(encoding="utf-8") as f: content = f.read() expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [metadata] lock-version = "1.1" python-versions = "*" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831" [metadata.files] A = [] """ assert content == expected
def save(locker: Locker, lock_data: _TOMLDocument, root: Package) -> None: """Validate the lock data and write it to disk. Args: locker: The locker object. lock_data: The lock data. root: The root package of the Poetry project. """ packages = load_packages(locker, lock_data) locker.set_lock_data(root, packages)
def push(hub, args): # TODO: local imports to avoid circular dependencies, can clean-up once Poetry's plugin system arrives from poetry.packages.locker import Locker from tomlkit.toml_file import TOMLFile current_index = hub.current.index root_url = hub.current.root_url.url current_user, current_index = current_index[len(root_url):].split('/') available_indices = hub.http_api("get", root_url, quiet=True, check_version=False).result local_config = TOMLFile('pyproject.toml').read() locker = Locker('poetry.lock', local_config) locked_repository = locker.locked_repository(with_dev_reqs=not args.no_dev) for pkg in locked_repository.packages: name, version = pkg.name, pkg.version.text if pkg.source_url and pkg.source_url != current_index: hub.warn( "'{}=={}' is locked from {} which is not the current index, skipping." .format(name, version, pkg.source_url)) continue # try to guess the index from the download link project_url = hub.current.get_project_url(name) reply = hub.http_api("get", project_url, type="projectconfig") link = reply.result.get(version, {}).get('+links', [{}])[0].get('href') if not link.startswith(root_url): hub.warn( "'{}=={}' is mirrored from an external url, skipping.".format( name, version)) continue user, index, _ = link[len(root_url):].split('/', 2) if ((user, index) != (current_user, current_index) and not args.include_local_bases and available_indices.get(user, {}).get('indexes', {}).get( index, {}).get('type') != 'mirror'): hub.info( "Skipping '{}=={}' available from local base '{}/{}'".format( name, version, user, index)) continue pkg_args = Namespace(pkgspec='{}=={}'.format(name, version), index='{}/{}'.format(user, index), **vars(args)) devpi_push(hub, pkg_args)
def load_packages(locker: Locker, lock_data: _TOMLDocument) -> List[Package]: """Load the packages from a TOML document with lock data. Args: locker: The locker object. lock_data: The lock data. Returns: The list of packages. """ locker._lock_data = lock_data repository = locker.locked_repository(with_dev_reqs=True) activate_dependencies(repository.packages) return repository.packages # type: ignore[no-any-return] # noqa: F723
def create_poetry( self, cwd: Optional[Path] = None, io: Optional[IO] = None, disable_plugins: bool = False, ) -> Poetry: if io is None: io = NullIO() base_poetry = super(Factory, self).create_poetry(cwd) locker = Locker( base_poetry.file.parent / "poetry.lock", base_poetry.local_config ) # Loading global configuration config = self.create_config(io) # Loading local configuration local_config_file = TOMLFile(base_poetry.file.parent / "poetry.toml") if local_config_file.exists(): if io.is_debug(): io.write_line( "Loading configuration file {}".format(local_config_file.path) ) config.merge(local_config_file.read()) # Load local sources repositories = {} existing_repositories = config.get("repositories", {}) for source in base_poetry.pyproject.poetry_config.get("source", []): name = source.get("name") url = source.get("url") if name and url: if name not in existing_repositories: repositories[name] = {"url": url} config.merge({"repositories": repositories}) poetry = Poetry( base_poetry.file.path, base_poetry.local_config, base_poetry.package, locker, config, ) # Configuring sources self.configure_sources( poetry, poetry.local_config.get("source", []), config, io ) plugin_manager = PluginManager("plugin", disable_plugins=disable_plugins) plugin_manager.load_plugins() poetry.set_plugin_manager(plugin_manager) plugin_manager.activate(poetry, io) return poetry
def test_lock_file_should_not_have_mixed_types(locker: Locker, root: ProjectPackage): package_a = get_package("A", "1.0.0") package_a.add_dependency(Factory.create_dependency("B", "^1.0.0")) package_a.add_dependency( Factory.create_dependency("B", { "version": ">=1.0.0", "optional": True })) package_a.requires[-1].activate() package_a.extras["foo"] = [get_dependency("B", ">=1.0.0")] locker.set_lock_data(root, [package_a]) expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] B = [ {version = "^1.0.0"}, {version = ">=1.0.0", optional = true}, ] [package.extras] foo = ["B (>=1.0.0)"] [metadata] lock-version = "1.1" python-versions = "*" content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] """ with locker.lock.open(encoding="utf-8") as f: content = f.read() assert content == expected
def test_locked_repository_uses_root_dir_of_package( locker: Locker, mocker: MockerFixture ): content = """\ [[package]] name = "lib-a" version = "0.1.0" description = "" category = "main" optional = false python-versions = "^2.7.9" develop = true [package.dependencies] lib-b = {path = "../libB", develop = true} [package.source] type = "directory" url = "lib/libA" [metadata] lock-version = "1.1" python-versions = "*" content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] lib-a = [] lib-b = [] """ locker.lock.write(tomlkit.parse(content)) create_dependency_patch = mocker.patch( "poetry.factory.Factory.create_dependency", autospec=True ) locker.locked_repository() create_dependency_patch.assert_called_once_with( "lib-b", {"develop": True, "path": "../libB"}, root_dir=mocker.ANY ) call_kwargs = create_dependency_patch.call_args[1] root_dir = call_kwargs["root_dir"] assert root_dir.match("*/lib/libA") # relative_to raises an exception if not relative - is_relative_to comes in py3.9 assert root_dir.relative_to(locker.lock.path.parent.resolve()) is not None
def test_lock_file_should_not_have_mixed_types(locker: Locker, root: ProjectPackage): package_a = get_package("A", "1.0.0") package_a.add_dependency(Factory.create_dependency("B", "^1.0.0")) package_a.add_dependency( Factory.create_dependency("B", { "version": ">=1.0.0", "optional": True })) package_a.requires[-1].activate() package_a.extras["foo"] = [get_dependency("B", ">=1.0.0")] locker.set_lock_data(root, [package_a]) expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] B = [ {version = "^1.0.0"}, {version = ">=1.0.0", optional = true}, ] [package.extras] foo = ["B (>=1.0.0)"] [metadata] lock-version = "1.1" python-versions = "*" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831" [metadata.files] A = [] """ with locker.lock.open(encoding="utf-8") as f: content = f.read() assert content == expected
def poetry(tmp_dir: str, config: Config) -> Poetry: poetry = Poetry( CWD / "pyproject.toml", {}, ProjectPackage("simple-project", "1.2.3"), Locker(CWD / "poetry.lock", {}), config, ) return poetry
def test_content_hash_with_legacy_is_compatible( local_config: dict[str, list[str]], fresh: bool, locker: Locker ) -> None: # old hash generation relevant_content = {} for key in locker._legacy_keys: relevant_content[key] = local_config.get(key) locker = locker.__class__( lock=locker.lock.path, local_config=local_config, ) old_content_hash = sha256( json.dumps(relevant_content, sort_keys=True).encode() ).hexdigest() content_hash = locker._get_content_hash() assert (content_hash == old_content_hash) or fresh
def test_locking_legacy_repository_package_should_include_source_section( root: ProjectPackage, locker: Locker ): package_a = Package( "A", "1.0.0", source_type="legacy", source_url="https://foo.bar", source_reference="legacy", ) packages = [package_a] locker.set_lock_data(root, packages) with locker.lock.open(encoding="utf-8") as f: content = f.read() expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [package.source] type = "legacy" url = "https://foo.bar" reference = "legacy" [metadata] lock-version = "1.1" python-versions = "*" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831" [metadata.files] A = [] """ assert content == expected
def test_locking_legacy_repository_package_should_include_source_section( root: ProjectPackage, locker: Locker ): package_a = Package( "A", "1.0.0", source_type="legacy", source_url="https://foo.bar", source_reference="legacy", ) packages = [package_a] locker.set_lock_data(root, packages) with locker.lock.open(encoding="utf-8") as f: content = f.read() expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [package.source] type = "legacy" url = "https://foo.bar" reference = "legacy" [metadata] lock-version = "1.1" python-versions = "*" content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] """ assert content == expected
def test_locker_properly_loads_extras_legacy(locker: Locker): content = """\ [[package]] name = "a" version = "1.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] b = {version = "^1.0", optional = true} [package.extras] b = ["b (^1.0)"] [[package]] name = "b" version = "1.0" description = "" category = "main" optional = false python-versions = "*" [metadata] python-versions = "*" lock-version = "1.1" content-hash = "123456789" [metadata.files] "a" = [] "b" = [] """ locker.lock.write(tomlkit.parse(content)) repository = locker.locked_repository() assert len(repository.packages) == 2 packages = repository.find_packages(get_dependency("a", "1.0")) assert len(packages) == 1 package = packages[0] assert len(package.requires) == 1 assert len(package.extras) == 1 dependency_b = package.extras["b"][0] assert dependency_b.name == "b"
def test_locker_properly_loads_extras(locker: Locker): content = """\ [[package]] name = "cachecontrol" version = "0.12.5" description = "httplib2 caching for requests" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] msgpack = "*" requests = "*" [package.dependencies.lockfile] optional = true version = ">=0.9" [package.extras] filecache = ["lockfile (>=0.9)"] redis = ["redis (>=2.10.5)"] [metadata] lock-version = "1.1" python-versions = "~2.7 || ^3.4" content-hash = "c3d07fca33fba542ef2b2a4d75bf5b48d892d21a830e2ad9c952ba5123a52f77" [metadata.files] cachecontrol = [] """ locker.lock.write(tomlkit.parse(content)) packages = locker.locked_repository().packages assert len(packages) == 1 package = packages[0] assert len(package.requires) == 3 assert len(package.extras) == 2 lockfile_dep = package.extras["filecache"][0] assert lockfile_dep.name == "lockfile"
def locker(): with tempfile.NamedTemporaryFile() as f: f.close() locker = Locker(f.name, {}) return locker
def test_lock_file_data_is_ordered(locker: Locker, root: ProjectPackage): package_a = get_package("A", "1.0.0") package_a.add_dependency(Factory.create_dependency("B", "^1.0")) package_a.files = [{"file": "foo", "hash": "456"}, {"file": "bar", "hash": "123"}] package_git = Package( "git-package", "1.2.3", source_type="git", source_url="https://github.com/python-poetry/poetry.git", source_reference="develop", source_resolved_reference="123456", ) packages = [package_a, get_package("B", "1.2"), package_git] locker.set_lock_data(root, packages) with locker.lock.open(encoding="utf-8") as f: content = f.read() expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] B = "^1.0" [[package]] name = "B" version = "1.2" description = "" category = "main" optional = false python-versions = "*" [[package]] name = "git-package" version = "1.2.3" description = "" category = "main" optional = false python-versions = "*" develop = false [package.source] type = "git" url = "https://github.com/python-poetry/poetry.git" reference = "develop" resolved_reference = "123456" [metadata] lock-version = "1.1" python-versions = "*" content-hash = "178f2cd01dc40e96be23a4a0ae1094816626346346618335e5ff4f0b2c0c5831" [metadata.files] A = [ {file = "bar", hash = "123"}, {file = "foo", hash = "456"}, ] B = [] git-package = [] """ assert content == expected
def test_locker_dumps_dependency_information_correctly( locker: Locker, root: ProjectPackage ): root_dir = Path(__file__).parent.parent.joinpath("fixtures") package_a = get_package("A", "1.0.0") package_a.add_dependency( Factory.create_dependency( "B", {"path": "project_with_extras", "develop": True}, root_dir=root_dir ) ) package_a.add_dependency( Factory.create_dependency( "C", {"path": "directory/project_with_transitive_directory_dependencies"}, root_dir=root_dir, ) ) package_a.add_dependency( Factory.create_dependency( "D", {"path": "distributions/demo-0.1.0.tar.gz"}, root_dir=root_dir ) ) package_a.add_dependency( Factory.create_dependency( "E", {"url": "https://python-poetry.org/poetry-1.2.0.tar.gz"} ) ) package_a.add_dependency( Factory.create_dependency( "F", {"git": "https://github.com/python-poetry/poetry.git", "branch": "foo"} ) ) packages = [package_a] locker.set_lock_data(root, packages) with locker.lock.open(encoding="utf-8") as f: content = f.read() expected = """[[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] B = {path = "project_with_extras", develop = true} C = {path = "directory/project_with_transitive_directory_dependencies"} D = {path = "distributions/demo-0.1.0.tar.gz"} E = {url = "https://python-poetry.org/poetry-1.2.0.tar.gz"} F = {git = "https://github.com/python-poetry/poetry.git", branch = "foo"} [metadata] lock-version = "1.1" python-versions = "*" content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [] """ assert content == expected
def test_lock_file_data_is_ordered(locker: Locker, root: ProjectPackage): package_a = get_package("A", "1.0.0") package_a.add_dependency(Factory.create_dependency("B", "^1.0")) package_a.files = [{"file": "foo", "hash": "456"}, {"file": "bar", "hash": "123"}] package_a2 = get_package("A", "2.0.0") package_a2.files = [{"file": "baz", "hash": "345"}] package_git = Package( "git-package", "1.2.3", source_type="git", source_url="https://github.com/python-poetry/poetry.git", source_reference="develop", source_resolved_reference="123456", ) package_url_linux = Package( "url-package", "1.0", source_type="url", source_url="https://example.org/url-package-1.0-cp39-manylinux_2_17_x86_64.whl", ) package_url_win32 = Package( "url-package", "1.0", source_type="url", source_url="https://example.org/url-package-1.0-cp39-win_amd64.whl", ) packages = [ package_a2, package_a, get_package("B", "1.2"), package_git, package_url_win32, package_url_linux, ] locker.set_lock_data(root, packages) with locker.lock.open(encoding="utf-8") as f: content = f.read() expected = """\ [[package]] name = "A" version = "1.0.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] B = "^1.0" [[package]] name = "A" version = "2.0.0" description = "" category = "main" optional = false python-versions = "*" [[package]] name = "B" version = "1.2" description = "" category = "main" optional = false python-versions = "*" [[package]] name = "git-package" version = "1.2.3" description = "" category = "main" optional = false python-versions = "*" develop = false [package.source] type = "git" url = "https://github.com/python-poetry/poetry.git" reference = "develop" resolved_reference = "123456" [[package]] name = "url-package" version = "1.0" description = "" category = "main" optional = false python-versions = "*" [package.source] type = "url" url = "https://example.org/url-package-1.0-cp39-manylinux_2_17_x86_64.whl" [[package]] name = "url-package" version = "1.0" description = "" category = "main" optional = false python-versions = "*" [package.source] type = "url" url = "https://example.org/url-package-1.0-cp39-win_amd64.whl" [metadata] lock-version = "1.1" python-versions = "*" content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" [metadata.files] A = [ {file = "bar", hash = "123"}, {file = "foo", hash = "456"}, {file = "baz", hash = "345"}, ] B = [] git-package = [] url-package = [] """ assert content == expected
def create_poetry( self, cwd: Path | None = None, io: IO | None = None, disable_plugins: bool = False, disable_cache: bool = False, ) -> Poetry: if io is None: io = NullIO() base_poetry = super().create_poetry(cwd) locker = Locker(base_poetry.file.parent / "poetry.lock", base_poetry.local_config) # Loading global configuration with warnings.catch_warnings(): # this is preserved to ensure export plugin tests pass in ci, # once poetry-plugin-export version is updated to use one that do not # use Factory.create_config(), this can be safely removed. warnings.filterwarnings("ignore", category=DeprecationWarning) config = self.create_config() # Loading local configuration local_config_file = TOMLFile(base_poetry.file.parent / "poetry.toml") if local_config_file.exists(): if io.is_debug(): io.write_line( f"Loading configuration file {local_config_file.path}") config.merge(local_config_file.read()) # Load local sources repositories = {} existing_repositories = config.get("repositories", {}) for source in base_poetry.pyproject.poetry_config.get("source", []): name = source.get("name") url = source.get("url") if name and url and name not in existing_repositories: repositories[name] = {"url": url} config.merge({"repositories": repositories}) poetry = Poetry( base_poetry.file.path, base_poetry.local_config, base_poetry.package, locker, config, ) # Configuring sources self.configure_sources( poetry, poetry.local_config.get("source", []), config, io, disable_cache=disable_cache, ) plugin_manager = PluginManager(Plugin.group, disable_plugins=disable_plugins) plugin_manager.load_plugins() poetry.set_plugin_manager(plugin_manager) plugin_manager.activate(poetry, io) return poetry
def test_locker_properly_loads_nested_extras(locker: Locker): content = """\ [[package]] name = "a" version = "1.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] b = {version = "^1.0", optional = true, extras = "c"} [package.extras] b = ["b[c] (>=1.0,<2.0)"] [[package]] name = "b" version = "1.0" description = "" category = "main" optional = false python-versions = "*" [package.dependencies] c = {version = "^1.0", optional = true} [package.extras] c = ["c (>=1.0,<2.0)"] [[package]] name = "c" version = "1.0" description = "" category = "main" optional = false python-versions = "*" [metadata] python-versions = "*" lock-version = "1.1" content-hash = "123456789" [metadata.files] "a" = [] "b" = [] "c" = [] """ locker.lock.write(tomlkit.parse(content)) repository = locker.locked_repository() assert len(repository.packages) == 3 packages = repository.find_packages(get_dependency("a", "1.0")) assert len(packages) == 1 package = packages[0] assert len(package.requires) == 1 assert len(package.extras) == 1 dependency_b = package.extras["b"][0] assert dependency_b.name == "b" assert dependency_b.extras == frozenset({"c"}) packages = repository.find_packages(dependency_b) assert len(packages) == 1 package = packages[0] assert len(package.requires) == 1 assert len(package.extras) == 1 dependency_c = package.extras["c"][0] assert dependency_c.name == "c" assert dependency_c.extras == frozenset() packages = repository.find_packages(dependency_c) assert len(packages) == 1