Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: add hex version, move code to common #12074

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/release/1.16.0-notes.rst
Expand Up @@ -19,6 +19,12 @@ Highlights
New functions
=============

``get_numpy_version_as_hex``
----------------------------------------
Added `numpy.version.get_numpy_version_as_hex` which returns a hex string
similar to ``PY_VERSION_HEX`` in the CPython headers. The value is cached in C
for fast version comparison and initialized at import.


Deprecations
============
Expand Down
1 change: 1 addition & 0 deletions numpy/core/code_generators/genapi.py
Expand Up @@ -58,6 +58,7 @@
join('umath', 'ufunc_object.c'),
join('umath', 'ufunc_type_resolution.c'),
join('umath', 'reduction.c'),
join('common', 'npy_version.c'),
]
THIS_DIR = os.path.dirname(__file__)
API_FILES = [os.path.join(THIS_DIR, '..', 'src', a) for a in API_FILES]
Expand Down
10 changes: 8 additions & 2 deletions numpy/core/setup.py
Expand Up @@ -715,9 +715,13 @@ def get_mathlib_info(*args):

config.add_extension('_multiarray_tests',
sources=[join('src', 'multiarray', '_multiarray_tests.c.src'),
join('src', 'common', 'mem_overlap.c')],
join('src', 'common', 'mem_overlap.c'),
join('src', 'common', 'npy_version.c'),
],
depends=[join('src', 'common', 'mem_overlap.h'),
join('src', 'common', 'npy_extint128.h')],
join('src', 'common', 'npy_extint128.h'),
join('src', 'common', 'npy_version.h'),
],
libraries=['npymath'])

#######################################################################
Expand All @@ -733,6 +737,7 @@ def get_mathlib_info(*args):
join('src', 'common', 'npy_config.h'),
join('src', 'common', 'npy_extint128.h'),
join('src', 'common', 'npy_longdouble.h'),
join('src', 'common', 'npy_version.h'),
join('src', 'common', 'templ_common.h.src'),
join('src', 'common', 'ucsnarrow.h'),
join('src', 'common', 'ufunc_override.h'),
Expand All @@ -743,6 +748,7 @@ def get_mathlib_info(*args):
join('src', 'common', 'array_assign.c'),
join('src', 'common', 'mem_overlap.c'),
join('src', 'common', 'npy_longdouble.c'),
join('src', 'common', 'npy_version.c'),
join('src', 'common', 'templ_common.h.src'),
join('src', 'common', 'ucsnarrow.c'),
join('src', 'common', 'ufunc_override.c'),
Expand Down
80 changes: 80 additions & 0 deletions numpy/core/src/common/npy_version.c
@@ -0,0 +1,80 @@
#include <Python.h>

#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE
#include <numpy/ndarraytypes.h>

#include "npy_config.h"
#include "npy_pycompat.h"

/*NUMPY_API
*
* Included at the very first so not auto-grabbed and thus not labeled.
*/
NPY_NO_EXPORT unsigned int
PyArray_GetNDArrayCVersion(void)
{
return (unsigned int)NPY_ABI_VERSION;
}

/*NUMPY_API
* Returns the built-in (at compilation time) C API version
*/
NPY_NO_EXPORT unsigned int
PyArray_GetNDArrayCFeatureVersion(void)
{
return (unsigned int)NPY_API_VERSION;
}

/*
* Return equivalent of PY_VERSION_HEX for NumPy's version
*/
NPY_NO_EXPORT long
get_numpy_version_as_hex(void)
{
static long ver = 0;
PyObject *_version, *_func, *result, *tmp;
char *buf, *endbuf;
Py_ssize_t len;
int retval;
if (ver != 0) {
return ver;
}
_version = PyImport_ImportModule("numpy.version");
if (_version == NULL) {
return -1L;
}
_func = PyObject_GetAttrString(_version, "get_numpy_version_as_hex");
Py_DECREF(_version);
if (_func == NULL) {
return -1L;
}
result = PyObject_CallFunction(_func, NULL);
Py_DECREF(_func);
if (result == NULL) {
return -1L;
}
#if defined(NPY_PY3K)
/* FIXME: XXX -- should it use UTF-8 here? */
tmp = PyUnicode_AsUTF8String(result);
Py_DECREF(result);
if (tmp == NULL) {
return -1;
}
#else
tmp = result;
#endif
retval = PyBytes_AsStringAndSize(tmp, &buf, &len);
Py_INCREF(tmp);
if (retval < 0) {
return -1L;
}
ver = strtol(buf, &endbuf, 16);
if (buf == endbuf || ver == LONG_MAX || ver <= 0) {
PyErr_SetString(PyExc_ValueError,
"invalid hex value from version.get_numpy_version_as_hex()");
ver = 0L;
return -1L;
}
return ver;
}
7 changes: 7 additions & 0 deletions numpy/core/src/common/npy_version.h
@@ -0,0 +1,7 @@
#ifndef _NPY_VERSION_H_
#define _NPY_VERSION_H_

