Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 8 additions & 0 deletions .claude/settings.local.json
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's worth adding

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"Bash(uvx ty:*)"
],
"deny": []
}
}
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:

- uses: actions/setup-python@v6
with:
python-version: "3.9"
python-version: "3.10"

- name: Install Pre-Commit
run: python -m pip install pre-commit
Expand All @@ -40,7 +40,7 @@ jobs:

- uses: actions/setup-python@v6
with:
python-version: "3.9"
python-version: "3.10"

- name: Install uv
uses: astral-sh/setup-uv@v6
Expand All @@ -57,7 +57,7 @@ jobs:

- uses: actions/setup-python@v6
with:
python-version: "3.9"
python-version: "3.10"

- name: Install uv
uses: astral-sh/setup-uv@v6
Expand All @@ -74,7 +74,7 @@ jobs:

- uses: actions/setup-python@v6
with:
python-version: "3.9"
python-version: "3.10"

- name: Install uv
uses: astral-sh/setup-uv@v6
Expand All @@ -89,10 +89,10 @@ jobs:
strategy:
fail-fast: true
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
uses: ./.github/workflows/test.yml
with:
coverage: ${{ (matrix.python-version == '3.12' || matrix.python-version == '3.9') }}
coverage: ${{ (matrix.python-version == '3.12' || matrix.python-version == '3.10') }}
python-version: ${{ matrix.python-version }}

# add an aggregate step here to check if any of the steps of the matrix 'test' job
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/openapi/customize_openapi_types.py
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why typing.Optional instead of Literal["1"] | None?

Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import Literal, Union
from typing import Literal, Optional

from litestar import Litestar, post


@post("/")
async def query_type_test(param: Union[Literal["1"], None]) -> None:
async def query_type_test(param: Optional[Literal["1"]]) -> None:
return None


Expand Down
4 changes: 2 additions & 2 deletions docs/examples/parameters/layered_parameters.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Annotated, Union
from typing import Annotated

from litestar import Controller, Litestar, Router, get
from litestar.params import Parameter
Expand All @@ -17,7 +17,7 @@ def my_handler(
local_param: str,
router_param: str,
controller_param: Annotated[int, Parameter(int, lt=50)],
) -> dict[str, Union[str, int]]:
) -> dict[str, str | int]:
return {
"path_param": path_param,
"local_param": local_param,
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/security/jwt/custom_decode_payload.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dataclasses
from collections.abc import Sequence
from typing import Any, Optional, Union
from typing import Any, Optional

from litestar.security.jwt.token import JWTDecodeOptions, Token

Expand All @@ -14,7 +14,7 @@ def decode_payload(
secret: str,
algorithms: list[str],
issuer: Optional[list[str]] = None,
audience: Union[str, Sequence[str], None] = None,
audience: Optional[str | Sequence[str]] = None,
options: Optional[JWTDecodeOptions] = None,
) -> Any:
payload = super().decode_payload(
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/todo_app/create/dict.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from typing import Any, Union
from typing import Any

from litestar import Litestar, post

TODO_LIST: list[dict[str, Union[str, bool]]] = []
TODO_LIST: list[dict[str, str | bool]] = []


@post("/")
async def add_item(data: dict[str, Any]) -> list[dict[str, Union[str, bool]]]:
async def add_item(data: dict[str, Any]) -> list[dict[str, str | bool]]:
TODO_LIST.append(data)
return TODO_LIST

Expand Down
6 changes: 2 additions & 4 deletions docs/examples/todo_app/get_list/dict.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
from typing import Union

from litestar import Litestar, get

TODO_LIST: list[dict[str, Union[str, bool]]] = [
TODO_LIST: list[dict[str, str | bool]] = [
{"title": "Start writing TODO list", "done": True},
{"title": "???", "done": False},
{"title": "Profit", "done": False},
]


@get("/")
async def get_list() -> list[dict[str, Union[str, bool]]]:
async def get_list() -> list[dict[str, str | bool]]:
return TODO_LIST


Expand Down
4 changes: 2 additions & 2 deletions docs/usage/templating.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,15 @@ argument which should be the template class, and it specifies two methods:

.. code-block:: python

from typing import Protocol, Union, List
from typing import Protocol, List
from pydantic import DirectoryPath

# the template class of the respective library
from some_lib import SomeTemplate


class TemplateEngineProtocol(Protocol[SomeTemplate]):
def __init__(self, directory: Union[DirectoryPath, List[DirectoryPath]]) -> None:
def __init__(self, directory: DirectoryPath | List[DirectoryPath]) -> None:
"""Builds a template engine."""
...

Expand Down
5 changes: 2 additions & 3 deletions litestar/_openapi/schema_generation/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
TYPE_CHECKING,
Any,
Literal,
Union,
cast,
)
from uuid import UUID
Expand Down Expand Up @@ -510,7 +509,7 @@ def for_constrained_field(self, field: FieldDefinition) -> Schema:
Returns:
A schema instance.
"""
kwarg_definition = cast("Union[ParameterKwarg, BodyKwarg]", field.kwarg_definition)
kwarg_definition = cast("ParameterKwarg | BodyKwarg", field.kwarg_definition)
if any(is_class_and_subclass(field.annotation, t) for t in (int, float, Decimal)):
return create_numerical_constrained_field_schema(field.annotation, kwarg_definition)
if any(is_class_and_subclass(field.annotation, t) for t in (str, bytes)):
Expand All @@ -529,7 +528,7 @@ def for_collection_constrained_field(self, field_definition: FieldDefinition) ->
A schema instance.
"""
schema = Schema(type=OpenAPIType.ARRAY)
kwarg_definition = cast("Union[ParameterKwarg, BodyKwarg]", field_definition.kwarg_definition)
kwarg_definition = cast("ParameterKwarg | BodyKwarg", field_definition.kwarg_definition)
if kwarg_definition.min_items:
schema.min_items = kwarg_definition.min_items
if kwarg_definition.max_items:
Expand Down
3 changes: 1 addition & 2 deletions litestar/_signature/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
Callable,
ClassVar,
Literal,
Optional,
TypedDict,
Union,
cast,
Expand Down Expand Up @@ -312,7 +311,7 @@ def _create_annotation(
for inner_type in field_definition.inner_types
if not inner_type.is_none_type
]
return Optional[Union[tuple(types)]] if field_definition.is_optional else Union[tuple(types)] # pyright: ignore
return Union[(*tuple(types), None)] if field_definition.is_optional else Union[tuple(types)] # type: ignore

if decoder := _get_decoder_for_type(annotation, type_decoders=type_decoders):
# FIXME: temporary (hopefully) hack, see: https://github.com/jcrist/msgspec/issues/497
Expand Down
5 changes: 2 additions & 3 deletions litestar/datastructures/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
Any,
ClassVar,
Optional,
Union,
cast,
)

Expand Down Expand Up @@ -46,14 +45,14 @@ def _encode_headers(headers: Iterable[tuple[str, str]]) -> "RawHeadersList":
class Headers(CIMultiDictProxy[str], MultiMixin[str]): # pyright: ignore
"""An immutable, case-insensitive multi dict for HTTP headers."""

def __init__(self, headers: Optional[Union[Mapping[str, str], "RawHeaders", MultiDict[str]]] = None) -> None: # pyright: ignore
def __init__(self, headers: "Optional[Mapping[str, str] | RawHeaders | MultiDict[str]]" = None) -> None: # pyright: ignore
"""Initialize ``Headers``.

Args:
headers: Initial value.
"""
if not isinstance(headers, MultiDict):
headers_: Union[Mapping[str, str], list[tuple[str, str]]] = {}
headers_: Mapping[str, str] | list[tuple[str, str]] = {}
if headers:
if isinstance(headers, Mapping):
headers_ = headers # pyright: ignore
Expand Down
3 changes: 1 addition & 2 deletions litestar/dto/_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
ClassVar,
Final,
Protocol,
Union,
cast,
)

