Skip to content

Commit 171f9bd

Browse files
V24.12 (#265)
* Add logging extraction to background logger * Fix coerce in Hint (#251) * Fix nullable type null value being converted * Unpin setuptools version * Update test * make format * Workflow changes * Remove python 3.8 * Workflow changes * Clean up tests * Fix type check --------- Co-authored-by: Zhiwei <[email protected]>
1 parent fd85ada commit 171f9bd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+537
-439
lines changed

.github/workflows/python-package.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-latest
1010
strategy:
1111
matrix:
12-
python-version: [3.8, 3.9, "3.10", "3.11"]
12+
python-version: [3.9, "3.10", "3.11", "3.12", "3.13"]
1313

1414
steps:
1515
- uses: actions/checkout@v2

sanic_ext/bootstrap.py

+13-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
from __future__ import annotations
22

3+
import os
4+
5+
from collections.abc import Mapping
36
from types import SimpleNamespace
4-
from typing import Any, Callable, Dict, List, Mapping, Optional, Type, Union
7+
from typing import Any, Callable, Optional, Union
58
from warnings import warn
69

710
from sanic import Sanic, __version__
@@ -40,7 +43,7 @@
4043

4144

4245
class Extend:
43-
_pre_registry: List[Union[Type[Extension], Extension]] = []
46+
_pre_registry: list[Union[type[Extension], Extension]] = []
4447

4548
if TEMPLATING_ENABLED:
4649
environment: Environment
@@ -50,9 +53,9 @@ def __init__(
5053
self,
5154
app: Sanic,
5255
*,
53-
extensions: Optional[List[Union[Type[Extension], Extension]]] = None,
56+
extensions: Optional[list[Union[type[Extension], Extension]]] = None,
5457
built_in_extensions: bool = True,
55-
config: Optional[Union[Config, Dict[str, Any]]] = None,
58+
config: Optional[Union[Config, dict[str, Any]]] = None,
5659
**kwargs,
5760
) -> None:
5861
"""
@@ -79,7 +82,7 @@ def __init__(
7982
self._constant_registry: Optional[ConstantRegistry] = None
8083
self._openapi: Optional[SpecificationBuilder] = None
8184
self.app = app
82-
self.extensions: List[Extension] = []
85+
self.extensions: list[Extension] = []
8386
self.sanic_version = sanic_version
8487
app._ext = self
8588
app.ctx._dependencies = SimpleNamespace()
@@ -114,6 +117,8 @@ def __init__(
114117
started.add(ext)
115118

116119
def _display(self):
120+
if "SANIC_WORKER_IDENTIFIER" in os.environ:
121+
return
117122
init_logs = ["Sanic Extensions:"]
118123
for extension in self.extensions:
119124
label = extension.render_label()
@@ -124,7 +129,7 @@ def _display(self):
124129

125130
def injection(
126131
self,
127-
type: Type,
132+
type: type,
128133
constructor: Optional[Callable[..., Any]] = None,
129134
) -> None:
130135
warn(
@@ -136,7 +141,7 @@ def injection(
136141

137142
def add_dependency(
138143
self,
139-
type: Type,
144+
type: type,
140145
constructor: Optional[Callable[..., Any]] = None,
141146
request_arg: Optional[str] = None,
142147
) -> None:
@@ -211,7 +216,7 @@ def template(self, template_name: str, **kwargs):
211216
return self.templating.template(template_name, **kwargs)
212217

213218
@classmethod
214-
def register(cls, extension: Union[Type[Extension], Extension]) -> None:
219+
def register(cls, extension: Union[type[Extension], Extension]) -> None:
215220
cls._pre_registry.append(extension)
216221

217222
@classmethod

sanic_ext/config.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import os
44

5-
from typing import Any, Dict, List, Optional, Sequence, Union
5+
from collections.abc import Sequence
6+
from typing import Any, Optional, Union
67

78
from sanic import Sanic
89
from sanic.config import Config as SanicConfig
@@ -23,7 +24,7 @@ def __init__(
2324
cors_expose_headers: str = "",
2425
cors_max_age: int = 5,
2526
cors_methods: str = "",
26-
cors_origins: Union[str, List[str]] = "",
27+
cors_origins: Union[str, list[str]] = "",
2728
cors_send_wildcard: bool = False,
2829
cors_supports_credentials: bool = False,
2930
cors_vary_header: bool = True,
@@ -44,7 +45,13 @@ def __init__(
4445
injection_load_custom_constants: bool = False,
4546
logging: bool = False,
4647
logging_queue_max_size: int = 4096,
47-
loggers: List[str] = ["sanic.access", "sanic.error", "sanic.root"],
48+
loggers: list[str] = [
49+
"sanic.access",
50+
"sanic.error",
51+
"sanic.root",
52+
"sanic.server",
53+
"sanic.websockets",
54+
],
4855
oas: bool = True,
4956
oas_autodoc: bool = True,
5057
oas_custom_file: Optional[os.PathLike] = None,
@@ -66,7 +73,7 @@ def __init__(
6673
oas_uri_to_redoc: str = "/redoc",
6774
oas_uri_to_swagger: str = "/swagger",
6875
oas_url_prefix: str = "/docs",
69-
swagger_ui_configuration: Optional[Dict[str, Any]] = None,
76+
swagger_ui_configuration: Optional[dict[str, Any]] = None,
7077
templating_path_to_templates: Union[
7178
str, os.PathLike, Sequence[Union[str, os.PathLike]]
7279
] = "templates",

sanic_ext/extensions/base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from abc import ABC, abstractmethod
4-
from typing import Any, Dict, Type, Union
4+
from typing import Any, Union
55

66
from sanic.app import Sanic
77
from sanic.exceptions import SanicException
@@ -18,7 +18,7 @@ def __setitem__(self, key: Any, value: Any) -> None:
1818

1919

2020
class Extension(ABC):
21-
_name_registry: Dict[str, Type[Extension]] = NoDuplicateDict()
21+
_name_registry: dict[str, type[Extension]] = NoDuplicateDict()
2222
_started: bool
2323
name: str
2424
app: Sanic
@@ -65,7 +65,7 @@ def included(self):
6565
@classmethod
6666
def create(
6767
cls,
68-
extension: Union[Type[Extension], Extension],
68+
extension: Union[type[Extension], Extension],
6969
app: Sanic,
7070
config: Config,
7171
) -> Extension:

sanic_ext/extensions/http/cors.py

+19-19
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from dataclasses import dataclass
44
from datetime import timedelta
55
from types import SimpleNamespace
6-
from typing import Any, FrozenSet, List, Optional, Tuple, Union
6+
from typing import Any, Optional, Union
77

88
from sanic import HTTPResponse, Request, Sanic
99
from sanic.exceptions import SanicException
@@ -27,12 +27,12 @@
2727

2828
@dataclass(frozen=True)
2929
class CORSSettings:
30-
allow_headers: FrozenSet[str]
31-
allow_methods: FrozenSet[str]
32-
allow_origins: Tuple[re.Pattern, ...]
30+
allow_headers: frozenset[str]
31+
allow_methods: frozenset[str]
32+
allow_origins: tuple[re.Pattern, ...]
3333
always_send: bool
3434
automatic_options: bool
35-
expose_headers: FrozenSet[str]
35+
expose_headers: frozenset[str]
3636
max_age: str
3737
send_wildcard: bool
3838
supports_credentials: bool
@@ -86,9 +86,9 @@ async def _assign_cors_settings(app, _):
8686
def cors(
8787
*,
8888
origin: Union[str, Default] = _default,
89-
expose_headers: Union[List[str], Default] = _default,
90-
allow_headers: Union[List[str], Default] = _default,
91-
allow_methods: Union[List[str], Default] = _default,
89+
expose_headers: Union[list[str], Default] = _default,
90+
allow_headers: Union[list[str], Default] = _default,
91+
allow_methods: Union[list[str], Default] = _default,
9292
supports_credentials: Union[bool, Default] = _default,
9393
max_age: Union[str, int, timedelta, Default] = _default,
9494
):
@@ -227,10 +227,10 @@ def _add_credentials_header(request: Request, response: HTTPResponse) -> None:
227227

228228
def _add_allow_header(request: Request, response: HTTPResponse) -> None:
229229
with_credentials = _is_request_with_credentials(request)
230-
request_headers = set(
230+
request_headers = {
231231
h.strip().lower()
232232
for h in request.headers.get(REQUEST_HEADERS_HEADER, "").split(",")
233-
)
233+
}
234234
allow_headers = _get_from_cors_ctx(
235235
request, "_cors_allow_headers", request.app.ctx.cors.allow_headers
236236
)
@@ -297,16 +297,16 @@ def _add_vary_header(request: Request, response: HTTPResponse) -> None:
297297
response.headers[VARY_HEADER] = "origin"
298298

299299

300-
def _get_allow_origins(app: Sanic) -> Tuple[re.Pattern, ...]:
300+
def _get_allow_origins(app: Sanic) -> tuple[re.Pattern, ...]:
301301
origins = app.config.CORS_ORIGINS
302302
return _parse_allow_origins(origins)
303303

304304

305305
def _parse_allow_origins(
306-
value: Union[str, re.Pattern, List[Union[str, re.Pattern]]],
307-
) -> Tuple[re.Pattern, ...]:
306+
value: Union[str, re.Pattern, list[Union[str, re.Pattern]]],
307+
) -> tuple[re.Pattern, ...]:
308308
origins: Optional[
309-
Union[List[str], List[re.Pattern], List[Union[str, re.Pattern]]]
309+
Union[list[str], list[re.Pattern], list[Union[str, re.Pattern]]]
310310
] = None
311311
if value and isinstance(value, str):
312312
if value == "*":
@@ -326,7 +326,7 @@ def _parse_allow_origins(
326326
)
327327

328328

329-
def _get_expose_headers(app: Sanic) -> FrozenSet[str]:
329+
def _get_expose_headers(app: Sanic) -> frozenset[str]:
330330
expose_headers = (
331331
(
332332
app.config.CORS_EXPOSE_HEADERS
@@ -341,11 +341,11 @@ def _get_expose_headers(app: Sanic) -> FrozenSet[str]:
341341
return frozenset(header.lower() for header in expose_headers)
342342

343343

344-
def _get_allow_headers(app: Sanic) -> FrozenSet[str]:
344+
def _get_allow_headers(app: Sanic) -> frozenset[str]:
345345
return _parse_allow_headers(app.config.CORS_ALLOW_HEADERS)
346346

347347

348-
def _parse_allow_headers(value: str) -> FrozenSet[str]:
348+
def _parse_allow_headers(value: str) -> frozenset[str]:
349349
allow_headers = (
350350
(
351351
value
@@ -372,11 +372,11 @@ def _parse_max_age(value) -> str:
372372
return str(max_age)
373373

374374

375-
def _get_allow_methods(app: Sanic) -> FrozenSet[str]:
375+
def _get_allow_methods(app: Sanic) -> frozenset[str]:
376376
return _parse_allow_methods(app.config.CORS_METHODS)
377377

378378

379-
def _parse_allow_methods(value) -> FrozenSet[str]:
379+
def _parse_allow_methods(value) -> frozenset[str]:
380380
allow_methods = (
381381
(
382382
value

sanic_ext/extensions/http/methods.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
from collections.abc import Sequence
12
from functools import partial
23
from inspect import isawaitable
34
from operator import itemgetter
4-
from typing import Sequence, Union
5+
from typing import Union
56

67
from sanic import Sanic
78
from sanic.constants import HTTPMethod

sanic_ext/extensions/injection/constructor.py

+11-15
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66
TYPE_CHECKING,
77
Any,
88
Callable,
9-
Dict,
109
Optional,
11-
Set,
12-
Tuple,
13-
Type,
1410
get_args,
1511
get_type_hints,
1612
)
@@ -39,8 +35,8 @@ def __init__(
3935
self, func: Callable[..., Any], request_arg: Optional[str] = None
4036
):
4137
self.func = func
42-
self.injections: Dict[str, Tuple[Type, Constructor]] = {}
43-
self.constants: Dict[str, Any] = {}
38+
self.injections: dict[str, tuple[type, Constructor]] = {}
39+
self.constants: dict[str, Any] = {}
4440
self.pass_kwargs: bool = False
4541
self.request_arg = request_arg
4642

@@ -77,7 +73,7 @@ def prepare(
7773
app: Sanic,
7874
injection_registry: InjectionRegistry,
7975
constant_registry: ConstantRegistry,
80-
allowed_types: Set[Type[object]],
76+
allowed_types: set[type[object]],
8177
) -> None:
8278
hints = self._get_hints()
8379
hints.pop("return", None)
@@ -118,25 +114,25 @@ def prepare(
118114
"html#injecting-services for more details."
119115
)
120116

121-
checked: Set[Type[object]] = set()
122-
current: Set[Type[object]] = set()
117+
checked: set[type[object]] = set()
118+
current: set[type[object]] = set()
123119
self.check_circular(checked, current)
124120

125121
def check_circular(
126122
self,
127-
checked: Set[Type[object]],
128-
current: Set[Type[object]],
123+
checked: set[type[object]],
124+
current: set[type[object]],
129125
) -> None:
130126
dependencies = set(self.injections.values())
131127
for dependency, constructor in dependencies:
132128
self._visit(dependency, constructor, checked, current)
133129

134130
def _visit(
135131
self,
136-
dependency: Type[object],
132+
dependency: type[object],
137133
constructor: Constructor,
138-
checked: Set[Type[object]],
139-
current: Set[Type[object]],
134+
checked: set[type[object]],
135+
current: set[type[object]],
140136
):
141137
if dependency in checked:
142138
return
@@ -167,7 +163,7 @@ def _get_hints(self):
167163
raise InitError(f"Cannot get type hints for {self.func}")
168164

169165

170-
async def gather_args(injections, request, **kwargs) -> Dict[str, Any]:
166+
async def gather_args(injections, request, **kwargs) -> dict[str, Any]:
171167
return {
172168
name: await do_cast(_type, constructor, request, **kwargs)
173169
for name, (_type, constructor) in injections.items()

sanic_ext/extensions/injection/injector.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from functools import partial
44
from inspect import getmembers, isclass, isfunction
5-
from typing import Any, Callable, Dict, Optional, Tuple, Type, get_type_hints
5+
from typing import Any, Callable, Optional, get_type_hints
66

77
from sanic import Sanic
88
from sanic.constants import HTTP_METHODS
@@ -24,9 +24,9 @@ def add_injection(
2424

2525
@app.listener("before_server_start", priority=PRIORITY)
2626
async def finalize_injections(app: Sanic, _):
27-
router_converters = set(
27+
router_converters = {
2828
allowed[0] for allowed in app.router.regex_types.values()
29-
)
29+
}
3030
router_types = set()
3131
for converter in router_converters:
3232
if isclass(converter):
@@ -105,10 +105,10 @@ async def setup_signatures(app, _):
105105
except TypeError:
106106
continue
107107

108-
dependencies: Dict[
109-
str, Tuple[Type, Optional[Callable[..., Any]]]
108+
dependencies: dict[
109+
str, tuple[type, Optional[Callable[..., Any]]]
110110
] = {}
111-
constants: Dict[str, Any] = {}
111+
constants: dict[str, Any] = {}
112112
for param, annotation in hints.items():
113113
if annotation in injection_registry:
114114
dependencies[param] = (

0 commit comments

Comments
 (0)