Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement pex3 lock sync. #2373

Merged
merged 18 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 60 additions & 46 deletions pex/cli/commands/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ def update(
updates=(), # type: Iterable[Requirement]
replacements=(), # type: Iterable[Requirement]
deletes=(), # type: Iterable[ProjectName]
pin=False, # type: bool
artifacts_can_change=False, # type: bool
):
# type: (...) -> Union[LockUpdate, Result]
if not self._update_requests:
Expand All @@ -333,24 +333,17 @@ def update(
updates=updates,
replacements=replacements,
deletes=deletes,
pin=pin,
artifacts_can_change=artifacts_can_change,
)

def sync(
self,
requirement_configuration, # type: RequirementConfiguration
pin=False, # type: bool
lock_config_updated=False, # type: bool
):
# type: (...) -> Union[LockUpdate, Result]
def sync(self, requirement_configuration):
# type: (RequirementConfiguration) -> Union[LockUpdate, Result]
if not self._update_requests:
return self._no_updates()

return self._lock_updater.sync(
update_requests=self._update_requests,
requirement_configuration=requirement_configuration,
pin=pin,
lock_config_updated=lock_config_updated,
)

def _no_updates(self):
Expand Down Expand Up @@ -640,20 +633,25 @@ def _add_update_arguments(cls, update_parser):
resolver_options.register_use_pip_config(resolver_options_parser)

@classmethod
def add_update_lock_options(cls, update_parser):
update_parser.add_argument(
"--strict",
"--no-strict",
"--non-strict",
action=HandleBoolAction,
default=True,
type=bool,
help=(
"Require all target platforms in the lock be updated at once. If any target "
"platform in the lock file does not have a representative local interpreter to "
"execute the lock update with, the update will fail."
),
)
def add_update_lock_options(
cls,
update_parser, # type: _ActionsContainer
include_strict=True, # type: bool
):
if include_strict:
update_parser.add_argument(
"--strict",
"--no-strict",
"--non-strict",
action=HandleBoolAction,
default=True,
type=bool,
help=(
"Require all target platforms in the lock be updated at once. If any target "
"platform in the lock file does not have a representative local interpreter to "
"execute the lock update with, the update will fail."
),
)
update_parser.add_argument(
"-n",
"--dry-run",
Expand Down Expand Up @@ -711,7 +709,7 @@ def _add_sync_arguments(cls, sync_parser):
)
cls._add_lockfile_option(sync_parser, verb="sync", positional=False)
cls.add_create_lock_options(sync_parser)
cls.add_update_lock_options(sync_parser)
cls.add_update_lock_options(sync_parser, include_strict=False)

@classmethod
def add_extra_arguments(
Expand Down Expand Up @@ -1002,23 +1000,42 @@ def _create_lock_update_request(
)

with TRACER.timed("Selecting locks to update"):
subset_result = try_(
subset(
targets=targets,
lock=lock_file,
network_configuration=network_configuration,
build_configuration=lock_file.build_configuration(),
transitive=lock_file.transitive,
locked_resolve_count = len(lock_file.locked_resolves)
if lock_file.style is LockStyle.UNIVERSAL and locked_resolve_count != 1:
return Error(
"The lock at {path} contains {count} locked resolves; so it "
"cannot be updated as a universal lock which requires exactly one locked "
"resolve.".format(path=lock_file_path, count=locked_resolve_count)
)
)

update_requests = [
ResolveUpdateRequest(
target=resolved_subset.target, locked_resolve=resolved_subset.resolved.source
)
for resolved_subset in subset_result.subsets
]
if self.options.strict and lock_file.style is not LockStyle.UNIVERSAL:
if locked_resolve_count == 1:
locked_resolve = lock_file.locked_resolves[0]
update_targets = (
[LocalInterpreter.create(targets.interpreter)]
if lock_file.style is LockStyle.UNIVERSAL
else targets.unique_targets()
)
update_requests = [
ResolveUpdateRequest(target=target, locked_resolve=locked_resolve)
for target in update_targets
]
else:
subset_result = try_(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

N.B.: This is the old logic and it still applies for lock files with more than one locked resolve within; so these locks will still be subject to the more strict locked resolve selection rules and potentially fail to sync. I'll add a comment here pointing to an issue that tracks possibly handling those style locks better with a less strict, but still correct, scheme for selecting locked resolves to sync.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added and filed #2386

subset(
targets=targets,
lock=lock_file,
network_configuration=network_configuration,
build_configuration=lock_file.build_configuration(),
transitive=lock_file.transitive,
)
)
update_requests = [
ResolveUpdateRequest(
target=resolved_subset.target,
locked_resolve=resolved_subset.resolved.source,
)
for resolved_subset in subset_result.subsets
]
if getattr(self.options, "strict", False) and lock_file.style is not LockStyle.UNIVERSAL:
missing_updates = set(lock_file.locked_resolves) - {
update_request.locked_resolve for update_request in update_requests
}
Expand Down Expand Up @@ -1341,7 +1358,7 @@ def _update(self):
updates=update_requirements,
replacements=replace_requirements,
deletes=delete_projects,
pin=pin,
artifacts_can_change=pin,
)
if isinstance(lock_update, Result):
return lock_update
Expand Down Expand Up @@ -1402,11 +1419,8 @@ def _sync(self):
self._create_lock_update_request(lock_file_path=lock_file_path, lock_file=lock_file)
)