NPY_NO_EXPORT long
get_numpy_version_as_hex(void);

#endif
14 changes: 14 additions & 0 deletions numpy/core/src/multiarray/_multiarray_tests.c.src
Expand Up @@ -9,6 +9,7 @@
#include "common.h"
#include "mem_overlap.h"
#include "npy_extint128.h"
#include "npy_version.h"
#include "common.h"

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
Expand Down Expand Up @@ -1709,6 +1710,16 @@ get_fpu_mode(PyObject *NPY_UNUSED(self), PyObject *args)
#endif
}

static PyObject *
get_hex_version(PyObject *NPY_UNUSED(self), PyObject* NPY_UNUSED(args))
{
long ver = get_numpy_version_as_hex();
if (ver < 0) {
return NULL;
}
return PyLong_FromLong(ver);
}

/*
* npymath wrappers
*/
Expand Down Expand Up @@ -1963,6 +1974,9 @@ static PyMethodDef Multiarray_TestsMethods[] = {
{"get_fpu_mode",
get_fpu_mode,
METH_VARARGS, get_fpu_mode_doc},
{"get_hex_version",
get_hex_version,
METH_NOARGS, NULL},
/**begin repeat
* #name = cabs, carg#
*/
Expand Down
19 changes: 0 additions & 19 deletions numpy/core/src/multiarray/multiarraymodule.c
Expand Up @@ -2871,25 +2871,6 @@ array_arange(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws) {
return range;
}

/*NUMPY_API
*
* Included at the very first so not auto-grabbed and thus not labeled.
*/
NPY_NO_EXPORT unsigned int
PyArray_GetNDArrayCVersion(void)
{
return (unsigned int)NPY_ABI_VERSION;
}

/*NUMPY_API
* Returns the built-in (at compilation time) C API version
*/
NPY_NO_EXPORT unsigned int
PyArray_GetNDArrayCFeatureVersion(void)
{
return (unsigned int)NPY_API_VERSION;
}

static PyObject *
array__get_ndarray_c_version(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
{
Expand Down
41 changes: 41 additions & 0 deletions numpy/core/tests/test_api.py
Expand Up @@ -514,3 +514,44 @@ def test_broadcast_arrays():
result = np.broadcast_arrays(a, b)
assert_equal(result[0], np.array([(1, 2, 3), (1, 2, 3), (1, 2, 3)], dtype='u4,u4,u4'))
assert_equal(result[1], np.array([(1, 2, 3), (4, 5, 6), (7, 8, 9)], dtype='u4,u4,u4'))

def test_hex_version():
from numpy.core._multiarray_tests import get_hex_version
from numpy import version
_orig = version.get_numpy_version_as_hex
try:
# Test error modes
def raise_value_error():
raise ValueError('bad call')
version.get_numpy_version_as_hex = raise_value_error
assert_raises(ValueError, get_hex_version)

def return_non_bytes():
return 3.14
version.get_numpy_version_as_hex = return_non_bytes
assert_raises(TypeError, get_hex_version)

def return_non_hex():
# First letter must be non-hex
return 'hello'
version.get_numpy_version_as_hex = return_non_hex
assert_raises(ValueError, get_hex_version)

def return_negative():
return '-1234'
version.get_numpy_version_as_hex = return_negative
assert_raises(ValueError, get_hex_version)

finally:
version.get_numpy_version_as_hex = _orig

ver = get_hex_version()
target = version.get_numpy_version_as_hex()
assert_equal(ver, int(target, 16))
# Make sure first successful call caches the value
try:
version.get_numpy_version_as_hex = raise_value_error
ver = get_hex_version()
assert_equal(ver, int(target, 16))
finally:
version.get_numpy_version_as_hex = _orig
4 changes: 4 additions & 0 deletions setup.py
Expand Up @@ -139,6 +139,10 @@ def write_version_py(filename='numpy/version.py'):

if not release:
version = full_version

def get_numpy_version_as_hex():
major, minor, micro = short_version.split('.')
return hex(int(major) << 24 | int(minor) << 16 | int(micro) << 8)
"""
FULLVERSION, GIT_REVISION = get_version_info()

Expand Down