8000 MNT: verify all entries in npy_interned_str and npy_static_pydata are… · numpy/numpy@2bf1f1f · GitHub
[go: up one dir, main page]

Skip to content

Commit 2bf1f1f

Browse files
committed
MNT: verify all entries in npy_interned_str and npy_static_pydata are non-null
1 parent 7b6dbaf commit 2bf1f1f

File tree

4 files changed

+112
-126
lines changed

4 files changed

+112
-126
lines changed

numpy/_core/src/multiarray/multiarraymodule.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5116,6 +5116,10 @@ PyMODINIT_FUNC PyInit__multiarray_umath(void) {
51165116
goto err;
51175117
}
51185118

5119+
if (verify_static_structs_initialized() < 0) {
5120+
goto err;
5121+
}
5122+
51195123
/*
51205124
* Export the API tables
51215125
*/

numpy/_core/src/multiarray/npy_static_data.c

Lines changed: 103 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -12,112 +12,55 @@
1212
#include "numpy/arrayobject.h"
1313
#include "npy_import.h"
1414
#include "npy_static_data.h"
15+
#include "extobj.h"
1516

1617
// static variables are zero-filled by default, no need to explicitly do so
1718
NPY_VISIBILITY_HIDDEN npy_interned_str_struct npy_interned_str;
1819
NPY_VISIBILITY_HIDDEN npy_static_pydata_struct npy_static_pydata;
1920
NPY_VISIBILITY_HIDDEN npy_static_cdata_struct npy_static_cdata;
2021

