starmallow 0.3.9__py3-none-any.whl → 0.5.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.
starmallow/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.3.9"
1
+ __version__ = "0.5.0"
2
2
 
3
3
  from .applications import StarMallow
4
4
  from .exceptions import RequestValidationError
@@ -251,6 +251,8 @@ class StarMallow(Starlette):
251
251
  name: str = None,
252
252
  include_in_schema: bool = True,
253
253
  status_code: Optional[int] = None,
254
+ middleware: Sequence[Middleware] | None = None,
255
+ request_class: Type[Request] = Default(Request),
254
256
  response_model: Optional[Type[Any]] = None,
255
257
  response_class: Type[Response] = JSONResponse,
256
258
  # OpenAPI summary
@@ -275,6 +277,8 @@ class StarMallow(Starlette):
275
277
  name=name,
276
278
  include_in_schema=include_in_schema,
277
279
  status_code=status_code,
280
+ middleware=middleware,
281
+ request_class=request_class,
278
282
  response_model=response_model,
279
283
  response_class=response_class,
280
284
  summary=summary,
@@ -296,6 +300,8 @@ class StarMallow(Starlette):
296
300
  name: str = None,
297
301
  include_in_schema: bool = True,
298
302
  status_code: Optional[int] = None,
303
+ middleware: Sequence[Middleware] | None = None,
304
+ request_class: Type[Request] = Default(Request),
299
305
  response_model: Optional[Type[Any]] = None,
300
306
  response_class: Type[Response] = JSONResponse,
301
307
  # OpenAPI summary
@@ -319,6 +325,8 @@ class StarMallow(Starlette):
319
325
  name=name,
320
326
  include_in_schema=include_in_schema,
321
327
  status_code=status_code,
328
+ middleware=middleware,
329
+ request_class=request_class,
322
330
  response_model=response_model,
323
331
  response_class=response_class,
324
332
  summary=summary,
@@ -356,6 +364,7 @@ class StarMallow(Starlette):
356
364
  callbacks: Optional[List[BaseRoute]] = None,
357
365
  deprecated: Optional[bool] = None,
358
366
  include_in_schema: bool = True,
367
+ default_request_class: Type[Request] = Default(Request),
359
368
  default_response_class: Type[Response] = Default(JSONResponse),
360
369
  generate_unique_id_function: Callable[[APIRoute], str] = Default(
361
370
  generate_unique_id,
@@ -369,6 +378,7 @@ class StarMallow(Starlette):
369
378
  callbacks=callbacks,
370
379
  deprecated=deprecated,
371
380
  include_in_schema=include_in_schema,
381
+ default_request_class=default_request_class,
372
382
  default_response_class=default_response_class,
373
383
  generate_unique_id_function=generate_unique_id_function,
374
384
  )
@@ -380,6 +390,8 @@ class StarMallow(Starlette):
380
390
  name: str = None,
381
391
  include_in_schema: bool = True,
382
392
  status_code: Optional[int] = None,
393
+ middleware: Sequence[Middleware] | None = None,
394
+ request_class: Type[Request] = Default(Request),
383
395
  response_model: Optional[Type[Any]] = None,
384
396
  response_class: Type[Response] = JSONResponse,
385
397
  # OpenAPI summary
@@ -402,6 +414,8 @@ class StarMallow(Starlette):
402
414
  name=name,
403
415
  include_in_schema=include_in_schema,
404
416
  status_code=status_code,
417
+ middleware=middleware,
418
+ request_class=request_class,
405
419
  response_model=response_model,
406
420
  response_class=response_class,
407
421
  summary=summary,
@@ -422,6 +436,8 @@ class StarMallow(Starlette):
422
436
  name: str = None,
423
437
  include_in_schema: bool = True,
424
438
  status_code: Optional[int] = None,
439
+ middleware: Sequence[Middleware] | None = None,
440
+ request_class: Type[Request] = Default(Request),
425
441
  response_model: Optional[Type[Any]] = None,
426
442
  response_class: Type[Response] = JSONResponse,
427
443
  # OpenAPI summary
@@ -444,6 +460,8 @@ class StarMallow(Starlette):
444
460
  name=name,
445
461
  include_in_schema=include_in_schema,
446
462
  status_code=status_code,
463
+ middleware=middleware,
464
+ request_class=request_class,
447
465
  response_model=response_model,
448
466
  response_class=response_class,
449
467
  summary=summary,
@@ -464,6 +482,8 @@ class StarMallow(Starlette):
464
482
  name: str = None,
465
483
  include_in_schema: bool = True,
466
484
  status_code: Optional[int] = None,
485
+ middleware: Sequence[Middleware] | None = None,
486
+ request_class: Type[Request] = Default(Request),
467
487
  response_model: Optional[Type[Any]] = None,
468
488
  response_class: Type[Response] = JSONResponse,
469
489
  # OpenAPI summary
@@ -486,6 +506,8 @@ class StarMallow(Starlette):
486
506
  name=name,
487
507
  include_in_schema=include_in_schema,
488
508
  status_code=status_code,
509
+ middleware=middleware,
510
+ request_class=request_class,
489
511
  response_model=response_model,
490
512
  response_class=response_class,
491
513
  summary=summary,
@@ -506,6 +528,8 @@ class StarMallow(Starlette):
506
528
  name: str = None,
507
529
  include_in_schema: bool = True,
508
530
  status_code: Optional[int] = None,
531
+ middleware: Sequence[Middleware] | None = None,
532
+ request_class: Type[Request] = Default(Request),
509
533
  response_model: Optional[Type[Any]] = None,
510
534
  response_class: Type[Response] = JSONResponse,
511
535
  # OpenAPI summary
@@ -528,6 +552,8 @@ class StarMallow(Starlette):
528
552
  name=name,
529
553
  include_in_schema=include_in_schema,
530
554
  status_code=status_code,
555
+ middleware=middleware,
556
+ request_class=request_class,
531
557
  response_model=response_model,
532
558
  response_class=response_class,
