python-openapi 0.1.10__py3-none-any.whl → 0.3.0__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.
pyopenapi/operations.py CHANGED
@@ -1,17 +1,26 @@
1
+ """
2
+ Generate an OpenAPI specification from a Python class definition
3
+
4
+ Copyright 2021-2026, Levente Hunyadi
5
+
6
+ :see: https://github.com/hunyadi/pyopenapi
7
+ """
8
+
1
9
  import collections.abc
2
10
  import enum
3
11
  import inspect
4
12
  import typing
5
13
  import uuid
6
14
  from dataclasses import dataclass
7
- from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union
15
+ from types import NoneType
16
+ from typing import Any, Callable, Iterable, Iterator
8
17
 
9
18
  from strong_typing.inspection import get_signature, is_type_enum, is_type_optional, unwrap_optional_type
10
19
 
11
20
  from .metadata import WebMethod
12
21
 
13
22
 
14
- def split_prefix(s: str, sep: str, prefix: Union[str, Iterable[str]]) -> Tuple[Optional[str], str]:
23
+ def split_prefix(s: str, sep: str, prefix: str | Iterable[str]) -> tuple[str | None, str]:
15
24
  """
16
25
  Recognizes a prefix at the beginning of a string.
17
26
 
@@ -34,11 +43,11 @@ def split_prefix(s: str, sep: str, prefix: Union[str, Iterable[str]]) -> Tuple[O
34
43
  return None, s
35
44
 
36
45
 
37
- def _get_annotation_type(annotation: Union[type, str], callable: Callable) -> type:
38
- "Maps a stringized reference to a type, as if using `from __future__ import annotations`."
46
+ def _get_annotation_type(annotation: type[Any] | str, callable: Callable[..., Any]) -> type[Any]:
47
+ "Maps a string (forward) reference to a type, as if using `from __future__ import annotations`."
39
48
 
40
49
  if isinstance(annotation, str):
41
- return eval(annotation, callable.__globals__)
50
+ return typing.cast(type[Any], eval(annotation, callable.__globals__))
42
51
  else:
43
52
  return annotation
44
53
 
@@ -54,7 +63,7 @@ class HTTPMethod(enum.Enum):
54
63
  PATCH = "PATCH"
55
64
 
56
65
 
57
- OperationParameter = Tuple[str, type]
66
+ OperationParameter = tuple[str, type[Any]]
58
67
 
59
68
 
60
69
  class ValidationError(TypeError):
@@ -66,7 +75,7 @@ class EndpointOperation:
66
75
  """
67
76
  Type information and metadata associated with an endpoint operation.
68
77
 
69
- "param defining_class: The most specific class that defines the endpoint operation.
78
+ :param defining_class: The most specific class that defines the endpoint operation.
70
79
  :param name: The short name of the endpoint operation.
71
80
  :param func_name: The name of the function to invoke when the operation is triggered.
72
81
  :param func_ref: The callable to invoke when the operation is triggered.
@@ -78,24 +87,26 @@ class EndpointOperation:
78
87
  :param response_type: The Python type of the data that is transmitted in the response body.
79
88
  :param http_method: The HTTP method used to invoke the endpoint such as POST, GET or PUT.
80
89
  :param public: True if the operation can be invoked without prior authentication.
90
+ :param deprecated: True if consumers should refrain from using the operation.
81
91
  :param request_examples: Sample requests that the operation might take.
82
92
  :param response_examples: Sample responses that the operation might produce.