Expand Down Expand Up @@ -825,7 +824,7 @@ def _create_struct_for_field_definitions(

field_type = _create_transfer_model_type_annotation(field_definition.transfer_type)
if field_definition.is_partial:
field_type = Union[field_type, UnsetType]
field_type = field_type | UnsetType

if field_definition.passthrough_constraints:
if (field_meta := _create_struct_field_meta_for_field_definition(field_definition)) is not None:
Expand Down
4 changes: 2 additions & 2 deletions litestar/file_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import pathlib
from datetime import datetime
from stat import S_ISDIR
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Final, Union, cast
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Final, cast

import anyio
from typing_extensions import NotRequired, TypeAlias, TypedDict
Expand Down Expand Up @@ -37,7 +37,7 @@
from litestar.types import PathType


AnyFileSystem: TypeAlias = "Union[BaseFileSystem, FsspecFileSystem, FsspecAsyncFileSystem]"
AnyFileSystem: TypeAlias = "BaseFileSystem | FsspecFileSystem | FsspecAsyncFileSystem"
SymlinkResolver: TypeAlias = "Callable[[AnyFileSystem, PathType], Awaitable[str]]"


Expand Down
8 changes: 4 additions & 4 deletions litestar/logging/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from dataclasses import dataclass, field, fields
from importlib.util import find_spec
from logging import INFO
from typing import TYPE_CHECKING, Any, Callable, Literal, Union, cast
from typing import TYPE_CHECKING, Any, Callable, Literal, cast

from litestar.exceptions import ImproperlyConfiguredException, MissingDependencyException
from litestar.serialization.msgspec_hooks import _msgspec_json_encoder
Expand Down Expand Up @@ -148,7 +148,7 @@ class BaseLoggingConfig(ABC):

exception_logging_handler: ExceptionLoggingHandler | None
"""Handler function for logging exceptions."""
disable_stack_trace: set[Union[int, type[Exception]]] # noqa: UP007
disable_stack_trace: set[int | type[Exception]]
"""Set of http status codes and exceptions to disable stack trace logging for."""

@abstractmethod
Expand Down Expand Up @@ -227,7 +227,7 @@ class LoggingConfig(BaseLoggingConfig):
"""Should the root logger be configured, defaults to True for ease of configuration."""
log_exceptions: Literal["always", "debug", "never"] = field(default="always")
"""Should exceptions be logged, defaults to log exceptions when 'app.debug == True'"""
disable_stack_trace: set[Union[int, type[Exception]]] = field(default_factory=set) # noqa: UP007
disable_stack_trace: set[int | type[Exception]] = field(default_factory=set)
"""Set of http status codes and exceptions to disable stack trace logging for."""
exception_logging_handler: ExceptionLoggingHandler | None = field(default=None)
"""Handler function for logging exceptions."""
Expand Down Expand Up @@ -453,7 +453,7 @@ class StructLoggingConfig(BaseLoggingConfig):
"""Whether to cache the logger configuration and reuse."""
log_exceptions: Literal["always", "debug", "never"] = field(default="always")
"""Should exceptions be logged, defaults to log exceptions when 'app.debug == True'"""
disable_stack_trace: set[Union[int, type[Exception]]] = field(default_factory=set) # noqa: UP007
disable_stack_trace: set[int | type[Exception]] = field(default_factory=set)
"""Set of http status codes and exceptions to disable stack trace logging for."""
exception_logging_handler: ExceptionLoggingHandler | None = field(default=None)
"""Handler function for logging exceptions."""
Expand Down
4 changes: 2 additions & 2 deletions litestar/middleware/_internal/exceptions/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from inspect import getmro
from sys import exc_info
from traceback import format_exception
from typing import TYPE_CHECKING, Any, Union, cast
from typing import TYPE_CHECKING, Any, cast

from litestar.enums import ScopeType
from litestar.exceptions import HTTPException, LitestarException, WebSocketException
Expand Down Expand Up @@ -220,7 +220,7 @@ def handle_exception_logging(self, logger: Logger, logging_config: BaseLoggingCo
None
"""
exc = exc_info()
exc_detail: set[Union[Exception, int]] = {exc[0], getattr(exc[0], "status_code", None)} # type: ignore[arg-type] # noqa: UP007
exc_detail: set[Exception | int] = {exc[0], getattr(exc[0], "status_code", None)} # type: ignore[arg-type]

if (
(
Expand Down
4 changes: 2 additions & 2 deletions litestar/middleware/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import dataclasses
import functools
import inspect
from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast
from typing import TYPE_CHECKING, Any, Literal, Optional, cast

from typing_extensions import Self

Expand Down Expand Up @@ -343,7 +343,7 @@ def check_middleware_constraints(middlewares: tuple[Middleware, ...]) -> None:
positions: collections.defaultdict[object, list[int]] = collections.defaultdict(list)

for i, middleware in enumerate(middlewares):
middleware_type: Union[object, type]
middleware_type: object | type
if inspect.isfunction(middleware):
positions[middleware].append(i)
middleware_type = middleware
Expand Down
9 changes: 1 addition & 8 deletions litestar/openapi/spec/callback.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Union

if TYPE_CHECKING:
from litestar.openapi.spec.path_item import PathItem
from litestar.openapi.spec.reference import Reference


Callback = dict[str, Union["PathItem", "Reference"]]
Callback = "dict[str, PathItem | Reference]"
"""A map of possible out-of band callbacks related to the parent operation. Each value in the map is a
`Path Item Object <https://spec.openapis.org/oas/v3.1.0#pathItemObject>`_ that describes a set of requests that may be
initiated by the API provider and the expected responses. The key value used to identify the path item object is an
Expand Down
10 changes: 2 additions & 8 deletions litestar/openapi/spec/responses.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Union

if TYPE_CHECKING:
from litestar.openapi.spec.reference import Reference
from litestar.openapi.spec.response import OpenAPIResponse

Responses = dict[str, Union["OpenAPIResponse", "Reference"]]
Responses = "dict[str, OpenAPIResponse | Reference]"
"""A container for the expected responses of an operation. The container maps a
HTTP response code to the expected response.

Expand All @@ -21,7 +15,7 @@

Fixed Fields

default: ``Optional[Union[Response, Reference]]``
default: ``Optional[Response | Reference]``

The documentation of responses other than the ones declared for specific HTTP response codes. Use this field to cover
undeclared responses. A `Reference Object <https://spec.openapis.org/oas/v3.1.0#referenceObject>`_ can link to a
Expand Down
13 changes: 4 additions & 9 deletions litestar/plugins/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import abc
from contextlib import contextmanager
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, Union, cast, runtime_checkable
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast, runtime_checkable

if TYPE_CHECKING:
from collections.abc import Iterator
Expand Down Expand Up @@ -274,14 +274,9 @@ def is_constrained_field(field_definition: FieldDefinition) -> bool:
return False


PluginProtocol = Union[
CLIPlugin,
InitPluginProtocol,
OpenAPISchemaPlugin,
ReceiveRoutePlugin,
SerializationPlugin,
DIPlugin,
]
PluginProtocol = (
CLIPlugin | InitPluginProtocol | OpenAPISchemaPlugin | ReceiveRoutePlugin | SerializationPlugin | DIPlugin
)

PluginT = TypeVar("PluginT", bound=PluginProtocol)

Expand Down
Loading
Loading