pin = getattr(self.options, "pin", False)
lock_update = lock_update_request.sync(
requirement_configuration=requirement_configuration,
pin=pin,
lock_config_updated=lock_file != original_lock_file,
)
if isinstance(lock_update, Result):
return lock_update
Expand Down
36 changes: 9 additions & 27 deletions pex/resolve/lockfile/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,10 +350,10 @@ def iter_updated_requirements(self):
def _calculate_requirement_configuration(
self,
locked_resolve, # type: LockedResolve
pin_all=False, # type: bool
artifacts_can_change=False, # type: bool
):
# type: (...) -> Iterator[Optional[RequirementConfiguration]]
if not self.update_constraints_by_project_name and not pin_all:
if not self.update_constraints_by_project_name and not artifacts_can_change:
yield None
return

Expand Down Expand Up @@ -402,15 +402,14 @@ def update_resolve(
self,
locked_resolve, # type: LockedResolve
target, # type: Target
pin_all=False, # type: bool
artifacts_can_change=False, # type: bool
):
# type: (...) -> Union[ResolveUpdate, Error]

updated_resolve = locked_resolve
updated_requirements = self.original_requirements
with self._calculate_requirement_configuration(
locked_resolve, pin_all=pin_all
locked_resolve, artifacts_can_change=artifacts_can_change
) as requirement_configuration:
if requirement_configuration:
updated_lock_file = try_(
Expand Down Expand Up @@ -626,8 +625,6 @@ def sync(
self,
update_requests, # type: Iterable[ResolveUpdateRequest]
requirement_configuration, # type: RequirementConfiguration
pin=False, # type: bool
lock_config_updated=False, # type: bool
):
# type: (...) -> Union[LockUpdate, Error]

Expand All @@ -636,20 +633,6 @@ def sync(
self.pip_configuration.network_configuration
)
)
constraints = tuple(
requirement_configuration.parse_constraints(
self.pip_configuration.network_configuration
)
)
if not any((pin, lock_config_updated, requirements, constraints)):
return LockUpdate(
requirements=self.lock_file.requirements,
resolves=tuple(
ResolveUpdate(updated_resolve=update_request.locked_resolve)
for update_request in update_requests
),
)

resolve_updater = try_(
ResolveUpdater.derived(
requirement_configuration=requirement_configuration,
Expand All @@ -662,8 +645,7 @@ def sync(
return self._perform_update(
update_requests=update_requests,
resolve_updater=resolve_updater,
pin=pin,
artifacts_can_change=lock_config_updated,
artifacts_can_change=True,
)

def update(
Expand All @@ -672,11 +654,11 @@ def update(
updates=(), # type: Iterable[Requirement]
replacements=(), # type: Iterable[Requirement]
deletes=(), # type: Iterable[ProjectName]
pin=False, # type: bool
artifacts_can_change=False, # type: bool
):
# type: (...) -> Union[LockUpdate, Error]

if not any((pin, updates, replacements, deletes)):
if not any((artifacts_can_change, updates, replacements, deletes)):
return LockUpdate(
requirements=self.lock_file.requirements,
resolves=tuple(
Expand All @@ -694,14 +676,15 @@ def update(
pip_configuration=self.pip_configuration,
)
return self._perform_update(
update_requests=update_requests, resolve_updater=resolve_updater, pin=pin
update_requests=update_requests,
resolve_updater=resolve_updater,
artifacts_can_change=artifacts_can_change,
)

def _perform_update(
self,
update_requests, # type: Iterable[ResolveUpdateRequest]
resolve_updater, # type: ResolveUpdater
pin=False, # type: bool
artifacts_can_change=False, # type: bool
):
# type: (...) -> Union[LockUpdate, Error]
Expand All @@ -722,7 +705,6 @@ def _perform_update(
resolve_updater.update_resolve,
locked_resolve=update_request.locked_resolve,
target=update_request.target,
pin_all=pin,
artifacts_can_change=artifacts_can_change,
)
if isinstance(result, Error):
Expand Down
Loading
Loading