generate_numpy_api.py 6.9 KB

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