Skip to content

Commit 6b71487

Browse files
authored
Merge pull request #82 from python-effect/py3-exceptions
[BACKWARDS INCOMPATIBLE] Use exception instances instead of exc_info tuples
2 parents c739312 + 3b7aeba commit 6b71487

32 files changed

+128
-201
lines changed

.travis.yml

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,16 @@ sudo: false
22

33
language: python
44
python:
5-
- "2.7"
6-
- "3.4"
7-
- "3.5"
8-
- "pypy"
9-
matrix:
10-
include:
11-
- python: 3.7
12-
dist: xenial
13-
sudo: true
5+
- "3.6"
6+
- "3.7"
7+
- "3.8"
8+
- "pypy3"
149
install:
1510
- pip install .
1611
- pip install -r dev-requirements.txt
1712
- pip install sphinx sphinx_rtd_theme
1813
script:
19-
- make lint
14+
- flake8
2015
- py.test
2116
- make doc
2217

Makefile

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
lint:
2-
flake8 --ignore=E131,E301,E302,E731,W503,E701,E704,E722 --max-line-length=100 effect/
3-
41
build-dist:
52
rm -rf dist
63
python setup.py sdist bdist_wheel

README.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ the effects (that is, IO or state manipulation) in your code. Documentation is
1212
available at https://effect.readthedocs.org/, and its PyPI page is
1313
https://pypi.python.org/pypi/effect.
1414

15-
It `supports`_ Python 2.7, 3.4 and 3.5 as well as PyPy.
15+
It `supports`_ 3.6 and above.
1616

1717
.. _`supports`: https://travis-ci.org/python-effect/effect
1818

@@ -44,7 +44,6 @@ A very quick example of using Effects:
4444

4545
.. code:: python
4646
47-
from __future__ import print_function
4847
from effect import sync_perform, sync_performer, Effect, TypeDispatcher
4948
5049
class ReadLine(object):

docs/source/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Effect
44
Effect is a library for helping you write purely functional code by
55
isolating the effects (that is, IO or state manipulation) in your code.
66

7-
It supports both Python 2.6 and up, and 3.4 and up, as well as PyPy.
7+
It supports both Python 3.6 and up, as well as PyPy.
88

99
It lives on PyPI at https://pypi.python.org/pypi/effect and GitHub at
1010
https://github.com/python-effect/effect.

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/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
See https://effect.readthedocs.org/ for documentation.
88
"""
99

10-
from __future__ import absolute_import
11-
1210
from ._base import Effect, perform, NoPerformerFoundError, catch, raise_
1311
from ._sync import (
1412
NotSynchronousError,

effect/_base.py

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
# -*- test-case-name: effect.test_base -*-
2-
from __future__ import print_function, absolute_import
3-
4-
import sys
5-
62
from functools import partial
73

84
import attr
95

10-
import six
11-
126
from ._continuation import trampoline
137

148

@@ -34,8 +28,7 @@ def on(self, success=None, error=None):
3428
The result of the Effect will be passed to the first callback. Any
3529
callbacks added afterwards will receive the result of the previous
3630
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.
31+
callback, and exceptions are passed to the next ``error`` callback.
3932
4033
If a callback returns an :obj:`Effect`, the result of that
4134
:obj:`Effect` will be passed to the next callback.
@@ -62,7 +55,7 @@ def succeed(self, result):
6255

6356
def fail(self, result):
6457
"""
65-
Indicate that the effect has failed. result must be an exc_info tuple.
58+
Indicate that the effect has failed. result must be an exception.
6659
"""
6760
self._cont((True, result))
6861

@@ -71,13 +64,13 @@ def guard(f, *args, **kwargs):
7164
"""
7265
Run a function.
7366
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()``.
67+
Return (is_error, result), where ``is_error`` is a boolean indicating whether
68+
it raised an exception. In that case, ``result`` will be an exception.
7669
"""
7770
try:
7871
return (False, f(*args, **kwargs))
79-
except:
80-
return (True, sys.exc_info())
72+
except Exception as e:
73+
return (True, e)
8174

8275

8376
class NoPerformerFoundError(Exception):
@@ -110,7 +103,7 @@ def perform(dispatcher, effect):
110103
or return another Effect, which will be recursively performed, such that
111104
the result of the returned Effect becomes the result passed to the next
112105
callback. In the case of exceptions, the next error-callback will be called
113-
with a ``sys.exc_info()``-style tuple.
106+
with the exception instance.
114107
115108
:returns: None
116109
@@ -123,9 +116,8 @@ def perform(dispatcher, effect):
123116
passed three arguments, not two: the dispatcher, the intent, and a
124117
"box". The box is an object that lets the performer provide the result,
125118
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.
119+
``box.succeed(result)`` or ``box.fail(exc)``, where ``exc`` is
120+
an exception. Decorators like :func:`sync_performer` simply abstract this away.
129121
"""
130122
def _run_callbacks(bouncer, chain, result):
131123
is_error, value = result
@@ -156,8 +148,7 @@ def _perform(bouncer, effect):
156148
effect.intent,
157149
_Box(partial(bouncer.bounce,
158150
_run_callbacks, effect.callbacks)))
159-
except:
160-
e = sys.exc_info()
151+
except Exception as e:
161152
_run_callbacks(bouncer, effect.callbacks, (True, e))
162153

