Skip to content

Commit 155b5fb

Browse files
committed
Improve overall typing
1 parent fa5d0df commit 155b5fb

File tree

10 files changed

+508
-494
lines changed

10 files changed

+508
-494
lines changed

openapidocs/common.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import base64
22
import copy
33
from abc import ABC, abstractmethod
4-
from dataclasses import asdict, fields, is_dataclass
4+
from dataclasses import asdict, dataclass, fields, is_dataclass
55
from datetime import date, datetime, time
66
from enum import Enum
7-
from typing import Any, List, Tuple
7+
from typing import Any, Callable, Iterable, cast
88
from uuid import UUID
99

1010
import yaml
@@ -18,10 +18,12 @@ class Format(Enum):
1818
JSON = "JSON"
1919

2020

21+
@dataclass
2122
class OpenAPIElement:
2223
"""Base class for all OpenAPI Elements"""
2324

2425

26+
@dataclass
2527
class OpenAPIRoot(OpenAPIElement):
2628
"""Base class for a root OpenAPI Documentation"""
2729

@@ -67,8 +69,8 @@ def normalize_key(key: Any) -> str:
6769
return "".join([first.lower(), *map(str.title, others)])
6870

6971

70-
def normalize_dict_factory(items: List[Tuple[Any, Any]]) -> Any:
71-
data = {}
72+
def normalize_dict_factory(items: list[tuple[Any, Any]]) -> dict[str, Any]:
73+
data: dict[str, Any] = {}
7274
for key, value in items:
7375
if value is None:
7476
continue
@@ -87,8 +89,8 @@ def normalize_dict_factory(items: List[Tuple[Any, Any]]) -> Any:
8789
return data
8890

8991

90-
def regular_dict_factory(items: List[Tuple[Any, Any]]) -> Any:
91-
data = {}
92+
def regular_dict_factory(items: list[tuple[Any, Any]]) -> dict[Any, Any]:
93+
data: dict[Any, Any] = {}
9294
for key, value in items:
9395
for handler in TYPES_HANDLERS:
9496
value = handler.normalize(value)
@@ -100,11 +102,11 @@ def regular_dict_factory(items: List[Tuple[Any, Any]]) -> Any:
100102
# replicates the asdict method from dataclasses module, to support
101103
# bypassing "asdict" on child properties when they implement a `to_obj`
102104
# method: some entities require a specific shape when represented
103-
def _asdict_inner(obj, dict_factory):
105+
def _asdict_inner(obj: Any, dict_factory: Callable[[Any], Any]) -> Any:
104106
if hasattr(obj, "to_obj"):
105107
return obj.to_obj()
106108
if isinstance(obj, OpenAPIElement):
107-
result = []
109+
result: list[tuple[str, Any]] = []
108110
for f in fields(obj):
109111
value = _asdict_inner(getattr(obj, f.name), dict_factory)
110112
result.append((f.name, value))
@@ -115,11 +117,13 @@ def _asdict_inner(obj, dict_factory):
115117
if hasattr(obj, "dict") and callable(obj.dict):
116118
# For Pydantic 1
117119
return obj.dict()
118-
if is_dataclass(obj):
120+
if is_dataclass(obj) and not isinstance(obj, type):
119121
return asdict(obj, dict_factory=regular_dict_factory)
120122
elif isinstance(obj, (list, tuple)):
123+
obj = cast(Iterable[Any], obj)
121124
return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
122125
elif isinstance(obj, dict):
126+
obj = cast(dict[Any, Any], obj)
123127
return type(obj)(
124128
(_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory))
125129
for k, v in obj.items()
@@ -128,7 +132,7 @@ def _asdict_inner(obj, dict_factory):
128132
return copy.deepcopy(obj)
129133

130134

131-
def normalize_dict(obj):
135+
def normalize_dict(obj: Any) -> Any:
132136
if hasattr(obj, "dict") and callable(obj.dict):
133137
return obj.dict()
134138
if hasattr(obj, "to_obj"):

openapidocs/logs.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import logging
2-
import logging.handlers
32

43
from rich.logging import RichHandler
54

openapidocs/mk/common.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66

77
from abc import ABC, abstractmethod
8+
from typing import Any, cast
89

910

1011
class DocumentsWriter(ABC):
@@ -14,13 +15,13 @@ class DocumentsWriter(ABC):
1415
"""
1516

1617
@abstractmethod
17-
def write(self, data, **kwargs) -> str:
18+
def write(self, data: object, **kwargs: dict[str, Any]) -> str:
1819
"""
1920
Writes markdown.
2021
"""
2122

2223

23-
def is_reference(data) -> bool:
24+
def is_reference(data: object) -> bool:
2425
"""
2526
Returns a value indicating whether the given dictionary represents
2627
a reference.
@@ -32,7 +33,7 @@ def is_reference(data) -> bool:
3233
return "$ref" in data
3334

3435

35-
def is_object_schema(data) -> bool:
36+
def is_object_schema(data: object) -> bool:
3637
"""
3738
Returns a value indicating whether the given schema dictionary represents
3839
an object schema.
@@ -41,10 +42,11 @@ def is_object_schema(data) -> bool:
4142
"""
4243
if not isinstance(data, dict):
4344
return False
45+
data = cast(dict[str, object], data)
4446
return data.get("type") == "object" and isinstance(data.get("properties"), dict)
4547

4648

47-
def is_array_schema(data) -> bool:
49+
def is_array_schema(data: object) -> bool:
4850
"""
4951
Returns a value indicating whether the given schema dictionary represents
5052
an array schema.
@@ -53,10 +55,11 @@ def is_array_schema(data) -> bool:
5355
"""
5456
if not isinstance(data, dict):
5557
return False
58+
data = cast(dict[str, object], data)
5659
return data.get("type") == "array" and isinstance(data.get("items"), dict)
5760

