qena-shared-lib 0.1.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.
@@ -0,0 +1,631 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+ from typing import Any, Callable, Sequence, TypeVar
4
+
5
+ from fastapi import APIRouter, Response
6
+ from fastapi.datastructures import Default
7
+ from fastapi.params import Depends
8
+ from fastapi.responses import JSONResponse
9
+ from fastapi.types import IncEx
10
+
11
+ __all__ = [
12
+ "ApiController",
13
+ "ControllerBase",
14
+ "delete",
15
+ "get",
16
+ "head",
17
+ "options",
18
+ "patch",
19
+ "post",
20
+ "put",
21
+ "trace",
22
+ ]
23
+
24
+ AC = TypeVar("AC")
25
+ API_CONTROLLER_ATTRIBUTE = "__api_controller_router__"
26
+ ROUTE_HANDLER_ATTRIBUTE = "__route_handler_meta__"
27
+
28
+
29
+ class HTTPMethods(Enum):
30
+ GET = 0
31
+ PUT = 1
32
+ POST = 2
33
+ DELETE = 3
34
+ OPTIONS = 4
35
+ HEAD = 5
36
+ PATCH = 6
37
+ TRACE = 7
38
+
39
+
40
+ @dataclass
41
+ class RouteHandlerMeta:
42
+ method: HTTPMethods
43
+ path: str | None = None
44
+ response_model: Any | None = Default(None)
45
+ status_code: int | None = None
46
+ tags: list[str | Enum] | None = None
47
+ dependencies: Sequence[Depends] | None = None
48
+ summary: str | None = None
49
+ description: str | None = None
50
+ response_description: str = "Successful Response"
51
+ responses: dict[int | str, dict[str, Any]] | None = None
52
+ deprecated: bool | None = None
53
+ response_model_include: IncEx | None = None
54
+ response_model_exclude: IncEx | None = None
55
+ response_model_by_alias: bool = True
56
+ response_model_exclude_unset: bool = False
57
+ response_model_exclude_defaults: bool = False
58
+ response_model_exclude_none: bool = False
59
+ include_in_schema: bool = True
60
+ response_class: type[Response] = Default(JSONResponse)
61
+ name: str | None = None
62
+ openapi_extra: dict[str, Any] | None = None
63
+
64
+
65
+ class ApiController:
66
+ def __init__(
67
+ self,
68
+ prefix: str | None = None,
69
+ *,
70
+ tags: list[str | Enum] | None = None,
71
+ dependencies: Sequence[Depends] | None = None,
72
+ default_response_class: type[Response] = Default(JSONResponse),
73
+ responses: dict[int | str, dict[str, Any]] | None = None,
74
+ redirect_slashes: bool = True,
75
+ deprecated: bool | None = None,
76
+ include_in_schema: bool = True,
77
+ ):
78
+ self._api_router = APIRouter(
79
+ prefix=prefix or "",
80
+ tags=tags,
81
+ dependencies=dependencies,
82
+ default_response_class=default_response_class,
83
+ responses=responses,
84
+ redirect_slashes=redirect_slashes,
85
+ deprecated=deprecated,
86
+ include_in_schema=include_in_schema,
87
+ )
88
+
89
+ def __call__(self, controller: type[AC]) -> type[AC]:
90
+ setattr(controller, API_CONTROLLER_ATTRIBUTE, self._api_router)
91
+
92
+ return controller
93
+
94
+
95
+ def get(
96
+ path: str | None = None,
97
+ *,
98
+ response_model: Any | None = Default(None),
99
+ status_code: int | None = None,
100
+ tags: list[str | Enum] | None = None,
101
+ dependencies: Sequence[Depends] | None = None,
102
+ summary: str | None = None,
103
+ description: str | None = None,
104
+ response_description: str = "Successful Response",
105
+ responses: dict[int | str, dict[str, Any]] | None = None,
106
+ deprecated: bool | None = None,
107
+ response_model_include: IncEx | None = None,
108
+ response_model_exclude: IncEx | None = None,
109
+ response_model_by_alias: bool = True,
110
+ response_model_exclude_unset: bool = False,
111
+ response_model_exclude_defaults: bool = False,
112
+ response_model_exclude_none: bool = False,
113
+ include_in_schema: bool = True,
114
+ response_class: type[Response] = Default(JSONResponse),
115
+ name: str | None = None,
116
+ openapi_extra: dict[str, Any] | None = None,
117
+ ) -> Callable[[Callable], Callable]:
118
+ def wrapper(route_handler: Callable) -> Callable:
119
+ setattr(
120
+ route_handler,
121
+ ROUTE_HANDLER_ATTRIBUTE,
122
+ RouteHandlerMeta(
123
+ method=HTTPMethods.GET,
124
+ path=path,
125
+ response_model=response_model,
126
+ status_code=status_code,
127
+ tags=tags,
128
+ dependencies=dependencies,
129
+ summary=summary,
130
+ description=description,
131
+ response_description=response_description,
132
+ responses=responses,
133
+ deprecated=deprecated,
134
+ response_model_include=response_model_include,
135
+ response_model_exclude=response_model_exclude,
136
+ response_model_by_alias=response_model_by_alias,
137
+ response_model_exclude_unset=response_model_exclude_unset,
138
+ response_model_exclude_defaults=response_model_exclude_defaults,
139
+ response_model_exclude_none=response_model_exclude_none,
140
+ include_in_schema=include_in_schema,
141
+ response_class=response_class,
142
+ name=name,
143
+ openapi_extra=openapi_extra,
144
+ ),
145
+ )
146
+
147
+ return route_handler
148
+
149
+ return wrapper
150
+
151
+
152
+ def put(
153
+ path: str | None = None,
154
+ *,
155
+ response_model: Any | None = Default(None),
156
+ status_code: int | None = None,
157
+ tags: list[str | Enum] | None = None,
158
+ dependencies: Sequence[Depends] | None = None,
159
+ summary: str | None = None,
160
+ description: str | None = None,
161
+ response_description: str = "Successful Response",
162
+ responses: dict[int | str, dict[str, Any]] | None = None,
163
+ deprecated: bool | None = None,
164
+ response_model_include: IncEx | None = None,
165
+ response_model_exclude: IncEx | None = None,
166
+ response_model_by_alias: bool = True,
167
+ response_model_exclude_unset: bool = False,
168
+ response_model_exclude_defaults: bool = False,
169
+ response_model_exclude_none: bool = False,
170
+ include_in_schema: bool = True,
171
+ response_class: type[Response] = Default(JSONResponse),
172
+ name: str | None = None,
173
+ openapi_extra: dict[str, Any] | None = None,
174
+ ) -> Callable[[Callable], Callable]:
175
+ def wrapper(route_handler: Callable) -> Callable:
176
+ setattr(
177
+ route_handler,
178
+ ROUTE_HANDLER_ATTRIBUTE,
179
+ RouteHandlerMeta(
180
+ method=HTTPMethods.PUT,
181
+ path=path,
182
+ response_model=response_model,
183
+ status_code=status_code,
184
+ tags=tags,
185
+ dependencies=dependencies,
186
+ summary=summary,
187
+ description=description,
188
+ response_description=response_description,
189
+ responses=responses,
190
+ deprecated=deprecated,
191
+ response_model_include=response_model_include,
192
+ response_model_exclude=response_model_exclude,
193
+ response_model_by_alias=response_model_by_alias,
194
+ response_model_exclude_unset=response_model_exclude_unset,
195
+ response_model_exclude_defaults=response_model_exclude_defaults,
196
+ response_model_exclude_none=response_model_exclude_none,
197
+ include_in_schema=include_in_schema,
198
+ response_class=response_class,
199
+ name=name,
200
+ openapi_extra=openapi_extra,
201
+ ),
202
+ )
203
+
204
+ return route_handler
205
+
206
+ return wrapper
207
+
208
+
209
+ def post(
210
+ path: str | None = None,
211
+ *,
212
+ response_model: Any | None = Default(None),
213
+ status_code: int | None = None,
214
+ tags: list[str | Enum] | None = None,
215
+ dependencies: Sequence[Depends] | None = None,
216
+ summary: str | None = None,
217
+ description: str | None = None,
218
+ response_description: str = "Successful Response",
219
+ responses: dict[int | str, dict[str, Any]] | None = None,
220
+ deprecated: bool | None = None,
221
+ response_model_include: IncEx | None = None,
222
+ response_model_exclude: IncEx | None = None,
223
+ response_model_by_alias: bool = True,
224
+ response_model_exclude_unset: bool = False,
225
+ response_model_exclude_defaults: bool = False,
226
+ response_model_exclude_none: bool = False,
227
+ include_in_schema: bool = True,
228
+ response_class: type[Response] = Default(JSONResponse),
229
+ name: str | None = None,
230
+ openapi_extra: dict[str, Any] | None = None,
231
+ ) -> Callable[[Callable], Callable]:
232
+ def wrapper(route_handler: Callable) -> Callable:
233
+ setattr(
234
+ route_handler,
235
+ ROUTE_HANDLER_ATTRIBUTE,
236
+ RouteHandlerMeta(
237
+ method=HTTPMethods.POST,
238
+ path=path,
239
+ response_model=response_model,
240
+ status_code=status_code,
241
+ tags=tags,
242
+ dependencies=dependencies,
243
+ summary=summary,
244
+ description=description,
245
+ response_description=response_description,
246
+ responses=responses,
247
+ deprecated=deprecated,
248
+ response_model_include=response_model_include,
249
+ response_model_exclude=response_model_exclude,
250
+ response_model_by_alias=response_model_by_alias,
251
+ response_model_exclude_unset=response_model_exclude_unset,
252
+ response_model_exclude_defaults=response_model_exclude_defaults,
253
+ response_model_exclude_none=response_model_exclude_none,
254
+ include_in_schema=include_in_schema,
255
+ response_class=response_class,
256
+ name=name,
257
+ openapi_extra=openapi_extra,
258
+ ),
259
+ )
260
+
261
+ return route_handler
262
+
263
+ return wrapper
264
+
265
+
266
+ def delete(
267
+ path: str | None = None,
268
+ *,
269
+ response_model: Any | None = Default(None),
270
+ status_code: int | None = None,
271
+ tags: list[str | Enum] | None = None,
272
+ dependencies: Sequence[Depends] | None = None,
273
+ summary: str | None = None,
274
+ description: str | None = None,
275
+ response_description: str = "Successful Response",
276
+ responses: dict[int | str, dict[str, Any]] | None = None,
277
+ deprecated: bool | None = None,
278
+ response_model_include: IncEx | None = None,
279
+ response_model_exclude: IncEx | None = None,
280
+ response_model_by_alias: bool = True,
281
+ response_model_exclude_unset: bool = False,
282
+ response_model_exclude_defaults: bool = False,
283
+ response_model_exclude_none: bool = False,
284
+ include_in_schema: bool = True,
285
+ response_class: type[Response] = Default(JSONResponse),
286
+ name: str | None = None,
287
+ openapi_extra: dict[str, Any] | None = None,
288
+ ) -> Callable[[Callable], Callable]:
289
+ def wrapper(route_handler: Callable) -> Callable:
290
+ setattr(
291
+ route_handler,
292
+ ROUTE_HANDLER_ATTRIBUTE,
293
+ RouteHandlerMeta(
294
+ method=HTTPMethods.DELETE,
295
+ path=path,
296
+ response_model=response_model,
297
+ status_code=status_code,
298
+ tags=tags,
299
+ dependencies=dependencies,
300
+ summary=summary,
301
+ description=description,
302
+ response_description=response_description,
303
+ responses=responses,
304
+ deprecated=deprecated,
305
+ response_model_include=response_model_include,
306
+ response_model_exclude=response_model_exclude,
307
+ response_model_by_alias=response_model_by_alias,
308
+ response_model_exclude_unset=response_model_exclude_unset,
309
+ response_model_exclude_defaults=response_model_exclude_defaults,
310
+ response_model_exclude_none=response_model_exclude_none,
311
+ include_in_schema=include_in_schema,
312
+ response_class=response_class,
313
+ name=name,
314
+ openapi_extra=openapi_extra,
315
+ ),
316
+ )
317
+
318
+ return route_handler
319
+
320
+ return wrapper
321
+
322
+
323
+ def options(
324
+ path: str | None = None,
325
+ *,
326
+ response_model: Any | None = Default(None),
327
+ status_code: int | None = None,
328
+ tags: list[str | Enum] | None = None,
329
+ dependencies: Sequence[Depends] | None = None,
330
+ summary: str | None = None,
331
+ description: str | None = None,
332
+ response_description: str = "Successful Response",
333
+ responses: dict[int | str, dict[str, Any]] | None = None,
334
+ deprecated: bool | None = None,
335
+ response_model_include: IncEx | None = None,
336
+ response_model_exclude: IncEx | None = None,
337
+ response_model_by_alias: bool = True,
338
+ response_model_exclude_unset: bool = False,
339
+ response_model_exclude_defaults: bool = False,
340
+ response_model_exclude_none: bool = False,
341
+ include_in_schema: bool = True,
342
+ response_class: type[Response] = Default(JSONResponse),
343
+ name: str | None = None,
344
+ openapi_extra: dict[str, Any] | None = None,
345
+ ) -> Callable[[Callable], Callable]:
346
+ def wrapper(route_handler: Callable) -> Callable:
347
+ setattr(
348
+ route_handler,
349
+ ROUTE_HANDLER_ATTRIBUTE,
350
+ RouteHandlerMeta(
351
+ method=HTTPMethods.OPTIONS,
352
+ path=path,
353
+ response_model=response_model,
354
+ status_code=status_code,
355
+ tags=tags,
356
+ dependencies=dependencies,
357
+ summary=summary,
358
+ description=description,
359
+ response_description=response_description,
360
+ responses=responses,
361
+ deprecated=deprecated,
362
+ response_model_include=response_model_include,
363
+ response_model_exclude=response_model_exclude,
364
+ response_model_by_alias=response_model_by_alias,
365
+ response_model_exclude_unset=response_model_exclude_unset,
366
+ response_model_exclude_defaults=response_model_exclude_defaults,
367
+ response_model_exclude_none=response_model_exclude_none,
368
+ include_in_schema=include_in_schema,
369
+ response_class=response_class,
370
+ name=name,
371
+ openapi_extra=openapi_extra,
372
+ ),
373
+ )
374
+
375
+ return route_handler
376
+
377
+ return wrapper
378
+
379
+
380
+ def head(
381
+ path: str | None = None,
382
+ *,
383
+ response_model: Any | None = Default(None),
384
+ status_code: int | None = None,
385
+ tags: list[str | Enum] | None = None,
386
+ dependencies: Sequence[Depends] | None = None,
387
+ summary: str | None = None,
388
+ description: str | None = None,
389
+ response_description: str = "Successful Response",
390
+ responses: dict[int | str, dict[str, Any]] | None = None,
391
+ deprecated: bool | None = None,
392
+ response_model_include: IncEx | None = None,
393
+ response_model_exclude: IncEx | None = None,
394
+ response_model_by_alias: bool = True,
395
+ response_model_exclude_unset: bool = False,
396
+ response_model_exclude_defaults: bool = False,
397
+ response_model_exclude_none: bool = False,
398
+ include_in_schema: bool = True,
399
+ response_class: type[Response] = Default(JSONResponse),
400
+ name: str | None = None,
401
+ openapi_extra: dict[str, Any] | None = None,
402
+ ) -> Callable[[Callable], Callable]:
403
+ def wrapper(route_handler: Callable) -> Callable:
404
+ setattr(
405
+ route_handler,
406
+ ROUTE_HANDLER_ATTRIBUTE,
407
+ RouteHandlerMeta(
408
+ method=HTTPMethods.HEAD,
409
+ path=path,
410
+ response_model=response_model,
411
+ status_code=status_code,
412
+ tags=tags,
413
+ dependencies=dependencies,
414
+ summary=summary,
415
+ description=description,
416
+ response_description=response_description,
417
+ responses=responses,
418
+ deprecated=deprecated,
419
+ response_model_include=response_model_include,
420
+ response_model_exclude=response_model_exclude,
421
+ response_model_by_alias=response_model_by_alias,
422
+ response_model_exclude_unset=response_model_exclude_unset,
423
+ response_model_exclude_defaults=response_model_exclude_defaults,
424
+ response_model_exclude_none=response_model_exclude_none,
425
+ include_in_schema=include_in_schema,
426
+ response_class=response_class,
427
+ name=name,
428
+ openapi_extra=openapi_extra,
429
+ ),
430
+ )
431
+
432
+ return route_handler
433
+
434
+ return wrapper
435
+
436
+
437
+ def patch(
438
+ path: str | None = None,
439
+ *,
440
+ response_model: Any | None = Default(None),
441
+ status_code: int | None = None,
442
+ tags: list[str | Enum] | None = None,
443
+ dependencies: Sequence[Depends] | None = None,
444
+ summary: str | None = None,
445
+ description: str | None = None,
446
+ response_description: str = "Successful Response",
447
+ responses: dict[int | str, dict[str, Any]] | None = None,
448
+ deprecated: bool | None = None,
449
+ response_model_include: IncEx | None = None,
450
+ response_model_exclude: IncEx | None = None,
451
+ response_model_by_alias: bool = True,
452
+ response_model_exclude_unset: bool = False,
453
+ response_model_exclude_defaults: bool = False,
454
+ response_model_exclude_none: bool = False,
455
+ include_in_schema: bool = True,
456
+ response_class: type[Response] = Default(JSONResponse),
457
+ name: str | None = None,
458
+ openapi_extra: dict[str, Any] | None = None,
459
+ ) -> Callable[[Callable], Callable]:
460
+ def wrapper(route_handler: Callable) -> Callable:
461
+ setattr(
462
+ route_handler,
463
+ ROUTE_HANDLER_ATTRIBUTE,
464
+ RouteHandlerMeta(
465
+ method=HTTPMethods.PATCH,
466
+ path=path,
467
+ response_model=response_model,
468
+ status_code=status_code,
469
+ tags=tags,
470
+ dependencies=dependencies,
471
+ summary=summary,
472
+ description=description,
473
+ response_description=response_description,
474
+ responses=responses,
475
+ deprecated=deprecated,
476
+ response_model_include=response_model_include,
477
+ response_model_exclude=response_model_exclude,
478
+ response_model_by_alias=response_model_by_alias,
479
+ response_model_exclude_unset=response_model_exclude_unset,
480
+ response_model_exclude_defaults=response_model_exclude_defaults,
481
+ response_model_exclude_none=response_model_exclude_none,
482
+ include_in_schema=include_in_schema,
483
+ response_class=response_class,
484
+ name=name,
485
+ openapi_extra=openapi_extra,
486
+ ),
487
+ )
488
+
489
+ return route_handler
490
+
491
+ return wrapper
492
+
493
+
494
+ def trace(
495
+ path: str | None = None,
496
+ *,
497
+ response_model: Any | None = Default(None),
498
+ status_code: int | None = None,
499
+ tags: list[str | Enum] | None = None,
500
+ dependencies: Sequence[Depends] | None = None,
501
+ summary: str | None = None,
502
+ description: str | None = None,
503
+ response_description: str = "Successful Response",
504
+ responses: dict[int | str, dict[str, Any]] | None = None,
505
+ deprecated: bool | None = None,
506
+ response_model_include: IncEx | None = None,
507
+ response_model_exclude: IncEx | None = None,
508
+ response_model_by_alias: bool = True,
509
+ response_model_exclude_unset: bool = False,
510
+ response_model_exclude_defaults: bool = False,
511
+ response_model_exclude_none: bool = False,
512
+ include_in_schema: bool = True,
513
+ response_class: type[Response] = Default(JSONResponse),
514
+ name: str | None = None,
515
+ openapi_extra: dict[str, Any] | None = None,
516
+ ) -> Callable[[Callable], Callable]:
517
+ def wrapper(route_handler: Callable) -> Callable:
518
+ setattr(
519
+ route_handler,
520
+ ROUTE_HANDLER_ATTRIBUTE,
521
+ RouteHandlerMeta(
522
+ method=HTTPMethods.TRACE,
523
+ path=path,
524
+ response_model=response_model,
525
+ status_code=status_code,
526
+ tags=tags,
527
+ dependencies=dependencies,
528
+ summary=summary,
529
+ description=description,
530
+ response_description=response_description,
531
+ responses=responses,
532
+ deprecated=deprecated,
533
+ response_model_include=response_model_include,
534
+ response_model_exclude=response_model_exclude,
535
+ response_model_by_alias=response_model_by_alias,
536
+ response_model_exclude_unset=response_model_exclude_unset,
537
+ response_model_exclude_defaults=response_model_exclude_defaults,
538
+ response_model_exclude_none=response_model_exclude_none,
539
+ include_in_schema=include_in_schema,
540
+ response_class=response_class,
541
+ name=name,
542
+ openapi_extra=openapi_extra,
543
+ ),
544
+ )
545
+
546
+ return route_handler
547
+
548
+ return wrapper
549
+
550
+
551
+ class ControllerBase:
552
+ def get_api_router(self) -> APIRouter:
553
+ api_router = getattr(self, API_CONTROLLER_ATTRIBUTE, None)
554
+
555
+ if api_router is None:
556
+ raise AttributeError(
557
+ f"{self.__class__.__name__} not a api controller, possibly no annotated with either `ApiController`"
558
+ )
559
+
560
+ return api_router
561
+
562
+ def register_route_handlers(self) -> APIRouter:
563
+ api_router = self.get_api_router()
564
+
565
+ for attribute_name in dir(self):
566
+ attribute = getattr(self, attribute_name, None)
567
+
568
+ if attribute is None:
569
+ continue
570
+
571
+ route_handler_meta = getattr(
572
+ attribute, ROUTE_HANDLER_ATTRIBUTE, None
573
+ )
574
+
575
+ if route_handler_meta is None:
576
+ continue
577
+
578
+ if not isinstance(route_handler_meta, RouteHandlerMeta):
579
+ raise TypeError(
580
+ f"expected `{ROUTE_HANDLER_ATTRIBUTE}` to be of type `RouteHandlerMeta`, got {type(route_handler_meta)}"
581
+ )
582
+
583
+ api_router_method = None
584
+
585
+ match route_handler_meta.method:
586
+ case HTTPMethods.GET:
587
+ api_router_method = api_router.get
588
+ case HTTPMethods.PUT:
589
+ api_router_method = api_router.put
590
+ case HTTPMethods.POST:
591
+ api_router_method = api_router.post
592
+ case HTTPMethods.DELETE:
593
+ api_router_method = api_router.delete
594
+ case HTTPMethods.OPTIONS:
595
+ api_router_method = api_router.options
596
+ case HTTPMethods.HEAD:
597
+ api_router_method = api_router.head
598
+ case HTTPMethods.PATCH:
599
+ api_router_method = api_router.patch
600
+ case HTTPMethods.TRACE:
601
+ api_router_method = api_router.trace
602
+
603
+ if api_router_method is None:
604
+ raise ValueError(
605
+ f"api router method {route_handler_meta.method} not supported"
606
+ )
607
+
608
+ api_router_method(
609
+ path=route_handler_meta.path or "",
610
+ response_model=route_handler_meta.response_model,
611
+ status_code=route_handler_meta.status_code,
612
+ tags=route_handler_meta.tags,
613
+ dependencies=route_handler_meta.dependencies,
614
+ summary=route_handler_meta.summary,
615
+ description=route_handler_meta.description,
616
+ response_description=route_handler_meta.response_description,
617
+ responses=route_handler_meta.responses,
618
+ deprecated=route_handler_meta.deprecated,
619
+ response_model_include=route_handler_meta.response_model_include,
620
+ response_model_exclude=route_handler_meta.response_model_exclude,
621
+ response_model_by_alias=route_handler_meta.response_model_by_alias,
622
+ response_model_exclude_unset=route_handler_meta.response_model_exclude_unset,
623
+ response_model_exclude_defaults=route_handler_meta.response_model_exclude_defaults,
624
+ response_model_exclude_none=route_handler_meta.response_model_exclude_none,
625
+ include_in_schema=route_handler_meta.include_in_schema,
626
+ response_class=route_handler_meta.response_class,
627
+ name=route_handler_meta.name,
628
+ openapi_extra=route_handler_meta.openapi_extra,
629
+ )(attribute)
630
+
631
+ return api_router
@@ -0,0 +1,63 @@
1
+ from functools import lru_cache
2
+ from logging import (
3
+ INFO,
4
+ Formatter,
5
+ Handler,
6
+ Logger,
7
+ StreamHandler,
8
+ getLogger,
9
+ )
10
+ from os import environ
11
+ from typing import Optional
12
+
13
+ __all__ = [
14
+ "LoggerProvider",
15
+ ]
16
+
17
+ ROOT_LOGGER_NAME = environ.get("LOGGER_NAME") or "qena_shared_lib"
18
+
19
+
20
+ class LoggerProvider:
21
+ def __init__(self):
22
+ logger = self.get_logger()
23
+
24
+ logger.setLevel(INFO)
25
+
26
+ @lru_cache
27
+ @staticmethod
28
+ def default() -> "LoggerProvider":
29
+ return LoggerProvider()
30
+
31
+ @lru_cache
32
+ def get_logger(self, name: str | None = None) -> Logger:
33
+ logger_name = ROOT_LOGGER_NAME
34
+
35
+ if name:
36
+ logger_name = f"{ROOT_LOGGER_NAME}.{name.strip('.')}"
37
+
38
+ logger = getLogger(logger_name)
39
+ handlers = [handler.__class__ for handler in logger.handlers]
40
+
41
+ if logger.parent is not None:
42
+ self._check_handler(handlers=handlers, logger=logger.parent)
43
+
44
+ if StreamHandler not in handlers:
45
+ stream_handler = StreamHandler()
46
+
47
+ stream_handler.setFormatter(
48
+ Formatter(
49
+ "[ %(levelname)-8s] %(name)s [ %(filename)s:%(lineno)d in %(funcName)s ] --- %(message)s"
50
+ )
51
+ )
52
+ logger.addHandler(stream_handler)
53
+
54
+ return logger
55
+
56
+ def _check_handler(
57
+ self, handlers: list[type[Handler]], logger: Optional[Logger] = None
58
+ ):
59
+ if logger is None:
60
+ return handlers
61
+
62
+ handlers.extend([handler.__class__ for handler in logger.handlers])
63
+ self._check_handler(handlers=handlers, logger=logger.parent)