533
559
  summary=summary,
@@ -548,6 +574,8 @@ class StarMallow(Starlette):
548
574
  name: str = None,
549
575
  include_in_schema: bool = True,
550
576
  status_code: Optional[int] = None,
577
+ middleware: Sequence[Middleware] | None = None,
578
+ request_class: Type[Request] = Default(Request),
551
579
  response_model: Optional[Type[Any]] = None,
552
580
  response_class: Type[Response] = JSONResponse,
553
581
  # OpenAPI summary
@@ -570,6 +598,8 @@ class StarMallow(Starlette):
570
598
  name=name,
571
599
  include_in_schema=include_in_schema,
572
600
  status_code=status_code,
601
+ middleware=middleware,
602
+ request_class=request_class,
573
603
  response_model=response_model,
574
604
  response_class=response_class,
575
605
  summary=summary,
@@ -590,6 +620,8 @@ class StarMallow(Starlette):
590
620
  name: str = None,
591
621
  include_in_schema: bool = True,
592
622
  status_code: Optional[int] = None,
623
+ middleware: Sequence[Middleware] | None = None,
624
+ request_class: Type[Request] = Default(Request),
593
625
  response_model: Optional[Type[Any]] = None,
594
626
  response_class: Type[Response] = JSONResponse,
595
627
  # OpenAPI summary
@@ -612,6 +644,8 @@ class StarMallow(Starlette):
612
644
  name=name,
613
645
  include_in_schema=include_in_schema,
614
646
  status_code=status_code,
647
+ middleware=middleware,
648
+ request_class=request_class,
615
649
  response_model=response_model,
616
650
  response_class=response_class,
617
651
  summary=summary,
@@ -632,6 +666,8 @@ class StarMallow(Starlette):
632
666
  name: str = None,
633
667
  include_in_schema: bool = True,
634
668
  status_code: Optional[int] = None,
669
+ middleware: Sequence[Middleware] | None = None,
670
+ request_class: Type[Request] = Default(Request),
635
671
  response_model: Optional[Type[Any]] = None,
636
672
  response_class: Type[Response] = JSONResponse,
637
673
  # OpenAPI summary
@@ -654,6 +690,8 @@ class StarMallow(Starlette):
654
690
  name=name,
655
691
  include_in_schema=include_in_schema,
656
692
  status_code=status_code,
693
+ middleware=middleware,
694
+ request_class=request_class,
657
695
  response_model=response_model,
658
696
  response_class=response_class,
659
697
  summary=summary,
@@ -674,6 +712,8 @@ class StarMallow(Starlette):
674
712
  name: str = None,
675
713
  include_in_schema: bool = True,
676
714
  status_code: Optional[int] = None,
715
+ middleware: Sequence[Middleware] | None = None,
716
+ request_class: Type[Request] = Default(Request),
677
717
  response_model: Optional[Type[Any]] = None,
678
718
  response_class: Type[Response] = JSONResponse,
679
719
  # OpenAPI summary
@@ -696,6 +736,8 @@ class StarMallow(Starlette):
696
736
  name=name,
697
737
  include_in_schema=include_in_schema,
698
738
  status_code=status_code,
739
+ middleware=middleware,
740
+ request_class=request_class,
699
741
  response_model=response_model,
700
742
  response_class=response_class,
701
743
  summary=summary,
starmallow/decorators.py CHANGED
@@ -1,7 +1,9 @@
1
1
  from dataclasses import dataclass, field
2
2
  from enum import Enum
3
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Type, Union
3
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Type, Union
4
4
 
5
+ from starlette.middleware import Middleware
6
+ from starlette.requests import Request
5
7
  from starlette.responses import Response
6
8
  from starlette.routing import BaseRoute
7
9
 
@@ -19,7 +21,9 @@ class EndpointOptions:
19
21
  name: str = None
20
22
  include_in_schema: bool = True
21
23
  status_code: Optional[int] = None
24
+ middleware: Sequence[Middleware] | None = None
22
25
  deprecated: Optional[bool] = None
26
+ request_class: Type[Request] = Request
23
27
  response_model: Optional[Type[Any]] = None
24
28
  response_class: Type[Response] = JSONResponse
25
29
  # OpenAPI summary
@@ -38,6 +42,7 @@ class EndpointOptions:
38
42
  tags: Optional[List[Union[str, Enum]]] = None
39
43
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
40
44
  openapi_extra: Optional[Dict[str, Any]] = None
45
+ route_class: Optional[Type["APIRoute"]] = None
41
46
 
42
47
 
