[Python-checkins] bpo-31862: Port binascii to PEP 489 multiphase initialization (GH-4108)

Petr Viktorin webhook-mailer at python.org
Wed May 22 07:51:37 EDT 2019


https://github.com/python/cpython/commit/33e71e01e95506cf8d93fd68251fc56352bc7b39
commit: 33e71e01e95506cf8d93fd68251fc56352bc7b39
branch: master
author: Marcel Plch <mplch at redhat.com>
committer: Petr Viktorin <pviktori at redhat.com>
date: 2019-05-22T13:51:25+02:00
summary:

bpo-31862: Port binascii to PEP 489 multiphase initialization (GH-4108)

files:
A Misc/NEWS.d/next/Core and Builtins/2017-10-24-17-26-58.bpo-31862.5Gea8L.rst
M Lib/test/test_capi.py
M Modules/binascii.c

diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 8bcbd82b2d9d..a062a6563fa2 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -473,6 +473,19 @@ def test_subinterps(self):
             self.assertNotEqual(pickle.load(f), id(sys.modules))
             self.assertNotEqual(pickle.load(f), id(builtins))
 
+    def test_mutate_exception(self):
+        """
+        Exceptions saved in global module state get shared between
+        individual module instances. This test checks whether or not
+        a change in one interpreter's module gets reflected into the
+        other ones.
+        """
+        import binascii
+
+        support.run_in_subinterp("import binascii; binascii.Error.foobar = 'foobar'")
+
+        self.assertFalse(hasattr(binascii.Error, "foobar"))
+
 
 class TestThreadState(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-10-24-17-26-58.bpo-31862.5Gea8L.rst b/Misc/NEWS.d/next/Core and Builtins/2017-10-24-17-26-58.bpo-31862.5Gea8L.rst
new file mode 100644
index 000000000000..0e80cdc6924f
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2017-10-24-17-26-58.bpo-31862.5Gea8L.rst	
@@ -0,0 +1,2 @@
+Port binascii to PEP 489 multiphase initialization.
+Patch by Marcel Plch.
diff --git a/Modules/binascii.c b/Modules/binascii.c
index 2e71ab97b05a..d22ab7b46838 100644
--- a/Modules/binascii.c
+++ b/Modules/binascii.c
@@ -61,8 +61,10 @@
 #include "zlib.h"
 #endif
 
-static PyObject *Error;
-static PyObject *Incomplete;
+typedef struct binascii_state {
+    PyObject *Error;
+    PyObject *Incomplete;
+} binascii_state;
 
 /*
 ** hqx lookup table, ascii->binary.
@@ -263,6 +265,7 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data)
     unsigned int leftchar = 0;
     PyObject *rv;
     Py_ssize_t ascii_len, bin_len;
+    binascii_state *state;
 
     ascii_data = data->buf;
     ascii_len = data->len;
@@ -294,7 +297,11 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data)
             ** '`' as zero instead of space.
             */
             if ( this_ch < ' ' || this_ch > (' ' + 64)) {
-                PyErr_SetString(Error, "Illegal char");
+                state = PyModule_GetState(module);
+                if (state == NULL) {
+                    return NULL;
+                }
+                PyErr_SetString(state->Error, "Illegal char");
                 Py_DECREF(rv);
                 return NULL;
             }
@@ -322,7 +329,11 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data)
         /* Extra '`' may be written as padding in some cases */
         if ( this_ch != ' ' && this_ch != ' '+64 &&
              this_ch != '\n' && this_ch != '\r' ) {
-            PyErr_SetString(Error, "Trailing garbage");
+            state = PyModule_GetState(module);
+            if (state == NULL) {
+                return NULL;
+            }
+            PyErr_SetString(state->Error, "Trailing garbage");
             Py_DECREF(rv);
             return NULL;
         }
@@ -350,6 +361,7 @@ binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick)
     int leftbits = 0;
     unsigned char this_ch;
     unsigned int leftchar = 0;
+    binascii_state *state;
     Py_ssize_t bin_len, out_len;
     _PyBytesWriter writer;
 
