diff --git a/examples/uploader/_localrepo.py b/examples/uploader/_localrepo.py index edae65821b..c4d746a34d 100644 --- a/examples/uploader/_localrepo.py +++ b/examples/uploader/_localrepo.py @@ -12,8 +12,8 @@ import os from datetime import datetime, timedelta, timezone -import requests from securesystemslib.signer import CryptoSigner, Signer +from urllib3 import request from tuf.api.exceptions import RepositoryError from tuf.api.metadata import Metadata, MetaFile, TargetFile, Targets @@ -92,8 +92,9 @@ def close(self, role_name: str, md: Metadata) -> None: # Upload using "api/role" uri = f"{self.base_url}/api/role/{role_name}" - r = requests.post(uri, data=md.to_bytes(JSONSerializer()), timeout=5) - r.raise_for_status() + r = request("POST", uri, body=md.to_bytes(JSONSerializer()), timeout=5) + if r.status != 200: + raise RuntimeError(f"HTTP error {r.status}") def add_target(self, role: str, targetpath: str) -> bool: """Add target to roles metadata and submit new metadata version""" @@ -124,8 +125,8 @@ def add_delegation(self, role: str) -> bool: data = {signer.public_key.keyid: signer.public_key.to_dict()} url = f"{self.base_url}/api/delegation/{role}" - r = requests.post(url, data=json.dumps(data), timeout=5) - if r.status_code != 200: + r = request("POST", url, body=json.dumps(data), timeout=5) + if r.status != 200: print(f"delegation failed with {r}") return False diff --git a/pyproject.toml b/pyproject.toml index 4eac696d2c..519739e5a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,6 @@ classifiers = [ "Topic :: Software Development", ] dependencies = [ - "requests>=2.19.1", "securesystemslib~=1.0", "urllib3<3,>=1.21.1", ] @@ -156,4 +155,4 @@ exclude_also = [ ] [tool.coverage.run] branch = true -omit = [ "tests/*", "tuf/ngclient/_internal/requests_fetcher.py" ] +omit = [ "tests/*", "tuf/ngclient/requests_fetcher.py" ] diff --git a/requirements/main.txt b/requirements/main.txt index e93071ff00..611c6589d8 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -7,4 +7,4 @@ # triggers CI/CD builds to automatically test against updated dependencies. # securesystemslib[crypto] -requests +urllib3 diff --git a/requirements/pinned.txt b/requirements/pinned.txt index e97461353f..038c8af1b0 100644 --- a/requirements/pinned.txt +++ b/requirements/pinned.txt @@ -4,21 +4,13 @@ # # pip-compile --output-file=requirements/pinned.txt --strip-extras requirements/main.txt # -certifi==2025.1.31 - # via requests cffi==1.17.1 # via cryptography -charset-normalizer==3.4.1 - # via requests cryptography==44.0.1 # via securesystemslib -idna==3.10 - # via requests pycparser==2.22 # via cffi -requests==2.32.3 - # via -r requirements/main.txt securesystemslib==1.2.0 # via -r requirements/main.txt urllib3==2.3.0 - # via requests + # via -r requirements/main.txt diff --git a/tuf/__init__.py b/tuf/__init__.py index b09503961c..4b25e51db8 100644 --- a/tuf/__init__.py +++ b/tuf/__init__.py @@ -3,5 +3,5 @@ """TUF.""" -# This value is used in the requests user agent. +# This value is used in the ngclient user agent. __version__ = "5.1.0" diff --git a/tuf/ngclient/__init__.py b/tuf/ngclient/__init__.py index 1d2084acf5..afab48f5cd 100644 --- a/tuf/ngclient/__init__.py +++ b/tuf/ngclient/__init__.py @@ -4,19 +4,13 @@ """TUF client public API.""" from tuf.api.metadata import TargetFile - -# requests_fetcher is public but comes from _internal for now (because -# sigstore-python 1.0 still uses the module from there). requests_fetcher -# can be moved out of _internal once sigstore-python 1.0 is not relevant. -from tuf.ngclient._internal.requests_fetcher import RequestsFetcher -from tuf.ngclient._internal.urllib3_fetcher import Urllib3Fetcher from tuf.ngclient.config import UpdaterConfig from tuf.ngclient.fetcher import FetcherInterface from tuf.ngclient.updater import Updater +from tuf.ngclient.urllib3_fetcher import Urllib3Fetcher __all__ = [ # noqa: PLE0604 FetcherInterface.__name__, - RequestsFetcher.__name__, Urllib3Fetcher.__name__, TargetFile.__name__, Updater.__name__, diff --git a/tuf/ngclient/_internal/requests_fetcher.py b/tuf/ngclient/requests_fetcher.py similarity index 94% rename from tuf/ngclient/_internal/requests_fetcher.py rename to tuf/ngclient/requests_fetcher.py index 2f89e47ab4..6edc699d9d 100644 --- a/tuf/ngclient/_internal/requests_fetcher.py +++ b/tuf/ngclient/requests_fetcher.py @@ -3,11 +3,14 @@ """Provides an implementation of ``FetcherInterface`` using the Requests HTTP library. -""" -# requests_fetcher is public but comes from _internal for now (because -# sigstore-python 1.0 still uses the module from there). requests_fetcher -# can be moved out of _internal once sigstore-python 1.0 is not relevant. +Note that this module is deprecated, and the default fetcher is +Urllib3Fetcher: +* RequestsFetcher is still available to make it easy to fall back to + previous implementation if issues are found with the Urllib3Fetcher +* If RequestsFetcher is used, note that `requests` must be explicitly + depended on: python-tuf does not do that. +""" from __future__ import annotations diff --git a/tuf/ngclient/updater.py b/tuf/ngclient/updater.py index 022d601f95..8c88a96ead 100644 --- a/tuf/ngclient/updater.py +++ b/tuf/ngclient/updater.py @@ -49,7 +49,8 @@ from tuf.api import exceptions from tuf.api.metadata import Root, Snapshot, TargetFile, Targets, Timestamp -from tuf.ngclient._internal import trusted_metadata_set, urllib3_fetcher +from tuf.ngclient import urllib3_fetcher +from tuf.ngclient._internal import trusted_metadata_set from tuf.ngclient.config import EnvelopeType, UpdaterConfig if TYPE_CHECKING: diff --git a/tuf/ngclient/_internal/urllib3_fetcher.py b/tuf/ngclient/urllib3_fetcher.py similarity index 100% rename from tuf/ngclient/_internal/urllib3_fetcher.py rename to tuf/ngclient/urllib3_fetcher.py diff --git a/verify_release b/verify_release index 549b7bab84..f6ae8330f0 100755 --- a/verify_release +++ b/verify_release @@ -10,7 +10,6 @@ on GitHub and PyPI match the built release artifacts. """ import argparse -import json import os import subprocess import sys @@ -20,10 +19,10 @@ from typing import Optional try: import build as _ # type: ignore[import-not-found] # noqa: F401 - import requests + from urllib3 import request except ImportError: - print("Error: verify_release requires modules 'requests' and 'build':") - print(" pip install requests build") + print("Error: verify_release requires modules 'urllib3' and 'build':") + print(" pip install urllib3 build") sys.exit(1) # Project variables @@ -75,9 +74,7 @@ def get_git_version() -> str: def get_github_version() -> str: """Return version string of latest GitHub release""" release_json = f"https://api.github.com/repos/{GITHUB_ORG}/{GITHUB_PROJECT}/releases/latest" - releases = json.loads( - requests.get(release_json, timeout=HTTP_TIMEOUT).content - ) + releases = request("GET", release_json, timeout=HTTP_TIMEOUT).json() return releases["tag_name"][1:] @@ -106,9 +103,11 @@ def verify_github_release(version: str, compare_dir: str) -> bool: with TemporaryDirectory() as github_dir: for filename in [tar, wheel]: url = f"{base_url}/v{version}/{filename}" - response = requests.get(url, stream=True, timeout=HTTP_TIMEOUT) + response = request( + "GET", url, preload_content=False, timeout=HTTP_TIMEOUT + ) with open(os.path.join(github_dir, filename), "wb") as f: - for data in response.iter_content(): + for data in response.stream(): f.write(data) return cmp(