[Python-checkins] cpython (merge 3.5 -> default): Merge #27782 fix from 3.5

nick.coghlan python-checkins at python.org
Sun Aug 21 03:44:12 EDT 2016


https://hg.python.org/cpython/rev/fb509792dffc
changeset:   102821:fb509792dffc
parent:      102819:fccd733aa78b
parent:      102820:913268337886
user:        Nick Coghlan <ncoghlan at gmail.com>
date:        Sun Aug 21 17:43:58 2016 +1000
summary:
  Merge #27782 fix from 3.5

files:
  Doc/c-api/module.rst                             |   2 +-
  Include/moduleobject.h                           |   2 +-
  Lib/test/test_importlib/extension/test_loader.py |   9 +
  Misc/ACKS                                        |   1 +
  Misc/NEWS                                        |   4 +
  Modules/_testmultiphase.c                        |  33 +++++-
  Objects/moduleobject.c                           |  64 +++++----
  7 files changed, 83 insertions(+), 32 deletions(-)


diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst
--- a/Doc/c-api/module.rst
+++ b/Doc/c-api/module.rst
@@ -324,7 +324,7 @@
    :c:type:`PyModule_Type`. Any type can be used, as long as it supports
    setting and getting import-related attributes.
    However, only ``PyModule_Type`` instances may be returned if the
-   ``PyModuleDef`` has non-*NULL* ``m_methods``, ``m_traverse``, ``m_clear``,
+   ``PyModuleDef`` has non-*NULL* ``m_traverse``, ``m_clear``,
    ``m_free``; non-zero ``m_size``; or slots other than ``Py_mod_create``.
 
 .. c:var:: Py_mod_exec
diff --git a/Include/moduleobject.h b/Include/moduleobject.h
--- a/Include/moduleobject.h
+++ b/Include/moduleobject.h
@@ -77,7 +77,7 @@
   traverseproc m_traverse;
   inquiry m_clear;
   freefunc m_free;
-}PyModuleDef;
+} PyModuleDef;
 
 #ifdef __cplusplus
 }
diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py
--- a/Lib/test/test_importlib/extension/test_loader.py
+++ b/Lib/test/test_importlib/extension/test_loader.py
@@ -212,6 +212,15 @@
         self.assertNotEqual(type(mod), type(unittest))
         self.assertEqual(mod.three, 3)
 
+    # issue 27782
+    def test_nonmodule_with_methods(self):
+        '''Test creating a non-module object with methods defined'''
+        name = self.name + '_nonmodule_with_methods'
+        mod = self.load_module_by_name(name)
+        self.assertNotEqual(type(mod), type(unittest))
+        self.assertEqual(mod.three, 3)
+        self.assertEqual(mod.bar(10, 1), 9)
+
     def test_null_slots(self):
         '''Test that NULL slots aren't a problem'''
         name = self.name + '_null_slots'
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1679,6 +1679,7 @@
 Yuxiao Zeng
 Uwe Zessin
 Cheng Zhang
+Xiang Zhang
 Kai Zhu
 Tarek Ziadé
 Jelle Zijlstra
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,10 @@
 Core and Builtins
 -----------------
 
+- Issue #27782: Multi-phase extension module import now correctly allows the
+  ``m_methods`` field to be used to add module level functions to instances
+  of non-module types returned from ``Py_create_mod``. Patch by Xiang Zhang.
+
 - Issue #27487: Warn if a submodule argument to "python -m" or
   runpy.run_module() is found in sys.modules after parent packages are
   imported, but before the submodule is executed.
diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c
--- a/Modules/_testmultiphase.c
+++ b/Modules/_testmultiphase.c
@@ -248,6 +248,7 @@
 /**** Importing a non-module object ****/
 
 static PyModuleDef def_nonmodule;
+static PyModuleDef def_nonmodule_with_methods;
 
 /* Create a SimpleNamespace(three=3) */
 static PyObject*
