python-neva 3.3.0__tar.gz → 3.4.0__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 (147) hide show
  1. python_neva-3.4.0/CHANGELOG.md +20 -0
  2. {python_neva-3.3.0 → python_neva-3.4.0}/PKG-INFO +1 -1
  3. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/service_provider.py +4 -0
  4. python_neva-3.4.0/neva/events/contracts/__init__.py +20 -0
  5. python_neva-3.4.0/neva/events/contracts/dispatcher.py +47 -0
  6. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/dispatcher.py +6 -18
  7. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/event.pyi +4 -5
  8. {python_neva-3.3.0 → python_neva-3.4.0}/neva/testing/fakes.py +17 -9
  9. {python_neva-3.3.0 → python_neva-3.4.0}/pyproject.toml +1 -1
  10. python_neva-3.4.0/tests/arch/test_context.py +60 -0
  11. {python_neva-3.3.0 → python_neva-3.4.0}/tests/arch/test_extends.py +0 -1
  12. {python_neva-3.3.0 → python_neva-3.4.0}/uv.lock +1 -1
  13. python_neva-3.3.0/CHANGELOG.md +0 -115
  14. python_neva-3.3.0/neva/events/contracts/__init__.py +0 -7
  15. python_neva-3.3.0/neva/events/contracts/dispatcher.py +0 -24
  16. {python_neva-3.3.0 → python_neva-3.4.0}/.envrc +0 -0
  17. {python_neva-3.3.0 → python_neva-3.4.0}/.gitignore +0 -0
  18. {python_neva-3.3.0 → python_neva-3.4.0}/.pre-commit-config.yaml +0 -0
  19. {python_neva-3.3.0 → python_neva-3.4.0}/.python-version +0 -0
  20. {python_neva-3.3.0 → python_neva-3.4.0}/README.md +0 -0
  21. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/__init__.py +0 -0
  22. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/application.py +0 -0
  23. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/config.py +0 -0
  24. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/facade.py +0 -0
  25. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/integrations/__init__.py +0 -0
  26. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/integrations/faststream.py +0 -0
  27. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/markers.py +0 -0
  28. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/py.typed +0 -0
  29. {python_neva-3.3.0 → python_neva-3.4.0}/neva/arch/scopes.py +0 -0
  30. {python_neva-3.3.0 → python_neva-3.4.0}/neva/config/__init__.py +0 -0
  31. {python_neva-3.3.0 → python_neva-3.4.0}/neva/config/base_providers.py +0 -0
  32. {python_neva-3.3.0 → python_neva-3.4.0}/neva/config/loader.py +0 -0
  33. {python_neva-3.3.0 → python_neva-3.4.0}/neva/config/py.typed +0 -0
  34. {python_neva-3.3.0 → python_neva-3.4.0}/neva/config/repository.py +0 -0
  35. {python_neva-3.3.0 → python_neva-3.4.0}/neva/database/__init__.py +0 -0
  36. {python_neva-3.3.0 → python_neva-3.4.0}/neva/database/config.py +0 -0
  37. {python_neva-3.3.0 → python_neva-3.4.0}/neva/database/connection.py +0 -0
  38. {python_neva-3.3.0 → python_neva-3.4.0}/neva/database/manager.py +0 -0
  39. {python_neva-3.3.0 → python_neva-3.4.0}/neva/database/provider.py +0 -0
  40. {python_neva-3.3.0 → python_neva-3.4.0}/neva/database/py.typed +0 -0
  41. {python_neva-3.3.0 → python_neva-3.4.0}/neva/database/transaction.py +0 -0
  42. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/__init__.py +0 -0
  43. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/contracts/event.py +0 -0
  44. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/contracts/handler.py +0 -0
  45. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/contracts/listener.py +0 -0
  46. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/event.py +0 -0
  47. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/event_registry.py +0 -0
  48. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/listener.py +0 -0
  49. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/policy.py +0 -0
  50. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/provider.py +0 -0
  51. {python_neva-3.3.0 → python_neva-3.4.0}/neva/events/py.typed +0 -0
  52. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/__init__.py +0 -0
  53. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/instrumentation/__init__.py +0 -0
  54. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/instrumentation/sqlalchemy.py +0 -0
  55. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/logging/__init__.py +0 -0
  56. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/logging/manager.py +0 -0
  57. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/logging/provider.py +0 -0
  58. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/middleware/__init__.py +0 -0
  59. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/middleware/correlation.py +0 -0
  60. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/middleware/profiler.py +0 -0
  61. {python_neva-3.3.0 → python_neva-3.4.0}/neva/obs/py.typed +0 -0
  62. {python_neva-3.3.0 → python_neva-3.4.0}/neva/polyfactory/__init__.py +0 -0
  63. {python_neva-3.3.0 → python_neva-3.4.0}/neva/polyfactory/factories.py +0 -0
  64. {python_neva-3.3.0 → python_neva-3.4.0}/neva/polyfactory/persistence.py +0 -0
  65. {python_neva-3.3.0 → python_neva-3.4.0}/neva/polyfactory/py.typed +0 -0
  66. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/__init__.py +0 -0
  67. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/encryption/__init__.py +0 -0
  68. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/encryption/encrypter.py +0 -0
  69. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/encryption/protocol.py +0 -0
  70. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/hashing/__init__.py +0 -0
  71. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/hashing/config.py +0 -0
  72. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/hashing/hash_manager.py +0 -0
  73. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/hashing/hashers/__init__.py +0 -0
  74. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/hashing/hashers/argon2.py +0 -0
  75. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/hashing/hashers/bcrypt.py +0 -0
  76. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/hashing/hashers/protocol.py +0 -0
  77. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/provider.py +0 -0
  78. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/py.typed +0 -0
  79. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/tokens/__init__.py +0 -0
  80. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/tokens/generate_token.py +0 -0
  81. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/tokens/hash_token.py +0 -0
  82. {python_neva-3.3.0 → python_neva-3.4.0}/neva/security/tokens/verify_token.py +0 -0
  83. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/__init__.py +0 -0
  84. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/accessors.py +0 -0
  85. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/__init__.py +0 -0
  86. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/app.py +0 -0
  87. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/app.pyi +0 -0
  88. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/config.py +0 -0
  89. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/config.pyi +0 -0
  90. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/crypt.py +0 -0
  91. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/crypt.pyi +0 -0
  92. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/db.py +0 -0
  93. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/db.pyi +0 -0
  94. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/event.py +0 -0
  95. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/hash.py +0 -0
  96. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/hash.pyi +0 -0
  97. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/log.py +0 -0
  98. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/facade/log.pyi +0 -0
  99. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/py.typed +0 -0
  100. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/results.py +0 -0
  101. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/strategy.py +0 -0
  102. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/strconv.py +0 -0
  103. {python_neva-3.3.0 → python_neva-3.4.0}/neva/support/time.py +0 -0
  104. {python_neva-3.3.0 → python_neva-3.4.0}/neva/testing/__init__.py +0 -0
  105. {python_neva-3.3.0 → python_neva-3.4.0}/neva/testing/fixtures.py +0 -0
  106. {python_neva-3.3.0 → python_neva-3.4.0}/neva/testing/py.typed +0 -0
  107. {python_neva-3.3.0 → python_neva-3.4.0}/neva/testing/test_case.py +0 -0
  108. {python_neva-3.3.0 → python_neva-3.4.0}/ruff.toml +0 -0
  109. {python_neva-3.3.0 → python_neva-3.4.0}/scripts/retag-with-changelog.sh +0 -0
  110. {python_neva-3.3.0 → python_neva-3.4.0}/tests/__init__.py +0 -0
  111. {python_neva-3.3.0 → python_neva-3.4.0}/tests/arch/__init__.py +0 -0
  112. {python_neva-3.3.0 → python_neva-3.4.0}/tests/arch/test_cache.py +0 -0
  113. {python_neva-3.3.0 → python_neva-3.4.0}/tests/arch/test_scope.py +0 -0
  114. {python_neva-3.3.0 → python_neva-3.4.0}/tests/config/__init__.py +0 -0
  115. {python_neva-3.3.0 → python_neva-3.4.0}/tests/config/test_loader.py +0 -0
  116. {python_neva-3.3.0 → python_neva-3.4.0}/tests/config/test_repository.py +0 -0
  117. {python_neva-3.3.0 → python_neva-3.4.0}/tests/conftest.py +0 -0
  118. {python_neva-3.3.0 → python_neva-3.4.0}/tests/database/__init__.py +0 -0
  119. {python_neva-3.3.0 → python_neva-3.4.0}/tests/database/test_connection_manager.py +0 -0
  120. {python_neva-3.3.0 → python_neva-3.4.0}/tests/database/test_database_manager.py +0 -0
  121. {python_neva-3.3.0 → python_neva-3.4.0}/tests/database/test_edge_cases.py +0 -0
  122. {python_neva-3.3.0 → python_neva-3.4.0}/tests/database/test_multi_connection.py +0 -0
  123. {python_neva-3.3.0 → python_neva-3.4.0}/tests/database/test_sqlalchemy_integration.py +0 -0
  124. {python_neva-3.3.0 → python_neva-3.4.0}/tests/database/test_transaction.py +0 -0
  125. {python_neva-3.3.0 → python_neva-3.4.0}/tests/database/test_transaction_context.py +0 -0
  126. {python_neva-3.3.0 → python_neva-3.4.0}/tests/database/test_transaction_registry.py +0 -0
  127. {python_neva-3.3.0 → python_neva-3.4.0}/tests/events/__init__.py +0 -0
  128. {python_neva-3.3.0 → python_neva-3.4.0}/tests/events/conftest.py +0 -0
  129. {python_neva-3.3.0 → python_neva-3.4.0}/tests/events/test_binding.py +0 -0
  130. {python_neva-3.3.0 → python_neva-3.4.0}/tests/events/test_deferred.py +0 -0
  131. {python_neva-3.3.0 → python_neva-3.4.0}/tests/events/test_dispatch.py +0 -0
  132. {python_neva-3.3.0 → python_neva-3.4.0}/tests/events/test_event.py +0 -0
  133. {python_neva-3.3.0 → python_neva-3.4.0}/tests/events/test_function_listener.py +0 -0
  134. {python_neva-3.3.0 → python_neva-3.4.0}/tests/events/test_immediate.py +0 -0
  135. {python_neva-3.3.0 → python_neva-3.4.0}/tests/events/test_listen_on_parent_class.py +0 -0
  136. {python_neva-3.3.0 → python_neva-3.4.0}/tests/obs/__init__.py +0 -0
  137. {python_neva-3.3.0 → python_neva-3.4.0}/tests/obs/test_correlation.py +0 -0
  138. {python_neva-3.3.0 → python_neva-3.4.0}/tests/obs/test_profiler.py +0 -0
  139. {python_neva-3.3.0 → python_neva-3.4.0}/tests/security/__init__.py +0 -0
  140. {python_neva-3.3.0 → python_neva-3.4.0}/tests/security/test_encrypter.py +0 -0
  141. {python_neva-3.3.0 → python_neva-3.4.0}/tests/security/test_hash_manager.py +0 -0
  142. {python_neva-3.3.0 → python_neva-3.4.0}/tests/testing/__init__.py +0 -0
  143. {python_neva-3.3.0 → python_neva-3.4.0}/tests/testing/test_event_fake.py +0 -0
  144. {python_neva-3.3.0 → python_neva-3.4.0}/tests/testing/test_facade_restore.py +0 -0
  145. {python_neva-3.3.0 → python_neva-3.4.0}/tests/testing/test_fixtures.py +0 -0
  146. {python_neva-3.3.0 → python_neva-3.4.0}/tests/testing/test_refresh_database.py +0 -0
  147. {python_neva-3.3.0 → python_neva-3.4.0}/tests/testing/test_test_case.py +0 -0
