Skip to content

Commit c8689b4

Browse files
committed
Merge pull request #57 from python-effect/switch-to-attrs
Switch to attrs
2 parents 6598278 + 0d2130b commit c8689b4

9 files changed

Lines changed: 95 additions & 81 deletions

File tree

.travis.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ python:
77
- "3.4"
88
- "pypy"
99
install:
10+
- pip install .
1011
- pip install -r dev-requirements.txt
11-
- pip install six
12-
- pip install 'characteristic>=14.0.0'
1312
- pip install sphinx
1413
script:
1514
- make lint

effect/_base.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,26 @@
55

66
from functools import partial
77

8-
from characteristic import attributes
8+
import attr
99

1010
import six
1111

1212
from ._continuation import trampoline
1313

1414

15-
@attributes([
16-
'intent', 'callbacks',
17-
], apply_with_init=False, apply_immutable=True)
15+
@attr.s
1816
class Effect(object):
1917
"""
2018
Take an object that describes a desired effect (called an "Intent"), and
2119
allow binding callbacks to be called with the result of the effect.
2220
2321
Effects can be performed with :func:`perform`.
22+
23+
:param intent: The intent to be performed.
2424
"""
25-
def __init__(self, intent, callbacks=None):
26-
"""
27-
:param intent: An object that describes an effect to be performed.
28-
"""
29-
self.intent = intent
30-
if callbacks is None:
31-
callbacks = []
32-
self.callbacks = callbacks
25+
26+
intent = attr.ib()
27+
callbacks = attr.ib(default=attr.Factory(list))
3328

3429
def on(self, success=None, error=None):
3530
"""

effect/_dispatcher.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,35 @@
22
Dispatcher!
33
"""
44

5+
import attr
6+
57
from six.moves import filter
6-
from characteristic import attributes
78

89

9-
@attributes(['mapping'], apply_with_init=False)
10+
@attr.s
1011
class TypeDispatcher(object):
1112
"""
1213
An Effect dispatcher which looks up the performer to use by type.
14+
15+
:param mapping: mapping of intent type to performer
1316
"""
14-
def __init__(self, mapping):
15-
"""
16-
:param collections.Mapping mapping: mapping of intent type to performer
17-
"""
18-
self.mapping = mapping
17+
mapping = attr.ib()
1918

2019
def __call__(self, intent):
2120
return self.mapping.get(type(intent))
2221

2322

24-
@attributes(['dispatchers'], apply_with_init=False)
23+
@attr.s
2524
class ComposedDispatcher(object):
2625
"""
2726
A dispatcher which composes other dispatchers.
2827
2928
The dispatchers given will be searched in order until a performer is found.
29+
30+
:param dispatchers: Dispatchers to search.
3031
"""
31-
def __init__(self, dispatchers):
32-
"""
33-
:param collections.Iterable dispatchers: Dispatchers to search.
34-
"""
35-
self.dispatchers = dispatchers
32+
33+
dispatchers = attr.ib()
3634

3735
def __call__(self, intent):
3836
return next(filter(None, (d(intent) for d in self.dispatchers)), None)

effect/_intents.py

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616

1717

1818
from __future__ import print_function, absolute_import
19-
from characteristic import attributes
19+
20+
import attr
2021

2122
from ._base import Effect
2223
from ._sync import sync_performer
2324
from ._dispatcher import TypeDispatcher
2425

2526

26-
@attributes(['effects'], apply_with_init=False, apply_immutable=True)
27+
@attr.s
2728
class ParallelEffects(object):
2829
"""
2930
An effect intent that asks for a number of effects to be run in parallel,
@@ -40,12 +41,11 @@ class ParallelEffects(object):
4041
4142
Performers of this intent must fail with a :obj:`FirstError` exception when
4243
any child effect fails, representing the first error.
44+
45+
:param effects: Effects to be performed in parallel.
4346
"""
44-
def __init__(self, effects):
45-
"""
46-
:param effects: Effects which should be performed in parallel.
47-
"""
48-
self.effects = effects
47+
48+
effects = attr.ib()
4949

5050

5151
def parallel(effects):
@@ -83,40 +83,41 @@ def parallel_all_errors(effects):
8383
return Effect(ParallelEffects(list(effects)))
8484

8585

86-
@attributes(['exc_info', 'index'])
86+
@attr.s
8787
class FirstError(Exception):
8888
"""
8989
One of the effects in a :obj:`ParallelEffects` resulted in an error. This
9090
represents the first such error that occurred.
9191
"""
92+
exc_info = attr.ib()
93+
index = attr.ib()
94+
9295
def __str__(self):
9396
return '(index=%s) %s: %s' % (
9497
self.index, self.exc_info[0].__name__, self.exc_info[1])
9598

9699