83
93
  """
84
94
 
85
- defining_class: type
95
+ defining_class: type[Any]
86
96
  name: str
87
97
  func_name: str
88
98
  func_ref: Callable[..., Any]
89
- route: Optional[str]
90
- path_params: List[OperationParameter]
91
- query_params: List[OperationParameter]
92
- request_param: Optional[OperationParameter]
93
- event_type: Optional[type]
94
- response_type: type
99
+ route: str | None
100
+ path_params: list[OperationParameter]
101
+ query_params: list[OperationParameter]
102
+ request_param: OperationParameter | None
103
+ event_type: type[Any] | None
104
+ response_type: type[Any]
95
105
  http_method: HTTPMethod
96
106
  public: bool
97
- request_examples: Optional[List[Any]] = None
98
- response_examples: Optional[List[Any]] = None
107
+ deprecated: bool
108
+ request_examples: list[Any] | None = None
109
+ response_examples: list[Any] | None = None
99
110
 
100
111
  def get_route(self) -> str:
101
112
  if self.route is not None:
@@ -108,9 +119,9 @@ class EndpointOperation:
108
119
 
109
120
 
110
121
  class _FormatParameterExtractor:
111
- "A visitor to exract parameters in a format string."
122
+ "A visitor to extract parameters in a format string."
112
123
 
113
- keys: List[str]
124
+ keys: list[str]
114
125
 
115
126
  def __init__(self) -> None:
116
127
  self.keys = []
@@ -120,13 +131,13 @@ class _FormatParameterExtractor:
120
131
  return None
121
132
 
122
133
 
123
- def _get_route_parameters(route: str) -> List[str]:
134
+ def _get_route_parameters(route: str) -> list[str]:
124
135
  extractor = _FormatParameterExtractor()
125
136
  route.format_map(extractor)
126
137
  return extractor.keys
127
138
 
128
139
 
129
- def _get_endpoint_functions(endpoint: type, prefixes: List[str]) -> Iterator[Tuple[str, str, str, Callable]]:
140
+ def _get_endpoint_functions(endpoint: type[Any], prefixes: list[str]) -> Iterator[tuple[str, str, str, Callable[..., Any]]]:
130
141
  if not inspect.isclass(endpoint):
131
142
  raise ValidationError(f"object is not a class type: {endpoint}")
132
143
 
@@ -139,7 +150,7 @@ def _get_endpoint_functions(endpoint: type, prefixes: List[str]) -> Iterator[Tup
139
150
  yield prefix, operation_name, func_name, func_ref
140
151
 
141
152
 
142
- def _get_defining_class(member_fn: str, derived_cls: type) -> type:
153
+ def _get_defining_class(member_fn: str, derived_cls: type[Any]) -> type[Any]:
143
154
  "Find the class in which a member function is first defined in a class inheritance hierarchy."
144
155
 
145
156
  # iterate in reverse member resolution order to find most specific class first
@@ -151,7 +162,7 @@ def _get_defining_class(member_fn: str, derived_cls: type) -> type:
151
162
  raise ValidationError(f"cannot find defining class for {member_fn} in {derived_cls}")
152
163
 
153
164
 
154
- def get_endpoint_operations(endpoint: type, use_examples: bool = True) -> List[EndpointOperation]:
165
+ def get_endpoint_operations(endpoint: type[Any], use_examples: bool = True) -> list[EndpointOperation]:
155
166
  """
156
167
  Extracts a list of member functions in a class eligible for HTTP interface binding.
157
168
 