5861

59-
def get_ref_type_name(reference) -> str:
62+
def get_ref_type_name(reference: dict[str, str] | str) -> str:
6063
"""
6164
Returns the type name of a reference.
6265

openapidocs/mk/contents.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
"""
22
This module contains classes to generate representations of content types by mime type.
33
"""
4+
45
import os
56
from abc import ABC, abstractmethod
67
from datetime import datetime
78
from json import JSONEncoder
9+
from typing import Any, Mapping, Sequence
810
from urllib.parse import urlencode
911

1012
from essentials.json import FriendlyEncoder, dumps
1113

1214

1315
class OADJSONEncoder(JSONEncoder):
14-
def default(self, obj):
16+
def default(self, o: object) -> Any:
1517
try:
16-
return JSONEncoder.default(self, obj)
18+
return JSONEncoder.default(self, o)
1719
except TypeError:
18-
if isinstance(obj, datetime):
20+
if isinstance(o, datetime):
1921
datetime_format = os.environ.get("OPENAPI_DATETIME_FORMAT")
2022
if datetime_format:
21-
return obj.strftime(datetime_format)
23+
return o.strftime(datetime_format)
2224
else:
23-
return obj.isoformat()
24-
return FriendlyEncoder.default(self, obj) # type: ignore
25+
return o.isoformat()
26+
return FriendlyEncoder.default(self, o) # type: ignore
2527

2628

2729
class ContentWriter(ABC):
@@ -37,7 +39,7 @@ def handle_content_type(self, content_type: str) -> bool:
3739
"""
3840

3941
@abstractmethod
40-
def write(self, value) -> str:
42+
def write(self, value: Any) -> str:
4143
"""
4244
Writes markdown to represent a value in a certain type of content.
4345
"""
@@ -47,7 +49,7 @@ class JSONContentWriter(ContentWriter):
4749
def handle_content_type(self, content_type: str) -> bool:
4850
return "json" in content_type.lower()
4951

50-
def write(self, value) -> str:
52+
def write(self, value: object) -> str:
5153
return dumps(value, indent=4, cls=OADJSONEncoder)
5254

5355

@@ -56,5 +58,5 @@ def handle_content_type(self, content_type: str) -> bool:
5658
# multipart/form-data. Otherwise, use application/x-www-form-urlencoded.
5759
return "x-www-form-urlencoded" == content_type.lower()
5860

59-
def write(self, value) -> str:
61+
def write(self, value: Mapping[Any, Any] | Sequence[tuple[Any, Any]]) -> str:
6062
return urlencode(value)

openapidocs/mk/generate.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
from typing import Union
2-
31
from openapidocs.mk.v3 import OpenAPIV3DocumentationHandler
42
from openapidocs.utils.source import read_from_source
53

64

7-
def generate_document(source: str, destination: str, style: Union[int, str]):
5+
def generate_document(source: str, destination: str, style: int | str):
86
# Note: if support for more kinds of OAD versions will be added, handle a version
97
# parameter in this function
108

openapidocs/mk/md.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
This module provides common functions to handle Markdown.
33
These functions apply to any kind of Markdown work.
44
"""
5-
from typing import Dict, Iterable
5+
6+
from typing import Iterable
67

78

89
def write_row(
910
row: Iterable[str],
10-
columns_widths: Dict[int, int],
11+
columns_widths: dict[int, int],
1112
padding: int = 1,
1213
indent: int = 0,
1314
) -> str:

openapidocs/utils/source.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
"""
22
This module provides methods to obtain OpenAPI Documentation from file or web sources.
33
"""
4+
45
import json
56
from pathlib import Path
7+
from typing import Any
68

79
import yaml
810

@@ -11,15 +13,15 @@
1113
from .web import ensure_success, http_get
1214

1315

14-
def read_from_json_file(file_path: Path):
16+
def read_from_json_file(file_path: Path) -> dict[Any, Any] | list[Any]:
1517
"""
1618
Reads JSON from a given file by path.
1719
"""
1820
with open(file_path, "rt", encoding="utf-8") as source_file:
1921
return json.loads(source_file.read())
2022

2123

22-
def read_from_yaml_file(file_path: Path):
24+
def read_from_yaml_file(file_path: Path) -> dict[Any, Any] | list[Any]:
2325
"""
2426
Reads YAML from a given file by path.
2527
"""
@@ -32,7 +34,7 @@ def __init__(self, message: str) -> None:
3234
super().__init__(message)
3335

3436

35-
def read_from_url(url: str):
37+
def read_from_url(url: str) -> dict[Any, Any] | list[Any]:
3638
"""
3739
Tries to read OpenAPI Documentation from the given source URL.
3840
This method will try to fetch JSON or YAML from the given source, in case of
@@ -63,7 +65,10 @@ def read_from_url(url: str):
6365
)
6466

6567

66-
def read_from_source(source: str, cwd: Path = None):
68+
def read_from_source(
69+
source: str,
70+
cwd: Path | None = None,
71+
) -> dict[Any, Any] | list[Any]:
6772
"""
6873
Tries to read a JSON or YAML file from a given source.
6974
The source can be a path to a file, or a URL.

openapidocs/utils/web.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77

88
class FailedRequestError(Exception):
9-
def __init__(self, message) -> None:
9+
def __init__(self, message: str) -> None:
1010
super().__init__(
1111
f"Failed request: {message}. "
1212
"Inspect the inner exception (__context__) for more information."

0 commit comments

Comments
 (0)