Skip to content

Commit 5962e7e

Browse files
committed
Use properties for Event.ok and Event.defused
New `Event.ok` and `Event.defused` properties wrap `Event._ok` and `Event._defused` attributes. The key benefit is that doc strings now can exist for `Event.ok` and `Event.defused`. This is a performance-neutral change since all of the fast-paths still use direct access to the `_ok` and `_defused` attributes.
1 parent 39163a3 commit 5962e7e

3 files changed

Lines changed: 54 additions & 24 deletions

File tree

src/simpy/core.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def run(self, until=None):
122122

123123
# Schedule the event with before all regular timeouts.
124124
until = Event(self)
125-
until.ok = True
125+
until._ok = True
126126
until._value = None
127127
self.schedule(until, URGENT, at - self.now)
128128

@@ -220,7 +220,7 @@ def step(self):
220220
for callback in callbacks:
221221
callback(event)
222222

223-
if not event.ok and not hasattr(event, 'defused'):
223+
if not event._ok and not event.defused:
224224
# The event has failed and has not been defused. Crash the
225225
# environment.
226226
# Create a copy of the failure exception with a new traceback.

src/simpy/events.py

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ class Event(object):
5151
examining *ok* and do further processing with the *value* it has produced.
5252
5353
Failed events are never silently ignored and will raise an exception upon
54-
being processed. If a callback handles an exception, it must set *defused*
55-
flag to ``True`` to prevent this.
54+
being processed. If a callback handles an exception, it must set :attr:`defused`
55+
to ``True`` to prevent this.
5656
5757
This class also implements ``__and__()`` (``&``) and ``__or__()`` (``|``).
5858
If you concatenate two events using one of these operators,
@@ -88,13 +88,43 @@ def processed(self):
8888
callbacks have been invoked)."""
8989
return self.callbacks is None
9090

91+
@property
92+
def ok(self):
93+
"""Becomes ``True`` when the event has been triggered successfully.
94+
95+
A "successful" event is one triggered with :meth:`succeed()`.
96+
97+
"""
98+
return self._value is not PENDING and self._ok
99+
100+
@property
101+
def defused(self):
102+
"""Becomes ``True`` when the failed event's exception is "defused".
103+
104+
When an event fails (i.e. with :meth:`fail()`), the failed event's
105+
`value` is an exception that will be re-raised when the
106+
:class:`~simpy.core.Environment` processes the event (i.e. in
107+
:meth:`~simpy.core.Environment.step()`).
108+
109+
It is also possible for the failed event's exception to be defused by
110+
setting :attr:`defused` to ``True`` from an event callback. Doing so
111+
prevents the event's exception from being re-raised when the event is
112+
processed by the :class:`~simpy.core.Environment`.
113+
114+
"""
115+
return not self.ok and hasattr(self, '_defused')
116+
117+
@defused.setter
118+
def defused(self, value):
119+
self._defused = True
120+
91121
@property
92122
def value(self):
93123
"""The value of the event if it is available.
94124
95125
The value is available when the event has been triggered.
96126
97-
Raise a :exc:`AttributeError` if the value is not yet available.
127+
Raises :exc:`AttributeError` if the value is not yet available.
98128
99129
"""
100130
if self._value is PENDING:
@@ -109,21 +139,21 @@ def trigger(self, event):
109139
chain reactions.
110140
111141
"""
112-
self.ok = event.ok
142+
self._ok = event._ok
113143
self._value = event._value
114144
self.env.schedule(self)
115145

116146
def succeed(self, value=None):
117147
"""Set the event's value, mark it as successful and schedule it for
118148
processing by the environment. Returns the event instance.
119149
120-
Raise a :exc:`RuntimeError` if this event has already been triggerd.
150+
Raises :exc:`RuntimeError` if this event has already been triggerd.
121151
122152
"""
123153
if self._value is not PENDING:
124154
raise RuntimeError('%s has already been triggered' % self)
125155

