From 00a51ab22b8548a236226b5e128fc724e70afede Mon Sep 17 00:00:00 2001 From: ohemorange Date: Mon, 18 Aug 2025 13:17:02 -0700 Subject: [PATCH] Drop in uv for pip (#10428) It's a [drop-in replacement](https://docs.astral.sh/uv/pip/compatibility/) that speeds things up. I don't see any reason why not. `--use-pep517` is [set by default]( https://docs.astral.sh/uv/pip/compatibility/#pep-517-build-isolation), so we don't need it. `--disable-pip-version-check` also does nothing on uv. `uv` [uses separate](https://docs.astral.sh/uv/pip/compatibility/#build-constraints) `UV_BUILD_CONSTRAINT` and `UV_CONSTRAINT`. I just added it to both to do the simplest thing here. We could split them. We probably don't actually need to pipstrap pip anymore, I could take that out. What's happening with `parsedatetime` and `python-digitalocean` is that they were always secretly wrong. Since `pip` compiles bytecode by default, it was suppressing the errors. If you add the `--compile-bytecode` flag to `uv`, it passes, but I don't think we should do that. You can see the failure happen on main by passing `--no-compile` to the pip args and running `certbot -r -e oldest`. Now what I don't understand is that some places seem to say the `'\/'` error from `parsedatetime` only started in python 3.12, whereas others see it on earlier python. Perhaps pytest is vendorizing python or something. Not too worried about that, needed to get updated anyway, and it's an accurate oldest version based on our oldest OSes. `python-digitalocean` is techincally newer than debian 11, but we've made that decision before so it seems fine to me. --- .gitignore | 3 +++ certbot-dns-digitalocean/setup.py | 2 +- certbot/pyproject.toml | 1 + certbot/setup.py | 2 +- newsfragments/10428.added | 1 + tools/oldest_constraints.txt | 13 +++++++------ tools/pinning/oldest/pyproject.toml | 4 ++-- tools/pip_install.py | 10 ++++++++-- tools/requirements.txt | 21 +++++++++++---------- 9 files changed, 35 insertions(+), 22 deletions(-) create mode 100644 newsfragments/10428.added diff --git a/.gitignore b/.gitignore index 096c31dd2..9ccbae27f 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,6 @@ certbot-dns*/certbot-dns*_arm*.txt /certbot_arm*.txt certbot-dns*/snap snapcraft.cfg + +# pyenv files +.python-version diff --git a/certbot-dns-digitalocean/setup.py b/certbot-dns-digitalocean/setup.py index bfe2f60e6..1e5045448 100644 --- a/certbot-dns-digitalocean/setup.py +++ b/certbot-dns-digitalocean/setup.py @@ -5,7 +5,7 @@ from setuptools import setup version = '5.0.0.dev0' install_requires = [ - 'python-digitalocean>=1.11', # 1.15.0 or newer is recommended for TTL support + 'python-digitalocean>=1.15.0', # 1.15.0 or newer is recommended for TTL support ] if os.environ.get('SNAP_BUILD'): diff --git a/certbot/pyproject.toml b/certbot/pyproject.toml index 578cacb23..fb6e6f107 100644 --- a/certbot/pyproject.toml +++ b/certbot/pyproject.toml @@ -68,6 +68,7 @@ test = [ "types-pywin32", "types-requests", "types-setuptools", + "uv", "wheel", ] all = [ diff --git a/certbot/setup.py b/certbot/setup.py index 71bb62540..39d869db6 100644 --- a/certbot/setup.py +++ b/certbot/setup.py @@ -33,7 +33,7 @@ install_requires = [ 'distro>=1.0.1', 'importlib_metadata>=8.6.1; python_version < "3.10"', 'josepy>=2.0.0', - 'parsedatetime>=2.4', + 'parsedatetime>=2.6', 'pyrfc3339', # This dependency needs to be added using environment markers to avoid its # installation on Linux. diff --git a/newsfragments/10428.added b/newsfragments/10428.added new file mode 100644 index 000000000..7e50dbc70 --- /dev/null +++ b/newsfragments/10428.added @@ -0,0 +1 @@ +Added `uv` as a test dependency, and switched most `pip` invocations to `uv pip` for faster installs. diff --git a/tools/oldest_constraints.txt b/tools/oldest_constraints.txt index 753fb22e8..d24c4f36d 100644 --- a/tools/oldest_constraints.txt +++ b/tools/oldest_constraints.txt @@ -15,7 +15,7 @@ cloudflare==2.19.0 ; python_version == "3.10" colorama==0.4.6 ; (sys_platform == "win32" or platform_system == "Windows") and python_version == "3.10" configargparse==1.5.3 ; python_version == "3.10" configobj==5.0.6 ; python_version == "3.10" -coverage==7.10.3 ; python_version == "3.10" +coverage==7.10.4 ; python_version == "3.10" cryptography==43.0.0 ; python_version == "3.10" cython==0.29.37 ; python_version == "3.10" dill==0.4.0 ; python_version == "3.10" @@ -25,9 +25,8 @@ dns-lexicon==3.15.1 ; python_version == "3.10" dnspython==2.6.1 ; python_version == "3.10" exceptiongroup==1.3.0 ; python_version == "3.10" execnet==2.1.1 ; python_version == "3.10" -filelock==3.18.0 ; python_version == "3.10" +filelock==3.19.1 ; python_version == "3.10" funcsigs==0.4 ; python_version == "3.10" -future==1.0.0 ; python_version == "3.10" google-api-python-client==1.6.5 ; python_version == "3.10" google-auth==2.16.0 ; python_version == "3.10" httplib2==0.9.2 ; python_version == "3.10" @@ -38,13 +37,14 @@ isort==6.0.1 ; python_version == "3.10" jmespath==0.10.0 ; python_version == "3.10" josepy==2.1.0 ; python_version == "3.10" jsonlines==4.0.0 ; python_version == "3.10" +jsonpickle==4.1.1 ; python_version == "3.10" mccabe==0.7.0 ; python_version == "3.10" mypy-extensions==1.1.0 ; python_version == "3.10" mypy==1.17.1 ; python_version == "3.10" ndg-httpsclient==0.3.2 ; python_version == "3.10" oauth2client==4.1.3 ; python_version == "3.10" packaging==25.0 ; python_version == "3.10" -parsedatetime==2.4 ; python_version == "3.10" +parsedatetime==2.6 ; python_version == "3.10" pathspec==0.12.1 ; python_version == "3.10" pbr==1.8.0 ; python_version == "3.10" pip==25.2 ; python_version == "3.10" @@ -66,14 +66,14 @@ pytest-xdist==3.8.0 ; python_version == "3.10" pytest==8.4.1 ; python_version == "3.10" python-augeas==0.5.0 ; python_version == "3.10" python-dateutil==2.9.0.post0 ; python_version == "3.10" -python-digitalocean==1.11 ; python_version == "3.10" +python-digitalocean==1.15.0 ; python_version == "3.10" pytz==2025.2 ; python_version == "3.10" pywin32==311 ; python_version == "3.10" and sys_platform == "win32" pyyaml==6.0.2 ; python_version == "3.10" requests-file==2.1.0 ; python_version == "3.10" requests==2.25.1 ; python_version == "3.10" rsa==4.9.1 ; python_version == "3.10" -ruff==0.12.8 ; python_version == "3.10" +ruff==0.12.9 ; python_version == "3.10" s3transfer==0.5.2 ; python_version == "3.10" setuptools==80.9.0 ; python_version == "3.10" six==1.16.0 ; python_version == "3.10" @@ -92,5 +92,6 @@ types-urllib3==1.26.25.14 ; python_version == "3.10" typing-extensions==4.14.1 ; python_version == "3.10" uritemplate==3.0.1 ; python_version == "3.10" urllib3==1.26.5 ; python_version == "3.10" +uv==0.8.11 ; python_version == "3.10" virtualenv==20.34.0 ; python_version == "3.10" wheel==0.45.1 ; python_version == "3.10" diff --git a/tools/pinning/oldest/pyproject.toml b/tools/pinning/oldest/pyproject.toml index 38510921f..ec027247c 100644 --- a/tools/pinning/oldest/pyproject.toml +++ b/tools/pinning/oldest/pyproject.toml @@ -72,7 +72,7 @@ httplib2 = "0.9.2" idna = "2.6" ipaddress = "1.0.16" ndg-httpsclient = "0.3.2" -parsedatetime = "2.4" +parsedatetime = "2.6" pbr = "1.8.0" ply = "3.4" pyOpenSSL = "25.0.0" @@ -81,7 +81,7 @@ pyasn1 = "0.4.8" pycparser = "2.14" pyparsing = "2.4.7" python-augeas = "0.5.0" -python-digitalocean = "1.11" +python-digitalocean = "1.15.0" requests = "2.25.1" six = "1.16.0" urllib3 = "1.26.5" diff --git a/tools/pip_install.py b/tools/pip_install.py index ebec4baef..dc74f2ed5 100755 --- a/tools/pip_install.py +++ b/tools/pip_install.py @@ -28,6 +28,10 @@ def call_with_print(command, env): subprocess.check_call(command, shell=True, env=env) +def uv_install_with_print(args_str, env): + command = ['"', sys.executable, '" -m uv pip install ', args_str] + call_with_print(''.join(command), env=env) + def pip_install_with_print(args_str, env): command = ['"', sys.executable, '" -m pip install --disable-pip-version-check --use-pep517 ', args_str] call_with_print(''.join(command), env=env) @@ -49,19 +53,21 @@ def pip_constrained_environ(): # are also used when installing build dependencies. See # https://github.com/certbot/certbot/pull/8443 for more info. env["PIP_CONSTRAINT"] = constraints_path + env["UV_CONSTRAINT"] = constraints_path + env["UV_BUILD_CONSTRAINT"] = constraints_path return env def pipstrap(env=None): if env is None: env = pip_constrained_environ() - pip_install_with_print('pip setuptools wheel', env=env) + pip_install_with_print('pip setuptools wheel uv', env=env) def main(args): env = pip_constrained_environ() pipstrap(env) - pip_install_with_print(' '.join(args), env=env) + uv_install_with_print(' '.join(args), env=env) if __name__ == '__main__': diff --git a/tools/requirements.txt b/tools/requirements.txt index 0fee425a3..5a96e9d63 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -17,8 +17,8 @@ babel==2.17.0 ; python_version >= "3.10" and python_version < "4.0" backports-tarfile==1.2.0 ; python_version >= "3.10" and python_version < "3.12" bcrypt==4.3.0 ; python_version >= "3.10" and python_version < "4.0" beautifulsoup4==4.13.4 ; python_version >= "3.10" and python_version < "4.0" -boto3==1.40.8 ; python_version >= "3.10" and python_version < "4.0" -botocore==1.40.8 ; python_version >= "3.10" and python_version < "4.0" +boto3==1.40.11 ; python_version >= "3.10" and python_version < "4.0" +botocore==1.40.11 ; python_version >= "3.10" and python_version < "4.0" build==1.3.0 ; python_version >= "3.10" and python_version < "4.0" cachecontrol==0.14.3 ; python_version >= "3.10" and python_version < "4.0" cachetools==5.5.2 ; python_version >= "3.10" and python_version < "4.0" @@ -32,7 +32,7 @@ cloudflare==2.19.4 ; python_version >= "3.10" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" configargparse==1.7.1 ; python_version >= "3.10" and python_version < "4.0" configobj==5.0.9 ; python_version >= "3.10" and python_version < "4.0" -coverage==7.10.3 ; python_version >= "3.10" and python_version < "4.0" +coverage==7.10.4 ; python_version >= "3.10" and python_version < "4.0" crashtest==0.4.1 ; python_version >= "3.10" and python_version < "4.0" cryptography==45.0.6 ; python_version >= "3.10" and python_version < "4.0" cython==0.29.37 ; python_version >= "3.10" and python_version < "4.0" @@ -49,11 +49,11 @@ exceptiongroup==1.3.0 ; python_version == "3.10" execnet==2.1.1 ; python_version >= "3.10" and python_version < "4.0" executing==2.2.0 ; python_version >= "3.10" and python_version < "4.0" fabric==3.2.2 ; python_version >= "3.10" and python_version < "4.0" -fastjsonschema==2.21.1 ; python_version >= "3.10" and python_version < "4.0" -filelock==3.18.0 ; python_version >= "3.10" and python_version < "4.0" +fastjsonschema==2.21.2 ; python_version >= "3.10" and python_version < "4.0" +filelock==3.19.1 ; python_version >= "3.10" and python_version < "4.0" findpython==0.6.3 ; python_version >= "3.10" and python_version < "4.0" google-api-core==2.25.1 ; python_version >= "3.10" and python_version < "4.0" -google-api-python-client==2.178.0 ; python_version >= "3.10" and python_version < "4.0" +google-api-python-client==2.179.0 ; python_version >= "3.10" and python_version < "4.0" google-auth-httplib2==0.2.0 ; python_version >= "3.10" and python_version < "4.0" google-auth==2.40.3 ; python_version >= "3.10" and python_version < "4.0" googleapis-common-protos==1.70.0 ; python_version >= "3.10" and python_version < "4.0" @@ -101,7 +101,7 @@ packaging==25.0 ; python_version >= "3.10" and python_version < "4.0" paramiko==4.0.0 ; python_version >= "3.10" and python_version < "4.0" parsedatetime==2.6 ; python_version >= "3.10" and python_version < "4.0" parso==0.8.4 ; python_version >= "3.10" and python_version < "4.0" -pbs-installer==2025.8.8 ; python_version >= "3.10" and python_version < "4.0" +pbs-installer==2025.8.14 ; python_version >= "3.10" and python_version < "4.0" pexpect==4.9.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform != "win32" and sys_platform != "emscripten" pip==25.2 ; python_version >= "3.10" and python_version < "4.0" pkginfo==1.12.1.2 ; python_version >= "3.10" and python_version < "4.0" @@ -113,7 +113,7 @@ poetry-plugin-export==1.9.0 ; python_version >= "3.10" and python_version < "4.0 poetry==2.1.4 ; python_version >= "3.10" and python_version < "4.0" prompt-toolkit==3.0.51 ; python_version >= "3.10" and python_version < "4.0" proto-plus==1.26.1 ; python_version >= "3.10" and python_version < "4.0" -protobuf==6.31.1 ; python_version >= "3.10" and python_version < "4.0" +protobuf==6.32.0 ; python_version >= "3.10" and python_version < "4.0" ptyprocess==0.7.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform != "win32" and sys_platform != "emscripten" pure-eval==0.2.3 ; python_version >= "3.10" and python_version < "4.0" pyasn1-modules==0.4.2 ; python_version >= "3.10" and python_version < "4.0" @@ -147,7 +147,7 @@ rfc3986==2.0.0 ; python_version >= "3.10" and python_version < "4.0" rich==14.1.0 ; python_version >= "3.10" and python_version < "4.0" roman-numerals-py==3.1.0 ; python_version >= "3.11" and python_version < "4.0" rsa==4.9.1 ; python_version >= "3.10" and python_version < "4.0" -ruff==0.12.8 ; python_version >= "3.10" and python_version < "4.0" +ruff==0.12.9 ; python_version >= "3.10" and python_version < "4.0" s3transfer==0.13.1 ; python_version >= "3.10" and python_version < "4.0" secretstorage==3.3.3 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "linux" semantic-version==2.10.0 ; python_version >= "3.10" and python_version < "4.0" @@ -186,10 +186,11 @@ types-setuptools==80.9.0.20250809 ; python_version >= "3.10" and python_version typing-extensions==4.14.1 ; python_version >= "3.10" and python_version < "4.0" uritemplate==4.2.0 ; python_version >= "3.10" and python_version < "4.0" urllib3==2.5.0 ; python_version >= "3.10" and python_version < "4.0" +uv==0.8.11 ; python_version >= "3.10" and python_version < "4.0" virtualenv==20.32.0 ; python_version >= "3.10" and python_version < "4.0" wcwidth==0.2.13 ; python_version >= "3.10" and python_version < "4.0" wheel==0.45.1 ; python_version >= "3.10" and python_version < "4.0" wrapt==1.17.3 ; python_version >= "3.10" and python_version < "4.0" xattr==1.2.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "darwin" zipp==3.23.0 ; python_version >= "3.10" and python_version < "3.12" -zstandard==0.23.0 ; python_version >= "3.10" and python_version < "4.0" +zstandard==0.24.0 ; python_version >= "3.10" and python_version < "4.0"