python-neva 3.2.0__py3-none-any.whl → 3.3.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.
- neva/events/__init__.py +2 -1
- neva/events/contracts/__init__.py +7 -0
- neva/events/contracts/dispatcher.py +24 -0
- neva/events/contracts/event.py +7 -0
- neva/events/contracts/handler.py +14 -0
- neva/events/contracts/listener.py +19 -0
- neva/events/dispatcher.py +55 -23
- neva/events/event.py +2 -1
- neva/events/event_registry.py +31 -16
- neva/events/listener.py +10 -33
- neva/events/policy.py +8 -0
- neva/events/provider.py +3 -2
- {python_neva-3.2.0.dist-info → python_neva-3.3.0.dist-info}/METADATA +6 -1
- {python_neva-3.2.0.dist-info → python_neva-3.3.0.dist-info}/RECORD +15 -9
- {python_neva-3.2.0.dist-info → python_neva-3.3.0.dist-info}/WHEEL +0 -0
neva/events/__init__.py
CHANGED
|
@@ -9,7 +9,8 @@ Three components are of particular interest:
|
|
|
9
9
|
|
|
10
10
|
from neva.events.dispatcher import EventDispatcher
|
|
11
11
|
from neva.events.event import Event
|
|
12
|
-
from neva.events.listener import EventListener,
|
|
12
|
+
from neva.events.listener import EventListener, listener
|
|
13
|
+
from neva.events.policy import HandlingPolicy
|
|
13
14
|
from neva.events.provider import EventServiceProvider
|
|
14
15
|
|
|
15
16
|
|
|
@@ -0,0 +1,7 @@
|
|
|
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"]
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
...
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
from neva.events.contracts.event import Event
|
|
4
|
+
from neva.support import Result
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EventHandler[T: Event](Protocol):
|
|
8
|
+
"""Define a valid function for event handling."""
|
|
9
|
+
|
|
10
|
+
__name__: str
|
|
11
|
+
|
|
12
|
+
async def __call__(self, event: T) -> Result[None, str]:
|
|
13
|
+
"""Handle an event."""
|
|
14
|
+
...
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
from neva.events.contracts.event import Event
|
|
4
|
+
from neva.events.policy import HandlingPolicy
|
|
5
|
+
from neva.support import Result
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EventListener[T: Event](Protocol):
|
|
9
|
+
"""Event listener protocol."""
|
|
10
|
+
|
|
11
|
+
policy: HandlingPolicy
|
|
12
|
+
|
|
13
|
+
async def handle(self, event: T) -> Result[None, str]:
|
|
14
|
+
"""Handle an event.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
A result indicating whether the event was handled successfully.
|
|
18
|
+
"""
|
|
19
|
+
...
|
neva/events/dispatcher.py
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
"""Base implementation of the event dispatcher."""
|
|
2
2
|
|
|
3
|
+
from typing import Callable, Protocol, override, runtime_checkable
|
|
4
|
+
|
|
3
5
|
from neva.arch.application import Application
|
|
4
6
|
from neva.database.manager import DatabaseManager
|
|
5
7
|
from neva.database.transaction import TransactionCallback
|
|
6
|
-
from neva.events
|
|
8
|
+
from neva.events import contracts
|
|
7
9
|
from neva.events.event_registry import EventRegistry
|
|
8
|
-
from neva.events.listener import EventListener
|
|
9
10
|
from neva.support import Err, Nothing, Result, Some
|
|
10
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
|
+
class EventDispatcher(contracts.EventDispatcher):
|
|
13
27
|
"""Event dispatcher implementation."""
|
|
14
28
|
|
|
15
29
|
def __init__(
|
|
@@ -18,11 +32,12 @@ class EventDispatcher:
|
|
|
18
32
|
db: DatabaseManager,
|
|
19
33
|
registry: EventRegistry,
|
|
20
34
|
) -> None:
|
|
21
|
-
self._registry = registry
|
|
22
|
-
self._app = app
|
|
23
|
-
self._db = db
|
|
35
|
+
self._registry: EventRegistry = registry
|
|
36
|
+
self._app: Application = app
|
|
37
|
+
self._db: DatabaseManager = db
|
|
38
|
+
self._before_dispatch_hooks: list[BeforeDispatchHook] = []
|
|
24
39
|
|
|
25
|
-
async def
|
|
40
|
+
async def _apply_before_dispatch(self, event: contracts.Event) -> None:
|
|
26
41
|
"""Extension hook called before listeners are invoked.
|
|
27
42
|
|
|
28
43
|
Override in a subclass to add cross-cutting behaviour such as
|
|
@@ -32,8 +47,19 @@ class EventDispatcher:
|
|
|
32
47
|
Args:
|
|
33
48
|
event: The event about to be dispatched.
|
|
34
49
|
"""
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
for hook in self._before_dispatch_hooks:
|
|
51
|
+
match hook:
|
|
52
|
+
case AsyncBeforeDispatchHook():
|
|
53
|
+
await hook(event)
|
|
54
|
+
case SyncBeforeDispatchHook():
|
|
55
|
+
hook(event)
|
|
56
|
+
|
|
57
|
+
async def before_dispatch(self, hook: BeforeDispatchHook) -> None:
|
|
58
|
+
"""Register a hook to be called before listeners are invoked."""
|
|
59
|
+
self._before_dispatch_hooks.append(hook)
|
|
60
|
+
|
|
61
|
+
@override
|
|
62
|
+
async def dispatch(self, event: contracts.Event) -> list[Result[None, str]]:
|
|
37
63
|
"""Dispatch an event to all registered listeners.
|
|
38
64
|
|
|
39
65
|
Listeners are resolved from the DI container when available,
|
|
@@ -45,20 +71,23 @@ class EventDispatcher:
|
|
|
45
71
|
Returns:
|
|
46
72
|
A list of results, one per listener invocation.
|
|
47
73
|
"""
|
|
48
|
-
await self.
|
|
74
|
+
await self._apply_before_dispatch(event)
|
|
49
75
|
results: list[Result[None, str]] = []
|
|
50
|
-
listeners = self._registry.
|
|
76
|
+
listeners = self._registry.resolve_listeners(event)
|
|
51
77
|
|
|
52
|
-
|
|
78
|
+
immediate: list[type[contracts.EventListener[contracts.Event]]] = []
|
|
79
|
+
deferred: list[type[contracts.EventListener[contracts.Event]]] = []
|
|
80
|
+
[immediate.extend(listener.immediate.copy()) for listener in listeners]
|
|
81
|
+
[deferred.extend(listener.deferred.copy()) for listener in listeners]
|
|
53
82
|
|
|
54
83
|
match self._db.current():
|
|
55
84
|
case Some(tx):
|
|
56
|
-
for listener_cls in
|
|
85
|
+
for listener_cls in deferred:
|
|
57
86
|
_ = tx.on_commit(self._build_callback(event, listener_cls))
|
|
58
87
|
case Nothing():
|
|
59
|
-
|
|
88
|
+
immediate.extend(deferred)
|
|
60
89
|
|
|
61
|
-
for listener_cls in
|
|
90
|
+
for listener_cls in immediate:
|
|
62
91
|
listener = self._resolve_listener(listener_cls)
|
|
63
92
|
try:
|
|
64
93
|
results.append(await listener.handle(event))
|
|
@@ -67,17 +96,20 @@ class EventDispatcher:
|
|
|
67
96
|
|
|
68
97
|
return results
|
|
69
98
|
|
|
70
|
-
|
|
99
|
+
@override
|
|
100
|
+
def listen[T: contracts.Event](
|
|
71
101
|
self,
|
|
72
|
-
event_cls: type[T],
|
|
73
|
-
listener_cls: type[EventListener[T]],
|
|
102
|
+
event_cls: type[T] | list[type[T]],
|
|
103
|
+
listener_cls: type[contracts.EventListener[T]],
|
|
74
104
|
) -> None:
|
|
75
105
|
"""Register a listener for an event."""
|
|
106
|
+
if not isinstance(event_cls, list):
|
|
107
|
+
event_cls = [event_cls]
|
|
76
108
|
self._registry.register(event_cls, listener_cls)
|
|
77
109
|
|
|
78
|
-
def _resolve_listener[T: Event](
|
|
79
|
-
self, listener_cls: type[EventListener[T]]
|
|
80
|
-
) -> EventListener[T]:
|
|
110
|
+
def _resolve_listener[T: contracts.Event](
|
|
111
|
+
self, listener_cls: type[contracts.EventListener[T]]
|
|
112
|
+
) -> contracts.EventListener[T]:
|
|
81
113
|
"""Resolve a listener from the container.
|
|
82
114
|
|
|
83
115
|
Listeners are resolved from the DI container when available,
|
|
@@ -91,10 +123,10 @@ class EventDispatcher:
|
|
|
91
123
|
return result.unwrap()
|
|
92
124
|
return listener_cls()
|
|
93
125
|
|
|
94
|
-
def _build_callback[T: Event](
|
|
126
|
+
def _build_callback[T: contracts.Event](
|
|
95
127
|
self,
|
|
96
128
|
event: T,
|
|
97
|
-
listener_cls: type[EventListener[T]],
|
|
129
|
+
listener_cls: type[contracts.EventListener[T]],
|
|
98
130
|
) -> TransactionCallback:
|
|
99
131
|
async def callback() -> Result[None, str]:
|
|
100
132
|
listener = self._resolve_listener(listener_cls)
|
neva/events/event.py
CHANGED
neva/events/event_registry.py
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
"""Event/listener registry."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import inspect
|
|
4
|
+
from typing import Any, TypeVar
|
|
4
5
|
|
|
5
|
-
from neva.events
|
|
6
|
-
from neva.events.listener import EventListener, HandlingPolicy
|
|
6
|
+
from neva.events import contracts, policy
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class EventListenerRegistry[T: Event]:
|
|
9
|
+
class EventListenerRegistry[T: contracts.Event]:
|
|
10
10
|
"""Event listener registry for a given event."""
|
|
11
11
|
|
|
12
12
|
def __init__(self) -> None:
|
|
13
|
-
self.immediate: list[type[EventListener[T]]] = []
|
|
14
|
-
self.deferred: list[type[EventListener[T]]] = []
|
|
13
|
+
self.immediate: list[type[contracts.EventListener[T]]] = []
|
|
14
|
+
self.deferred: list[type[contracts.EventListener[T]]] = []
|
|
15
15
|
|
|
16
|
-
def add_listener(self, listener_cls: type[EventListener[T]]) -> None:
|
|
16
|
+
def add_listener(self, listener_cls: type[contracts.EventListener[T]]) -> None:
|
|
17
17
|
"""Add a listener to the correct category in the registry."""
|
|
18
18
|
match listener_cls.policy:
|
|
19
|
-
case HandlingPolicy.IMMEDIATE:
|
|
19
|
+
case policy.HandlingPolicy.IMMEDIATE:
|
|
20
20
|
self.immediate.append(listener_cls)
|
|
21
|
-
case HandlingPolicy.DEFERRED:
|
|
21
|
+
case policy.HandlingPolicy.DEFERRED:
|
|
22
22
|
self.deferred.append(listener_cls)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
T = TypeVar("T", bound=contracts.Event, covariant=True)
|
|
26
|
+
|
|
27
|
+
|
|
25
28
|
class EventRegistry:
|
|
26
29
|
"""Event/listener registry."""
|
|
27
30
|
|
|
@@ -31,14 +34,26 @@ class EventRegistry:
|
|
|
31
34
|
"""Initialize the registry."""
|
|
32
35
|
self.__listeners = {}
|
|
33
36
|
|
|
34
|
-
def register
|
|
35
|
-
self,
|
|
37
|
+
def register(
|
|
38
|
+
self,
|
|
39
|
+
event_cls: list[type[T]],
|
|
40
|
+
listener_cls: type[contracts.EventListener[T]],
|
|
36
41
|
) -> None:
|
|
37
42
|
"""Register a listener for an event."""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
for cls in event_cls:
|
|
44
|
+
self.__listeners.setdefault(cls, EventListenerRegistry()).add_listener(
|
|
45
|
+
listener_cls
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def resolve_listeners(self, event: T) -> list[EventListenerRegistry[T]]:
|
|
49
|
+
"""Return all listeners registries for an event type."""
|
|
50
|
+
registries = [
|
|
51
|
+
self.__listeners[cls]
|
|
52
|
+
for cls in inspect.getmro(type(event))
|
|
53
|
+
if cls in self.__listeners
|
|
54
|
+
]
|
|
55
|
+
return registries
|
|
56
|
+
|
|
57
|
+
def get_listeners(self, event: T) -> EventListenerRegistry[T]:
|
|
43
58
|
"""Return all listeners registered for an event type."""
|
|
44
59
|
return self.__listeners.get(type(event), EventListenerRegistry[T]())
|
neva/events/listener.py
CHANGED
|
@@ -1,49 +1,26 @@
|
|
|
1
1
|
"""Event listener protocol."""
|
|
2
2
|
|
|
3
|
-
import abc
|
|
4
|
-
from enum import StrEnum
|
|
5
3
|
from types import new_class
|
|
6
|
-
from typing import Callable,
|
|
4
|
+
from typing import Callable, override
|
|
7
5
|
|
|
8
|
-
from neva.events
|
|
6
|
+
from neva.events import contracts
|
|
7
|
+
from neva.events.policy import HandlingPolicy
|
|
9
8
|
from neva.support import Result
|
|
10
9
|
from neva.support.strconv import snake2pascal
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
class
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
IMMEDIATE = "immediate"
|
|
17
|
-
DEFERRED = "deferred"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class EventListener[T: Event](abc.ABC):
|
|
21
|
-
"""Event listener protocol."""
|
|
12
|
+
class EventListener[T: contracts.Event](contracts.EventListener[T]):
|
|
13
|
+
"""Base event listener class."""
|
|
22
14
|
|
|
23
15
|
policy: HandlingPolicy = HandlingPolicy.IMMEDIATE
|
|
24
16
|
|
|
25
|
-
@
|
|
26
|
-
async def handle(self, event:
|
|
27
|
-
"""Handle an event.
|
|
28
|
-
|
|
29
|
-
Returns:
|
|
30
|
-
A result indicating whether the event was handled successfully.
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class EventHandler[T: Event](Protocol):
|
|
35
|
-
"""Define a valid function for event handling."""
|
|
36
|
-
|
|
37
|
-
__name__: str
|
|
38
|
-
|
|
39
|
-
async def __call__(self, event: T) -> Result[None, str]:
|
|
40
|
-
"""Handle an event."""
|
|
41
|
-
...
|
|
17
|
+
@override
|
|
18
|
+
async def handle(self, event: contracts.Event) -> Result[None, str]: ...
|
|
42
19
|
|
|
43
20
|
|
|
44
|
-
def listener[T: Event](
|
|
21
|
+
def listener[T: contracts.Event](
|
|
45
22
|
policy: HandlingPolicy = HandlingPolicy.IMMEDIATE,
|
|
46
|
-
) -> Callable[[EventHandler[T]], type[EventListener[T]]]:
|
|
23
|
+
) -> Callable[[contracts.EventHandler[T]], type[EventListener[T]]]:
|
|
47
24
|
"""Create a listener from a function.
|
|
48
25
|
|
|
49
26
|
Returns:
|
|
@@ -51,7 +28,7 @@ def listener[T: Event](
|
|
|
51
28
|
"""
|
|
52
29
|
|
|
53
30
|
def decorator(
|
|
54
|
-
func: EventHandler[T],
|
|
31
|
+
func: contracts.EventHandler[T],
|
|
55
32
|
) -> type[EventListener[T]]:
|
|
56
33
|
async def handle(_: EventListener[T], event: T) -> Result[None, str]:
|
|
57
34
|
return await func(event)
|
neva/events/policy.py
ADDED
neva/events/provider.py
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
from typing import Self, override
|
|
4
4
|
|
|
5
5
|
from neva.arch.service_provider import ServiceProvider
|
|
6
|
+
from neva.events import contracts
|
|
7
|
+
from neva.events.dispatcher import EventDispatcher
|
|
6
8
|
from neva.events.event_registry import EventRegistry
|
|
7
9
|
from neva.support import Ok, Result
|
|
8
10
|
|
|
@@ -22,8 +24,7 @@ class EventServiceProvider(ServiceProvider):
|
|
|
22
24
|
Returns:
|
|
23
25
|
Result[Self, str]: The result of the registration.
|
|
24
26
|
"""
|
|
25
|
-
from neva.events.dispatcher import EventDispatcher
|
|
26
|
-
|
|
27
27
|
self.bind(EventRegistry)
|
|
28
28
|
self.bind(EventDispatcher)
|
|
29
|
+
self.bind(EventDispatcher, interface=contracts.EventDispatcher)
|
|
29
30
|
return Ok(self)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-neva
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: aiosqlite>=0.20.0
|
|
@@ -20,3 +20,8 @@ Requires-Dist: neva-fastapi>=1.0.0; extra == 'fastapi'
|
|
|
20
20
|
Provides-Extra: testing
|
|
21
21
|
Requires-Dist: pytest-asyncio>=0.25.3; extra == 'testing'
|
|
22
22
|
Requires-Dist: pytest>=9.0.2; extra == 'testing'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# The Neva framework
|
|
26
|
+
|
|
27
|
+
PLACEHOLDER
|
|
@@ -20,13 +20,19 @@ neva/database/manager.py,sha256=VNvhVumZD2hoGKpkdXMAkT6KykE4H80WcG2DgShK5Hs,4240
|
|
|
20
20
|
neva/database/provider.py,sha256=l2be4Ri53RtQq1-qzohy3cqzJQpTaNzqXGVhXPKUCXc,1674
|
|
21
21
|
neva/database/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
neva/database/transaction.py,sha256=_0Dsao_vhCJT5YRUmrHOLb0uxvfdE-5k3n0yMPLC1o8,5167
|
|
23
|
-
neva/events/__init__.py,sha256=
|
|
24
|
-
neva/events/dispatcher.py,sha256=
|
|
25
|
-
neva/events/event.py,sha256=
|
|
26
|
-
neva/events/event_registry.py,sha256=
|
|
27
|
-
neva/events/listener.py,sha256
|
|
28
|
-
neva/events/
|
|
23
|
+
neva/events/__init__.py,sha256=Gpi5q-pNlIi-q23U2ZOsNf8Y9rB6AQtsBbbBFU0tkFc,776
|
|
24
|
+
neva/events/dispatcher.py,sha256=fFhF39sba1VOiwLa2eHzib5qWk4n_SOoWiYkmZL044o,4686
|
|
25
|
+
neva/events/event.py,sha256=q-KyyxNkU3AN2YSaujuNPIoXQMXhGOwClTap5y8fU5I,282
|
|
26
|
+
neva/events/event_registry.py,sha256=IZnpR3oLyvt5K2pn9KMCBw5W4JP7Ok_zMKI2SgQoXVM,1945
|
|
27
|
+
neva/events/listener.py,sha256=2d5RHVKO0pU-eL_evLReTG0DIoX9tZyOcn8PWLeECZo,1435
|
|
28
|
+
neva/events/policy.py,sha256=I67ziob0IEVeXdkilNPpj2f48gEz2EZneR1Fz8Nlt2U,154
|
|
29
|
+
neva/events/provider.py,sha256=cVxADLv7VXCK6gTMJsDNRzspMOH2lrIkvMRhqVh2cK4,954
|
|
29
30
|
neva/events/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
neva/events/contracts/__init__.py,sha256=6-xRFR_5t6PbU-owl0WtM8mmdvmNvuHPUyL1sbFfqU8,293
|
|
32
|
+
neva/events/contracts/dispatcher.py,sha256=jJ4qLM_5-bXcBPH_txnbwHXAs6GFB080MiDkEViatTk,594
|
|
33
|
+
neva/events/contracts/event.py,sha256=kUUECex_AlmaPJ-fXRg32eMkiqNS75CHldOr7bbpFqo,89
|
|
34
|
+
neva/events/contracts/handler.py,sha256=SwLuuRtXhCzGkcBBLdH3MjUAkjEr0_rhqgE6dkWVJxI,327
|
|
35
|
+
neva/events/contracts/listener.py,sha256=0xwPrjprkMzXu9vY1Z-EjSLFdvOUpomiE65hH16nfxA,465
|
|
30
36
|
neva/obs/__init__.py,sha256=dVzgljk9Hvo44LI34RcwbDyT42_z4nSnFVmL4GLbKD0,331
|
|
31
37
|
neva/obs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
38
|
neva/obs/instrumentation/__init__.py,sha256=SfIAvuCs-L0kEzOxUJ4aOIkhbydCe7qBwWtGeDELVA0,36
|
|
@@ -85,6 +91,6 @@ neva/testing/fakes.py,sha256=5HuuwMeAKhFSZfn9T8raQIjdgHkjFeZsFXDud0T4v7Q,2674
|
|
|
85
91
|
neva/testing/fixtures.py,sha256=oiPa5ntUkW-SbySDvwEHXtti8NqSwI-R0CT1_ezTx_Y,1445
|
|
86
92
|
neva/testing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
87
93
|
neva/testing/test_case.py,sha256=zJBZJeKI-sv-EOaXNuSJ0V2Zp_6hdk64W4dOh0eP8RI,3290
|
|
88
|
-
python_neva-3.
|
|
89
|
-
python_neva-3.
|
|
90
|
-
python_neva-3.
|
|
94
|
+
python_neva-3.3.0.dist-info/METADATA,sha256=7J-vn_EdbJAX-DgR0zsWf8NWVIq2BPd5Fp5YYiZcbic,846
|
|
95
|
+
python_neva-3.3.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
96
|
+
python_neva-3.3.0.dist-info/RECORD,,
|
|
File without changes
|