[Python-checkins] bpo-32571: Avoid raising unneeded AttributeError and silencing it in C code (GH-5222)

INADA Naoki webhook-mailer at python.org
Thu Jan 25 03:49:46 EST 2018


https://github.com/python/cpython/commit/f320be77ffb73e3b9e7fc98c37b8df3975d84b40
commit: f320be77ffb73e3b9e7fc98c37b8df3975d84b40
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: INADA Naoki <methane at users.noreply.github.com>
date: 2018-01-25T17:49:40+09:00
summary:

 bpo-32571: Avoid raising unneeded AttributeError and silencing it in C code (GH-5222)

Add two new private APIs: _PyObject_LookupAttr() and _PyObject_LookupAttrId()

files:
M Include/object.h
M Modules/_collectionsmodule.c
M Modules/_io/bufferedio.c
M Modules/_io/fileio.c
M Modules/_io/iobase.c
M Modules/_io/textio.c
M Modules/_pickle.c
M Modules/arraymodule.c
M Modules/itertoolsmodule.c
M Objects/abstract.c
M Objects/classobject.c
M Objects/dictobject.c
M Objects/genobject.c
M Objects/object.c
M Objects/odictobject.c
M Objects/typeobject.c
M Parser/asdl_c.py
M Python/Python-ast.c
M Python/_warnings.c
M Python/bltinmodule.c
M Python/ceval.c
M Python/codecs.c

diff --git a/Include/object.h b/Include/object.h
index 274b67656ee..c772deaf57d 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -536,11 +536,20 @@ PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *);
 PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *);
 #ifndef Py_LIMITED_API
 PyAPI_FUNC(int) _PyObject_IsAbstract(PyObject *);
-/* Same as PyObject_GetAttr(), but don't raise AttributeError. */
-PyAPI_FUNC(PyObject *) _PyObject_GetAttrWithoutError(PyObject *, PyObject *);
 PyAPI_FUNC(PyObject *) _PyObject_GetAttrId(PyObject *, struct _Py_Identifier *);
 PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *);
 PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *);
+/* Replacements of PyObject_GetAttr() and _PyObject_GetAttrId() which
+   don't raise AttributeError.
+
+   Return 1 and set *result != NULL if an attribute is found.
+   Return 0 and set *result == NULL if an attribute is not found;
+   an AttributeError is silenced.
+   Return -1 and set *result == NULL if an error other than AttributeError
+   is raised.
+*/
+PyAPI_FUNC(int) _PyObject_LookupAttr(PyObject *, PyObject *, PyObject **);
+PyAPI_FUNC(int) _PyObject_LookupAttrId(PyObject *, struct _Py_Identifier *, PyObject **);
 PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *);
 #endif
 PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *);
diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c
index 222cace5af7..5753dd946a5 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -1339,12 +1339,10 @@ deque_reduce(dequeobject *deque)
     PyObject *dict, *it;
     _Py_IDENTIFIER(__dict__);
 
-    dict = _PyObject_GetAttrId((PyObject *)deque, &PyId___dict__);
+    if (_PyObject_LookupAttrId((PyObject *)deque, &PyId___dict__, &dict) < 0) {
+        return NULL;
+    }
     if (dict == NULL) {
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            return NULL;
-        }
-        PyErr_Clear();
         dict = Py_None;
         Py_INCREF(dict);
     }
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index b81abde5c0a..2b7aaaf7c7d 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -1541,7 +1541,9 @@ _bufferedreader_read_all(buffered *self)
     }
     _bufferedreader_reset_buf(self);
 
-    readall = _PyObject_GetAttrWithoutError(self->raw, _PyIO_str_readall);
+    if (_PyObject_LookupAttr(self->raw, _PyIO_str_readall, &readall) < 0) {
+        goto cleanup;
+    }
     if (readall) {
         tmp = _PyObject_CallNoArg(readall);
         Py_DECREF(readall);
@@ -1561,9 +1563,6 @@ _bufferedreader_read_all(buffered *self)
         }
         goto cleanup;
     }
-    else if (PyErr_Occurred()) {
-        goto cleanup;
-    }
 
     chunks = PyList_New(0);
     if (chunks == NULL)
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
index 269142cfee5..0d5bf3b2211 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -1073,12 +1073,10 @@ fileio_repr(fileio *self)
     if (self->fd < 0)
         return PyUnicode_FromFormat("<_io.FileIO [closed]>");
 
-    nameobj = _PyObject_GetAttrId((PyObject *) self, &PyId_name);
+    if (_PyObject_LookupAttrId((PyObject *) self, &PyId_name, &nameobj) < 0) {
+        return NULL;
+    }
     if (nameobj == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError))
-            PyErr_Clear();
-        else
-            return NULL;
         res = PyUnicode_FromFormat(
             "<_io.FileIO fd=%d mode='%s' closefd=%s>",
             self->fd, mode_string(self), self->closefd ? "True" : "False");
diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c
index 348d449a272..0c329bff840 100644
--- a/Modules/_io/iobase.c
+++ b/Modules/_io/iobase.c
@@ -133,18 +133,12 @@ static int
 iobase_is_closed(PyObject *self)
 {
     PyObject *res;
+    int ret;
     /* This gets the derived attribute, which is *not* __IOBase_closed
        in most cases! */
-    res = _PyObject_GetAttrId(self, &PyId___IOBase_closed);
-    if (res == NULL) {
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            return -1;
-        }
-        PyErr_Clear();
-        return 0;
-    }
-    Py_DECREF(res);
-    return 1;
+    ret = _PyObject_LookupAttrId(self, &PyId___IOBase_closed, &res);
+    Py_XDECREF(res);
+    return ret;
 }
 
 /* Flush and close methods */
@@ -190,20 +184,16 @@ iobase_check_closed(PyObject *self)
     int closed;
     /* This gets the derived attribute, which is *not* __IOBase_closed
        in most cases! */
