Skip to content

Commit 532cf34

Browse files
committed
fix: document the new methods
1 parent 8001dfa commit 532cf34

File tree

5 files changed

+216
-59
lines changed

5 files changed

+216
-59
lines changed

aws_lambda_powertools/event_handler/api_gateway.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
import traceback
66
import warnings
7+
import zlib
78
from abc import ABC, abstractmethod
89
from enum import Enum
910
from functools import partial
@@ -23,7 +24,6 @@
2324
Union,
2425
)
2526

26-
import zlib
2727
from pydantic.fields import ModelField
2828
from pydantic.schema import get_flat_models_from_fields, get_model_name_map, model_process_schema
2929

@@ -685,7 +685,7 @@ def get_openapi_schema(
685685

686686
for route in all_routes:
687687
dependant = get_dependant(
688-
path=route.func.__name__,
688+
path=route.path,
689689
call=route.func,
690690
)
691691

aws_lambda_powertools/event_handler/openapi/dependant.py

+84-1
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,38 @@
77

88
from aws_lambda_powertools.event_handler.openapi.params import Dependant, Param, ParamTypes, analyze_param
99

10+
"""
11+
This turns the opaque function signature into typed, validated models.
12+
13+
It relies on Pydantic's typing and validation to achieve this in a declarative way.
14+
This enables traits like autocompletion, validation, and declarative structure vs imperative parsing.
15+
16+
This code parses an OpenAPI operation handler function signature into Pydantic models. It uses inspect to get the
17+
signature and regex to parse path parameters. Each parameter is analyzed to extract its type annotation and generate
18+
a corresponding Pydantic field, which are added to a Dependant model. Return values are handled similarly.
19+
20+
This modeling allows for type checking, automatic parameter name/location/type extraction, and input validation -
21+
turning the opaque signature into validated models. It relies on Pydantic's typing and validation for a declarative
22+
approach over imperative parsing, enabling autocompletion, validation and structure.
23+
"""
24+
1025

1126
def add_param_to_fields(
1227
*,
1328
field: ModelField,
1429
dependant: Dependant,
1530
) -> None:
31+
"""
32+
Adds a parameter to the list of parameters in the dependant model.
33+
34+
Parameters
35+
----------
36+
field: ModelField
37+
The field to add
38+
dependant: Dependant
39+
The dependant model to add the field to
40+
41+
"""
1642
field_info = cast(Param, field.field_info)
1743
if field_info.in_ == ParamTypes.path:
1844
dependant.path_params.append(field)
@@ -26,15 +52,34 @@ def add_param_to_fields(
2652

2753

2854
def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
55+
"""
56+
Evaluates a type annotation, which can be a string or a ForwardRef.
57+
"""
2958
if isinstance(annotation, str):
3059
annotation = ForwardRef(annotation)
3160
annotation = evaluate_forwardref(annotation, globalns, globalns)
3261
return annotation
3362

3463

3564
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
65+
"""
66+
Returns a typed signature for a callable, resolving forward references.
67+
68+
Parameters
69+
----------
70+
call: Callable[..., Any]
71+
The callable to get the signature for
72+
73+
Returns
74+
-------
75+
inspect.Signature
76+
The typed signature
77+
"""
3678
signature = inspect.signature(call)
79+
80+
# Gets the global namespace for the call. This is used to resolve forward references.
3781
globalns = getattr(call, "__global__", {})
82+
3883
typed_params = [
3984
inspect.Parameter(
4085
name=param.name,
@@ -45,6 +90,7 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
4590
for param in signature.parameters.values()
4691
]
4792

93+
# If the return annotation is not empty, add it to the signature.
4894
if signature.return_annotation is not inspect.Signature.empty:
4995
return_param = inspect.Parameter(
5096
name="Return",
@@ -58,7 +104,21 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
58104

59105

60106
def get_path_param_names(path: str) -> Set[str]:
61-
return set(re.findall("{(.*?)}", path))
107+
"""
108+
Returns the path parameter names from a path template. Those are the strings between < and >.
109+
110+
Parameters
111+
----------
112+
path: str
113+
The path template
114+
115+
Returns
116+
-------
117+
Set[str]
118+
The path parameter names
119+
120+
"""
121+
return set(re.findall("<(.*?)>", path))
62122

63123

64124
def get_dependant(
@@ -67,17 +127,39 @@ def get_dependant(
67127
call: Callable[..., Any],
68128
name: Optional[str] = None,
69129
) -> Dependant:
130+
"""
131+
Returns a dependant model for a handler function. A dependant model is a model that contains
132+
the parameters and return value of a handler function.
133+
134+
Parameters
135+
----------
136+
path: str
137+
The path template
138+
call: Callable[..., Any]
139+
The handler function
140+
name: str, optional
141+
The name of the handler function
142+
143+
Returns
144+
-------
145+
Dependant
146+
The dependant model for the handler function
147+
"""
70148
path_param_names = get_path_param_names(path)
71149
endpoint_signature = get_typed_signature(call)
72150
signature_params = endpoint_signature.parameters
151+
73152
dependant = Dependant(
74153
call=call,
75154
name=name,
76155
path=path,
77156
)
78157

79158
for param_name, param in signature_params.items():
159+
# If the parameter is a path parameter, we need to set the in_ field to "path".
80160
is_path_param = param_name in path_param_names
161+
162+
# Analyze the parameter to get the type annotation and the Pydantic field.
81163
type_annotation, param_field = analyze_param(
82164
param_name=param_name,
83165
annotation=param.annotation,
@@ -88,6 +170,7 @@ def get_dependant(
88170

89171
add_param_to_fields(field=param_field, dependant=dependant)
90172

173+
# If the return annotation is not empty, add it to the dependant model.
91174
return_annotation = endpoint_signature.return_annotation
92175
if return_annotation is not inspect.Signature.empty:
93176
type_annotation, param_field = analyze_param(

aws_lambda_powertools/event_handler/openapi/models.py

+28
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@
77

88
PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.")
99

10+
"""
11+
The code defines Pydantic models for the various OpenAPI objects like OpenAPI, PathItem, Operation, Parameter etc.
12+
These models can be used to parse OpenAPI JSON/YAML files into Python objects, or generate OpenAPI from Python data.
13+
"""
1014

15+
16+
# https://swagger.io/specification/#contact-object
1117
class Contact(BaseModel):
1218
name: Optional[str] = None
1319
url: Optional[AnyUrl] = None
@@ -21,6 +27,7 @@ class Config:
2127
extra = "allow"
2228

2329

30+
# https://swagger.io/specification/#license-object
2431
class License(BaseModel):
2532
name: str
2633
identifier: Optional[str] = None
@@ -35,6 +42,7 @@ class Config:
3542
extra = "allow"
3643

3744

45+
# https://swagger.io/specification/#info-object
3846
class Info(BaseModel):
3947
title: str
4048
summary: Optional[str] = None
@@ -53,6 +61,7 @@ class Config:
5361
extra = "allow"
5462

5563

64+
# https://swagger.io/specification/#server-variable-object
5665
class ServerVariable(BaseModel):
5766
enum: Annotated[Optional[List[str]], Field(min_length=1)] = None
5867
default: str
@@ -67,6 +76,7 @@ class Config:
6776
extra = "allow"
6877

6978

79+
# https://swagger.io/specification/#server-object
7080
class Server(BaseModel):
7181
url: Union[AnyUrl, str]
7282
description: Optional[str] = None
@@ -81,15 +91,18 @@ class Config:
8191
extra = "allow"
8292

8393

94+
# https://swagger.io/specification/#reference-object
8495
class Reference(BaseModel):
8596
ref: str = Field(alias="$ref")
8697

8798

99+
# https://swagger.io/specification/#discriminator-object
88100
class Discriminator(BaseModel):
89101
propertyName: str
90102
mapping: Optional[Dict[str, str]] = None
91103

92104

105+
# https://swagger.io/specification/#xml-object
93106
class XML(BaseModel):
94107
name: Optional[str] = None
95108
namespace: Optional[str] = None
@@ -106,6 +119,7 @@ class Config:
106119
extra = "allow"
107120

108121

122+
# https://swagger.io/specification/#external-documentation-object
109123
class ExternalDocumentation(BaseModel):
110124
description: Optional[str] = None
111125
url: AnyUrl
@@ -119,6 +133,7 @@ class Config:
119133
extra = "allow"
120134

121135

136+
# https://swagger.io/specification/#schema-object
122137
class Schema(BaseModel):
123138
# Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu
124139
# Core Vocabulary
@@ -212,6 +227,7 @@ class Config:
212227
SchemaOrBool = Union[Schema, bool]
213228

214229

230+
# https://swagger.io/specification/#example-object
215231
class Example(BaseModel):
216232
summary: Optional[str] = None
217233
description: Optional[str] = None
@@ -234,6 +250,7 @@ class ParameterInType(Enum):
234250
cookie = "cookie"
235251

236252

253+
# https://swagger.io/specification/#encoding-object
237254
class Encoding(BaseModel):
238255
contentType: Optional[str] = None
239256
headers: Optional[Dict[str, Union["Header", Reference]]] = None
@@ -250,6 +267,7 @@ class Config:
250267
extra = "allow"
251268

252269

270+
# https://swagger.io/specification/#media-type-object
253271
class MediaType(BaseModel):
254272
schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
255273
example: Optional[Any] = None
@@ -265,6 +283,7 @@ class Config:
265283
extra = "allow"
266284

267285

286+
# https://swagger.io/specification/#parameter-object
268287
class ParameterBase(BaseModel):
269288
description: Optional[str] = None
270289
required: Optional[bool] = None
@@ -297,6 +316,7 @@ class Header(ParameterBase):
297316
pass
298317

299318

319+
# https://swagger.io/specification/#request-body-object
300320
class RequestBody(BaseModel):
301321
description: Optional[str] = None
302322
content: Dict[str, MediaType]
@@ -311,6 +331,7 @@ class Config:
311331
extra = "allow"
312332

313333

334+
# https://swagger.io/specification/#link-object
314335
class Link(BaseModel):
315336
operationRef: Optional[str] = None
316337
operationId: Optional[str] = None
@@ -328,6 +349,7 @@ class Config:
328349
extra = "allow"
329350

330351

352+
# https://swagger.io/specification/#response-object
331353
class Response(BaseModel):
332354
description: str
333355
headers: Optional[Dict[str, Union[Header, Reference]]] = None
@@ -343,6 +365,7 @@ class Config:
343365
extra = "allow"
344366

345367

368+
# https://swagger.io/specification/#operation-object
346369
class Operation(BaseModel):
347370
tags: Optional[List[str]] = None
348371
summary: Optional[str] = None
@@ -367,6 +390,7 @@ class Config:
367390
extra = "allow"
368391

369392

393+
# https://swagger.io/specification/#path-item-object
370394
class PathItem(BaseModel):
371395
ref: Optional[str] = Field(default=None, alias="$ref")
372396
summary: Optional[str] = None
@@ -391,6 +415,7 @@ class Config:
391415
extra = "allow"
392416

393417

418+
# https://swagger.io/specification/#security-scheme-object
394419
class SecuritySchemeType(Enum):
395420
apiKey = "apiKey"
396421
http = "http"
@@ -494,6 +519,7 @@ class OpenIdConnect(SecurityBase):
494519
SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer]
495520

496521

522+
# https://swagger.io/specification/#components-object
497523
class Components(BaseModel):
498524
schemas: Optional[Dict[str, Union[Schema, Reference]]] = None
499525
responses: Optional[Dict[str, Union[Response, Reference]]] = None
@@ -516,6 +542,7 @@ class Config:
516542
extra = "allow"
517543

518544

545+
# https://swagger.io/specification/#tag-object
519546
class Tag(BaseModel):
520547
name: str
521548
description: Optional[str] = None
@@ -530,6 +557,7 @@ class Config:
530557
extra = "allow"
531558

532559

560+
# https://swagger.io/specification/#openapi-object
533561
class OpenAPI(BaseModel):
534562
openapi: str
535563
info: Info

0 commit comments

Comments
 (0)