edri 2025.11.1rc4__tar.gz → 2025.12.1rc1__tar.gz

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.
Files changed (168) hide show
  1. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/PKG-INFO +2 -1
  2. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/abstract/__init__.py +10 -3
  3. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/abstract/manager/manager_base.py +38 -6
  4. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/config/setting.py +4 -0
  5. edri-2025.12.1rc1/edri/utility/cache.py +19 -0
  6. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri.egg-info/PKG-INFO +2 -1
  7. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri.egg-info/SOURCES.txt +1 -0
  8. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri.egg-info/requires.txt +1 -0
  9. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/setup.py +3 -2
  10. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/abstract/manager/test_manager_base.py +1 -0
  11. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/README.md +0 -0
  12. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/__init__.py +0 -0
  13. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/abstract/manager/__init__.py +0 -0
  14. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/abstract/manager/manager_priority_base.py +0 -0
  15. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/abstract/manager/worker.py +0 -0
  16. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/abstract/worker/__init__.py +0 -0
  17. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/abstract/worker/worker.py +0 -0
  18. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/abstract/worker/worker_process.py +0 -0
  19. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/abstract/worker/worker_thread.py +0 -0
  20. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/__init__.py +0 -0
  21. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/broker.py +0 -0
  22. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/dataclass/__init__.py +0 -0
  23. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/dataclass/api_event.py +0 -0
  24. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/dataclass/client.py +0 -0
  25. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/dataclass/file.py +0 -0
  26. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/extensions/__init__.py +0 -0
  27. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/extensions/url_extension.py +0 -0
  28. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/extensions/url_prefix.py +0 -0
  29. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/handlers/__init__.py +0 -0
  30. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/handlers/base_handler.py +0 -0
  31. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/handlers/html_handler.py +0 -0
  32. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/handlers/http_handler.py +0 -0
  33. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/handlers/rest_handler.py +0 -0
  34. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/handlers/websocket_handler.py +0 -0
  35. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/listener.py +0 -0
  36. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/middleware.py +0 -0
  37. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/static_pages/documentation.j2 +0 -0
  38. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/static_pages/health_check_status.j2 +0 -0
  39. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/static_pages/status_300.j2 +0 -0
  40. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/static_pages/status_400.j2 +0 -0
  41. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/api/static_pages/status_500.j2 +0 -0
  42. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/config/__init__.py +0 -0
  43. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/config/constant.py +0 -0
  44. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/dataclass/__init__.py +0 -0
  45. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/dataclass/directive/__init__.py +0 -0
  46. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/dataclass/directive/base.py +0 -0
  47. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/dataclass/directive/html.py +0 -0
  48. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/dataclass/directive/http.py +0 -0
  49. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/dataclass/event.py +0 -0
  50. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/dataclass/health_checker.py +0 -0
  51. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/dataclass/injection.py +0 -0
  52. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/dataclass/response.py +0 -0
  53. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/__init__.py +0 -0
  54. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/__init__.py +0 -0
  55. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/client/__init__.py +0 -0
  56. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/client/documentation.py +0 -0
  57. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/client/register.py +0 -0
  58. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/client/unregister.py +0 -0
  59. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/group/__init__.py +0 -0
  60. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/group/client.py +0 -0
  61. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/group/manage.py +0 -0
  62. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/manage/__init__.py +0 -0
  63. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/manage/list_registered.py +0 -0
  64. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/manage/register.py +0 -0
  65. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/manage/unregister.py +0 -0
  66. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/api/manage/unregister_all.py +0 -0
  67. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/__init__.py +0 -0
  68. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/group/__init__.py +0 -0
  69. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/group/manager.py +0 -0
  70. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/group/router.py +0 -0
  71. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/group/scheduler.py +0 -0
  72. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/group/store.py +0 -0
  73. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/group/switch.py +0 -0
  74. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/group/test.py +0 -0
  75. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/manager/__init__.py +0 -0
  76. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/manager/restart.py +0 -0
  77. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/manager/stream_close.py +0 -0
  78. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/manager/stream_create.py +0 -0
  79. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/manager/stream_message.py +0 -0
  80. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/manager/worker_quit.py +0 -0
  81. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/__init__.py +0 -0
  82. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/demands.py +0 -0
  83. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/health_check.py +0 -0
  84. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/last_events.py +0 -0
  85. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/send_from.py +0 -0
  86. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/subscribe.py +0 -0
  87. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/subscribe_connector.py +0 -0
  88. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/subscribed_external.py +0 -0
  89. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/subscribed_new.py +0 -0
  90. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/unsubscribe.py +0 -0
  91. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/router/unsubscribe_all.py +0 -0
  92. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/scheduler/__init__.py +0 -0
  93. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/scheduler/cancel.py +0 -0
  94. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/scheduler/set.py +0 -0
  95. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/scheduler/set_or_update.py +0 -0
  96. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/scheduler/update.py +0 -0
  97. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/store/__init__.py +0 -0
  98. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/store/delete.py +0 -0
  99. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/store/get.py +0 -0
  100. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/store/get_callback.py +0 -0
  101. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/events/edri/store/set.py +0 -0
  102. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/router/__init__.py +0 -0
  103. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/router/cache.py +0 -0
  104. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/router/connector/__init__.py +0 -0
  105. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/router/connector/connector.py +0 -0
  106. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/router/connector/socket.py +0 -0
  107. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/router/health_checker.py +0 -0
  108. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/router/router.py +0 -0
  109. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/switch/__init__.py +0 -0
  110. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/switch/connection.py +0 -0
  111. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/switch/forwarder.py +0 -0
  112. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/switch/receiver.py +0 -0
  113. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/switch/sender.py +0 -0
  114. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/switch/switch.py +0 -0
  115. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/__init__.py +0 -0
  116. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/function.py +0 -0
  117. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/json_encoder.py +0 -0
  118. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/manager/__init__.py +0 -0
  119. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/manager/scheduler.py +0 -0
  120. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/manager/store.py +0 -0
  121. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/normalized_default_dict.py +0 -0
  122. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/queue.py +0 -0
  123. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/shared_memory_pipe.py +0 -0
  124. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/storage.py +0 -0
  125. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/transformation.py +0 -0
  126. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/validation.py +0 -0
  127. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri/utility/watcher.py +0 -0
  128. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri.egg-info/dependency_links.txt +0 -0
  129. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/edri.egg-info/top_level.txt +0 -0
  130. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/setup.cfg +0 -0
  131. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/__init__.py +0 -0
  132. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/abstract/__init__.py +0 -0
  133. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/abstract/manager/__init__.py +0 -0
  134. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/abstract/manager/test_manager_base_priority.py +0 -0
  135. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/abstract/worker/__init__.py +0 -0
  136. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/abstract/worker/test_worker.py +0 -0
  137. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/api/__init__.py +0 -0
  138. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/api/handlers/__init__.py +0 -0
  139. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/api/handlers/test_base_handler.py +0 -0
  140. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/api/handlers/test_html_handler.py +0 -0
  141. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/api/handlers/test_http_handler.py +0 -0
  142. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/api/test_broker.py +0 -0
  143. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/dataclass/__init__.py +0 -0
  144. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/dataclass/event/__init__.py +0 -0
  145. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/dataclass/event/test_event.py +0 -0
  146. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/dataclass/event/test_event_init.py +0 -0
  147. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/dataclass/event/test_response.py +0 -0
  148. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/events/__init__.py +0 -0
  149. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/events/test/__init__.py +0 -0
  150. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/events/test/event_request.py +0 -0
  151. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/events/test/ping.py +0 -0
  152. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/events/test/ping2.py +0 -0
  153. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/router/__init__.py +0 -0
  154. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/router/test_cache.py +0 -0
  155. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/router/test_health_checker.py +0 -0
  156. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/router/test_router.py +0 -0
  157. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/test_edri_init.py +0 -0
  158. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/__init__.py +0 -0
  159. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/manager/__init__.py +0 -0
  160. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/manager/test_scheduler.py +0 -0
  161. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/manager/test_store.py +0 -0
  162. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/test_function.py +0 -0
  163. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/test_json_encoder.py +0 -0
  164. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/test_normalized_default_dict.py +0 -0
  165. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/test_shared_memory_pipe.py +0 -0
  166. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/test_storage.py +0 -0
  167. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/test_transformation.py +0 -0
  168. {edri-2025.11.1rc4 → edri-2025.12.1rc1}/tests/utility/test_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edri
