fastapi 0.96.1__py3-none-any.whl → 0.100.0b1__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/openapi/utils.py CHANGED
@@ -1,32 +1,32 @@
1
1
  import http.client
2
2
  import inspect
3
3
  import warnings
4
- from enum import Enum
5
4
  from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast
6
5
 
7
6
  from fastapi import routing
7
+ from fastapi._compat import (
8
+ GenerateJsonSchema,
9
+ ModelField,
10
+ Undefined,
11
+ get_compat_model_name_map,
12
+ get_definitions,
13
+ get_schema_from_model_field,
14
+ lenient_issubclass,
15
+ )
8
16
  from fastapi.datastructures import DefaultPlaceholder
9
17
  from fastapi.dependencies.models import Dependant
10
18
  from fastapi.dependencies.utils import get_flat_dependant, get_flat_params
11
19
  from fastapi.encoders import jsonable_encoder
12
- from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX
20
+ from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX, REF_TEMPLATE
13
21
  from fastapi.openapi.models import OpenAPI
14
22
  from fastapi.params import Body, Param
15
23
  from fastapi.responses import Response
24
+ from fastapi.types import ModelNameMap
16
25
  from fastapi.utils import (
17
26
  deep_dict_update,
18
27
  generate_operation_id_for_path,
19
- get_model_definitions,
20
28
  is_body_allowed_for_status_code,
21
29
  )
22
- from pydantic import BaseModel
23
- from pydantic.fields import ModelField, Undefined
24
- from pydantic.schema import (
25
- field_schema,
26
- get_flat_models_from_fields,
27
- get_model_name_map,
28
- )
29
- from pydantic.utils import lenient_issubclass
30
30
  from starlette.responses import JSONResponse
31
31
  from starlette.routing import BaseRoute
32
32
  from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
