Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update dependencies, fix deprecations #240

Merged
merged 3 commits into from
Mar 11, 2024
Merged
Changes from 1 commit
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
Next Next commit
Update dependencies, fix deprecations
Update pre-commit dependencies and apply formatting changes. Update
test suite for the new call syntax for initializing an httpx
AsyncClient with a specific application.
rra committed Mar 11, 2024
commit 7819189c09134104ac3100760ace151dbb850a37
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-merge-conflict
- id: check-toml
- id: check-yaml
- id: trailing-whitespace

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.286
rev: v0.3.2
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]

- repo: https://github.com/ambv/black
rev: 23.3.0
rev: 24.2.0
hooks:
- id: black

- repo: https://github.com/asottile/blacken-docs
rev: 1.14.0
rev: 1.16.0
hooks:
- id: blacken-docs
additional_dependencies: [black==23.1.0]
10 changes: 6 additions & 4 deletions docs/user-guide/gafaelfawr.rst
Original file line number Diff line number Diff line change
@@ -60,14 +60,16 @@ This is most easily done by defining a fixture, as follows.

import pytest_asyncio
from fastapi import FastAPI
from httpx import AsyncClient
from httpx import ASGITransport, AsyncClient


@pytest_asyncio.fixture
async def client(app: FastAPI) -> AsyncIterator[AsyncClient]:
base = "https://example.com/"
hdrs = {"X-Auth-Request-User": "user"}
async with AsyncClient(app=app, base_url=base, headers=hdrs) as client:
async with AsyncClient(
transport=ASGITransport(app=app), # type: ignore[arg-type]
base_url="https://example.com/",
headers={"X-Auth-Request-User": "user"},
) as client:
yield client

Tests can then use ``client`` as a fixture and don't have to provide the ``X-Auth-Request-User`` header with every call.
14 changes: 8 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -187,6 +187,9 @@ exclude = [
"docs/**",
]
line-length = 79
target-version = "py311"