3
- Version: 2025.11.1rc4
3
+ Version: 2025.12.1rc1
4
4
  Summary: Event Driven Routing Infrastructure
5
5
  Author: Marek Olšan
6
6
  Author-email: marek.olsan@gmail.com
@@ -26,6 +26,7 @@ Requires-Dist: watchdog>=6
26
26
  Requires-Dist: websockets>=14
27
27
  Requires-Dist: posix-ipc>=1.2.0
28
28
  Requires-Dist: markdown>=3.0
29
+ Requires-Dist: pytz>=2024.1
29
30
  Provides-Extra: uvicorn
30
31
  Requires-Dist: uvicorn[standard]>=0.32.0; extra == "uvicorn"
31
32
  Provides-Extra: hypercorn
@@ -2,9 +2,16 @@ from .manager.manager_base import ManagerBase
2
2
  from .manager.manager_priority_base import ManagerPriorityBase
3
3
 
4
4
 
5
- def request(func):
6
- func.__purpose__ = "request"
7
- return func
5
+ def request(func=None, /, *, cache: str = None):
6
+ def wrapper(func):
7
+ func.__purpose__ = "request"
8
+ func.__cache__ = cache
9
+ return func
10
+
11
+ if func is None:
12
+ return wrapper
13
+
14
+ return wrapper(func)
8
15
 
