1import os
2import genapi
3
4from genapi import \
5        TypeApi, GlobalVarApi, FunctionApi, BoolValuesApi
6
7import numpy_api
8
9# use annotated api when running under cpychecker
10h_template = r"""
11#if defined(_MULTIARRAYMODULE) || defined(WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE)
12
13typedef struct {
14        PyObject_HEAD
15        npy_bool obval;
16} PyBoolScalarObject;
17
18extern NPY_NO_EXPORT PyTypeObject PyArrayMapIter_Type;
19extern NPY_NO_EXPORT PyTypeObject PyArrayNeighborhoodIter_Type;
20extern NPY_NO_EXPORT PyBoolScalarObject _PyArrayScalar_BoolValues[2];
21
22%s
23
24#else
25
26#if defined(PY_ARRAY_UNIQUE_SYMBOL)
27#define PyArray_API PY_ARRAY_UNIQUE_SYMBOL
28#endif
29
30#if defined(NO_IMPORT) || defined(NO_IMPORT_ARRAY)
31extern void **PyArray_API;
32#else
33#if defined(PY_ARRAY_UNIQUE_SYMBOL)
34void **PyArray_API;
35#else
36static void **PyArray_API=NULL;
37#endif
38#endif
39
40%s
41
42#if !defined(NO_IMPORT_ARRAY) && !defined(NO_IMPORT)
43static int
44_import_array(void)
45{
46  int st;
47  PyObject *numpy = PyImport_ImportModule("numpy.core._multiarray_umath");
48  PyObject *c_api = NULL;
49
50  if (numpy == NULL) {
51      return -1;
52  }
53  c_api = PyObject_GetAttrString(numpy, "_ARRAY_API");
54  Py_DECREF(numpy);
55  if (c_api == NULL) {
56      PyErr_SetString(PyExc_AttributeError, "_ARRAY_API not found");
57      return -1;
58  }
59
60  if (!PyCapsule_CheckExact(c_api)) {
61      PyErr_SetString(PyExc_RuntimeError, "_ARRAY_API is not PyCapsule object");
62      Py_DECREF(c_api);
63      return -1;
64  }
65  PyArray_API = (void **)PyCapsule_GetPointer(c_api, NULL);
66  Py_DECREF(c_api);
67  if (PyArray_API == NULL) {
68      PyErr_SetString(PyExc_RuntimeError, "_ARRAY_API is NULL pointer");
69      return -1;
70  }
71
72  /* Perform runtime check of C API version */
73  if (NPY_VERSION != PyArray_GetNDArrayCVersion()) {
74      PyErr_Format(PyExc_RuntimeError, "module compiled against "\
75             "ABI version 0x%%x but this version of numpy is 0x%%x", \
76             (int) NPY_VERSION, (int) PyArray_GetNDArrayCVersion());
77      return -1;
78  }
79  if (NPY_FEATURE_VERSION > PyArray_GetNDArrayCFeatureVersion()) {
80      PyErr_Format(PyExc_RuntimeError, "module compiled against "\
81             "API version 0x%%x but this version of numpy is 0x%%x", \
82             (int) NPY_FEATURE_VERSION, (int) PyArray_GetNDArrayCFeatureVersion());
83      return -1;
84  }
85
86  /*
87   * Perform runtime check of endianness and check it matches the one set by
88   * the headers (npy_endian.h) as a safeguard
89   */
90  st = PyArray_GetEndianness();
91  if (st == NPY_CPU_UNKNOWN_ENDIAN) {
92      PyErr_Format(PyExc_RuntimeError, "FATAL: module compiled as unknown endian");
93      return -1;
94  }
95#if NPY_BYTE_ORDER == NPY_BIG_ENDIAN
96  if (st != NPY_CPU_BIG) {
97      PyErr_Format(PyExc_RuntimeError, "FATAL: module compiled as "\
98             "big endian, but detected different endianness at runtime");
99      return -1;
100  }
101#elif NPY_BYTE_ORDER == NPY_LITTLE_ENDIAN
102  if (st != NPY_CPU_LITTLE) {
103      PyErr_Format(PyExc_RuntimeError, "FATAL: module compiled as "\
104             "little endian, but detected different endianness at runtime");
105      return -1;
106  }
107#endif
108
109  return 0;
110}
111
112#define import_array() {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); return NULL; } }
113
114#define import_array1(ret) {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); return ret; } }
115
116#define import_array2(msg, ret) {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, msg); return ret; } }
117
118#endif
119
120#endif
121"""
122
123
124c_template = r"""
125/* These pointers will be stored in the C-object for use in other
126    extension modules
127*/
128
129void *PyArray_API[] = {
130%s
131};
132"""
133
134c_api_header = """
135===========
136NumPy C-API
137===========
138"""
139
140def generate_api(output_dir, force=False):
141    basename = 'multiarray_api'
142
143    h_file = os.path.join(output_dir, '__%s.h' % basename)
144    c_file = os.path.join(output_dir, '__%s.c' % basename)
145    d_file = os.path.join(output_dir, '%s.txt' % basename)
146    targets = (h_file, c_file, d_file)
147
148    sources = numpy_api.multiarray_api
149
150    if (not force and not genapi.should_rebuild(targets, [numpy_api.__file__, __file__])):
151        return targets
152    else:
153        do_generate_api(targets, sources)
154
155    return targets
156
157def do_generate_api(targets, sources):
158    header_file = targets[0]
159    c_file = targets[1]
160    doc_file = targets[2]
161
162    global_vars = sources[0]
163    scalar_bool_values = sources[1]
164    types_api = sources[2]
165    multiarray_funcs = sources[3]
166
167    multiarray_api = sources[:]
168
169    module_list = []
170    extension_list = []
171    init_list = []
172
173    # Check multiarray api indexes
174    multiarray_api_index = genapi.merge_api_dicts(multiarray_api)
175    genapi.check_api_dict(multiarray_api_index)
176
177    numpyapi_list = genapi.get_api_functions('NUMPY_API',
178                                             multiarray_funcs)
179
180    # FIXME: ordered_funcs_api is unused
181    ordered_funcs_api = genapi.order_dict(multiarray_funcs)
182
183    # Create dict name -> *Api instance
184    api_name = 'PyArray_API'
185    multiarray_api_dict = {}
186    for f in numpyapi_list:
187        name = f.name
188        index = multiarray_funcs[name][0]
189        annotations = multiarray_funcs[name][1:]
190        multiarray_api_dict[f.name] = FunctionApi(f.name, index, annotations,
191                                                  f.return_type,
192                                                  f.args, api_name)
193
194    for name, val in global_vars.items():
195        index, type = val
196        multiarray_api_dict[name] = GlobalVarApi(name, index, type, api_name)
197
198    for name, val in scalar_bool_values.items():
199        index = val[0]
200        multiarray_api_dict[name] = BoolValuesApi(name, index, api_name)
201
202    for name, val in types_api.items():
203        index = val[0]
204        internal_type =  None if len(val) == 1 else val[1]
205        multiarray_api_dict[name] = TypeApi(
206            name, index, 'PyTypeObject', api_name, internal_type)
207
208    if len(multiarray_api_dict) != len(multiarray_api_index):
209        keys_dict = set(multiarray_api_dict.keys())
210        keys_index = set(multiarray_api_index.keys())
211        raise AssertionError(
212            "Multiarray API size mismatch - "
213            "index has extra keys {}, dict has extra keys {}"
214            .format(keys_index - keys_dict, keys_dict - keys_index)
215        )
216
217    extension_list = []
218    for name, index in genapi.order_dict(multiarray_api_index):
219        api_item = multiarray_api_dict[name]
220        extension_list.append(api_item.define_from_array_api_string())
221        init_list.append(api_item.array_api_define())
222        module_list.append(api_item.internal_define())
223
224    # Write to header
225    s = h_template % ('\n'.join(module_list), '\n'.join(extension_list))
226    genapi.write_file(header_file, s)
227
228    # Write to c-code
229    s = c_template % ',\n'.join(init_list)
230    genapi.write_file(c_file, s)
231
232    # write to documentation
233    s = c_api_header
234    for func in numpyapi_list:
235        s += func.to_ReST()
236        s += '\n\n'
237    genapi.write_file(doc_file, s)
238
239    return targets
240