Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions example/.appenv/current
6 changes: 4 additions & 2 deletions example/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# appenv-requirements-hash: 23726ae5a1e34c3fd5735728c69f2c436b6801d9ac7df6e3d0d5eb7d03fc2a0d
ducker==2.0.1
setuptools==65.5.0
ducker==2.0.1 \
--hash=sha256:159b84ee1103e495bbf89a69922f64f98e73f92ee76acea53a207cd45fc80ac9 \
--hash=sha256:50131934a2cd8aa62af8ff5e114463c7639c1fd8bb1a0c70102a351a64afe40a
# via -r requirements.in
93 changes: 40 additions & 53 deletions src/appenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ def pip(path, c, **kwargs):
return python(path, ["-m", "pip"] + c, **kwargs)


def pip_compile(use_uv, path, c, **kwargs):
if use_uv:
return cmd([os.path.join(path, "bin/uv"), "pip", "compile"] + c,
**kwargs)
else:
return cmd([os.path.join(path, "bin/pip-compile")] + c, **kwargs)


def get(host, path, f):
conn = http.client.HTTPSConnection(host)
conn.request("GET", path)
Expand Down Expand Up @@ -141,6 +149,19 @@ def ensure_venv(target):
python(target, ["-m", "ensurepip", "--default-pip"])
pip(target, ["install", "--upgrade", "pip"])

# try to install uv
print("Ensuring uv ...")
uses_uv_pip_compile = False
try:
pip(target, ["install", "uv"])
uses_uv_pip_compile = True
except ValueError:
print("uv not available, falling back to pip-compile")
# pip-compile is not available in the venv, so we need to install it
# in the system python
cmd(["pip", "install", "pip-tools"])
return uses_uv_pip_compile


def parse_preferences():
preferences = None
Expand Down Expand Up @@ -520,59 +541,25 @@ def update_lockfile(self, args=None, remaining=None):
tmpdir = os.path.join(self.appenv_dir, "updatelock")
if os.path.exists(tmpdir):
cmd(["rm", "-rf", tmpdir])
ensure_venv(tmpdir)
print("Installing packages ...")
pip(tmpdir, ["install", "-r", "requirements.in"])

extra_specs = []
result = pip(
tmpdir, ["freeze", "--all", "--exclude", "pip"],
merge_stderr=False).decode('ascii')
# They changed this behaviour in https://github.com/pypa/pip/pull/12032
pinned_versions = {}
for line in result.splitlines():
if line.strip().startswith('-e '):
# We'd like to pick up the original -e statement here.
continue
parsed_requirement = parse_requirement_string(line)
pinned_versions[parsed_requirement.name] = parsed_requirement
requested_versions = {}
with open('requirements.in') as f:
for line in f.readlines():
if line.strip().startswith('-e '):
extra_specs.append(line.strip())
continue
if line.strip().startswith('--'):
extra_specs.append(line.strip())
continue

# filter comments, in particular # appenv-python-preferences
if line.strip().startswith('#'):
continue
parsed_requirement = parse_requirement_string(line)
requested_versions[
parsed_requirement.name] = parsed_requirement

final_versions = {}
for spec in requested_versions.values():
# Pick versions with URLs to ensure we don't get the screwed up
# results from pip freeze.
if spec.url:
final_versions[spec.name] = spec
for spec in pinned_versions.values():
# Ignore versions we already picked
if spec.name in final_versions:
continue
final_versions[spec.name] = spec
lines = [str(spec) for spec in final_versions.values()]
lines.extend(extra_specs)
lines.sort()
with open(os.path.join(self.base, "requirements.txt"), "w") as f:
f.write('# appenv-requirements-hash: {}\n'.format(
self._hash_requirements()))
f.write('\n'.join(lines))
f.write('\n')
cmd(["rm", "-rf", tmpdir])
has_uv = ensure_venv(tmpdir)
# print("Installing packages ...")
# use uv pip compile or pip-compile to generate the requirements.txt

requirements_out = pip_compile(
has_uv,
tmpdir, [
"requirements.in", "--generate-hashes", "--no-header",
"--upgrade"],
merge_stderr=False)

requirements_out = requirements_out.decode("utf-8", "replace")
# prepend appenv-requirements-hash
requirements_hash = self._hash_requirements()
requirements_out = (
"# appenv-requirements-hash: {}\n".format(requirements_hash) +
requirements_out)
with open("requirements.txt", "w") as f:
f.write(requirements_out)


def main():
Expand Down
Loading