Skip to content

Commit

Permalink
installer: support for duplicate direct origin dependencies with same…
Browse files Browse the repository at this point in the history
… version
  • Loading branch information
radoering committed May 29, 2022
1 parent 1a15cb4 commit 4a4a8a8
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 18 deletions.
36 changes: 18 additions & 18 deletions src/poetry/installation/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from poetry.repositories import Pool
from poetry.repositories import Repository
from poetry.repositories.installed_repository import InstalledRepository
from poetry.repositories.lockfile_repository import LockfileRepository
from poetry.utils.extras import get_extra_package_names
from poetry.utils.helpers import canonicalize_name
from poetry.utils.helpers import pluralize
Expand Down Expand Up @@ -107,9 +108,7 @@ def run(self) -> int:
self._write_lock = False
self._execute_operations = False

local_repo = Repository()

return self._do_install(local_repo)
return self._do_install()

def dry_run(self, dry_run: bool = True) -> Installer:
self._dry_run = dry_run
Expand Down Expand Up @@ -204,14 +203,14 @@ def _do_refresh(self) -> int:
):
ops = solver.solve(use_latest=[]).calculate_operations()

local_repo = Repository()
self._populate_local_repo(local_repo, ops)
lockfile_repo = LockfileRepository()
self._populate_lockfile_repo(lockfile_repo, ops)

self._write_lock_file(local_repo, force=True)
self._write_lock_file(lockfile_repo, force=True)

return 0

def _do_install(self, local_repo: Repository) -> int:
def _do_install(self) -> int:
from poetry.puzzle.solver import Solver

locked_repository = Repository()
Expand Down Expand Up @@ -266,10 +265,11 @@ def _do_install(self, local_repo: Repository) -> int:
# currently installed
ops = self._get_operations_from_lock(locked_repository)

self._populate_local_repo(local_repo, ops)
lockfile_repo = LockfileRepository()
self._populate_lockfile_repo(lockfile_repo, ops)

if self._update:
self._write_lock_file(local_repo)
self._write_lock_file(lockfile_repo)

if self._lock:
# If we are only in lock mode, no need to go any further
Expand All @@ -292,8 +292,8 @@ def _do_install(self, local_repo: Repository) -> int:
# Making a new repo containing the packages
# newly resolved and the ones from the current lock file
repo = Repository()
for package in local_repo.packages + locked_repository.packages:
if not repo.has_package(package):
for package in lockfile_repo.packages + locked_repository.packages:
if not package.is_direct_origin() and not repo.has_package(package):
repo.add_package(package)

pool.add_repository(repo)
Expand All @@ -318,7 +318,7 @@ def _do_install(self, local_repo: Repository) -> int:

transaction = Transaction(
locked_repository.packages,
[(package, 0) for package in local_repo.packages],
[(package, 0) for package in lockfile_repo.packages],
installed_packages=self._installed_repository.packages,
root_package=root,
)
Expand All @@ -332,12 +332,12 @@ def _do_install(self, local_repo: Repository) -> int:
# We need to filter operations so that packages
# not compatible with the current system,
# or optional and not requested, are dropped
self._filter_operations(ops, local_repo)
self._filter_operations(ops, lockfile_repo)

# Execute operations
return self._execute(ops)

def _write_lock_file(self, repo: Repository, force: bool = False) -> None:
def _write_lock_file(self, repo: LockfileRepository, force: bool = False) -> None:
if self._write_lock and (force or self._update):
updated_lock = self._locker.set_lock_data(self._package, repo.packages)

Expand Down Expand Up @@ -460,8 +460,8 @@ def _execute_uninstall(self, operation: Uninstall) -> None:

self._installer.remove(operation.package)

def _populate_local_repo(
self, local_repo: Repository, ops: Sequence[Operation]
def _populate_lockfile_repo(
self, repo: LockfileRepository, ops: Sequence[Operation]
) -> None:
for op in ops:
if isinstance(op, Uninstall):
Expand All @@ -471,8 +471,8 @@ def _populate_local_repo(
else:
package = op.package

if not local_repo.has_package(package):
local_repo.add_package(package)
if not repo.has_package(package):
repo.add_package(package)

def _get_operations_from_lock(
self, locked_repository: Repository
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
[[package]]
name = "demo"
version = "0.1.0"
description = ""
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[package.source]
type = "url"
url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"

[package.dependencies]
pendulum = ">=1.4.4"

[package.extras]
bar = ["tomlkit"]
foo = ["cleo"]

[[package]]
name = "demo"
version = "0.1.0"
description = ""
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[package.source]
type = "url"
url = "https://python-poetry.org/distributions/demo-0.1.0.tar.gz"

[package.dependencies]
pendulum = ">=1.4.4"

[package.extras]
bar = ["tomlkit"]
foo = ["cleo"]

[[package]]
name = "pendulum"
version = "1.4.4"
description = ""
category = "main"
optional = false
python-versions = "*"

[metadata]
python-versions = "*"
lock-version = "1.1"
content-hash = "123456789"

[metadata.files]
demo = []
pendulum = []
50 changes: 50 additions & 0 deletions tests/installation/test_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2231,6 +2231,56 @@ def test_run_installs_with_url_file(
assert installer.executor.installations_count == 2


@pytest.mark.parametrize("env_platform", ["linux", "win32"])
def test_run_installs_with_same_version_url_files(
pool: Pool,
locker: Locker,
installed: CustomInstalledRepository,
config: Config,
repo: Repository,
package: ProjectPackage,
env_platform: str,
) -> None:
urls = {
"linux": "https://python-poetry.org/distributions/demo-0.1.0.tar.gz",
"win32": (
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
),
}
for platform, url in urls.items():
package.add_dependency(
Factory.create_dependency(
"demo",
{"url": url, "markers": f"sys_platform == '{platform}'"},
)
)
repo.add_package(get_package("pendulum", "1.4.4"))

installer = Installer(
NullIO(),
MockEnv(platform=env_platform),
package,
locker,
pool,
config,
installed=installed,
executor=Executor(
MockEnv(platform=env_platform),
pool,
config,
NullIO(),
),
)
installer.use_executor(True)
installer.run()

expected = fixture("with-same-version-url-dependencies")
assert locker.written_data == expected
assert installer.executor.installations_count == 2
demo_package = next(p for p in installer.executor.installations if p.name == "demo")
assert demo_package.source_url == urls[env_platform]


def test_installer_uses_prereleases_if_they_are_compatible(
installer: Installer, locker: Locker, package: ProjectPackage, repo: Repository
):
Expand Down

0 comments on commit 4a4a8a8

Please sign in to comment.