-
-
Notifications
You must be signed in to change notification settings - Fork 646
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
[12610] add versions to lockfiles #12788
Changes from 4 commits
a1acfcf
91a756f
fce4cb6
2c79762
29a6825
0a08ec7
32d3bf9
711b9d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,8 @@ | |
BEGIN_LOCKFILE_HEADER = b"# --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE ---" | ||
END_LOCKFILE_HEADER = b"# --- END PANTS LOCKFILE METADATA ---" | ||
|
||
LOCKFILE_VERSION = 1 | ||
|
||
|
||
class InvalidLockfileError(Exception): | ||
pass | ||
|
@@ -26,7 +28,9 @@ class LockfileMetadata: | |
valid_for_interpreter_constraints: InterpreterConstraints | ||
|
||
@classmethod | ||
def from_lockfile(cls, lockfile: bytes) -> LockfileMetadata: | ||
def from_lockfile( | ||
cls, lockfile: bytes, lockfile_scope_name: str | None = None | ||
) -> LockfileMetadata: | ||
"""Parse all relevant metadata from the lockfile's header.""" | ||
in_metadata_block = False | ||
metadata_lines = [] | ||
|
@@ -38,40 +42,63 @@ def from_lockfile(cls, lockfile: bytes) -> LockfileMetadata: | |
elif in_metadata_block: | ||
metadata_lines.append(line[2:]) | ||
|
||
error_suffix = "To resolve this error, you will need to regenerate the lockfile by running `./pants generate-lockfiles" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Formatting nit, this is over 100 lines and Black won't attempt to fix. You'd need to use implicit string concatenation. |
||
|
||
if lockfile_scope_name: | ||
error_suffix += "--resolve={tool_name}" | ||
|
||
error_suffix += "`." | ||
|
||
lockfile_description: str = "" | ||
if lockfile_scope_name: | ||
lockfile_description = f"the lockfile for `{lockfile_scope_name}`" | ||
else: | ||
"""this lockfile.""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't do anything. Also prefer ternary expressions: https://www.pantsbuild.org/docs/style-guide#prefer-conditional-expressions-ternary-expressions There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ack There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for catching the typo There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is now a 4-level conditional, and it reads pretty poorly as a ternary expression, so I've kept it as an if statement for now. Let me know if this is a problem. |
||
|
||
if not metadata_lines: | ||
# TODO(#12314): Add a good error. | ||
raise InvalidLockfileError("") | ||
raise InvalidLockfileError( | ||
f"Could not find a pants metadata block in this {lockfile_scope_name}. {error_suffix}" | ||
) | ||
|
||
try: | ||
metadata = json.loads(b"\n".join(metadata_lines)) | ||
except json.decoder.JSONDecodeError: | ||
# TODO(#12314): Add a good error. | ||
raise InvalidLockfileError("") | ||
raise InvalidLockfileError( | ||
f"Metadata header in {lockfile_description} is not a valid JSON string and can't " | ||
"be decoded. " + error_suffix | ||
) | ||
|
||
def get_or_raise(key: str) -> Any: | ||
try: | ||
return metadata[key] | ||
except KeyError: | ||
# TODO(#12314): Add a good error about the key not being defined. | ||
raise InvalidLockfileError("") | ||
raise InvalidLockfileError( | ||
f"Required key `{key}` is not present in metadata header for " | ||
f"{lockfile_description}. {error_suffix}" | ||
) | ||
|
||
requirements_digest = get_or_raise("requirements_invalidation_digest") | ||
if not isinstance(requirements_digest, str): | ||
# TODO(#12314): Add a good error about invalid data type. | ||
raise InvalidLockfileError("") | ||
raise InvalidLockfileError( | ||
f"Metadata value `requirements_invalidation_digest` in {lockfile_description} must " | ||
"be a string. " + error_suffix | ||
) | ||
|
||
try: | ||
interpreter_constraints = InterpreterConstraints( | ||
get_or_raise("valid_for_interpreter_constraints") | ||
) | ||
except TypeError: | ||
# TODO(#12314): Add a good error about invalid data type. | ||
raise InvalidLockfileError("") | ||
raise InvalidLockfileError( | ||
f"Metadata value `valid_for_interpreter_constraints` in {lockfile_description} " | ||
"must be a list of valid Python interpreter constraints strings. " + error_suffix | ||
) | ||
|
||
return LockfileMetadata(requirements_digest, interpreter_constraints) | ||
|
||
def add_header_to_lockfile(self, lockfile: bytes, *, regenerate_command: str) -> bytes: | ||
metadata_dict = { | ||
"version": LOCKFILE_VERSION, | ||
"requirements_invalidation_digest": self.requirements_invalidation_digest, | ||
"valid_for_interpreter_constraints": [ | ||
str(ic) for ic in self.valid_for_interpreter_constraints | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -483,6 +483,12 @@ async def build_pex_component( | |
constraint_file_digest = EMPTY_DIGEST | ||
requirements_file_digest = EMPTY_DIGEST | ||
|
||
lockfile_scope_name: str | None | ||
if isinstance(request.requirements, (ToolDefaultLockfile, ToolCustomLockfile)): | ||
lockfile_scope_name = request.requirements.options_scope_name | ||
else: | ||
lockfile_scope_name = None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As explained above, please call it resolve_name = (
request.requirements.options_scope_name
if isinstance(request.requirements, (ToolDefaultLockfile, ToolCustomLockfile))
else None
) In the future, this will need to capture the resolve name for multiple user lockfiles. It's fine to punt on that, but can you please add something like:
|
||
|
||
if isinstance(request.requirements, Lockfile): | ||
argv.extend(["--requirement", request.requirements.file_path]) | ||
argv.append("--no-transitive") | ||
|
@@ -494,7 +500,9 @@ async def build_pex_component( | |
) | ||
|
||
requirements_file_digest_contents = await Get(DigestContents, PathGlobs, globs) | ||
metadata = LockfileMetadata.from_lockfile(requirements_file_digest_contents[0].content) | ||
metadata = LockfileMetadata.from_lockfile( | ||
requirements_file_digest_contents[0].content, lockfile_scope_name | ||
) | ||
_validate_metadata(metadata, request, request.requirements, python_setup) | ||
|
||
requirements_file_digest = await Get(Digest, PathGlobs, globs) | ||
|
@@ -504,7 +512,7 @@ async def build_pex_component( | |
argv.extend(["--requirement", file_content.path]) | ||
argv.append("--no-transitive") | ||
|
||
metadata = LockfileMetadata.from_lockfile(file_content.content) | ||
metadata = LockfileMetadata.from_lockfile(file_content.content, lockfile_scope_name) | ||
_validate_metadata(metadata, request, request.requirements, python_setup) | ||
|
||
requirements_file_digest = await Get(Digest, CreateDigest([file_content])) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If my read is correct, you always have the
lockfile_path
available. Only for tool lockfiles do you have theresolve_name
(what you calllockfile_scope_name
). If you're up for it, the error message would be even better if you could sayOr if resolve_name is None,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also please call it
resolve_name
. This error applies to multiple user lockfiles too, andlockfile_scope_name
is a misnomer there. The way to refer to this isresolve_name
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. Making that change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like
lockfile_path
is only available for non-default lockfiles, so I've addedlockfile_path
but made it optional.