@@ -255,7 +256,7 @@
 {
     PyObject *dct, *ns, *three;
 
-    if (def != &def_nonmodule) {
+    if (def != &def_nonmodule && def != &def_nonmodule_with_methods) {
         PyErr_SetString(PyExc_SystemError, "def does not match");
         return NULL;
     }
@@ -291,6 +292,36 @@
     return PyModuleDef_Init(&def_nonmodule);
 }
 
+PyDoc_STRVAR(nonmodule_bar_doc,
+"bar(i,j)\n\
+\n\
+Return the difference of i - j.");
+
+static PyObject *
+nonmodule_bar(PyObject *self, PyObject *args)
+{
+    long i, j;
+    long res;
+    if (!PyArg_ParseTuple(args, "ll:bar", &i, &j))
+        return NULL;
+    res = i - j;
+    return PyLong_FromLong(res);
+}
+
+static PyMethodDef nonmodule_methods[] = {
+    {"bar", nonmodule_bar, METH_VARARGS, nonmodule_bar_doc},
+    {NULL, NULL}           /* sentinel */
+};
+
+static PyModuleDef def_nonmodule_with_methods = TEST_MODULE_DEF(
+    "_testmultiphase_nonmodule_with_methods", slots_create_nonmodule, nonmodule_methods);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_nonmodule_with_methods(PyObject *spec)
+{
+    return PyModuleDef_Init(&def_nonmodule_with_methods);
+}
+
 /**** Non-ASCII-named modules ****/
 
 static PyModuleDef def_nonascii_latin = { \
diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c
--- a/Objects/moduleobject.c
+++ b/Objects/moduleobject.c
@@ -130,6 +130,34 @@
     return 1;
 }
 
+static int
+_add_methods_to_object(PyObject *module, PyObject *name, PyMethodDef *functions)
+{
+    PyObject *func;
+    PyMethodDef *fdef;
+
+    for (fdef = functions; fdef->ml_name != NULL; fdef++) {
+        if ((fdef->ml_flags & METH_CLASS) ||
+            (fdef->ml_flags & METH_STATIC)) {
+            PyErr_SetString(PyExc_ValueError,
+                            "module functions cannot set"
+                            " METH_CLASS or METH_STATIC");
+            return -1;
+        }
+        func = PyCFunction_NewEx(fdef, (PyObject*)module, name);
+        if (func == NULL) {
+            return -1;
+        }
+        if (PyObject_SetAttrString(module, fdef->ml_name, func) != 0) {
+            Py_DECREF(func);
+            return -1;
+        }
+        Py_DECREF(func);
+    }
+
+    return 0;
+}
+
 PyObject *
 PyModule_Create2(struct PyModuleDef* module, int module_api_version)
 {
@@ -269,7 +297,7 @@
             }
         }
     } else {
-        m = PyModule_New(name);
+        m = PyModule_NewObject(nameobj);
         if (m == NULL) {
             goto error;
         }
@@ -297,7 +325,7 @@
     }
 
     if (def->m_methods != NULL) {
-        ret = PyModule_AddFunctions(m, def->m_methods);
+        ret = _add_methods_to_object(m, nameobj, def->m_methods);
         if (ret != 0) {
             goto error;
         }
@@ -331,7 +359,7 @@
         return -1;
     }
 
-    if (PyModule_Check(module) && def->m_size >= 0) {
+    if (def->m_size >= 0) {
         PyModuleObject *md = (PyModuleObject*)module;
         if (md->md_state == NULL) {
             /* Always set a state pointer; this serves as a marker to skip
@@ -387,37 +415,15 @@
 int
 PyModule_AddFunctions(PyObject *m, PyMethodDef *functions)
 {
-    PyObject *name, *func;
-    PyMethodDef *fdef;
-
-    name = PyModule_GetNameObject(m);
+    int res;
+    PyObject *name = PyModule_GetNameObject(m);
     if (name == NULL) {
         return -1;
     }
 
-    for (fdef = functions; fdef->ml_name != NULL; fdef++) {
-        if ((fdef->ml_flags & METH_CLASS) ||
-            (fdef->ml_flags & METH_STATIC)) {
-            PyErr_SetString(PyExc_ValueError,
-                            "module functions cannot set"
-                            " METH_CLASS or METH_STATIC");
-            Py_DECREF(name);
-            return -1;
-        }
-        func = PyCFunction_NewEx(fdef, (PyObject*)m, name);
-        if (func == NULL) {
-            Py_DECREF(name);
-            return -1;
-        }
-        if (PyObject_SetAttrString(m, fdef->ml_name, func) != 0) {
-            Py_DECREF(func);
-            Py_DECREF(name);
-            return -1;
-        }
-        Py_DECREF(func);
-    }
+    res = _add_methods_to_object(m, name, functions);
     Py_DECREF(name);
-    return 0;
+    return res;
 }
 
 int

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list