Skip to content

bpo-31862: Port binascii to PEP 489 multiphase initialization #4108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from May 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Lib/test/test_capi.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,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):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Port binascii to PEP 489 multiphase initialization.
Patch by Marcel Plch.
137 changes: 103 additions & 34 deletions Modules/binascii.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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++; \
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand All @@ -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;
}
Expand Down Expand Up @@ -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
Expand All @@ -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);
}