Skip to content

Commit

Permalink
Merge pull request #1078 from lsst-sqre/tickets/DM-45858
Browse files Browse the repository at this point in the history
DM-45858: Update dependencies and fix broken tests
  • Loading branch information
rra committed Aug 22, 2024
2 parents 39a3ee6 + 6740827 commit 3277638
Show file tree
Hide file tree
Showing 12 changed files with 545 additions and 503 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repos:
- id: trailing-whitespace

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.7
rev: v0.6.1
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
3 changes: 3 additions & 0 deletions docs/dev/internals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ Python internal API
.. automodapi:: gafaelfawr.providers.oidc
:include-all-objects:

.. automodapi:: gafaelfawr.pydantic
:include-all-objects:

.. automodapi:: gafaelfawr.services.admin
:include-all-objects:

Expand Down
7 changes: 4 additions & 3 deletions docs/documenteer.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ extensions = [
nitpicky = true
nitpick_ignore = [
# Ignore missing cross-references for modules that don't provide
# intersphinx. The documentation itself should use double-quotes instead
# intersphinx. The documentation itself should use double-quotes instead
# of single-quotes to not generate a reference, but automatic references
# are generated from the type signatures and can't be avoided. These are
# are generated from the type signatures and can't be avoided. These are
# intentionally listed specifically because I've caught documentation bugs
# by having Sphinx complain about a new symbol.
["py:class", "fastapi.applications.FastAPI"],
Expand All @@ -45,7 +45,6 @@ nitpick_ignore = [
["py:class", "pydantic_core._pydantic_core.ValidationError"],
["py:class", "pydantic_settings.main.BaseSettings"],
["py:class", "pydantic_settings.sources.CliSettingsSource"],
["py:obj", "safir.pydantic.validate_exactly_one_of.<locals>.validator"],
["py:class", "starlette.datastructures.URL"],
["py:class", "starlette.middleware.base.BaseHTTPMiddleware"],
["py:class", "starlette.requests.Request"],
Expand All @@ -57,6 +56,8 @@ nitpick_ignore = [
# the combination of Sphinx extensions we're using confuse themselves and
# there doesn't seem to be any way to fix this.
["py:class", "asyncio.locks.Lock"],
# Cannot be represented by intersphinx.
["py:obj", "safir.pydantic._validators.validate_exactly_one_of.<locals>.validator"],
]
nitpick_ignore_regex = [
["py:class", "kubernetes_asyncio\\.client\\.models\\..*"],
Expand Down
232 changes: 116 additions & 116 deletions requirements/dev.txt

Large diffs are not rendered by default.

528 changes: 281 additions & 247 deletions requirements/main.txt

Large diffs are not rendered by default.

44 changes: 22 additions & 22 deletions requirements/tox.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# This file was autogenerated by uv via the following command:
# uv pip compile --generate-hashes --output-file requirements/tox.txt requirements/tox.in
cachetools==5.4.0 \
--hash=sha256:3ae3b49a3d5e28a77a0be2b37dbcb89005058959cb2323858c2657c4a8cab474 \
--hash=sha256:b8adc2e7c07f105ced7bc56dbb6dfbe7c4a00acce20e2227b3f355be89bc6827
cachetools==5.5.0 \
--hash=sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292 \
--hash=sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a
# via
# -c requirements/main.txt
# tox
Expand Down Expand Up @@ -192,25 +192,25 @@ urllib3==2.2.2 \
# -c requirements/main.txt
# docker
# requests
uv==0.2.36 \
--hash=sha256:083e56a18fc33395aeed4f56a47003e08f2ad9d5039af63ad5b107a241d0e7a3 \
--hash=sha256:139889680c2475afbab61c725df951c4dfa030c42c4eaa8f27d05286c96e8aab \
--hash=sha256:3f18322decfb0da577e40675620f6e6b9ffe1d8ee1de88a448bfe67fe7884626 \
--hash=sha256:463a45a34277b9334e500fce463f59408a6bab0c1b5cb3023f25185a4805a562 \
--hash=sha256:5a3800d2484b989041139ef96b395cec0e4e0a13132584b0147c739063a2494f \
--hash=sha256:5c8d624975f8355e00ad5f802ed27fcfc7b86d0bd50b57efe24bd665fd3f9a9b \
--hash=sha256:8753851cc10b0a67e5c5dd29a6f35a072341290cf27a7bb3193ddd92bda19f51 \
--hash=sha256:8820dd5b77ffcda07dde09712a43d969d39b0aace112d8074c540f19a4911cc2 \
--hash=sha256:89d3fb3d7a66fa4a4f7c938be0277457fe71179ec4e72758cfe16faec1daa362 \
--hash=sha256:8e5e2e8e218eb672a3bb57af0ab2d2d3de79119b5dc6b6edb03d349739e474ff \
--hash=sha256:a08d485cd8eae0352b4765005a4499ad5db073c3534866d68617bbb831ee219a \
--hash=sha256:a4fddaf0a6a995591042a57ac48557b9b2c1e2c7f09e0f7880f40c34e61f53f8 \
--hash=sha256:a7961f4d88100fc48129c918545cbb17b9a0d8e3d163c65985e1d1682e056321 \
--hash=sha256:a837b799e3af1535473b8ab14e414e50f595d547d295879db0d6b0943b7476df \
--hash=sha256:d093fd10aaf29e85128beaa514f8d37d7374cf1d1a95da966e15788a6fe7d55d \
--hash=sha256:e36cd4e9c1187d155139b98bcd2cfbfb275f9f601c550fcc38a283983c74f93d \
--hash=sha256:e79a4cdb3b89b011fafcaa853ebbb9254115f3f7cadbd9141492c48ceeac1b2d \
--hash=sha256:f1d711629dd8610933687ceea4ad82156ef7b2102c4e9da72afe6c01981f8a1a
uv==0.3.0 \
--hash=sha256:084551ee0743339aa5d0d4c76a94c9f9df16c33030b850f0cd98f316db7b42cc \
--hash=sha256:0da4f060d583325846cde0727a8cc0cb4e8c63b30ac9373dae213a7315056d90 \
--hash=sha256:160a1f3b01298942d6cfe21f95a9b7daa3eb73231ba1fc4689157eb9f23b3438 \
--hash=sha256:21ebc6ca30df7ff57a8e17e3abeeba8a9d1d4ac79c1adf842fa42d48a5c7f372 \
--hash=sha256:24a1388f5e285058f97576b7dfee79bb5007a712a9e368f3fcdcfeb2dfd9ce92 \
--hash=sha256:2f937ebdf9976ec1ffe7228fd608ef3e6ce2a61ed68cf7b157ae6900a9c80f41 \
--hash=sha256:39a4276afe0808ca6c033e0cd6cb73249f934b4a0c9d7b18a944f3f8ea635e27 \
--hash=sha256:3b62e44f61a154303fc9f4aa87ae54891957d49769d21dcf2be9c22e640c3e92 \
--hash=sha256:4303364d717b1def58e82b11271259d2ee3bb03da0ca6111819ee254f65b38f4 \
--hash=sha256:503fc619238550be222b41422b415677c9b8045c92a9815f80ff5d7477671fe6 \
--hash=sha256:52b3a6110705ff27462ddc68657fedf8a296ed545619a90fa73354f130ad632e \
--hash=sha256:5c826d9daace67d67790503b0c1152093b3cecd35a91de10f5bb9e26afea9de9 \
--hash=sha256:6d1025349cbaeba9a974d413795d0ce8d37de5ad7fb7654c0519968b2c083ba1 \
--hash=sha256:a15b2321444f3668bc95863d2b13ce44ea54053189427ea48d112ecd8b3d2f89 \
--hash=sha256:a71b7080ee6d7658b22f93aa750cbfd19111cd6c8ac643a73d6778598dd06559 \
--hash=sha256:b44ebf501de5eef33e4f3cf4b6ea9a458d1f1b3cf26737c25ac507ab7914076a \
--hash=sha256:d3da56b87ec5aa4f2ae572127c754655bad3820dd41a4d37ed4d5e2f67035990 \
--hash=sha256:d87ff76da5128036c05db0291db7510a85cb8efb86538e8f49adc8074bb292f0
# via tox-uv
virtualenv==20.26.3 \
--hash=sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a \
Expand Down
28 changes: 6 additions & 22 deletions src/gafaelfawr/models/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@
from typing import Any, Generic, Self, TypeVar
from urllib.parse import parse_qs, urlencode

from pydantic import (
BaseModel,
ConfigDict,
Field,
field_serializer,
field_validator,
)
from pydantic import BaseModel, ConfigDict, Field, field_validator
from safir.datetime import current_datetime
from safir.pydantic import normalize_datetime
from starlette.datastructures import URL

from ..exceptions import InvalidCursorError
from ..pydantic import Timestamp
from ..util import normalize_ip_address, normalize_scopes
from .token import TokenType

Expand Down Expand Up @@ -75,7 +69,7 @@ class AdminHistoryEntry(BaseModel):
),
)

event_time: datetime = Field(
event_time: Timestamp = Field(
default_factory=current_datetime,
title="Timestamp",
description="When the change was made",
Expand All @@ -84,9 +78,6 @@ class AdminHistoryEntry(BaseModel):

model_config = ConfigDict(from_attributes=True)

_normalize_event_time = field_validator("event_time", mode="before")(
normalize_datetime
)
_normalize_ip_address = field_validator("ip_address", mode="before")(
normalize_ip_address
)
Expand Down Expand Up @@ -267,7 +258,7 @@ class TokenChangeHistoryEntry(BaseModel):
examples=["some-service"],
)

expires: datetime | None = Field(
expires: Timestamp | None = Field(
None,
title="Expiration of the token",
description=(
Expand Down Expand Up @@ -309,7 +300,7 @@ class TokenChangeHistoryEntry(BaseModel):
examples=[["read:some"]],
)

old_expires: datetime | None = Field(
old_expires: Timestamp | None = Field(
None,
title="Previous expiration of the token",
description=(
Expand Down Expand Up @@ -339,24 +330,17 @@ class TokenChangeHistoryEntry(BaseModel):
examples=["198.51.100.50"],
)

event_time: datetime = Field(
event_time: Timestamp = Field(
default_factory=current_datetime,
title="Whent he change was made",
examples=[1614985631],
)

model_config = ConfigDict(from_attributes=True)

@field_serializer("expires", "old_expires", "event_time")
def _serialize_datetime(self, time: datetime | None) -> int | None:
return int(time.timestamp()) if time is not None else None

_normalize_scopes = field_validator("scopes", "old_scopes", mode="before")(
normalize_scopes
)
_normalize_expires = field_validator(
"expires", "old_expires", "event_time", mode="before"
)(normalize_datetime)
_normalize_ip_address = field_validator("ip_address", mode="before")(
normalize_ip_address
)
Expand Down
15 changes: 3 additions & 12 deletions src/gafaelfawr/models/oidc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
from __future__ import annotations

from contextlib import suppress
from datetime import datetime
from enum import StrEnum
from typing import Any, Self

from pydantic import BaseModel, Field, field_serializer, field_validator
from pydantic import BaseModel, Field
from safir.datetime import current_datetime
from safir.pydantic import normalize_datetime

from ..constants import ALGORITHM, OIDC_AUTHORIZATION_LIFETIME
from ..exceptions import InvalidGrantError
from ..pydantic import Timestamp
from ..util import random_128_bits
from .token import Token

Expand Down Expand Up @@ -149,7 +148,7 @@ class OIDCAuthorization(BaseModel):
title="The underlying authentication token for the user",
)

created_at: datetime = Field(
created_at: Timestamp = Field(
default_factory=current_datetime,
title="When the authorization was created",
)
Expand All @@ -167,14 +166,6 @@ class OIDCAuthorization(BaseModel):
),
)

@field_serializer("created_at")
def _serialize_datetime(self, time: datetime | None) -> int | None:
return int(time.timestamp()) if time is not None else None

_normalize_created_at = field_validator("created_at", mode="before")(
normalize_datetime
)

@property
def lifetime(self) -> int:
"""The object lifetime in seconds."""
Expand Down
21 changes: 4 additions & 17 deletions src/gafaelfawr/models/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@
ConfigDict,
Field,
ValidationInfo,
field_serializer,
field_validator,
)
from safir.datetime import current_datetime
from safir.pydantic import normalize_datetime

from ..constants import USERNAME_REGEX
from ..exceptions import InvalidTokenError
from ..pydantic import Timestamp
from ..util import normalize_scopes, random_128_bits
from .userinfo import Group

Expand Down Expand Up @@ -174,24 +173,20 @@ class TokenBase(BaseModel):
examples=[["read:all", "user:token"]],
)

created: datetime = Field(
created: Timestamp = Field(
default_factory=current_datetime,
title="Creation time",
description="Creation timestamp of the token in seconds since epoch",
examples=[1614986130],
)

expires: datetime | None = Field(
expires: Timestamp | None = Field(
None,
title="Expiration time",
description="Expiration timestamp of the token in seconds since epoch",
examples=[1616986130],
)

@field_serializer("created", "expires")
def _serialize_datetime(self, time: datetime | None) -> int | None:
return int(time.timestamp()) if time is not None else None

_normalize_scopes = field_validator("scopes", mode="before")(
normalize_scopes
)
Expand Down Expand Up @@ -232,7 +227,7 @@ class TokenInfo(TokenBase):
max_length=64,
)

last_used: datetime | None = Field(
last_used: Timestamp | None = Field(
None,
title="Last used",
description="When the token was last used in seconds since epoch",
Expand All @@ -249,14 +244,6 @@ class TokenInfo(TokenBase):

model_config = ConfigDict(from_attributes=True)

@field_serializer("created", "expires", "last_used")
def _serialize_datetime(self, time: datetime | None) -> int | None:
return int(time.timestamp()) if time is not None else None

_normalize_created = field_validator(
"created", "last_used", "expires", mode="before"
)(normalize_datetime)


class TokenUserInfo(BaseModel):
"""User metadata stored with the token.
Expand Down
19 changes: 19 additions & 0 deletions src/gafaelfawr/pydantic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Pydantic data types for Gafaelfawr models."""

from __future__ import annotations

from datetime import datetime
from typing import Annotated, TypeAlias

from pydantic import BeforeValidator, PlainSerializer
from safir.pydantic import normalize_datetime

__all__ = ["Timestamp"]


Timestamp: TypeAlias = Annotated[
datetime,
BeforeValidator(normalize_datetime),
PlainSerializer(lambda t: int(t.timestamp()), return_type=int),
]
"""Type for a `datetime` field that only accepts seconds since epoch."""
Loading

0 comments on commit 3277638

Please sign in to comment.