fastapi 0.115.10__py3-none-any.whl → 0.115.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fastapi might be problematic. Click here for more details.

fastapi/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
2
2
 
3
- __version__ = "0.115.10"
3
+ __version__ = "0.115.12"
4
4
 
5
5
  from starlette import status as status
6
6
 
@@ -133,9 +133,9 @@ def get_param_sub_dependant(
133
133
 
134
134
 
135
135
  def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:
136
- assert callable(
137
- depends.dependency
138
- ), "A parameter-less dependency must have a callable dependency"
136
+ assert callable(depends.dependency), (
137
+ "A parameter-less dependency must have a callable dependency"
138
+ )
139
139
  return get_sub_dependant(depends=depends, dependency=depends.dependency, path=path)
140
140
 
141
141
 
@@ -302,9 +302,9 @@ def get_dependant(
302
302
  type_annotation=param_details.type_annotation,
303
303
  dependant=dependant,
304
304
  ):
305
- assert (
306
- param_details.field is None
307
- ), f"Cannot specify multiple FastAPI annotations for {param_name!r}"
305
+ assert param_details.field is None, (
306
+ f"Cannot specify multiple FastAPI annotations for {param_name!r}"
307
+ )
308
308
  continue
309
309
  assert param_details.field is not None
310
310
  if isinstance(param_details.field.field_info, params.Body):
@@ -439,9 +439,9 @@ def analyze_param(
439
439
  ),
440
440
  ):
441
441
  assert depends is None, f"Cannot specify `Depends` for type {type_annotation!r}"
442
- assert (
443
- field_info is None
444
- ), f"Cannot specify FastAPI annotation for type {type_annotation!r}"
442
+ assert field_info is None, (
443
+ f"Cannot specify FastAPI annotation for type {type_annotation!r}"
444
+ )
445
445
  # Handle default assignations, neither field_info nor depends was not found in Annotated nor default value
446
446
  elif field_info is None and depends is None:
447
447
  default_value = value if value is not inspect.Signature.empty else RequiredParam