9
16
 
10
17
  def response(func):
@@ -1,7 +1,7 @@
1
1
  from abc import ABC, ABCMeta
2
2
  from copy import deepcopy
3
- from dataclasses import dataclass
4
3
  from datetime import datetime
4
+ from http import HTTPMethod
5
5
  from importlib import invalidate_caches, import_module, reload
6
6
  from inspect import signature, Signature, ismethod, isfunction
7
7
  from logging import getLogger, Logger
@@ -12,16 +12,21 @@ from random import randint
12
12
  from time import sleep
13
13
  from traceback import format_exc
14
14
  from types import UnionType
15
- from typing import Optional, Type, Tuple, Callable, Union, Generic, TypeVar, Never, get_args, Iterable, get_origin
15
+ from typing import Optional, Type, Tuple, Callable, Union, TypeVar, Never, get_args, Iterable, get_origin
16
16
 
17
17
  from edri.abstract.manager.worker import Worker
18
- from edri.abstract.worker import WorkerThread, WorkerProcess
18
+ from edri.abstract.worker import WorkerProcess
19
+ from edri.api.dataclass.api_event import api_events
20
+ from edri.config.setting import API_CACHE_CONTROL
21
+ from edri.dataclass.directive.http import NotModifiedResponseDirective, HeaderResponseDirective
19
22
  from edri.dataclass.event import Event
20
23
  from edri.dataclass.health_checker import Status
24
+ from edri.dataclass.response import ResponseStatus
21
25
  from edri.events.edri.group import Manager
22
26
  from edri.events.edri.manager import StreamCreate, StreamMessage, StreamClose, WorkerQuit, Restart
23
27
  from edri.events.edri.router import Subscribe, HealthCheck, UnsubscribeAll
24
28
  from edri.events.edri.store import Get
29
+ from edri.utility.cache import Cache
25
30
  from edri.utility.storage import Storage
26
31
 
27
32
  T = TypeVar("T", bound=Event)
@@ -30,11 +35,23 @@ T = TypeVar("T", bound=Event)
30
35
  class ManagerBaseMeta(ABCMeta):
31
36
 
32
37
  def __new__(mcls, name, bases, namespace, /, **kwargs):
38
+ namespace["_cache_keys"] = dict()
39
+ namespace["_cache_methods"] = dict()
33
40
  for attr_name, attr_value in list(namespace.items()):