@@ -171,7 +182,7 @@ def get_endpoint_operations(endpoint: type, use_examples: bool = True) -> List[E
171
182
  :param use_examples: Whether to return examples associated with member functions.
172
183
  """
173
184
 
174
- result = []
185
+ result: list[EndpointOperation] = []
175
186
 
176
187
  for prefix, operation_name, func_name, func_ref in _get_endpoint_functions(
177
188
  endpoint,
@@ -188,25 +199,27 @@ def get_endpoint_operations(endpoint: type, use_examples: bool = True) -> List[E
188
199
  ],
189
200
  ):
190
201
  # extract routing information from function metadata
191
- webmethod: Optional[WebMethod] = getattr(func_ref, "__webmethod__", None)
202
+ webmethod: WebMethod | None = getattr(func_ref, "__webmethod__", None)
192
203
  if webmethod is not None:
193
204
  route = webmethod.route
194
205
  route_params = _get_route_parameters(route) if route is not None else None
195
206
  public = webmethod.public
207
+ deprecated = webmethod.deprecated
196
208
  request_examples = webmethod.request_examples
197
209
  response_examples = webmethod.response_examples
198
210
  else:
199
211
  route = None
200
212
  route_params = None
201
213
  public = False
214
+ deprecated = False
202
215
  request_examples = None
203
216
  response_examples = None
204
217
 
205
218
  # inspect function signature for path and query parameters, and request/response payload type
206
219
  signature = get_signature(func_ref)
207
220
 
208
- path_params = []
209
- query_params = []
221
+ path_params: list[tuple[str, type[Any]]] = []
222
+ query_params: list[tuple[str, type[Any]]] = []
210
223
  request_param = None
211
224
 
212
225
  for param_name, parameter in signature.parameters.items():
@@ -221,7 +234,7 @@ def get_endpoint_operations(endpoint: type, use_examples: bool = True) -> List[E
221
234
  raise ValidationError(f"parameter '{param_name}' in function '{func_name}' has no type annotation")
222
235
 
223
236
  if is_type_optional(param_type):
224
- inner_type: type = unwrap_optional_type(param_type)
237
+ inner_type: type[Any] = unwrap_optional_type(param_type)
225
238
  else:
226
239
  inner_type = param_type
227
240
 
@@ -247,7 +260,8 @@ def get_endpoint_operations(endpoint: type, use_examples: bool = True) -> List[E
247
260
  if request_param is not None:
248
261
  param = (param_name, param_type)
249
262
  raise ValidationError(
250
- f"only a single composite type is permitted in a signature but multiple composite types found in function '{func_name}': {request_param} and {param}"
263
+ f"only a single composite type is permitted in a signature but multiple composite types found in function '{func_name}': "
264
+ f"{request_param} and {param}"
251
265
  )
252
266
 
253
267
  # composite types are read from body
@@ -263,7 +277,7 @@ def get_endpoint_operations(endpoint: type, use_examples: bool = True) -> List[E
263
277
  # where YieldType is the event type, SendType is None, and ReturnType is the immediate response type to the request
264
278
  if typing.get_origin(return_type) is collections.abc.Generator:
265
279
  event_type, send_type, response_type = typing.get_args(return_type)
266
- if send_type is not type(None):
280
+ if send_type is not NoneType:
267
281
  raise ValidationError(
268
282
  f"function '{func_name}' has a return type Generator[Y,S,R] and therefore looks like an event but has an explicit send type"
269
283
  )
@@ -299,6 +313,7 @@ def get_endpoint_operations(endpoint: type, use_examples: bool = True) -> List[E
299
313
  response_type=response_type,
300
314
  http_method=http_method,
301
315
  public=public,
316
+ deprecated=deprecated,
302
317
  request_examples=request_examples if use_examples else None,
303
318
  response_examples=response_examples if use_examples else None,
304
319
  )
@@ -310,8 +325,8 @@ def get_endpoint_operations(endpoint: type, use_examples: bool = True) -> List[E
310
325
  return result
311
326
 
312
327
 
313
- def get_endpoint_events(endpoint: type) -> Dict[str, type]:
314
- results = {}
328
+ def get_endpoint_events(endpoint: type[Any]) -> dict[str, type[Any]]:
329
+ results: dict[str, type[Any]] = {}
315
330
 
316
331
  for decl in typing.get_type_hints(endpoint).values():
317
332
  # check if signature is Callable[...]
@@ -324,11 +339,11 @@ def get_endpoint_events(endpoint: type) -> Dict[str, type]:
324
339
  if len(args) != 2:
325
340
  continue
326
341
  params_type, return_type = args
327
- if not isinstance(params_type, list):
342
+ if typing.get_origin(params_type) is not list:
328
343
  continue
329
344
 
330
345
  # check if signature is Callable[[...], None]
331
- if not issubclass(return_type, type(None)):
346
+ if not issubclass(return_type, NoneType):
332
347
  continue
333
348
 
334
349
  # check if signature is Callable[[EventType], None]
pyopenapi/options.py CHANGED
@@ -1,15 +1,22 @@
1
+ """
2
+ Generate an OpenAPI specification from a Python class definition
3
+
4
+ Copyright 2021-2026, Levente Hunyadi
5
+
6
+ :see: https://github.com/hunyadi/pyopenapi
7
+ """
8
+
1
9
  import dataclasses
2
10
  from dataclasses import dataclass
3
11
  from http import HTTPStatus
4
- from typing import Callable, ClassVar, Dict, List, Optional, Tuple, Union
12
+ from typing import Any, Callable, ClassVar
5
13
 
6
- from .specification import Info, SecurityScheme
14
+ from .specification import Info, SecurityScheme, Server
7
15
  from .specification import SecuritySchemeAPI as SecuritySchemeAPI
8
16
  from .specification import SecuritySchemeHTTP as SecuritySchemeHTTP
9
17
  from .specification import SecuritySchemeOpenIDConnect as SecuritySchemeOpenIDConnect
10
- from .specification import Server
11
18
 
12
- HTTPStatusCode = Union[HTTPStatus, int, str]
19
+ HTTPStatusCode = HTTPStatus | int | str
13
20
 
14
21
 
15
22
  @dataclass
@@ -30,17 +37,17 @@ class Options:
30
37
 
31
38
  server: Server
32
39
  info: Info
33
- version: Tuple[int, int, int] = (3, 1, 0)
34
- default_security_scheme: Optional[SecurityScheme] = None
35
- extra_types: Union[List[type], Dict[str, List[type]], None] = None
40
+ version: tuple[int, int, int] = (3, 1, 0)
41
+ default_security_scheme: SecurityScheme | None = None
42
+ extra_types: list[type[Any]] | dict[str, list[type[Any]]] | None = None
36
43
  use_examples: bool = True
37
- success_responses: Dict[type, HTTPStatusCode] = dataclasses.field(default_factory=dict)
38
- error_responses: Dict[type, HTTPStatusCode] = dataclasses.field(default_factory=dict)
44
+ success_responses: dict[type[Any], HTTPStatusCode] = dataclasses.field(default_factory=dict[type[Any], HTTPStatusCode])
45
+ error_responses: dict[type[Any], HTTPStatusCode] = dataclasses.field(default_factory=dict[type[Any], HTTPStatusCode])
39
46
  error_wrapper: bool = False
40
- property_description_fun: Optional[Callable[[type, str, str], str]] = None
41
- captions: Optional[Dict[str, str]] = None
47
+ property_description_fun: Callable[[type, str, str], str] | None = None
48
+ captions: dict[str, str] | None = None
42
49
 
43
- default_captions: ClassVar[Dict[str, str]] = {
50
+ default_captions: ClassVar[dict[str, str]] = {
44
51
  "Operations": "Operations",
45
52
  "Types": "Types",
46
53
  "Events": "Events",
pyopenapi/proxy.py CHANGED
@@ -1,19 +1,29 @@
1
+ """
2
+ Generate an OpenAPI specification from a Python class definition
3
+
4
+ Copyright 2021-2026, Levente Hunyadi
5
+
6
+ :see: https://github.com/hunyadi/pyopenapi
7
+ """
8
+
1
9
  import json
2
- from typing import Any, Callable, Dict, Optional, Tuple, Type, TypeVar
10
+ import typing
11
+ from typing import Any, Callable, TypeVar
3
12
 
4
13
  import aiohttp
14
+ from strong_typing.inspection import get_signature
5
15
  from strong_typing.serialization import json_to_object, object_to_json
6
16
 
7
- from .operations import EndpointOperation, HTTPMethod, get_endpoint_operations, get_signature
17
+ from .operations import EndpointOperation, HTTPMethod, get_endpoint_operations
8
18
 
9
19
 
10
20
  async def make_request(
11
21
  http_method: HTTPMethod,
12
22
  server: str,
13
23
  path: str,
14
- query: Dict[str, str],
15
- data: Optional[str],
16
- ) -> Tuple[int, str]:
24
+ query: dict[str, str],
25
+ data: str | None,
26
+ ) -> tuple[int, str]:
17
27
  "Makes an asynchronous HTTP request and returns the response."
18
28
 
19
29
  headers = {"Accept": "application/json"}
@@ -90,14 +100,14 @@ class OperationProxy:
90
100
  data = None
91
101
 
92
102
  # make HTTP request
93
- status, response = await make_request(self.op.http_method, endpoint_proxy.base_url, path, query, data)
103
+ _status, response = await make_request(self.op.http_method, endpoint_proxy.base_url, path, query, data)
94
104
 
95
105
  # process HTTP response
96
106
  if response:
97
107
  try:
98
108
  s = json.loads(response)
99
109
  except json.JSONDecodeError:
100
- raise ProxyInvokeError(f"response body is not well-formed JSON:\n{response}")
110
+ raise ProxyInvokeError(f"response body is not well-formed JSON:\n{response}") from None
101
111
 
102
112
  return json_to_object(self.op.response_type, s)
103
113
  else:
@@ -118,7 +128,7 @@ def _get_operation_proxy(op: EndpointOperation) -> Callable[..., Any]:
118
128
  T = TypeVar("T")
119
129
 
120
130
 
121
- def make_proxy_class(api: Type[T]) -> Type[T]:
131
+ def make_proxy_class(api: type[T]) -> type[T]:
122
132
  """
123
133
  Creates a proxy class for calling an HTTP REST API.
124
134
 
@@ -127,5 +137,5 @@ def make_proxy_class(api: Type[T]) -> Type[T]:
127
137
 
128
138
  ops = get_endpoint_operations(api)
129
139
  properties = {op.func_name: _get_operation_proxy(op) for op in ops}
130
- proxy = type(f"{api.__name__}Proxy", (api, EndpointProxy), properties)
131
- return proxy
140
+ proxy = type(f"{api.__name__}Proxy", (api, EndpointProxy), properties) # pyright: ignore[reportGeneralTypeIssues]
141
+ return typing.cast(type[T], proxy)
@@ -1,10 +1,18 @@
1
+ """
2
+ Generate an OpenAPI specification from a Python class definition
3
+
4
+ Copyright 2021-2026, Levente Hunyadi
5
+
6
+ :see: https://github.com/hunyadi/pyopenapi
7
+ """
8
+
1
9
  import dataclasses
2
10
  import enum
3
11
  from dataclasses import dataclass
4
- from typing import Any, ClassVar, Dict, List, Optional, Union
12
+ from typing import Any, ClassVar
5
13
 
6
- from strong_typing.schema import JsonType as JsonType
7
- from strong_typing.schema import Schema, StrictJsonType
14
+ from strong_typing.core import JsonType as JsonType
15
+ from strong_typing.core import Schema, StrictJsonType
8
16
 
9
17
  URL = str
10
18
 
@@ -23,9 +31,6 @@ class SchemaRef(Ref):
23
31
  ref_type: ClassVar[str] = "schemas"
24
32
 
25
33
 
26
- SchemaOrRef = Union[Schema, SchemaRef]
27
-
28
-
29
34
  @dataclass
30
35
  class ResponseRef(Ref):
31
36
  ref_type: ClassVar[str] = "responses"
@@ -43,45 +48,45 @@ class ExampleRef(Ref):
43
48
 
44
49
  @dataclass
45
50
  class Contact:
46
- name: Optional[str] = None
47
- url: Optional[URL] = None
48
- email: Optional[str] = None
51
+ name: str | None = None
52
+ url: URL | None = None
53
+ email: str | None = None
49
54
 
50
55
 
51
56
  @dataclass
52
57
  class License:
53
58
  name: str
54
- url: Optional[URL] = None
59
+ url: URL | None = None
55
60
 
56
61
 
57
62
  @dataclass
58
63
  class Info:
59
64
  title: str
60
65
  version: str
61
- description: Optional[str] = None
62
- termsOfService: Optional[str] = None
63
- contact: Optional[Contact] = None
64
- license: Optional[License] = None
66
+ description: str | None = None
67
+ termsOfService: str | None = None
68
+ contact: Contact | None = None
69
+ license: License | None = None
65
70
 
66
71
 
67
72
  @dataclass
68
73
  class MediaType:
69
- schema: Optional[SchemaOrRef] = None
70
- example: Optional[Any] = None
71
- examples: Optional[Dict[str, Union["Example", ExampleRef]]] = None
74
+ schema: Schema | SchemaRef | None = None
75
+ example: Any | None = None
76
+ examples: dict[str, "Example | ExampleRef"] | None = None
72
77
 
73
78
 
74
79
  @dataclass
75
80
  class RequestBody:
76
- content: Dict[str, MediaType]
77
- description: Optional[str] = None
78
- required: Optional[bool] = None
81
+ content: dict[str, MediaType]
82
+ description: str | None = None
83
+ required: bool | None = None
79
84
 
80
85
 
81
86
  @dataclass
82
87
  class Response:
83
88
  description: str
84
- content: Optional[Dict[str, MediaType]] = None
89
+ content: dict[str, MediaType] | None = None
85
90
 
86
91
 
87
92
  @enum.unique
@@ -96,37 +101,38 @@ class ParameterLocation(enum.Enum):
96
101
  class Parameter:
97
102
  name: str
98
103
  in_: ParameterLocation
99
- description: Optional[str] = None
100
- required: Optional[bool] = None
101
- schema: Optional[SchemaOrRef] = None
102
- example: Optional[Any] = None
104
+ description: str | None = None
105
+ required: bool | None = None
106
+ schema: Schema | SchemaRef | None = None
107
+ example: Any | None = None
103
108
 
104
109
 
105
110
  @dataclass
106
111
  class Operation:
107
- responses: Dict[str, Union[Response, ResponseRef]]
108
- tags: Optional[List[str]] = None
109
- summary: Optional[str] = None
110
- description: Optional[str] = None
111
- operationId: Optional[str] = None
112
- parameters: Optional[List[Parameter]] = None
113
- requestBody: Optional[RequestBody] = None
114
- callbacks: Optional[Dict[str, "Callback"]] = None
115
- security: Optional[List["SecurityRequirement"]] = None
112
+ responses: dict[str, Response | ResponseRef]
113
+ tags: list[str] | None = None
114
+ summary: str | None = None
115
+ description: str | None = None
116
+ operationId: str | None = None
117
+ parameters: list[Parameter] | None = None
118
+ requestBody: RequestBody | None = None
119
+ callbacks: dict[str, "Callback"] | None = None
120
+ security: list["SecurityRequirement"] | None = None
121
+ deprecated: bool | None = None
116
122
 
117
123
 
118
124
  @dataclass
119
125
  class PathItem:
120
- summary: Optional[str] = None
121
- description: Optional[str] = None
122
- get: Optional[Operation] = None
123
- put: Optional[Operation] = None
124
- post: Optional[Operation] = None
125
- delete: Optional[Operation] = None
126
- options: Optional[Operation] = None
127
- head: Optional[Operation] = None
128
- patch: Optional[Operation] = None
129
- trace: Optional[Operation] = None
126
+ summary: str | None = None
127
+ description: str | None = None
128
+ get: Operation | None = None
129
+ put: Operation | None = None
130
+ post: Operation | None = None
131
+ delete: Operation | None = None
132
+ options: Operation | None = None
133
+ head: Operation | None = None
134
+ patch: Operation | None = None
135
+ trace: Operation | None = None
130
136
 
131
137
  def update(self, other: "PathItem") -> None:
132
138
  "Merges another instance of this class into this object."
@@ -138,21 +144,21 @@ class PathItem:
138
144
 
139
145
 
140
146
  # maps run-time expressions such as "$request.body#/url" to path items
141
- Callback = Dict[str, PathItem]
147
+ Callback = dict[str, PathItem]
142
148
 
143
149
 
144
150
  @dataclass
145
151
  class Example:
146
- summary: Optional[str] = None
147
- description: Optional[str] = None
148
- value: Optional[Any] = None
149
- externalValue: Optional[URL] = None
152
+ summary: str | None = None
153
+ description: str | None = None
154
+ value: Any | None = None
155
+ externalValue: URL | None = None
150
156
 
151
157
 
152
158
  @dataclass
153
159
  class Server:
154
160
  url: URL
155
- description: Optional[str] = None
161
+ description: str | None = None
156
162
 
157
163
 
158
164
  @enum.unique
@@ -183,9 +189,9 @@ class SecuritySchemeAPI(SecurityScheme):
183
189
  @dataclass(init=False)
184
190
  class SecuritySchemeHTTP(SecurityScheme):
185
191
  scheme: str
186
- bearerFormat: Optional[str] = None
192
+ bearerFormat: str | None = None
187
193
 
188
- def __init__(self, description: str, scheme: str, bearerFormat: Optional[str] = None) -> None:
194
+ def __init__(self, description: str, scheme: str, bearerFormat: str | None = None) -> None:
189
195
  super().__init__(SecuritySchemeType.HTTP, description)
190
196
  self.scheme = scheme
191
197
  self.bearerFormat = bearerFormat
@@ -202,24 +208,24 @@ class SecuritySchemeOpenIDConnect(SecurityScheme):
202
208
 
203
209
  @dataclass
204
210
  class Components:
205
- schemas: Optional[Dict[str, Schema]] = None
206
- responses: Optional[Dict[str, Response]] = None
207
- parameters: Optional[Dict[str, Parameter]] = None
208
- examples: Optional[Dict[str, Example]] = None
209
- requestBodies: Optional[Dict[str, RequestBody]] = None
210
- securitySchemes: Optional[Dict[str, SecurityScheme]] = None
211
- callbacks: Optional[Dict[str, Callback]] = None
211
+ schemas: dict[str, Schema] | None = None
212
+ responses: dict[str, Response] | None = None
213
+ parameters: dict[str, Parameter] | None = None
214
+ examples: dict[str, Example] | None = None
215
+ requestBodies: dict[str, RequestBody] | None = None
216
+ securitySchemes: dict[str, SecurityScheme] | None = None
217
+ callbacks: dict[str, Callback] | None = None
212
218
 
213
219
 
214
220
  SecurityScope = str
215
- SecurityRequirement = Dict[str, List[SecurityScope]]
221
+ SecurityRequirement = dict[str, list[SecurityScope]]
216
222
 
217
223
 
218
224
  @dataclass
219
225
  class Tag:
220
226
  name: str
221
- description: Optional[str] = None
222
- displayName: Optional[str] = None
227
+ description: str | None = None
228
+ displayName: str | None = None
223
229
 
224
230
 
225
231
  @dataclass
@@ -228,10 +234,13 @@ class TagGroup:
228
234
  A ReDoc extension to provide information about groups of tags.
229
235
 
230
236
  Exposed via the vendor-specific property "x-tagGroups" of the top-level object.
237
+
238
+ :param name: Group name.
239
+ :param tags: Tags that are members of this group.
231
240
  """
232
241
 
233
242
  name: str
234
- tags: List[str]
243
+ tags: list[str]
235
244
 
236
245
 
237
246
  @dataclass
@@ -240,14 +249,24 @@ class Document:
240
249
  This class is a Python dataclass adaptation of the OpenAPI Specification.
241
250
 
242
251
  For details, see <https://swagger.io/specification/>
252
+
253
+ :param openapi: Version number of the OpenAPI Specification that the OpenAPI document uses.
254
+ :param info: Provides metadata about the API.
255
+ :param servers: An array of objects that provide connectivity information to a target server.
256
+ :param paths: The available paths and operations for the API.
257
+ :param jsonSchemaDialect: The default value for the `$schema` keyword within schema objects in this document, in the form of a URI.
258
+ :param components: An element to hold various objects for the OpenAPI description.
259
+ :param security: A declaration of which security mechanisms can be used across the API.
260
+ :param tags: A list of tags used by the OpenAPI description with additional metadata.
261
+ :param tagGroups: Provides information about a group of tags. (Extension to OpenAPI.)
243
262
  """
244
263
 
245
264
  openapi: str
246
265
  info: Info
247
- servers: List[Server]
248
- paths: Dict[str, PathItem]
249
- jsonSchemaDialect: Optional[str] = None
250
- components: Optional[Components] = None
251
- security: Optional[List[SecurityRequirement]] = None
252
- tags: Optional[List[Tag]] = None
253
- tagGroups: Optional[List[TagGroup]] = None
266
+ servers: list[Server]
267
+ paths: dict[str, PathItem]
268
+ jsonSchemaDialect: str | None = None
269
+ components: Components | None = None
270
+ security: list[SecurityRequirement] | None = None
271
+ tags: list[Tag] | None = None
272
+ tagGroups: list[TagGroup] | None = None
pyopenapi/utility.py CHANGED
@@ -1,10 +1,18 @@
1
+ """
2
+ Generate an OpenAPI specification from a Python class definition
3
+
4
+ Copyright 2021-2026, Levente Hunyadi
5
+
6
+ :see: https://github.com/hunyadi/pyopenapi
7
+ """
8
+
1
9
  import importlib.resources
2
10
  import json
3
- import sys
4
11
  import typing
5
12
  from typing import TextIO
6
13
 
7
- from strong_typing.schema import StrictJsonType, object_to_json
14
+ from strong_typing.core import StrictJsonType
15
+ from strong_typing.serialization import object_to_json
8
16
 
9
17
  from .generator import Generator
10
18
  from .options import Options
@@ -94,12 +102,8 @@ class Specification:
94
102
  :param pretty_print: Whether to use line indents to beautify the JSON string in the HTML file.
95
103
  """
96
104
 
97
- if sys.version_info >= (3, 9):
98
- with importlib.resources.files(__package__).joinpath("template.html").open(encoding="utf-8", errors="strict") as html_template_file:
99
- html_template = html_template_file.read()
100
- else:
101
- with importlib.resources.open_text(__package__, "template.html", encoding="utf-8", errors="strict") as html_template_file:
102
- html_template = html_template_file.read()
105
+ with importlib.resources.files(__package__).joinpath("template.html").open(encoding="utf-8", errors="strict") as html_template_file:
106
+ html_template = html_template_file.read()
103
107
 
104
108
  html = html_template.replace(
105
109
  "{ /* OPENAPI_SPECIFICATION */ }",