Skip to content

Commit 0ef082c

Browse files
committed
Remove curry.__getattr__ forwarding and comment out the tests this affects.
IMHO, this is a failure of how Python handles `__qualname__` during pickling. Objects wrapped by decorators should only need to use `__wrapped__` for pickling to work as expected. It's unreasonable to expect wrapping objects such as `curry` to forward attributes to allow pickling to work.
1 parent 7d9ad01 commit 0ef082c

File tree

3 files changed

+46
-41
lines changed

3 files changed

+46
-41
lines changed

toolz/functoolz.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ def __init__(self, *args, **kwargs):
201201
self.__doc__ = getattr(func, '__doc__', None)
202202
self.__name__ = getattr(func, '__name__', '<curry>')
203203
self.__module__ = getattr(func, '__module__', None)
204+
self.__qualname__ = getattr(func, '__qualname__', None)
204205
self._sigspec = None
205206
self._has_unknown_args = None
206207

@@ -259,9 +260,6 @@ def keywords(self):
259260
def func_name(self):
260261
return self.__name__
261262

262-
def __getattr__(self, attr):
263-
return getattr(self.func, attr)
264-
265263
def __str__(self):
266264
return str(self.func)
267265

toolz/tests/test_functoolz.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,14 +285,17 @@ def foo(a, b, c=1):
285285
def test_curry_attributes_writable():
286286
def foo(a, b, c=1):
287287
return a + b + c
288-
288+
foo.__qualname__ = 'this.is.foo'
289289
f = curry(foo, 1, c=2)
290+
assert f.__qualname__ == 'this.is.foo'
290291
f.__name__ = 'newname'
291292
f.__doc__ = 'newdoc'
292293
f.__module__ = 'newmodule'
294+
f.__qualname__ = 'newqualname'
293295
assert f.__name__ == 'newname'
294296
assert f.__doc__ == 'newdoc'
295297
assert f.__module__ == 'newmodule'
298+
assert f.__qualname__ == 'newqualname'
296299
if hasattr(f, 'func_name'):
297300
assert f.__name__ == f.func_name
298301

toolz/tests/test_serialization.py

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def g3(self):
116116
def test_curried_qualname():
117117
if not PY3:
118118
return
119+
119120
def preserves_identity(obj):
120121
return pickle.loads(pickle.dumps(obj)) is obj
121122

@@ -124,22 +125,6 @@ def preserves_identity(obj):
124125
assert preserves_identity(GlobalCurried.func.NestedCurried)
125126
assert preserves_identity(GlobalCurried.func.NestedCurried.func.f2)
126127
assert preserves_identity(GlobalCurried.func.Nested.f3)
127-
if not PY33 and not PY34:
128-
assert preserves_identity(GlobalCurried.func.g1)
129-
assert preserves_identity(GlobalCurried.func.NestedCurried.func.g2)
130-
assert preserves_identity(GlobalCurried.func.Nested)
131-
assert preserves_identity(GlobalCurried.func.Nested.g3)
132-
133-
# Rely on curry.__getattr__
134-
assert preserves_identity(GlobalCurried.f1)
135-
assert preserves_identity(GlobalCurried.NestedCurried)
136-
assert preserves_identity(GlobalCurried.NestedCurried.f2)
137-
assert preserves_identity(GlobalCurried.Nested.f3)
138-
if not PY33 and not PY34:
139-
assert preserves_identity(GlobalCurried.g1)
140-
assert preserves_identity(GlobalCurried.NestedCurried.g2)
141-
assert preserves_identity(GlobalCurried.Nested)
142-
assert preserves_identity(GlobalCurried.Nested.g3)
143128

144129
global_curried1 = GlobalCurried(1)
145130
global_curried2 = pickle.loads(pickle.dumps(global_curried1))
@@ -156,31 +141,50 @@ def preserves_identity(obj):
156141
assert func1 is not func2
157142
assert func1(4) == func2(4) == 10
158143

159-
nested_curried1 = GlobalCurried.NestedCurried(1)
144+
nested_curried1 = GlobalCurried.func.NestedCurried(1)
160145
nested_curried2 = pickle.loads(pickle.dumps(nested_curried1))
161146
assert nested_curried1 is not nested_curried2
162147
assert nested_curried1(2).f2(3, 4) == nested_curried2(2).f2(3, 4) == 10
163148

164-
nested_curried3 = nested_curried1(2)
165-
nested_curried4 = pickle.loads(pickle.dumps(nested_curried3))
166-
assert nested_curried3 is not nested_curried4
167-
assert nested_curried3.f2(3, 4) == nested_curried4.f2(3, 4) == 10
168-
169-
func1 = nested_curried1(2).f2(3)
170-
func2 = pickle.loads(pickle.dumps(func1))
171-
assert func1 is not func2
172-
assert func1(4) == func2(4) == 10
173-
174-
if not PY33 and not PY34:
175-
nested3 = GlobalCurried.Nested(1, 2)
176-
nested4 = pickle.loads(pickle.dumps(nested3))
177-
assert nested3 is not nested4
178-
assert nested3.f3(3, 4) == nested4.f3(3, 4) == 10
179-
180-
func1 = nested3.f3(3)
181-
func2 = pickle.loads(pickle.dumps(func1))
182-
assert func1 is not func2
183-
assert func1(4) == func2(4) == 10
149+
# If we add `curry.__getattr__` forwarding, the following tests will pass
150+
151+
# if not PY33 and not PY34:
152+
# assert preserves_identity(GlobalCurried.func.g1)
153+
# assert preserves_identity(GlobalCurried.func.NestedCurried.func.g2)
154+
# assert preserves_identity(GlobalCurried.func.Nested)
155+
# assert preserves_identity(GlobalCurried.func.Nested.g3)
156+
#
157+
# # Rely on curry.__getattr__
158+
# assert preserves_identity(GlobalCurried.f1)
159+
# assert preserves_identity(GlobalCurried.NestedCurried)
160+
# assert preserves_identity(GlobalCurried.NestedCurried.f2)
161+
# assert preserves_identity(GlobalCurried.Nested.f3)
162+
# if not PY33 and not PY34:
163+
# assert preserves_identity(GlobalCurried.g1)
164+
# assert preserves_identity(GlobalCurried.NestedCurried.g2)
165+
# assert preserves_identity(GlobalCurried.Nested)
166+
# assert preserves_identity(GlobalCurried.Nested.g3)
167+
#
168+
# nested_curried3 = nested_curried1(2)
169+
# nested_curried4 = pickle.loads(pickle.dumps(nested_curried3))
170+
# assert nested_curried3 is not nested_curried4
171+
# assert nested_curried3.f2(3, 4) == nested_curried4.f2(3, 4) == 10
172+
#
173+
# func1 = nested_curried1(2).f2(3)
174+
# func2 = pickle.loads(pickle.dumps(func1))
175+
# assert func1 is not func2
176+
# assert func1(4) == func2(4) == 10
177+
#
178+
# if not PY33 and not PY34:
179+
# nested3 = GlobalCurried.func.Nested(1, 2)
180+
# nested4 = pickle.loads(pickle.dumps(nested3))
181+
# assert nested3 is not nested4
182+
# assert nested3.f3(3, 4) == nested4.f3(3, 4) == 10
183+
#
184+
# func1 = nested3.f3(3)
185+
# func2 = pickle.loads(pickle.dumps(func1))
186+
# assert func1 is not func2
187+
# assert func1(4) == func2(4) == 10
184188

185189

186190
def test_curried_bad_qualname():

0 commit comments

Comments
 (0)