43
48
  def route(
@@ -45,7 +50,9 @@ def route(
45
50
  name: str = None,
46
51
  include_in_schema: bool = True,
47
52
  status_code: Optional[int] = None,
53
+ middleware: Sequence[Middleware] | None = None,
48
54
  deprecated: Optional[bool] = None,
55
+ request_class: Type[Request] = Default(Request),
49
56
  response_model: Optional[Type[Any]] = None,
50
57
  response_class: Type[Response] = JSONResponse,
51
58
  # OpenAPI summary
@@ -62,6 +69,7 @@ def route(
62
69
  tags: Optional[List[Union[str, Enum]]] = None,
63
70
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
64
71
  openapi_extra: Optional[Dict[str, Any]] = None,
72
+ route_class: Optional[Type["APIRoute"]] = None,
65
73
  ) -> Callable[[DecoratedCallable], DecoratedCallable]:
66
74
  '''
67
75
  Intended to be used with APIHTTPEndpoint to override options on a per method basis
@@ -72,7 +80,9 @@ def route(
72
80
  name=name,
73
81
  include_in_schema=include_in_schema,
74
82
  status_code=status_code,
83
+ middleware=middleware,
75
84
  deprecated=deprecated,
85
+ request_class=request_class,
76
86
  response_model=response_model,
77
87
  response_class=response_class,
78
88
  summary=summary,
@@ -84,6 +94,7 @@ def route(
84
94
  generate_unique_id_function=generate_unique_id_function,
85
95
  openapi_extra=openapi_extra,
86
96
  tags=tags,
97
+ route_class=route_class,
87
98
  )
88
99
  return func
89
100
  return decorator
starmallow/endpoint.py CHANGED
@@ -245,6 +245,9 @@ class EndpointMixin:
245
245
  elif is_marshmallow_schema(model):
246
246
  return SchemaModel(model() if inspect.isclass(model) else model, **kwargs)
247
247
  elif is_marshmallow_field(model):
248
+ if inspect.isclass(model):
249
+ model = model()
250
+
248
251
  if model.load_default is not None and model.load_default != kwargs.get('load_default', ma.missing):
249
252
  logger.warning(
250
253
  f"'{parameter_name}' model and annotation have different 'load_default' values."
starmallow/requests.py ADDED
@@ -0,0 +1,36 @@
1
+ from typing import Any
2
+
3
+ from starlette.requests import Request
4
+
5
+ try:
6
+ import ujson
7
+ except ImportError: # pragma: nocover
8
+ ujson = None # type: ignore
9
+
10
+
11
+ try:
12
+ import orjson
13
+ except ImportError: # pragma: nocover
14
+ orjson = None # type: ignore
15
+
16
+
17
+ class UJSONRequest(Request):
18
+
19
+ async def json(self) -> Any:
20
+ assert ujson is not None, "ujson must be installed to use UJSONRequest"
21
+
22
+ if not hasattr(self, "_json"):
23
+ body = await self.body()
24
+ self._json = ujson.loads(body)
25
+ return self._json
26
+
27
+
28
+ class ORJSONRequest(Request):
29
+
30
+ async def json(self) -> Any:
31
+ assert orjson is not None, "orjson must be installed to use ORJSONRequest"
32
+
33
+ if not hasattr(self, "_json"):
34
+ body = await self.body()
35
+ self._json = orjson.loads(body)
36
+ return self._json
starmallow/routing.py CHANGED
@@ -3,14 +3,34 @@ import functools
3
3
  import inspect
4
4
  import logging
5
5
  from enum import Enum, IntEnum
6
- from typing import Any, Callable, Coroutine, Dict, List, Optional, Set, Tuple, Type, Union
6
+ from typing import (
7
+ Any,
8
+ Awaitable,
9
+ Callable,
10
+ Coroutine,
11
+ Dict,
12
+ List,
13
+ Optional,
14
+ Sequence,
15
+ Set,
16
+ Tuple,
17
+ Type,
18
+ Union,
19
+ )
7
20
 
8
21
  import marshmallow as ma
9
22
  from starlette import routing
10
23
  from starlette.concurrency import run_in_threadpool
24
+ from starlette.middleware import Middleware
11
25
  from starlette.requests import Request
12
26
  from starlette.responses import Response
13
- from starlette.routing import BaseRoute, Match, compile_path, request_response
27
+ from starlette.routing import (
28
+ BaseRoute,
29
+ Match,
30
+ compile_path,
31
+ is_async_callable,
32
+ wrap_app_handling_exceptions,
33
+ )
14
34
  from starlette.status import WS_1008_POLICY_VIOLATION
15
35
  from starlette.types import ASGIApp, Receive, Scope, Send
16
36
  from starlette.websockets import WebSocket
@@ -57,6 +77,30 @@ async def run_endpoint_function(
57
77
  return await run_in_threadpool(endpoint_model.call, **kwargs)
58
78
 
59
79
 
80
+ def request_response(
81
+ func: Callable[[Request], Union[Awaitable[Response], Response]],
82
+ request_class: Type[Request],
83
+ ) -> ASGIApp:
84
+ """
85
+ Takes a function or coroutine `func(request) -> response`,
86
+ and returns an ASGI application.
87
+ """
88
+
89
+ async def app(scope: Scope, receive: Receive, send: Send) -> None:
90
+ request = request_class(scope, receive, send)
91
+
92
+ async def app(scope: Scope, receive: Receive, send: Send) -> None:
93
+ if is_async_callable(func):
94
+ response = await func(request)
95
+ else:
96
+ response = await run_in_threadpool(func, request)
97
+ await response(scope, receive, send)
98
+
99
+ await wrap_app_handling_exceptions(app, request)(scope, receive, send)
100
+
101
+ return app
102
+
103
+
60
104
  def websocket_session(func: Callable) -> ASGIApp:
61
105
  """
62
106
  Takes a coroutine `func(session)`, and returns an ASGI application.
@@ -171,8 +215,12 @@ class APIRoute(routing.Route, EndpointMixin):
171
215
  name: Optional[str] = None,
172
216
  methods: Optional[Union[Set[str], List[str]]] = None,
173
217
  include_in_schema: bool = True,
218
+ middleware: Sequence[Middleware] | None = None,
174
219
  status_code: Optional[int] = None,
175
220
  deprecated: Optional[bool] = None,
221
+ request_class: Union[Type[Request], DefaultPlaceholder] = Default(
222
+ Request,
223
+ ),
176
224
  response_model: Optional[ma.Schema] = None,
177
225
  response_class: Union[Type[Response], DefaultPlaceholder] = Default(
178
226
  JSONResponse,
@@ -205,7 +253,7 @@ class APIRoute(routing.Route, EndpointMixin):
205
253
  endpoint_handler = endpoint_handler.func
206
254
  if inspect.isfunction(endpoint_handler) or inspect.ismethod(endpoint_handler):
207
255
  # Endpoint is function or method. Treat it as `func(request) -> response`.
208
- self.app = request_response(endpoint)
256
+ self.app = request_response(endpoint, request_class)
209
257
  if methods is None:
210
258
  methods = ["GET"]
211
259
  else:
@@ -226,7 +274,6 @@ class APIRoute(routing.Route, EndpointMixin):
226
274
  self.status_code = status_code
227
275
  self.deprecated = deprecated
228
276
  self.response_model = response_model
229
- self.response_class = response_class
230
277
  self.summary = summary
231
278
  self.operation_id = operation_id
232
279
  self.callbacks = callbacks
@@ -234,6 +281,16 @@ class APIRoute(routing.Route, EndpointMixin):
234
281
  self.responses = responses or {}
235
282
  self.openapi_extra = openapi_extra
236
283
 
284
+ if isinstance(request_class, DefaultPlaceholder):
285
+ self.request_class: Request = request_class.value
286
+ else:
287
+ self.request_class = request_class
288
+
289
+ if isinstance(response_class, DefaultPlaceholder):
290
+ self.response_class: Response = response_class.value
291
+ else:
292
+ self.response_class = response_class
293
+
237
294
  self.generate_unique_id_function = generate_unique_id_function
238
295
  if isinstance(generate_unique_id_function, DefaultPlaceholder):
239
296
  current_generate_unique_id: Callable[
@@ -279,16 +336,23 @@ class APIRoute(routing.Route, EndpointMixin):
279
336
 
280
337
  self.endpoint_model = self.get_endpoint_model(
281
338
  self.path_format,
282
- endpoint,
283
- name=name,
339
+ self.endpoint,
340
+ name=self.name,
284
341
  methods=self.methods,
285
- status_code=status_code,
286
- response_model=response_model,
287
- response_class=response_class,
342
+ status_code=self.status_code,
343
+ response_model=self.response_model,
344
+ response_class=self.response_class,
288
345
  route=self,
289
346
  )
290
347
 
291
- self.app = request_response(get_request_handler(self.endpoint_model))
348
+ self.middleware = middleware # Store for include_router
349
+ self.app = request_response(self.get_route_handler(), self.request_class)
350
+ if middleware is not None:
351
+ for cls, args, kwargs in reversed(middleware):
352
+ self.app = cls(app=self.app, *args, **kwargs) # noqa: B026
353
+
354
+ def get_route_handler(self):
355
+ return get_request_handler(self.endpoint_model)
292
356
 
293
357
 
294
358
  class APIRouter(routing.Router):
@@ -297,6 +361,7 @@ class APIRouter(routing.Router):
297
361
  self,
298
362
  *args,
299
363
  tags: Optional[List[Union[str, Enum]]] = None,
364
+ default_request_class: Type[Request] = Default(Request),
300
365
  default_response_class: Type[Response] = Default(JSONResponse),
301
366
  deprecated: Optional[bool] = None,
302
367
  include_in_schema: bool = True,
@@ -306,11 +371,14 @@ class APIRouter(routing.Router):
306
371
  generate_unique_id,
307
372
  ),
308
373
  prefix: str = "",
374
+ route_class: Optional[Type[APIRoute]] = APIRoute,
375
+ middleware: Sequence[Middleware] | None = None,
309
376
  **kwargs,
310
377
  ) -> None:
311
- super().__init__(*args, **kwargs)
378
+ super().__init__(*args, middleware=middleware, **kwargs)
312
379
 
313
380
  self.tags: List[Union[str, Enum]] = tags or []
381
+ self.default_request_class = default_request_class
314
382
  self.default_response_class = default_response_class
315
383
  self.deprecated = deprecated
316
384
  self.include_in_schema = include_in_schema
@@ -318,6 +386,8 @@ class APIRouter(routing.Router):
318
386
  self.callbacks = callbacks or []
319
387
  self.generate_unique_id_function = generate_unique_id_function
320
388
  self.prefix = prefix
389
+ self.route_class = route_class
390
+ self.middleware = middleware
321
391
 
322
392
  def route(
323
393
  self,
@@ -347,7 +417,9 @@ class APIRouter(routing.Router):
347
417
  name: str = None,
348
418
  include_in_schema: bool = True,
349
419
  status_code: Optional[int] = None,
420
+ middleware: Sequence[Middleware] | None = None,
350
421
  deprecated: Optional[bool] = None,
422
+ request_class: Type[Request] = Default(Request),
351
423
  response_model: Optional[Type[Any]] = None,
352
424
  response_class: Type[Response] = JSONResponse,
353
425
  # OpenAPI summary
@@ -364,7 +436,9 @@ class APIRouter(routing.Router):
364
436
  tags: Optional[List[Union[str, Enum]]] = None,
365
437
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
366
438
  openapi_extra: Optional[Dict[str, Any]] = None,
439
+ route_class: Optional[Type[APIRoute]] = None,
367
440
  ) -> None:
441
+ route_class = route_class or self.route_class
368
442
 
369
443
  current_tags = self.tags.copy()
370
444
  if tags:
@@ -386,14 +460,17 @@ class APIRouter(routing.Router):
386
460
  if endpoint_options.tags:
387
461
  method_tags.extend(endpoint_options.tags)
388
462
 
389
- route = APIRoute(
463
+ endpoint_route_class = endpoint_options.route_class or route_class
464
+ route = endpoint_route_class(
390
465
  self.prefix + path,
391
466
  endpoint_function,
392
467
  methods=[method],
393
468
  name=endpoint_options.name or name,
394
469
  include_in_schema=endpoint_options.include_in_schema and include_in_schema and self.include_in_schema,
395
470
  status_code=endpoint_options.status_code or status_code,
471
+ middleware=endpoint_options.middleware or middleware,
396
472
  deprecated=endpoint_options.deprecated or deprecated or self.deprecated,
473
+ request_class=endpoint_options.request_class or request_class,
397
474
  response_model=endpoint_options.response_model or response_model,
398
475
  response_class=endpoint_options.response_class or response_class,
399
476
  summary=endpoint_options.summary or summary,
@@ -410,14 +487,16 @@ class APIRouter(routing.Router):
410
487
  self.routes.append(route)
411
488
 
412
489
  else:
413
- route = APIRoute(
490
+ route = route_class(
414
491
  self.prefix + path,
415
492
  endpoint,
416
493
  methods=methods,
417
494
  name=name,
418
495
  include_in_schema=include_in_schema and self.include_in_schema,
419
496
  status_code=status_code,
497
+ middleware=middleware,
420
498
  deprecated=deprecated or self.deprecated,
499
+ request_class=request_class,
421
500
  response_model=response_model,
422
501
  response_class=response_class,
423
502
  summary=summary,
@@ -441,7 +520,9 @@ class APIRouter(routing.Router):
441
520
  name: str = None,
442
521
  include_in_schema: bool = True,
443
522
  status_code: Optional[int] = None,
523
+ middleware: Sequence[Middleware] | None = None,
444
524
  deprecated: Optional[bool] = None,
525
+ request_class: Type[Request] = Default(Request),
445
526
  response_model: Optional[Type[Any]] = None,
446
527
  response_class: Type[Response] = JSONResponse,
447
528
  # OpenAPI summary
@@ -458,6 +539,7 @@ class APIRouter(routing.Router):
458
539
  tags: Optional[List[Union[str, Enum]]] = None,
459
540
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
460
541
  openapi_extra: Optional[Dict[str, Any]] = None,
542
+ route_class: Optional[Type[APIRoute]] = None,
461
543
  ) -> Callable[[DecoratedCallable], DecoratedCallable]:
462
544
  def decorator(func: DecoratedCallable) -> DecoratedCallable:
463
545
  self.add_api_route(
@@ -467,7 +549,9 @@ class APIRouter(routing.Router):
467
549
  name=name,
468
550
  include_in_schema=include_in_schema,
469
551
  status_code=status_code,
552
+ middleware=middleware,
470
553
  deprecated=deprecated,
554
+ request_class=request_class,
471
555
  response_model=response_model,
472
556
  response_class=response_class,
473
557
  summary=summary,
@@ -479,6 +563,7 @@ class APIRouter(routing.Router):
479
563
  generate_unique_id_function=generate_unique_id_function,
480
564
  openapi_extra=openapi_extra,
481
565
  tags=tags,
566
+ route_class=route_class,
482
567
  )
483
568
  return func
484
569
  return decorator
@@ -517,6 +602,7 @@ class APIRouter(routing.Router):
517
602
  *,
518
603
  prefix: str = "",
519
604
  tags: Optional[List[Union[str, Enum]]] = None,
605
+ default_request_class: Type[Request] = Default(Request),
520
606
  default_response_class: Type[Response] = Default(JSONResponse),
521
607
  responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
522
608
  callbacks: Optional[List[BaseRoute]] = None,
@@ -543,6 +629,12 @@ class APIRouter(routing.Router):
543
629
  for route in router.routes:
544
630
  if isinstance(route, APIRoute):
545
631
  combined_responses = {**responses, **route.responses}
632
+ use_request_class = get_value_or_default(
633
+ route.request_class,
634
+ router.default_request_class,
635
+ default_request_class,
636
+ self.default_request_class,
637
+ )
546
638
  use_response_class = get_value_or_default(
547
639
  route.response_class,
548
640
  router.default_response_class,
@@ -565,11 +657,19 @@ class APIRouter(routing.Router):
565
657
  generate_unique_id_function,
566
658
  self.generate_unique_id_function,
567
659
  )
660
+
661
+ middleware = []
662
+ if router.middleware:
663
+ middleware.extend(router.middleware)
664
+ if route.middleware:
665
+ middleware.extend(route.middleware)
666
+
568
667
  self.add_api_route(
569
668
  prefix + route.path,
570
669
  route.endpoint,
571
670
  response_model=route.response_model,
572
671
  status_code=route.status_code,
672
+ middleware=middleware,
573
673
  tags=current_tags,
574
674
  summary=route.summary,
575
675
  description=route.description,
@@ -584,10 +684,12 @@ class APIRouter(routing.Router):
584
684
  and self.include_in_schema
585
685
  and include_in_schema
586
686
  ),
687
+ request_class=use_request_class,
587
688
  response_class=use_response_class,
588
689
  name=route.name,
589
690
  openapi_extra=route.openapi_extra,
590
691
  generate_unique_id_function=current_generate_unique_id,
692
+ route_class=route.__class__,
591
693
  )
592
694
  elif isinstance(route, routing.Route):
593
695
  methods = list(route.methods or [])
@@ -619,7 +721,9 @@ class APIRouter(routing.Router):
619
721
  name: str = None,
620
722
  include_in_schema: bool = True,
621
723
  status_code: Optional[int] = None,
724
+ middleware: Sequence[Middleware] | None = None,
622
725
  deprecated: Optional[bool] = None,
726
+ request_class: Type[Request] = Default(Request),
623
727
  response_model: Optional[Type[Any]] = None,
624
728
  response_class: Type[Response] = JSONResponse,
625
729
  # OpenAPI summary
@@ -636,6 +740,7 @@ class APIRouter(routing.Router):
636
740
  tags: Optional[List[Union[str, Enum]]] = None,
637
741
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
638
742
  openapi_extra: Optional[Dict[str, Any]] = None,
743
+ route_class: Optional[Type[APIRoute]] = None,
639
744
  ):
640
745
  return self.api_route(
641
746
  path,
@@ -643,7 +748,9 @@ class APIRouter(routing.Router):
643
748
  name=name,
644
749
  include_in_schema=include_in_schema,
645
750
  status_code=status_code,
751
+ middleware=middleware,
646
752
  deprecated=deprecated,
753
+ request_class=request_class,
647
754
  response_model=response_model,
648
755
  response_class=response_class,
649
756
  summary=summary,
@@ -655,6 +762,7 @@ class APIRouter(routing.Router):
655
762
  generate_unique_id_function=generate_unique_id_function,
656
763
  openapi_extra=openapi_extra,
657
764
  tags=tags,
765
+ route_class=route_class,
658
766
  )
659
767
 
660
768
  def put(
@@ -664,7 +772,9 @@ class APIRouter(routing.Router):
664
772
  name: str = None,
665
773
  include_in_schema: bool = True,
666
774
  status_code: Optional[int] = None,
775
+ middleware: Sequence[Middleware] | None = None,
667
776
  deprecated: Optional[bool] = None,
777
+ request_class: Type[Request] = Default(Request),
668
778
  response_model: Optional[Type[Any]] = None,
669
779
  response_class: Type[Response] = JSONResponse,
670
780
  # OpenAPI summary
@@ -681,6 +791,7 @@ class APIRouter(routing.Router):
681
791
  tags: Optional[List[Union[str, Enum]]] = None,
682
792
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
683
793
  openapi_extra: Optional[Dict[str, Any]] = None,
794
+ route_class: Optional[Type[APIRoute]] = None,
684
795
  ):
685
796
  return self.api_route(
686
797
  path,
@@ -688,7 +799,9 @@ class APIRouter(routing.Router):
688
799
  name=name,
689
800
  include_in_schema=include_in_schema,
690
801
  status_code=status_code,
802
+ middleware=middleware,
691
803
  deprecated=deprecated,
804
+ request_class=request_class,
692
805
  response_model=response_model,
693
806
  response_class=response_class,
694
807
  summary=summary,
@@ -700,6 +813,7 @@ class APIRouter(routing.Router):
700
813
  generate_unique_id_function=generate_unique_id_function,
701
814
  openapi_extra=openapi_extra,
702
815
  tags=tags,
816
+ route_class=route_class,
703
817
  )
704
818
 
705
819
  def post(
@@ -709,7 +823,9 @@ class APIRouter(routing.Router):
709
823
  name: str = None,
710
824
  include_in_schema: bool = True,
711
825
  status_code: Optional[int] = None,
826
+ middleware: Sequence[Middleware] | None = None,
712
827
  deprecated: Optional[bool] = None,
828
+ request_class: Type[Request] = Default(Request),
713
829
  response_model: Optional[Type[Any]] = None,
714
830
  response_class: Type[Response] = JSONResponse,
715
831
  # OpenAPI summary
@@ -726,6 +842,7 @@ class APIRouter(routing.Router):
726
842
  tags: Optional[List[Union[str, Enum]]] = None,
727
843
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
728
844
  openapi_extra: Optional[Dict[str, Any]] = None,
845
+ route_class: Optional[Type[APIRoute]] = None,
729
846
  ):
730
847
  return self.api_route(
731
848
  path,
@@ -733,7 +850,9 @@ class APIRouter(routing.Router):
733
850
  name=name,
734
851
  include_in_schema=include_in_schema,
735
852
  status_code=status_code,
853
+ middleware=middleware,
736
854
  deprecated=deprecated,
855
+ request_class=request_class,
737
856
  response_model=response_model,
738
857
  response_class=response_class,
739
858
  summary=summary,
@@ -745,6 +864,7 @@ class APIRouter(routing.Router):
745
864
  generate_unique_id_function=generate_unique_id_function,
746
865
  openapi_extra=openapi_extra,
747
866
  tags=tags,
867
+ route_class=route_class,
748
868
  )
749
869
 
750
870
  def delete(
@@ -754,7 +874,9 @@ class APIRouter(routing.Router):
754
874
  name: str = None,
755
875
  include_in_schema: bool = True,
756
876
  status_code: Optional[int] = None,
877
+ middleware: Sequence[Middleware] | None = None,
757
878
  deprecated: Optional[bool] = None,
879
+ request_class: Type[Request] = Default(Request),
758
880
  response_model: Optional[Type[Any]] = None,
759
881
  response_class: Type[Response] = JSONResponse,
760
882
  # OpenAPI summary
@@ -771,6 +893,7 @@ class APIRouter(routing.Router):
771
893
  tags: Optional[List[Union[str, Enum]]] = None,
772
894
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
773
895
  openapi_extra: Optional[Dict[str, Any]] = None,
896
+ route_class: Optional[Type[APIRoute]] = None,
774
897
  ):
775
898
  return self.api_route(
776
899
  path,
@@ -778,7 +901,9 @@ class APIRouter(routing.Router):
778
901
  name=name,
779
902
  include_in_schema=include_in_schema,
780
903
  status_code=status_code,
904
+ middleware=middleware,
781
905
  deprecated=deprecated,
906
+ request_class=request_class,
782
907
  response_model=response_model,
783
908
  response_class=response_class,
784
909
  summary=summary,
@@ -790,6 +915,7 @@ class APIRouter(routing.Router):
790
915
  generate_unique_id_function=generate_unique_id_function,
791
916
  openapi_extra=openapi_extra,
792
917
  tags=tags,
918
+ route_class=route_class,
793
919
  )
794
920
 
795
921
  def options(
@@ -799,7 +925,9 @@ class APIRouter(routing.Router):
799
925
  name: str = None,
800
926
  include_in_schema: bool = True,
801
927
  status_code: Optional[int] = None,
928
+ middleware: Sequence[Middleware] | None = None,
802
929
  deprecated: Optional[bool] = None,
930
+ request_class: Type[Request] = Default(Request),
803
931
  response_model: Optional[Type[Any]] = None,
804
932
  response_class: Type[Response] = JSONResponse,
805
933
  # OpenAPI summary
@@ -816,6 +944,7 @@ class APIRouter(routing.Router):
816
944
  tags: Optional[List[Union[str, Enum]]] = None,
817
945
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
818
946
  openapi_extra: Optional[Dict[str, Any]] = None,
947
+ route_class: Optional[Type[APIRoute]] = None,
819
948
  ):
820
949
  return self.api_route(
821
950
  path,
@@ -823,7 +952,9 @@ class APIRouter(routing.Router):
823
952
  name=name,
824
953
  include_in_schema=include_in_schema,
825
954
  status_code=status_code,
955
+ middleware=middleware,
826
956
  deprecated=deprecated,
957
+ request_class=request_class,
827
958
  response_model=response_model,
828
959
  response_class=response_class,
829
960
  summary=summary,
@@ -835,6 +966,7 @@ class APIRouter(routing.Router):
835
966
  generate_unique_id_function=generate_unique_id_function,
836
967
  openapi_extra=openapi_extra,
837
968
  tags=tags,
969
+ route_class=route_class,
838
970
  )
839
971
 
840
972
  def head(
@@ -844,7 +976,9 @@ class APIRouter(routing.Router):
844
976
  name: str = None,
845
977
  include_in_schema: bool = True,
846
978
  status_code: Optional[int] = None,
979
+ middleware: Sequence[Middleware] | None = None,
847
980
  deprecated: Optional[bool] = None,
981
+ request_class: Type[Request] = Default(Request),
848
982
  response_model: Optional[Type[Any]] = None,
849
983
  response_class: Type[Response] = JSONResponse,
850
984
  # OpenAPI summary
@@ -861,6 +995,7 @@ class APIRouter(routing.Router):
861
995
  tags: Optional[List[Union[str, Enum]]] = None,
862
996
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
863
997
  openapi_extra: Optional[Dict[str, Any]] = None,
998
+ route_class: Optional[Type[APIRoute]] = None,
864
999
  ):
865
1000
  return self.api_route(
866
1001
  path,
@@ -868,7 +1003,9 @@ class APIRouter(routing.Router):
868
1003
  name=name,
869
1004
  include_in_schema=include_in_schema,
870
1005
  status_code=status_code,
1006
+ middleware=middleware,
871
1007
  deprecated=deprecated,
1008
+ request_class=request_class,
872
1009
  response_model=response_model,
873
1010
  response_class=response_class,
874
1011
  summary=summary,
@@ -880,6 +1017,7 @@ class APIRouter(routing.Router):
880
1017
  generate_unique_id_function=generate_unique_id_function,
881
1018
  openapi_extra=openapi_extra,
882
1019
  tags=tags,
1020
+ route_class=route_class,
883
1021
  )
884
1022
 
885
1023
  def patch(
@@ -889,7 +1027,9 @@ class APIRouter(routing.Router):
889
1027
  name: str = None,
890
1028
  include_in_schema: bool = True,
891
1029
  status_code: Optional[int] = None,
1030
+ middleware: Sequence[Middleware] | None = None,
892
1031
  deprecated: Optional[bool] = None,
1032
+ request_class: Type[Request] = Default(Request),
893
1033
  response_model: Optional[Type[Any]] = None,
894
1034
  response_class: Type[Response] = JSONResponse,
895
1035
  # OpenAPI summary
@@ -906,6 +1046,7 @@ class APIRouter(routing.Router):
906
1046
  tags: Optional[List[Union[str, Enum]]] = None,
907
1047
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
908
1048
  openapi_extra: Optional[Dict[str, Any]] = None,
1049
+ route_class: Optional[Type[APIRoute]] = None,
909
1050
  ):
910
1051
  return self.api_route(
911
1052
  path,
@@ -913,7 +1054,9 @@ class APIRouter(routing.Router):
913
1054
  name=name,
914
1055
  include_in_schema=include_in_schema,
915
1056
  status_code=status_code,
1057
+ middleware=middleware,
916
1058
  deprecated=deprecated,
1059
+ request_class=request_class,
917
1060
  response_model=response_model,
918
1061
  response_class=response_class,
919
1062
  summary=summary,
@@ -925,6 +1068,7 @@ class APIRouter(routing.Router):
925
1068
  generate_unique_id_function=generate_unique_id_function,
926
1069
  openapi_extra=openapi_extra,
927
1070
  tags=tags,
1071
+ route_class=route_class,
928
1072
  )
929
1073
 
930
1074
  def trace(
@@ -934,7 +1078,9 @@ class APIRouter(routing.Router):
934
1078
  name: str = None,
935
1079
  include_in_schema: bool = True,
936
1080
  status_code: Optional[int] = None,
1081
+ middleware: Sequence[Middleware] | None = None,
937
1082
  deprecated: Optional[bool] = None,
1083
+ request_class: Type[Request] = Default(Request),
938
1084
  response_model: Optional[Type[Any]] = None,
939
1085
  response_class: Type[Response] = JSONResponse,
940
1086
  # OpenAPI summary
@@ -951,6 +1097,7 @@ class APIRouter(routing.Router):
951
1097
  tags: Optional[List[Union[str, Enum]]] = None,
952
1098
  # Will be deeply merged with the automatically generated OpenAPI schema for the path operation.
953
1099
  openapi_extra: Optional[Dict[str, Any]] = None,
1100
+ route_class: Optional[Type[APIRoute]] = None,
954
1101
  ):
955
1102
  return self.api_route(
956
1103
  path,
@@ -958,7 +1105,9 @@ class APIRouter(routing.Router):
958
1105
  name=name,
959
1106
  include_in_schema=include_in_schema,
960
1107
  status_code=status_code,
1108
+ middleware=middleware,
961
1109
  deprecated=deprecated,
1110
+ request_class=request_class,
962
1111
  response_model=response_model,
963
1112
  response_class=response_class,
964
1113
  summary=summary,
@@ -970,4 +1119,5 @@ class APIRouter(routing.Router):
970
1119
  generate_unique_id_function=generate_unique_id_function,
971
1120
  openapi_extra=openapi_extra,
972
1121
  tags=tags,
1122
+ route_class=route_class,
973
1123
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: starmallow
3
- Version: 0.3.9
3
+ Version: 0.5.0
4
4
  Summary: StarMallow framework
5
5
  Project-URL: Homepage, https://github.com/mvanderlee/starmallow
6
6
  Author-email: Michiel Vanderlee <jmt.vanderlee@gmail.com>
@@ -1,21 +1,22 @@
1
- starmallow/__init__.py,sha256=qxUolB3Gs6F-zlmlo45q_n51vgxqvj4J0kYDV9Rd2CE,322
2
- starmallow/applications.py,sha256=oZxxLof82QdfK44-q6wUHq9z8_sRGSEVzSQkNvfGKIg,29708
1
+ starmallow/__init__.py,sha256=01Y6juyLnOP83a39nE_mBYK-sEnBIX2SceZ_W32qY9w,322
2
+ starmallow/applications.py,sha256=mSL4YDozP8n6v22g4NX7EAMXmGhzzhtjtZd68YHcFvw,31720
3
3
  starmallow/concurrency.py,sha256=MVRjo4Vqss_yqhaoeVt3xb7rLaSuAq_q9uYgTwbsojE,1375
4
4
  starmallow/constants.py,sha256=u0h8cJKhJY0oIZqzr7wpEZG2bPLrw5FroMnn3d8KBNQ,129
5
5
  starmallow/dataclasses.py,sha256=ap9DInvQjH2AyI4MAAnbDEuNnbPb94PigaNmEb7AQU8,2658
6
6
  starmallow/datastructures.py,sha256=iH_KJuJ6kBCWEsnHFLdA3iyb6ZxhfdMHYrJlhiEZtDU,839
7
- starmallow/decorators.py,sha256=SBrzmKxzF2q7hNCW_V7j0UV461QERSh9OTtTdTFi6Kg,3597
7
+ starmallow/decorators.py,sha256=MYk3WEFRSfQTN0Y3JoL3Y_Cz47gatMrVEPtNDw42XwU,4105
8
8
  starmallow/delimited_field.py,sha256=gonWgYg6G5xH2yXAyfDgkePmQ8dUaRSp2hdJ3mCfOBw,3466
9
9
  starmallow/docs.py,sha256=eA39LunVMEoPU5ge4qxm2eiJbrFTUSUu5EhG1L_LKxk,6268
10
- starmallow/endpoint.py,sha256=TFpNIqSvRbgI_NFKGzE6vq0bkqioBrVyprngzVR6J3w,15816
10
+ starmallow/endpoint.py,sha256=YdEtRKF1Z-p_KXoqJJNP_9nEwOzvgxYoMI3Rly7ECrM,15888
11
11
  starmallow/endpoints.py,sha256=UrwVZCxbmWI20iNtJ0oXxo4d3-y12TjsOGs_jnStTiU,939
12
12
  starmallow/exception_handlers.py,sha256=gr2qLYWEtsIEH28n7OreEiiLVz6Y7b6osRyS9esJbBk,891
13
13
  starmallow/exceptions.py,sha256=vabtPJkTmtCdC8_2OPBE8Osz0v0KxaSOX6IWf1jgNkc,872
14
14
  starmallow/fields.py,sha256=arrTabCYoJFZcoY69EZTBH3YUg7CUSr3-zYLiAjYLTM,1238
15
15
  starmallow/params.py,sha256=-f6ut1p_w_I8lyEgRciJxUtHmoVXKAD2Ug4KuRjPlJA,8661
16
16
  starmallow/request_resolver.py,sha256=kT7B0BWSqBP_9Jl865Ya_Op9I3W4FyfBxXjvtucWT_0,10785
17
+ starmallow/requests.py,sha256=o_yNhH9WJ35uAGuoXa5_gihevHeaveOvkP525dbwQSU,843
17
18
  starmallow/responses.py,sha256=k2pf_m21ykf_FECdODUz400pMucMJJf_Zm8TXFujvaU,2012
18
- starmallow/routing.py,sha256=sy7wbWc20zDjxo5CIR4j1VXlyKsan6i8zPpql0RFrNw,39203
19
+ starmallow/routing.py,sha256=Uxnlkl4eIs56TZ8VAgNmlh-K89ku1Xu_ROR5S8x-rkg,45128
19
20
  starmallow/schema_generator.py,sha256=BKtXVQoNFWoAIEtiRNylWls_7nyFIshy3_myooogjoI,17806
20
21
  starmallow/serializers.py,sha256=rBEKMNgONgz_bai12uDvAEMCI_aEFGsqMSeIoWtlrOI,12514
21
22
  starmallow/types.py,sha256=8GXWjvzXQhF5NMHf14fbid6uErxVd1Xk_w2I4FoUgZ4,717
@@ -33,7 +34,7 @@ starmallow/security/http.py,sha256=cpGjM1kFDq3i_bOY96kMkf4cspBUxFkkET9lTK3NA-0,6
33
34
  starmallow/security/oauth2.py,sha256=1nv1580PY4cwgu5gzpQCf2MfMNv2Cfv05753AUHPOhQ,10005
34
35
  starmallow/security/open_id_connect_url.py,sha256=IPsL2YzWc2mPwJbrUn6oFRTi7uRAG6mR62CGwmzBs1k,1399
35
36
  starmallow/security/utils.py,sha256=bd8T0YM7UQD5ATKucr1bNtAvz_Y3__dVNAv5UebiPvc,293
36
- starmallow-0.3.9.dist-info/METADATA,sha256=SFjwx09PtLdQKW40bKWwSIuYobGkUGuav6EVvWBoxIk,5651
37
- starmallow-0.3.9.dist-info/WHEEL,sha256=y1bSCq4r5i4nMmpXeUJMqs3ipKvkZObrIXSvJHm1qCI,87
38
- starmallow-0.3.9.dist-info/licenses/LICENSE.md,sha256=QelyGgOzch8CXzy6HrYwHh7nmj0rlWkDA0YzmZ3CPaY,1084
39
- starmallow-0.3.9.dist-info/RECORD,,
37
+ starmallow-0.5.0.dist-info/METADATA,sha256=M7Y6uK8nmKdGATGmuX96mPkyvlAlww0xnASnYcddDkE,5651
38
+ starmallow-0.5.0.dist-info/WHEEL,sha256=y1bSCq4r5i4nMmpXeUJMqs3ipKvkZObrIXSvJHm1qCI,87
39
+ starmallow-0.5.0.dist-info/licenses/LICENSE.md,sha256=QelyGgOzch8CXzy6HrYwHh7nmj0rlWkDA0YzmZ3CPaY,1084
40
+ starmallow-0.5.0.dist-info/RECORD,,