163154
trampoline(_perform, effect)
@@ -168,27 +159,24 @@ def catch(exc_type, callable):
168159
A helper for handling errors of a specific type::
169160
170161
eff.on(error=catch(SpecificException,
171-
lambda exc_info: "got an error!"))
162+
lambda exc: "got an error!"))
172163
173164
If any exception other than a ``SpecificException`` is thrown, it will be
174165
ignored by this handler and propogate further down the chain of callbacks.
175166
"""
176-
def catcher(exc_info):
177-
if isinstance(exc_info[1], exc_type):
178-
return callable(exc_info)
179-
six.reraise(*exc_info)
167+
def catcher(error):
168+
if isinstance(error, exc_type):
169+
return callable(error)
170+
raise error
180171
return catcher
181172

182173

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.
174+
def raise_(exception):
175+
"""Simple convenience function to allow raising exceptions as an expression,
176+
useful in lambdas.
188177
189178
:param exception: An exception *instance* (not an exception type).
190179
191-
- ``raise_(exc)`` is the same as ``raise exc``.
192-
- ``raise_(exc, tb)`` is the same as ``raise type(exc), exc, tb``.
180+
``raise_(exc)`` is the same as ``raise exc``.
193181
"""
194-
six.reraise(type(exception), exception, tb)
182+
raise exception

effect/_dispatcher.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
import attr
66

7-
from six.moves import filter
8-
97

108
@attr.s
119
class TypeDispatcher(object):

effect/_intents.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
performed, sunch as :class:`Func`, :class:`Error`, and :class:`Constant`.
99
"""
1010

11-
12-
from __future__ import print_function, absolute_import
13-
1411
import time
1512

1613
import attr
@@ -77,7 +74,7 @@ def parallel_all_errors(effects):
7774
:param effects: Effects which should be performed in parallel.
7875
:return: An Effect that results in a list of ``(is_error, result)`` tuples,
7976
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
77+
which case ``result`` will be the exception. If ``is_error`` is
8178
False, then ``result`` will just be the result as provided by the child
8279
effect.
8380
"""
@@ -93,12 +90,12 @@ class FirstError(Exception):
9390
One of the effects in a :obj:`ParallelEffects` resulted in an error. This
9491
represents the first such error that occurred.
9592
"""
96-
exc_info = attr.ib()
93+
exception = attr.ib()
9794
index = attr.ib()
9895

9996
def __str__(self):
10097
return '(index=%s) %s: %s' % (
101-
self.index, self.exc_info[0].__name__, self.exc_info[1])
98+
self.index, type(self.exception).__name__, self.exception)
10299

103100

104101
@attr.s

effect/_sync.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
Tools for dealing with Effects synchronously.
55
"""
66

7-
import six
8-
import sys
9-
107
from ._base import perform
118
from ._utils import wraps
129

@@ -31,7 +28,7 @@ def sync_perform(dispatcher, effect):
3128
if successes:
3229
return successes[0]
3330
elif errors:
34-
six.reraise(*errors[0])
31+
raise errors[0]
3532
else:
3633
raise NotSynchronousError("Performing %r was not synchronous!"
3734
% (effect,))
@@ -70,6 +67,6 @@ def sync_wrapper(*args, **kwargs):
7067
pass_args = args[:-1]
7168
try:
7269
box.succeed(f(*pass_args, **kwargs))
73-
except:
74-
box.fail(sys.exc_info())
70+
except Exception as e:
71+
box.fail(e)
7572
return sync_wrapper

0 commit comments

Comments
 (0)