]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Make ArgReplacer compatible with cython-compiled functions.
authorBen Darnell <ben@bendarnell.com>
Mon, 14 Sep 2015 04:50:26 +0000 (00:50 -0400)
committerBen Darnell <ben@bendarnell.com>
Mon, 14 Sep 2015 04:50:26 +0000 (00:50 -0400)
This requires a directive in the cython code to make the signature
introspectable.

maint/test/cython/cythonapp.pyx
maint/test/cython/cythonapp_test.py
tornado/util.py

index 3bd1bc9deb3a9c5b3cc2f82a3b73b04904ac9dad..0539a22899dc1daa6cac67d80f3fd57c6965c4bd 100644 (file)
@@ -1,3 +1,4 @@
+import cython
 from tornado import gen
 import pythonmodule
 
@@ -13,3 +14,9 @@ def decorated_coroutine():
     if x != "hello":
         raise ValueError("expected hello, got %r" % x)
     return "goodbye"
+
+# The binding directive is necessary for compatibility with
+# ArgReplacer (and therefore return_future).
+@cython.binding(True)
+def function_with_args(one, two, three):
+    return (one, two, three)
index 76427f4221ea823458bba9e5bb30342066142075..975194ebd93a40cc22f452bbba5b1b94b5f93668 100644 (file)
@@ -6,6 +6,8 @@ else:
     backports_abc.patch()
 
 from tornado.testing import AsyncTestCase, gen_test
+from tornado.util import ArgReplacer
+import unittest
 
 import cythonapp
 
@@ -20,3 +22,13 @@ class CythonCoroutineTest(AsyncTestCase):
     def test_decorated_coroutine(self):
         x = yield cythonapp.decorated_coroutine()
         self.assertEqual(x, "goodbye")
+
+
+class CythonArgReplacerTest(unittest.TestCase):
+    def test_arg_replacer(self):
+        replacer = ArgReplacer(cythonapp.function_with_args, 'two')
+        args = (1, 'old', 3)
+        kwargs = {}
+        self.assertEqual(replacer.get_old_value(args, kwargs), 'old')
+        self.assertEqual(replacer.replace('new', args, kwargs),
+                         ('old', [1, 'new', 3], {}))
index ea4da8763310bbc54ca5f292929cd47a6e15f4bd..ce5362dc0812636c10032b3b6980036119db2833 100644 (file)
@@ -290,11 +290,25 @@ class ArgReplacer(object):
     def __init__(self, func, name):
         self.name = name
         try:
-            self.arg_pos = getargspec(func).args.index(self.name)
+            self.arg_pos = self._getargnames(func).index(name)
         except ValueError:
             # Not a positional parameter
             self.arg_pos = None
 
+    def _getargnames(self, func):
+        try:
+            return getargspec(func).args
+        except TypeError:
+            if hasattr(func, 'func_code'):
+                # Cython-generated code has all the attributes needed
+                # by inspect.getargspec (when the
+                # @cython.binding(True) directive is used), but the
+                # inspect module only works with ordinary functions.
+                # Inline the portion of getargspec that we need here.
+                code = func.func_code
+                return code.co_varnames[:code.co_argcount]
+            raise
+
     def get_old_value(self, args, kwargs, default=None):
         """Returns the old value of the named argument without replacing it.