22+
#define INTERN_STRING(struct_member, string) \
23+
assert(npy_interned_str.struct_member == NULL); \
24+
npy_interned_str.struct_member = PyUnicode_InternFromString(string); \
25+
if (npy_interned_str.struct_member == NULL) { \
26+
return -1; \
27+
} \
28+
2129
NPY_NO_EXPORT int
2230
intern_strings(void)
2331
{
24-
npy_interned_str.current_allocator = PyUnicode_InternFromString("current_allocator");
25-
if (npy_interned_str.current_allocator == NULL) {
26-
return -1;
27-
}
28-
npy_interned_str.array = PyUnicode_InternFromString("__array__");
29-
if (npy_interned_str.array == NULL) {
30-
return -1;
31-
}
32-
npy_interned_str.array_function = PyUnicode_InternFromString("__array_function__");
33-
if (npy_interned_str.array_function == NULL) {
34-
return -1;
35-
}
36-
npy_interned_str.array_struct = PyUnicode_InternFromString("__array_struct__");
37-
if (npy_interned_str.array_struct == NULL) {
38-
return -1;
39-
}
40-
npy_interned_str.array_priority = PyUnicode_InternFromString("__array_priority__");
41-
if (npy_interned_str.array_priority == NULL) {
42-
return -1;
43-
}
44-
npy_interned_str.array_interface = PyUnicode_InternFromString("__array_interface__");
45-
if (npy_interned_str.array_interface == NULL) {
46-
return -1;
47-
}
48-
npy_interned_str.array_ufunc = PyUnicode_InternFromString("__array_ufunc__");
49-
if (npy_interned_str.array_ufunc == NULL) {
50-
return -1;
51-
}
52-
npy_interned_str.array_wrap = PyUnicode_InternFromString("__array_wrap__");
53-
if (npy_interned_str.array_wrap == NULL) {
54-
return -1;
55-
}
56-
npy_interned_str.array_finalize = PyUnicode_InternFromString("__array_finalize__");
57-
if (npy_interned_str.array_finalize == NULL) {
58-
return -1;
59-
}
60-
npy_interned_str.implementation = PyUnicode_InternFromString("_implementation");
61-
if (npy_interned_str.implementation == NULL) {
62-
return -1;
63-
}
64-
npy_interned_str.axis1 = PyUnicode_InternFromString("axis1");
65-
if (npy_interned_str.axis1 == NULL) {
66-
return -1;
67-
}
68-
npy_interned_str.axis2 = PyUnicode_InternFromString("axis2");
69-
if (npy_interned_str.axis2 == NULL) {
70-
return -1;
71-
}
72-
npy_interned_str.like = PyUnicode_InternFromString("like");
73-
if (npy_interned_str.like == NULL) {
74-
return -1;
75-
}
76-
npy_interned_str.numpy = PyUnicode_InternFromString("numpy");
77-
if (npy_interned_str.numpy == NULL) {
78-
return -1;
79-
}
80-
npy_interned_str.where = PyUnicode_InternFromString("where");
81-
if (npy_interned_str.where == NULL) {
82-
return -1;
83-
}
84-
npy_interned_str.convert = PyUnicode_InternFromString("convert");
85-
if (npy_interned_str.convert == NULL) {
86-
return -1;
87-
}
88-
npy_interned_str.preserve = PyUnicode_InternFromString("preserve");
89-
if (npy_interned_str.preserve == NULL) {
90-
return -1;
91-
}
92-
npy_interned_str.convert_if_no_array = PyU F438 nicode_InternFromString("convert_if_no_array");
93-
if (npy_interned_str.convert_if_no_array == NULL) {
94-
return -1;
95-
}
96-
npy_interned_str.cpu = PyUnicode_InternFromString("cpu");
97-
if (npy_interned_str.cpu == NULL) {
98-
return -1;
99-
}
100-
npy_interned_str.dtype = PyUnicode_InternFromString("dtype");
101-
if (npy_interned_str.dtype == NULL) {
102-
return -1;
103-
}
104-
npy_interned_str.array_err_msg_substr = PyUnicode_InternFromString(
32+
INTERN_STRING(current_allocator, "current_allocator");
33+
INTERN_STRING(array, "__array__");
34+
INTERN_STRING(array_function, "__array_function__");
35+
INTERN_STRING(array_struct, "__array_struct__");
36+
INTERN_STRING(array_priority, "__array_priority__");
37+
INTERN_STRING(array_interface, "__array_interface__");
38+
INTERN_STRING(array_ufunc, "__array_ufunc__");
39+
INTERN_STRING(array_wrap, "__array_wrap__");
40+
INTERN_STRING(array_finalize, "__array_finalize__");
41+
INTERN_STRING(implementation, "_implementation");
42+
INTERN_STRING(axis1, "axis1");
43+
INTERN_STRING(axis2, "axis2");
44+
INTERN_STRING(like, "like");
45+
INTERN_STRING(numpy, "numpy");
46+
INTERN_STRING(where, "where");
47+
INTERN_STRING(convert, "convert");
48+
INTERN_STRING(preserve, "preserve");
49+
INTERN_STRING(convert_if_no_array, "convert_if_no_array");
50+
INTERN_STRING(cpu, "cpu");
51+
INTERN_STRING(dtype, "dtype");
52+
INTERN_STRING(
53+
array_err_msg_substr,
10554
"__array__() got an unexpected keyword argument 'copy'");
106-
if (npy_interned_str.array_err_msg_substr == NULL) {
107-
return -1;
108-
}
109-
npy_interned_str.out = PyUnicode_InternFromString("out");
110-
if (npy_interned_str.out == NULL) {
111-
return -1;
112-
}
113-
npy_interned_str.__dlpack__ = PyUnicode_InternFromString("__dlpack__");
114-
if (npy_interned_str.__dlpack__ == NULL) {
115-
return -1;
116-
}
117-
npy_interned_str.pyvals_name = PyUnicode_InternFromString("UFUNC_PYVALS_NAME");
118-
if (npy_interned_str.pyvals_name == NULL) {
119-
return -1;
120-
}
55+
INTERN_STRING(out, "out");
56+
INTERN_STRING(errmode_strings[0], "ignore");
57+
INTERN_STRING(errmode_strings[1], "warn");
58+
INTERN_STRING(errmode_strings[2], "raise");
59+
INTERN_STRING(errmode_strings[3], "call");
60+
INTERN_STRING(errmode_strings[4], "print");
61+
INTERN_STRING(errmode_strings[5], "log");
62+
INTERN_STRING(__dlpack__, "__dlpack__");
63+
INTERN_STRING(pyvals_name, "UFUNC_PYVALS_NAME");
12164
return 0;
12265
}
12366

@@ -149,7 +92,13 @@ intern_strings(void)
14992
NPY_NO_EXPORT int
15093
initialize_static_globals(void)
15194
{
152-
// cached reference to objects defined in python
95+
/*
96+
* Initialize contents of npy_static_pydata struct
97+
*
98+
* This struct holds cached references to python objects
99+
* that we want to keep alive for the lifetime of the
100+
* module for performance reasons
101+
*/
153102

154103
IMPORT_GLOBAL("math", "floor",
155104
npy_static_pydata.math_floor_func);
@@ -219,6 +168,32 @@ initialize_static_globals(void)
219168
}
220169
Py_DECREF(tmp);
221170

171+
npy_static_pydata.kwnames_is_copy = Py_BuildValue("(s)", "copy");
172+
if (npy_static_pydata.kwnames_is_copy == NULL) {
173+
return -1;
174+
}
175+
176+
npy_static_pydata.one_obj = PyLong_FromLong((long) 1);
177+
if (npy_static_pydata.one_obj == NULL) {
178+
return -1;
179+
}
180+
181+
npy_static_pydata.zero_obj = PyLong_FromLong((long) 0);
182+
if (npy_static_pydata.zero_obj == NULL) {
183+
return -1;
184+
}
185+
186+
/*
187+
* Initialize contents of npy_static_cdata struct
188+
*
189+
* Note that some entries are initialized elsewhere. Care
190+
* must be taken to ensure all entries are initialized during
191+
* module initialization and immutable thereafter.
192+
*
193+
* This struct holds global static caches. These are set
194+
* up this way for performance reasons.
195+
*/
196+
222197
PyObject *flags = PySys_GetObject("flags"); /* borrowed object */
223198
if (flags == NULL) {
224199
PyErr_SetString(PyExc_AttributeError, "cannot get sys.flags");
@@ -248,22 +223,40 @@ initialize_static_globals(void)
248223
}
249224
}
250225

