Skip to content
Merged
Show file tree
Hide file tree
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
Prev Previous commit
Next Next commit
Fix linter errors.
Also begin adding a test for multi-maps.
  • Loading branch information
ZeroIntensity committed Jan 1, 2026
commit cef49bb2e34a9aa3a2229b1a9b83ae159418a595
3 changes: 2 additions & 1 deletion src/view/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
if TYPE_CHECKING:
from collections.abc import Callable

from view.core.headers import HTTPHeaders

from view.core.response import (
Response,
TextResponse,
ViewResult,
wrap_view_result,
)
from view.core.headers import HTTPHeaders

__all__ = ("in_memory_cache",)

Expand Down
2 changes: 1 addition & 1 deletion src/view/core/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from view.run.asgi import ASGIProtocol
from view.run.wsgi import WSGIProtocol

__all__ = "BaseApp", "as_app", "App"
__all__ = "App", "BaseApp", "as_app"

T = TypeVar("T")
P = ParamSpec("P")
Expand Down
10 changes: 7 additions & 3 deletions src/view/core/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, TypeAlias

from view.exceptions import InvalidTypeError
from typing_extensions import Self

from view.core.multi_map import MultiMap
from view.exceptions import InvalidTypeError

if TYPE_CHECKING:
from view.run.asgi import ASGIHeaders
Expand All @@ -26,7 +28,9 @@ class LowerStr(str):
comparisons.
"""

def __new__(cls, data: object) -> LowerStr:
__slots__ = ()

def __new__(cls, data: object) -> Self:
return super().__new__(cls, cls._to_lower(data))

@staticmethod
Expand Down Expand Up @@ -67,7 +71,7 @@ def get_exactly_one(self, key: str) -> str:
return super().get_exactly_one(LowerStr(key))

def with_new_value(self, key: str, value: str) -> HTTPHeaders:
new_sequence = list(self.as_sequence()) + [(LowerStr(key), value)]
new_sequence = [*list(self.as_sequence()), (LowerStr(key), value)]
return type(self)(new_sequence)


Expand Down
15 changes: 10 additions & 5 deletions src/view/core/multi_map.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from __future__ import annotations

from collections.abc import (
ItemsView,
Iterable,
Iterator,
KeysView,
Mapping,
Sequence,
KeysView,
ValuesView,
ItemsView,
)
from typing import Any, TypeVar

from view.exceptions import ViewError

__all__ = "HasMultipleValuesError", "MultiMap"
Expand All @@ -32,7 +34,7 @@ class MultiMap(Mapping[KeyT, ValueT]):
Mapping of individual keys to one or many values.
"""

__slots__ = "_values"
__slots__ = ("_values",)

def __init__(self, items: Iterable[tuple[KeyT, ValueT]] = ()) -> None:
self._values: dict[KeyT, list[ValueT]] = {}
Expand Down Expand Up @@ -75,6 +77,9 @@ def __ne__(self, other: object, /) -> bool:
def __repr__(self) -> str:
return f"MultiMap({self.as_sequence()})"

def __hash__(self) -> int:
return hash(self._values)

def _as_flat(self) -> dict[KeyT, ValueT]:
"""
Turn this into a "flat" representation of the mapping in which all
Expand Down Expand Up @@ -137,7 +142,7 @@ def as_sequence(self) -> Sequence[tuple[KeyT, ValueT]]:
result: list[tuple[KeyT, ValueT]] = []
for key, values in self._values.items():
for value in values:
result.append((key, value))
result.append((key, value)) # noqa: PERF401

return result

Expand All @@ -147,5 +152,5 @@ def with_new_value(
"""
Create a copy of this map with a new key and value included.
"""
new_sequence = list(self.as_sequence()) + [(key, value)]
new_sequence = [*list(self.as_sequence()), (key, value)]
return type(self)(new_sequence)
2 changes: 1 addition & 1 deletion src/view/core/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from typing import TYPE_CHECKING, Any

from view.core.body import BodyMixin
from view.core.router import normalize_route
from view.core.multi_map import MultiMap
from view.core.router import normalize_route

if TYPE_CHECKING:
from collections.abc import Mapping
Expand Down
4 changes: 2 additions & 2 deletions src/view/core/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
from view.core.body import BodyMixin
from view.core.headers import (
HeadersLike,
LowerStr,
HTTPHeaders,
LowerStr,
as_real_headers,
)
from view.exceptions import InvalidTypeError, ViewError

__all__ = "Response", "ViewResult", "ResponseLike"
__all__ = "Response", "ResponseLike", "ViewResult"


@dataclass(slots=True)
Expand Down
4 changes: 2 additions & 2 deletions src/view/run/servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,6 @@ def run_app_on_any_server(self) -> None:
) from error

# I'm not sure what Ruff is complaining about here
for start_server in servers.values(): # noqa: RET503
for start_server in servers.values():
with suppress(ImportError):
return start_server() # noqa: RET503
return start_server()
2 changes: 1 addition & 1 deletion src/view/run/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from collections.abc import Callable, Iterable
from typing import IO, TYPE_CHECKING, Any, TypeAlias

from view.core.headers import wsgi_to_headers, headers_to_wsgi
from view.core.headers import headers_to_wsgi, wsgi_to_headers
from view.core.request import Method, Request, extract_query_parameters
from view.core.status_codes import STATUS_STRINGS

Expand Down
2 changes: 1 addition & 1 deletion src/view/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
from collections.abc import AsyncGenerator, Awaitable

from view.core.app import BaseApp
from view.core.response import Response
from view.core.headers import HTTPHeaders
from view.core.response import Response

__all__ = ("AppTestClient",)

Expand Down
5 changes: 5 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
from view.core.app import App, as_app
from view.exceptions import InvalidTypeError
from view.core.multi_map import MultiMap


def test_as_app_invalid():
Expand All @@ -16,3 +17,7 @@ def test_invalid_type_route():

with pytest.raises(InvalidTypeError):
app.get("/")(object()) # type: ignore


def test_multi_map():
pass