nost-tools 2.0.0__py3-none-any.whl → 2.0.1__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.

Potentially problematic release.


This version of nost-tools might be problematic. Click here for more details.

nost_tools/observer.py CHANGED
@@ -1,181 +1,181 @@
1
- """
2
- Provides base classes that implement the observer pattern to loosely couple an observable and observer.
3
- """
4
-
5
- from abc import ABC, abstractmethod
6
- from datetime import datetime, timezone
7
- from typing import List, Optional, Union
8
-
9
-
10
- class Observer(ABC):
11
- """
12
- Abstract base class that can be notified of property changes from an associated :obj:`Observable`.
13
- """
14
-
15
- @abstractmethod
16
- def on_change(
17
- self, source: object, property_name: str, old_value: object, new_value: object
18
- ) -> None:
19
- """Callback notifying of a change.
20
-
21
- Args:
22
- source (object): object that triggered a property change
23
- property_name (str): name of the changed property
24
- old_value (object): old value of the named property
25
- new_value (object): new value of the named property
26
- """
27
- pass
28
-
29
-
30
- class RecordingObserver(Observer):
31
- """
32
- Observer that records all changes.
33
- """
34
-
35
- def __init__(
36
- self,
37
- property_filters: Optional[Union[str, List[str]]] = None,
38
- timestamped: bool = False,
39
- ):
40
- """
41
- Initializes a new recording obsever.
42
-
43
- Args:
44
- properties (Optional[Union[str,List[str]]]): optional list of property names to record
45
- timestamped (bool): True, if the changes shall be timestamped
46
- """
47
- if isinstance(property_filters, str):
48
- self.property_filters = [property_filters]
49
- else:
50
- self.property_filters = property_filters
51
- self.changes = []
52
- self.timestamped = timestamped
53
-
54
- def on_change(
55
- self, source: object, property_name: str, old_value: object, new_value: object
56
- ) -> None:
57
- """Callback notifying of a change.
58
-
59
- Args:
60
- source (object): object that triggered a property change
61
- property_name (str): name of the changed property
62
- old_value (object): old value of the named property
63
- new_value (object): new value of the named property
64
- """
65
- if self.property_filters is None or property_name in self.property_filters:
66
- change = {
67
- "source": source,
68
- "property_name": property_name,
69
- "old_value": old_value,
70
- "new_value": new_value,
71
- }
72
- if self.timestamped:
73
- change["time"] = datetime.now(tz=timezone.utc)
74
- self.changes.append(change)
75
-
76
-
77
- class Observable(object):
78
- """
79
- Base class that can register (add/remove) and notify observers of property changes.
80
- """
81
-
82
- def __init__(self):
83
- """
84
- Initializes a new observable.
85
- """
86
- # list of observers to be notified of events
87
- self._observers = []
88
-
89
- def add_observer(self, observer: Observer) -> None:
90
- """
91
- Adds an observer to this observable.
92
-
93
- Args:
94
- observer (:obj:`Observer`): observer to be added
95
- """
96
- self._observers.append(observer)
97
-
98
- def remove_observer(self, observer: Observer) -> Observer:
99
- """
100
- Removes an observer from this observable.
101
-
102
- Args:
103
- observer (:obj:`Observer`): obsever to be removed
104
-
105
- Returns:
106
- :obj:`Observer`: removed observer
107
- """
108
- return self._observers.remove(observer)
109
-
110
- def notify_observers(
111
- self, property_name: str, old_value: object, new_value: object
112
- ) -> None:
113
- """
114
- Notifies observers of a property change.
115
-
116
- Args:
117
- property_name (str): name of the changed property
118
- old_value (object): old value of the named property
119
- new_value (object): new value of the named property
120
- """
121
- if old_value != new_value:
122
- for observer in self._observers:
123
- observer.on_change(self, property_name, old_value, new_value)
124
-
125
-
126
- # Add after the existing Observer class
127
- class MessageObserver(ABC):
128
- """
129
- Abstract base class for message observers that can receive RabbitMQ messages.
130
- """
131
-
132
- @abstractmethod
133
- def on_message(self, ch, method, properties, body) -> None:
134
- """Callback for when a message is received.
135
-
136
- Args:
137
- ch: Channel object
138
- method: Method frame
139
- properties: Message properties
140
- body: Message body
141
- """
142
- pass
143
-
144
-
145
- class MessageObservable(Observable):
146
- """
147
- Observable that can notify observers of received messages.
148
- """
149
-
150
- def __init__(self):
151
- """Initialize message observable"""
152
- super().__init__()
153
- self._message_observers = []
154
-
155
- def add_message_observer(self, observer: MessageObserver) -> None:
156
- """Add a message observer.
157
-
158
- Args:
159
- observer (MessageObserver): The observer to add
160
- """
161
- self._message_observers.append(observer)
162
-
163
- def remove_message_observer(self, observer: MessageObserver) -> None:
164
- """Remove a message observer.
165
-
166
- Args:
167
- observer (MessageObserver): The observer to remove
168
- """
169
- self._message_observers.remove(observer)
170
-
171
- def notify_message_observers(self, ch, method, properties, body) -> None:
172
- """Notify all message observers about a received message.
173
-
174
- Args:
175
- ch: Channel object
176
- method: Method frame
177
- properties: Message properties
178
- body: Message body
179
- """
180
- for observer in self._message_observers:
181
- observer.on_message(ch, method, properties, body)
1
+ """
2
+ Provides base classes that implement the observer pattern to loosely couple an observable and observer.
3
+ """
4
+
5
+ from abc import ABC, abstractmethod
6
+ from datetime import datetime, timezone
7
+ from typing import List, Optional, Union
8
+
9
+
10
+ class Observer(ABC):
11
+ """
12
+ Abstract base class that can be notified of property changes from an associated :obj:`Observable`.
13
+ """
14
+
15
+ @abstractmethod
16
+ def on_change(
17
+ self, source: object, property_name: str, old_value: object, new_value: object
18
+ ) -> None:
19
+ """Callback notifying of a change.
20
+
21
+ Args:
22
+ source (object): object that triggered a property change
23
+ property_name (str): name of the changed property
24
+ old_value (object): old value of the named property
25
+ new_value (object): new value of the named property
26
+ """
27
+ pass
28
+
29
+
30
+ class RecordingObserver(Observer):
31
+ """
32
+ Observer that records all changes.
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ property_filters: Optional[Union[str, List[str]]] = None,
38
+ timestamped: bool = False,
39
+ ):
40
+ """
41
+ Initializes a new recording obsever.
42
+
43
+ Args:
44
+ properties (Optional[Union[str,List[str]]]): optional list of property names to record
45
+ timestamped (bool): True, if the changes shall be timestamped
46
+ """
47
+ if isinstance(property_filters, str):
48
+ self.property_filters = [property_filters]
49
+ else:
50
+ self.property_filters = property_filters
51
+ self.changes = []
52
+ self.timestamped = timestamped
53
+
54
+ def on_change(
55
+ self, source: object, property_name: str, old_value: object, new_value: object
56
+ ) -> None:
57
+ """Callback notifying of a change.
58
+
59
+ Args:
60
+ source (object): object that triggered a property change
61
+ property_name (str): name of the changed property
62
+ old_value (object): old value of the named property
63
+ new_value (object): new value of the named property
64
+ """
65
+ if self.property_filters is None or property_name in self.property_filters:
66
+ change = {
67
+ "source": source,
68
+ "property_name": property_name,
69
+ "old_value": old_value,
70
+ "new_value": new_value,
71
+ }
72
+ if self.timestamped:
73
+ change["time"] = datetime.now(tz=timezone.utc)
74
+ self.changes.append(change)
75
+
76
+
77
+ class Observable(object):
78
+ """
79
+ Base class that can register (add/remove) and notify observers of property changes.
80
+ """
81
+
82
+ def __init__(self):
83
+ """
84
+ Initializes a new observable.
85
+ """
86
+ # list of observers to be notified of events
87
+ self._observers = []
88
+
89
+ def add_observer(self, observer: Observer) -> None:
90
+ """
91
+ Adds an observer to this observable.
92
+
93
+ Args:
94
+ observer (:obj:`Observer`): observer to be added
95
+ """
96
+ self._observers.append(observer)
97
+
98
+ def remove_observer(self, observer: Observer) -> Observer:
99
+ """
100
+ Removes an observer from this observable.
101
+
102
+ Args:
103
+ observer (:obj:`Observer`): obsever to be removed
104
+
105
+ Returns:
106
+ :obj:`Observer`: removed observer
107
+ """
108
+ return self._observers.remove(observer)
109
+
110
+ def notify_observers(
111
+ self, property_name: str, old_value: object, new_value: object
112
+ ) -> None:
113
+ """
114
+ Notifies observers of a property change.
115
+
116
+ Args:
117
+ property_name (str): name of the changed property
118
+ old_value (object): old value of the named property
119
+ new_value (object): new value of the named property
120
+ """
121
+ if old_value != new_value:
122
+ for observer in self._observers:
123
+ observer.on_change(self, property_name, old_value, new_value)
124
+
125
+
126
+ # Add after the existing Observer class
127
+ class MessageObserver(ABC):
128
+ """
129
+ Abstract base class for message observers that can receive RabbitMQ messages.
130
+ """
131
+
132
+ @abstractmethod
133
+ def on_message(self, ch, method, properties, body) -> None:
134
+ """Callback for when a message is received.
135
+
136
+ Args:
137
+ ch: Channel object
138
+ method: Method frame
139
+ properties: Message properties
140
+ body: Message body
141
+ """
142
+ pass
143
+
144
+
145
+ class MessageObservable(Observable):
146
+ """
147
+ Observable that can notify observers of received messages.
148
+ """
149
+
150
+ def __init__(self):
151
+ """Initialize message observable"""
152
+ super().__init__()
153
+ self._message_observers = []
154
+
155
+ def add_message_observer(self, observer: MessageObserver) -> None:
156
+ """Add a message observer.
157
+
158
+ Args:
159
+ observer (MessageObserver): The observer to add
160
+ """
161
+ self._message_observers.append(observer)
162
+
163
+ def remove_message_observer(self, observer: MessageObserver) -> None:
164
+ """Remove a message observer.
165
+
166
+ Args:
167
+ observer (MessageObserver): The observer to remove
168
+ """
169
+ self._message_observers.remove(observer)
170
+
171
+ def notify_message_observers(self, ch, method, properties, body) -> None:
172
+ """Notify all message observers about a received message.
173
+
174
+ Args:
175
+ ch: Channel object
176
+ method: Method frame
177
+ properties: Message properties
178
+ body: Message body
179
+ """
180
+ for observer in self._message_observers:
181
+ observer.on_message(ch, method, properties, body)
nost_tools/publisher.py CHANGED
@@ -1,141 +1,141 @@
1
- """
2
- Provides utility classes to help applications bind behavior to temporal events.
3
- """
4
-
5
- from abc import ABC, abstractmethod
6
- from datetime import datetime, timedelta
7
- from typing import TYPE_CHECKING
8
-
9
- from .observer import Observer
10
- from .simulator import Mode, Simulator
11
-
12
- if TYPE_CHECKING:
13
- from .application import Application
14
-
15
-
16
- class ScenarioTimeIntervalPublisher(Observer, ABC):
17
- """
18
- Publishes messages at a regular interval (scenario time).
19
-
20
- Provides the simulation with time status messages, also refered to as 'heartbeat messages',
21
- or 'simulation time statuses'.
22
-
23
- Attributes:
24
- app (:obj:`Application`): application to publish messages
25
- time_status_step (:obj:`timedelta`): scenario duration between time status messages
26
- time_status_init (:obj:`datetime`): scenario time for first time status message
27
- """
28
-
29
- def __init__(
30
- self,
31
- app: "Application",
32
- time_status_step: timedelta = None,
33
- time_status_init: datetime = None,
34
- ):
35
- """
36
- Initializes a new scenario time interval publisher.
37
-
38
- Args:
39
- app (:obj:`Application`): application to publish messages
40
- time_status_step (:obj:`timedelta`): scenario duration between time status messages
41
- time_status_init (:obj:`datetime`): scenario time for first time status message
42
- """
43
- self.app = app
44
- self.time_status_step = time_status_step
45
- self.time_status_init = time_status_init
46
- self._next_time_status = None
47
- # TODO: consider adding the `publish_message` callable as an argument rather than abstract method
48
-
49
- @abstractmethod
50
- def publish_message(self) -> None:
51
- """
52
- Publishes a message.
53
- """
54
- pass
55
-
56
- def on_change(
57
- self, source: object, property_name: str, old_value: object, new_value: object
58
- ) -> None:
59
- """
60
- Publishes a message after a designated interval of scenario time.
61
-
62
- Args:
63
- source (object): observable that triggered the change
64
- property_name (str): name of the changed property
65
- old_value (obj): old value of the named property
66
- new_value (obj): new value of the named property
67
- """
68
- if property_name == Simulator.PROPERTY_MODE and new_value == Mode.INITIALIZED:
69
- if self.time_status_init is None:
70
- self._next_time_status = self.app.simulator.get_init_time()
71
- else:
72
- self._next_time_status = self.time_status_init
73
- elif property_name == Simulator.PROPERTY_TIME:
74
- while self._next_time_status <= new_value:
75
- self.publish_message()
76
- if self.time_status_step is None:
77
- self._next_time_status += self.app.simulator.get_time_step()
78
- else:
79
- self._next_time_status += self.time_status_step
80
-
81
-
82
- class WallclockTimeIntervalPublisher(Observer, ABC):
83
- """
84
- Publishes messages at a regular interval (wallclock time).
85
-
86
- Attributes:
87
- app (:obj:`Application`): application to publish messages
88
- time_status_step (:obj:`timedelta`): wallclock duration between time status messages
89
- time_status_init (:obj:`datetime`): wallclock time for first time status message
90
- """
91
-
92
- def __init__(
93
- self,
94
- app: "Application",
95
- time_status_step: timedelta = None,
96
- time_status_init: datetime = None,
97
- ):
98
- """
99
- Initializes a new wallclock time interval publisher.
100
-
101
- Args:
102
- app (:obj:`Application`): application to publish messages
103
- time_status_step (:obj:`timedelta`): wallclock duration between time status messages
104
- time_status_init (:obj:`datetime`): wallclock time for first time status message
105
- """
106
- self.app = app
107
- self.time_status_step = time_status_step
108
- self.time_status_init = time_status_init
109
- self._next_time_status = None
110
-
111
- @abstractmethod
112
- def publish_message(self) -> None:
113
- """
114
- Publishes a message.
115
- """
116
- pass
117
-
118
- def on_change(self, source, property_name, old_value, new_value):
119
- """
120
- Publishes a message after a designated interval of scenario time.
121
-
122
- Args:
123
- source (object): observable that triggered the change
124
- property_name (str): name of the changed property
125
- old_value (obj): old value of the named property
126
- new_value (obj): new value of the named property
127
- """
128
- if property_name == Simulator.PROPERTY_MODE and new_value == Mode.INITIALIZED:
129
- if self.time_status_init is None:
130
- self._next_time_status = self.app.simulator.get_wallclock_time()
131
- else:
132
- self._next_time_status = self.time_status_init
133
- elif property_name == Simulator.PROPERTY_TIME:
134
- while self._next_time_status <= self.app.simulator.get_wallclock_time():
135
- self.publish_message()
136
- if self.time_status_step is None:
137
- self._next_time_status += (
138
- self.app.simulator.get_wallclock_time_step()
139
- )
140
- else:
141
- self._next_time_status += self.time_status_step
1
+ """
2
+ Provides utility classes to help applications bind behavior to temporal events.
3
+ """
4
+
5
+ from abc import ABC, abstractmethod
6
+ from datetime import datetime, timedelta
7
+ from typing import TYPE_CHECKING
8
+
9
+ from .observer import Observer
10
+ from .simulator import Mode, Simulator
11
+
12
+ if TYPE_CHECKING:
13
+ from .application import Application
14
+
15
+
16
+ class ScenarioTimeIntervalPublisher(Observer, ABC):
17
+ """
18
+ Publishes messages at a regular interval (scenario time).
19
+
20
+ Provides the simulation with time status messages, also refered to as 'heartbeat messages',
21
+ or 'simulation time statuses'.
22
+
23
+ Attributes:
24
+ app (:obj:`Application`): application to publish messages
25
+ time_status_step (:obj:`timedelta`): scenario duration between time status messages
26
+ time_status_init (:obj:`datetime`): scenario time for first time status message
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ app: "Application",
32
+ time_status_step: timedelta = None,
33
+ time_status_init: datetime = None,
34
+ ):
35
+ """
36
+ Initializes a new scenario time interval publisher.
37
+
38
+ Args:
39
+ app (:obj:`Application`): application to publish messages
40
+ time_status_step (:obj:`timedelta`): scenario duration between time status messages
41
+ time_status_init (:obj:`datetime`): scenario time for first time status message
42
+ """
43
+ self.app = app
44
+ self.time_status_step = time_status_step
45
+ self.time_status_init = time_status_init
46
+ self._next_time_status = None
47
+ # TODO: consider adding the `publish_message` callable as an argument rather than abstract method
48
+
49
+ @abstractmethod
50
+ def publish_message(self) -> None:
51
+ """
52
+ Publishes a message.
53
+ """
54
+ pass
55
+
56
+ def on_change(
57
+ self, source: object, property_name: str, old_value: object, new_value: object
58
+ ) -> None:
59
+ """
60
+ Publishes a message after a designated interval of scenario time.
61
+
62
+ Args:
63
+ source (object): observable that triggered the change
64
+ property_name (str): name of the changed property
65
+ old_value (obj): old value of the named property
66
+ new_value (obj): new value of the named property
67
+ """
68
+ if property_name == Simulator.PROPERTY_MODE and new_value == Mode.INITIALIZED:
69
+ if self.time_status_init is None:
70
+ self._next_time_status = self.app.simulator.get_init_time()
71
+ else:
72
+ self._next_time_status = self.time_status_init
73
+ elif property_name == Simulator.PROPERTY_TIME:
74
+ while self._next_time_status <= new_value:
75
+ self.publish_message()
76
+ if self.time_status_step is None:
77
+ self._next_time_status += self.app.simulator.get_time_step()
78
+ else:
79
+ self._next_time_status += self.time_status_step
80
+
81
+
82
+ class WallclockTimeIntervalPublisher(Observer, ABC):
83
+ """
84
+ Publishes messages at a regular interval (wallclock time).
85
+
86
+ Attributes:
87
+ app (:obj:`Application`): application to publish messages
88
+ time_status_step (:obj:`timedelta`): wallclock duration between time status messages
89
+ time_status_init (:obj:`datetime`): wallclock time for first time status message
90
+ """
91
+
92
+ def __init__(
93
+ self,
94
+ app: "Application",
95
+ time_status_step: timedelta = None,
96
+ time_status_init: datetime = None,
97
+ ):
98
+ """
99
+ Initializes a new wallclock time interval publisher.
100
+
101
+ Args:
102
+ app (:obj:`Application`): application to publish messages
103
+ time_status_step (:obj:`timedelta`): wallclock duration between time status messages
104
+ time_status_init (:obj:`datetime`): wallclock time for first time status message
105
+ """
106
+ self.app = app
107
+ self.time_status_step = time_status_step
108
+ self.time_status_init = time_status_init
109
+ self._next_time_status = None
110
+
111
+ @abstractmethod
112
+ def publish_message(self) -> None:
113
+ """
114
+ Publishes a message.
115
+ """
116
+ pass
117
+
118
+ def on_change(self, source, property_name, old_value, new_value):
119
+ """
120
+ Publishes a message after a designated interval of scenario time.
121
+
122
+ Args:
123
+ source (object): observable that triggered the change
124
+ property_name (str): name of the changed property
125
+ old_value (obj): old value of the named property
126
+ new_value (obj): new value of the named property
127
+ """
128
+ if property_name == Simulator.PROPERTY_MODE and new_value == Mode.INITIALIZED:
129
+ if self.time_status_init is None:
130
+ self._next_time_status = self.app.simulator.get_wallclock_time()
131
+ else:
132
+ self._next_time_status = self.time_status_init
133
+ elif property_name == Simulator.PROPERTY_TIME:
134
+ while self._next_time_status <= self.app.simulator.get_wallclock_time():
135
+ self.publish_message()
136
+ if self.time_status_step is None:
137
+ self._next_time_status += (
138
+ self.app.simulator.get_wallclock_time_step()
139
+ )
140
+ else:
141
+ self._next_time_status += self.time_status_step