251-
npy_static_pydata.kwnames_is_copy = Py_BuildValue("(s)", "copy");
252-
if (npy_static_pydata.kwnames_is_copy == NULL) {
253-
return -1;
254-
}
226+
return 0;
227+
}
228+
/*
229+
* Verifies all entries in npy_interned_str and npy_static_pydata are
230+
* non-NULL.
231+
*
232+
* Called at the end of initialization for _multiarray_umath. Some
233+
* entries are initialized outside of this file because they depend on
234+
* items that are initialized late in module initialization but they
235+
* should all be initialized by the time this function is called.
236+
*/
255237

256-
npy_static_pydata.one_obj = PyLong_FromLong((long) 1);
257-
if (npy_static_pydata.one_obj == NULL) {
258-
return -1;
238+
NPY_NO_EXPORT int
239+
verify_static_structs_initialized(void) {
240+
// verify all entries in npy_interned_str are filled in
241+
for (int i=0; i < (sizeof(npy_interned_str_struct)/sizeof(PyObject *)); i++) {
242+
if (*(((PyObject **)&npy_interned_str) + i) == NULL) {
243+
PyErr_Format(
244+
PyExc_SystemError,
245+
"NumPy internal error: NULL entry detected in "
246+
"npy_interned_str at index %d", i);
247+
return -1;
248+
}
259249
}
260250

261-
npy_static_pydata.zero_obj = PyLong_FromLong((long) 0);
262-
if (npy_static_pydata.zero_obj == NULL) {
263-
return -1;
251+
// verify all entries in npy_static_pydata are filled in
252+
for (int i=0; i < (sizeof(npy_static_pydata_struct)/sizeof(PyObject *)); i++) {
253+
if (*(((PyObject **)&npy_static_pydata) + i) == NULL) {
254+
PyErr_Format(
255+
PyExc_SystemError,
256+
"NumPy internal error: NULL entry detected in "
257+
"npy_static_pydata at index %d", i);
258+
return -1;
259+
}
264260
}
265-
266261
return 0;
267262
}
268-
269-

numpy/_core/src/multiarray/npy_static_data.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ initialize_static_globals(void);
77
NPY_NO_EXPORT int
88
intern_strings(void);
99

10+
NPY_NO_EXPORT int
11+
verify_static_structs_initialized(void);
12+
1013
typedef struct npy_interned_str_struct {
1114
PyObject *current_allocator;
1215
PyObject *array;
@@ -146,6 +149,8 @@ typedef struct npy_static_cdata_struct {
146149
* See the _MAX_LETTER and LETTER_TO_NUM macros in arraytypes.c.src.
147150
*
148151
* The smallest type number is ?, the largest is bounded by 'z'.
152+
*
153+
* This is initialized alongside the built-in dtypes
149154
*/
150155
npy_int16 _letter_to_num['z' + 1 - '?'];
151156
} npy_static_cdata_struct;

numpy/_core/src/umath/extobj.c

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@
3535
#define UFUNC_SHIFT_UNDERFLOW 6
3636
#define UFUNC_SHIFT_INVALID 9
3737

38-
/* The python strings for the above error modes defined in extobj.h */
39-
const char *errmode_cstrings[] = {
40-
"ignore", "warn", "raise", "call", "print", "log"};
41-
4238
/* Default user error mode (underflows are ignored, others warn) */
4339
#define UFUNC_ERR_DEFAULT \
4440
(UFUNC_ERR_WARN << UFUNC_SHIFT_DIVIDEBYZERO) + \
@@ -144,18 +140,6 @@ fetch_curr_extobj_state(npy_extobj *extobj)
144140
NPY_NO_EXPORT int
145141
init_extobj(void)
146142
{
147-
/*
148-
* First initialize the string constants we need to parse `errstate()`
149-
* inputs.
150-
*/
151-
for (int i = 0; i <= UFUNC_ERR_LOG; i++) {
152-
npy_interned_str.errmode_strings[i] = PyUnicode_InternFromString(
153-
errmode_cstrings[i]);
154-
if (npy_interned_str.errmode_strings[i] == NULL) {
155-
return -1;
156-
}
157-
}
158-
159143
npy_static_pydata.default_extobj_capsule = make_extobj_capsule(
160144
NPY_BUFSIZE, UFUNC_ERR_DEFAULT, Py_None);
161145
if (npy_static_pydata.default_extobj_capsule == NULL) {

0 commit comments

Comments
 (0)
0