-    res = _PyObject_GetAttrWithoutError(self, _PyIO_str_closed);
-    if (res == NULL) {
-        if (PyErr_Occurred()) {
+    closed = _PyObject_LookupAttr(self, _PyIO_str_closed, &res);
+    if (closed > 0) {
+        closed = PyObject_IsTrue(res);
+        Py_DECREF(res);
+        if (closed > 0) {
+            PyErr_SetString(PyExc_ValueError, "I/O operation on closed file.");
             return -1;
         }
-        return 0;
-    }
-    closed = PyObject_IsTrue(res);
-    Py_DECREF(res);
-    if (closed <= 0) {
-        return closed;
     }
-    PyErr_SetString(PyExc_ValueError, "I/O operation on closed file.");
-    return -1;
+    return closed;
 }
 
 PyObject *
@@ -273,8 +263,7 @@ iobase_finalize(PyObject *self)
 
     /* If `closed` doesn't exist or can't be evaluated as bool, then the
        object is probably in an unusable state, so ignore. */
-    res = _PyObject_GetAttrWithoutError(self, _PyIO_str_closed);
-    if (res == NULL) {
+    if (_PyObject_LookupAttr(self, _PyIO_str_closed, &res) <= 0) {
         PyErr_Clear();
         closed = -1;
     }
@@ -538,8 +527,7 @@ _io__IOBase_readline_impl(PyObject *self, Py_ssize_t limit)
     PyObject *peek, *buffer, *result;
     Py_ssize_t old_size = -1;
 
-    peek = _PyObject_GetAttrWithoutError(self, _PyIO_str_peek);
-    if (peek == NULL && PyErr_Occurred()) {
+    if (_PyObject_LookupAttr(self, _PyIO_str_peek, &peek) < 0) {
         return NULL;
     }
 
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index 9f3fd2d2346..717b56ab319 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -924,14 +924,10 @@ _textiowrapper_set_encoder(textio *self, PyObject *codec_info,
         return -1;
 
     /* Get the normalized named of the codec */
-    res = _PyObject_GetAttrId(codec_info, &PyId_name);
-    if (res == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError))
-            PyErr_Clear();
-        else
-            return -1;
+    if (_PyObject_LookupAttrId(codec_info, &PyId_name, &res) < 0) {
+        return -1;
     }
-    else if (PyUnicode_Check(res)) {
+    if (res != NULL && PyUnicode_Check(res)) {
         const encodefuncentry *e = encodefuncs;
         while (e->name != NULL) {
             if (_PyUnicode_EqualToASCIIString(res, e->name)) {
@@ -1177,19 +1173,17 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
 
     if (Py_TYPE(buffer) == &PyBufferedReader_Type ||
         Py_TYPE(buffer) == &PyBufferedWriter_Type ||
-        Py_TYPE(buffer) == &PyBufferedRandom_Type) {
-        raw = _PyObject_GetAttrId(buffer, &PyId_raw);
+        Py_TYPE(buffer) == &PyBufferedRandom_Type)
+    {
+        if (_PyObject_LookupAttrId(buffer, &PyId_raw, &raw) < 0)
+            goto error;
         /* Cache the raw FileIO object to speed up 'closed' checks */
-        if (raw == NULL) {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError))
-                PyErr_Clear();
+        if (raw != NULL) {
+            if (Py_TYPE(raw) == &PyFileIO_Type)
+                self->raw = raw;
             else
-                goto error;
+                Py_DECREF(raw);
         }
-        else if (Py_TYPE(raw) == &PyFileIO_Type)
-            self->raw = raw;
-        else
-            Py_DECREF(raw);
     }
 
     res = _PyObject_CallMethodId(buffer, &PyId_seekable, NULL);
@@ -1201,17 +1195,12 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
         goto error;
     self->seekable = self->telling = r;
 
-    res = _PyObject_GetAttrWithoutError(buffer, _PyIO_str_read1);
-    if (res != NULL) {
-        Py_DECREF(res);
-        self->has_read1 = 1;
-    }
-    else if (!PyErr_Occurred()) {
-        self->has_read1 = 0;
-    }
-    else {
+    r = _PyObject_LookupAttr(buffer, _PyIO_str_read1, &res);
+    if (r < 0) {
         goto error;
     }
+    Py_XDECREF(res);
+    self->has_read1 = r;
 
     self->encoding_start_of_stream = 0;
     if (_textiowrapper_fix_encoder_state(self) < 0) {
@@ -3020,10 +3009,9 @@ textiowrapper_newlines_get(textio *self, void *context)
 {
     PyObject *res;
     CHECK_ATTACHED(self);
-    if (self->decoder == NULL)
-        Py_RETURN_NONE;
-    res = _PyObject_GetAttrWithoutError(self->decoder, _PyIO_str_newlines);
-    if (res == NULL && !PyErr_Occurred()) {
+    if (self->decoder == NULL ||
+        _PyObject_LookupAttr(self->decoder, _PyIO_str_newlines, &res) == 0)
+    {
         Py_RETURN_NONE;
     }
     return res;
diff --git a/Modules/_pickle.c b/Modules/_pickle.c
index f06a87d04f2..05f9a023d92 100644
--- a/Modules/_pickle.c
+++ b/Modules/_pickle.c
@@ -367,18 +367,15 @@ init_method_ref(PyObject *self, _Py_Identifier *name,
                 PyObject **method_func, PyObject **method_self)
 {
     PyObject *func, *func2;
+    int ret;
 
     /* *method_func and *method_self should be consistent.  All refcount decrements
        should be occurred after setting *method_self and *method_func. */
-    func = _PyObject_GetAttrId(self, name);
+    ret = _PyObject_LookupAttrId(self, name, &func);
     if (func == NULL) {
         *method_self = NULL;
         Py_CLEAR(*method_func);
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            return -1;
-        }
-        PyErr_Clear();
-        return 0;
+        return ret;
     }
 
     if (PyMethod_Check(func) && PyMethod_GET_SELF(func) == self) {
@@ -1155,11 +1152,12 @@ _Pickler_SetOutputStream(PicklerObject *self, PyObject *file)
 {
     _Py_IDENTIFIER(write);
     assert(file != NULL);
-    self->write = _PyObject_GetAttrId(file, &PyId_write);
+    if (_PyObject_LookupAttrId(file, &PyId_write, &self->write) < 0) {
+        return -1;
+    }
     if (self->write == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError))
-            PyErr_SetString(PyExc_TypeError,
-                            "file must have a 'write' attribute");
+        PyErr_SetString(PyExc_TypeError,
+                        "file must have a 'write' attribute");
         return -1;
     }
 
@@ -1504,19 +1502,16 @@ _Unpickler_SetInputStream(UnpicklerObject *self, PyObject *file)
     _Py_IDENTIFIER(read);
     _Py_IDENTIFIER(readline);
 
-    self->peek = _PyObject_GetAttrId(file, &PyId_peek);
-    if (self->peek == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError))
-            PyErr_Clear();
-        else
-            return -1;
+    if (_PyObject_LookupAttrId(file, &PyId_peek, &self->peek) < 0) {
+        return -1;
     }
-    self->read = _PyObject_GetAttrId(file, &PyId_read);
-    self->readline = _PyObject_GetAttrId(file, &PyId_readline);
+    (void)_PyObject_LookupAttrId(file, &PyId_read, &self->read);
+    (void)_PyObject_LookupAttrId(file, &PyId_readline, &self->readline);
     if (self->readline == NULL || self->read == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError))
+        if (!PyErr_Occurred()) {
             PyErr_SetString(PyExc_TypeError,
                             "file must have 'read' and 'readline' attributes");
+        }
         Py_CLEAR(self->read);
         Py_CLEAR(self->readline);
         Py_CLEAR(self->peek);
@@ -1691,7 +1686,7 @@ get_deep_attribute(PyObject *obj, PyObject *names, PyObject **pparent)
         PyObject *name = PyList_GET_ITEM(names, i);
         Py_XDECREF(parent);
         parent = obj;
-        obj = PyObject_GetAttr(parent, name);
+        (void)_PyObject_LookupAttr(parent, name, &obj);
         if (obj == NULL) {
             Py_DECREF(parent);
             return NULL;
@@ -1704,16 +1699,6 @@ get_deep_attribute(PyObject *obj, PyObject *names, PyObject **pparent)
     return obj;
 }
 
-static void
-reformat_attribute_error(PyObject *obj, PyObject *name)
-{
-    if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-        PyErr_Clear();
-        PyErr_Format(PyExc_AttributeError,
-                     "Can't get attribute %R on %R", name, obj);
-    }
-}
-
 
 static PyObject *
 getattribute(PyObject *obj, PyObject *name, int allow_qualname)
@@ -1727,10 +1712,13 @@ getattribute(PyObject *obj, PyObject *name, int allow_qualname)
         attr = get_deep_attribute(obj, dotted_path, NULL);
         Py_DECREF(dotted_path);
     }
-    else
-        attr = PyObject_GetAttr(obj, name);
-    if (attr == NULL)
-        reformat_attribute_error(obj, name);
+    else {
+        (void)_PyObject_LookupAttr(obj, name, &attr);
+    }
+    if (attr == NULL && !PyErr_Occurred()) {
+        PyErr_Format(PyExc_AttributeError,
+                     "Can't get attribute %R on %R", name, obj);
+    }
     return attr;
 }
 
@@ -1748,9 +1736,6 @@ _checkmodule(PyObject *module_name, PyObject *module,
 
     PyObject *candidate = get_deep_attribute(module, dotted_path, NULL);
     if (candidate == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_Clear();
-        }
         return -1;
     }
     if (candidate != global) {
@@ -1772,14 +1757,10 @@ whichmodule(PyObject *global, PyObject *dotted_path)
     _Py_IDENTIFIER(modules);
     _Py_IDENTIFIER(__main__);
 
-    module_name = _PyObject_GetAttrId(global, &PyId___module__);
-
-    if (module_name == NULL) {
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError))
-            return NULL;
-        PyErr_Clear();
+    if (_PyObject_LookupAttrId(global, &PyId___module__, &module_name) < 0) {
+        return NULL;
     }
-    else {
+    if (module_name) {
         /* In some rare cases (e.g., bound methods of extension types),
            __module__ can be None. If it is so, then search sys.modules for
            the module of global. */
@@ -3328,12 +3309,8 @@ save_global(PicklerObject *self, PyObject *obj, PyObject *name)
         global_name = name;
     }
     else {
-        global_name = _PyObject_GetAttrId(obj, &PyId___qualname__);
-        if (global_name == NULL) {
-            if (!PyErr_ExceptionMatches(PyExc_AttributeError))
-                goto error;
-            PyErr_Clear();
-        }
+        if (_PyObject_LookupAttrId(obj, &PyId___qualname__, &global_name) < 0)
+            goto error;
         if (global_name == NULL) {
             global_name = _PyObject_GetAttrId(obj, &PyId___name__);
             if (global_name == NULL)
@@ -3656,13 +3633,9 @@ get_class(PyObject *obj)
     PyObject *cls;
     _Py_IDENTIFIER(__class__);
 
-    cls = _PyObject_GetAttrId(obj, &PyId___class__);
-    if (cls == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_Clear();
-            cls = (PyObject *) Py_TYPE(obj);
-            Py_INCREF(cls);
-        }
+    if (_PyObject_LookupAttrId(obj, &PyId___class__, &cls) == 0) {
+        cls = (PyObject *) Py_TYPE(obj);
+        Py_INCREF(cls);
     }
     return cls;
 }
@@ -3734,14 +3707,10 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
         PyObject *name;
         _Py_IDENTIFIER(__name__);
 
-        name = _PyObject_GetAttrId(callable, &PyId___name__);
-        if (name == NULL) {
-            if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                return -1;
-            }
-            PyErr_Clear();
+        if (_PyObject_LookupAttrId(callable, &PyId___name__, &name) < 0) {
+            return -1;
         }
-        else if (PyUnicode_Check(name)) {
+        if (name != NULL && PyUnicode_Check(name)) {
             _Py_IDENTIFIER(__newobj_ex__);
             use_newobj_ex = _PyUnicode_EqualToASCIIId(
                     name, &PyId___newobj_ex__);
@@ -4108,7 +4077,9 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
            don't actually have to check for a __reduce__ method. */
 
         /* Check for a __reduce_ex__ method. */
-        reduce_func = _PyObject_GetAttrId(obj, &PyId___reduce_ex__);
+        if (_PyObject_LookupAttrId(obj, &PyId___reduce_ex__, &reduce_func) < 0) {
+            goto error;
+        }
         if (reduce_func != NULL) {
             PyObject *proto;
             proto = PyLong_FromLong(self->proto);
@@ -4119,12 +4090,6 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
         else {
             PickleState *st = _Pickle_GetGlobalState();
 
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_Clear();
-            }
-            else {
-                goto error;
-            }
             /* Check for a __reduce__ method. */
             reduce_func = _PyObject_GetAttrId(obj, &PyId___reduce__);
             if (reduce_func != NULL) {
@@ -4401,13 +4366,9 @@ _pickle_Pickler___init___impl(PicklerObject *self, PyObject *file,
         return -1;
     }
 
-    self->dispatch_table = _PyObject_GetAttrId((PyObject *)self,
-                                               &PyId_dispatch_table);
-    if (self->dispatch_table == NULL) {
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            return -1;
-        }
-        PyErr_Clear();
+    if (_PyObject_LookupAttrId((PyObject *)self,
+                                    &PyId_dispatch_table, &self->dispatch_table) < 0) {
+        return -1;
     }
 
     return 0;
@@ -5370,12 +5331,11 @@ instantiate(PyObject *cls, PyObject *args)
     if (!PyTuple_GET_SIZE(args) && PyType_Check(cls)) {
         _Py_IDENTIFIER(__getinitargs__);
         _Py_IDENTIFIER(__new__);
-        PyObject *func = _PyObject_GetAttrId(cls, &PyId___getinitargs__);
+        PyObject *func;
+        if (_PyObject_LookupAttrId(cls, &PyId___getinitargs__, &func) < 0) {
+            return NULL;
+        }
         if (func == NULL) {
-            if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                return NULL;
-            }
-            PyErr_Clear();
             return _PyObject_CallMethodIdObjArgs(cls, &PyId___new__, cls, NULL);
         }
         Py_DECREF(func);
@@ -6225,16 +6185,11 @@ load_build(UnpicklerObject *self)
 
     inst = self->stack->data[Py_SIZE(self->stack) - 1];
 
-    setstate = _PyObject_GetAttrId(inst, &PyId___setstate__);
-    if (setstate == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError))
-            PyErr_Clear();
-        else {
-            Py_DECREF(state);
-            return -1;
-        }
+    if (_PyObject_LookupAttrId(inst, &PyId___setstate__, &setstate) < 0) {
+        Py_DECREF(state);
+        return -1;
     }
-    else {
+    if (setstate != NULL) {
         PyObject *result;
 
         /* The explicit __setstate__ is responsible for everything. */
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c
index 8c3f0a1c6c6..900c374c585 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -2203,11 +2203,10 @@ array_array___reduce_ex__(arrayobject *self, PyObject *value)
     if (protocol == -1 && PyErr_Occurred())
         return NULL;
 
-    dict = _PyObject_GetAttrId((PyObject *)self, &PyId___dict__);
+    if (_PyObject_LookupAttrId((PyObject *)self, &PyId___dict__, &dict) < 0) {
+        return NULL;
+    }
     if (dict == NULL) {
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError))
-            return NULL;
-        PyErr_Clear();
         dict = Py_None;
         Py_INCREF(dict);
     }
diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c
index 985915f09d5..1113fb6b76c 100644
--- a/Modules/itertoolsmodule.c
+++ b/Modules/itertoolsmodule.c
@@ -830,17 +830,15 @@ tee(PyObject *self, PyObject *args)
         return NULL;
     }
 
-    copyfunc = _PyObject_GetAttrId(it, &PyId___copy__);
-    if (copyfunc != NULL) {
-        copyable = it;
-    }
-    else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
+    if (_PyObject_LookupAttrId(it, &PyId___copy__, &copyfunc) < 0) {
         Py_DECREF(it);
         Py_DECREF(result);
         return NULL;
     }
+    if (copyfunc != NULL) {
+        copyable = it;
+    }
     else {
-        PyErr_Clear();
         copyable = tee_fromiterable(it);
         Py_DECREF(it);
         if (copyable == NULL) {
diff --git a/Objects/abstract.c b/Objects/abstract.c
index a68253bdb43..93eda62a274 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -171,16 +171,14 @@ PyObject_GetItem(PyObject *o, PyObject *key)
     if (PyType_Check(o)) {
         PyObject *meth, *result, *stack[1] = {key};
         _Py_IDENTIFIER(__class_getitem__);
-        meth = _PyObject_GetAttrId(o, &PyId___class_getitem__);
+        if (_PyObject_LookupAttrId(o, &PyId___class_getitem__, &meth) < 0) {
+            return NULL;
+        }
         if (meth) {
             result = _PyObject_FastCall(meth, stack, 1);
             Py_DECREF(meth);
             return result;
         }
-        else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            return NULL;
-        }
-        PyErr_Clear();
     }
 
     return type_error("'%.200s' object is not subscriptable", o);
@@ -2268,14 +2266,9 @@ abstract_get_bases(PyObject *cls)
     PyObject *bases;
 
     Py_ALLOW_RECURSION
-    bases = _PyObject_GetAttrId(cls, &PyId___bases__);
+    (void)_PyObject_LookupAttrId(cls, &PyId___bases__, &bases);
     Py_END_ALLOW_RECURSION
-    if (bases == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError))
-            PyErr_Clear();
-        return NULL;
-    }
-    if (!PyTuple_Check(bases)) {
+    if (bases != NULL && !PyTuple_Check(bases)) {
         Py_DECREF(bases);
         return NULL;
     }
@@ -2338,26 +2331,23 @@ static int
 recursive_isinstance(PyObject *inst, PyObject *cls)
 {
     PyObject *icls;
-    int retval = 0;
+    int retval;
     _Py_IDENTIFIER(__class__);
 
     if (PyType_Check(cls)) {
         retval = PyObject_TypeCheck(inst, (PyTypeObject *)cls);
         if (retval == 0) {
-            PyObject *c = _PyObject_GetAttrId(inst, &PyId___class__);
-            if (c == NULL) {
-                if (PyErr_ExceptionMatches(PyExc_AttributeError))
-                    PyErr_Clear();
-                else
-                    retval = -1;
-            }
-            else {
-                if (c != (PyObject *)(inst->ob_type) &&
-                    PyType_Check(c))
+            retval = _PyObject_LookupAttrId(inst, &PyId___class__, &icls);
+            if (icls != NULL) {
+                if (icls != (PyObject *)(inst->ob_type) && PyType_Check(icls)) {
                     retval = PyType_IsSubtype(
-                        (PyTypeObject *)c,
+                        (PyTypeObject *)icls,
                         (PyTypeObject *)cls);
-                Py_DECREF(c);
+                }
+                else {
+                    retval = 0;
+                }
+                Py_DECREF(icls);
             }
         }
     }
@@ -2365,14 +2355,8 @@ recursive_isinstance(PyObject *inst, PyObject *cls)
         if (!check_class(cls,
             "isinstance() arg 2 must be a type or tuple of types"))
             return -1;
-        icls = _PyObject_GetAttrId(inst, &PyId___class__);
-        if (icls == NULL) {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError))
-                PyErr_Clear();
-            else
-                retval = -1;
-        }
-        else {
+        retval = _PyObject_LookupAttrId(inst, &PyId___class__, &icls);
+        if (icls != NULL) {
             retval = abstract_issubclass(icls, cls);
             Py_DECREF(icls);
         }
diff --git a/Objects/classobject.c b/Objects/classobject.c
index e88c95cbfbd..3dc23b796c2 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -246,21 +246,14 @@ method_repr(PyMethodObject *a)
 {
     PyObject *self = a->im_self;
     PyObject *func = a->im_func;
-    PyObject *funcname = NULL, *result = NULL;
+    PyObject *funcname, *result;
     const char *defname = "?";
 
-    funcname = _PyObject_GetAttrId(func, &PyId___qualname__);
-    if (funcname == NULL) {
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError))
-            return NULL;
-        PyErr_Clear();
-
-        funcname = _PyObject_GetAttrId(func, &PyId___name__);
-        if (funcname == NULL) {
-            if (!PyErr_ExceptionMatches(PyExc_AttributeError))
-                return NULL;
-            PyErr_Clear();
-        }
+    if (_PyObject_LookupAttrId(func, &PyId___qualname__, &funcname) < 0 ||
+        (funcname == NULL &&
+         _PyObject_LookupAttrId(func, &PyId___name__, &funcname) < 0))
+    {
+        return NULL;
     }
 
     if (funcname != NULL && !PyUnicode_Check(funcname)) {
@@ -542,7 +535,7 @@ static PyObject *
 instancemethod_repr(PyObject *self)
 {
     PyObject *func = PyInstanceMethod_Function(self);
-    PyObject *funcname = NULL , *result = NULL;
+    PyObject *funcname, *result;
     const char *defname = "?";
 
     if (func == NULL) {
@@ -550,13 +543,10 @@ instancemethod_repr(PyObject *self)
         return NULL;
     }
 
-    funcname = _PyObject_GetAttrId(func, &PyId___name__);
-    if (funcname == NULL) {
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError))
-            return NULL;
-        PyErr_Clear();
+    if (_PyObject_LookupAttrId(func, &PyId___name__, &funcname) < 0) {
+        return NULL;
     }
-    else if (!PyUnicode_Check(funcname)) {
+    if (funcname != NULL && !PyUnicode_Check(funcname)) {
         Py_DECREF(funcname);
         funcname = NULL;
     }
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 259fa255653..0d25739f72c 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -2234,17 +2234,16 @@ dict_update_common(PyObject *self, PyObject *args, PyObject *kwds,
     }
     else if (arg != NULL) {
         _Py_IDENTIFIER(keys);
-        PyObject *func = _PyObject_GetAttrId(arg, &PyId_keys);
-        if (func != NULL) {
+        PyObject *func;
+        if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) {
+            result = -1;
+        }
+        else if (func != NULL) {
             Py_DECREF(func);
             result = PyDict_Merge(self, arg, 1);
         }
-        else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_Clear();
-            result = PyDict_MergeFromSeq2(self, arg, 1);
-        }
         else {
-            result = -1;
+            result = PyDict_MergeFromSeq2(self, arg, 1);
         }
     }
 
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 0a34c1f6a97..7baffa7fc02 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -352,13 +352,11 @@ gen_close_iter(PyObject *yf)
             return -1;
     }
     else {
-        PyObject *meth = _PyObject_GetAttrId(yf, &PyId_close);
-        if (meth == NULL) {
-            if (!PyErr_ExceptionMatches(PyExc_AttributeError))
-                PyErr_WriteUnraisable(yf);
-            PyErr_Clear();
+        PyObject *meth;
+        if (_PyObject_LookupAttrId(yf, &PyId_close, &meth) < 0) {
+            PyErr_WriteUnraisable(yf);
         }
-        else {
+        if (meth) {
             retval = _PyObject_CallNoArg(meth);
             Py_DECREF(meth);
             if (retval == NULL)
@@ -471,13 +469,12 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
             gen->gi_running = 0;
         } else {
             /* `yf` is an iterator or a coroutine-like object. */
-            PyObject *meth = _PyObject_GetAttrId(yf, &PyId_throw);
+            PyObject *meth;
+            if (_PyObject_LookupAttrId(yf, &PyId_throw, &meth) < 0) {
+                Py_DECREF(yf);
+                return NULL;
+            }
             if (meth == NULL) {
-                if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                    Py_DECREF(yf);
-                    return NULL;
-                }
-                PyErr_Clear();
                 Py_DECREF(yf);
                 goto throw_here;
             }
diff --git a/Objects/object.c b/Objects/object.c
index 8cec6e2f122..7b2adbea1a1 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -817,16 +817,11 @@ _PyObject_IsAbstract(PyObject *obj)
     if (obj == NULL)
         return 0;
 
-    isabstract = _PyObject_GetAttrId(obj, &PyId___isabstractmethod__);
-    if (isabstract == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_Clear();
-            return 0;
-        }
-        return -1;
+    res = _PyObject_LookupAttrId(obj, &PyId___isabstractmethod__, &isabstract);
+    if (res > 0) {
+        res = PyObject_IsTrue(isabstract);
+        Py_DECREF(isabstract);
     }
-    res = PyObject_IsTrue(isabstract);
-    Py_DECREF(isabstract);
     return res;
 }
 
@@ -888,47 +883,74 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
     return NULL;
 }
 
-PyObject *
-_PyObject_GetAttrWithoutError(PyObject *v, PyObject *name)
+int
+_PyObject_LookupAttr(PyObject *v, PyObject *name, PyObject **result)
 {
     PyTypeObject *tp = Py_TYPE(v);
-    PyObject *ret = NULL;
 
     if (!PyUnicode_Check(name)) {
         PyErr_Format(PyExc_TypeError,
                      "attribute name must be string, not '%.200s'",
                      name->ob_type->tp_name);
-        return NULL;
+        *result = NULL;
+        return -1;
     }
 
     if (tp->tp_getattro == PyObject_GenericGetAttr) {
-        return _PyObject_GenericGetAttrWithDict(v, name, NULL, 1);
+        *result = _PyObject_GenericGetAttrWithDict(v, name, NULL, 1);
+        if (*result != NULL) {
+            return 1;
+        }
+        if (PyErr_Occurred()) {
+            return -1;
+        }
+        return 0;
     }
     if (tp->tp_getattro != NULL) {
-        ret = (*tp->tp_getattro)(v, name);
+        *result = (*tp->tp_getattro)(v, name);
     }
     else if (tp->tp_getattr != NULL) {
         const char *name_str = PyUnicode_AsUTF8(name);
-        if (name_str == NULL)
-            return NULL;
-        ret = (*tp->tp_getattr)(v, (char *)name_str);
+        if (name_str == NULL) {
+            *result = NULL;
+            return -1;
+        }
+        *result = (*tp->tp_getattr)(v, (char *)name_str);
     }
-    if (ret == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
-        PyErr_Clear();
+    if (*result != NULL) {
+        return 1;
     }
-    return ret;
+    if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
+        return -1;
+    }
+    PyErr_Clear();
+    return 0;
+}
+
+int
+_PyObject_LookupAttrId(PyObject *v, _Py_Identifier *name, PyObject **result)
+{
+    PyObject *oname = _PyUnicode_FromId(name); /* borrowed */
+    if (!oname) {
+        *result = NULL;
+        return -1;
+    }
+    return  _PyObject_LookupAttr(v, oname, result);
 }
 
 int
 PyObject_HasAttr(PyObject *v, PyObject *name)
 {
-    PyObject *res = _PyObject_GetAttrWithoutError(v, name);
-    if (res != NULL) {
-        Py_DECREF(res);
-        return 1;
+    PyObject *res;
+    if (_PyObject_LookupAttr(v, name, &res) < 0) {
+        PyErr_Clear();
+        return 0;
     }
-    PyErr_Clear();
-    return 0;
+    if (res == NULL) {
+        return 0;
+    }
+    Py_DECREF(res);
+    return 1;
 }
 
 int
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
index 218aa2c5d9c..bf19fedb07f 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -2359,7 +2359,10 @@ mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
             goto handle_kwargs;
         }
 
-        func = _PyObject_GetAttrId(other, &PyId_keys);
+        if (_PyObject_LookupAttrId(other, &PyId_keys, &func) < 0) {
+            Py_DECREF(other);
+            return NULL;
+        }
         if (func != NULL) {
             PyObject *keys, *iterator, *key;
             keys = _PyObject_CallNoArg(func);
@@ -2391,15 +2394,11 @@ mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
                 return NULL;
             goto handle_kwargs;
         }
-        else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
+
+        if (_PyObject_LookupAttrId(other, &PyId_items, &func) < 0) {
             Py_DECREF(other);
             return NULL;
         }
-        else {
-            PyErr_Clear();
-        }
-
-        func = _PyObject_GetAttrId(other, &PyId_items);
         if (func != NULL) {
             PyObject *items;
             Py_DECREF(other);
@@ -2413,13 +2412,6 @@ mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
                 return NULL;
             goto handle_kwargs;
         }
-        else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            Py_DECREF(other);
-            return NULL;
-        }
-        else {
-            PyErr_Clear();
-        }
 
         res = mutablemapping_add_pairs(self, other);
         Py_DECREF(other);
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 0c8f0adfb78..a22173878e8 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -2403,7 +2403,9 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
             if (PyType_Check(tmp)) {
                 continue;
             }
-            tmp = _PyObject_GetAttrId(tmp, &PyId___mro_entries__);
+            if (_PyObject_LookupAttrId(tmp, &PyId___mro_entries__, &tmp) < 0) {
+                return NULL;
+            }
             if (tmp != NULL) {
                 PyErr_SetString(PyExc_TypeError,
                                 "type() doesn't support MRO entry resolution; "
@@ -2411,12 +2413,6 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
                 Py_DECREF(tmp);
                 return NULL;
             }
-            else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_Clear();
-            }
-            else {
-                return NULL;
-            }
         }
         /* Search the bases for the proper metatype to deal with this: */
         winner = _PyType_CalculateMetaclass(metatype, bases);
@@ -4099,15 +4095,12 @@ _PyObject_GetState(PyObject *obj, int required)
     PyObject *getstate;
     _Py_IDENTIFIER(__getstate__);
 
-    getstate = _PyObject_GetAttrId(obj, &PyId___getstate__);
+    if (_PyObject_LookupAttrId(obj, &PyId___getstate__, &getstate) < 0) {
+        return NULL;
+    }
     if (getstate == NULL) {
         PyObject *slotnames;
 
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            return NULL;
-        }
-        PyErr_Clear();
-
         if (required && obj->ob_type->tp_itemsize) {
             PyErr_Format(PyExc_TypeError,
                          "can't pickle %.200s objects",
@@ -4174,14 +4167,12 @@ _PyObject_GetState(PyObject *obj, int required)
 
                 name = PyList_GET_ITEM(slotnames, i);
                 Py_INCREF(name);
-                value = PyObject_GetAttr(obj, name);
+                if (_PyObject_LookupAttr(obj, name, &value) < 0) {
+                    goto error;
+                }
                 if (value == NULL) {
                     Py_DECREF(name);
-                    if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                        goto error;
-                    }
                     /* It is not an error if the attribute is not present. */
-                    PyErr_Clear();
                 }
                 else {
                     int err = PyDict_SetItem(slots, name, value);
diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py
index 69583d914f6..399e79e0597 100644
--- a/Parser/asdl_c.py
+++ b/Parser/asdl_c.py
@@ -497,11 +497,27 @@ def isSimpleType(self, field):
 
     def visitField(self, field, name, sum=None, prod=None, depth=0):
         ctype = get_c_type(field.type)
+        self.emit("if (_PyObject_LookupAttrId(obj, &PyId_%s, &tmp) < 0) {" % field.name, depth)
+        self.emit("return 1;", depth+1)
+        self.emit("}", depth)
         if not field.opt:
-            self.emit("tmp = _PyObject_GetAttrId(obj, &PyId_%s);" % field.name, depth)
+            self.emit("if (tmp == NULL) {", depth)
+            message = "required field \\\"%s\\\" missing from %s" % (field.name, name)
+            format = "PyErr_SetString(PyExc_TypeError, \"%s\");"
+            self.emit(format % message, depth+1, reflow=False)
+            self.emit("return 1;", depth+1)
         else:
-            self.emit("tmp = get_not_none(obj, &PyId_%s);" % field.name, depth)
-        self.emit("if (tmp != NULL) {", depth)
+            self.emit("if (tmp == NULL || tmp == Py_None) {", depth)
+            self.emit("Py_CLEAR(tmp);", depth+1)
+            if self.isNumeric(field):
+                self.emit("%s = 0;" % field.name, depth+1)
+            elif not self.isSimpleType(field):
+                self.emit("%s = NULL;" % field.name, depth+1)
+            else:
+                raise TypeError("could not determine the default value for %s" % field.name)
+        self.emit("}", depth)
+        self.emit("else {", depth)
+
         self.emit("int res;", depth+1)
         if field.seq:
             self.emit("Py_ssize_t len;", depth+1)
@@ -539,25 +555,6 @@ def visitField(self, field, name, sum=None, prod=None, depth=0):
             self.emit("if (res != 0) goto failed;", depth+1)
 
         self.emit("Py_CLEAR(tmp);", depth+1)
-        if not field.opt:
-            self.emit("} else {", depth)
-            self.emit("if (PyErr_ExceptionMatches(PyExc_AttributeError)) {", depth+1)
-            message = "required field \\\"%s\\\" missing from %s" % (field.name, name)
-            format = "PyErr_SetString(PyExc_TypeError, \"%s\");"
-            self.emit(format % message, depth+2, reflow=False)
-            self.emit("}", depth+1)
-            self.emit("return 1;", depth+1)
-        else:
-            self.emit("} else if (PyErr_Occurred()) {", depth)
-            self.emit("return 1;", depth+1)
-            self.emit("} else {", depth)
-
-            if self.isNumeric(field):
-                self.emit("%s = 0;" % field.name, depth+1)
-            elif not self.isSimpleType(field):
-                self.emit("%s = NULL;" % field.name, depth+1)
-            else:
-                raise TypeError("could not determine the default value for %s" % field.name)
         self.emit("}", depth)
 
 
@@ -662,18 +659,14 @@ def visitModule(self, mod):
     Py_ssize_t i, numfields = 0;
     int res = -1;
     PyObject *key, *value, *fields;
-    fields = _PyObject_GetAttrId((PyObject*)Py_TYPE(self), &PyId__fields);
+    if (_PyObject_LookupAttrId((PyObject*)Py_TYPE(self), &PyId__fields, &fields) < 0) {
+        goto cleanup;
+    }
     if (fields) {
         numfields = PySequence_Size(fields);
         if (numfields == -1)
             goto cleanup;
     }
-    else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-        PyErr_Clear();
-    }
-    else {
-        goto cleanup;
-    }
 
     res = 0; /* if no error occurs, this stays 0 to the end */
     if (numfields < PyTuple_GET_SIZE(args)) {
@@ -713,19 +706,13 @@ def visitModule(self, mod):
 static PyObject *
 ast_type_reduce(PyObject *self, PyObject *unused)
 {
-    PyObject *res;
     _Py_IDENTIFIER(__dict__);
-    PyObject *dict = _PyObject_GetAttrId(self, &PyId___dict__);
-    if (dict == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError))
-            PyErr_Clear();
-        else
-            return NULL;
+    PyObject *dict;
+    if (_PyObject_LookupAttrId(self, &PyId___dict__, &dict) < 0) {
+        return NULL;
     }
     if (dict) {
-        res = Py_BuildValue("O()O", Py_TYPE(self), dict);
-        Py_DECREF(dict);
-        return res;
+        return Py_BuildValue("O()N", Py_TYPE(self), dict);
     }
     return Py_BuildValue("O()", Py_TYPE(self));
 }
@@ -965,22 +952,6 @@ def visitModule(self, mod):
     return 0;
 }
 
-static PyObject *get_not_none(PyObject *obj, _Py_Identifier *id)
-{
-    PyObject *attr = _PyObject_GetAttrId(obj, id);
-    if (!attr) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_Clear();
-        }
-        return NULL;
-    }
-    else if (attr == Py_None) {
-        Py_DECREF(attr);
-        return NULL;
-    }
-    return attr;
-}
-
 """, 0, reflow=False)
 
         self.emit("static int init_types(void)",0)
diff --git a/Python/Python-ast.c b/Python/Python-ast.c
index 81226c8a060..f7a056845e9 100644
--- a/Python/Python-ast.c
+++ b/Python/Python-ast.c
@@ -545,18 +545,14 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
     Py_ssize_t i, numfields = 0;
     int res = -1;
     PyObject *key, *value, *fields;
-    fields = _PyObject_GetAttrId((PyObject*)Py_TYPE(self), &PyId__fields);
+    if (_PyObject_LookupAttrId((PyObject*)Py_TYPE(self), &PyId__fields, &fields) < 0) {
+        goto cleanup;
+    }
     if (fields) {
         numfields = PySequence_Size(fields);
         if (numfields == -1)
             goto cleanup;
     }
-    else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-        PyErr_Clear();
-    }
-    else {
-        goto cleanup;
-    }
 
     res = 0; /* if no error occurs, this stays 0 to the end */
     if (numfields < PyTuple_GET_SIZE(args)) {
@@ -596,19 +592,13 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
 static PyObject *
 ast_type_reduce(PyObject *self, PyObject *unused)
 {
-    PyObject *res;
     _Py_IDENTIFIER(__dict__);
-    PyObject *dict = _PyObject_GetAttrId(self, &PyId___dict__);
-    if (dict == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError))
-            PyErr_Clear();
-        else
-            return NULL;
+    PyObject *dict;
+    if (_PyObject_LookupAttrId(self, &PyId___dict__, &dict) < 0) {
+        return NULL;
     }
     if (dict) {
-        res = Py_BuildValue("O()O", Py_TYPE(self), dict);
-        Py_DECREF(dict);
-        return res;
+        return Py_BuildValue("O()N", Py_TYPE(self), dict);
     }
     return Py_BuildValue("O()", Py_TYPE(self));
 }
@@ -848,22 +838,6 @@ static int add_ast_fields(void)
     return 0;
 }
 
-static PyObject *get_not_none(PyObject *obj, _Py_Identifier *id)
-{
-    PyObject *attr = _PyObject_GetAttrId(obj, id);
-    if (!attr) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_Clear();
-        }
-        return NULL;
-    }
-    else if (attr == Py_None) {
-        Py_DECREF(attr);
-        return NULL;
-    }
-    return attr;
-}
-
 
 static int init_types(void)
 {
@@ -4012,8 +3986,14 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
         asdl_seq* body;
         string docstring;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Module");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4035,22 +4015,19 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Module");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_docstring, &tmp) < 0) {
             return 1;
         }
-        tmp = get_not_none(obj, &PyId_docstring);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            docstring = NULL;
+        }
+        else {
             int res;
             res = obj2ast_string(tmp, &docstring, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            docstring = NULL;
         }
         *out = Module(body, docstring, arena);
         if (*out == NULL) goto failed;
@@ -4063,8 +4040,14 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
     if (isinstance) {
         asdl_seq* body;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Interactive");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4086,11 +4069,6 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Interactive");
-            }
-            return 1;
         }
         *out = Interactive(body, arena);
         if (*out == NULL) goto failed;
@@ -4103,17 +4081,18 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
     if (isinstance) {
         expr_ty body;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Expression");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &body, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Expression");
-            }
-            return 1;
         }
         *out = Expression(body, arena);
         if (*out == NULL) goto failed;
@@ -4126,8 +4105,14 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
     if (isinstance) {
         asdl_seq* body;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Suite");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4149,11 +4134,6 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Suite");
-            }
-            return 1;
         }
         *out = Suite(body, arena);
         if (*out == NULL) goto failed;
@@ -4179,29 +4159,31 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         *out = NULL;
         return 0;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_lineno);
-    if (tmp != NULL) {
+    if (_PyObject_LookupAttrId(obj, &PyId_lineno, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"lineno\" missing from stmt");
+        return 1;
+    }
+    else {
         int res;
         res = obj2ast_int(tmp, &lineno, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"lineno\" missing from stmt");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_col_offset, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"col_offset\" missing from stmt");
         return 1;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_col_offset);
-    if (tmp != NULL) {
+    else {
         int res;
         res = obj2ast_int(tmp, &col_offset, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"col_offset\" missing from stmt");
-        }
-        return 1;
     }
     isinstance = PyObject_IsInstance(obj, (PyObject*)FunctionDef_type);
     if (isinstance == -1) {
@@ -4215,32 +4197,40 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         expr_ty returns;
         string docstring;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_name);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_name, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from FunctionDef");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_identifier(tmp, &name, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from FunctionDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_args, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from FunctionDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_args);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_arguments(tmp, &args, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from FunctionDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from FunctionDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4262,14 +4252,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from FunctionDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_decorator_list, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"decorator_list\" missing from FunctionDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_decorator_list);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4291,33 +4282,32 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(decorator_list, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"decorator_list\" missing from FunctionDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_returns, &tmp) < 0) {
             return 1;
         }
-        tmp = get_not_none(obj, &PyId_returns);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            returns = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &returns, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_docstring, &tmp) < 0) {
             return 1;
-        } else {
-            returns = NULL;
         }
-        tmp = get_not_none(obj, &PyId_docstring);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            docstring = NULL;
+        }
+        else {
             int res;
             res = obj2ast_string(tmp, &docstring, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            docstring = NULL;
         }
         *out = FunctionDef(name, args, body, decorator_list, returns,
                            docstring, lineno, col_offset, arena);
@@ -4336,32 +4326,40 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         expr_ty returns;
         string docstring;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_name);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_name, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from AsyncFunctionDef");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_identifier(tmp, &name, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from AsyncFunctionDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_args, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from AsyncFunctionDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_args);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_arguments(tmp, &args, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from AsyncFunctionDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from AsyncFunctionDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4383,14 +4381,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from AsyncFunctionDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_decorator_list, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"decorator_list\" missing from AsyncFunctionDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_decorator_list);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4412,33 +4411,32 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(decorator_list, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"decorator_list\" missing from AsyncFunctionDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_returns, &tmp) < 0) {
             return 1;
         }
-        tmp = get_not_none(obj, &PyId_returns);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            returns = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &returns, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_docstring, &tmp) < 0) {
             return 1;
-        } else {
-            returns = NULL;
         }
-        tmp = get_not_none(obj, &PyId_docstring);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            docstring = NULL;
+        }
+        else {
             int res;
             res = obj2ast_string(tmp, &docstring, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            docstring = NULL;
         }
         *out = AsyncFunctionDef(name, args, body, decorator_list, returns,
                                 docstring, lineno, col_offset, arena);
@@ -4457,20 +4455,27 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* decorator_list;
         string docstring;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_name);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_name, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from ClassDef");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_identifier(tmp, &name, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from ClassDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_bases, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"bases\" missing from ClassDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_bases);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4492,14 +4497,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(bases, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"bases\" missing from ClassDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_keywords, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"keywords\" missing from ClassDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_keywords);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4521,14 +4527,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(keywords, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"keywords\" missing from ClassDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from ClassDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4550,14 +4557,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from ClassDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_decorator_list, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"decorator_list\" missing from ClassDef");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_decorator_list);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4579,22 +4587,19 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(decorator_list, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"decorator_list\" missing from ClassDef");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_docstring, &tmp) < 0) {
             return 1;
         }
-        tmp = get_not_none(obj, &PyId_docstring);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            docstring = NULL;
+        }
+        else {
             int res;
             res = obj2ast_string(tmp, &docstring, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            docstring = NULL;
         }
         *out = ClassDef(name, bases, keywords, body, decorator_list, docstring,
                         lineno, col_offset, arena);
@@ -4608,16 +4613,18 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
     if (isinstance) {
         expr_ty value;
 
-        tmp = get_not_none(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            value = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            value = NULL;
         }
         *out = Return(value, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -4630,8 +4637,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
     if (isinstance) {
         asdl_seq* targets;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_targets);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_targets, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"targets\" missing from Delete");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4653,11 +4666,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(targets, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"targets\" missing from Delete");
-            }
-            return 1;
         }
         *out = Delete(targets, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -4671,8 +4679,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* targets;
         expr_ty value;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_targets);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_targets, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"targets\" missing from Assign");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4694,23 +4708,19 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(targets, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"targets\" missing from Assign");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Assign");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Assign");
-            }
-            return 1;
         }
         *out = Assign(targets, value, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -4725,41 +4735,44 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         operator_ty op;
         expr_ty value;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_target);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_target, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from AugAssign");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &target, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from AugAssign");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_op, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_op);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from AugAssign");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_operator(tmp, &op, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from AugAssign");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from AugAssign");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from AugAssign");
-            }
-            return 1;
         }
         *out = AugAssign(target, op, value, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -4775,52 +4788,57 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         expr_ty value;
         int simple;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_target);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_target, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from AnnAssign");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &target, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from AnnAssign");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_annotation, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"annotation\" missing from AnnAssign");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_annotation);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr(tmp, &annotation, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"annotation\" missing from AnnAssign");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
             return 1;
         }
-        tmp = get_not_none(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            value = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_simple, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"simple\" missing from AnnAssign");
             return 1;
-        } else {
-            value = NULL;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_simple);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_int(tmp, &simple, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"simple\" missing from AnnAssign");
-            }
-            return 1;
         }
         *out = AnnAssign(target, annotation, value, simple, lineno, col_offset,
                          arena);
@@ -4837,32 +4855,40 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* body;
         asdl_seq* orelse;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_target);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_target, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from For");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &target, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from For");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_iter, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"iter\" missing from For");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_iter);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr(tmp, &iter, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"iter\" missing from For");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from For");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4884,14 +4910,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from For");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_orelse, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from For");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_orelse);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4913,11 +4940,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from For");
-            }
-            return 1;
         }
         *out = For(target, iter, body, orelse, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -4933,32 +4955,40 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* body;
         asdl_seq* orelse;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_target);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_target, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from AsyncFor");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &target, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from AsyncFor");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_iter, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"iter\" missing from AsyncFor");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_iter);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr(tmp, &iter, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"iter\" missing from AsyncFor");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from AsyncFor");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -4980,14 +5010,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from AsyncFor");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_orelse, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from AsyncFor");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_orelse);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5009,11 +5040,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from AsyncFor");
-            }
-            return 1;
         }
         *out = AsyncFor(target, iter, body, orelse, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5028,20 +5054,27 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* body;
         asdl_seq* orelse;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_test);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_test, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"test\" missing from While");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &test, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"test\" missing from While");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from While");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5063,14 +5096,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from While");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_orelse, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from While");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_orelse);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5092,11 +5126,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from While");
-            }
-            return 1;
         }
         *out = While(test, body, orelse, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5111,21 +5140,28 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* body;
         asdl_seq* orelse;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_test);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_test, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"test\" missing from If");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &test, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"test\" missing from If");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
-            int res;
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from If");
+            return 1;
+        }
+        else {
+            int res;
             Py_ssize_t len;
             Py_ssize_t i;
             if (!PyList_Check(tmp)) {
@@ -5146,14 +5182,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from If");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_orelse, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_orelse);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from If");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5175,11 +5212,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from If");
-            }
-            return 1;
         }
         *out = If(test, body, orelse, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5193,8 +5225,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* items;
         asdl_seq* body;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_items);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_items, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"items\" missing from With");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5216,14 +5254,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(items, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"items\" missing from With");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from With");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5245,11 +5284,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from With");
-            }
-            return 1;
         }
         *out = With(items, body, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5263,8 +5297,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* items;
         asdl_seq* body;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_items);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_items, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"items\" missing from AsyncWith");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5286,14 +5326,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(items, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"items\" missing from AsyncWith");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from AsyncWith");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5315,11 +5356,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from AsyncWith");
-            }
-            return 1;
         }
         *out = AsyncWith(items, body, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5333,27 +5369,31 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         expr_ty exc;
         expr_ty cause;
 
-        tmp = get_not_none(obj, &PyId_exc);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_exc, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            exc = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &exc, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_cause, &tmp) < 0) {
             return 1;
-        } else {
-            exc = NULL;
         }
-        tmp = get_not_none(obj, &PyId_cause);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            cause = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &cause, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            cause = NULL;
         }
         *out = Raise(exc, cause, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5369,8 +5409,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* orelse;
         asdl_seq* finalbody;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Try");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5392,14 +5438,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Try");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_handlers, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_handlers);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"handlers\" missing from Try");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5421,14 +5468,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(handlers, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"handlers\" missing from Try");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_orelse, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_orelse);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from Try");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5450,14 +5498,15 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from Try");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_finalbody, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"finalbody\" missing from Try");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_finalbody);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5479,11 +5528,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(finalbody, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"finalbody\" missing from Try");
-            }
-            return 1;
         }
         *out = Try(body, handlers, orelse, finalbody, lineno, col_offset,
                    arena);
@@ -5498,28 +5542,31 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         expr_ty test;
         expr_ty msg;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_test);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_test, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"test\" missing from Assert");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &test, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"test\" missing from Assert");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_msg, &tmp) < 0) {
             return 1;
         }
-        tmp = get_not_none(obj, &PyId_msg);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            msg = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &msg, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            msg = NULL;
         }
         *out = Assert(test, msg, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5532,8 +5579,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
     if (isinstance) {
         asdl_seq* names;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_names);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_names, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"names\" missing from Import");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5555,11 +5608,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(names, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"names\" missing from Import");
-            }
-            return 1;
         }
         *out = Import(names, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5574,19 +5622,27 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
         asdl_seq* names;
         int level;
 
-        tmp = get_not_none(obj, &PyId_module);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_module, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            module = NULL;
+        }
+        else {
             int res;
             res = obj2ast_identifier(tmp, &module, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_names, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"names\" missing from ImportFrom");
             return 1;
-        } else {
-            module = NULL;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_names);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5608,22 +5664,19 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(names, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"names\" missing from ImportFrom");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_level, &tmp) < 0) {
             return 1;
         }
-        tmp = get_not_none(obj, &PyId_level);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            level = 0;
+        }
+        else {
             int res;
             res = obj2ast_int(tmp, &level, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            level = 0;
         }
         *out = ImportFrom(module, names, level, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5636,8 +5689,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
     if (isinstance) {
         asdl_seq* names;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_names);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_names, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"names\" missing from Global");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5659,11 +5718,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(names, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"names\" missing from Global");
-            }
-            return 1;
         }
         *out = Global(names, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5676,8 +5730,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
     if (isinstance) {
         asdl_seq* names;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_names);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_names, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"names\" missing from Nonlocal");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5699,11 +5759,6 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
                 asdl_seq_SET(names, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"names\" missing from Nonlocal");
-            }
-            return 1;
         }
         *out = Nonlocal(names, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5716,17 +5771,18 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
     if (isinstance) {
         expr_ty value;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Expr");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Expr");
-            }
-            return 1;
         }
         *out = Expr(value, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5782,29 +5838,31 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         *out = NULL;
         return 0;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_lineno);
-    if (tmp != NULL) {
+    if (_PyObject_LookupAttrId(obj, &PyId_lineno, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"lineno\" missing from expr");
+        return 1;
+    }
+    else {
         int res;
         res = obj2ast_int(tmp, &lineno, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"lineno\" missing from expr");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_col_offset, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"col_offset\" missing from expr");
         return 1;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_col_offset);
-    if (tmp != NULL) {
+    else {
         int res;
         res = obj2ast_int(tmp, &col_offset, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"col_offset\" missing from expr");
-        }
-        return 1;
     }
     isinstance = PyObject_IsInstance(obj, (PyObject*)BoolOp_type);
     if (isinstance == -1) {
@@ -5814,20 +5872,27 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         boolop_ty op;
         asdl_seq* values;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_op);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_op, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from BoolOp");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_boolop(tmp, &op, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from BoolOp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_values, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_values);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"values\" missing from BoolOp");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -5849,11 +5914,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(values, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"values\" missing from BoolOp");
-            }
-            return 1;
         }
         *out = BoolOp(op, values, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5868,41 +5928,44 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         operator_ty op;
         expr_ty right;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_left);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_left, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"left\" missing from BinOp");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &left, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"left\" missing from BinOp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_op, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from BinOp");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_op);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_operator(tmp, &op, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from BinOp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_right, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"right\" missing from BinOp");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_right);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr(tmp, &right, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"right\" missing from BinOp");
-            }
-            return 1;
         }
         *out = BinOp(left, op, right, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5916,29 +5979,31 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         unaryop_ty op;
         expr_ty operand;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_op);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_op, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from UnaryOp");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_unaryop(tmp, &op, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"op\" missing from UnaryOp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_operand, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"operand\" missing from UnaryOp");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_operand);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr(tmp, &operand, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"operand\" missing from UnaryOp");
-            }
-            return 1;
         }
         *out = UnaryOp(op, operand, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5952,29 +6017,31 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         arguments_ty args;
         expr_ty body;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_args);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_args, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from Lambda");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_arguments(tmp, &args, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from Lambda");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Lambda");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr(tmp, &body, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from Lambda");
-            }
-            return 1;
         }
         *out = Lambda(args, body, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -5989,41 +6056,44 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         expr_ty body;
         expr_ty orelse;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_test);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_test, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"test\" missing from IfExp");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &test, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"test\" missing from IfExp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from IfExp");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &body, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from IfExp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_orelse, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from IfExp");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_orelse);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr(tmp, &orelse, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"orelse\" missing from IfExp");
-            }
-            return 1;
         }
         *out = IfExp(test, body, orelse, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6037,8 +6107,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         asdl_seq* keys;
         asdl_seq* values;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_keys);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_keys, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"keys\" missing from Dict");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6060,14 +6136,15 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(keys, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"keys\" missing from Dict");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_values, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"values\" missing from Dict");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_values);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6089,11 +6166,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(values, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"values\" missing from Dict");
-            }
-            return 1;
         }
         *out = Dict(keys, values, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6106,8 +6178,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         asdl_seq* elts;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_elts);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_elts, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"elts\" missing from Set");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6129,11 +6207,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(elts, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"elts\" missing from Set");
-            }
-            return 1;
         }
         *out = Set(elts, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6147,20 +6220,27 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         expr_ty elt;
         asdl_seq* generators;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_elt);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_elt, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"elt\" missing from ListComp");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &elt, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"elt\" missing from ListComp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_generators, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_generators);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"generators\" missing from ListComp");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6182,11 +6262,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(generators, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"generators\" missing from ListComp");
-            }
-            return 1;
         }
         *out = ListComp(elt, generators, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6200,20 +6275,27 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         expr_ty elt;
         asdl_seq* generators;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_elt);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_elt, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"elt\" missing from SetComp");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &elt, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"elt\" missing from SetComp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_generators, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"generators\" missing from SetComp");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_generators);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6235,11 +6317,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(generators, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"generators\" missing from SetComp");
-            }
-            return 1;
         }
         *out = SetComp(elt, generators, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6254,32 +6331,40 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         expr_ty value;
         asdl_seq* generators;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_key);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_key, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"key\" missing from DictComp");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &key, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"key\" missing from DictComp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from DictComp");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from DictComp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_generators, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_generators);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"generators\" missing from DictComp");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6301,11 +6386,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(generators, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"generators\" missing from DictComp");
-            }
-            return 1;
         }
         *out = DictComp(key, value, generators, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6319,20 +6399,27 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         expr_ty elt;
         asdl_seq* generators;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_elt);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_elt, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"elt\" missing from GeneratorExp");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &elt, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"elt\" missing from GeneratorExp");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_generators, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_generators);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"generators\" missing from GeneratorExp");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6354,11 +6441,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(generators, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"generators\" missing from GeneratorExp");
-            }
-            return 1;
         }
         *out = GeneratorExp(elt, generators, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6371,17 +6453,18 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         expr_ty value;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Await");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Await");
-            }
-            return 1;
         }
         *out = Await(value, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6394,16 +6477,18 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         expr_ty value;
 
-        tmp = get_not_none(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            value = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            value = NULL;
         }
         *out = Yield(value, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6416,17 +6501,18 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         expr_ty value;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from YieldFrom");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from YieldFrom");
-            }
-            return 1;
         }
         *out = YieldFrom(value, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6441,20 +6527,27 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         asdl_int_seq* ops;
         asdl_seq* comparators;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_left);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_left, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"left\" missing from Compare");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &left, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"left\" missing from Compare");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_ops, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"ops\" missing from Compare");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_ops);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6476,14 +6569,15 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(ops, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"ops\" missing from Compare");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_comparators, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_comparators);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"comparators\" missing from Compare");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6505,11 +6599,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(comparators, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"comparators\" missing from Compare");
-            }
-            return 1;
         }
         *out = Compare(left, ops, comparators, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6524,20 +6613,27 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         asdl_seq* args;
         asdl_seq* keywords;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_func);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_func, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"func\" missing from Call");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &func, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"func\" missing from Call");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_args, &tmp) < 0) {
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_args);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from Call");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6559,14 +6655,15 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(args, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from Call");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_keywords, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"keywords\" missing from Call");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_keywords);
-        if (tmp != NULL) {
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6588,11 +6685,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(keywords, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"keywords\" missing from Call");
-            }
-            return 1;
         }
         *out = Call(func, args, keywords, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6605,17 +6697,18 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         object n;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_n);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_n, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"n\" missing from Num");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_object(tmp, &n, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"n\" missing from Num");
-            }
-            return 1;
         }
         *out = Num(n, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6628,17 +6721,18 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         string s;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_s);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_s, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"s\" missing from Str");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_string(tmp, &s, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"s\" missing from Str");
-            }
-            return 1;
         }
         *out = Str(s, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6653,39 +6747,44 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         int conversion;
         expr_ty format_spec;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from FormattedValue");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from FormattedValue");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_conversion, &tmp) < 0) {
             return 1;
         }
-        tmp = get_not_none(obj, &PyId_conversion);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            conversion = 0;
+        }
+        else {
             int res;
             res = obj2ast_int(tmp, &conversion, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_format_spec, &tmp) < 0) {
             return 1;
-        } else {
-            conversion = 0;
         }
-        tmp = get_not_none(obj, &PyId_format_spec);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            format_spec = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &format_spec, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            format_spec = NULL;
         }
         *out = FormattedValue(value, conversion, format_spec, lineno,
                               col_offset, arena);
@@ -6699,8 +6798,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         asdl_seq* values;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_values);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_values, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"values\" missing from JoinedStr");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -6722,11 +6827,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(values, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"values\" missing from JoinedStr");
-            }
-            return 1;
         }
         *out = JoinedStr(values, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6739,17 +6839,18 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         bytes s;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_s);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_s, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"s\" missing from Bytes");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_bytes(tmp, &s, arena);
             if (res != 0) goto failed;
-            Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"s\" missing from Bytes");
-            }
-            return 1;
+            Py_CLEAR(tmp);
         }
         *out = Bytes(s, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6762,17 +6863,18 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         singleton value;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from NameConstant");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_singleton(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from NameConstant");
-            }
-            return 1;
         }
         *out = NameConstant(value, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6795,17 +6897,18 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
     if (isinstance) {
         constant value;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Constant");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_constant(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Constant");
-            }
-            return 1;
         }
         *out = Constant(value, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6820,41 +6923,44 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         identifier attr;
         expr_context_ty ctx;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Attribute");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Attribute");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_attr, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"attr\" missing from Attribute");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_attr);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_identifier(tmp, &attr, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"attr\" missing from Attribute");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_ctx, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Attribute");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_ctx);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr_context(tmp, &ctx, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Attribute");
-            }
-            return 1;
         }
         *out = Attribute(value, attr, ctx, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6869,41 +6975,44 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         slice_ty slice;
         expr_context_ty ctx;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Subscript");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Subscript");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_slice, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"slice\" missing from Subscript");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_slice);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_slice(tmp, &slice, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"slice\" missing from Subscript");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_ctx, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Subscript");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_ctx);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr_context(tmp, &ctx, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Subscript");
-            }
-            return 1;
         }
         *out = Subscript(value, slice, ctx, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6917,29 +7026,31 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         expr_ty value;
         expr_context_ty ctx;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Starred");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Starred");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_ctx, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Starred");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_ctx);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr_context(tmp, &ctx, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Starred");
-            }
-            return 1;
         }
         *out = Starred(value, ctx, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6953,29 +7064,31 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         identifier id;
         expr_context_ty ctx;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_id);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_id, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"id\" missing from Name");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_identifier(tmp, &id, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"id\" missing from Name");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_ctx, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Name");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_ctx);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr_context(tmp, &ctx, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Name");
-            }
-            return 1;
         }
         *out = Name(id, ctx, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -6989,8 +7102,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         asdl_seq* elts;
         expr_context_ty ctx;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_elts);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_elts, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"elts\" missing from List");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -7012,23 +7131,19 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(elts, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"elts\" missing from List");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_ctx, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from List");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_ctx);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr_context(tmp, &ctx, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from List");
-            }
-            return 1;
         }
         *out = List(elts, ctx, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -7042,8 +7157,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
         asdl_seq* elts;
         expr_context_ty ctx;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_elts);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_elts, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"elts\" missing from Tuple");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -7065,23 +7186,19 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
                 asdl_seq_SET(elts, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"elts\" missing from Tuple");
-            }
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_ctx, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Tuple");
             return 1;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_ctx);
-        if (tmp != NULL) {
+        else {
             int res;
             res = obj2ast_expr_context(tmp, &ctx, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"ctx\" missing from Tuple");
-            }
-            return 1;
         }
         *out = Tuple(elts, ctx, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -7172,38 +7289,44 @@ obj2ast_slice(PyObject* obj, slice_ty* out, PyArena* arena)
         expr_ty upper;
         expr_ty step;
 
-        tmp = get_not_none(obj, &PyId_lower);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_lower, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            lower = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &lower, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_upper, &tmp) < 0) {
             return 1;
-        } else {
-            lower = NULL;
         }
-        tmp = get_not_none(obj, &PyId_upper);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            upper = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &upper, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_step, &tmp) < 0) {
             return 1;
-        } else {
-            upper = NULL;
         }
-        tmp = get_not_none(obj, &PyId_step);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            step = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &step, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
-            return 1;
-        } else {
-            step = NULL;
         }
         *out = Slice(lower, upper, step, arena);
         if (*out == NULL) goto failed;
@@ -7216,8 +7339,14 @@ obj2ast_slice(PyObject* obj, slice_ty* out, PyArena* arena)
     if (isinstance) {
         asdl_seq* dims;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_dims);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_dims, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"dims\" missing from ExtSlice");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -7239,11 +7368,6 @@ obj2ast_slice(PyObject* obj, slice_ty* out, PyArena* arena)
                 asdl_seq_SET(dims, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"dims\" missing from ExtSlice");
-            }
-            return 1;
         }
         *out = ExtSlice(dims, arena);
         if (*out == NULL) goto failed;
@@ -7256,17 +7380,18 @@ obj2ast_slice(PyObject* obj, slice_ty* out, PyArena* arena)
     if (isinstance) {
         expr_ty value;
 
-        tmp = _PyObject_GetAttrId(obj, &PyId_value);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Index");
+            return 1;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &value, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Index");
-            }
-            return 1;
         }
         *out = Index(value, arena);
         if (*out == NULL) goto failed;
@@ -7560,32 +7685,40 @@ obj2ast_comprehension(PyObject* obj, comprehension_ty* out, PyArena* arena)
     asdl_seq* ifs;
     int is_async;
 
-    tmp = _PyObject_GetAttrId(obj, &PyId_target);
-    if (tmp != NULL) {
+    if (_PyObject_LookupAttrId(obj, &PyId_target, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from comprehension");
+        return 1;
+    }
+    else {
         int res;
         res = obj2ast_expr(tmp, &target, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"target\" missing from comprehension");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_iter, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"iter\" missing from comprehension");
         return 1;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_iter);
-    if (tmp != NULL) {
+    else {
         int res;
         res = obj2ast_expr(tmp, &iter, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"iter\" missing from comprehension");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_ifs, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"ifs\" missing from comprehension");
         return 1;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_ifs);
-    if (tmp != NULL) {
+    else {
         int res;
         Py_ssize_t len;
         Py_ssize_t i;
@@ -7607,23 +7740,19 @@ obj2ast_comprehension(PyObject* obj, comprehension_ty* out, PyArena* arena)
             asdl_seq_SET(ifs, i, val);
         }
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"ifs\" missing from comprehension");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_is_async, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"is_async\" missing from comprehension");
         return 1;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_is_async);
-    if (tmp != NULL) {
+    else {
         int res;
         res = obj2ast_int(tmp, &is_async, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"is_async\" missing from comprehension");
-        }
-        return 1;
     }
     *out = comprehension(target, iter, ifs, is_async, arena);
     return 0;
@@ -7645,29 +7774,31 @@ obj2ast_excepthandler(PyObject* obj, excepthandler_ty* out, PyArena* arena)
         *out = NULL;
         return 0;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_lineno);
-    if (tmp != NULL) {
+    if (_PyObject_LookupAttrId(obj, &PyId_lineno, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"lineno\" missing from excepthandler");
+        return 1;
+    }
+    else {
         int res;
         res = obj2ast_int(tmp, &lineno, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"lineno\" missing from excepthandler");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_col_offset, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"col_offset\" missing from excepthandler");
         return 1;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_col_offset);
-    if (tmp != NULL) {
+    else {
         int res;
         res = obj2ast_int(tmp, &col_offset, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"col_offset\" missing from excepthandler");
-        }
-        return 1;
     }
     isinstance = PyObject_IsInstance(obj, (PyObject*)ExceptHandler_type);
     if (isinstance == -1) {
@@ -7678,30 +7809,40 @@ obj2ast_excepthandler(PyObject* obj, excepthandler_ty* out, PyArena* arena)
         identifier name;
         asdl_seq* body;
 
-        tmp = get_not_none(obj, &PyId_type);
-        if (tmp != NULL) {
+        if (_PyObject_LookupAttrId(obj, &PyId_type, &tmp) < 0) {
+            return 1;
+        }
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            type = NULL;
+        }
+        else {
             int res;
             res = obj2ast_expr(tmp, &type, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_name, &tmp) < 0) {
             return 1;
-        } else {
-            type = NULL;
         }
-        tmp = get_not_none(obj, &PyId_name);
-        if (tmp != NULL) {
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            name = NULL;
+        }
+        else {
             int res;
             res = obj2ast_identifier(tmp, &name, arena);
             if (res != 0) goto failed;
             Py_CLEAR(tmp);
-        } else if (PyErr_Occurred()) {
+        }
+        if (_PyObject_LookupAttrId(obj, &PyId_body, &tmp) < 0) {
             return 1;
-        } else {
-            name = NULL;
         }
-        tmp = _PyObject_GetAttrId(obj, &PyId_body);
-        if (tmp != NULL) {
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from ExceptHandler");
+            return 1;
+        }
+        else {
             int res;
             Py_ssize_t len;
             Py_ssize_t i;
@@ -7723,11 +7864,6 @@ obj2ast_excepthandler(PyObject* obj, excepthandler_ty* out, PyArena* arena)
                 asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
-        } else {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_SetString(PyExc_TypeError, "required field \"body\" missing from ExceptHandler");
-            }
-            return 1;
         }
         *out = ExceptHandler(type, name, body, lineno, col_offset, arena);
         if (*out == NULL) goto failed;
@@ -7751,8 +7887,14 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
     arg_ty kwarg;
     asdl_seq* defaults;
 
-    tmp = _PyObject_GetAttrId(obj, &PyId_args);
-    if (tmp != NULL) {
+    if (_PyObject_LookupAttrId(obj, &PyId_args, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from arguments");
+        return 1;
+    }
+    else {
         int res;
         Py_ssize_t len;
         Py_ssize_t i;
@@ -7774,25 +7916,28 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
             asdl_seq_SET(args, i, val);
         }
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from arguments");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_vararg, &tmp) < 0) {
         return 1;
     }
-    tmp = get_not_none(obj, &PyId_vararg);
-    if (tmp != NULL) {
+    if (tmp == NULL || tmp == Py_None) {
+        Py_CLEAR(tmp);
+        vararg = NULL;
+    }
+    else {
         int res;
         res = obj2ast_arg(tmp, &vararg, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else if (PyErr_Occurred()) {
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_kwonlyargs, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"kwonlyargs\" missing from arguments");
         return 1;
-    } else {
-        vararg = NULL;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_kwonlyargs);
-    if (tmp != NULL) {
+    else {
         int res;
         Py_ssize_t len;
         Py_ssize_t i;
@@ -7814,14 +7959,15 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
             asdl_seq_SET(kwonlyargs, i, val);
         }
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"kwonlyargs\" missing from arguments");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_kw_defaults, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"kw_defaults\" missing from arguments");
         return 1;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_kw_defaults);
-    if (tmp != NULL) {
+    else {
         int res;
         Py_ssize_t len;
         Py_ssize_t i;
@@ -7843,25 +7989,28 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
             asdl_seq_SET(kw_defaults, i, val);
         }
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"kw_defaults\" missing from arguments");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_kwarg, &tmp) < 0) {
         return 1;
     }
-    tmp = get_not_none(obj, &PyId_kwarg);
-    if (tmp != NULL) {
+    if (tmp == NULL || tmp == Py_None) {
+        Py_CLEAR(tmp);
+        kwarg = NULL;
+    }
+    else {
         int res;
         res = obj2ast_arg(tmp, &kwarg, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else if (PyErr_Occurred()) {
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_defaults, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"defaults\" missing from arguments");
         return 1;
-    } else {
-        kwarg = NULL;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_defaults);
-    if (tmp != NULL) {
+    else {
         int res;
         Py_ssize_t len;
         Py_ssize_t i;
@@ -7883,11 +8032,6 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
             asdl_seq_SET(defaults, i, val);
         }
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"defaults\" missing from arguments");
-        }
-        return 1;
     }
     *out = arguments(args, vararg, kwonlyargs, kw_defaults, kwarg, defaults,
                      arena);
@@ -7906,52 +8050,57 @@ obj2ast_arg(PyObject* obj, arg_ty* out, PyArena* arena)
     int lineno;
     int col_offset;
 
-    tmp = _PyObject_GetAttrId(obj, &PyId_arg);
-    if (tmp != NULL) {
+    if (_PyObject_LookupAttrId(obj, &PyId_arg, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"arg\" missing from arg");
+        return 1;
+    }
+    else {
         int res;
         res = obj2ast_identifier(tmp, &arg, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"arg\" missing from arg");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_annotation, &tmp) < 0) {
         return 1;
     }
-    tmp = get_not_none(obj, &PyId_annotation);
-    if (tmp != NULL) {
+    if (tmp == NULL || tmp == Py_None) {
+        Py_CLEAR(tmp);
+        annotation = NULL;
+    }
+    else {
         int res;
         res = obj2ast_expr(tmp, &annotation, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else if (PyErr_Occurred()) {
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_lineno, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"lineno\" missing from arg");
         return 1;
-    } else {
-        annotation = NULL;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_lineno);
-    if (tmp != NULL) {
+    else {
         int res;
         res = obj2ast_int(tmp, &lineno, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"lineno\" missing from arg");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_col_offset, &tmp) < 0) {
         return 1;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_col_offset);
-    if (tmp != NULL) {
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"col_offset\" missing from arg");
+        return 1;
+    }
+    else {
         int res;
         res = obj2ast_int(tmp, &col_offset, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"col_offset\" missing from arg");
-        }
-        return 1;
     }
     *out = arg(arg, annotation, lineno, col_offset, arena);
     return 0;
@@ -7967,28 +8116,31 @@ obj2ast_keyword(PyObject* obj, keyword_ty* out, PyArena* arena)
     identifier arg;
     expr_ty value;
 
-    tmp = get_not_none(obj, &PyId_arg);
-    if (tmp != NULL) {
+    if (_PyObject_LookupAttrId(obj, &PyId_arg, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL || tmp == Py_None) {
+        Py_CLEAR(tmp);
+        arg = NULL;
+    }
+    else {
         int res;
         res = obj2ast_identifier(tmp, &arg, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else if (PyErr_Occurred()) {
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from keyword");
         return 1;
-    } else {
-        arg = NULL;
     }
-    tmp = _PyObject_GetAttrId(obj, &PyId_value);
-    if (tmp != NULL) {
+    else {
         int res;
         res = obj2ast_expr(tmp, &value, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from keyword");
-        }
-        return 1;
     }
     *out = keyword(arg, value, arena);
     return 0;
@@ -8004,28 +8156,31 @@ obj2ast_alias(PyObject* obj, alias_ty* out, PyArena* arena)
     identifier name;
     identifier asname;
 
-    tmp = _PyObject_GetAttrId(obj, &PyId_name);
-    if (tmp != NULL) {
+    if (_PyObject_LookupAttrId(obj, &PyId_name, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from alias");
+        return 1;
+    }
+    else {
         int res;
         res = obj2ast_identifier(tmp, &name, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from alias");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_asname, &tmp) < 0) {
         return 1;
     }
-    tmp = get_not_none(obj, &PyId_asname);
-    if (tmp != NULL) {
+    if (tmp == NULL || tmp == Py_None) {
+        Py_CLEAR(tmp);
+        asname = NULL;
+    }
+    else {
         int res;
         res = obj2ast_identifier(tmp, &asname, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else if (PyErr_Occurred()) {
-        return 1;
-    } else {
-        asname = NULL;
     }
     *out = alias(name, asname, arena);
     return 0;
@@ -8041,28 +8196,31 @@ obj2ast_withitem(PyObject* obj, withitem_ty* out, PyArena* arena)
     expr_ty context_expr;
     expr_ty optional_vars;
 
-    tmp = _PyObject_GetAttrId(obj, &PyId_context_expr);
-    if (tmp != NULL) {
+    if (_PyObject_LookupAttrId(obj, &PyId_context_expr, &tmp) < 0) {
+        return 1;
+    }
+    if (tmp == NULL) {
+        PyErr_SetString(PyExc_TypeError, "required field \"context_expr\" missing from withitem");
+        return 1;
+    }
+    else {
         int res;
         res = obj2ast_expr(tmp, &context_expr, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_SetString(PyExc_TypeError, "required field \"context_expr\" missing from withitem");
-        }
+    }
+    if (_PyObject_LookupAttrId(obj, &PyId_optional_vars, &tmp) < 0) {
         return 1;
     }
-    tmp = get_not_none(obj, &PyId_optional_vars);
-    if (tmp != NULL) {
+    if (tmp == NULL || tmp == Py_None) {
+        Py_CLEAR(tmp);
+        optional_vars = NULL;
+    }
+    else {
         int res;
         res = obj2ast_expr(tmp, &optional_vars, arena);
         if (res != 0) goto failed;
         Py_CLEAR(tmp);
-    } else if (PyErr_Occurred()) {
-        return 1;
-    } else {
-        optional_vars = NULL;
     }
     *out = withitem(context_expr, optional_vars, arena);
     return 0;
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 20ba7a16616..16ae932450c 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -80,11 +80,8 @@ get_warnings_attr(_Py_Identifier *attr_id, int try_import)
             return NULL;
     }
 
-    obj = _PyObject_GetAttrId(warnings_module, attr_id);
+    (void)_PyObject_LookupAttrId(warnings_module, attr_id, &obj);
     Py_DECREF(warnings_module);
-    if (obj == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
-        PyErr_Clear();
-    }
     return obj;
 }
 
@@ -893,13 +890,10 @@ get_source_line(PyObject *module_globals, int lineno)
     Py_INCREF(module_name);
 
     /* Make sure the loader implements the optional get_source() method. */
-    get_source = _PyObject_GetAttrId(loader, &PyId_get_source);
+    (void)_PyObject_LookupAttrId(loader, &PyId_get_source, &get_source);
     Py_DECREF(loader);
     if (!get_source) {
         Py_DECREF(module_name);
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_Clear();
-        }
         return NULL;
     }
     /* Call get_source() to get the source code. */
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 7fc2261ec64..d8971bef2ae 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -71,12 +71,10 @@ update_bases(PyObject *bases, PyObject *const *args, Py_ssize_t nargs)
             }
             continue;
         }
-        meth = _PyObject_GetAttrId(base, &PyId___mro_entries__);
+        if (_PyObject_LookupAttrId(base, &PyId___mro_entries__, &meth) < 0) {
+            goto error;
+        }
         if (!meth) {
-            if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                goto error;
-            }
-            PyErr_Clear();
             if (new_bases) {
                 if (PyList_Append(new_bases, base) < 0) {
                     goto error;
@@ -218,18 +216,11 @@ builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
     }
     /* else: meta is not a class, so we cannot do the metaclass
        calculation, so we will use the explicitly given object as it is */
-    prep = _PyObject_GetAttrId(meta, &PyId___prepare__);
-    if (prep == NULL) {
-        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-            PyErr_Clear();
-            ns = PyDict_New();
-        }
-        else {
-            Py_DECREF(meta);
-            Py_XDECREF(mkw);
-            Py_DECREF(bases);
-            return NULL;
-        }
+    if (_PyObject_LookupAttrId(meta, &PyId___prepare__, &prep) < 0) {
+        ns = NULL;
+    }
+    else if (prep == NULL) {
+        ns = PyDict_New();
     }
     else {
         PyObject *pargs[2] = {name, bases};
@@ -1127,8 +1118,7 @@ builtin_getattr(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
         return NULL;
     }
     if (dflt != NULL) {
-        result = _PyObject_GetAttrWithoutError(v, name);
-        if (result == NULL && !PyErr_Occurred()) {
+        if (_PyObject_LookupAttr(v, name, &result) == 0) {
             Py_INCREF(dflt);
             return dflt;
         }
@@ -1191,13 +1181,12 @@ builtin_hasattr_impl(PyObject *module, PyObject *obj, PyObject *name)
                         "hasattr(): attribute name must be string");
         return NULL;
     }
-    v = _PyObject_GetAttrWithoutError(obj, name);
-    if (v == NULL) {
-        if (!PyErr_Occurred()) {
-            Py_RETURN_FALSE;
-        }
+    if (_PyObject_LookupAttr(obj, name, &v) < 0) {
         return NULL;
     }
+    if (v == NULL) {
+        Py_RETURN_FALSE;
+    }
     Py_DECREF(v);
     Py_RETURN_TRUE;
 }
diff --git a/Python/ceval.c b/Python/ceval.c
index 2b7c0c80242..3d7bed0111b 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -4813,13 +4813,12 @@ import_from(PyObject *v, PyObject *name)
     _Py_IDENTIFIER(__name__);
     PyObject *fullmodname, *pkgname, *pkgpath, *pkgname_or_unknown, *errmsg;
 
-    x = PyObject_GetAttr(v, name);
-    if (x != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError))
+    if (_PyObject_LookupAttr(v, name, &x) != 0) {
         return x;
+    }
     /* Issue #17636: in case this failed because of a circular relative
        import, try to fallback on reading the module directly from
        sys.modules. */
-    PyErr_Clear();
     pkgname = _PyObject_GetAttrId(v, &PyId___name__);
     if (pkgname == NULL) {
         goto error;
@@ -4881,21 +4880,20 @@ import_all_from(PyObject *locals, PyObject *v)
 {
     _Py_IDENTIFIER(__all__);
     _Py_IDENTIFIER(__dict__);
-    PyObject *all = _PyObject_GetAttrId(v, &PyId___all__);
-    PyObject *dict, *name, *value;
+    PyObject *all, *dict, *name, *value;
     int skip_leading_underscores = 0;
     int pos, err;
 
+    if (_PyObject_LookupAttrId(v, &PyId___all__, &all) < 0) {
+        return -1; /* Unexpected error */
+    }
     if (all == NULL) {
-        if (!PyErr_ExceptionMatches(PyExc_AttributeError))
-            return -1; /* Unexpected error */
-        PyErr_Clear();
-        dict = _PyObject_GetAttrId(v, &PyId___dict__);
+        if (_PyObject_LookupAttrId(v, &PyId___dict__, &dict) < 0) {
+            return -1;
+        }
         if (dict == NULL) {
-            if (!PyErr_ExceptionMatches(PyExc_AttributeError))
-                return -1;
             PyErr_SetString(PyExc_ImportError,
-            "from-import-* object has no __dict__ and no __all__");
+                    "from-import-* object has no __dict__ and no __all__");
             return -1;
         }
         all = PyMapping_Keys(dict);
diff --git a/Python/codecs.c b/Python/codecs.c
index 18edfbdab94..223ccca603f 100644
--- a/Python/codecs.c
+++ b/Python/codecs.c
@@ -540,15 +540,11 @@ PyObject * _PyCodec_LookupTextEncoding(const char *encoding,
      * attribute.
      */
     if (!PyTuple_CheckExact(codec)) {
-        attr = _PyObject_GetAttrId(codec, &PyId__is_text_encoding);
-        if (attr == NULL) {
-            if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                PyErr_Clear();
-            } else {
-                Py_DECREF(codec);
-                return NULL;
-            }
-        } else {
+        if (_PyObject_LookupAttrId(codec, &PyId__is_text_encoding, &attr) < 0) {
+            Py_DECREF(codec);
+            return NULL;
+        }
+        if (attr != NULL) {
             is_text_codec = PyObject_IsTrue(attr);
             Py_DECREF(attr);
             if (is_text_codec <= 0) {



More information about the Python-checkins mailing list