[tool.ruff.lint]
ignore = [
"ANN101", # self should not have a type annotation
"ANN102", # cls should not have a type annotation
@@ -228,9 +231,8 @@ ignore = [
"PLW0603", # necessary trick for safir.logging
]
select = ["ALL"]
target-version = "py311"

[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
"src/safir/testing/**" = [
"S101", # test support functions are allowed to use assert
]
@@ -244,13 +246,13 @@ target-version = "py311"
"SLF001", # tests are allowed to access private members
]

[tool.ruff.isort]
[tool.ruff.lint.isort]
known-first-party = ["safir", "tests"]
split-on-trailing-comma = false

# These are too useful as attributes or methods to allow the conflict with the
# built-in to rule out their use.
[tool.ruff.flake8-builtins]
[tool.ruff.lint.flake8-builtins]
builtins-ignorelist = [
"all",
"any",
@@ -262,11 +264,11 @@ builtins-ignorelist = [
"type",
]

[tool.ruff.flake8-pytest-style]
[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false
mark-parentheses = false

[tool.ruff.pydocstyle]
[tool.ruff.lint.pydocstyle]
convention = "numpy"

[tool.scriv]
2 changes: 1 addition & 1 deletion src/safir/asyncio.py
Original file line number Diff line number Diff line change
@@ -123,7 +123,7 @@ async def iterator() -> AsyncIterator[T]:
for item in contents[position:end]:
if item is Ellipsis:
return
yield item
yield item # type: ignore[misc]
position = end
elif contents and contents[-1] is Ellipsis:
return
12 changes: 4 additions & 8 deletions src/safir/database.py
Original file line number Diff line number Diff line change
@@ -82,13 +82,11 @@ def _build_database_url(


@overload
def datetime_from_db(time: datetime) -> datetime:
...
def datetime_from_db(time: datetime) -> datetime: ...


@overload
def datetime_from_db(time: None) -> None:
...
def datetime_from_db(time: None) -> None: ...


def datetime_from_db(time: datetime | None) -> datetime | None:
@@ -113,13 +111,11 @@ def datetime_from_db(time: datetime | None) -> datetime | None:


@overload
def datetime_to_db(time: datetime) -> datetime:
...
def datetime_to_db(time: datetime) -> datetime: ...


@overload
def datetime_to_db(time: None) -> None:
...
def datetime_to_db(time: None) -> None: ...


def datetime_to_db(time: datetime | None) -> datetime | None:
6 changes: 2 additions & 4 deletions src/safir/datetime.py
Original file line number Diff line number Diff line change
@@ -44,13 +44,11 @@ def current_datetime(*, microseconds: bool = False) -> datetime:


@overload
def format_datetime_for_logging(timestamp: datetime) -> str:
...
def format_datetime_for_logging(timestamp: datetime) -> str: ...


@overload
def format_datetime_for_logging(timestamp: None) -> None:
...
def format_datetime_for_logging(timestamp: None) -> None: ...


def format_datetime_for_logging(timestamp: datetime | None) -> str | None:
5 changes: 2 additions & 3 deletions src/safir/testing/kubernetes.py
Original file line number Diff line number Diff line change
@@ -170,8 +170,7 @@ class _KubernetesModel(Protocol):

def to_dict(
self, serialize: bool = False # noqa: FBT001, FBT002
) -> dict[str, Any]:
...
) -> dict[str, Any]: ...


class _EventStream:
@@ -429,7 +428,7 @@ def get_all_objects_for_test(self, kind: str) -> list[Any]:
All objects of that kind found in the mock, sorted by namespace
and then name.
"""
key = self._custom_kinds[kind] if kind in self._custom_kinds else kind
key = self._custom_kinds.get(kind, kind)
results = []
for namespace in sorted(self._objects.keys()):
if key not in self._objects[namespace]:
2 changes: 1 addition & 1 deletion src/safir/testing/uvicorn.py
Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ def _wait_for_server(port: int, timeout: float = 5.0) -> None:
sock = socket.socket()
sock.settimeout(socket_timeout)
sock.connect(("localhost", port))
except socket.timeout:
except TimeoutError:
pass
except OSError as e:
if e.errno not in (errno.ETIMEDOUT, errno.ECONNREFUSED):
6 changes: 4 additions & 2 deletions tests/dependencies/arq_test.py
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
from arq.constants import default_queue_name
from asgi_lifespan import LifespanManager
from fastapi import Depends, FastAPI, HTTPException
from httpx import AsyncClient
from httpx import ASGITransport, AsyncClient

from safir.arq import ArqMode, JobNotFound, JobResultUnavailable, MockArqQueue
from safir.dependencies.arq import arq_dependency
@@ -121,8 +121,10 @@ async def post_job_complete(
except JobNotFound as e:
raise HTTPException(status_code=404, detail=str(e)) from e

transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "http://example.com"
async with LifespanManager(app):
async with AsyncClient(app=app, base_url="http://example.com") as c:
async with AsyncClient(transport=transport, base_url=base_url) as c:
r = await c.post("/")
assert r.status_code == 200
data = r.json()
6 changes: 4 additions & 2 deletions tests/dependencies/db_session_test.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
import pytest
import structlog
from fastapi import Depends, FastAPI
from httpx import AsyncClient
from httpx import ASGITransport, AsyncClient
from sqlalchemy import Column, String
from sqlalchemy.ext.asyncio import async_scoped_session
from sqlalchemy.future import select
@@ -66,7 +66,9 @@ async def get_list(
result = await session.scalars(select(User.username))
return list(result.all())

async with AsyncClient(app=app, base_url="https://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "https://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get("/list")
assert r.status_code == 200
assert r.json() == []
14 changes: 10 additions & 4 deletions tests/dependencies/gafaelfawr_test.py
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
import pytest
from _pytest.logging import LogCaptureFixture
from fastapi import Depends, FastAPI
from httpx import AsyncClient
from httpx import ASGITransport, AsyncClient
from structlog.stdlib import BoundLogger

from safir.dependencies.gafaelfawr import (
@@ -30,7 +30,9 @@ async def handler(
) -> dict[str, str]:
return {"user": user}

async with AsyncClient(app=app, base_url="https://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "https://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get("/")
assert r.status_code == 422

@@ -49,7 +51,9 @@ async def handler(
) -> dict[str, str]:
return {"token": token}

async with AsyncClient(app=app, base_url="https://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "https://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get("/")
assert r.status_code == 422

@@ -74,7 +78,9 @@ async def handler(
return {}

caplog.clear()
async with AsyncClient(app=app, base_url="https://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "https://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get("/", headers={"User-Agent": ""})
assert r.status_code == 422

6 changes: 4 additions & 2 deletions tests/dependencies/http_client_test.py
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
import respx
from asgi_lifespan import LifespanManager
from fastapi import Depends, FastAPI
from httpx import AsyncClient
from httpx import ASGITransport, AsyncClient

from safir.dependencies.http_client import http_client_dependency

@@ -39,7 +39,9 @@ async def handler(
await http_client.get("https://www.google.com")
return {}

transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "https://example.com"
async with LifespanManager(app):
async with AsyncClient(app=app, base_url="http://example.com") as c:
async with AsyncClient(transport=transport, base_url=base_url) as c:
r = await c.get("/")
assert r.status_code == 200
10 changes: 7 additions & 3 deletions tests/dependencies/logger_test.py
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
import pytest
from _pytest.logging import LogCaptureFixture
from fastapi import Depends, FastAPI
from httpx import AsyncClient
from httpx import ASGITransport, AsyncClient
from structlog.stdlib import BoundLogger

from safir.dependencies.logger import logger_dependency
@@ -31,7 +31,9 @@ async def handler(
return {}

caplog.clear()
async with AsyncClient(app=app, base_url="http://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "http://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get(
"/", headers={"User-Agent": "some-user-agent/1.0"}
)
@@ -68,7 +70,9 @@ async def handler(
return {}

caplog.clear()
async with AsyncClient(app=app, base_url="http://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "https://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get(
"/",
headers={
6 changes: 4 additions & 2 deletions tests/fastapi_test.py
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@

import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from httpx import ASGITransport, AsyncClient

from safir.fastapi import ClientRequestError, client_request_error_handler
from safir.models import ErrorLocation, ErrorModel
@@ -43,7 +43,9 @@ async def address_error() -> dict[str, str]:
async def permission_error() -> dict[str, str]:
raise PermissionDeniedError("Permission denied")

async with AsyncClient(app=app, base_url="https://example.com/") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "https://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get("/user")
assert r.status_code == 404
assert r.json() == {
6 changes: 4 additions & 2 deletions tests/middleware/ivoa_test.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

import pytest
from fastapi import FastAPI, Query
from httpx import AsyncClient
from httpx import ASGITransport, AsyncClient

from safir.middleware.ivoa import CaseInsensitiveQueryMiddleware

@@ -36,7 +36,9 @@ async def list_handler(
) -> dict[str, list[str]]:
return {"param": param}

async with AsyncClient(app=app, base_url="https://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "https://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get("/", params={"param": "foo"})
assert r.status_code == 200
assert r.json() == {"param": "foo"}
26 changes: 19 additions & 7 deletions tests/middleware/x_forwarded_test.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

import pytest
from fastapi import FastAPI, Request
from httpx import AsyncClient
from httpx import ASGITransport, AsyncClient
from starlette.datastructures import Headers

from safir.middleware.x_forwarded import XForwardedMiddleware
@@ -31,7 +31,9 @@ async def handler(request: Request) -> dict[str, str]:
assert request.url == "https://foo.example.com/"
return {}

async with AsyncClient(app=app, base_url="http://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "http://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get(
"/",
headers={
@@ -56,7 +58,9 @@ async def handler(request: Request) -> dict[str, str]:
assert request.url == "http://example.com/"
return {}

async with AsyncClient(app=app, base_url="http://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "http://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get(
"/",
headers={
@@ -80,7 +84,9 @@ async def handler(request: Request) -> dict[str, str]:
assert request.url == "http://example.com/"
return {}

async with AsyncClient(app=app, base_url="http://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "http://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get("/")
assert r.status_code == 200

@@ -97,7 +103,9 @@ async def handler(request: Request) -> dict[str, str]:
assert request.url == "https://example.com/"
return {}

async with AsyncClient(app=app, base_url="http://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "http://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get(
"/",
headers={
@@ -121,7 +129,9 @@ async def handler(request: Request) -> dict[str, str]:
assert request.url == "https://example.com/"
return {}

async with AsyncClient(app=app, base_url="http://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "http://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get(
"/",
headers={
@@ -145,7 +155,9 @@ async def handler(request: Request) -> dict[str, str]:
assert request.url == "http://example.com/"
return {}

async with AsyncClient(app=app, base_url="http://example.com") as client:
transport = ASGITransport(app=app) # type: ignore[arg-type]
base_url = "http://example.com"
async with AsyncClient(transport=transport, base_url=base_url) as client:
r = await client.get(
"/", headers={"X-Forwarded-For": "10.10.10.10, 11.11.11.11"}
)