71
71
from starlette .requests import HTTPConnection , Request
72
72
from starlette .responses import Response
73
73
from starlette .websockets import WebSocket
74
- from typing_extensions import Annotated , get_args , get_origin
75
-
76
- try :
77
- from typing_extensions import TypeAliasType
78
- except ImportError : # pragma: no cover
79
- TypeAliasType = None # type: ignore[misc,assignment]
80
-
74
+ from typing_extensions import get_args , get_origin
75
+ from typing_inspection .introspection import AnnotationSource , inspect_annotation
81
76
82
77
multipart_not_installed_error = (
83
78
'Form data requires "python-multipart" to be installed. \n '
@@ -360,22 +355,22 @@ def analyze_param(
360
355
) -> ParamDetails :
361
356
field_info = None
362
357
depends = None
363
- type_annotation : Any = Any
364
- use_annotation : Any = Any
365
- if TypeAliasType is not None and isinstance (annotation , TypeAliasType ):
366
- # unpack in case py3.12 type syntax is used
367
- annotation = annotation .__value__
358
+ type : Any = Any
359
+ meta : list [Any ] = []
368
360
if annotation is not inspect .Signature .empty :
369
- use_annotation = annotation
370
- type_annotation = annotation
361
+ inspected = inspect_annotation (
362
+ annotation ,
363
+ annotation_source = AnnotationSource .BARE ,
364
+ unpack_type_aliases = "lenient" ,
365
+ )
366
+ meta = inspected .metadata
367
+ type = inspected .type
368
+ else :
369
+ annotation = Any
371
370
# Extract Annotated info
372
- if get_origin (use_annotation ) is Annotated :
373
- annotated_args = get_args (annotation )
374
- type_annotation = annotated_args [0 ]
371
+ if meta :
375
372
fastapi_annotations = [
376
- arg
377
- for arg in annotated_args [1 :]
378
- if isinstance (arg , (FieldInfo , params .Depends ))
373
+ arg for arg in meta if isinstance (arg , (FieldInfo , params .Depends ))
379
374
]
380
375
fastapi_specific_annotations = [
381
376
arg
@@ -392,7 +387,7 @@ def analyze_param(
392
387
if isinstance (fastapi_annotation , FieldInfo ):
393
388
# Copy `field_info` because we mutate `field_info.default` below.
394
389
field_info = copy_field_info (
395
- field_info = fastapi_annotation , annotation = use_annotation
390
+ field_info = fastapi_annotation , annotation = annotation
396
391
)
397
392
assert (
398
393
field_info .default is Undefined or field_info .default is RequiredParam
@@ -427,17 +422,17 @@ def analyze_param(
427
422
)
428
423
field_info = value
429
424
if PYDANTIC_V2 :
430
- field_info .annotation = type_annotation
425
+ field_info .annotation = type
431
426
432
427
# Get Depends from type annotation
433
428
if depends is not None and depends .dependency is None :
434
429
# Copy `depends` before mutating it
435
430
depends = copy (depends )
436
- depends .dependency = type_annotation
431
+ depends .dependency = type
437
432
438
433
# Handle non-param type annotations like Request
439
434
if lenient_issubclass (
440
- type_annotation ,
435
+ type ,
441
436
(
442
437
Request ,
443
438
WebSocket ,
@@ -447,9 +442,9 @@ def analyze_param(
447
442
SecurityScopes ,
448
443
),
449
444
):
450
- assert depends is None , f"Cannot specify `Depends` for type { type_annotation !r} "
445
+ assert depends is None , f"Cannot specify `Depends` for type { type !r} "
451
446
assert field_info is None , (
452
- f"Cannot specify FastAPI annotation for type { type_annotation !r} "
447
+ f"Cannot specify FastAPI annotation for type { type !r} "
453
448
)
454
449
# Handle default assignations, neither field_info nor depends was not found in Annotated nor default value
455
450
elif field_info is None and depends is None :
@@ -458,15 +453,15 @@ def analyze_param(
458
453
# We might check here that `default_value is RequiredParam`, but the fact is that the same
459
454
# parameter might sometimes be a path parameter and sometimes not. See
460
455
# `tests/test_infer_param_optionality.py` for an example.
461
- field_info = params .Path (annotation = use_annotation )
456
+ field_info = params .Path (annotation = annotation )
462
457
elif is_uploadfile_or_nonable_uploadfile_annotation (
463
- type_annotation
464
- ) or is_uploadfile_sequence_annotation (type_annotation ):
465
- field_info = params .File (annotation = use_annotation , default = default_value )
466
- elif not field_annotation_is_scalar (annotation = type_annotation ):
467
- field_info = params .Body (annotation = use_annotation , default = default_value )
458
+ type
459
+ ) or is_uploadfile_sequence_annotation (type ):
460
+ field_info = params .File (annotation = annotation , default = default_value )
461
+ elif not field_annotation_is_scalar (annotation = type ):
462
+ field_info = params .Body (annotation = annotation , default = default_value )
468
463
else :
469
- field_info = params .Query (annotation = use_annotation , default = default_value )
464
+ field_info = params .Query (annotation = annotation , default = default_value )
470
465
471
466
field = None
472
467
# It's a field_info, not a dependency
@@ -483,7 +478,7 @@ def analyze_param(
483
478
):
484
479
field_info .in_ = params .ParamTypes .query
485
480
use_annotation_from_field_info = get_annotation_from_field_info (
486
- use_annotation ,
481
+ annotation ,
487
482
field_info ,
488
483
param_name ,
489
484
)
@@ -517,7 +512,7 @@ def analyze_param(
517
512
)
518
513
)
519
514
520
- return ParamDetails (type_annotation = type_annotation , depends = depends , field = field )
515
+ return ParamDetails (type_annotation = type , depends = depends , field = field )
521
516
522
517
523
518
def add_param_to_fields (* , field : ModelField , dependant : Dependant ) -> None :
0 commit comments