@@ -449,15 +449,15 @@ def analyze_param(
449
449
  # We might check here that `default_value is RequiredParam`, but the fact is that the same
450
450
  # parameter might sometimes be a path parameter and sometimes not. See
451
451
  # `tests/test_infer_param_optionality.py` for an example.
452
- field_info = params.Path(annotation=type_annotation)
452
+ field_info = params.Path(annotation=use_annotation)
453
453
  elif is_uploadfile_or_nonable_uploadfile_annotation(
454
454
  type_annotation
455
455
  ) or is_uploadfile_sequence_annotation(type_annotation):
456
- field_info = params.File(annotation=type_annotation, default=default_value)
456
+ field_info = params.File(annotation=use_annotation, default=default_value)
457
457
  elif not field_annotation_is_scalar(annotation=type_annotation):
458
- field_info = params.Body(annotation=type_annotation, default=default_value)
458
+ field_info = params.Body(annotation=use_annotation, default=default_value)
459
459
  else:
460
- field_info = params.Query(annotation=type_annotation, default=default_value)
460
+ field_info = params.Query(annotation=use_annotation, default=default_value)
461
461
 
462
462
  field = None
463
463
  # It's a field_info, not a dependency
@@ -494,9 +494,9 @@ def analyze_param(
494
494
  field_info=field_info,
495
495
  )
496
496
  if is_path_param:
497
- assert is_scalar_field(
498
- field=field
499
- ), "Path params must be of one of the supported types"
497
+ assert is_scalar_field(field=field), (
498
+ "Path params must be of one of the supported types"
499
+ )
500
500
  elif isinstance(field_info, params.Query):
501
501
  assert (
502
502
  is_scalar_field(field)
@@ -521,9 +521,9 @@ def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
521
521
  elif field_info_in == params.ParamTypes.header:
522
522
  dependant.header_params.append(field)
523
523
  else:
524
- assert (
525
- field_info_in == params.ParamTypes.cookie
526
- ), f"non-body parameters must be in path, query, header or cookie: {field.name}"
524
+ assert field_info_in == params.ParamTypes.cookie, (
525
+ f"non-body parameters must be in path, query, header or cookie: {field.name}"
526
+ )
527
527
  dependant.cookie_params.append(field)
528
528
 
529
529
 
@@ -750,9 +750,15 @@ def request_params_to_args(
750
750
  first_field = fields[0]
751
751
  fields_to_extract = fields
752
752
  single_not_embedded_field = False
753
+ default_convert_underscores = True
753
754
  if len(fields) == 1 and lenient_issubclass(first_field.type_, BaseModel):
754
755
  fields_to_extract = get_cached_model_fields(first_field.type_)
755
756
  single_not_embedded_field = True
757
+ # If headers are in a Pydantic model, the way to disable convert_underscores
758
+ # would be with Header(convert_underscores=False) at the Pydantic model level
759
+ default_convert_underscores = getattr(
760
+ first_field.field_info, "convert_underscores", True
761
+ )
756
762
 
757
763
  params_to_process: Dict[str, Any] = {}
758
764
 
@@ -763,7 +769,9 @@ def request_params_to_args(
763
769
  if isinstance(received_params, Headers):
764
770
  # Handle fields extracted from a Pydantic Model for a header, each field
765
771
  # doesn't have a FieldInfo of type Header with the default convert_underscores=True
766
- convert_underscores = getattr(field.field_info, "convert_underscores", True)
772
+ convert_underscores = getattr(
773
+ field.field_info, "convert_underscores", default_convert_underscores
774
+ )
767
775
  if convert_underscores:
768
776
  alias = (
769
777
  field.alias
@@ -782,9 +790,9 @@ def request_params_to_args(
782
790
 
783
791
  if single_not_embedded_field:
784
792
  field_info = first_field.field_info
785
- assert isinstance(
786
- field_info, params.Param
787
- ), "Params must be subclasses of Param"
793
+ assert isinstance(field_info, params.Param), (
794
+ "Params must be subclasses of Param"
795
+ )
788
796
  loc: Tuple[str, ...] = (field_info.in_.value,)
789
797
  v_, errors_ = _validate_value_with_model_field(
790
798
  field=first_field, value=params_to_process, values=values, loc=loc
@@ -794,9 +802,9 @@ def request_params_to_args(
794
802
  for field in fields:
795
803
  value = _get_multidict_value(field, received_params)
796
804
  field_info = field.field_info
797
- assert isinstance(
798
- field_info, params.Param
799
- ), "Params must be subclasses of Param"
805
+ assert isinstance(field_info, params.Param), (
806
+ "Params must be subclasses of Param"
807
+ )
800
808
  loc = (field_info.in_.value, field.alias)
801
809
  v_, errors_ = _validate_value_with_model_field(
802
810
  field=field, value=value, values=values, loc=loc
fastapi/openapi/utils.py CHANGED
@@ -32,6 +32,7 @@ from fastapi.utils import (
32
32
  generate_operation_id_for_path,
33
33
  is_body_allowed_for_status_code,
34
34
  )
35
+ from pydantic import BaseModel
35
36
  from starlette.responses import JSONResponse
36
37
  from starlette.routing import BaseRoute
37
38
  from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
@@ -113,6 +114,13 @@ def _get_openapi_operation_parameters(
113
114
  (ParamTypes.header, header_params),
114
115
  (ParamTypes.cookie, cookie_params),
115
116
  ]
117
+ default_convert_underscores = True
118
+ if len(flat_dependant.header_params) == 1:
119
+ first_field = flat_dependant.header_params[0]
120
+ if lenient_issubclass(first_field.type_, BaseModel):
121
+ default_convert_underscores = getattr(
122
+ first_field.field_info, "convert_underscores", True
123
+ )
116
124
  for param_type, param_group in parameter_groups:
117
125
  for param in param_group:
118
126
  field_info = param.field_info
@@ -126,8 +134,21 @@ def _get_openapi_operation_parameters(
126
134
  field_mapping=field_mapping,
127
135
  separate_input_output_schemas=separate_input_output_schemas,
128
136
  )
137
+ name = param.alias
138
+ convert_underscores = getattr(
139
+ param.field_info,
140
+ "convert_underscores",
141
+ default_convert_underscores,
142
+ )
143
+ if (
144
+ param_type == ParamTypes.header
145
+ and param.alias == param.name
146
+ and convert_underscores
147
+ ):
148
+ name = param.name.replace("_", "-")
149
+
129
150
  parameter = {
130
- "name": param.alias,
151
+ "name": name,
131
152
  "in": param_type.value,
132
153
  "required": param.required,
133
154
  "schema": param_schema,
@@ -364,9 +385,9 @@ def get_openapi_path(
364
385
  openapi_response = operation_responses.setdefault(
365
386
  status_code_key, {}
366
387
  )
367
- assert isinstance(
368
- process_response, dict
369
- ), "An additional response must be a dict"
388
+ assert isinstance(process_response, dict), (
389
+ "An additional response must be a dict"
390
+ )
370
391
  field = route.response_fields.get(additional_status_code)
371
392
  additional_field_schema: Optional[Dict[str, Any]] = None
372
393
  if field:
@@ -434,9 +455,9 @@ def get_fields_from_routes(
434
455
  route, routing.APIRoute
435
456
  ):
436
457
  if route.body_field:
437
- assert isinstance(
438
- route.body_field, ModelField
439
- ), "A request body must be a Pydantic Field"
458
+ assert isinstance(route.body_field, ModelField), (
459
+ "A request body must be a Pydantic Field"
460
+ )
440
461
  body_fields_from_routes.append(route.body_field)
441
462
  if route.response_field:
442
463
  responses_from_routes.append(route.response_field)
fastapi/routing.py CHANGED
@@ -504,9 +504,9 @@ class APIRoute(routing.Route):
504
504
  status_code = int(status_code)
505
505
  self.status_code = status_code
506
506
  if self.response_model:
507
- assert is_body_allowed_for_status_code(
508
- status_code
509
- ), f"Status code {status_code} must not have a response body"
507
+ assert is_body_allowed_for_status_code(status_code), (
508
+ f"Status code {status_code} must not have a response body"
509
+ )
510
510
  response_name = "Response_" + self.unique_id
511
511
  self.response_field = create_model_field(
512
512
  name=response_name,
@@ -537,9 +537,9 @@ class APIRoute(routing.Route):
537
537
  assert isinstance(response, dict), "An additional response must be a dict"
538
538
  model = response.get("model")
539
539
  if model:
540
- assert is_body_allowed_for_status_code(
541
- additional_status_code
542
- ), f"Status code {additional_status_code} must not have a response body"
540
+ assert is_body_allowed_for_status_code(additional_status_code), (
541
+ f"Status code {additional_status_code} must not have a response body"
542
+ )
543
543
  response_name = f"Response_{additional_status_code}_{self.unique_id}"
544
544
  response_field = create_model_field(
545
545
  name=response_name, type_=model, mode="serialization"
@@ -844,9 +844,9 @@ class APIRouter(routing.Router):
844
844
  )
845
845
  if prefix:
846
846
  assert prefix.startswith("/"), "A path prefix must start with '/'"
847
- assert not prefix.endswith(
848
- "/"
849
- ), "A path prefix must not end with '/', as the routes will start with '/'"
847
+ assert not prefix.endswith("/"), (
848
+ "A path prefix must not end with '/', as the routes will start with '/'"
849
+ )
850
850
  self.prefix = prefix
851
851
  self.tags: List[Union[str, Enum]] = tags or []
852
852
  self.dependencies = list(dependencies or [])
@@ -1256,9 +1256,9 @@ class APIRouter(routing.Router):
1256
1256
  """
1257
1257
  if prefix:
1258
1258
  assert prefix.startswith("/"), "A path prefix must start with '/'"
1259
- assert not prefix.endswith(
1260
- "/"
1261
- ), "A path prefix must not end with '/', as the routes will start with '/'"
1259
+ assert not prefix.endswith("/"), (
1260
+ "A path prefix must not end with '/', as the routes will start with '/'"
1261
+ )
1262
1262
  else:
1263
1263
  for r in router.routes:
1264
1264
  path = getattr(r, "path") # noqa: B009
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastapi
3
- Version: 0.115.10
3
+ Version: 0.115.12
4
4
  Summary: FastAPI framework, high performance, easy to learn, fast to code, ready for production
5
5
  Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= <tiangolo@gmail.com>
6
6
  Classifier: Intended Audience :: Information Technology
@@ -1,8 +1,8 @@
1
- fastapi-0.115.10.dist-info/METADATA,sha256=DCVDfIRQ-BQPnNWPl6HZ2CZf1kUHaIhu87smfkpJGFo,27671
2
- fastapi-0.115.10.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- fastapi-0.115.10.dist-info/entry_points.txt,sha256=GCf-WbIZxyGT4MUmrPGj1cOHYZoGsNPHAvNkT6hnGeA,61
4
- fastapi-0.115.10.dist-info/licenses/LICENSE,sha256=Tsif_IFIW5f-xYSy1KlhAy7v_oNEU4lP2cEnSQbMdE4,1086
5
- fastapi/__init__.py,sha256=nMP1hR9QRINvAUrWR4lWM1l9t-FPY5cedXhjxYc1lzQ,1082
1
+ fastapi-0.115.12.dist-info/METADATA,sha256=mV7D-DjELmGAedtcxayASLcleQv41h4mqHQhlqtLu1s,27671
2
+ fastapi-0.115.12.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ fastapi-0.115.12.dist-info/entry_points.txt,sha256=GCf-WbIZxyGT4MUmrPGj1cOHYZoGsNPHAvNkT6hnGeA,61
4
+ fastapi-0.115.12.dist-info/licenses/LICENSE,sha256=Tsif_IFIW5f-xYSy1KlhAy7v_oNEU4lP2cEnSQbMdE4,1086
5
+ fastapi/__init__.py,sha256=sgu-sth5uRYC--mOXSta-IPYjnDKUrpRSz6LuvKHf9o,1082
6
6
  fastapi/__main__.py,sha256=bKePXLdO4SsVSM6r9SVoLickJDcR2c0cTOxZRKq26YQ,37
7
7
  fastapi/_compat.py,sha256=Rg7kA7uue4Z6yr8T7hf8b7G6PeC_06mK004Nnykijfk,23953
8
8
  fastapi/applications.py,sha256=Ix-o9pQAWhEDf9J0Q1hZ0nBB1uP72c-Y3oiYzvrwqiM,176316
@@ -12,7 +12,7 @@ fastapi/concurrency.py,sha256=MirfowoSpkMQZ8j_g0ZxaQKpV6eB3G-dB5TgcXCrgEA,1424
12
12
  fastapi/datastructures.py,sha256=b2PEz77XGq-u3Ur1Inwk0AGjOsQZO49yF9C7IPJ15cY,5766
13
13
  fastapi/dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  fastapi/dependencies/models.py,sha256=Pjl6vx-4nZ5Tta9kJa3-RfQKkXtCpS09-FhMgs9eWNs,1507
15
- fastapi/dependencies/utils.py,sha256=UexghAjDUH8l4DudFQB-yASK8_J56oVAZGgD6dRIceU,35583
15
+ fastapi/dependencies/utils.py,sha256=SwOOdQYOC0E4thJ-n1a0ohslWN9duyasNY_JBdWPTsY,35971
16
16
  fastapi/encoders.py,sha256=LvwYmFeOz4tVwvgBoC5rvZnbr7hZr73KGrU8O7zSptU,11068
17
17
  fastapi/exception_handlers.py,sha256=MBrIOA-ugjJDivIi4rSsUJBdTsjuzN76q4yh0q1COKw,1332
18
18
  fastapi/exceptions.py,sha256=taNixuFEXb67lI1bnX1ubq8y8TseJ4yoPlWjyP0fTzk,4969
@@ -27,13 +27,13 @@ fastapi/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  fastapi/openapi/constants.py,sha256=adGzmis1L1HJRTE3kJ5fmHS_Noq6tIY6pWv_SFzoFDU,153
28
28
  fastapi/openapi/docs.py,sha256=XcQq-ZbQdC5sI0gIGu5MoHK1q-OFaqws7-ORTo6sjY4,10348
29
29
  fastapi/openapi/models.py,sha256=PqkxQiqcEgjKuhfUIWPZPQcyTcubtUCB3vcObLsB7VE,15397
30
- fastapi/openapi/utils.py,sha256=vpbAzWpuNaJL_ocBxt4jp0GUUwrDKNB1anyoAx69fhA,23177
30
+ fastapi/openapi/utils.py,sha256=e00G_p0IdpiffBUaq31BUyiloXbpld8RryKYnYKisdY,23964
31
31
  fastapi/param_functions.py,sha256=JHNPLIYvoAwdnZZavIVsxOat8x23fX_Kl33reh7HKl8,64019
32
32
  fastapi/params.py,sha256=g450axUBQgQJODdtM7WBxZbQj9Z64inFvadrgHikBbU,28237
33
33
  fastapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  fastapi/requests.py,sha256=zayepKFcienBllv3snmWI20Gk0oHNVLU4DDhqXBb4LU,142
35
35
  fastapi/responses.py,sha256=QNQQlwpKhQoIPZTTWkpc9d_QGeGZ_aVQPaDV3nQ8m7c,1761
36
- fastapi/routing.py,sha256=WK06IwZeyRwxkdB4uHZ7JXinxtVOxM8Ke0tqHaDOvYA,176208
36
+ fastapi/routing.py,sha256=qwk_Vm1k4vDGdeEskNCVYTK01fDgYikqfOS_lf0DqAc,176216
37
37
  fastapi/security/__init__.py,sha256=bO8pNmxqVRXUjfl2mOKiVZLn0FpBQ61VUYVjmppnbJw,881
38
38
  fastapi/security/api_key.py,sha256=cBI5Z4zWVjL1uJrsjTeLy7MafHPAO2HQPzTrpyoIYWA,9094
39
39
  fastapi/security/base.py,sha256=dl4pvbC-RxjfbWgPtCWd8MVU-7CB2SZ22rJDXVCXO6c,141
@@ -47,4 +47,4 @@ fastapi/testclient.py,sha256=nBvaAmX66YldReJNZXPOk1sfuo2Q6hs8bOvIaCep6LQ,66
47
47
  fastapi/types.py,sha256=nFb36sK3DSoqoyo7Miwy3meKK5UdFBgkAgLSzQlUVyI,383
48
48
  fastapi/utils.py,sha256=y8Bj5ttMaI9tS4D60OUgXqKnktBr99NdYUnHHV9LgoY,7948
49
49
  fastapi/websockets.py,sha256=419uncYObEKZG0YcrXscfQQYLSWoE10jqxVMetGdR98,222
50
- fastapi-0.115.10.dist-info/RECORD,,
50
+ fastapi-0.115.12.dist-info/RECORD,,