@@ -88,7 +88,8 @@ def get_openapi_security_definitions(
88
88
  def get_openapi_operation_parameters(
89
89
  *,
90
90
  all_route_params: Sequence[ModelField],
91
- model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
91
+ schema_generator: GenerateJsonSchema,
92
+ model_name_map: ModelNameMap,
92
93
  ) -> List[Dict[str, Any]]:
93
94
  parameters = []
94
95
  for param in all_route_params:
@@ -96,13 +97,16 @@ def get_openapi_operation_parameters(
96
97
  field_info = cast(Param, field_info)
97
98
  if not field_info.include_in_schema:
98
99
  continue
100
+ param_schema = get_schema_from_model_field(
101
+ field=param,
102
+ schema_generator=schema_generator,
103
+ model_name_map=model_name_map,
104
+ )
99
105
  parameter = {
100
106
  "name": param.alias,
101
107
  "in": field_info.in_.value,
102
108
  "required": param.required,
103
- "schema": field_schema(
104
- param, model_name_map=model_name_map, ref_prefix=REF_PREFIX
105
- )[0],
109
+ "schema": param_schema,
106
110
  }
107
111
  if field_info.description:
108
112
  parameter["description"] = field_info.description
@@ -119,13 +123,16 @@ def get_openapi_operation_parameters(
119
123
  def get_openapi_operation_request_body(
120
124
  *,
121
125
  body_field: Optional[ModelField],
122
- model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
126
+ schema_generator: GenerateJsonSchema,
127
+ model_name_map: ModelNameMap,
123
128
  ) -> Optional[Dict[str, Any]]:
124
129
  if not body_field:
125
130
  return None
126
131
  assert isinstance(body_field, ModelField)
127
- body_schema, _, _ = field_schema(
128
- body_field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
132
+ body_schema = get_schema_from_model_field(
133
+ field=body_field,
134
+ schema_generator=schema_generator,
135
+ model_name_map=model_name_map,
129
136
  )
130
137
  field_info = cast(Body, body_field.field_info)
131
138
  request_media_type = field_info.media_type
@@ -181,7 +188,7 @@ def get_openapi_operation_metadata(
181
188
  file_name = getattr(route.endpoint, "__globals__", {}).get("__file__")
182
189
  if file_name:
183
190
  message += f" at {file_name}"
184
- warnings.warn(message)
191
+ warnings.warn(message, stacklevel=1)
185
192
  operation_ids.add(operation_id)
186
193
  operation["operationId"] = operation_id
187
194
  if route.deprecated:
@@ -190,7 +197,11 @@ def get_openapi_operation_metadata(
190
197
 
191
198
 
192
199
  def get_openapi_path(
193
- *, route: routing.APIRoute, model_name_map: Dict[type, str], operation_ids: Set[str]
200
+ *,
201
+ route: routing.APIRoute,
202
+ operation_ids: Set[str],
203
+ schema_generator: GenerateJsonSchema,
204
+ model_name_map: ModelNameMap,
194
205
  ) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
195
206
  path = {}
196
207
  security_schemes: Dict[str, Any] = {}
@@ -218,7 +229,9 @@ def get_openapi_path(
218
229
  security_schemes.update(security_definitions)
219
230
  all_route_params = get_flat_params(route.dependant)
220
231
  operation_parameters = get_openapi_operation_parameters(
221
- all_route_params=all_route_params, model_name_map=model_name_map
232
+ all_route_params=all_route_params,
233
+ schema_generator=schema_generator,
234
+ model_name_map=model_name_map,
222
235
  )
223
236
  parameters.extend(operation_parameters)
224
237
  if parameters:
@@ -236,7 +249,9 @@ def get_openapi_path(
236
249
  operation["parameters"] = list(all_parameters.values())
237
250
  if method in METHODS_WITH_BODY:
238
251
  request_body_oai = get_openapi_operation_request_body(
239
- body_field=route.body_field, model_name_map=model_name_map
252
+ body_field=route.body_field,
253
+ schema_generator=schema_generator,
254
+ model_name_map=model_name_map,
240
255
  )
241
256
  if request_body_oai:
242
257
  operation["requestBody"] = request_body_oai
@@ -250,8 +265,9 @@ def get_openapi_path(
250
265
  cb_definitions,
251
266
  ) = get_openapi_path(
252
267
  route=callback,
253
- model_name_map=model_name_map,
254
268
  operation_ids=operation_ids,
269
+ schema_generator=schema_generator,
270
+ model_name_map=model_name_map,
255
271
  )
256
272
  callbacks[callback.name] = {callback.path: cb_path}
257
273
  operation["callbacks"] = callbacks
@@ -277,10 +293,10 @@ def get_openapi_path(
277
293
  response_schema = {"type": "string"}
278
294
  if lenient_issubclass(current_response_class, JSONResponse):
279
295
  if route.response_field:
280
- response_schema, _, _ = field_schema(
281
- route.response_field,
296
+ response_schema = get_schema_from_model_field(
297
+ field=route.response_field,
298
+ schema_generator=schema_generator,
282
299
  model_name_map=model_name_map,
283
- ref_prefix=REF_PREFIX,
284
300
  )
285
301
  else:
286
302
  response_schema = {}
@@ -309,8 +325,10 @@ def get_openapi_path(
309
325
  field = route.response_fields.get(additional_status_code)
310
326
  additional_field_schema: Optional[Dict[str, Any]] = None
311
327
  if field:
312
- additional_field_schema, _, _ = field_schema(
313
- field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
328
+ additional_field_schema = get_schema_from_model_field(
329
+ field=field,
330
+ schema_generator=schema_generator,
331
+ model_name_map=model_name_map,
314
332
  )
315
333
  media_type = route_response_media_type or "application/json"
316
334
  additional_schema = (
@@ -332,10 +350,8 @@ def get_openapi_path(
332
350
  openapi_response["description"] = description
333
351
  http422 = str(HTTP_422_UNPROCESSABLE_ENTITY)
334
352
  if (all_route_params or route.body_field) and not any(
335
- [
336
- status in operation["responses"]
337
- for status in [http422, "4XX", "default"]
338
- ]
353
+ status in operation["responses"]
354
+ for status in [http422, "4XX", "default"]
339
355
  ):
340
356
  operation["responses"][http422] = {
341
357
  "description": "Validation Error",
@@ -358,13 +374,13 @@ def get_openapi_path(
358
374
  return path, security_schemes, definitions
359
375
 
360
376
 
361
- def get_flat_models_from_routes(
377
+ def get_fields_from_routes(
362
378
  routes: Sequence[BaseRoute],
363
- ) -> Set[Union[Type[BaseModel], Type[Enum]]]:
379
+ ) -> List[ModelField]:
364
380
  body_fields_from_routes: List[ModelField] = []
365
381
  responses_from_routes: List[ModelField] = []
366
382
  request_fields_from_routes: List[ModelField] = []
367
- callback_flat_models: Set[Union[Type[BaseModel], Type[Enum]]] = set()
383
+ callback_flat_models: List[ModelField] = []
368
384
  for route in routes:
369
385
  if getattr(route, "include_in_schema", None) and isinstance(
370
386
  route, routing.APIRoute
@@ -379,13 +395,12 @@ def get_flat_models_from_routes(
379
395
  if route.response_fields:
380
396
  responses_from_routes.extend(route.response_fields.values())
381
397
  if route.callbacks:
382
- callback_flat_models |= get_flat_models_from_routes(route.callbacks)
398
+ callback_flat_models.extend(get_fields_from_routes(route.callbacks))
383
399
  params = get_flat_params(route.dependant)
384
400
  request_fields_from_routes.extend(params)
385
401
 
386
- flat_models = callback_flat_models | get_flat_models_from_fields(
387
- body_fields_from_routes + responses_from_routes + request_fields_from_routes,
388
- known_models=set(),
402
+ flat_models = callback_flat_models + list(
403
+ body_fields_from_routes + responses_from_routes + request_fields_from_routes
389
404
  )
390
405
  return flat_models
391
406
 
@@ -418,15 +433,21 @@ def get_openapi(
418
433
  components: Dict[str, Dict[str, Any]] = {}
419
434
  paths: Dict[str, Dict[str, Any]] = {}
420
435
  operation_ids: Set[str] = set()
421
- flat_models = get_flat_models_from_routes(routes)
422
- model_name_map = get_model_name_map(flat_models)
423
- definitions = get_model_definitions(
424
- flat_models=flat_models, model_name_map=model_name_map
436
+ all_fields = get_fields_from_routes(routes)
437
+ model_name_map = get_compat_model_name_map(all_fields)
438
+ schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
439
+ definitions = get_definitions(
440
+ fields=all_fields,
441
+ schema_generator=schema_generator,
442
+ model_name_map=model_name_map,
425
443
  )
426
444
  for route in routes:
427
445
  if isinstance(route, routing.APIRoute):
428
446
  result = get_openapi_path(
429
- route=route, model_name_map=model_name_map, operation_ids=operation_ids
447
+ route=route,
448
+ operation_ids=operation_ids,
449
+ schema_generator=schema_generator,
450
+ model_name_map=model_name_map,
430
451
  )
431
452
  if result:
432
453
  path, security_schemes, path_definitions = result
@@ -1,7 +1,7 @@
1
1
  from typing import Any, Callable, Dict, Optional, Sequence
2
2
 
3
3
  from fastapi import params
4
- from pydantic.fields import Undefined
4
+ from fastapi._compat import Undefined
5
5
 
6
6
 
7
7
  def Path( # noqa: N802
@@ -16,6 +16,7 @@ def Path( # noqa: N802
16
16
  le: Optional[float] = None,
17
17
  min_length: Optional[int] = None,
18
18
  max_length: Optional[int] = None,
19
+ pattern: Optional[str] = None,
19
20
  regex: Optional[str] = None,
20
21
  example: Any = Undefined,
21
22
  examples: Optional[Dict[str, Any]] = None,
@@ -34,6 +35,7 @@ def Path( # noqa: N802
34
35
  le=le,
35
36
  min_length=min_length,
36
37
  max_length=max_length,
38
+ pattern=pattern,
37
39
  regex=regex,
38
40
  example=example,
39
41
  examples=examples,
@@ -55,6 +57,7 @@ def Query( # noqa: N802
55
57
  le: Optional[float] = None,
56
58
  min_length: Optional[int] = None,
57
59
  max_length: Optional[int] = None,
60
+ pattern: Optional[str] = None,
58
61
  regex: Optional[str] = None,
59
62
  example: Any = Undefined,
60
63
  examples: Optional[Dict[str, Any]] = None,
@@ -73,6 +76,7 @@ def Query( # noqa: N802
73
76
  le=le,
74
77
  min_length=min_length,
75
78
  max_length=max_length,
79
+ pattern=pattern,
76
80
  regex=regex,
77
81
  example=example,
78
82
  examples=examples,
@@ -95,6 +99,7 @@ def Header( # noqa: N802
95
99
  le: Optional[float] = None,
96
100
  min_length: Optional[int] = None,
97
101
  max_length: Optional[int] = None,
102
+ pattern: Optional[str] = None,
98
103
  regex: Optional[str] = None,
99
104
  example: Any = Undefined,
100
105
  examples: Optional[Dict[str, Any]] = None,
@@ -114,6 +119,7 @@ def Header( # noqa: N802
114
119
  le=le,
115
120
  min_length=min_length,
116
121
  max_length=max_length,
122
+ pattern=pattern,
117
123
  regex=regex,
118
124
  example=example,
119
125
  examples=examples,
@@ -135,6 +141,7 @@ def Cookie( # noqa: N802
135
141
  le: Optional[float] = None,
136
142
  min_length: Optional[int] = None,
137
143
  max_length: Optional[int] = None,
144
+ pattern: Optional[str] = None,
138
145
  regex: Optional[str] = None,
139
146
  example: Any = Undefined,
140
147
  examples: Optional[Dict[str, Any]] = None,
@@ -153,6 +160,7 @@ def Cookie( # noqa: N802
153
160
  le=le,
154
161
  min_length=min_length,
155
162
  max_length=max_length,
163
+ pattern=pattern,
156
164
  regex=regex,
157
165
  example=example,
158
166
  examples=examples,
@@ -176,6 +184,7 @@ def Body( # noqa: N802
176
184
  le: Optional[float] = None,
177
185
  min_length: Optional[int] = None,
178
186
  max_length: Optional[int] = None,
187
+ pattern: Optional[str] = None,
179
188
  regex: Optional[str] = None,
180
189
  example: Any = Undefined,
181
190
  examples: Optional[Dict[str, Any]] = None,
@@ -194,6 +203,7 @@ def Body( # noqa: N802
194
203
  le=le,
195
204
  min_length=min_length,
196
205
  max_length=max_length,
206
+ pattern=pattern,
197
207
  regex=regex,
198
208
  example=example,
199
209
  examples=examples,
@@ -214,6 +224,7 @@ def Form( # noqa: N802
214
224
  le: Optional[float] = None,
215
225
  min_length: Optional[int] = None,
216
226
  max_length: Optional[int] = None,
227
+ pattern: Optional[str] = None,
217
228
  regex: Optional[str] = None,
218
229
  example: Any = Undefined,
219
230
  examples: Optional[Dict[str, Any]] = None,
@@ -231,6 +242,7 @@ def Form( # noqa: N802
231
242
  le=le,
232
243
  min_length=min_length,
233
244
  max_length=max_length,
245
+ pattern=pattern,
234
246
  regex=regex,
235
247
  example=example,
236
248
  examples=examples,
@@ -251,6 +263,7 @@ def File( # noqa: N802
251
263
  le: Optional[float] = None,
252
264
  min_length: Optional[int] = None,
253
265
  max_length: Optional[int] = None,
266
+ pattern: Optional[str] = None,
254
267
  regex: Optional[str] = None,
255
268
  example: Any = Undefined,
256
269
  examples: Optional[Dict[str, Any]] = None,
@@ -268,6 +281,7 @@ def File( # noqa: N802
268
281
  le=le,
269
282
  min_length=min_length,
270
283
  max_length=max_length,
284
+ pattern=pattern,
271
285
  regex=regex,
272
286
  example=example,
273
287
  examples=examples,
fastapi/params.py CHANGED
@@ -1,7 +1,9 @@
1
1
  from enum import Enum
2
- from typing import Any, Callable, Dict, Optional, Sequence
2
+ from typing import Any, Callable, Dict, Optional, Sequence, Type
3
3
 
4
- from pydantic.fields import FieldInfo, Undefined
4
+ from pydantic.fields import FieldInfo
5
+
6
+ from ._compat import PYDANTIC_V2, Undefined
5
7
 
6
8
 
7
9
  class ParamTypes(Enum):
@@ -18,6 +20,7 @@ class Param(FieldInfo):
18
20
  self,
19
21
  default: Any = Undefined,
20
22
  *,
23
+ annotation: Optional[Type[Any]] = None,
21
24
  alias: Optional[str] = None,
22
25
  title: Optional[str] = None,
23
26
  description: Optional[str] = None,
@@ -27,6 +30,7 @@ class Param(FieldInfo):
27
30
  le: Optional[float] = None,
28
31
  min_length: Optional[int] = None,
29
32
  max_length: Optional[int] = None,
33
+ pattern: Optional[str] = None,
30
34
  regex: Optional[str] = None,
31
35
  example: Any = Undefined,
32
36
  examples: Optional[Dict[str, Any]] = None,
@@ -36,9 +40,8 @@ class Param(FieldInfo):
36
40
  ):
37
41
  self.deprecated = deprecated
38
42
  self.example = example
39
- self.examples = examples
40
43
  self.include_in_schema = include_in_schema
41
- super().__init__(
44
+ kwargs = dict(
42
45
  default=default,
43
46
  alias=alias,
44
47
  title=title,
@@ -49,9 +52,19 @@ class Param(FieldInfo):
49
52
  le=le,
50
53
  min_length=min_length,
51
54
  max_length=max_length,
52
- regex=regex,
53
55
  **extra,
54
56
  )
57
+ if PYDANTIC_V2:
58
+ kwargs["annotation"] = annotation
59
+ kwargs["pattern"] = pattern or regex
60
+ else:
61
+ # TODO: pv2 figure out how to deprecate regex
62
+ kwargs["regex"] = pattern or regex
63
+
64
+ super().__init__(**kwargs)
65
+ # TODO: pv2 decide how to handle OpenAPI examples vs JSON Schema examples
66
+ # and how to deprecate OpenAPI examples
67
+ self.examples = examples # type: ignore[assignment]
55
68
 
56
69
  def __repr__(self) -> str:
57
70
  return f"{self.__class__.__name__}({self.default})"
@@ -64,6 +77,7 @@ class Path(Param):
64
77
  self,
65
78
  default: Any = ...,
66
79
  *,
80
+ annotation: Optional[Type[Any]] = None,
67
81
  alias: Optional[str] = None,
68
82
  title: Optional[str] = None,
69
83
  description: Optional[str] = None,
@@ -73,6 +87,7 @@ class Path(Param):
73
87
  le: Optional[float] = None,
74
88
  min_length: Optional[int] = None,
75
89
  max_length: Optional[int] = None,
90
+ pattern: Optional[str] = None,
76
91
  regex: Optional[str] = None,
77
92
  example: Any = Undefined,
78
93
  examples: Optional[Dict[str, Any]] = None,
@@ -84,6 +99,7 @@ class Path(Param):
84
99
  self.in_ = self.in_
85
100
  super().__init__(
86
101
  default=default,
102
+ annotation=annotation,
87
103
  alias=alias,
88
104
  title=title,
89
105
  description=description,
@@ -93,6 +109,7 @@ class Path(Param):
93
109
  le=le,
94
110
  min_length=min_length,
95
111
  max_length=max_length,
112
+ pattern=pattern,
96
113
  regex=regex,
97
114
  deprecated=deprecated,
98
115
  example=example,
@@ -109,6 +126,7 @@ class Query(Param):
109
126
  self,
110
127
  default: Any = Undefined,
111
128
  *,
129
+ annotation: Optional[Type[Any]] = None,
112
130
  alias: Optional[str] = None,
113
131
  title: Optional[str] = None,
114
132
  description: Optional[str] = None,
@@ -118,6 +136,7 @@ class Query(Param):
118
136
  le: Optional[float] = None,
119
137
  min_length: Optional[int] = None,
120
138
  max_length: Optional[int] = None,
139
+ pattern: Optional[str] = None,
121
140
  regex: Optional[str] = None,
122
141
  example: Any = Undefined,
123
142
  examples: Optional[Dict[str, Any]] = None,
@@ -127,6 +146,7 @@ class Query(Param):
127
146
  ):
128
147
  super().__init__(
129
148
  default=default,
149
+ annotation=annotation,
130
150
  alias=alias,
131
151
  title=title,
132
152
  description=description,
@@ -136,6 +156,7 @@ class Query(Param):
136
156
  le=le,
137
157
  min_length=min_length,
138
158
  max_length=max_length,
159
+ pattern=pattern,
139
160
  regex=regex,
140
161
  deprecated=deprecated,
141
162
  example=example,
@@ -152,6 +173,7 @@ class Header(Param):
152
173
  self,
153
174
  default: Any = Undefined,
154
175
  *,
176
+ annotation: Optional[Type[Any]] = None,
155
177
  alias: Optional[str] = None,
156
178
  convert_underscores: bool = True,
157
179
  title: Optional[str] = None,
@@ -162,6 +184,7 @@ class Header(Param):
162
184
  le: Optional[float] = None,
163
185
  min_length: Optional[int] = None,
164
186
  max_length: Optional[int] = None,
187
+ pattern: Optional[str] = None,
165
188
  regex: Optional[str] = None,
166
189
  example: Any = Undefined,
167
190
  examples: Optional[Dict[str, Any]] = None,
@@ -172,6 +195,7 @@ class Header(Param):
172
195
  self.convert_underscores = convert_underscores
173
196
  super().__init__(
174
197
  default=default,
198
+ annotation=annotation,
175
199
  alias=alias,
176
200
  title=title,
177
201
  description=description,
@@ -181,6 +205,7 @@ class Header(Param):
181
205
  le=le,
182
206
  min_length=min_length,
183
207
  max_length=max_length,
208
+ pattern=pattern,
184
209
  regex=regex,
185
210
  deprecated=deprecated,
186
211
  example=example,
@@ -197,6 +222,7 @@ class Cookie(Param):
197
222
  self,
198
223
  default: Any = Undefined,
199
224
  *,
225
+ annotation: Optional[Type[Any]] = None,
200
226
  alias: Optional[str] = None,
201
227
  title: Optional[str] = None,
202
228
  description: Optional[str] = None,
@@ -206,6 +232,7 @@ class Cookie(Param):
206
232
  le: Optional[float] = None,
207
233
  min_length: Optional[int] = None,
208
234
  max_length: Optional[int] = None,
235
+ pattern: Optional[str] = None,
209
236
  regex: Optional[str] = None,
210
237
  example: Any = Undefined,
211
238
  examples: Optional[Dict[str, Any]] = None,
@@ -215,6 +242,7 @@ class Cookie(Param):
215
242
  ):
216
243
  super().__init__(
217
244
  default=default,
245
+ annotation=annotation,
218
246
  alias=alias,
219
247
  title=title,
220
248
  description=description,
@@ -224,6 +252,7 @@ class Cookie(Param):
224
252
  le=le,
225
253
  min_length=min_length,
226
254
  max_length=max_length,
255
+ pattern=pattern,
227
256
  regex=regex,
228
257
  deprecated=deprecated,
229
258
  example=example,
@@ -238,6 +267,7 @@ class Body(FieldInfo):
238
267
  self,
239
268
  default: Any = Undefined,
240
269
  *,
270
+ annotation: Optional[Type[Any]] = None,
241
271
  embed: bool = False,
242
272
  media_type: str = "application/json",
243
273
  alias: Optional[str] = None,
@@ -249,6 +279,7 @@ class Body(FieldInfo):
249
279
  le: Optional[float] = None,
250
280
  min_length: Optional[int] = None,
251
281
  max_length: Optional[int] = None,
282
+ pattern: Optional[str] = None,
252
283
  regex: Optional[str] = None,
253
284
  example: Any = Undefined,
254
285
  examples: Optional[Dict[str, Any]] = None,
@@ -257,8 +288,7 @@ class Body(FieldInfo):
257
288
  self.embed = embed
258
289
  self.media_type = media_type
259
290
  self.example = example
260
- self.examples = examples
261
- super().__init__(
291
+ kwargs = dict(
262
292
  default=default,
263
293
  alias=alias,
264
294
  title=title,
@@ -269,9 +299,20 @@ class Body(FieldInfo):
269
299
  le=le,
270
300
  min_length=min_length,
271
301
  max_length=max_length,
272
- regex=regex,
273
302
  **extra,
274
303
  )
304
+ if PYDANTIC_V2:
305
+ kwargs["annotation"] = annotation
306
+ kwargs["pattern"] = pattern or regex
307
+ else:
308
+ # TODO: pv2 figure out how to deprecate regex
309
+ kwargs["regex"] = pattern or regex
310
+ super().__init__(
311
+ **kwargs,
312
+ )
313
+ # TODO: pv2 decide how to handle OpenAPI examples vs JSON Schema examples
314
+ # and how to deprecate OpenAPI examples
315
+ self.examples = examples # type: ignore[assignment]
275
316
 
276
317
  def __repr__(self) -> str:
277
318
  return f"{self.__class__.__name__}({self.default})"
@@ -282,6 +323,7 @@ class Form(Body):
282
323
  self,
283
324
  default: Any = Undefined,
284
325
  *,
326
+ annotation: Optional[Type[Any]] = None,
285
327
  media_type: str = "application/x-www-form-urlencoded",
286
328
  alias: Optional[str] = None,
287
329
  title: Optional[str] = None,
@@ -292,6 +334,7 @@ class Form(Body):
292
334
  le: Optional[float] = None,
293
335
  min_length: Optional[int] = None,
294
336
  max_length: Optional[int] = None,
337
+ pattern: Optional[str] = None,
295
338
  regex: Optional[str] = None,
296
339
  example: Any = Undefined,
297
340
  examples: Optional[Dict[str, Any]] = None,
@@ -299,6 +342,7 @@ class Form(Body):
299
342
  ):
300
343
  super().__init__(
301
344
  default=default,
345
+ annotation=annotation,
302
346
  embed=True,
303
347
  media_type=media_type,
304
348
  alias=alias,
@@ -310,6 +354,7 @@ class Form(Body):
310
354
  le=le,
311
355
  min_length=min_length,
312
356
  max_length=max_length,
357
+ pattern=pattern,
313
358
  regex=regex,
314
359
  example=example,
315
360
  examples=examples,
@@ -322,6 +367,7 @@ class File(Form):
322
367
  self,
323
368
  default: Any = Undefined,
324
369
  *,
370
+ annotation: Optional[Type[Any]] = None,
325
371
  media_type: str = "multipart/form-data",
326
372
  alias: Optional[str] = None,
327
373
  title: Optional[str] = None,
@@ -332,6 +378,7 @@ class File(Form):
332
378
  le: Optional[float] = None,
333
379
  min_length: Optional[int] = None,
334
380
  max_length: Optional[int] = None,
381
+ pattern: Optional[str] = None,
335
382
  regex: Optional[str] = None,
336
383
  example: Any = Undefined,
337
384
  examples: Optional[Dict[str, Any]] = None,
@@ -339,6 +386,7 @@ class File(Form):
339
386
  ):
340
387
  super().__init__(
341
388
  default=default,
389
+ annotation=annotation,
342
390
  media_type=media_type,
343
391
  alias=alias,
344
392
  title=title,
@@ -349,6 +397,7 @@ class File(Form):
349
397
  le=le,
350
398
  min_length=min_length,
351
399
  max_length=max_length,
400
+ pattern=pattern,
352
401
  regex=regex,
353
402
  example=example,
354
403
  examples=examples,