From e493242b3b2933c25634572432e2c584070ffda1 Mon Sep 17 00:00:00 2001 From: sun1638650145 <1638650145@qq.com> Date: Sat, 4 Dec 2021 11:47:14 +0800 Subject: [PATCH 01/16] Add NumPy Scalar. --- include/pybind11/numpy.h | 186 ++++++++++++++++++++++++++++++--------- 1 file changed, 143 insertions(+), 43 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index b43a771684..2254a0c0dd 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -37,6 +37,8 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) class array; // Forward declaration +template struct numpy_scalar; // Forward declaration + PYBIND11_NAMESPACE_BEGIN(detail) template <> struct handle_type_name { static constexpr auto name = _("numpy.ndarray"); }; @@ -110,16 +112,12 @@ inline numpy_internals& get_numpy_internals() { return *ptr; } -template struct same_size { - template using as = bool_constant; -}; - -template constexpr int platform_lookup() { return -1; } +template constexpr int platform_lookup() { return -1; } // Lookup a type according to its size, and return a value corresponding to the NumPy typenum. -template +template constexpr int platform_lookup(int I, Ints... Is) { - return sizeof(Concrete) == sizeof(T) ? I : platform_lookup(Is...); + return sizeof(size) == sizeof(T) ? I : platform_lookup(Is...); } struct npy_api { @@ -149,14 +147,23 @@ struct npy_api { // `npy_common.h` defines the integer aliases. In order, it checks: // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR // and assigns the alias to the first matching size, so we should check in this order. - NPY_INT32_ = platform_lookup( + NPY_INT32_ = platform_lookup<4, long, int, short>( NPY_LONG_, NPY_INT_, NPY_SHORT_), - NPY_UINT32_ = platform_lookup( + NPY_UINT32_ = platform_lookup<4, unsigned long, unsigned int, unsigned short>( NPY_ULONG_, NPY_UINT_, NPY_USHORT_), - NPY_INT64_ = platform_lookup( + NPY_INT64_ = platform_lookup<8, long, long long, int>( NPY_LONG_, NPY_LONGLONG_, NPY_INT_), - NPY_UINT64_ = platform_lookup( + NPY_UINT64_ = platform_lookup<8, unsigned long, unsigned long long, unsigned int>( NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_), + NPY_FLOAT32_ = platform_lookup<4, double, float, long double>( + NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), + NPY_FLOAT64_ = platform_lookup<8, double, float, long double>( + NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), + NPY_COMPLEX64_ = platform_lookup<8, double, float, long double>( + NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), + NPY_COMPLEX128_ = platform_lookup<8, double, float, long double>( + NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), + NPY_CHAR_ = std::is_signed::value ? NPY_BYTE_ : NPY_UBYTE_, }; struct PyArray_Dims { @@ -178,6 +185,7 @@ struct npy_api { unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); PyObject *(*PyArray_DescrFromType_)(int); + PyObject *(*PyArray_TypeObjectFromType_)(int); PyObject *(*PyArray_NewFromDescr_) (PyTypeObject *, PyObject *, int, Py_intptr_t const *, Py_intptr_t const *, void *, int, PyObject *); @@ -189,6 +197,8 @@ struct npy_api { PyTypeObject *PyVoidArrType_Type_; PyTypeObject *PyArrayDescr_Type_; PyObject *(*PyArray_DescrFromScalar_)(PyObject *); + PyObject *(*PyArray_Scalar_)(void *, PyObject *, PyObject *); + void (*PyArray_ScalarAsCtype_)(PyObject *, void *); PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); int (*PyArray_DescrConverter_) (PyObject *, PyObject **); bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); @@ -208,7 +218,10 @@ struct npy_api { API_PyArrayDescr_Type = 3, API_PyVoidArrType_Type = 39, API_PyArray_DescrFromType = 45, + API_PyArray_TypeObjectFromType = 46, API_PyArray_DescrFromScalar = 57, + API_PyArray_Scalar = 60, + API_PyArray_ScalarAsCtype = 62, API_PyArray_FromAny = 69, API_PyArray_Resize = 80, API_PyArray_CopyInto = 82, @@ -241,7 +254,10 @@ struct npy_api { DECL_NPY_API(PyVoidArrType_Type); DECL_NPY_API(PyArrayDescr_Type); DECL_NPY_API(PyArray_DescrFromType); + DECL_NPY_API(PyArray_TypeObjectFromType); DECL_NPY_API(PyArray_DescrFromScalar); + DECL_NPY_API(PyArray_Scalar); + DECL_NPY_API(PyArray_ScalarAsCtype); DECL_NPY_API(PyArray_FromAny); DECL_NPY_API(PyArray_Resize); DECL_NPY_API(PyArray_CopyInto); @@ -261,6 +277,74 @@ struct npy_api { } }; +template struct is_complex : std::false_type { }; +template struct is_complex> : std::true_type { }; + +template +struct npy_format_descriptor_name; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value>( + _("bool"), _::value>("int", "uint") + _() + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value || std::is_same::value>( + _("float") + _(), _("longdouble") + ); +}; + +template +struct npy_format_descriptor_name::value>> { +static constexpr auto name = _::value + || std::is_same::value>( + _("complex") + _(), _("longcomplex") +); +}; + +template struct numpy_scalar_info {}; + +#define DECL_NPY_SCALAR(ctype_, typenum_) \ +template<> struct numpy_scalar_info { \ + static constexpr auto name = npy_format_descriptor_name::name; \ + static constexpr int typenum = npy_api::typenum_##_; \ +} + +// boolean type +DECL_NPY_SCALAR(bool, NPY_BOOL); + +// character types +DECL_NPY_SCALAR(char, NPY_CHAR); +DECL_NPY_SCALAR(signed char, NPY_BYTE); +DECL_NPY_SCALAR(unsigned char, NPY_UBYTE); + +// signed integer types +DECL_NPY_SCALAR(short, NPY_SHORT); +DECL_NPY_SCALAR(int, NPY_INT); +DECL_NPY_SCALAR(long, NPY_LONG); +DECL_NPY_SCALAR(long long, NPY_LONGLONG); + +// unsigned integer types +DECL_NPY_SCALAR(unsigned short, NPY_USHORT); +DECL_NPY_SCALAR(unsigned int, NPY_UINT); +DECL_NPY_SCALAR(unsigned long, NPY_ULONG); +DECL_NPY_SCALAR(unsigned long long, NPY_ULONGLONG); + +// floating point types +DECL_NPY_SCALAR(float, NPY_FLOAT); +DECL_NPY_SCALAR(double, NPY_DOUBLE); +DECL_NPY_SCALAR(long double, NPY_LONGDOUBLE); + +// complex types +DECL_NPY_SCALAR(std::complex, NPY_CFLOAT); +DECL_NPY_SCALAR(std::complex, NPY_CDOUBLE); +DECL_NPY_SCALAR(std::complex, NPY_CLONGDOUBLE); + +#undef DECL_NPY_SCALAR + inline PyArray_Proxy* array_proxy(void* ptr) { return reinterpret_cast(ptr); } @@ -283,8 +367,6 @@ inline bool check_flags(const void* ptr, int flag) { template struct is_std_array : std::false_type { }; template struct is_std_array> : std::true_type { }; -template struct is_complex : std::false_type { }; -template struct is_complex> : std::true_type { }; template struct array_info_scalar { using type = T; @@ -459,8 +541,56 @@ struct type_caster> { template struct type_caster> : type_caster> {}; +template +struct type_caster> { + using value_type = T; + using type_info = numpy_scalar_info; + + PYBIND11_TYPE_CASTER(numpy_scalar, type_info::name); + + static handle& target_type() { + static handle tp = npy_api::get().PyArray_TypeObjectFromType_(type_info::typenum); + return tp; + } + + static handle& target_dtype() { + static handle tp = npy_api::get().PyArray_DescrFromType_(type_info::typenum); + return tp; + } + + bool load(handle src, bool) { + if (isinstance(src, target_type())) { + npy_api::get().PyArray_ScalarAsCtype_(src.ptr(), &value.value); + return true; + } + return false; + } + + static handle cast(numpy_scalar src, return_value_policy, handle) { + return npy_api::get().PyArray_Scalar_(&src.value, target_dtype().ptr(), nullptr); + } +}; + PYBIND11_NAMESPACE_END(detail) +template +struct numpy_scalar { + using value_type = T; + + value_type value; + + numpy_scalar() = default; + numpy_scalar(value_type value) : value(value) {} + + operator value_type() { return value; } + numpy_scalar& operator=(value_type value) { this->value = value; return *this; } +}; + +template +numpy_scalar make_scalar(T value) { + return numpy_scalar(value); +} + class dtype : public object { public: PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); @@ -1051,36 +1181,6 @@ struct compare_buffer_info::valu } }; -template -struct npy_format_descriptor_name; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value>( - _("bool"), _::value>("numpy.int", "numpy.uint") + _() - ); -}; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value - || std::is_same::value - || std::is_same::value - || std::is_same::value>( - _("numpy.float") + _(), _("numpy.longdouble") - ); -}; - -template -struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value - || std::is_same::value - || std::is_same::value - || std::is_same::value>( - _("numpy.complex") + _(), _("numpy.longcomplex") - ); -}; - template struct npy_format_descriptor::value>> : npy_format_descriptor_name { From 9d846f6e27be38ec26bf2a6e6f9df76d1f526817 Mon Sep 17 00:00:00 2001 From: sun1638650145 <1638650145@qq.com> Date: Tue, 7 Dec 2021 11:22:45 +0800 Subject: [PATCH 02/16] Add doc of py::numpy_scalar --- docs/advanced/pycpp/numpy.rst | 40 +++++++++++++++++++++++++++++++++++ include/pybind11/numpy.h | 8 +++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/docs/advanced/pycpp/numpy.rst b/docs/advanced/pycpp/numpy.rst index 30daeefff9..bccbc5f849 100644 --- a/docs/advanced/pycpp/numpy.rst +++ b/docs/advanced/pycpp/numpy.rst @@ -232,6 +232,46 @@ prevent many types of unsupported structures, it is still the user's responsibility to use only "plain" structures that can be safely manipulated as raw memory without violating invariants. +Scalar types +============ + +In some cases we may want to accept or return NumPy scalar values such as +``np.float32`` or ``np.float64``. We hope to be able to handle single-precision +and double-precision on the C-side. However, both are bound to Python's +double-precision builtin float by default, so they cannot be processed separately. +We used the ``py::buffer`` trick to implement the previous approach, which +will cause the readability of the code to drop significantly. + +Luckily, there's a helper type for this occasion - ``py::numpy_scalar``: + +.. code-block:: cpp + + m.def("add", [](py::numpy_scalar a, py::numpy_scalar b) { + return py::make_scalar(a + b); + }); + m.def("add", [](py::numpy_scalar a, py::numpy_scalar b) { + return py::make_scalar(a + b); + }); + +This type is trivially convertible to and from the type it wraps; currently +supported scalar types are NumPy arithmetic types: ``bool_``, ``int8``, +``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``, +``uint64``, ``float32``, ``float64``, ``complex64``, ``complex128``, all of +them mapping to respective C++ counterparts. + +.. note:: + + This is a strict type, it will only allow input arguments of the specified + NumPy type and nothing else (e.g., ``py::numpy_scalar`` will not + accept built-in ``int`` or any other type for that matter). + +.. note:: + + Native C types are mapped to NumPy types in a platform specific way: for + instance, ``char`` may be mapped to either ``np.int8`` or ``np.uint8`` + depending on the platform. If you want to ensure specific NumPy types, + it is recommended to use fixed-width aliases from ````. + Vectorizing functions ===================== diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 2254a0c0dd..cfc977a948 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -9,8 +9,8 @@ #pragma once -#include "pybind11.h" -#include "complex.h" +#include "pybind11/pybind11.h" +#include "pybind11/complex.h" #include #include #include @@ -159,9 +159,9 @@ struct npy_api { NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), NPY_FLOAT64_ = platform_lookup<8, double, float, long double>( NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), - NPY_COMPLEX64_ = platform_lookup<8, double, float, long double>( + NPY_COMPLEX64_ = platform_lookup<8, std::complex, std::complex, std::complex>( NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), - NPY_COMPLEX128_ = platform_lookup<8, double, float, long double>( + NPY_COMPLEX128_ = platform_lookup<8, std::complex, std::complex, std::complex>( NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), NPY_CHAR_ = std::is_signed::value ? NPY_BYTE_ : NPY_UBYTE_, }; From 043adbc7247497edd5d91e54320afd854007144c Mon Sep 17 00:00:00 2001 From: sun1638650145 <1638650145@qq.com> Date: Tue, 7 Dec 2021 11:24:09 +0800 Subject: [PATCH 03/16] Fix path error --- include/pybind11/numpy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index cfc977a948..11237a95f0 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -9,8 +9,8 @@ #pragma once -#include "pybind11/pybind11.h" -#include "pybind11/complex.h" +#include "pybind11.h" +#include "complex.h" #include #include #include From 74b027e5862dd2488f56ccba0d3268f7e11744f3 Mon Sep 17 00:00:00 2001 From: sun1638650145 <1638650145@qq.com> Date: Thu, 9 Dec 2021 17:54:07 +0800 Subject: [PATCH 04/16] Strictly defined integer. --- docs/advanced/pycpp/numpy.rst | 10 +++++----- include/pybind11/numpy.h | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/advanced/pycpp/numpy.rst b/docs/advanced/pycpp/numpy.rst index bccbc5f849..e49cafcfec 100644 --- a/docs/advanced/pycpp/numpy.rst +++ b/docs/advanced/pycpp/numpy.rst @@ -261,16 +261,16 @@ them mapping to respective C++ counterparts. .. note:: - This is a strict type, it will only allow input arguments of the specified - NumPy type and nothing else (e.g., ``py::numpy_scalar`` will not - accept built-in ``int`` or any other type for that matter). + This is a strict type, it will only allows to specify NumPy type as input + arguments, and does not allow other types of input parameters (e.g., + ``py::numpy_scalar`` will not accept Python's builtin ``int`` ). .. note:: Native C types are mapped to NumPy types in a platform specific way: for instance, ``char`` may be mapped to either ``np.int8`` or ``np.uint8`` - depending on the platform. If you want to ensure specific NumPy types, - it is recommended to use fixed-width aliases from ````. + and ``long`` may use 4 or 8 bytes depending on the platform. Unless you + clearly understand the difference and your needs, please use ````. Vectorizing functions ===================== diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 11237a95f0..6240df2b26 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -322,16 +322,16 @@ DECL_NPY_SCALAR(signed char, NPY_BYTE); DECL_NPY_SCALAR(unsigned char, NPY_UBYTE); // signed integer types -DECL_NPY_SCALAR(short, NPY_SHORT); -DECL_NPY_SCALAR(int, NPY_INT); +DECL_NPY_SCALAR(std::int16_t, NPY_SHORT); +DECL_NPY_SCALAR(std::int32_t, NPY_INT); +DECL_NPY_SCALAR(std::int64_t, NPY_LONG); DECL_NPY_SCALAR(long, NPY_LONG); -DECL_NPY_SCALAR(long long, NPY_LONGLONG); // unsigned integer types -DECL_NPY_SCALAR(unsigned short, NPY_USHORT); -DECL_NPY_SCALAR(unsigned int, NPY_UINT); +DECL_NPY_SCALAR(std::uint16_t, NPY_USHORT); +DECL_NPY_SCALAR(std::uint32_t, NPY_UINT); +DECL_NPY_SCALAR(std::uint64_t, NPY_ULONG); DECL_NPY_SCALAR(unsigned long, NPY_ULONG); -DECL_NPY_SCALAR(unsigned long long, NPY_ULONGLONG); // floating point types DECL_NPY_SCALAR(float, NPY_FLOAT); From 00a85a6969a8ff028f7e12fdb623bef7fff8ec80 Mon Sep 17 00:00:00 2001 From: sun1638650145 <1638650145@qq.com> Date: Fri, 10 Dec 2021 15:58:52 +0800 Subject: [PATCH 05/16] Add Add py::numpy_scalar test. --- tests/CMakeLists.txt | 1 + tests/test_numpy_scalars.cpp | 49 +++++++++++++++++++++++++++++ tests/test_numpy_scalars.py | 61 ++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 tests/test_numpy_scalars.cpp create mode 100644 tests/test_numpy_scalars.py diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8b19b19670..8b6d0a9ac0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -120,6 +120,7 @@ set(PYBIND11_TEST_FILES test_multiple_inheritance.cpp test_numpy_array.cpp test_numpy_dtypes.cpp + test_numpy_scalars.cpp test_numpy_vectorize.cpp test_opaque_types.cpp test_operator_overloading.cpp diff --git a/tests/test_numpy_scalars.cpp b/tests/test_numpy_scalars.cpp new file mode 100644 index 0000000000..c8f8fcafe5 --- /dev/null +++ b/tests/test_numpy_scalars.cpp @@ -0,0 +1,49 @@ +/* + tests/test_numpy_scalars.cpp -- strict NumPy scalars + + Copyright (c) 2021 Steve R. Sun + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include +#include + +#include "pybind11_tests.h" +#include + +namespace py = pybind11; + +template +struct add { + T x; + add(T x) : x(x) {} + T operator()(T y) const { return static_cast(x + y); } +}; + +template +void register_test(py::module& m, const char *name, F&& func) { + m.def((std::string("test_") + name).c_str(), [=](py::numpy_scalar v) { + return std::make_tuple(name, py::make_scalar(static_cast(func(v.value)))); + }, py::arg("x")); +} + +TEST_SUBMODULE(numpy_scalars, m) { + using cfloat = std::complex; + using cdouble = std::complex; + + register_test(m, "bool", [](bool x) { return !x; }); + register_test(m, "int8", add(-8)); + register_test(m, "int16", add(-16)); + register_test(m, "int32", add(-32)); + register_test(m, "int64", add(-64)); + register_test(m, "uint8", add(8)); + register_test(m, "uint16", add(16)); + register_test(m, "uint32", add(32)); + register_test(m, "uint64", add(64)); + register_test(m, "float32", add(0.125f)); + register_test(m, "float64", add(0.25f)); + register_test(m, "complex64", add({0, -0.125f})); + register_test(m, "complex128", add({0, -0.25f})); +} diff --git a/tests/test_numpy_scalars.py b/tests/test_numpy_scalars.py new file mode 100644 index 0000000000..a63093dadf --- /dev/null +++ b/tests/test_numpy_scalars.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +import sys + +import pytest + +from pybind11_tests import numpy_scalars as m + +np = pytest.importorskip("numpy") + +SCALAR_TYPES = dict([ + (np.bool_, False), + (np.int8, -7), + (np.int16, -15), + (np.int32, -31), + (np.int64, -63), + (np.uint8, 9), + (np.uint16, 17), + (np.uint32, 33), + (np.uint64, 65), + (np.single, 1.125), + (np.double, 1.25), + (np.complex64, 1 - 0.125j), + (np.complex128, 1 - 0.25j), +]) +ALL_TYPES = [int, bool, float, bytes, str] + list(SCALAR_TYPES) + + +def type_name(tp): + try: + return tp.__name__.rstrip('_') + except BaseException: + # no numpy + return str(tp) + + +@pytest.fixture(scope='module', params=list(SCALAR_TYPES), ids=type_name) +def scalar_type(request): + return request.param + + +def expected_signature(tp): + s = 'str' if sys.version_info[0] >= 3 else 'unicode' + t = type_name(tp) + return 'test_{t}(x: {t}) -> Tuple[{s}, {t}]\n'.format(s=s, t=t) + + +def test_numpy_scalars(scalar_type): + expected = SCALAR_TYPES[scalar_type] + name = type_name(scalar_type) + func = getattr(m, 'test_' + name) + assert func.__doc__ == expected_signature(scalar_type) + for tp in ALL_TYPES: + value = tp(1) + if tp is scalar_type: + result = func(value) + assert result[0] == name + assert isinstance(result[1], tp) + assert result[1] == tp(expected) + else: + with pytest.raises(TypeError): + func(value) From 4be007670c24476d403ba1cb0988ab2b7f6891f8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 10 Dec 2021 08:16:36 +0000 Subject: [PATCH 06/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_numpy_scalars.py | 42 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/tests/test_numpy_scalars.py b/tests/test_numpy_scalars.py index a63093dadf..1a28f22f58 100644 --- a/tests/test_numpy_scalars.py +++ b/tests/test_numpy_scalars.py @@ -7,47 +7,49 @@ np = pytest.importorskip("numpy") -SCALAR_TYPES = dict([ - (np.bool_, False), - (np.int8, -7), - (np.int16, -15), - (np.int32, -31), - (np.int64, -63), - (np.uint8, 9), - (np.uint16, 17), - (np.uint32, 33), - (np.uint64, 65), - (np.single, 1.125), - (np.double, 1.25), - (np.complex64, 1 - 0.125j), - (np.complex128, 1 - 0.25j), -]) +SCALAR_TYPES = dict( + [ + (np.bool_, False), + (np.int8, -7), + (np.int16, -15), + (np.int32, -31), + (np.int64, -63), + (np.uint8, 9), + (np.uint16, 17), + (np.uint32, 33), + (np.uint64, 65), + (np.single, 1.125), + (np.double, 1.25), + (np.complex64, 1 - 0.125j), + (np.complex128, 1 - 0.25j), + ] +) ALL_TYPES = [int, bool, float, bytes, str] + list(SCALAR_TYPES) def type_name(tp): try: - return tp.__name__.rstrip('_') + return tp.__name__.rstrip("_") except BaseException: # no numpy return str(tp) -@pytest.fixture(scope='module', params=list(SCALAR_TYPES), ids=type_name) +@pytest.fixture(scope="module", params=list(SCALAR_TYPES), ids=type_name) def scalar_type(request): return request.param def expected_signature(tp): - s = 'str' if sys.version_info[0] >= 3 else 'unicode' + s = "str" if sys.version_info[0] >= 3 else "unicode" t = type_name(tp) - return 'test_{t}(x: {t}) -> Tuple[{s}, {t}]\n'.format(s=s, t=t) + return "test_{t}(x: {t}) -> Tuple[{s}, {t}]\n".format(s=s, t=t) def test_numpy_scalars(scalar_type): expected = SCALAR_TYPES[scalar_type] name = type_name(scalar_type) - func = getattr(m, 'test_' + name) + func = getattr(m, "test_" + name) assert func.__doc__ == expected_signature(scalar_type) for tp in ALL_TYPES: value = tp(1) From f5b4000b4f5f420ada0909fdf6cf27926d703cf0 Mon Sep 17 00:00:00 2001 From: "Steve R. Sun" <1638650145@qq.com> Date: Sat, 25 Dec 2021 13:20:05 +0800 Subject: [PATCH 07/16] Replace _ with const_name --- include/pybind11/numpy.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 016a84b019..ea7ecfb8dc 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -285,23 +285,23 @@ struct npy_format_descriptor_name; template struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value>( - _("bool"), _::value>("int", "uint") + _() + static constexpr auto name = const_name::value>( + const_name("bool"), const_name::value>("int", "uint") + const_name() ); }; template struct npy_format_descriptor_name::value>> { - static constexpr auto name = _::value || std::is_same::value>( - _("float") + _(), _("longdouble") + static constexpr auto name = const_name::value || std::is_same::value>( + const_name("float") + const_name(), const_name("longdouble") ); }; template struct npy_format_descriptor_name::value>> { -static constexpr auto name = _::value +static constexpr auto name = const_name::value || std::is_same::value>( - _("complex") + _(), _("longcomplex") + const_name("complex") + const_name(), const_name("longcomplex") ); }; From 732a831828291b7f060767fc0a51063434cbf360 Mon Sep 17 00:00:00 2001 From: "Steve R. Sun" <1638650145@qq.com> Date: Mon, 27 Dec 2021 18:15:31 +0800 Subject: [PATCH 08/16] Bugfix. --- include/pybind11/numpy.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index ea7ecfb8dc..cd052c6602 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -325,13 +325,21 @@ DECL_NPY_SCALAR(unsigned char, NPY_UBYTE); DECL_NPY_SCALAR(std::int16_t, NPY_SHORT); DECL_NPY_SCALAR(std::int32_t, NPY_INT); DECL_NPY_SCALAR(std::int64_t, NPY_LONG); +#if defined(__linux__) +DECL_NPY_SCALAR(long long, NPY_LONG); +#else DECL_NPY_SCALAR(long, NPY_LONG); +#endif // unsigned integer types DECL_NPY_SCALAR(std::uint16_t, NPY_USHORT); DECL_NPY_SCALAR(std::uint32_t, NPY_UINT); DECL_NPY_SCALAR(std::uint64_t, NPY_ULONG); +#if defined(__linux__) +DECL_NPY_SCALAR(unsigned long long, NPY_ULONG); +#else DECL_NPY_SCALAR(unsigned long, NPY_ULONG); +#endif // floating point types DECL_NPY_SCALAR(float, NPY_FLOAT); From dc782ba969afb28fa56412b3de8f1c9366c4af71 Mon Sep 17 00:00:00 2001 From: "Steve R. Sun" <1638650145@qq.com> Date: Thu, 30 Dec 2021 12:18:51 +0800 Subject: [PATCH 09/16] Reformat of numpy.h --- include/pybind11/numpy.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index cd052c6602..29adc28e0f 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -299,10 +299,10 @@ struct npy_format_descriptor_name::valu template struct npy_format_descriptor_name::value>> { -static constexpr auto name = const_name::value - || std::is_same::value>( + static constexpr auto name = const_name::value + || std::is_same::value>( const_name("complex") + const_name(), const_name("longcomplex") -); + ); }; template struct numpy_scalar_info {}; From 945edbb8325ac58b0d772174511827323e139567 Mon Sep 17 00:00:00 2001 From: "Steve R. Sun" <1638650145@qq.com> Date: Thu, 13 Jan 2022 11:34:03 +0800 Subject: [PATCH 10/16] Update CMakeLists.txt --- tests/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9040cf8c06..d6e2171b99 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -142,6 +142,7 @@ set(PYBIND11_TEST_FILES test_multiple_inheritance test_numpy_array test_numpy_dtypes + test_numpy_scalars test_numpy_vectorize test_opaque_types test_operator_overloading From 2f8dbb3b061d6b5b5969a9531eab13fe2a23a08b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 23:53:22 +0000 Subject: [PATCH 11/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- include/pybind11/numpy.h | 90 +++++++++++++++++++----------------- tests/test_numpy_scalars.cpp | 21 +++++---- tests/test_numpy_scalars.py | 1 - 3 files changed, 60 insertions(+), 52 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index c85aebbc99..67b37fe9c8 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -38,7 +38,8 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) class array; // Forward declaration -template struct numpy_scalar; // Forward declaration +template +struct numpy_scalar; // Forward declaration PYBIND11_NAMESPACE_BEGIN(detail) @@ -120,18 +121,17 @@ inline numpy_internals &get_numpy_internals() { return *ptr; } - template struct same_size { template using as = bool_constant; }; -template constexpr int platform_lookup() { - return -1; +template +constexpr int platform_lookup() { + return -1; } - // Lookup a type according to its size, and return a value corresponding to the NumPy typenum. template constexpr int platform_lookup(int I, Ints... Is) { @@ -176,22 +176,22 @@ struct npy_api { // `npy_common.h` defines the integer aliases. In order, it checks: // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR // and assigns the alias to the first matching size, so we should check in this order. - NPY_INT32_ = platform_lookup<4, long, int, short>( - NPY_LONG_, NPY_INT_, NPY_SHORT_), + NPY_INT32_ = platform_lookup<4, long, int, short>(NPY_LONG_, NPY_INT_, NPY_SHORT_), NPY_UINT32_ = platform_lookup<4, unsigned long, unsigned int, unsigned short>( NPY_ULONG_, NPY_UINT_, NPY_USHORT_), - NPY_INT64_ = platform_lookup<8, long, long long, int>( - NPY_LONG_, NPY_LONGLONG_, NPY_INT_), + NPY_INT64_ = platform_lookup<8, long, long long, int>(NPY_LONG_, NPY_LONGLONG_, NPY_INT_), NPY_UINT64_ = platform_lookup<8, unsigned long, unsigned long long, unsigned int>( NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_), - NPY_FLOAT32_ = platform_lookup<4, double, float, long double>( - NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), - NPY_FLOAT64_ = platform_lookup<8, double, float, long double>( - NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), - NPY_COMPLEX64_ = platform_lookup<8, std::complex, std::complex, std::complex>( - NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), - NPY_COMPLEX128_ = platform_lookup<8, std::complex, std::complex, std::complex>( - NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), + NPY_FLOAT32_ + = platform_lookup<4, double, float, long double>(NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), + NPY_FLOAT64_ + = platform_lookup<8, double, float, long double>(NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), + NPY_COMPLEX64_ + = platform_lookup<8, std::complex, std::complex, std::complex>( + NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), + NPY_COMPLEX128_ + = platform_lookup<8, std::complex, std::complex, std::complex>( + NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_), NPY_CHAR_ = std::is_signed::value ? NPY_BYTE_ : NPY_UBYTE_, }; @@ -217,7 +217,7 @@ struct npy_api { PyObject *(*PyArray_TypeObjectFromType_)(int); PyObject *(*PyArray_NewFromDescr_)(PyTypeObject *, PyObject *, - int, + int, Py_intptr_t const *, Py_intptr_t const *, void *, @@ -325,32 +325,36 @@ struct npy_format_descriptor_name; template struct npy_format_descriptor_name::value>> { static constexpr auto name = const_name::value>( - const_name("bool"), const_name::value>("int", "uint") + const_name() - ); + const_name("bool"), + const_name::value>("int", "uint") + const_name()); }; template struct npy_format_descriptor_name::value>> { - static constexpr auto name = const_name::value || std::is_same::value>( - const_name("float") + const_name(), const_name("longdouble") - ); + static constexpr auto name + = const_name < std::is_same::value + || std::is_same::value + > (const_name("float") + const_name(), const_name("longdouble")); }; template struct npy_format_descriptor_name::value>> { - static constexpr auto name = const_name::value - || std::is_same::value>( - const_name("complex") + const_name(), const_name("longcomplex") - ); + static constexpr auto name + = const_name < std::is_same::value + || std::is_same::value + > (const_name("complex") + const_name(), + const_name("longcomplex")); }; -template struct numpy_scalar_info {}; +template +struct numpy_scalar_info {}; -#define DECL_NPY_SCALAR(ctype_, typenum_) \ -template<> struct numpy_scalar_info { \ - static constexpr auto name = npy_format_descriptor_name::name; \ - static constexpr int typenum = npy_api::typenum_##_; \ -} +#define DECL_NPY_SCALAR(ctype_, typenum_) \ + template <> \ + struct numpy_scalar_info { \ + static constexpr auto name = npy_format_descriptor_name::name; \ + static constexpr int typenum = npy_api::typenum_##_; \ + } // boolean type DECL_NPY_SCALAR(bool, NPY_BOOL); @@ -392,8 +396,7 @@ DECL_NPY_SCALAR(std::complex, NPY_CLONGDOUBLE); #undef DECL_NPY_SCALAR -inline PyArray_Proxy* array_proxy(void* ptr) { return reinterpret_cast(ptr); } - +inline PyArray_Proxy *array_proxy(void *ptr) { return reinterpret_cast(ptr); } inline const PyArray_Proxy *array_proxy(const void *ptr) { return reinterpret_cast(ptr); @@ -413,7 +416,7 @@ inline bool check_flags(const void *ptr, int flag) { template struct is_std_array : std::false_type {}; -template +template struct is_std_array> : std::true_type {}; template @@ -628,19 +631,19 @@ template struct type_caster> : type_caster> {}; -template +template struct type_caster> { using value_type = T; using type_info = numpy_scalar_info; PYBIND11_TYPE_CASTER(numpy_scalar, type_info::name); - static handle& target_type() { + static handle &target_type() { static handle tp = npy_api::get().PyArray_TypeObjectFromType_(type_info::typenum); return tp; } - static handle& target_dtype() { + static handle &target_dtype() { static handle tp = npy_api::get().PyArray_DescrFromType_(type_info::typenum); return tp; } @@ -660,7 +663,7 @@ struct type_caster> { PYBIND11_NAMESPACE_END(detail) -template +template struct numpy_scalar { using value_type = T; @@ -670,10 +673,13 @@ struct numpy_scalar { numpy_scalar(value_type value) : value(value) {} operator value_type() { return value; } - numpy_scalar& operator=(value_type value) { this->value = value; return *this; } + numpy_scalar &operator=(value_type value) { + this->value = value; + return *this; + } }; -template +template numpy_scalar make_scalar(T value) { return numpy_scalar(value); } diff --git a/tests/test_numpy_scalars.cpp b/tests/test_numpy_scalars.cpp index c8f8fcafe5..046a9c07a9 100644 --- a/tests/test_numpy_scalars.cpp +++ b/tests/test_numpy_scalars.cpp @@ -7,26 +7,29 @@ BSD-style license that can be found in the LICENSE file. */ -#include -#include +#include #include "pybind11_tests.h" -#include + +#include +#include namespace py = pybind11; -template +template struct add { T x; add(T x) : x(x) {} T operator()(T y) const { return static_cast(x + y); } }; -template -void register_test(py::module& m, const char *name, F&& func) { - m.def((std::string("test_") + name).c_str(), [=](py::numpy_scalar v) { - return std::make_tuple(name, py::make_scalar(static_cast(func(v.value)))); - }, py::arg("x")); +template +void register_test(py::module &m, const char *name, F &&func) { + m.def((std::string("test_") + name).c_str(), + [=](py::numpy_scalar v) { + return std::make_tuple(name, py::make_scalar(static_cast(func(v.value)))); + }, + py::arg("x")); } TEST_SUBMODULE(numpy_scalars, m) { diff --git a/tests/test_numpy_scalars.py b/tests/test_numpy_scalars.py index 1a28f22f58..7e013d3145 100644 --- a/tests/test_numpy_scalars.py +++ b/tests/test_numpy_scalars.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys import pytest From 8eb33fef6f3ffbae308ac17dd37a5ff875ce16f9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 23 Feb 2023 01:30:47 +0000 Subject: [PATCH 12/16] style: pre-commit fixes --- tests/test_numpy_scalars.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/tests/test_numpy_scalars.py b/tests/test_numpy_scalars.py index 7e013d3145..2256e78bcc 100644 --- a/tests/test_numpy_scalars.py +++ b/tests/test_numpy_scalars.py @@ -6,23 +6,21 @@ np = pytest.importorskip("numpy") -SCALAR_TYPES = dict( - [ - (np.bool_, False), - (np.int8, -7), - (np.int16, -15), - (np.int32, -31), - (np.int64, -63), - (np.uint8, 9), - (np.uint16, 17), - (np.uint32, 33), - (np.uint64, 65), - (np.single, 1.125), - (np.double, 1.25), - (np.complex64, 1 - 0.125j), - (np.complex128, 1 - 0.25j), - ] -) +SCALAR_TYPES = { + np.bool_: False, + np.int8: -7, + np.int16: -15, + np.int32: -31, + np.int64: -63, + np.uint8: 9, + np.uint16: 17, + np.uint32: 33, + np.uint64: 65, + np.single: 1.125, + np.double: 1.25, + np.complex64: 1 - 0.125j, + np.complex128: 1 - 0.25j, +} ALL_TYPES = [int, bool, float, bytes, str] + list(SCALAR_TYPES) From 88f4a48cd87af0d6a9e10c38deaa095e8a5d772a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 Feb 2023 00:58:45 +0000 Subject: [PATCH 13/16] style: pre-commit fixes --- tests/test_numpy_scalars.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test_numpy_scalars.py b/tests/test_numpy_scalars.py index 2256e78bcc..ccebf548e4 100644 --- a/tests/test_numpy_scalars.py +++ b/tests/test_numpy_scalars.py @@ -8,18 +8,18 @@ SCALAR_TYPES = { np.bool_: False, - np.int8: -7, - np.int16: -15, - np.int32: -31, - np.int64: -63, - np.uint8: 9, - np.uint16: 17, - np.uint32: 33, - np.uint64: 65, - np.single: 1.125, - np.double: 1.25, - np.complex64: 1 - 0.125j, - np.complex128: 1 - 0.25j, + np.int8: -7, + np.int16: -15, + np.int32: -31, + np.int64: -63, + np.uint8: 9, + np.uint16: 17, + np.uint32: 33, + np.uint64: 65, + np.single: 1.125, + np.double: 1.25, + np.complex64: 1 - 0.125j, + np.complex128: 1 - 0.25j, } ALL_TYPES = [int, bool, float, bytes, str] + list(SCALAR_TYPES) From 15ff9f48f72d5cde3176ec053c2e261dbda59260 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:54:16 +0000 Subject: [PATCH 14/16] style: pre-commit fixes --- tests/test_numpy_scalars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_numpy_scalars.py b/tests/test_numpy_scalars.py index ccebf548e4..612242ad46 100644 --- a/tests/test_numpy_scalars.py +++ b/tests/test_numpy_scalars.py @@ -40,7 +40,7 @@ def scalar_type(request): def expected_signature(tp): s = "str" if sys.version_info[0] >= 3 else "unicode" t = type_name(tp) - return "test_{t}(x: {t}) -> Tuple[{s}, {t}]\n".format(s=s, t=t) + return f"test_{t}(x: {t}) -> Tuple[{s}, {t}]\n" def test_numpy_scalars(scalar_type): From af4200ce4597df088fa8bb8b33713fa8f5d18738 Mon Sep 17 00:00:00 2001 From: sun1638650145 <1638650145@qq.com> Date: Fri, 29 Mar 2024 12:16:17 +0800 Subject: [PATCH 15/16] Bugfix. --- tests/test_numpy_scalars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_numpy_scalars.py b/tests/test_numpy_scalars.py index 612242ad46..a21cf84e92 100644 --- a/tests/test_numpy_scalars.py +++ b/tests/test_numpy_scalars.py @@ -40,7 +40,7 @@ def scalar_type(request): def expected_signature(tp): s = "str" if sys.version_info[0] >= 3 else "unicode" t = type_name(tp) - return f"test_{t}(x: {t}) -> Tuple[{s}, {t}]\n" + return f"test_{t}(x: {t}) -> tuple[{s}, {t}]\n" def test_numpy_scalars(scalar_type): From 894234b6ca6daec9e45437d67312db640861b8e1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 01:55:05 +0000 Subject: [PATCH 16/16] style: pre-commit fixes --- tests/test_numpy_scalars.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_numpy_scalars.py b/tests/test_numpy_scalars.py index a21cf84e92..52c2861a1c 100644 --- a/tests/test_numpy_scalars.py +++ b/tests/test_numpy_scalars.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys import pytest