From 33e71e01e95506cf8d93fd68251fc56352bc7b39 Mon Sep 17 00:00:00 2001 From: Marcel Plch Date: Wed, 22 May 2019 13:51:26 +0200 Subject: bpo-31862: Port binascii to PEP 489 multiphase initialization (GH-4108) --- Lib/test/test_capi.py | 13 ++ .../2017-10-24-17-26-58.bpo-31862.5Gea8L.rst | 2 + Modules/binascii.c | 137 ++++++++++++++++----- 3 files changed, 118 insertions(+), 34 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2017-10-24-17-26-58.bpo-31862.5Gea8L.rst diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 8bcbd82..a062a65 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -473,6 +473,19 @@ class SubinterpreterTest(unittest.TestCase): 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 0000000..0e80cdc --- /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 2e71ab9..d22ab7b 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); } -- cgit v0.12