Skip to content

Commit 33e71e0

Browse files
Marcel Plchencukou
authored andcommitted
bpo-31862: Port binascii to PEP 489 multiphase initialization (GH-4108)
1 parent 77aa396 commit 33e71e0

File tree

3 files changed

+118
-34
lines changed

3 files changed

+118
-34
lines changed

Lib/test/test_capi.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,19 @@ def test_subinterps(self):
473473
self.assertNotEqual(pickle.load(f), id(sys.modules))
474474
self.assertNotEqual(pickle.load(f), id(builtins))
475475

476+
def test_mutate_exception(self):
477+
"""
478+
Exceptions saved in global module state get shared between
479+
individual module instances. This test checks whether or not
480+
a change in one interpreter's module gets reflected into the
481+
other ones.
482+
"""
483+
import binascii
484+
485+
support.run_in_subinterp("import binascii; binascii.Error.foobar = 'foobar'")
486+
487+
self.assertFalse(hasattr(binascii.Error, "foobar"))
488+
476489

477490
class TestThreadState(unittest.TestCase):
478491

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Port binascii to PEP 489 multiphase initialization.
2+
Patch by Marcel Plch.

Modules/binascii.c

Lines changed: 103 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@
6161
#include "zlib.h"
6262
#endif
6363

64-
static PyObject *Error;
65-
static PyObject *Incomplete;
64+
typedef struct binascii_state {
65+
PyObject *Error;
66+
PyObject *Incomplete;
67+
} binascii_state;
6668

6769
/*
6870
** hqx lookup table, ascii->binary.
@@ -263,6 +265,7 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data)
263265
unsigned int leftchar = 0;
264266
PyObject *rv;
265267
Py_ssize_t ascii_len, bin_len;
268+
binascii_state *state;
266269

267270
ascii_data = data->buf;
268271
ascii_len = data->len;
@@ -294,7 +297,11 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data)
294297
** '`' as zero instead of space.
295298
*/
296299
if ( this_ch < ' ' || this_ch > (' ' + 64)) {
297-
PyErr_SetString(Error, "Illegal char");
300+
state = PyModule_GetState(module);
301+
if (state == NULL) {
302+
return NULL;
303+
}
304+
PyErr_SetString(state->Error, "Illegal char");
298305
Py_DECREF(rv);
299306
return NULL;
300307
}
@@ -322,7 +329,11 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data)
322329
/* Extra '`' may be written as padding in some cases */
323330
if ( this_ch != ' ' && this_ch != ' '+64 &&
324331
this_ch != '\n' && this_ch != '\r' ) {
325-
PyErr_SetString(Error, "Trailing garbage");
332+
state = PyModule_GetState(module);
333+
if (state == NULL) {
334+
return NULL;
335+
}
336+
PyErr_SetString(state->Error, "Trailing garbage");
326337
Py_DECREF(rv);
327338
return NULL;
328339
}
@@ -350,6 +361,7 @@ binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick)
350361
int leftbits = 0;
351362
unsigned char this_ch;
352363
unsigned int leftchar = 0;
364+
binascii_state *state;
353365
Py_ssize_t bin_len, out_len;
354366
_PyBytesWriter writer;
355367

@@ -358,7 +370,11 @@ binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick)
358370
bin_len = data->len;
359371
if ( bin_len > 45 ) {
360372
/* The 45 is a limit that appears in all uuencode's */
361-
PyErr_SetString(Error, "At most 45 bytes at once");
373+
state = PyModule_GetState(module);
374+
if (state == NULL) {
375+
return NULL;
376+
}
377+
PyErr_SetString(state->Error, "At most 45 bytes at once");
362378
return NULL;
363379
}
364380

@@ -445,6 +461,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data)
445461
Py_ssize_t ascii_len, bin_len;
446462
int quad_pos = 0;
447463
_PyBytesWriter writer;
464+
binascii_state *state;
448465

449466
ascii_data = data->buf;
450467
ascii_len = data->len;
@@ -512,19 +529,23 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data)
512529
}
513530