@@ -0,0 +1,20 @@
1
+ ## 3.4.0 (2026-06-10)
2
+
3
+ ### ✨ Features
4
+
5
+ - **arch**: new from_context method
6
+ - **tooling**: placeholder commit
7
+
8
+ ### 💚👷 CI & Build
9
+
10
+ - **tooling**: default to base tag version for commitizen
11
+
12
+ ### 📝💡 Documentation
13
+
14
+ - **project**: update doc
15
+ - **core**: placeholde readme
16
+
17
+ ### 🔧🔨📦️ Configuration, Scripts, Packages
18
+
19
+ - **tooling**: update changelog script
20
+ - **project**: update versioning config
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-neva
3
- Version: 3.3.0
3
+ Version: 3.4.0
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: aiosqlite>=0.20.0
@@ -175,6 +175,10 @@ class ServiceProvider(abc.ABC):
175
175
  _ = self.provider.decorate(source, provides=interface, scope=scope, when=when)
176
176
  return self
177
177
 
178
+ def from_context(self, interface: type, scope: Scope | None = None) -> None:
179
+ """Declares an interface as provided by the current context."""
180
+ _ = self.provider.from_context(provides=interface, scope=scope)
181
+
178
182
  @abc.abstractmethod
179
183
  def register(self) -> Result[Self, str]:
