-
Notifications
You must be signed in to change notification settings - Fork 27
feat: basic support for uv.lock #750
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
Changes from all commits
75cc0b7
a65d151
0dc42f7
37ef483
d1eebdf
3f02bf8
6757b0b
869b342
ded1556
6452f94
ff3dffc
a2bbf09
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 |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| import json | ||
| import locale | ||
| import os | ||
| import tempfile | ||
| import re | ||
| import subprocess | ||
| import sys | ||
|
|
@@ -74,6 +75,8 @@ def detect_environment(dirname: str, requirements_file: Optional[str] = "require | |
|
|
||
| if requirements_file is None: | ||
| result = pip_freeze() | ||
| elif os.path.basename(requirements_file) == "uv.lock": | ||
| result = uv_export(dirname, requirements_file) | ||
| else: | ||
| result = output_file(dirname, requirements_file, "pip") or pip_freeze() | ||
|
|
||
|
|
@@ -186,6 +189,66 @@ def pip_freeze(): | |
| } | ||
|
|
||
|
|
||
| def uv_export(dirname: str, lock_filename: str): | ||
| """ | ||
| Export requirements from a uv.lock file using `uv export`. | ||
| """ | ||
| lock_path = lock_filename | ||
| if not os.path.isabs(lock_filename): | ||
| lock_path = os.path.join(dirname, lock_filename) | ||
|
|
||
| if not os.path.exists(lock_path): | ||
| raise EnvironmentException("uv.lock not found: %s" % lock_filename) | ||
|
|
||
| with tempfile.TemporaryDirectory() as tmpdir: | ||
| output_path = os.path.join(tmpdir, "requirements.txt.lock") | ||
| try: | ||
| result = subprocess.run( | ||
| [ | ||
| "uv", | ||
| "export", | ||
| "--format", | ||
| "requirements-txt", | ||
| "--frozen", | ||
| "--no-hashes", | ||
| "--no-annotate", | ||
| "--offline", | ||
| "--no-header", | ||
| "--no-emit-project", | ||
| "--output-file", | ||
| output_path, | ||
| ], | ||
| cwd=os.path.dirname(lock_path), | ||
| stdout=sys.stderr, | ||
| stderr=sys.stderr, | ||
| check=False, | ||
| ) | ||
| except Exception as exception: | ||
| raise EnvironmentException("Error during uv export: %s" % str(exception)) | ||
|
|
||
| if result.returncode != 0: | ||
| raise EnvironmentException("Error during uv export: exited with code %d" % result.returncode) | ||
|
|
||
| with open(output_path, mode="r", encoding="utf-8") as output_file: | ||
| exported = output_file.read() | ||
|
|
||
| requirements = filter_pip_freeze_output(exported) | ||
| requirements = ( | ||
| "# requirements.txt.lock generated from uv.lock by rsconnect-python on " | ||
|
Contributor
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. I am providing a uv.lock file and this message is not what I am seeing. Instead I see
Collaborator
Author
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. Uhm, Can you elaborate? This is a comment that is prefixed to the requirements file uploaded to connect so that when the bundle is accessed for debugging purposes there is a hint that the requirements were not written by a user but were generated from a uv.lock
Contributor
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. My bad, actually referring to this in the logs when deploying with uv.lock attached:
Collaborator
Author
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. Oh yes, that's connect server producing those logs. From the point of view of connect you still uploaded a requirements.txt.lock so it emits that log
Contributor
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. Slightly confusing but only if you are looking for it! Thanks for clarifying |
||
| + str(datetime.datetime.now(datetime.timezone.utc)) | ||
| + "\n" | ||
| + requirements | ||
| ) | ||
|
|
||
| return { | ||
| "filename": "requirements.txt.lock", | ||
| "contents": requirements, | ||
| "source": "uv_lock", | ||
| "package_manager": "uv", | ||
| "pip": None, | ||
| } | ||
|
|
||
|
|
||
| def filter_pip_freeze_output(pip_stdout: str): | ||
| # Filter out dependency on `rsconnect` and ignore output lines from pip which start with `[notice]` | ||
| return "\n".join( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.