514531
if (leftbits != 0) {
532+
state = PyModule_GetState(module);
533+
if (state == NULL) {
534+
return NULL;
535+
}
515536
if (leftbits == 6) {
516537
/*
517538
** There is exactly one extra valid, non-padding, base64 character.
518539
** This is an invalid length, as there is no possible input that
519540
** could encoded into such a base64 string.
520541
*/
521-
PyErr_Format(Error,
542+
PyErr_Format(state->Error,
522543
"Invalid base64-encoded string: "
523544
"number of data characters (%zd) cannot be 1 more "
524545
"than a multiple of 4",
525546
(bin_data - bin_data_start) / 3 * 4 + 1);
526547
} else {
527-
PyErr_SetString(Error, "Incorrect padding");
548+
PyErr_SetString(state->Error, "Incorrect padding");
528549
}
529550
_PyBytesWriter_Dealloc(&writer);
530551
return NULL;
@@ -556,6 +577,7 @@ binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, int newline)
556577
unsigned int leftchar = 0;
557578
Py_ssize_t bin_len, out_len;
558579
_PyBytesWriter writer;
580+
binascii_state *state;
559581

560582
bin_data = data->buf;
561583
bin_len = data->len;
@@ -564,7 +586,11 @@ binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, int newline)
564586
assert(bin_len >= 0);
565587

566588
if ( bin_len > BASE64_MAXBIN ) {
567-
PyErr_SetString(Error, "Too much data for base64 line");
589+
state = PyModule_GetState(module);
590+
if (state == NULL) {
591+
return NULL;
592+
}
593+
PyErr_SetString(state->Error, "Too much data for base64 line");
568594
return NULL;
569595
}
570596

@@ -626,6 +652,7 @@ binascii_a2b_hqx_impl(PyObject *module, Py_buffer *data)
626652
Py_ssize_t len;
627653
int done = 0;
628654
_PyBytesWriter writer;
655+
binascii_state *state;
629656

630657
ascii_data = data->buf;
631658
len = data->len;
@@ -649,7 +676,11 @@ binascii_a2b_hqx_impl(PyObject *module, Py_buffer *data)
649676
if ( this_ch == SKIP )
650677
continue;
651678
if ( this_ch == FAIL ) {
652-
PyErr_SetString(Error, "Illegal char");
679+
state = PyModule_GetState(module);
680+
if (state == NULL) {
681+
return NULL;
682+
}
683+
PyErr_SetString(state->Error, "Illegal char");
653684
_PyBytesWriter_Dealloc(&writer);
654685
return NULL;
655686
}
@@ -670,7 +701,11 @@ binascii_a2b_hqx_impl(PyObject *module, Py_buffer *data)
670701
}
671702

672703
if ( leftbits && !done ) {
673-
PyErr_SetString(Incomplete,
704+
state = PyModule_GetState(module);
705+
if (state == NULL) {
706+
return NULL;
707+
}
708+
PyErr_SetString(state->Incomplete,
674709
"String has incomplete number of bytes");
675710
_PyBytesWriter_Dealloc(&writer);
676711
return NULL;
@@ -822,6 +857,7 @@ binascii_rledecode_hqx_impl(PyObject *module, Py_buffer *data)
822857
in_data = data->buf;
823858
in_len = data->len;
824859
_PyBytesWriter_Init(&writer);
860+
binascii_state *state;
825861

826862
assert(in_len >= 0);
827863

@@ -846,7 +882,11 @@ binascii_rledecode_hqx_impl(PyObject *module, Py_buffer *data)
846882
#define INBYTE(b) \
847883
do { \
848884
if ( --in_len < 0 ) { \
849-
PyErr_SetString(Incomplete, ""); \
885+
state = PyModule_GetState(module); \
886+
if (state == NULL) { \
887+
return NULL; \
888+
} \
889+
PyErr_SetString(state->Incomplete, ""); \
850890
goto error; \
851891
} \
852892
b = *in_data++; \
@@ -868,7 +908,11 @@ binascii_rledecode_hqx_impl(PyObject *module, Py_buffer *data)
868908
/* Note Error, not Incomplete (which is at the end
869909
** of the string only). This is a programmer error.
870910
*/
871-
PyErr_SetString(Error, "Orphaned RLE code at start");
911+
state = PyModule_GetState(module);
912+
if (state == NULL) {
913+
return NULL;
914+
}
915+
PyErr_SetString(state->Error, "Orphaned RLE code at start");
872916
goto error;
873917
}
874918
*out_data++ = RUNCHAR;
@@ -1166,6 +1210,7 @@ binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr)
11661210
PyObject *retval;
11671211
char* retbuf;
11681212
Py_ssize_t i, j;
1213+
binascii_state *state;
11691214

11701215
argbuf = hexstr->buf;
11711216
arglen = hexstr->len;
@@ -1177,7 +1222,11 @@ binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr)
11771222
* raise an exception.
11781223
*/
11791224
if (arglen % 2) {
1180-
PyErr_SetString(Error, "Odd-length string");
1225+
state = PyModule_GetState(module);
1226+
if (state == NULL) {
1227+
return NULL;
1228+
}
1229+
PyErr_SetString(state->Error, "Odd-length string");
11811230
return NULL;
11821231
}
11831232