97-
@attributes(['delay'], apply_with_init=False, apply_immutable=True)
100+
@attr.s
98101
class Delay(object):
99102
"""
100103
An intent which represents a delay in time.
101104
102105
When performed, the specified delay will pass and then the effect will
103106
result in None.
107+
108+
:param float delay: The number of seconds to delay.
104109
"""
105-
def __init__(self, delay):
106-
"""
107-
:param float delay: The number of seconds to delay.
108-
"""
109-
self.delay = delay
110+
delay = attr.ib()
110111

111112

112-
@attributes(['result'], apply_with_init=False, apply_immutable=True)
113+
@attr.s
113114
class Constant(object):
114-
"""An intent that returns a pre-specified result when performed."""
115-
def __init__(self, result):
116-
"""
117-
:param result: The object which the Effect should result in.
118-
"""
119-
self.result = result
115+
"""
116+
An intent that returns a pre-specified result when performed.
117+
118+
:param result: The object which the Effect will result in.
119+
"""
120+
result = attr.ib()
120121

121122

122123
@sync_performer
@@ -125,11 +126,14 @@ def perform_constant(dispatcher, intent):
125126
return intent.result
126127

127128

128-
@attributes(['exception'], apply_with_init=False, apply_immutable=True)
129+
@attr.s
129130
class Error(object):
130-
"""An intent that raises a pre-specified exception when performed."""
131-
def __init__(self, exception):
132-
self.exception = exception
131+
"""
132+
An intent that raises a pre-specified exception when performed.
133+
134+
:param BaseException exception: Exception instance to raise.
135+
"""
136+
exception = attr.ib()
133137

134138

135139
@sync_performer
@@ -138,7 +142,7 @@ def perform_error(dispatcher, intent):
138142
raise intent.exception
139143

140144

141-
@attributes(['func'], apply_with_init=False, apply_immutable=True)
145+
@attr.s
142146
class Func(object):
143147
"""
144148
An intent that returns the result of the specified function.
@@ -157,12 +161,10 @@ class Func(object):
157161
intents as inert objects with public attributes of simple data. However,
158162
this is useful for integrating wih "legacy" side-effecting code in a quick
159163
way.
164+
165+
:param func: The function to call when this intent is performed.
160166
"""
161-
def __init__(self, func):
162-
"""
163-
:param func: The function to call when this intent is performed.
164-
"""
165-
self.func = func
167+
func = attr.ib()
166168

167169

168170
@sync_performer

effect/_test_utils.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
import sys
44
import traceback
55

6-
from characteristic import attributes
6+
import attr
77

88
from testtools.matchers import Equals
99

1010

11-
@attributes(['expected_tb', 'got_tb'])
11+
@attr.s
1212
class ReraisedTracebackMismatch(object):
13+
expected_tb = attr.ib()
14+
got_tb = attr.ib()
15+
1316
def describe(self):
1417
return ("The reference traceback:\n"
1518
+ ''.join(self.expected_tb)
@@ -18,11 +21,10 @@ def describe(self):
1821
+ "\nbut it doesn't.")
1922

2023

21-
@attributes(['expected'], apply_with_init=False)
24+
@attr.s
2225
class MatchesReraisedExcInfo(object):
2326

24-
def __init__(self, expected):
25-
self.expected = expected
27+
expected = attr.ib()
2628

2729
def match(self, actual):
2830
valcheck = Equals(self.expected[1]).match(actual[1])

effect/ref.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from characteristic import attributes
1+
import attr
22

33
from ._base import Effect
44
from ._dispatcher import TypeDispatcher
@@ -42,12 +42,14 @@ def __repr__(self):
4242
return "<Reference({})>".format(self._value)
4343

4444

45-
@attributes(['ref'])
45+
@attr.s
4646
class ReadReference(object):
4747
"""Intent that gets a Reference's current value."""
4848

49+
ref = attr.ib()
4950

50-
@attributes(['ref', 'transformer'])
51+
52+
@attr.s
5153
class ModifyReference(object):
5254
"""
5355
Intent that modifies a Reference value in-place with a transformer func.
@@ -56,6 +58,9 @@ class ModifyReference(object):
5658
modifying the same reference at the same time.
5759
"""
5860

61+
ref = attr.ib()
62+
transformer = attr.ib()
63+
5964

6065
@sync_performer
6166
def perform_read_reference(dispatcher, intent):

effect/test_parallel_performers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from functools import partial
22

3-
from characteristic import attributes
3+
import attr
44

55
import six
66

@@ -12,9 +12,9 @@
1212
from ._test_utils import MatchesReraisedExcInfo, get_exc_info
1313

1414

15-
@attributes(['message'])
15+
@attr.s
1616
class EquitableException(Exception):
17-
pass
17+
message = attr.ib()
1818

1919

2020
class ParallelPerformerTestsMixin(object):

0 commit comments

Comments
 (0)