126-
self.ok = True
156+
self._ok = True
127157
self._value = value
128158
self.env.schedule(self)
129159
return self
@@ -132,16 +162,16 @@ def fail(self, exception):
132162
"""Set *exception* as the events value, mark it as failed and schedule
133163
it for processing by the environment. Returns the event instance.
134164
135-
Raise a :exc:`ValueError` if *exception* is not an :exc:`Exception`.
165+
Raises :exc:`ValueError` if *exception* is not an :exc:`Exception`.
136166
137-
Raise a :exc:`RuntimeError` if this event has already been triggered.
167+
Raises :exc:`RuntimeError` if this event has already been triggered.
138168
139169
"""
140170
if self._value is not PENDING:
141171
raise RuntimeError('%s has already been triggered' % self)
142172
if not isinstance(exception, BaseException):
143173
raise ValueError('%s is not an exception.' % exception)
144-
self.ok = False
174+
self._ok = False
145175
self._value = exception
146176
self.env.schedule(self)
147177
return self
@@ -174,7 +204,7 @@ def __init__(self, env, delay, value=None):
174204
self.callbacks = []
175205
self._value = value
176206
self._delay = delay
177-
self.ok = True
207+
self._ok = True
178208
env.schedule(self, NORMAL, delay)
179209

180210
def _desc(self):
@@ -200,7 +230,7 @@ def __init__(self, env, process):
200230
# The initialization events needs to be scheduled as urgent so that it
201231
# will be handled before interrupts. Otherwise a process whose
202232
# generator has not yet been started could be interrupted.
203-
self.ok = True
233+
self._ok = True
204234
env.schedule(self, URGENT)
205235

206236

@@ -217,8 +247,8 @@ def __init__(self, process, cause):
217247
self.env = process.env
218248
self.callbacks = [self._interrupt]
219249
self._value = Interrupt(cause)
220-
self.ok = False
221-
self.defused = True
250+
self._ok = False
251+
self._defused = True
222252

223253
if process._value is not PENDING:
224254
raise RuntimeError('%s has terminated and cannot be interrupted.' %
@@ -328,12 +358,12 @@ def _resume(self, event):
328358
while True:
329359
# Get next event from process
330360
try:
331-
if event.ok:
361+
if event._ok:
332362
event = self._generator.send(event._value)
333363
else:
334364
# The process has no choice but to handle the failed event
335365
# (or fail itself).
336-
event.defused = True
366+
event._defused = True
337367

338368
# Create an exclusive copy of the exception for this
339369
# process to prevent traceback modifications by other
@@ -347,14 +377,14 @@ def _resume(self, event):
347377
except StopIteration as e:
348378
# Process has terminated.
349379
event = None
350-
self.ok = True
380+
self._ok = True
351381
self._value = e.args[0] if len(e.args) else None
352382
self.env.schedule(self)
353383
break
354384
except BaseException as e:
355385
# Process has failed.
356386
event = None
357-
self.ok = False
387+
self._ok = False
358388
tb = e.__traceback__ if not PY2 else sys.exc_info()[2]
359389
# Strip the frame of this function from the traceback as it
360390
# does not add any useful information.
@@ -496,7 +526,7 @@ def _populate_value(self, value):
496526

497527
def _build_value(self, event):
498528
"""Build the value of this condition."""
499-
if event.ok:
529+
if event._ok:
500530
self._value = ConditionValue()
501531
self._populate_value(self._value)
502532

@@ -508,9 +538,9 @@ def _check(self, event):
508538

509539
self._count += 1
510540

511-
if not event.ok:
541+
if not event._ok:
512542
# Abort if the event has failed.
513-
event.defused = True
543+
event._defused = True
514544
self.fail(event._value)
515545
elif self._evaluate(self._events, self._count):
516546
# The condition has been met. The _collect_values callback will

tests/test_exceptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def callback(event):
167167
event = env.event()
168168
event.callbacks.append(callback)
169169
event.fail(RuntimeError())
170-
assert not hasattr(event, 'defused'), 'Event has been defused immediately'
170+
assert not event.defused, 'Event has been defused immediately'
171171
env.run(until=1)
172172
assert event.defused, 'Event has not been defused'
173173

@@ -185,7 +185,7 @@ def pem(env, event):
185185
env.process(pem(env, event))
186186
event.fail(RuntimeError())
187187

188-
assert not hasattr(event, 'defused'), 'Event has been defuseed immediately'
188+
assert not event.defused, 'Event has been defused immediately'
189189
env.run(until=1)
190190
assert event.defused, 'Event has not been defused'
191191

0 commit comments

Comments
 (0)