34
- if callable(attr_value) and (
35
- purpose := getattr(attr_value, "__purpose__", None)): # Check for the decorator's marker
41
+ if not callable(attr_value):
42
+ continue
43
+ if purpose := getattr(attr_value, "__purpose__", None): # Check for the decorator's marker
36
44
  if purpose == "request":
37
45
  new_name = f"solve_req_{attr_name}"
46
+
47
+ if cache := getattr(attr_value, "__cache__", None):
48
+ namespace["_cache_keys"][attr_name] = cache
49
+ event = signature(attr_value).parameters["event"].annotation
50
+ for api_event in api_events:
51
+ if api_event.event == event:
52
+ break
53
+ namespace["_cache_methods"][event] = api_event.method
54
+
38
55
  elif purpose == "response":
39
56
  new_name = f"solve_res_{attr_name}"
40
57
  else:
@@ -109,6 +126,7 @@ class ManagerBase(ABC, Process, metaclass=ManagerBaseMeta):
109
126
  self._from_time = from_time
110
127
  self._store_get: Optional[Callable] = None
111
128
  self._exceptions: list[tuple[str, dict, Exception, str]] = []
129
+ self._cache: Cache
112
130
 
113
131
  def _subscribe(self) -> None:
114
132
  """
@@ -609,10 +627,24 @@ class ManagerBase(ABC, Process, metaclass=ManagerBaseMeta):
609
627
  self.resolve_unknown(event)
610
628
  else:
611
629
  had_response = event.has_response()
630
+ cache_key = self._cache_keys.get(resolver.__name__, None)
631
+ if cache_key:
632
+ etag = self._cache.tag(cache_key)
633
+ method = self._cache_methods[event.__class__]
634
+ if etag and hasattr(event, "etag") and event.etag and etag in event.etag and method == HTTPMethod.GET:
635
+ event.response.add_directive(NotModifiedResponseDirective())
636
+ self.router_queue.put(event)
637
+ return
612
638
  resolver(event)
613
639
  if not had_response and event.has_response() and event.response._changed:
614
640
  if event._switch:
615
641
  event._switch.received = False
642
+ if cache_key and etag:
643
+ if event.response.get_status() == ResponseStatus.OK and method in (HTTPMethod.POST, HTTPMethod.PUT, HTTPMethod.PATCH, HTTPMethod.DELETE):
644
+ self._cache.renew(cache_key)
645
+ else:
646
+ event.response.add_directive(HeaderResponseDirective(name="ETag", value=etag))
647
+ event.response.add_directive(HeaderResponseDirective(name="Cache-Control", value=API_CACHE_CONTROL))
616
648
  self.router_queue.put(event)
617
649
 
618
650
  def get_pipes(self) -> set[Connection]:
@@ -828,7 +860,7 @@ class ManagerBase(ABC, Process, metaclass=ManagerBaseMeta):
828
860
  """
829
861
  Hook method called after the manager process starts. Can be overridden to perform initialization tasks.
