diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 69efceca7e08..754fcf24075e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -19,6 +19,11 @@ updates: - dependency-name: "botocore" - dependency-name: "botocore-stubs" groups: + celery: + # Keep both celery and kombu together + patterns: + - "celery" + - "kombu" psycopg: # Keep both psycopg and psycopg-c together patterns: diff --git a/.github/workflows/node-ci.yml b/.github/workflows/node-ci.yml index 364f610cef80..0fbab7c8bd10 100644 --- a/.github/workflows/node-ci.yml +++ b/.github/workflows/node-ci.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20.5.0 + node-version: 20.9.0 cache: 'npm' - name: Install Node dependencies run: npm ci diff --git a/warehouse/config.py b/warehouse/config.py index 63e3915ae63f..378bdf303f85 100644 --- a/warehouse/config.py +++ b/warehouse/config.py @@ -199,7 +199,6 @@ def configure(settings=None): maybe_set(settings, "oidc.jwk_cache_url", "REDIS_URL") maybe_set(settings, "database.url", "DATABASE_URL") maybe_set(settings, "elasticsearch.url", "ELASTICSEARCH_URL") - maybe_set(settings, "elasticsearch.url", "ELASTICSEARCH_SIX_URL") maybe_set(settings, "sentry.dsn", "SENTRY_DSN") maybe_set(settings, "sentry.transport", "SENTRY_TRANSPORT") maybe_set(settings, "sessions.url", "REDIS_URL") diff --git a/warehouse/db.py b/warehouse/db.py index 243883222d1c..9438daad72f0 100644 --- a/warehouse/db.py +++ b/warehouse/db.py @@ -30,7 +30,7 @@ from warehouse.metrics import IMetricsService from warehouse.utils.attrs import make_repr -__all__ = ["includeme", "metadata", "ModelBase"] +__all__ = ["includeme", "metadata", "ModelBase", "Model"] logger = logging.getLogger(__name__) diff --git a/warehouse/macaroons/caveats/_core.py b/warehouse/macaroons/caveats/_core.py index c03a4ea4f541..2bfe6ca805f3 100644 --- a/warehouse/macaroons/caveats/_core.py +++ b/warehouse/macaroons/caveats/_core.py @@ -15,7 +15,7 @@ import json import typing -from collections.abc import Mapping, Sequence +from collections.abc import Callable, Mapping, Sequence from dataclasses import dataclass from typing import Any, ClassVar, TypeVar @@ -109,13 +109,7 @@ def lookup(self, /, tag: int) -> type[Caveat] | None: _caveat_registry = _CaveatRegistry() -# TODO: The return signature detected is `"Union[Type[Dataclass], DataclassProxy]"`, -# but the expectation is `Type[Dataclass]`. -# See https://github.com/pydantic/pydantic/issues/4498 but not exactly the same. -# This might not be corrected in pydantic until 2.0. -# Original signature with type hints: -# def as_caveat(*, tag: int) -> Callable[[type[T]], type[T]]: -def as_caveat(*, tag: int): +def as_caveat(*, tag: int) -> Callable[[type[T]], type[T]]: def deco(cls: type[T]) -> type[T]: _caveat_registry.add(tag, typing.cast(type[Caveat], cls)) return cls diff --git a/warehouse/oidc/views.py b/warehouse/oidc/views.py index 2a326671f1d7..251e13677c1c 100644 --- a/warehouse/oidc/views.py +++ b/warehouse/oidc/views.py @@ -89,7 +89,7 @@ def _invalid(errors): ) try: - payload = TokenPayload.parse_raw(request.body) + payload = TokenPayload.model_validate_json(request.body) unverified_jwt = payload.token except ValidationError as exc: return _invalid(errors=[{"code": "invalid-payload", "description": str(exc)}]) diff --git a/warehouse/utils/__init__.py b/warehouse/utils/__init__.py index d839c8d12d21..b6bb743e9fb0 100644 --- a/warehouse/utils/__init__.py +++ b/warehouse/utils/__init__.py @@ -13,8 +13,8 @@ import datetime -def now(): - return datetime.datetime.utcnow() +def now() -> datetime.datetime: + return datetime.datetime.now(datetime.UTC) def dotted_navigator(path): diff --git a/warehouse/utils/crypto.py b/warehouse/utils/crypto.py index c8f640623a15..87e7c413d0d6 100644 --- a/warehouse/utils/crypto.py +++ b/warehouse/utils/crypto.py @@ -34,7 +34,7 @@ ] -def random_token(): +def random_token() -> str: token = base64.urlsafe_b64encode(os.urandom(32)).rstrip(b"=") return token.decode("utf8") diff --git a/warehouse/utils/gravatar.py b/warehouse/utils/gravatar.py index ed4b8f70a59c..bc75d1e26b13 100644 --- a/warehouse/utils/gravatar.py +++ b/warehouse/utils/gravatar.py @@ -14,7 +14,7 @@ import urllib.parse -def _hash(email): +def _hash(email: str | None) -> str: if email is None: email = "" @@ -30,5 +30,5 @@ def gravatar(request, email, size=80): return request.camo_url("?".join([url, urllib.parse.urlencode(params)])) -def profile(email): +def profile(email: str) -> str: return f"https://gravatar.com/{_hash(email)}" diff --git a/warehouse/utils/webauthn.py b/warehouse/utils/webauthn.py index d7505bb79cfa..d77ec2fbddf0 100644 --- a/warehouse/utils/webauthn.py +++ b/warehouse/utils/webauthn.py @@ -127,7 +127,7 @@ def verify_registration_response(response, challenge, *, rp_id, origin): # for the individual challenge. encoded_challenge = _webauthn_b64encode(challenge) try: - _credential = RegistrationCredential.parse_raw(response) + _credential = RegistrationCredential.model_validate_json(response) return pywebauthn.verify_registration_response( credential=_credential, expected_challenge=encoded_challenge, @@ -160,7 +160,7 @@ def verify_assertion_response(assertion, *, challenge, user, origin, rp_id): for public_key, current_sign_count in webauthn_user_public_keys: try: - _credential = AuthenticationCredential.parse_raw(assertion) + _credential = AuthenticationCredential.model_validate_json(assertion) return pywebauthn.verify_authentication_response( credential=_credential, expected_challenge=encoded_challenge,