fix builtin hasattr() to suppress only AttributeError exceptions
authorLisandro Dalcin <dalcinl@gmail.com>
Wed, 23 Feb 2011 00:57:11 +0000 (21:57 -0300)
committerLisandro Dalcin <dalcinl@gmail.com>
Wed, 23 Feb 2011 00:57:11 +0000 (21:57 -0300)
Cython/Compiler/Builtin.py
tests/run/hasattr.pyx [new file with mode: 0644]

index 7c88272412d3739fb641f85ab6f5e0873d7cbfde..d9c4207e184ed63a1f7eea5528748e9ae8081f27 100644 (file)
@@ -76,6 +76,25 @@ bad:
 }
 """)
 
+hasattr_utility_code = UtilityCode(
+proto = """
+static CYTHON_INLINE int __Pyx_HasAttr(PyObject *, PyObject *); /*proto*/
+""",
+impl = """
+static CYTHON_INLINE int __Pyx_HasAttr(PyObject *o, PyObject *n) {
+    PyObject *v = PyObject_GetAttr(o, n);
+    if (v) {
+        Py_DECREF(v);
+        return 1;
+    }
+    if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+        PyErr_Clear();
+        return 0;
+    }
+    return -1;
+}
+""")
+
 pyexec_utility_code = UtilityCode(
 proto = """
 #if PY_VERSION_HEX < 0x02040000
@@ -365,7 +384,8 @@ builtin_function_table = [
                     utility_code = getattr3_utility_code),
     BuiltinFunction('getattr3',   "OOO",  "O",     "__Pyx_GetAttr3",     "getattr",
                     utility_code = getattr3_utility_code), # Pyrex compatibility
-    BuiltinFunction('hasattr',    "OO",   "b",     "PyObject_HasAttr"),
+    BuiltinFunction('hasattr',    "OO",   "b",     "__Pyx_HasAttr",
+                    utility_code = hasattr_utility_code),
     BuiltinFunction('hash',       "O",    "h",     "PyObject_Hash"),
     #('hex',       "",     "",      ""),
     #('id',        "",     "",      ""),
diff --git a/tests/run/hasattr.pyx b/tests/run/hasattr.pyx
new file mode 100644 (file)
index 0000000..de130ab
--- /dev/null
@@ -0,0 +1,29 @@
+class Foo:
+    @property
+    def foo(self):
+        return None
+    @property
+    def bar(self):
+        raise AttributeError
+    @property
+    def baz(self):
+        return int(1)/int(0)
+
+def wrap_hasattr(obj, name):
+    """
+    >>> wrap_hasattr(None, "abc")
+    False
+    >>> wrap_hasattr(list, "append")
+    True
+    >>> wrap_hasattr(Foo(), "foo")
+    True
+    >>> wrap_hasattr(Foo(), "spam")
+    False
+    >>> wrap_hasattr(Foo(), "bar")
+    False
+    >>> wrap_hasattr(Foo(), "baz") #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+       ...
+    ZeroDivisionError: ...
+    """
+    return hasattr(obj, name)