@@ -358,7 +370,11 @@ binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick)
     bin_len = data->len;
     if ( bin_len > 45 ) {
         /* The 45 is a limit that appears in all uuencode's */
-        PyErr_SetString(Error, "At most 45 bytes at once");
+        state = PyModule_GetState(module);
+        if (state == NULL) {
+            return NULL;
+        }
+        PyErr_SetString(state->Error, "At most 45 bytes at once");
         return NULL;
     }
 
@@ -445,6 +461,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data)
     Py_ssize_t ascii_len, bin_len;
     int quad_pos = 0;
     _PyBytesWriter writer;
+    binascii_state *state;
 
     ascii_data = data->buf;
     ascii_len = data->len;
@@ -512,19 +529,23 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data)
     }
 
     if (leftbits != 0) {
+        state = PyModule_GetState(module);
+        if (state == NULL) {
+            return NULL;
+        }
         if (leftbits == 6) {
             /*
             ** There is exactly one extra valid, non-padding, base64 character.
             ** This is an invalid length, as there is no possible input that
             ** could encoded into such a base64 string.
             */
-            PyErr_Format(Error,
+            PyErr_Format(state->Error,
                          "Invalid base64-encoded string: "
                          "number of data characters (%zd) cannot be 1 more "
                          "than a multiple of 4",
                          (bin_data - bin_data_start) / 3 * 4 + 1);
         } else {
-            PyErr_SetString(Error, "Incorrect padding");
+            PyErr_SetString(state->Error, "Incorrect padding");
         }
         _PyBytesWriter_Dealloc(&writer);
         return NULL;
@@ -556,6 +577,7 @@ binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, int newline)
     unsigned int leftchar = 0;
     Py_ssize_t bin_len, out_len;
     _PyBytesWriter writer;
+    binascii_state *state;
 
     bin_data = data->buf;
     bin_len = data->len;
@@ -564,7 +586,11 @@ binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, int newline)
     assert(bin_len >= 0);
 
     if ( bin_len > BASE64_MAXBIN ) {
-        PyErr_SetString(Error, "Too much data for base64 line");
+        state = PyModule_GetState(module);
+        if (state == NULL) {
+            return NULL;
+        }
+        PyErr_SetString(state->Error, "Too much data for base64 line");
         return NULL;
     }
 
@@ -626,6 +652,7 @@ binascii_a2b_hqx_impl(PyObject *module, Py_buffer *data)
     Py_ssize_t len;
     int done = 0;
     _PyBytesWriter writer;
+    binascii_state *state;
 
     ascii_data = data->buf;
     len = data->len;
@@ -649,7 +676,11 @@ binascii_a2b_hqx_impl(PyObject *module, Py_buffer *data)
         if ( this_ch == SKIP )
             continue;
         if ( this_ch == FAIL ) {
-            PyErr_SetString(Error, "Illegal char");
+            state = PyModule_GetState(module);
+            if (state == NULL) {
+                return NULL;
+            }
+            PyErr_SetString(state->Error, "Illegal char");
             _PyBytesWriter_Dealloc(&writer);
             return NULL;
         }
@@ -670,7 +701,11 @@ binascii_a2b_hqx_impl(PyObject *module, Py_buffer *data)
     }
 
     if ( leftbits && !done ) {
-        PyErr_SetString(Incomplete,
+        state = PyModule_GetState(module);
+        if (state == NULL) {
+            return NULL;
+        }
+        PyErr_SetString(state->Incomplete,
                         "String has incomplete number of bytes");
         _PyBytesWriter_Dealloc(&writer);
         return NULL;
@@ -822,6 +857,7 @@ binascii_rledecode_hqx_impl(PyObject *module, Py_buffer *data)
     in_data = data->buf;
     in_len = data->len;
     _PyBytesWriter_Init(&writer);
+    binascii_state *state;
 
     assert(in_len >= 0);
 
@@ -846,7 +882,11 @@ binascii_rledecode_hqx_impl(PyObject *module, Py_buffer *data)
 #define INBYTE(b)                                                       \
     do {                                                                \
          if ( --in_len < 0 ) {                                          \
-           PyErr_SetString(Incomplete, "");                             \
+           state = PyModule_GetState(module);           \
+           if (state == NULL) {                                         \
+               return NULL;                                             \
+           }                                                            \
+           PyErr_SetString(state->Incomplete, "");                      \
            goto error;                                                  \
          }                                                              \
          b = *in_data++;                                                \
@@ -868,7 +908,11 @@ binascii_rledecode_hqx_impl(PyObject *module, Py_buffer *data)
             /* Note Error, not Incomplete (which is at the end
             ** of the string only). This is a programmer error.
             */
-            PyErr_SetString(Error, "Orphaned RLE code at start");
+            state = PyModule_GetState(module);
+            if (state == NULL) {
+                return NULL;
+            }
+            PyErr_SetString(state->Error, "Orphaned RLE code at start");
             goto error;
         }
         *out_data++ = RUNCHAR;
@@ -1166,6 +1210,7 @@ binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr)
     PyObject *retval;
     char* retbuf;
     Py_ssize_t i, j;
+    binascii_state *state;
 
     argbuf = hexstr->buf;
     arglen = hexstr->len;
@@ -1177,7 +1222,11 @@ binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr)
      * raise an exception.
      */
     if (arglen % 2) {
-        PyErr_SetString(Error, "Odd-length string");
+        state = PyModule_GetState(module);
+        if (state == NULL) {
+            return NULL;
+        }
+        PyErr_SetString(state->Error, "Odd-length string");
         return NULL;
     }
 