830
862
  """
831
- pass
863
+ self._cache = Cache()
832
864
 
833
865
  def quit(self) -> None:
834
866
  self.router_queue.close()
@@ -1,6 +1,8 @@
1
1
  from os import getenv
2
2
  from typing import Literal
3
3
 
4
+ from pytz import timezone
5
+
4
6
 
5
7
  def getenv_bool(name: str, default: bool) -> bool:
6
8
  """Read an environment variable as a boolean, fallback to default if unset."""
@@ -13,9 +15,11 @@ ENVIRONMENT: Literal["development", "production"] = "production" if getenv("ENVI
13
15
 
14
16
  HEALTH_CHECK_TIMEOUT = int(getenv("EDRI_HEALTH_CHECK_TIMEOUT", 10))
15
17
  HEALTH_CHECK_FAILURE_LIMIT = int(getenv("EDRI_HEALTH_CHECK_FAILURE_LIMIT", 3))
18
+ TIMEZONE = timezone(getenv("EDRI_TIMEZONE", "UTC"))
16
19
  API_RESPONSE_TIMEOUT = int(getenv("EDRI_API_RESPONSE_TIMEOUT", 60))
17
20
  API_RESPONSE_WRAPPED = getenv_bool("EDRI_API_RESPONSE_WRAPPED", True)
18
21
  API_RESPONSE_TIMING = getenv_bool("EDRI_API_RESPONSE_TIMING", ENVIRONMENT == 'development')
22
+ API_CACHE_CONTROL = getenv("EDRI_API_CACHE_CONTROL", "max-age=0, must-revalidate")
19
23
 
20
24
  SWITCH_KEY_LENGTH = int(getenv("EDRI_SWITCH_KEY_LENGTH ", 8))
21
25
  SWITCH_HOST = getenv("EDRI_SWITCH_HOST", "localhost")
@@ -0,0 +1,19 @@
1
+ from collections import defaultdict
2
+ from datetime import datetime
3
+ from pytz import timezone
4
+
5
+
6
+ class Cache:
7
+ timezone = timezone("Europe/Prague")
8
+
9
+ def __init__(self):
10
+ self.last_modified = defaultdict(lambda: datetime.now(tz=self.timezone))
11
+
12
+ def last_change(self, key: str) -> datetime:
13
+ return self.last_modified[key]
14
+
15
+ def tag(self, key: str) -> str:
16
+ return f"{key}-{int(self.last_change(key).timestamp())}"
17
+
18
+ def renew(self, key: str) -> None:
19
+ self.last_modified[key] = datetime.now(tz=self.timezone)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edri
3
- Version: 2025.11.1rc4
3
+ Version: 2025.12.1rc1
4
4
  Summary: Event Driven Routing Infrastructure
5
5
  Author: Marek Olšan
6
6
  Author-email: marek.olsan@gmail.com
@@ -26,6 +26,7 @@ Requires-Dist: watchdog>=6
26
26
  Requires-Dist: websockets>=14
27
27
  Requires-Dist: posix-ipc>=1.2.0
28
28
  Requires-Dist: markdown>=3.0
29
+ Requires-Dist: pytz>=2024.1
29
30
  Provides-Extra: uvicorn
30
31
  Requires-Dist: uvicorn[standard]>=0.32.0; extra == "uvicorn"
31
32
  Provides-Extra: hypercorn
@@ -112,6 +112,7 @@ edri/switch/receiver.py
112
112
  edri/switch/sender.py
113
113
  edri/switch/switch.py
114
114
  edri/utility/__init__.py
115
+ edri/utility/cache.py
115
116
  edri/utility/function.py
116
117
  edri/utility/json_encoder.py
117
118
  edri/utility/normalized_default_dict.py
@@ -8,6 +8,7 @@ watchdog>=6
8
8
  websockets>=14
9
9
  posix-ipc>=1.2.0
10
10
  markdown>=3.0
11
+ pytz>=2024.1
11
12
 
12
13
  [dev]
13
14
  uvicorn>=0.32.0
@@ -122,7 +122,7 @@ Whether you're building a real-time data processing system, a distributed servic
122
122
 
123
123
  setup(
124
124
  name='edri',
125
- version='2025.11.01rc4',
125
+ version='2025.12.01rc1',
126
126
  packages=find_packages(),
127
127
  description='Event Driven Routing Infrastructure',
128
128
  long_description=long_description,
@@ -140,7 +140,8 @@ setup(
140
140
  "watchdog>=6",
141
141
  "websockets>=14",
142
142
  "posix-ipc>=1.2.0",
143
- "markdown>=3.0"
143
+ "markdown>=3.0",
144
+ "pytz>=2024.1",
144
145
  ],
145
146
  extras_require={
146
147
  "uvicorn": ["uvicorn[standard]>=0.32.0"],
@@ -229,6 +229,7 @@ class TestManagerBase(unittest.TestCase):
229
229
  event_response = MagicMock(spec=Event)
230
230
  event_response._stream = None
231
231
  resolver = MagicMock(side_effect=add_response)
232
+ resolver.__name__ = "resolver"
232
233
 
233
234
  self.manager._requests[event.__class__] = resolver
234
235
  event.has_response.return_value = False
File without changes
File without changes