Skip to content

Commit 4ea34a4

Browse files
committed
change effect to use Except instances instead of exc_info tuples.
This means that effect now only supports Python 3 and above.
1 parent c739312 commit 4ea34a4

22 files changed

Lines changed: 119 additions & 131 deletions

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"python.pythonPath": "C:\\Users\\radix\\.virtualenvs\\effect\\Scripts\\python.exe"
3+
}

docs/source/intro.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ the ``on`` method:
4747
def greet():
4848
return get_user_name().on(
4949
success=lambda r: Effect(Print("Hello,", r)),
50-
error=lambda exc_info: Effect(Print("There was an error!", exc_info[1])))
50+
error=lambda exc: Effect(Print("There was an error!", exc)))
5151
5252
5353
(Here we assume another intent, ``Print``, which shows some text to the user.)

effect/_base.py

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@
22
from __future__ import print_function, absolute_import
33

44
import sys
5-
65
from functools import partial
76

87
import attr
98

10-
import six
11-
129
from ._continuation import trampoline
1310

1411

@@ -34,8 +31,7 @@ def on(self, success=None, error=None):
3431
The result of the Effect will be passed to the first callback. Any
3532
callbacks added afterwards will receive the result of the previous
3633
callback. Normal return values are passed on to the next ``success``
37-
callback, and exceptions are passed to the next ``error`` callback
38-
as a ``sys.exc_info()`` tuple.
34+
callback, and exceptions are passed to the next ``error`` callback.
3935
4036
If a callback returns an :obj:`Effect`, the result of that
4137
:obj:`Effect` will be passed to the next callback.
@@ -62,7 +58,7 @@ def succeed(self, result):
6258

6359
def fail(self, result):
6460
"""
65-
Indicate that the effect has failed. result must be an exc_info tuple.
61+
Indicate that the effect has failed. result must be an exception.
6662
"""
6763
self._cont((True, result))
6864

@@ -71,13 +67,13 @@ def guard(f, *args, **kwargs):
7167
"""
7268
Run a function.
7369
74-
Return (is_error, result), where is_error is a boolean indicating whether
75-
it raised an exception. In that case result will be ``sys.exc_info()``.
70+
Return (is_error, result), where ``is_error`` is a boolean indicating whether
71+
it raised an exception. In that case, ``result`` will be an exception.
7672
"""
7773
try:
7874
return (False, f(*args, **kwargs))
79-
except:
80-
return (True, sys.exc_info())
75+
except Exception as e:
76+
return (True, e)
8177

8278

8379
class NoPerformerFoundError(Exception):
@@ -110,7 +106,7 @@ def perform(dispatcher, effect):
110106
or return another Effect, which will be recursively performed, such that
111107
the result of the returned Effect becomes the result passed to the next
112108
callback. In the case of exceptions, the next error-callback will be called
113-
with a ``sys.exc_info()``-style tuple.
109+
with the exception instance.
114110
115111
:returns: None
116112
@@ -123,9 +119,8 @@ def perform(dispatcher, effect):
123119
passed three arguments, not two: the dispatcher, the intent, and a
124120
"box". The box is an object that lets the performer provide the result,
125121
optionally asynchronously. To provide the result, use
126-
``box.succeed(result)`` or ``box.fail(exc_info)``, where ``exc_info`` is
127-
a ``sys.exc_info()``-style tuple. Decorators like :func:`sync_performer`
128-
simply abstract this away.
122+
``box.succeed(result)`` or ``box.fail(exc)``, where ``exc`` is
123+
an exception. Decorators like :func:`sync_performer` simply abstract this away.
129124
"""
130125
def _run_callbacks(bouncer, chain, result):
131126
is_error, value = result
@@ -156,8 +151,7 @@ def _perform(bouncer, effect):
156151
effect.intent,
157152
_Box(partial(bouncer.bounce,
158153
_run_callbacks, effect.callbacks)))
159-
except:
160-
e = sys.exc_info()
154+
except Exception as e:
161155
_run_callbacks(bouncer, effect.callbacks, (True, e))
162156

163157
trampoline(_perform, effect)
@@ -168,27 +162,25 @@ def catch(exc_type, callable):
168162
A helper for handling errors of a specific type::
169163
170164
eff.on(error=catch(SpecificException,
171-
lambda exc_info: "got an error!"))
165+
lambda exc: "got an error!"))
172166
173167
If any exception other than a ``SpecificException`` is thrown, it will be
174168
ignored by this handler and propogate further down the chain of callbacks.
175169
"""
176-
def catcher(exc_info):
177-
if isinstance(exc_info[1], exc_type):
178-
return callable(exc_info)
179-
six.reraise(*exc_info)
170+
def catcher(error):
171+
if isinstance(error, exc_type):
172+
return callable(error)
173+
raise error
180174
return catcher
181175

