Skip to content

Commit

Permalink
Merge pull request #116 from itamarst/wrapt-issues-115
Browse files Browse the repository at this point in the history
Fix regression in 1.8.
  • Loading branch information
itamarst committed Aug 17, 2017
2 parents 2b87285 + 4982364 commit 779e918
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 22 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ MANIFEST
# Documentation
docs/_build

build/
build/
.tox
17 changes: 5 additions & 12 deletions crochet/_eventloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from __future__ import absolute_import

import select
import sys
import threading
import weakref
import warnings
Expand Down Expand Up @@ -463,15 +462,11 @@ def run_in_reactor(self, function):
When the wrapped function is called, an EventualResult is returned.
"""
result = self._run_in_reactor(function)
# Backwards compatibility; we could have used wrapt's version, but
# older Crochet code exposed different attribute name:
# Backwards compatibility; use __wrapped__ instead.
try:
result.wrapped_function = function
except AttributeError:
if sys.version_info[0] == 3:
raise
# In Python 2 e.g. classmethod has some limitations where you can't
# set stuff on it.
pass
return result

def wait_for_reactor(self, function):
Expand Down Expand Up @@ -518,14 +513,12 @@ def run():
raise

result = wrapper(function)
# Expose underling function for testing purposes:
# Expose underling function for testing purposes; this attribute is
# deprecated, use __wrapped__ instead:
try:
result.wrapped_function = function
except AttributeError:
if sys.version_info[0] == 3:
raise
# In Python 2 e.g. classmethod has some limitations where you
# can't set stuff on it.
pass
return result

return decorator
Expand Down
24 changes: 22 additions & 2 deletions crochet/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,23 @@ def func2(cls, a, b, c):
C.func2(1, 2, c=3)
self.assertEqual(calls, [(C, 1, 2, 3), (C, 1, 2, 3)])

def test_wrap_method(self):
"""
The object decorated with the wait decorator can be a method object
"""
myreactor = FakeReactor()
c = EventLoop(lambda: myreactor, lambda f, g: None)
c.no_setup()
calls = []

class C(object):
def func(self, a, b, c):
calls.append((a, b, c))

f = c.run_in_reactor(C().func)
f(4, 5, c=6)
self.assertEqual(calls, [(4, 5, 6)])

def make_wrapped_function(self):
"""
Return a function wrapped with run_in_reactor that returns its first
Expand Down Expand Up @@ -809,14 +826,16 @@ def run():
def test_wrapped_function(self):
"""
The function wrapped by @run_in_reactor can be accessed via the
`wrapped_function` attribute.
`__wrapped__` attribute and the `wrapped_function` attribute
(deprecated, and not always available).
"""
c = EventLoop(lambda: None, lambda f, g: None)

def func():
pass

wrapper = c.run_in_reactor(func)
self.assertIdentical(wrapper.__wrapped__, func)
self.assertIdentical(wrapper.wrapped_function, func)


Expand Down Expand Up @@ -881,14 +900,15 @@ def some_name(arg1, arg2, karg1=2, *args, **kw):
def test_wrapped_function(self):
"""
The function wrapped by the wait decorator can be accessed via the
`wrapped_function` attribute.
`__wrapped__`, and the deprecated `wrapped_function` attribute.
"""
decorator = self.decorator()

def func():
pass

wrapper = decorator(func)
self.assertIdentical(wrapper.__wrapped__, func)
self.assertIdentical(wrapper.wrapped_function, func)

def test_reactor_thread_disallowed(self):
Expand Down
6 changes: 3 additions & 3 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,14 @@ Unit testing
^^^^^^^^^^^^

Both ``@wait_for`` and ``@run_in_reactor`` expose the underlying Twisted
function via a ``wrapped_function`` attribute. This allows unit testing of the
function via a ``__wrapped__`` attribute. This allows unit testing of the
Twisted code without having to go through the Crochet layer.

.. literalinclude:: ../examples/testing.py

When run, this gives the following output::

add() returns EventualResult:
add(1, 2) returns EventualResult:
<crochet._eventloop.EventualResult object at 0x2e8b390>
add.wrapped_function() returns result of underlying function:
add.__wrapped__(1, 2) returns result of underlying function:
3
19 changes: 18 additions & 1 deletion docs/news.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
What's New
==========

1.9.0
^^^^^

New features:

* The underlying callable wrapped ``@run_in_reactor`` and ``@wait_for`` is now available via the more standard ``__wrapped__`` attribute.

Backwards incompatibility (in tests):

* This was actually introduced in 1.8.0: ``wrapped_function`` may not always be available on decorated callables.
You should use ``__wrapped__`` instead.

Bug fixes:

* Fixed regression in 1.8.0 where bound method couldn't be wrapped.
Thanks to 2mf for the bug report.

1.8.0
^^^^^

Expand All @@ -23,7 +40,7 @@ Bug fixes:

Bug fixes:

* If the Python `logging.Handler` throws an exception Crochet no longer goes into a death spiral.
* If the Python ``logging.Handler`` throws an exception Crochet no longer goes into a death spiral.
Thanks to Michael Schlenker for the bug report.

Removed features:
Expand Down
6 changes: 3 additions & 3 deletions examples/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def add(x, y):


if __name__ == '__main__':
print("add() returns EventualResult:")
print("add(1, 2) returns EventualResult:")
print(" ", add(1, 2))
print("add.wrapped_function() returns result of underlying function:")
print(" ", add.wrapped_function(1, 2))
print("add.__wrapped__(1, 2) is the result of the underlying function:")
print(" ", add.__wrapped__(1, 2))
17 changes: 17 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[tox]
envlist = py27, py35, lint3
usedevelop = true

[testenv]
commands =
{envpython} setup.py --version
pip install .
{envpython} -m unittest {posargs:discover -v crochet.tests}

[testenv:lint3]
deps = flake8
pylint
yapf
basepython = python3.5
commands = flake8 crochet
pylint crochet

0 comments on commit 779e918

Please sign in to comment.