@@ -1190,7 +1239,11 @@ binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr)
11901239
unsigned int top = _PyLong_DigitValue[Py_CHARMASK(argbuf[i])];
11911240
unsigned int bot = _PyLong_DigitValue[Py_CHARMASK(argbuf[i+1])];
11921241
if (top >= 16 || bot >= 16) {
1193-
PyErr_SetString(Error,
1242+
state = PyModule_GetState(module);
1243+
if (state == NULL) {
1244+
return NULL;
1245+
}
1246+
PyErr_SetString(state->Error,
11941247
"Non-hexadecimal digit found");
11951248
goto finally;
11961249
}
@@ -1545,14 +1598,47 @@ static struct PyMethodDef binascii_module_methods[] = {
15451598
/* Initialization function for the module (*must* be called PyInit_binascii) */
15461599
PyDoc_STRVAR(doc_binascii, "Conversion between binary data and ASCII");
15471600

1601+
static int
1602+
binascii_exec(PyObject *m) {
1603+
int result;
1604+
binascii_state *state = PyModule_GetState(m);
1605+
if (state == NULL) {
1606+
return -1;
1607+
}
1608+
1609+
state->Error = PyErr_NewException("binascii.Error", PyExc_ValueError, NULL);
1610+
if (state->Error == NULL) {
1611+
return -1;
1612+
}
1613+
result = PyModule_AddObject(m, "Error", state->Error);
1614+
if (result == -1) {
1615+
return -1;
1616+
}
1617+
1618+
state->Incomplete = PyErr_NewException("binascii.Incomplete", NULL, NULL);
1619+
if (state->Incomplete == NULL) {
1620+
return -1;
1621+
}
1622+
result = PyModule_AddObject(m, "Incomplete", state->Incomplete);
1623+
if (result == -1) {
1624+
return -1;
1625+
}
1626+
1627+
return 0;
1628+
}
1629+
1630+
static PyModuleDef_Slot binascii_slots[] = {
1631+
{Py_mod_exec, binascii_exec},
1632+
{0, NULL}
1633+
};
15481634

15491635
static struct PyModuleDef binasciimodule = {
15501636
PyModuleDef_HEAD_INIT,
15511637
"binascii",
15521638
doc_binascii,
1553-
-1,
1639+
sizeof(binascii_state),
15541640
binascii_module_methods,
1555-
NULL,
1641+
binascii_slots,
15561642
NULL,
15571643
NULL,
15581644
NULL
@@ -1561,22 +1647,5 @@ static struct PyModuleDef binasciimodule = {
15611647
PyMODINIT_FUNC
15621648
PyInit_binascii(void)
15631649
{
1564-
PyObject *m, *d;
1565-
1566-
/* Create the module and add the functions */
1567-
m = PyModule_Create(&binasciimodule);
1568-
if (m == NULL)
1569-
return NULL;
1570-
1571-
d = PyModule_GetDict(m);
1572-
1573-
Error = PyErr_NewException("binascii.Error", PyExc_ValueError, NULL);
1574-
PyDict_SetItemString(d, "Error", Error);
1575-
Incomplete = PyErr_NewException("binascii.Incomplete", NULL, NULL);
1576-
PyDict_SetItemString(d, "Incomplete", Incomplete);
1577-
if (PyErr_Occurred()) {
1578-
Py_DECREF(m);
1579-
m = NULL;
1580-
}
1581-
return m;
1650+
return PyModuleDef_Init(&binasciimodule);
15821651
}

0 commit comments

Comments
 (0)