182176

183-
def raise_(exception, tb=None):
184-
"""Simple convenience function to allow raising exceptions from lambdas.
185-
186-
This is slightly more convenient than ``six.reraise`` because it takes an
187-
exception instance instead of needing the type separate from the instance.
177+
def raise_(exception):
178+
"""Simple convenience function to allow raising exceptions as an expression,
179+
useful in lambdas.
188180
189181
:param exception: An exception *instance* (not an exception type).
190182
191-
- ``raise_(exc)`` is the same as ``raise exc``.
192-
- ``raise_(exc, tb)`` is the same as ``raise type(exc), exc, tb``.
183+
``raise_(exc)`` is the same as ``raise exc``.
193184
"""
194-
six.reraise(type(exception), exception, tb)
185+
raise exception
186+

effect/_intents.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def parallel_all_errors(effects):
7777
:param effects: Effects which should be performed in parallel.
7878
:return: An Effect that results in a list of ``(is_error, result)`` tuples,
7979
where ``is_error`` is True if the child effect raised an exception, in
80-
which case ``result`` will be an exc_info tuple. If ``is_error`` is
80+
which case ``result`` will be the exception. If ``is_error`` is
8181
False, then ``result`` will just be the result as provided by the child
8282
effect.
8383
"""
@@ -93,12 +93,12 @@ class FirstError(Exception):
9393
One of the effects in a :obj:`ParallelEffects` resulted in an error. This
9494
represents the first such error that occurred.
9595
"""
96-
exc_info = attr.ib()
96+
exception = attr.ib()
9797
index = attr.ib()
9898

9999
def __str__(self):
100100
return '(index=%s) %s: %s' % (
101-
self.index, self.exc_info[0].__name__, self.exc_info[1])
101+
self.index, type(self.exception).__name__, self.exception)
102102

103103

104104
@attr.s

effect/_sync.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def sync_perform(dispatcher, effect):
3131
if successes:
3232
return successes[0]
3333
elif errors:
34-
six.reraise(*errors[0])
34+
raise errors[0]
3535
else:
3636
raise NotSynchronousError("Performing %r was not synchronous!"
3737
% (effect,))
@@ -70,6 +70,6 @@ def sync_wrapper(*args, **kwargs):
7070
pass_args = args[:-1]
7171
try:
7272
box.succeed(f(*pass_args, **kwargs))
73-
except:
74-
box.fail(sys.exc_info())
73+
except Exception as e:
74+
box.fail(e)
7575
return sync_wrapper

effect/_test_utils.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import attr
77

8-
from testtools.matchers import Equals
8+
from testtools.matchers import Equals, Mismatch
99

1010

1111
@attr.s
@@ -20,35 +20,39 @@ def describe(self):
2020
+ ''.join(self.got_tb)
2121
+ "\nbut it doesn't.")
2222

23+
@attr.s
24+
class MatchesException(object):
25+
expected = attr.ib()
26+
27+
def match(self, other):
28+
expected_type = type(self.expected)
29+
if type(other) is not expected_type:
30+
return Mismatch('{} is not a {}'.format(other, expected_type))
31+
if other.args != self.expected.args:
32+
return Mismatch('{} has different arguments: {}.'.format(
33+
other.args, self.expected.args))
34+
2335

2436
@attr.s
2537
class MatchesReraisedExcInfo(object):
2638

2739
expected = attr.ib()
2840

2941
def match(self, actual):
30-
valcheck = Equals(self.expected[1]).match(actual[1])
42+
valcheck = Equals(self.expected.args).match(actual.args)
3143
if valcheck is not None:
3244
return valcheck
33-
typecheck = Equals(self.expected[0]).match(actual[0])
45+
typecheck = Equals(type(self.expected)).match(type(actual))
3446
if typecheck is not None:
3547
return typecheck
36-
expected = traceback.format_exception(*self.expected)
37-
new = traceback.format_exception(*actual)
48+
expected = list(traceback.TracebackException.from_exception(self.expected).format())
49+
new = list(traceback.TracebackException.from_exception(actual).format())
3850
tail_equals = lambda a, b: a == b[-len(a):]
3951
if not tail_equals(expected[1:], new[1:]):
4052
return ReraisedTracebackMismatch(expected_tb=expected,
4153
got_tb=new)
4254

4355

44-
def get_exc_info(exception):
45-
"""Get an exc_info tuple based on an exception instance."""
46-
try:
47-
raise exception
48-
except:
49-
return sys.exc_info()
50-
51-
5256
def raise_(e):
5357
"""Raise an exception instance. Exists so you can raise in a lambda."""
5458
raise e

effect/do.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def foo():
9595
def _do(result, generator, is_error):
9696
try:
9797
if is_error:
98-
val = generator.throw(*result)
98+
val = generator.throw(result)
9999
else:
100100
val = generator.send(result)
101101
except StopIteration as stop:
@@ -104,8 +104,7 @@ def _do(result, generator, is_error):
104104
# case where some other code is raising StopIteration up through this
105105
# generator, in which case we shouldn't really treat it like a function
106106
# return -- it could quite easily hide bugs.
107-
tb = sys.exc_info()[2]
108-
if tb.tb_next:
107+
if stop.__traceback__.tb_next:
109108
raise
110109
else:
111110
# Python 3 allows you to use `return val` in a generator, which

effect/fold.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@ class FoldError(Exception):
99
Raised when one of the Effects passed to :func:`fold_effect` fails.
1010
1111
:ivar accumulator: The data accumulated so far, before the failing Effect.
12-
:ivar wrapped_exception: The exc_info tuple representing the original
13-
exception raised by the failing Effect.
12+
:ivar wrapped_exception: The original exception raised by the failing Effect.
1413
"""
1514
def __init__(self, accumulator, wrapped_exception):
1615
self.accumulator = accumulator
1716
self.wrapped_exception = wrapped_exception
1817

1918
def __str__(self):
20-
tb_lines = traceback.format_exception(*self.wrapped_exception)
19+
tb_lines = traceback.TracebackException.from_exception(self.wrapped_exception).format()
2120
tb = ''.join(tb_lines)
2221
st = (
2322
"<FoldError after accumulating %r> Original traceback follows:\n%s"

effect/parallel_async.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ def succeed(index, result):
2828
box.succeed(results)
2929

3030
def fail(index, result):
31-
box.fail((FirstError,
32-
FirstError(exc_info=result, index=index),
33-
result[2]))
31+
box.fail(FirstError(exception=result, index=index))
3432

3533
for index, effect in enumerate(effects):
3634
perform(

effect/retry.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ def retry(effect, should_retry):
1414
will fail with the most recent error from func.
1515
1616
:param effect.Effect effect: Any effect.
17-
:param should_retry: A function which should take an exc_info tuple as an
17+
:param should_retry: A function which should take an exception as an
1818
argument and return an effect of bool.
1919
"""
2020

2121
def maybe_retry(error, retry_allowed):
2222
if retry_allowed:
2323
return try_()
2424
else:
25-
six.reraise(*error)
25+
raise error
2626

2727
def try_():
2828
return effect.on(

0 commit comments

Comments
 (0)