@@ -1190,7 +1239,11 @@ binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr)
         unsigned int top = _PyLong_DigitValue[Py_CHARMASK(argbuf[i])];
         unsigned int bot = _PyLong_DigitValue[Py_CHARMASK(argbuf[i+1])];
         if (top >= 16 || bot >= 16) {
-            PyErr_SetString(Error,
+            state = PyModule_GetState(module);
+            if (state == NULL) {
+                return NULL;
+            }
+            PyErr_SetString(state->Error,
                             "Non-hexadecimal digit found");
             goto finally;
         }
@@ -1545,14 +1598,47 @@ static struct PyMethodDef binascii_module_methods[] = {
 /* Initialization function for the module (*must* be called PyInit_binascii) */
 PyDoc_STRVAR(doc_binascii, "Conversion between binary data and ASCII");
 
+static int
+binascii_exec(PyObject *m) {
+    int result;
+    binascii_state *state = PyModule_GetState(m);
+    if (state == NULL) {
+        return -1;
+    }
+
+    state->Error = PyErr_NewException("binascii.Error", PyExc_ValueError, NULL);
+    if (state->Error == NULL) {
+        return -1;
+    }
+    result = PyModule_AddObject(m, "Error", state->Error);
+    if (result == -1) {
+        return -1;
+    }
+
+    state->Incomplete = PyErr_NewException("binascii.Incomplete", NULL, NULL);
+    if (state->Incomplete == NULL) {
+        return -1;
+    }
+    result = PyModule_AddObject(m, "Incomplete", state->Incomplete);
+    if (result == -1) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static PyModuleDef_Slot binascii_slots[] = {
+    {Py_mod_exec, binascii_exec},
+    {0, NULL}
+};
 
 static struct PyModuleDef binasciimodule = {
     PyModuleDef_HEAD_INIT,
     "binascii",
     doc_binascii,
-    -1,
+    sizeof(binascii_state),
     binascii_module_methods,
-    NULL,
+    binascii_slots,
     NULL,
     NULL,
     NULL
@@ -1561,22 +1647,5 @@ static struct PyModuleDef binasciimodule = {
 PyMODINIT_FUNC
 PyInit_binascii(void)
 {
-    PyObject *m, *d;
-
-    /* Create the module and add the functions */
-    m = PyModule_Create(&binasciimodule);
-    if (m == NULL)
-        return NULL;
-
-    d = PyModule_GetDict(m);
-
-    Error = PyErr_NewException("binascii.Error", PyExc_ValueError, NULL);
-    PyDict_SetItemString(d, "Error", Error);
-    Incomplete = PyErr_NewException("binascii.Incomplete", NULL, NULL);
-    PyDict_SetItemString(d, "Incomplete", Incomplete);
-    if (PyErr_Occurred()) {
-        Py_DECREF(m);
-        m = NULL;
-    }
-    return m;
+    return PyModuleDef_Init(&binasciimodule);
 }



More information about the Python-checkins mailing list