180
184
  """Register services into the application container.
@@ -0,0 +1,20 @@
1
+ from neva.events.contracts.dispatcher import (
2
+ AsyncBeforeDispatchHook,
3
+ BeforeDispatchHook,
4
+ EventDispatcher,
5
+ SyncBeforeDispatchHook,
6
+ )
7
+ from neva.events.contracts.event import Event
8
+ from neva.events.contracts.handler import EventHandler
9
+ from neva.events.contracts.listener import EventListener
10
+
11
+
12
+ __all__ = [
13
+ "AsyncBeforeDispatchHook",
14
+ "BeforeDispatchHook",
15
+ "Event",
16
+ "EventDispatcher",
17
+ "EventHandler",
18
+ "EventListener",
19
+ "SyncBeforeDispatchHook",
20
+ ]
@@ -0,0 +1,47 @@
1
+ from typing import Protocol, runtime_checkable
2
+
3
+ from neva.events.contracts.event import Event
4
+ from neva.events.contracts.listener import EventListener
5
+ from neva.support import Result
6
+
7
+
8
+ @runtime_checkable
9
+ class AsyncBeforeDispatchHook(Protocol):
10
+ """Asynchronous hook to be called before listeners are invoked."""
11
+
12
+ async def __call__(self, context: Event) -> None:
13
+ """Execute the hook."""
14
+
15
+
16
+ @runtime_checkable
17
+ class SyncBeforeDispatchHook(Protocol):
18
+ """Synchronous hook to be called before listeners are invoked."""
19
+
20
+ def __call__(self, context: Event) -> None:
21
+ """Execute the hook."""
22
+
23
+
24
+ BeforeDispatchHook = AsyncBeforeDispatchHook | SyncBeforeDispatchHook
25
+
26
+
27
+ class EventDispatcher(Protocol):
28
+ """Event dispatcher protocol."""
29
+
30
+ async def dispatch(
31
+ self,
32
+ event: Event,
33
+ ) -> list[Result[None, str]]:
34
+ """Dispatch an event to all registered listeners."""
35
+ ...
36
+
37
+ async def before_dispatch(self, hook: BeforeDispatchHook) -> None:
38
+ """Register a hook to be called before listeners are invoked."""
39
+ ...
40
+
41
+ def listen[T: Event](
42
+ self,
43
+ event_cls: type[T],
44
+ listener_cls: type[EventListener[T]],
45
+ ) -> None:
46
+ """Register a listener for an event."""
47
+ ...
@@ -1,6 +1,6 @@
1
1
  """Base implementation of the event dispatcher."""
2
2
 
3
- from typing import Callable, Protocol, override, runtime_checkable
3
+ from typing import override
4
4
 
5
5
  from neva.arch.application import Application
6
6
  from neva.database.manager import DatabaseManager
@@ -10,19 +10,6 @@ from neva.events.event_registry import EventRegistry
10
10
  from neva.support import Err, Nothing, Result, Some
11
11
 
12
12
 
13
- @runtime_checkable
14
- class AsyncBeforeDispatchHook(Protocol):
15
- async def __call__(self, context: contracts.Event) -> None: ...
16
-
17
-
18
- @runtime_checkable
19
- class SyncBeforeDispatchHook(Protocol):
20
- def __call__(self, context: contracts.Event) -> None: ...
21
-
22
-
23
- BeforeDispatchHook = AsyncBeforeDispatchHook | SyncBeforeDispatchHook
24
-
25
-
26
13
  class EventDispatcher(contracts.EventDispatcher):
27
14
  """Event dispatcher implementation."""
28
15
 
@@ -35,7 +22,7 @@ class EventDispatcher(contracts.EventDispatcher):
35
22
  self._registry: EventRegistry = registry
36
23
  self._app: Application = app
37
24
  self._db: DatabaseManager = db
38
- self._before_dispatch_hooks: list[BeforeDispatchHook] = []
25
+ self._before_dispatch_hooks: list[contracts.BeforeDispatchHook] = []
39
26
 
40
27
  async def _apply_before_dispatch(self, event: contracts.Event) -> None:
41
28
  """Extension hook called before listeners are invoked.
