mirror of https://github.com/tiangolo/fastapi.git
Merge branch 'master' into feat/depr
# Conflicts: # fastapi/_compat/v2.py # fastapi/encoders.py
This commit is contained in:
commit
987f1d9fbd
|
|
@ -20,7 +20,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
# For pull requests it's not necessary to checkout the code but for the main branch it is
|
# For pull requests it's not necessary to checkout the code but for the main branch it is
|
||||||
- uses: dorny/paths-filter@v3
|
- uses: dorny/paths-filter@v4
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
# For pull requests it's not necessary to checkout the code but for the main branch it is
|
# For pull requests it's not necessary to checkout the code but for the main branch it is
|
||||||
- uses: dorny/paths-filter@v3
|
- uses: dorny/paths-filter@v4
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,13 @@ repos:
|
||||||
language: unsupported
|
language: unsupported
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|
||||||
|
- id: local-ty
|
||||||
|
name: ty check
|
||||||
|
entry: uv run ty check fastapi
|
||||||
|
require_serial: true
|
||||||
|
language: unsupported
|
||||||
|
pass_filenames: false
|
||||||
|
|
||||||
- id: add-permalinks-pages
|
- id: add-permalinks-pages
|
||||||
language: unsupported
|
language: unsupported
|
||||||
name: add-permalinks-pages
|
name: add-permalinks-pages
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ hide:
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
|
|
||||||
|
* 👷 Add `ty` to precommit. PR [#15091](https://github.com/fastapi/fastapi/pull/15091) by [@svlandeg](https://github.com/svlandeg).
|
||||||
|
* ⬆ Bump dorny/paths-filter from 3 to 4. PR [#15106](https://github.com/fastapi/fastapi/pull/15106) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
|
* ⬆ Bump cairosvg from 2.8.2 to 2.9.0. PR [#15108](https://github.com/fastapi/fastapi/pull/15108) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
|
* ⬆ Bump pyjwt from 2.11.0 to 2.12.0. PR [#15110](https://github.com/fastapi/fastapi/pull/15110) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
* ⬆ Bump black from 26.1.0 to 26.3.1. PR [#15100](https://github.com/fastapi/fastapi/pull/15100) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
* ⬆ Bump black from 26.1.0 to 26.3.1. PR [#15100](https://github.com/fastapi/fastapi/pull/15100) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
* 🔨 Update script to autofix permalinks to account for headers with Markdown links. PR [#15062](https://github.com/fastapi/fastapi/pull/15062) by [@tiangolo](https://github.com/tiangolo).
|
* 🔨 Update script to autofix permalinks to account for headers with Markdown links. PR [#15062](https://github.com/fastapi/fastapi/pull/15062) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* 📌 Pin Click for MkDocs live reload. PR [#15057](https://github.com/fastapi/fastapi/pull/15057) by [@tiangolo](https://github.com/tiangolo).
|
* 📌 Pin Click for MkDocs live reload. PR [#15057](https://github.com/fastapi/fastapi/pull/15057) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ from .v2 import Undefined as Undefined
|
||||||
from .v2 import Url as Url
|
from .v2 import Url as Url
|
||||||
from .v2 import copy_field_info as copy_field_info
|
from .v2 import copy_field_info as copy_field_info
|
||||||
from .v2 import create_body_model as create_body_model
|
from .v2 import create_body_model as create_body_model
|
||||||
from .v2 import evaluate_forwardref as evaluate_forwardref
|
from .v2 import evaluate_forwardref as evaluate_forwardref # ty: ignore[deprecated]
|
||||||
from .v2 import get_cached_model_fields as get_cached_model_fields
|
from .v2 import get_cached_model_fields as get_cached_model_fields
|
||||||
from .v2 import get_definitions as get_definitions
|
from .v2 import get_definitions as get_definitions
|
||||||
from .v2 import get_flat_models_from_fields as get_flat_models_from_fields
|
from .v2 import get_flat_models_from_fields as get_flat_models_from_fields
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ class ModelField:
|
||||||
Field(**field_dict["attributes"]),
|
Field(**field_dict["attributes"]),
|
||||||
)
|
)
|
||||||
self._type_adapter: TypeAdapter[Any] = TypeAdapter(
|
self._type_adapter: TypeAdapter[Any] = TypeAdapter(
|
||||||
Annotated[annotated_args],
|
Annotated[annotated_args], # ty: ignore[invalid-type-form]
|
||||||
config=self.config,
|
config=self.config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -456,7 +456,7 @@ def get_flat_models_from_annotation(
|
||||||
for arg in get_args(annotation):
|
for arg in get_args(annotation):
|
||||||
if lenient_issubclass(arg, (BaseModel, Enum)):
|
if lenient_issubclass(arg, (BaseModel, Enum)):
|
||||||
if arg not in known_models:
|
if arg not in known_models:
|
||||||
known_models.add(arg) # type: ignore[arg-type]
|
known_models.add(arg) # type: ignore[arg-type] # ty: ignore[unused-ignore-comment]
|
||||||
if lenient_issubclass(arg, BaseModel):
|
if lenient_issubclass(arg, BaseModel):
|
||||||
get_flat_models_from_model(arg, known_models=known_models)
|
get_flat_models_from_model(arg, known_models=known_models)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
from collections.abc import Awaitable, Callable, Coroutine, Sequence
|
from collections.abc import Awaitable, Callable, Coroutine, Sequence
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import (
|
from typing import Annotated, Any, TypeVar
|
||||||
Annotated,
|
|
||||||
Any,
|
|
||||||
TypeVar,
|
|
||||||
)
|
|
||||||
|
|
||||||
from annotated_doc import Doc
|
from annotated_doc import Doc
|
||||||
from fastapi import routing
|
from fastapi import routing
|
||||||
|
|
@ -1006,11 +1002,12 @@ class FastAPI(Starlette):
|
||||||
self.exception_handlers.setdefault(
|
self.exception_handlers.setdefault(
|
||||||
RequestValidationError, request_validation_exception_handler
|
RequestValidationError, request_validation_exception_handler
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Starlette still has incorrect type specification for the handlers
|
||||||
self.exception_handlers.setdefault(
|
self.exception_handlers.setdefault(
|
||||||
WebSocketRequestValidationError,
|
WebSocketRequestValidationError,
|
||||||
# Starlette still has incorrect type specification for the handlers
|
websocket_request_validation_exception_handler, # type: ignore[arg-type] # ty: ignore[unused-ignore-comment]
|
||||||
websocket_request_validation_exception_handler, # type: ignore
|
) # ty: ignore[no-matching-overload]
|
||||||
)
|
|
||||||
|
|
||||||
self.user_middleware: list[Middleware] = (
|
self.user_middleware: list[Middleware] = (
|
||||||
[] if middleware is None else list(middleware)
|
[] if middleware is None else list(middleware)
|
||||||
|
|
@ -1032,11 +1029,13 @@ class FastAPI(Starlette):
|
||||||
exception_handlers[key] = value
|
exception_handlers[key] = value
|
||||||
|
|
||||||
middleware = (
|
middleware = (
|
||||||
[Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)]
|
[Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)] # ty: ignore[invalid-argument-type]
|
||||||
+ self.user_middleware
|
+ self.user_middleware
|
||||||
+ [
|
+ [
|
||||||
Middleware(
|
Middleware(
|
||||||
ExceptionMiddleware, handlers=exception_handlers, debug=debug
|
ExceptionMiddleware, # ty: ignore[invalid-argument-type]
|
||||||
|
handlers=exception_handlers,
|
||||||
|
debug=debug,
|
||||||
),
|
),
|
||||||
# Add FastAPI-specific AsyncExitStackMiddleware for closing files.
|
# Add FastAPI-specific AsyncExitStackMiddleware for closing files.
|
||||||
# Before this was also used for closing dependencies with yield but
|
# Before this was also used for closing dependencies with yield but
|
||||||
|
|
@ -1057,7 +1056,7 @@ class FastAPI(Starlette):
|
||||||
# user middlewares, the same context is used.
|
# user middlewares, the same context is used.
|
||||||
# This is currently not needed, only for closing files, but used to be
|
# This is currently not needed, only for closing files, but used to be
|
||||||
# important when dependencies with yield were closed here.
|
# important when dependencies with yield were closed here.
|
||||||
Middleware(AsyncExitStackMiddleware),
|
Middleware(AsyncExitStackMiddleware), # ty: ignore[invalid-argument-type]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -4596,7 +4595,7 @@ class FastAPI(Starlette):
|
||||||
Read more about it in the
|
Read more about it in the
|
||||||
[FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/#alternative-events-deprecated).
|
[FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/#alternative-events-deprecated).
|
||||||
"""
|
"""
|
||||||
return self.router.on_event(event_type)
|
return self.router.on_event(event_type) # ty: ignore[deprecated]
|
||||||
|
|
||||||
def middleware(
|
def middleware(
|
||||||
self,
|
self,
|
||||||
|
|
@ -4639,7 +4638,7 @@ class FastAPI(Starlette):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func: DecoratedCallable) -> DecoratedCallable:
|
def decorator(func: DecoratedCallable) -> DecoratedCallable:
|
||||||
self.add_middleware(BaseHTTPMiddleware, dispatch=func)
|
self.add_middleware(BaseHTTPMiddleware, dispatch=func) # ty: ignore[invalid-argument-type]
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ except ImportError: # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
if not cli_main: # type: ignore[truthy-function]
|
if not cli_main: # type: ignore[truthy-function] # ty: ignore[unused-ignore-comment]
|
||||||
message = 'To use the fastapi command, please install "fastapi[standard]":\n\n\tpip install "fastapi[standard]"\n'
|
message = 'To use the fastapi command, please install "fastapi[standard]":\n\n\tpip install "fastapi[standard]"\n'
|
||||||
print(message)
|
print(message)
|
||||||
raise RuntimeError(message) # noqa: B904
|
raise RuntimeError(message) # noqa: B904
|
||||||
|
|
|
||||||
|
|
@ -179,3 +179,8 @@ def Default(value: DefaultType) -> DefaultType:
|
||||||
if the overridden default value was truthy.
|
if the overridden default value was truthy.
|
||||||
"""
|
"""
|
||||||
return DefaultPlaceholder(value) # type: ignore
|
return DefaultPlaceholder(value) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
# Sentinel for "parameter not provided" in Param/FieldInfo.
|
||||||
|
# Typed as None to satisfy ty
|
||||||
|
_Unset = Default(None)
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ from fastapi._compat import (
|
||||||
Undefined,
|
Undefined,
|
||||||
copy_field_info,
|
copy_field_info,
|
||||||
create_body_model,
|
create_body_model,
|
||||||
evaluate_forwardref,
|
evaluate_forwardref, # ty: ignore[deprecated]
|
||||||
field_annotation_is_scalar,
|
field_annotation_is_scalar,
|
||||||
field_annotation_is_scalar_sequence,
|
field_annotation_is_scalar_sequence,
|
||||||
field_annotation_is_sequence,
|
field_annotation_is_sequence,
|
||||||
|
|
@ -100,12 +100,14 @@ def ensure_multipart_is_installed() -> None:
|
||||||
except (ImportError, AssertionError):
|
except (ImportError, AssertionError):
|
||||||
try:
|
try:
|
||||||
# __version__ is available in both multiparts, and can be mocked
|
# __version__ is available in both multiparts, and can be mocked
|
||||||
from multipart import __version__ # type: ignore[no-redef,import-untyped]
|
from multipart import ( # type: ignore[no-redef,import-untyped] # ty: ignore[unused-ignore-comment]
|
||||||
|
__version__,
|
||||||
|
)
|
||||||
|
|
||||||
assert __version__
|
assert __version__
|
||||||
try:
|
try:
|
||||||
# parse_options_header is only available in the right multipart
|
# parse_options_header is only available in the right multipart
|
||||||
from multipart.multipart import ( # type: ignore[import-untyped]
|
from multipart.multipart import ( # type: ignore[import-untyped] # ty: ignore[unused-ignore-comment]
|
||||||
parse_options_header,
|
parse_options_header,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -243,7 +245,7 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
|
||||||
def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
|
def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
|
||||||
if isinstance(annotation, str):
|
if isinstance(annotation, str):
|
||||||
annotation = ForwardRef(annotation)
|
annotation = ForwardRef(annotation)
|
||||||
annotation = evaluate_forwardref(annotation, globalns, globalns)
|
annotation = evaluate_forwardref(annotation, globalns, globalns) # ty: ignore[deprecated]
|
||||||
if annotation is type(None):
|
if annotation is type(None):
|
||||||
return None
|
return None
|
||||||
return annotation
|
return annotation
|
||||||
|
|
@ -320,8 +322,9 @@ def get_dependant(
|
||||||
and param_details.depends.scope == "function"
|
and param_details.depends.scope == "function"
|
||||||
):
|
):
|
||||||
assert dependant.call
|
assert dependant.call
|
||||||
|
call_name = getattr(dependant.call, "__name__", "<unnamed_callable>")
|
||||||
raise DependencyScopeError(
|
raise DependencyScopeError(
|
||||||
f'The dependency "{dependant.call.__name__}" has a scope of '
|
f'The dependency "{call_name}" has a scope of '
|
||||||
'"request", it cannot depend on dependencies with scope "function".'
|
'"request", it cannot depend on dependencies with scope "function".'
|
||||||
)
|
)
|
||||||
sub_own_oauth_scopes: list[str] = []
|
sub_own_oauth_scopes: list[str] = []
|
||||||
|
|
@ -596,7 +599,7 @@ async def solve_dependencies(
|
||||||
*,
|
*,
|
||||||
request: Request | WebSocket,
|
request: Request | WebSocket,
|
||||||
dependant: Dependant,
|
dependant: Dependant,
|
||||||
body: dict[str, Any] | FormData | None = None,
|
body: dict[str, Any] | FormData | bytes | None = None,
|
||||||
background_tasks: StarletteBackgroundTasks | None = None,
|
background_tasks: StarletteBackgroundTasks | None = None,
|
||||||
response: Response | None = None,
|
response: Response | None = None,
|
||||||
dependency_overrides_provider: Any | None = None,
|
dependency_overrides_provider: Any | None = None,
|
||||||
|
|
@ -619,7 +622,7 @@ async def solve_dependencies(
|
||||||
if response is None:
|
if response is None:
|
||||||
response = Response()
|
response = Response()
|
||||||
del response.headers["content-length"]
|
del response.headers["content-length"]
|
||||||
response.status_code = None # type: ignore
|
response.status_code = None # type: ignore # ty: ignore[unused-ignore-comment]
|
||||||
if dependency_cache is None:
|
if dependency_cache is None:
|
||||||
dependency_cache = {}
|
dependency_cache = {}
|
||||||
for sub_dependant in dependant.dependencies:
|
for sub_dependant in dependant.dependencies:
|
||||||
|
|
@ -826,7 +829,7 @@ def request_params_to_args(
|
||||||
|
|
||||||
for key in received_params.keys():
|
for key in received_params.keys():
|
||||||
if key not in processed_keys:
|
if key not in processed_keys:
|
||||||
if hasattr(received_params, "getlist"):
|
if isinstance(received_params, (ImmutableMultiDict, Headers)):
|
||||||
value = received_params.getlist(key)
|
value = received_params.getlist(key)
|
||||||
if isinstance(value, list) and (len(value) == 1):
|
if isinstance(value, list) and (len(value) == 1):
|
||||||
params_to_process[key] = value[0]
|
params_to_process[key] = value[0]
|
||||||
|
|
@ -947,7 +950,7 @@ async def _extract_form_body(
|
||||||
|
|
||||||
async def request_body_to_args(
|
async def request_body_to_args(
|
||||||
body_fields: list[ModelField],
|
body_fields: list[ModelField],
|
||||||
received_body: dict[str, Any] | FormData | None,
|
received_body: dict[str, Any] | FormData | bytes | None,
|
||||||
embed_body_fields: bool,
|
embed_body_fields: bool,
|
||||||
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
|
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
|
||||||
values: dict[str, Any] = {}
|
values: dict[str, Any] = {}
|
||||||
|
|
@ -978,7 +981,7 @@ async def request_body_to_args(
|
||||||
for field in body_fields:
|
for field in body_fields:
|
||||||
loc = ("body", get_validation_alias(field))
|
loc = ("body", get_validation_alias(field))
|
||||||
value: Any | None = None
|
value: Any | None = None
|
||||||
if body_to_process is not None:
|
if body_to_process is not None and not isinstance(body_to_process, bytes):
|
||||||
try:
|
try:
|
||||||
value = body_to_process.get(get_validation_alias(field))
|
value = body_to_process.get(get_validation_alias(field))
|
||||||
# If the received body is a list, not a dict
|
# If the received body is a list, not a dict
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ def decimal_encoder(dec_value: Decimal) -> int | float:
|
||||||
|
|
||||||
ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
|
ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
|
||||||
bytes: lambda o: o.decode(),
|
bytes: lambda o: o.decode(),
|
||||||
Color: str,
|
Color: str, # ty: ignore[deprecated]
|
||||||
datetime.date: isoformat,
|
datetime.date: isoformat,
|
||||||
datetime.datetime: isoformat,
|
datetime.datetime: isoformat,
|
||||||
datetime.time: isoformat,
|
datetime.time: isoformat,
|
||||||
|
|
@ -220,9 +220,9 @@ def jsonable_encoder(
|
||||||
if isinstance(obj, encoder_type):
|
if isinstance(obj, encoder_type):
|
||||||
return encoder_instance(obj)
|
return encoder_instance(obj)
|
||||||
if include is not None and not isinstance(include, (set, dict)):
|
if include is not None and not isinstance(include, (set, dict)):
|
||||||
include = set(include) # type: ignore[assignment]
|
include = set(include) # type: ignore[assignment] # ty: ignore[unused-ignore-comment]
|
||||||
if exclude is not None and not isinstance(exclude, (set, dict)):
|
if exclude is not None and not isinstance(exclude, (set, dict)):
|
||||||
exclude = set(exclude) # type: ignore[assignment]
|
exclude = set(exclude) # type: ignore[assignment] # ty: ignore[unused-ignore-comment]
|
||||||
if isinstance(obj, BaseModel):
|
if isinstance(obj, BaseModel):
|
||||||
obj_dict = obj.model_dump(
|
obj_dict = obj.model_dump(
|
||||||
mode="json",
|
mode="json",
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ try:
|
||||||
from pydantic import EmailStr
|
from pydantic import EmailStr
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
|
|
||||||
class EmailStr(str): # type: ignore
|
class EmailStr(str): # type: ignore # ty: ignore[unused-ignore-comment]
|
||||||
@classmethod
|
@classmethod
|
||||||
def __get_validators__(cls) -> Iterable[Callable[..., Any]]:
|
def __get_validators__(cls) -> Iterable[Callable[..., Any]]:
|
||||||
yield cls.validate
|
yield cls.validate
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,13 @@ from typing import Any, Literal, cast
|
||||||
from fastapi import routing
|
from fastapi import routing
|
||||||
from fastapi._compat import (
|
from fastapi._compat import (
|
||||||
ModelField,
|
ModelField,
|
||||||
Undefined,
|
|
||||||
get_definitions,
|
get_definitions,
|
||||||
get_flat_models_from_fields,
|
get_flat_models_from_fields,
|
||||||
get_model_name_map,
|
get_model_name_map,
|
||||||
get_schema_from_model_field,
|
get_schema_from_model_field,
|
||||||
lenient_issubclass,
|
lenient_issubclass,
|
||||||
)
|
)
|
||||||
from fastapi.datastructures import DefaultPlaceholder
|
from fastapi.datastructures import DefaultPlaceholder, _Unset
|
||||||
from fastapi.dependencies.models import Dependant
|
from fastapi.dependencies.models import Dependant
|
||||||
from fastapi.dependencies.utils import (
|
from fastapi.dependencies.utils import (
|
||||||
_get_flat_fields_from_params,
|
_get_flat_fields_from_params,
|
||||||
|
|
@ -170,7 +169,7 @@ def _get_openapi_operation_parameters(
|
||||||
example = getattr(field_info, "example", None)
|
example = getattr(field_info, "example", None)
|
||||||
if openapi_examples:
|
if openapi_examples:
|
||||||
parameter["examples"] = jsonable_encoder(openapi_examples)
|
parameter["examples"] = jsonable_encoder(openapi_examples)
|
||||||
elif example != Undefined:
|
elif example is not _Unset:
|
||||||
parameter["example"] = jsonable_encoder(example)
|
parameter["example"] = jsonable_encoder(example)
|
||||||
if getattr(field_info, "deprecated", None):
|
if getattr(field_info, "deprecated", None):
|
||||||
parameter["deprecated"] = True
|
parameter["deprecated"] = True
|
||||||
|
|
@ -207,7 +206,7 @@ def get_openapi_operation_request_body(
|
||||||
request_media_content["examples"] = jsonable_encoder(
|
request_media_content["examples"] = jsonable_encoder(
|
||||||
field_info.openapi_examples
|
field_info.openapi_examples
|
||||||
)
|
)
|
||||||
elif field_info.example != Undefined:
|
elif field_info.example is not _Unset:
|
||||||
request_media_content["example"] = jsonable_encoder(field_info.example)
|
request_media_content["example"] = jsonable_encoder(field_info.example)
|
||||||
request_body_oai["content"] = {request_media_type: request_media_content}
|
request_body_oai["content"] = {request_media_type: request_media_content}
|
||||||
return request_body_oai
|
return request_body_oai
|
||||||
|
|
@ -245,10 +244,8 @@ def get_openapi_operation_metadata(
|
||||||
operation["description"] = route.description
|
operation["description"] = route.description
|
||||||
operation_id = route.operation_id or route.unique_id
|
operation_id = route.operation_id or route.unique_id
|
||||||
if operation_id in operation_ids:
|
if operation_id in operation_ids:
|
||||||
message = (
|
endpoint_name = getattr(route.endpoint, "__name__", "<unnamed_endpoint>")
|
||||||
f"Duplicate Operation ID {operation_id} for function "
|
message = f"Duplicate Operation ID {operation_id} for function {endpoint_name}"
|
||||||
+ f"{route.endpoint.__name__}"
|
|
||||||
)
|
|
||||||
file_name = getattr(route.endpoint, "__globals__", {}).get("__file__")
|
file_name = getattr(route.endpoint, "__globals__", {}).get("__file__")
|
||||||
if file_name:
|
if file_name:
|
||||||
message += f" at {file_name}"
|
message += f" at {file_name}"
|
||||||
|
|
@ -606,4 +603,4 @@ def get_openapi(
|
||||||
output["tags"] = tags
|
output["tags"] = tags
|
||||||
if external_docs:
|
if external_docs:
|
||||||
output["externalDocs"] = external_docs
|
output["externalDocs"] = external_docs
|
||||||
return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True) # type: ignore
|
return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True) # type: ignore # ty: ignore[unused-ignore-comment]
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,11 @@ from typing import Annotated, Any, Literal
|
||||||
from annotated_doc import Doc
|
from annotated_doc import Doc
|
||||||
from fastapi import params
|
from fastapi import params
|
||||||
from fastapi._compat import Undefined
|
from fastapi._compat import Undefined
|
||||||
|
from fastapi.datastructures import _Unset
|
||||||
from fastapi.openapi.models import Example
|
from fastapi.openapi.models import Example
|
||||||
from pydantic import AliasChoices, AliasPath
|
from pydantic import AliasChoices, AliasPath
|
||||||
from typing_extensions import deprecated
|
from typing_extensions import deprecated
|
||||||
|
|
||||||
_Unset: Any = Undefined
|
|
||||||
|
|
||||||
|
|
||||||
def Path( # noqa: N802
|
def Path( # noqa: N802
|
||||||
default: Annotated[
|
default: Annotated[
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,7 @@ from typing_extensions import deprecated
|
||||||
from ._compat import (
|
from ._compat import (
|
||||||
Undefined,
|
Undefined,
|
||||||
)
|
)
|
||||||
|
from .datastructures import _Unset
|
||||||
_Unset: Any = Undefined
|
|
||||||
|
|
||||||
|
|
||||||
class ParamTypes(Enum):
|
class ParamTypes(Enum):
|
||||||
|
|
@ -135,7 +134,7 @@ class Param(FieldInfo): # type: ignore[misc]
|
||||||
return f"{self.__class__.__name__}({self.default})"
|
return f"{self.__class__.__name__}({self.default})"
|
||||||
|
|
||||||
|
|
||||||
class Path(Param): # type: ignore[misc]
|
class Path(Param): # type: ignore[misc] # ty: ignore[unused-ignore-comment]
|
||||||
in_ = ParamTypes.path
|
in_ = ParamTypes.path
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -219,7 +218,7 @@ class Path(Param): # type: ignore[misc]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Query(Param): # type: ignore[misc]
|
class Query(Param): # type: ignore[misc] # ty: ignore[unused-ignore-comment]
|
||||||
in_ = ParamTypes.query
|
in_ = ParamTypes.query
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -301,7 +300,7 @@ class Query(Param): # type: ignore[misc]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Header(Param): # type: ignore[misc]
|
class Header(Param): # type: ignore[misc] # ty: ignore[unused-ignore-comment]
|
||||||
in_ = ParamTypes.header
|
in_ = ParamTypes.header
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -385,7 +384,7 @@ class Header(Param): # type: ignore[misc]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Cookie(Param): # type: ignore[misc]
|
class Cookie(Param): # type: ignore[misc] # ty: ignore[unused-ignore-comment]
|
||||||
in_ = ParamTypes.cookie
|
in_ = ParamTypes.cookie
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -579,7 +578,7 @@ class Body(FieldInfo): # type: ignore[misc]
|
||||||
return f"{self.__class__.__name__}({self.default})"
|
return f"{self.__class__.__name__}({self.default})"
|
||||||
|
|
||||||
|
|
||||||
class Form(Body): # type: ignore[misc]
|
class Form(Body): # type: ignore[misc] # ty: ignore[unused-ignore-comment]
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
default: Any = Undefined,
|
default: Any = Undefined,
|
||||||
|
|
@ -661,7 +660,7 @@ class Form(Body): # type: ignore[misc]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class File(Form): # type: ignore[misc]
|
class File(Form): # type: ignore[misc] # ty: ignore[unused-ignore-comment]
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
default: Any = Undefined,
|
default: Any = Undefined,
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ from typing import (
|
||||||
Annotated,
|
Annotated,
|
||||||
Any,
|
Any,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
|
cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
import anyio
|
import anyio
|
||||||
|
|
@ -75,6 +76,7 @@ from starlette import routing
|
||||||
from starlette._exception_handler import wrap_app_handling_exceptions
|
from starlette._exception_handler import wrap_app_handling_exceptions
|
||||||
from starlette._utils import is_async_callable
|
from starlette._utils import is_async_callable
|
||||||
from starlette.concurrency import iterate_in_threadpool, run_in_threadpool
|
from starlette.concurrency import iterate_in_threadpool, run_in_threadpool
|
||||||
|
from starlette.datastructures import FormData
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.responses import JSONResponse, Response, StreamingResponse
|
from starlette.responses import JSONResponse, Response, StreamingResponse
|
||||||
|
|
@ -100,8 +102,10 @@ def request_response(
|
||||||
and returns an ASGI application.
|
and returns an ASGI application.
|
||||||
"""
|
"""
|
||||||
f: Callable[[Request], Awaitable[Response]] = (
|
f: Callable[[Request], Awaitable[Response]] = (
|
||||||
func if is_async_callable(func) else functools.partial(run_in_threadpool, func) # type:ignore
|
func # type: ignore[assignment] # ty: ignore[unused-ignore-comment]
|
||||||
)
|
if is_async_callable(func)
|
||||||
|
else functools.partial(run_in_threadpool, func) # type: ignore[call-arg] # ty: ignore[unused-ignore-comment]
|
||||||
|
) # ty: ignore[invalid-assignment]
|
||||||
|
|
||||||
async def app(scope: Scope, receive: Receive, send: Send) -> None:
|
async def app(scope: Scope, receive: Receive, send: Send) -> None:
|
||||||
request = Request(scope, receive, send)
|
request = Request(scope, receive, send)
|
||||||
|
|
@ -453,7 +457,7 @@ def get_request_handler(
|
||||||
solved_result = await solve_dependencies(
|
solved_result = await solve_dependencies(
|
||||||
request=request,
|
request=request,
|
||||||
dependant=dependant,
|
dependant=dependant,
|
||||||
body=body,
|
body=cast(dict[str, Any] | FormData | bytes | None, body),
|
||||||
dependency_overrides_provider=dependency_overrides_provider,
|
dependency_overrides_provider=dependency_overrides_provider,
|
||||||
async_exit_stack=async_exit_stack,
|
async_exit_stack=async_exit_stack,
|
||||||
embed_body_fields=embed_body_fields,
|
embed_body_fields=embed_body_fields,
|
||||||
|
|
@ -635,7 +639,7 @@ def get_request_handler(
|
||||||
else:
|
else:
|
||||||
|
|
||||||
def _sync_stream_jsonl() -> Iterator[bytes]:
|
def _sync_stream_jsonl() -> Iterator[bytes]:
|
||||||
for item in gen:
|
for item in gen: # ty: ignore[not-iterable]
|
||||||
yield _serialize_item(item)
|
yield _serialize_item(item)
|
||||||
|
|
||||||
jsonl_stream_content = _sync_stream_jsonl()
|
jsonl_stream_content = _sync_stream_jsonl()
|
||||||
|
|
@ -908,7 +912,7 @@ class APIRoute(routing.Route):
|
||||||
mode="serialization",
|
mode="serialization",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.response_field = None # type: ignore
|
self.response_field = None # type: ignore # ty: ignore[unused-ignore-comment]
|
||||||
if self.stream_item_type:
|
if self.stream_item_type:
|
||||||
stream_item_name = "StreamItem_" + self.unique_id
|
stream_item_name = "StreamItem_" + self.unique_id
|
||||||
self.stream_item_field: ModelField | None = create_model_field(
|
self.stream_item_field: ModelField | None = create_model_field(
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ from starlette.status import HTTP_401_UNAUTHORIZED
|
||||||
|
|
||||||
|
|
||||||
class APIKeyBase(SecurityBase):
|
class APIKeyBase(SecurityBase):
|
||||||
|
model: APIKey
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
location: APIKeyIn,
|
location: APIKeyIn,
|
||||||
|
|
@ -20,7 +22,7 @@ class APIKeyBase(SecurityBase):
|
||||||
self.auto_error = auto_error
|
self.auto_error = auto_error
|
||||||
|
|
||||||
self.model: APIKey = APIKey(
|
self.model: APIKey = APIKey(
|
||||||
**{"in": location},
|
**{"in": location}, # ty: ignore[invalid-argument-type]
|
||||||
name=name,
|
name=name,
|
||||||
description=description,
|
description=description,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,8 @@ class HTTPAuthorizationCredentials(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class HTTPBase(SecurityBase):
|
class HTTPBase(SecurityBase):
|
||||||
|
model: HTTPBaseModel
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
|
|
@ -75,9 +77,7 @@ class HTTPBase(SecurityBase):
|
||||||
description: str | None = None,
|
description: str | None = None,
|
||||||
auto_error: bool = True,
|
auto_error: bool = True,
|
||||||
):
|
):
|
||||||
self.model: HTTPBaseModel = HTTPBaseModel(
|
self.model = HTTPBaseModel(scheme=scheme, description=description)
|
||||||
scheme=scheme, description=description
|
|
||||||
)
|
|
||||||
self.scheme_name = scheme_name or self.__class__.__name__
|
self.scheme_name = scheme_name or self.__class__.__name__
|
||||||
self.auto_error = auto_error
|
self.auto_error = auto_error
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,7 @@ tests = [
|
||||||
"pyyaml >=5.3.1,<7.0.0",
|
"pyyaml >=5.3.1,<7.0.0",
|
||||||
"sqlmodel >=0.0.31",
|
"sqlmodel >=0.0.31",
|
||||||
"strawberry-graphql >=0.200.0,<1.0.0",
|
"strawberry-graphql >=0.200.0,<1.0.0",
|
||||||
|
"ty>=0.0.9",
|
||||||
"types-orjson >=3.6.2",
|
"types-orjson >=3.6.2",
|
||||||
"types-ujson >=5.10.0.20240515",
|
"types-ujson >=5.10.0.20240515",
|
||||||
"a2wsgi >=1.9.0,<=2.0.0",
|
"a2wsgi >=1.9.0,<=2.0.0",
|
||||||
|
|
|
||||||
41
uv.lock
41
uv.lock
|
|
@ -469,7 +469,7 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairosvg"
|
name = "cairosvg"
|
||||||
version = "2.8.2"
|
version = "2.9.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "cairocffi" },
|
{ name = "cairocffi" },
|
||||||
|
|
@ -478,10 +478,7 @@ dependencies = [
|
||||||
{ name = "pillow" },
|
{ name = "pillow" },
|
||||||
{ name = "tinycss2" },
|
{ name = "tinycss2" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/b9/5106168bd43d7cd8b7cc2a2ee465b385f14b63f4c092bb89eee2d48c8e67/cairosvg-2.8.2.tar.gz", hash = "sha256:07cbf4e86317b27a92318a4cac2a4bb37a5e9c1b8a27355d06874b22f85bef9f", size = 8398590, upload-time = "2025-05-15T06:56:32.653Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/38/07/e8412a13019b3f737972dea23a2c61ca42becafc16c9338f4ca7a0caa993/cairosvg-2.9.0.tar.gz", hash = "sha256:1debb00cd2da11350d8b6f5ceb739f1b539196d71d5cf5eb7363dbd1bfbc8dc5", size = 40877, upload-time = "2026-03-13T15:42:00.564Z" }
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/67/48/816bd4aaae93dbf9e408c58598bc32f4a8c65f4b86ab560864cb3ee60adb/cairosvg-2.8.2-py3-none-any.whl", hash = "sha256:eab46dad4674f33267a671dce39b64be245911c901c70d65d2b7b0821e852bf5", size = 45773, upload-time = "2025-05-15T06:56:28.552Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
|
|
@ -1161,6 +1158,7 @@ dev = [
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
{ name = "sqlmodel" },
|
{ name = "sqlmodel" },
|
||||||
{ name = "strawberry-graphql" },
|
{ name = "strawberry-graphql" },
|
||||||
|
{ name = "ty" },
|
||||||
{ name = "typer" },
|
{ name = "typer" },
|
||||||
{ name = "types-orjson" },
|
{ name = "types-orjson" },
|
||||||
{ name = "types-ujson" },
|
{ name = "types-ujson" },
|
||||||
|
|
@ -1224,6 +1222,7 @@ tests = [
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
{ name = "sqlmodel" },
|
{ name = "sqlmodel" },
|
||||||
{ name = "strawberry-graphql" },
|
{ name = "strawberry-graphql" },
|
||||||
|
{ name = "ty" },
|
||||||
{ name = "types-orjson" },
|
{ name = "types-orjson" },
|
||||||
{ name = "types-ujson" },
|
{ name = "types-ujson" },
|
||||||
{ name = "ujson" },
|
{ name = "ujson" },
|
||||||
|
|
@ -1312,6 +1311,7 @@ dev = [
|
||||||
{ name = "ruff", specifier = ">=0.14.14" },
|
{ name = "ruff", specifier = ">=0.14.14" },
|
||||||
{ name = "sqlmodel", specifier = ">=0.0.31" },
|
{ name = "sqlmodel", specifier = ">=0.0.31" },
|
||||||
{ name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" },
|
{ name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" },
|
||||||
|
{ name = "ty", specifier = ">=0.0.9" },
|
||||||
{ name = "typer", specifier = ">=0.21.1" },
|
{ name = "typer", specifier = ">=0.21.1" },
|
||||||
{ name = "types-orjson", specifier = ">=3.6.2" },
|
{ name = "types-orjson", specifier = ">=3.6.2" },
|
||||||
{ name = "types-ujson", specifier = ">=5.10.0.20240515" },
|
{ name = "types-ujson", specifier = ">=5.10.0.20240515" },
|
||||||
|
|
@ -1375,6 +1375,7 @@ tests = [
|
||||||
{ name = "ruff", specifier = ">=0.14.14" },
|
{ name = "ruff", specifier = ">=0.14.14" },
|
||||||
{ name = "sqlmodel", specifier = ">=0.0.31" },
|
{ name = "sqlmodel", specifier = ">=0.0.31" },
|
||||||
{ name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" },
|
{ name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" },
|
||||||
|
{ name = "ty", specifier = ">=0.0.9" },
|
||||||
{ name = "types-orjson", specifier = ">=3.6.2" },
|
{ name = "types-orjson", specifier = ">=3.6.2" },
|
||||||
{ name = "types-ujson", specifier = ">=5.10.0.20240515" },
|
{ name = "types-ujson", specifier = ">=5.10.0.20240515" },
|
||||||
{ name = "ujson", specifier = ">=5.8.0" },
|
{ name = "ujson", specifier = ">=5.8.0" },
|
||||||
|
|
@ -4321,11 +4322,11 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyjwt"
|
name = "pyjwt"
|
||||||
version = "2.11.0"
|
version = "2.12.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/a8/10/e8192be5f38f3e8e7e046716de4cae33d56fd5ae08927a823bb916be36c1/pyjwt-2.12.0.tar.gz", hash = "sha256:2f62390b667cd8257de560b850bb5a883102a388829274147f1d724453f8fb02", size = 102511, upload-time = "2026-03-12T17:15:30.831Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" },
|
{ url = "https://files.pythonhosted.org/packages/15/70/70f895f404d363d291dcf62c12c85fdd47619ad9674ac0f53364d035925a/pyjwt-2.12.0-py3-none-any.whl", hash = "sha256:9bb459d1bdd0387967d287f5656bf7ec2b9a26645d1961628cda1764e087fd6e", size = 29700, upload-time = "2026-03-12T17:15:29.257Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
|
|
@ -5654,6 +5655,30 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/41/bf/945d527ff706233636c73880b22c7c953f3faeb9d6c7e2e85bfbfd0134a0/trio-0.32.0-py3-none-any.whl", hash = "sha256:4ab65984ef8370b79a76659ec87aa3a30c5c7c83ff250b4de88c29a8ab6123c5", size = 512030, upload-time = "2025-10-31T07:18:15.885Z" },
|
{ url = "https://files.pythonhosted.org/packages/41/bf/945d527ff706233636c73880b22c7c953f3faeb9d6c7e2e85bfbfd0134a0/trio-0.32.0-py3-none-any.whl", hash = "sha256:4ab65984ef8370b79a76659ec87aa3a30c5c7c83ff250b4de88c29a8ab6123c5", size = 512030, upload-time = "2025-10-31T07:18:15.885Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ty"
|
||||||
|
version = "0.0.21"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ee/20/2ba8fd9493c89c41dfe9dbb73bc70a28b28028463bc0d2897ba8be36230a/ty-0.0.21.tar.gz", hash = "sha256:a4c2ba5d67d64df8fcdefd8b280ac1149d24a73dbda82fa953a0dff9d21400ed", size = 5297967, upload-time = "2026-03-06T01:57:13.809Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/36/70/edf38bb37517531681d1c37f5df64744e5ad02673c02eb48447eae4bea08/ty-0.0.21-py3-none-linux_armv6l.whl", hash = "sha256:7bdf2f572378de78e1f388d24691c89db51b7caf07cf90f2bfcc1d6b18b70a76", size = 10299222, upload-time = "2026-03-06T01:57:16.64Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/62/0047b0bd19afeefbc7286f20a5f78a2aa39f92b4d89853f0d7185ab89edc/ty-0.0.21-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7e9613994610431ab8625025bd2880dbcb77c5c9fabdd21134cda12d840a529d", size = 10130513, upload-time = "2026-03-06T01:57:29.93Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a2/20/0b93a9e91aaed23155780258cdfdb4726ef68b6985378ac069bc427291a0/ty-0.0.21-py3-none-macosx_11_0_arm64.whl", hash = "sha256:56d3b198b64dd0a19b2b66e257deaed2ecea568e722ae5352f3c6fb62027f89d", size = 9605425, upload-time = "2026-03-06T01:57:27.115Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ea/fd/9945e2fa2996a1287b1e1d7ce050e97e1f420233b271e770934bfa0880a0/ty-0.0.21-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d23d2c34f7a77d974bb08f0860ef700addc8a683d81a0319f71c08f87506cfd0", size = 10108298, upload-time = "2026-03-06T01:57:35.429Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/e7/4ec52fcb15f3200826c9f048472c062549a05b0d1ef0b51f32d527b513c4/ty-0.0.21-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56b01fd2519637a4ca88344f61c96225f540c98ff18bca321d4eaa7bb0f7aa2f", size = 10121556, upload-time = "2026-03-06T01:57:03.242Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ee/c0/ad457be2a8abea0f25549598bd098554540ced66229488daa0d558dad3c8/ty-0.0.21-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9de7e11c63c6afc40f3e9ba716374add171aee7fabc70b5146a510705c6d41b", size = 10603264, upload-time = "2026-03-06T01:56:52.134Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/5b/2ecc7a2175243a4bcb72f5298ae41feabbb93b764bb0dc45722f3752c2c2/ty-0.0.21-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:62f7f5b235c4f7876db305c36997aea07b7af29b1a068f373d0e2547e25f32ff", size = 11196428, upload-time = "2026-03-06T01:57:32.94Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/37/f5/aff507d6a901f328ef96a298032b0c11aaaf950a146ed7dd3b5bf2cd3acf/ty-0.0.21-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ee8399f7c453a425291e6688efe430cfae7ab0ac4ffd50eba9f872bf878b54f6", size = 10866355, upload-time = "2026-03-06T01:56:57.831Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/30/822bbcb92d55b65989aa7ed06d9585f28ade9c9447369194ed4b0fb3b5b9/ty-0.0.21-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210e7568c9f886c4d01308d751949ee714ad7ad9d7d928d2ba90d329dd880367", size = 10738177, upload-time = "2026-03-06T01:57:11.256Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/cc/46e7991b6469e93ac2c7e533a028983e402485580150ac864c56352a3a82/ty-0.0.21-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:53508e345b11569f78b21ba8e2b4e61df38a9754947fb3cd9f2ef574367338fb", size = 10079158, upload-time = "2026-03-06T01:57:00.516Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/15/c2/0bbdadfbd008240f8f1a87dc877433cb3884436097926107ccf06e618199/ty-0.0.21-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:553e43571f4a35604c36cfd07d8b61a5eb7a714e3c67f8c4ff2cf674fefbaef9", size = 10150535, upload-time = "2026-03-06T01:57:08.815Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/b5/2dbdb7b57b5362200ef0a39738ebd31331726328336def0143ac097ee59d/ty-0.0.21-py3-none-musllinux_1_2_i686.whl", hash = "sha256:666f6822e3b9200abfa7e95eb0ddd576460adb8d66b550c0ad2c70abc84a2048", size = 10319803, upload-time = "2026-03-06T01:57:19.106Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/84/70e52c0b7abc7c2086f9876ef454a73b161d3125315536d8d7e911c94ca4/ty-0.0.21-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a0854d008347ce4a5fb351af132f660a390ab2a1163444d075251d43e6f74b9b", size = 10826239, upload-time = "2026-03-06T01:57:21.727Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a1/8a/1f72480fd013bbc6cd1929002abbbcde9a0b08ead6a15154de9d7f7fa37e/ty-0.0.21-py3-none-win32.whl", hash = "sha256:bef3ab4c7b966bcc276a8ac6c11b63ba222d21355b48d471ea782c4104eee4e0", size = 9693196, upload-time = "2026-03-06T01:57:24.126Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8d/f8/1104808b875c26c640e536945753a78562d606bef4e241d9dbf3d92477f6/ty-0.0.21-py3-none-win_amd64.whl", hash = "sha256:a709d576e5bea84b745d43058d8b9cd4f27f74a0b24acb4b0cbb7d3d41e0d050", size = 10668660, upload-time = "2026-03-06T01:56:55.06Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1b/b8/25e0adc404bbf986977657b25318991f93097b49f8aea640d93c0b0db68e/ty-0.0.21-py3-none-win_arm64.whl", hash = "sha256:f72047996598ac20553fb7e21ba5741e3c82dee4e9eadf10d954551a5fe09391", size = 10104161, upload-time = "2026-03-06T01:57:06.072Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typer"
|
name = "typer"
|
||||||
version = "0.24.1"
|
version = "0.24.1"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue