qena-shared-lib 0.1.8__py3-none-any.whl → 0.1.9__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.
@@ -121,8 +121,8 @@ def handle_http_service_error(
121
121
  if exception.logstash_logging:
122
122
  logstash_logger_method(
123
123
  message=exception.message,
124
- tags=exception.tags or tags,
125
- extra=exception.extra or extra,
124
+ tags=tags,
125
+ extra=extra,
126
126
  exception=exception if exception.extract_exc_info else None,
127
127
  )
128
128
  else:
qena_shared_lib/http.py CHANGED
@@ -10,7 +10,6 @@ from fastapi.types import IncEx
10
10
 
11
11
  __all__ = [
12
12
  "api_controller",
13
- "ApiController",
14
13
  "ControllerBase",
15
14
  "delete",
16
15
  "get",
@@ -63,20 +62,19 @@ class RouteHandlerMeta:
63
62
  openapi_extra: dict[str, Any] | None = None
64
63
 
65
64
 
66
- class ApiController:
67
- def __init__(
68
- self,
69
- prefix: str | None = None,
70
- *,
71
- tags: list[str | Enum] | None = None,
72
- dependencies: Sequence[Depends] | None = None,
73
- default_response_class: type[Response] = JSONResponse,
74
- responses: dict[int | str, dict[str, Any]] | None = None,
75
- redirect_slashes: bool = True,
76
- deprecated: bool | None = None,
77
- include_in_schema: bool = True,
78
- ):
79
- self._api_router = APIRouter(
65
+ def api_controller(
66
+ prefix: str | None = None,
67
+ *,
68
+ tags: list[str | Enum] | None = None,
69
+ dependencies: Sequence[Depends] | None = None,
70
+ default_response_class: type[Response] = JSONResponse,
71
+ responses: dict[int | str, dict[str, Any]] | None = None,
72
+ redirect_slashes: bool = True,
73
+ deprecated: bool | None = None,
74
+ include_in_schema: bool = True,
75
+ ) -> Callable[["ControllerBase"], "ControllerBase"]:
76
+ def annotate_class(api_controller_class: ControllerBase):
77
+ router = APIRouter(
80
78
  prefix=prefix or "",
81
79
  tags=tags,
82
80
  dependencies=dependencies,
@@ -87,33 +85,11 @@ class ApiController:
87
85
  include_in_schema=include_in_schema,
88
86
  )
89
87
 
90
- def __call__(self, controller: type[AC]) -> type[AC]:
91
- setattr(controller, API_CONTROLLER_ATTRIBUTE, self._api_router)
92
-
93
- return controller
88
+ setattr(api_controller_class, API_CONTROLLER_ATTRIBUTE, router)
94
89
 
90
+ return api_controller_class
95
91
 
96
- def api_controller(
97
- prefix: str | None = None,
98
- *,
99
- tags: list[str | Enum] | None = None,
100
- dependencies: Sequence[Depends] | None = None,
101
- default_response_class: type[Response] = JSONResponse,
102
- responses: dict[int | str, dict[str, Any]] | None = None,
103
- redirect_slashes: bool = True,
104
- deprecated: bool | None = None,
105
- include_in_schema: bool = True,
106
- ) -> ApiController:
107
- return ApiController(
108
- prefix=prefix,
109
- tags=tags,
110
- dependencies=dependencies,
111
- default_response_class=default_response_class,
112
- responses=responses,
113
- redirect_slashes=redirect_slashes,
114
- deprecated=deprecated,
115
- include_in_schema=include_in_schema,
116
- )
92
+ return annotate_class
117
93
 
118
94
 
119
95
  def get(
@@ -87,8 +87,8 @@ def handle_rabbit_mq_service_exception(
87
87
  if exception.logstash_logging:
88
88
  logstash_logger_method(
89
89
  message=exception.message,
90
- tags=exception.tags or tags,
91
- extra=exception.extra or extra,
90
+ tags=tags,
91
+ extra=extra,
92
92
  exception=exception if exception.extract_exc_info else None,
93
93
  )
94
94
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qena-shared-lib
3
- Version: 0.1.8
3
+ Version: 0.1.9
4
4
  Summary: A shared tools for other services
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: cronsim==2.6
@@ -40,6 +40,9 @@ A shared tools for other services. It includes.
40
40
  To create fastapi app.
41
41
 
42
42
  ``` py
43
+ from qena_shared_lib.application import Builder, Environment
44
+
45
+
43
46
  def main() -> FastAPI:
44
47
  builder = (
45
48
  Builder()
@@ -63,6 +66,11 @@ $ uvicorn --factory main:main
63
66
  ### Lifespan
64
67
 
65
68
  ``` py
69
+ from contextlib import asynccontextmanager
70
+
71
+ from fastapi import FastAPI
72
+
73
+
66
74
  @asynccontextmanager
67
75
  def lifespan(app: FastAPI):
68
76
  ...
@@ -83,6 +91,16 @@ def main() -> FastAPI:
83
91
  ### Dependencies
84
92
 
85
93
  ``` py
94
+ class EmailService:
95
+ def __init__(self):
96
+ ...
97
+
98
+
99
+ class Database:
100
+ def __init__(self):
101
+ ...
102
+
103
+
86
104
  def main() -> FastAPI:
87
105
  ...
88
106
 
@@ -95,7 +113,10 @@ def main() -> FastAPI:
95
113
  ### Controllers
96
114
 
97
115
  ``` py
98
- @ApiController("/users")
116
+ from qena_shared_lib.http import ControllerBase, api_controller, post
117
+
118
+
119
+ @api_controller("/users")
99
120
  class UserController(ControllerBase):
100
121
 
101
122
  def __init__(self, email_service: EmailService):
@@ -112,11 +133,18 @@ def main() -> FastAPI:
112
133
  builder.with_controllers([
113
134
  UserController
114
135
  ])
136
+
137
+ ...
115
138
  ```
116
139
 
117
140
  ### Routers
118
141
 
119
142
  ``` py
143
+ from fastapi import APIRouter
144
+
145
+ from qena_shared_lib.dependencies.http import DependsOn
146
+
147
+
120
148
  router = APIRouter(prefix="/auth")
121
149
 
122
150
 
@@ -153,6 +181,9 @@ def main() -> FastAPI:
153
181
  ## Logstash
154
182
 
155
183
  ``` py
184
+ from qena_shared_lib.logstash import BaseLogstashSender, HTTPSender, # TCPSender
185
+
186
+
156
187
  @asynccontextmanager
157
188
  async def lifespan(app: FastAPI):
158
189
  logstash = get_service(BaseLogstashSender)
@@ -203,6 +234,9 @@ def log_message(
203
234
  To create rabbitmq connection manager.
204
235
 
205
236
  ``` py
237
+ from qena_shared_lib.rabbitmq import ListenerBase, consume, consumer
238
+
239
+
206
240
  @asynccontextmanager
207
241
  async def lifespan(app: FastAPI):
208
242
  rabbitmq = get_service(RabbitMqManager)
@@ -214,7 +248,7 @@ async def lifespan(app: FastAPI):
214
248
  rabbitmq.disconnect()
215
249
 
216
250
 
217
- @Consumer("UserQueue")
251
+ @consumer("UserQueue")
218
252
  class UserConsumer(ListenerBase):
219
253
 
220
254
  def __init__(self, db: Database):
@@ -249,7 +283,7 @@ def main() -> FastAPI:
249
283
  async def store_user(
250
284
  rabbitmq: Annotated[
251
285
  RabbitMqManager,
252
- DependsOne(RabbitMqManager)
286
+ DependsOn(RabbitMqManager)
253
287
  ],
254
288
  user: User,
255
289
  )
@@ -266,7 +300,7 @@ async def store_user(
266
300
  async def get_user(
267
301
  rabbitmq: Annotated[
268
302
  RabbitMqManager,
269
- DependsOne(RabbitMqManager)
303
+ DependsOn(RabbitMqManager)
270
304
  ],
271
305
  user_id: str,
272
306
  )
@@ -281,7 +315,10 @@ async def get_user(
281
315
  ### Flow control
282
316
 
283
317
  ``` py
284
- @Consumer("UserQueue")
318
+ from qena_shared_lib.rabbitmq import ... , ListenerContext
319
+
320
+
321
+ @consumer("UserQueue")
285
322
  class UserConsumer(ListenerBase):
286
323
 
287
324
  @consume()
@@ -299,7 +336,10 @@ class UserConsumer(ListenerBase):
299
336
  Optionally it is possible to reply to rpc calls, through.
300
337
 
301
338
  ``` py
302
- @RpcWorker("UserQueue")
339
+ from qena_shared_lib.rabbitmq import ... , rpc_worker
340
+
341
+
342
+ @rpc_worker("UserQueue")
303
343
  class UserWorker(ListenerBase):
304
344
 
305
345
  @execute()
@@ -311,10 +351,92 @@ class UserWorker(ListenerBase):
311
351
  ...
312
352
  ```
313
353
 
354
+ ### Retry consumer
355
+
356
+ Consumer can retry to consumer a message in an event of failure.
357
+
358
+ ``` py
359
+ from qena_shared_lib.rabbitmq import (
360
+ BackoffRetryDelay,
361
+ FixedRetryDelay,
362
+ RabbitMqManager,
363
+ RetryDelayJitter,
364
+ RetryPolicy,
365
+ )
366
+
367
+
368
+ @consumer(
369
+ queue="UserQueue",
370
+ # can be defined for consumer of specific queue
371
+ retry_policy=RetryPolicy(
372
+ exceptions=(AMQPError,),
373
+ max_retry=5,
374
+ retry_delay_strategy=FixedRetryDelay(
375
+ retry_delay=2
376
+ ),
377
+ retry_delay_jitter=RetryDelayJitter(min=0.5, max=5.0),
378
+ )
379
+ )
380
+ class UserConsumer(ListenerBase):
381
+
382
+ @consume(
383
+ # for specific target
384
+ retry_policy=RetryPolicy(
385
+ exceptions=(AMQPError,),
386
+ max_retry=5,
387
+ retry_delay_strategy=FixedRetryDelay(
388
+ retry_delay=2
389
+ ),
390
+ retry_delay_jitter=RetryDelayJitter(min=0.5, max=5.0),
391
+ )
392
+ )
393
+ async def store_user(self, ctx: ListenerContext, user: User):
394
+ ...
395
+
396
+ await ctx.flow_control.request(10)
397
+
398
+ ...
399
+
400
+
401
+ def main() -> FastAPI:
402
+ ...
403
+
404
+ rabbitmq = RabbitMqManager(
405
+ logstash=logstash,
406
+ container=builder.container,
407
+ # or globally for all consumers
408
+ listener_global_retry_policy=RetryPolicy(
409
+ exceptions=(AMQPError,),
410
+ max_retry=10,
411
+ retry_delay_strategy=BackoffRetryDelay(
412
+ multiplier=1.5, min=2, max=10
413
+ ),
414
+ retry_delay_jitter=RetryDelayJitter(min=0.5, max=5.0),
415
+ match_by_cause=True,
416
+ ),
417
+ )
418
+
419
+ rabbitmq.include_listener(UserConsumer)
420
+ builder.add_singleton(
421
+ service=RabbitMqManager,
422
+ instance=rabbitmq,
423
+ )
424
+ ```
425
+
426
+
314
427
 
315
428
  ## Scheduler
316
429
 
317
430
  ``` py
431
+ from qena_shared_lib.scheduler import (
432
+ ScheduleManager,
433
+ # Scheduler,
434
+ SchedulerBase,
435
+ schedule,
436
+ scheduler,
437
+ )
438
+
439
+
318
440
  @asynccontextmanager
319
441
  async def lifespan(app: FastAPI):
320
442
  schedule_manager = get_service(ScheduleManager)
@@ -326,7 +448,7 @@ async def lifespan(app: FastAPI):
326
448
  schedule_manager.stop()
327
449
 
328
450
 
329
- @Scheduler()
451
+ @scheduler()
330
452
  class TaskScheduler(SchedulerBase):
331
453
 
332
454
  def __init__(self, db: Database)
@@ -367,6 +489,9 @@ def main() -> FastAPI:
367
489
  ## Background
368
490
 
369
491
  ``` py
492
+ from qena_shared_lib.background import Background
493
+
494
+
370
495
  @asynccontextmanager
371
496
  async def lifespan(app: FastAPI):
372
497
  background = get_service(Background)
@@ -410,7 +535,10 @@ async def process_data(
410
535
  ### Password hasher
411
536
 
412
537
  ``` py
413
- @ApiController("/users")
538
+ from qena_shared_lib.security import PasswordHasher
539
+
540
+
541
+ @api_controller("/users")
414
542
  class UserController(ControllerBase):
415
543
 
416
544
  def __init__(self, password_hasher: PasswordHasher):
@@ -439,6 +567,9 @@ def main() -> FastAPI:
439
567
  ### JWT
440
568
 
441
569
  ``` py
570
+ from qena_shared_lib.security import JwtAdapter
571
+
572
+
442
573
  @ApiController("/users")
443
574
  class UserController(ControllerBase):
444
575
 
@@ -478,7 +609,10 @@ def main() -> FastAPI:
478
609
  ### ACL
479
610
 
480
611
  ``` py
481
- @ApiController("/users")
612
+ from qena_shared_lib.security import Authorization
613
+
614
+
615
+ @api_controller("/users")
482
616
  class UserController(ControllerBase):
483
617
 
484
618
  @post()
@@ -486,7 +620,7 @@ class UserController(ControllerBase):
486
620
  self,
487
621
  user: Annotated[
488
622
  UserInfo,
489
- EndpointACL(
623
+ Authorization(
490
624
  user_type="ADMIN",
491
625
  persmission=[
492
626
  "READ"
@@ -501,7 +635,7 @@ class UserController(ControllerBase):
501
635
  async def get_users(
502
636
  user: Annotated[
503
637
  UserInfo,
504
- EndpointACL("ADMIN")
638
+ Authorization("ADMIN")
505
639
  ]
506
640
  )
507
641
  ...
@@ -1,9 +1,9 @@
1
1
  qena_shared_lib/__init__.py,sha256=WokKEFaMNow6h2ZY_fyB-tiYnic-Ed6K6kyzKgg3Rlw,371
2
2
  qena_shared_lib/application.py,sha256=LyMgCrp1wqUfHsQktybWW-fZG-XugxZ6_Bafhp3gRls,5472
3
3
  qena_shared_lib/background.py,sha256=upWeJ747-4S4xsBOR-P2_oNar1YiaxCpnLhEDvnArmQ,3097
4
- qena_shared_lib/exception_handlers.py,sha256=DqE6nyYzn-TeJw3KDeDrfJiSmEogZ0WEY2-VId6rogM,6225
4
+ qena_shared_lib/exception_handlers.py,sha256=qAXb8yE06yJRO2Qe9tfTu9D2DJhqgvX5wsc1_OPOjX4,6188
5
5
  qena_shared_lib/exceptions.py,sha256=GEGcSNaNMhFCdgCl6crNrrw-tmzPD-2Jz6MnpMvqXIU,9419
6
- qena_shared_lib/http.py,sha256=EQ19rf9-qu6MhD3FCrPYayjcEEyDQL0JVYpstVGOO2c,24572
6
+ qena_shared_lib/http.py,sha256=ZWbCV0C4SOKasfGhMLDcqZpZgIUUA_rEPAooeBkKieo,23846
7
7
  qena_shared_lib/logging.py,sha256=JL6bAmkK1BJA84rZNpvDEmrec3dogQRpSug4fj1Omkw,1618
8
8
  qena_shared_lib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  qena_shared_lib/scheduler.py,sha256=wVLgHM0cCBZhqSmOAgEa5McEJSSTHfoMAEIrFGI0tT0,11946
@@ -19,11 +19,11 @@ qena_shared_lib/logstash/_tcp_sender.py,sha256=ncvc3wYxOJPkB7c_0W8vboaxMRJ_1UYGd
19
19
  qena_shared_lib/rabbitmq/__init__.py,sha256=HKGd-CfbqNkTMDzF9dd-ZQlj5fQfnO5S9qzMcILy8xk,1151
20
20
  qena_shared_lib/rabbitmq/_base.py,sha256=S7jo4NbtAO8rzr7mBwhxRJrSmzrypOalrJChfrQ6-6I,23098
21
21
  qena_shared_lib/rabbitmq/_channel.py,sha256=R0xzZvLkeE4YWsyLhx8bPDramsTFDZIcCgfsDyFmSB4,5429
22
- qena_shared_lib/rabbitmq/_exception_handlers.py,sha256=k9G7tQ5v9-9HnWH47RTX4W8rsjY8bqWBljLWtAKhTyk,4566
22
+ qena_shared_lib/rabbitmq/_exception_handlers.py,sha256=gLkPFxQUW9faL8RApPT-Bqmn-fQ134q1ZvKInfRKjuo,4529
23
23
  qena_shared_lib/rabbitmq/_listener.py,sha256=1w5JAgwYy0xIU4kEZwL_KHyudfUWeqMWSXX5UdZ-p7M,46210
24
24
  qena_shared_lib/rabbitmq/_pool.py,sha256=MGubA-nYxxjgsZRF1yK4UBkzUy65t-suh0W1jZDk5q8,1952
25
25
  qena_shared_lib/rabbitmq/_publisher.py,sha256=Dr2u1IELCboCbozjTRsRtyQh7TRaJvujPsp74TF7utA,2434
26
26
  qena_shared_lib/rabbitmq/_rpc_client.py,sha256=eNzKpU_BvfDTR3udiSNtjJ4MaJqHrIOxMItKHA8aJbo,8909
27
- qena_shared_lib-0.1.8.dist-info/METADATA,sha256=UhHaHwUtUsGtTeKuk6Zv7KSV94oKCyfRv5HMhb4xxDs,8488
28
- qena_shared_lib-0.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
- qena_shared_lib-0.1.8.dist-info/RECORD,,
27
+ qena_shared_lib-0.1.9.dist-info/METADATA,sha256=DJuWOWR6IvgDxREbIXdIK3TQJUNfcAtFPOmQdooaZO4,11232
28
+ qena_shared_lib-0.1.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
+ qena_shared_lib-0.1.9.dist-info/RECORD,,