@@ -49,12 +36,13 @@ class EventDispatcher(contracts.EventDispatcher):
49
36
  """
50
37
  for hook in self._before_dispatch_hooks:
51
38
  match hook:
52
- case AsyncBeforeDispatchHook():
39
+ case contracts.AsyncBeforeDispatchHook():
53
40
  await hook(event)
54
- case SyncBeforeDispatchHook():
41
+ case contracts.SyncBeforeDispatchHook():
55
42
  hook(event)
56
43
 
57
- async def before_dispatch(self, hook: BeforeDispatchHook) -> None:
44
+ @override
45
+ async def before_dispatch(self, hook: contracts.BeforeDispatchHook) -> None:
58
46
  """Register a hook to be called before listeners are invoked."""
59
47
  self._before_dispatch_hooks.append(hook)
60
48
 
@@ -2,9 +2,8 @@
2
2
 
3
3
  from typing import override
4
4
 
5
- from neva import events
6
5
  from neva.arch import Facade
7
- from neva.events import EventListener
6
+ from neva.events import contracts
8
7
  from neva.support import Result
9
8
 
10
9
  class Event(Facade):
@@ -14,10 +13,10 @@ class Event(Facade):
14
13
  @override
15
14
  def get_facade_accessor(cls) -> type: ...
16
15
  @classmethod
17
- async def dispatch(cls, event: events.Event) -> list[Result[None, str]]: ...
16
+ async def dispatch(cls, event: contracts.Event) -> list[Result[None, str]]: ...
18
17
  @classmethod
19
- def listen[T: events.Event](
18
+ def listen[T: contracts.Event](
20
19
  cls,
21
20
  event_cls: type[T],
22
- listener_cls: type[EventListener[T]],
21
+ listener_cls: type[contracts.EventListener[T]],
23
22
  ) -> None: ...
@@ -1,19 +1,20 @@
1
1
  """Testing fakes for Neva facades."""
2
2
 
3
3
  from collections.abc import Callable
4
- from typing import Any
4
+ from typing import override
5
5
 
6
- from neva.events.event import Event as BaseEvent
6
+ from neva.events import contracts
7
7
  from neva.support import Result
8
8
 
9
9
 
10
- class EventFake:
10
+ class EventFake(contracts.EventDispatcher):
11
11
  """Recording fake for the Event facade. No listeners are dispatched."""
12
12
 
13
13
  def __init__(self) -> None:
14
- self._dispatched: list[BaseEvent[Any]] = []
14
+ self._dispatched: list[contracts.Event] = []
15
15
 
16
- async def dispatch(self, event: BaseEvent[Any]) -> list[Result[None, str]]:
16
+ @override
17
+ async def dispatch(self, event: contracts.Event) -> list[Result[None, str]]:
17
18
  """Record the event without invoking any listeners.
