diff options
| author | Sebastian Berg <sebastianb@nvidia.com> | 2023-04-04 16:46:56 +0200 |
|---|---|---|
| committer | Sebastian Berg <sebastianb@nvidia.com> | 2023-04-04 17:17:17 +0200 |
| commit | 3a811358830c324b7b6819b88dec4e4bcd91444a (patch) | |
| tree | 62a3387e47d173ee888b0017bec527d5307ea467 /numpy | |
| parent | 6309cf291ca806f554723d659777acca46cdad1f (diff) | |
| download | numpy-3a811358830c324b7b6819b88dec4e4bcd91444a.tar.gz | |
ENH: Allow compiling compatibly to old NumPy versions
The default compiles compatibly with 1.17.x, we allow going back to
1.15 (mainly because it is easy).
There were few additions in this time, a few structs grew and very
few API functions were added. Added a way to mark API functions
as requiring a specific target version.
If a user wishes to use the *new* API, they have to add the definition:
#define NPY_TARGET_VERSION NPY_1_22_API_VERSION
Before importing NumPy. (Our version numbering is a bit funny
I first thought to use a hex version of the main NumPy version,
but since we already have the `NPY_1_22_API_VERSION` defines...)
Diffstat (limited to 'numpy')
| -rw-r--r-- | numpy/core/code_generators/cversions.txt | 4 | ||||
| -rw-r--r-- | numpy/core/code_generators/genapi.py | 58 | ||||
| -rw-r--r-- | numpy/core/code_generators/numpy_api.py | 12 | ||||
| -rw-r--r-- | numpy/core/include/numpy/arrayscalars.h | 4 | ||||
| -rw-r--r-- | numpy/core/include/numpy/ndarraytypes.h | 16 | ||||
| -rw-r--r-- | numpy/core/include/numpy/ufuncobject.h | 6 | ||||
| -rw-r--r-- | numpy/core/setup_common.py | 7 |
7 files changed, 84 insertions, 23 deletions
diff --git a/numpy/core/code_generators/cversions.txt b/numpy/core/code_generators/cversions.txt index bd4b71180..e52193d7a 100644 --- a/numpy/core/code_generators/cversions.txt +++ b/numpy/core/code_generators/cversions.txt @@ -67,5 +67,7 @@ # Version 16 (NumPy 1.23) # NonNull attributes removed from numpy_api.py # Version 16 (NumPy 1.24) No change. -# Version 16 (NumPy 1.25) No change. 0x00000010 = 04a7bf1e65350926a0e528798da263c0 + +# Version 17 (NumPy 1.25) No actual change. +0x00000011 = ca1aebdad799358149567d9d93cbca09 diff --git a/numpy/core/code_generators/genapi.py b/numpy/core/code_generators/genapi.py index f23b5a564..c5a16d2b8 100644 --- a/numpy/core/code_generators/genapi.py +++ b/numpy/core/code_generators/genapi.py @@ -12,6 +12,7 @@ import os import re import sys import importlib.util +import textwrap from os.path import join @@ -98,6 +99,33 @@ def _repl(str): return str.replace('Bool', 'npy_bool') +class MinVersion: + def __init__(self, version): + """ Version should be the normal NumPy version, e.g. "1.25" """ + major, minor = version.split(".") + self.version = f"NPY_{major}_{minor}_API_VERSION" + + def __str__(self): + # Used by version hashing: + return self.version + + def add_guard(self, name, normal_define): + """Wrap a definition behind a version guard""" + numpy_target_help_pointer = ( + "(Old_NumPy_target see also: " + "https://numpy.org/devdocs/dev/depending_on_numpy.html)") + + wrap = textwrap.dedent(f""" + #if NPY_FEATURE_VERSION < {self.version} + #define {name} {numpy_target_help_pointer} + #else + {{define}} + #endif""") + + # we only insert `define` later to avoid confusing dedent: + return wrap.format(define=normal_define) + + class StealRef: def __init__(self, arg): self.arg = arg # counting from 1 @@ -391,7 +419,20 @@ class FunctionApi: def __init__(self, name, index, annotations, return_type, args, api_name): self.name = name self.index = index - self.annotations = annotations + + self.min_version = None + self.annotations = [] + for annotation in annotations: + # String checks, because manual import breaks isinstance + if type(annotation).__name__ == "StealRef": + self.annotations.append(annotation) + elif type(annotation).__name__ == "MinVersion": + if self.min_version is not None: + raise ValueError("Two minimum versions specified!") + self.min_version = annotation + else: + raise ValueError(f"unknown annotation {annotation}") + self.return_type = return_type self.args = args self.api_name = api_name @@ -403,13 +444,14 @@ class FunctionApi: return argstr def define_from_array_api_string(self): - define = """\ -#define %s \\\n (*(%s (*)(%s)) \\ - %s[%d])""" % (self.name, - self.return_type, - self._argtypes_string(), - self.api_name, - self.index) + arguments = self._argtypes_string() + define = textwrap.dedent(f"""\ + #define {self.name} \\ + (*({self.return_type} (*)({arguments})) \\ + {self.api_name}[{self.index}])""") + + if self.min_version is not None: + define = self.min_version.add_guard(self.name, define) return define def array_api_define(self): diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py index 6b9080403..bb3b15806 100644 --- a/numpy/core/code_generators/numpy_api.py +++ b/numpy/core/code_generators/numpy_api.py @@ -18,17 +18,17 @@ import os import importlib.util -def get_StealRef(): +def get_annotations(): # Convoluted because we can't import from numpy.distutils # (numpy is not yet built) genapi_py = os.path.join(os.path.dirname(__file__), 'genapi.py') spec = importlib.util.spec_from_file_location('conv_template', genapi_py) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) - return mod.StealRef + return mod.StealRef, mod.MinVersion -StealRef = get_StealRef() +StealRef, MinVersion = get_annotations() #from code_generators.genapi import StealRef # index, type @@ -367,8 +367,8 @@ multiarray_funcs_api = { 'PyArray_ResolveWritebackIfCopy': (302,), 'PyArray_SetWritebackIfCopyBase': (303,), # End 1.14 API - 'PyDataMem_SetHandler': (304,), - 'PyDataMem_GetHandler': (305,), + 'PyDataMem_SetHandler': (304, MinVersion("1.22")), + 'PyDataMem_GetHandler': (305, MinVersion("1.22")), # End 1.22 API } @@ -422,7 +422,7 @@ ufunc_funcs_api = { # End 1.7 API 'PyUFunc_RegisterLoopForDescr': (41,), # End 1.8 API - 'PyUFunc_FromFuncAndDataAndSignatureAndIdentity': (42,), + 'PyUFunc_FromFuncAndDataAndSignatureAndIdentity': (42, MinVersion("1.16")), # End 1.16 API } diff --git a/numpy/core/include/numpy/arrayscalars.h b/numpy/core/include/numpy/arrayscalars.h index a20a68016..258bf95b6 100644 --- a/numpy/core/include/numpy/arrayscalars.h +++ b/numpy/core/include/numpy/arrayscalars.h @@ -139,7 +139,9 @@ typedef struct { /* note that the PyObject_HEAD macro lives right here */ PyUnicodeObject base; Py_UCS4 *obval; + #if NPY_FEATURE_VERSION >= NPY_1_20_API_VERSION char *buffer_fmt; + #endif } PyUnicodeScalarObject; @@ -149,7 +151,9 @@ typedef struct { PyArray_Descr *descr; int flags; PyObject *base; + #if NPY_FEATURE_VERSION >= NPY_1_20_API_VERSION void *_buffer_info; /* private buffer info, tagged to allow warning */ + #endif } PyVoidScalarObject; /* Macros diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h index f771d62cd..742ba5261 100644 --- a/numpy/core/include/numpy/ndarraytypes.h +++ b/numpy/core/include/numpy/ndarraytypes.h @@ -712,11 +712,15 @@ typedef struct tagPyArrayObject_fields { int flags; /* For weak references */ PyObject *weakreflist; +#if NPY_FEATURE_VERSION >= NPY_1_20_API_VERSION void *_buffer_info; /* private buffer info, tagged to allow warning */ +#endif /* * For malloc/calloc/realloc/free per object */ +#if NPY_FEATURE_VERSION >= NPY_1_22_API_VERSION PyObject *mem_handler; +#endif } PyArrayObject_fields; /* @@ -1654,11 +1658,13 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags) ((PyArrayObject_fields *)arr)->flags &= ~flags; } -static inline NPY_RETURNS_BORROWED_REF PyObject * -PyArray_HANDLER(PyArrayObject *arr) -{ - return ((PyArrayObject_fields *)arr)->mem_handler; -} +#if NPY_FEATURE_VERSION >= NPY_1_22_API_VERSION + static inline NPY_RETURNS_BORROWED_REF PyObject * + PyArray_HANDLER(PyArrayObject *arr) + { + return ((PyArrayObject_fields *)arr)->mem_handler; + } +#endif #define PyTypeNum_ISBOOL(type) ((type) == NPY_BOOL) diff --git a/numpy/core/include/numpy/ufuncobject.h b/numpy/core/include/numpy/ufuncobject.h index bb0633100..9e00f2e56 100644 --- a/numpy/core/include/numpy/ufuncobject.h +++ b/numpy/core/include/numpy/ufuncobject.h @@ -197,7 +197,7 @@ typedef struct _tagPyUFuncObject { npy_uint32 iter_flags; /* New in NPY_API_VERSION 0x0000000D and above */ - + #if NPY_FEATURE_VERSION >= NPY_1_16_API_VERSION /* * for each core_num_dim_ix distinct dimension names, * the possible "frozen" size (-1 if not frozen). @@ -211,13 +211,15 @@ typedef struct _tagPyUFuncObject { /* Identity for reduction, when identity == PyUFunc_IdentityValue */ PyObject *identity_value; + #endif /* NPY_FEATURE_VERSION >= NPY_1_16_API_VERSION */ /* New in NPY_API_VERSION 0x0000000F and above */ - + #if NPY_FEATURE_VERSION >= NPY_1_22_API_VERSION /* New private fields related to dispatching */ void *_dispatch_cache; /* A PyListObject of `(tuple of DTypes, ArrayMethod/Promoter)` */ PyObject *_loops; + #endif } PyUFuncObject; #include "arrayobject.h" diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py index af8eeecde..cec934973 100644 --- a/numpy/core/setup_common.py +++ b/numpy/core/setup_common.py @@ -48,7 +48,8 @@ C_ABI_VERSION = 0x01000009 # 0x0000000f - 1.22.x # 0x00000010 - 1.23.x # 0x00000010 - 1.24.x -C_API_VERSION = 0x00000010 +# 0x00000011 - 1.25.x +C_API_VERSION = 0x00000011 # When compiling against NumPy (downstream libraries), NumPy will by default # pick an older feature version. For example, for 1.25.x we default to the @@ -96,6 +97,10 @@ def check_api_version(apiversion, codegen_dir): f"checksum in core/codegen_dir/cversions.txt is {api_hash}. If " "functions were added in the C API, you have to update " f"C_API_VERSION in {__file__}." + "\n" + "Please make sure that new additions are guarded with " + "MinVersion() to make them unavailable when wishing support " + "for older NumPy versions." ) raise MismatchCAPIError(msg) |