18
19
 
19
20
  Returns:
@@ -22,10 +23,17 @@ class EventFake:
22
23
  self._dispatched.append(event)
23
24
  return []
24
25
 
25
- def listen[T: BaseEvent[Any]](self, event_cls: type[T], listener_cls: type) -> None:
26
+ @override
27
+ def listen[T: contracts.Event](
28
+ self, event_cls: type[T], listener_cls: type[contracts.EventListener[T]]
29
+ ) -> None:
26
30
  """No-op."""
27
31
 
28
- def dispatched[E: BaseEvent[Any]](self, event_cls: type[E]) -> list[E]:
32
+ @override
33
+ async def before_dispatch(self, hook: contracts.BeforeDispatchHook) -> None:
34
+ """Register a hook to be called before listeners are invoked."""
35
+
36
+ def dispatched[E: contracts.Event](self, event_cls: type[E]) -> list[E]:
29
37
  """Return all recorded events of the given type.
30
38
 
31
39
  Returns:
@@ -33,7 +41,7 @@ class EventFake:
33
41
  """
34
42
  return [e for e in self._dispatched if isinstance(e, event_cls)]
35
43
 
36
- def assert_dispatched[E: BaseEvent[Any]](
44
+ def assert_dispatched[E: contracts.Event](
37
45
  self,
38
46
  event_cls: type[E],
39
47
  *,
@@ -61,7 +69,7 @@ class EventFake:
61
69
  if match is not None and not any(match(e) for e in matching):
62
70
  raise AssertionError(f"No {event_cls.__name__} matched the predicate")
63
71
 
64
- def assert_not_dispatched[E: BaseEvent[Any]](self, event_cls: type[E]) -> None:
72
+ def assert_not_dispatched[E: contracts.Event](self, event_cls: type[E]) -> None:
65
73
  """Assert that no event of the given type was dispatched.
66
74
 
67
75
  Args:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-neva"
3
- version = "3.3.0"
3
+ version = "3.4.0"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -0,0 +1,60 @@
1
+ from pathlib import Path
2
+ from typing import Self, override
3
+
4
+
5
+ from neva import arch
6
+ from neva.arch.scopes import Scope
7
+ from neva.support import Ok, Result
8
+ from neva.support.facade import Log
9
+ from neva.testing import TestCase
10
+
11
+
12
+ class RequestService:
13
+ """A service scoped to REQUEST."""
14
+
15
+ def __init__(self) -> None:
16
+ self.value: int = id(self)
17
+
18
+
19
+ class RequestScopedProvider(arch.ServiceProvider):
20
+ """Registers a REQUEST-scoped service."""
21
+
22
+ @override
23
+ def register(self) -> Result[Self, str]:
24
+ self.from_context(RequestService, scope=Scope.REQUEST)
25
+ return Ok(self)
26
+
27
+
28
+ def _make_config_dir(tmp_path: Path) -> Path:
29
+ config_dir = tmp_path / "config"
30
+ config_dir.mkdir()
31
+
32
+ _ = (config_dir / "app.py").write_text(
33
+ 'config = {"name": "TestApp", "debug": True}'
34
+ )
35
+ _ = (config_dir / "providers.py").write_text(
36
+ """
37
+ from tests.arch.test_context import RequestScopedProvider
38
+
39
+ config = {"providers": [RequestScopedProvider]}
40
+ """
41
+ )
42
+
43
+ return config_dir
44
+
45
+
46
+ class TestScopeEntersChildScope(TestCase):
47
+ @override
48
+ def create_config(self, tmp_path: Path) -> Path:
49
+ return _make_config_dir(tmp_path)
50
+
51
+ async def test_request_scoped_service_resolved_in_scope(self) -> None:
52
+ test_service = RequestService()
53
+ async with self.app.scope(
54
+ scope=Scope.REQUEST,
55
+ context={RequestService: test_service},
56
+ ):
57
+ new_service_result = self.app.make(RequestService)
58
+ new_service = new_service_result.unwrap()
59
+
60
+ assert new_service.value == test_service.value
@@ -1,4 +1,3 @@
1
- from dataclasses import dataclass
2
1
  import uuid
3
2
  from datetime import datetime
4
3
  from pathlib import Path
@@ -1733,7 +1733,7 @@ wheels = [
1733
1733
 
1734
1734
  [[package]]
1735
1735
  name = "python-neva"
1736
- version = "3.3.0"
1736
+ version = "3.4.0"
1737
1737
  source = { editable = "." }
1738
1738
  dependencies = [
1739
1739
  { name = "aiosqlite" },
@@ -1,115 +0,0 @@
1
- ## 3.3.0 (2026-06-05)
2
-
3
- ### ✨ Features
4
-
5
- - **tooling**: placeholder commit
6
-
7
- ### 💚👷 CI & Build
8
-
9
- - **tooling**: default to base tag version for commitizen
10
-
11
- ### 📝💡 Documentation
12
-
13
- - **project**: update doc
14
- - **core**: placeholde readme
15
-
16
- ### 🔧🔨📦️ Configuration, Scripts, Packages
17
-
18
- - **tooling**: update changelog script
19
- - **project**: update versioning config
20
-
21
- ## 3.2.0 (2026-06-05)
22
-
23
- ### ✨ Features
24
-
25
- - **tooling**: placeholder commit
26
-
27
- ### 🔧🔨📦️ Configuration, Scripts, Packages
28
-
29
- - **tooling**: update changelog script
30
- - **project**: update versioning config
31
-
32
- ### 📝💡 Documentation
33
-
34
- - **core**: placeholde readme
35
-
36
- ## 3.1.1 (2026-05-11)
37
-
38
- ### 📌➕⬇️➖⬆️ Dependencies
39
-
40
- - bump opt dep on neva-fastapi
41
-
42
- ## 3.1.0 (2026-05-11)
43
-
44
- ### ✨ Features
45
-
46
- - fixing db facade
47
- - Add eq method to Some / Ok
48
- - Deprecate register_engine, add register_connection
49
- - RefreshDatabase now properly use the testcase inner app
50
- - Add neva-fastapi as optional dependency
51
- - renaming of make / make_async for retrocompatibility
52
-
53
- ### 🐛🚑️ Fixes
54
-
55
- - Fix provider registration ordering
56
-
57
- ### ♻️ Refactorings
58
-
59
- - refactor of results / db toolkit
60
-
61
- ## 3.1.0a1 (2026-05-05)
62
-
63
- ### build
64
-
65
- - add commitizen + versioningit
66
-
67
- ### 💚👷 CI & Build
68
-
69
- - update perms on tag script
70
- - tweaking cz tags
71
- - remove auto-annotated tags
72
- - configure versioningit + cz
73
- - Fix cz config >>> ⏰ 1
74
- - add and configure cz_gitmoji >>> ⏰ 5m
75
-
76
- ### 📝💡 Documentation
77
-
78
- - clear changelog
79
-
80
- ### 🔧🔨📦️ Configuration, Scripts, Packages
81
-
82
- - improve auto-annotation of tags
83
- - Fix tagging script
84
-
85
- ## python-neva-v3.2.0 (2026-06-04)
86
-
87
- ### ✨ Features
88
-
89
- - **core**: add optional activator on all binding functions
90
- - **core**: remove scoping option from scoped, default to request scope
91
- - **core**: introduce conditional activation
92
- - **core**: add specifying interface in extends method
93
- - **core**: add extends feature
94
-
95
- ### build
96
-
97
- - **core**: remove redundant .envrc files
98
- - **core**: fixing version derivation
99
-
100
- ### docs
101
-
102
- - **core,-fastapi**: add contributing documentation
103
-
104
- ### feat
105
-
106
- - **core**: allow passing context data to scope method
107
- - **core**: add some utility functions for binding
108
- - **core**: add some utility functions for binding
109
- - **core**: add the possibility to turn off dependency caching
110
- - **core,-fastapi**: add .envrc file
111
-
112
- ### refactor
113
-
114
- - **core**: cleaning up type hints
115
- - **core**: update type hints on main application
@@ -1,7 +0,0 @@
1
- from neva.events.contracts.dispatcher import EventDispatcher
2
- from neva.events.contracts.event import Event
3
- from neva.events.contracts.handler import EventHandler
4
- from neva.events.contracts.listener import EventListener
5
-
6
-
7
- __all__ = ["Event", "EventDispatcher", "EventHandler", "EventListener"]
@@ -1,24 +0,0 @@
1
- from typing import Protocol
2
-
3
- from neva.events.contracts.event import Event
4
- from neva.events.contracts.listener import EventListener
5
- from neva.support import Result
6
-
7
-
8
- class EventDispatcher(Protocol):
9
- """Event dispatcher protocol."""
10
-
11
- async def dispatch(
12
- self,
13
- event: Event,
14
- ) -> list[Result[None, str]]:
15
- """Dispatch an event to all registered listeners."""
16
- ...
17
-
18
- def listen[T: Event](
19
- self,
20
- event_cls: type[T],
21
- listener_cls: type[EventListener[T]],
22
- ) -> None:
23
- """Register a listener for an event."""
24
- ...
File without changes
File without changes
File without changes
File without changes
File without changes