diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 062d742b81d9..df73fe1d2169 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -261,7 +261,7 @@ jobs: # Preinstall build requirements to enable no-build-isolation builds. python -m pip install --upgrade $PRE \ 'contourpy>=1.0.1' cycler fonttools kiwisolver importlib_resources \ - numpy packaging pillow 'pyparsing!=3.1.0' python-dateutil setuptools-scm \ + packaging pillow 'pyparsing!=3.1.0' python-dateutil setuptools-scm \ 'meson-python>=0.13.1' 'pybind11>=2.6' \ -r requirements/testing/all.txt \ ${{ matrix.extra-requirements }} diff --git a/pyproject.toml b/pyproject.toml index 5b5c60d60d54..cd0a5c039758 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,6 @@ requires-python = ">=3.10" # Should be a copy of the build dependencies below. dev = [ "meson-python>=0.13.1", - "numpy>=1.25", "pybind11>=2.6,!=2.13.3", "setuptools_scm>=7", # Not required by us but setuptools_scm without a version, cso _if_ @@ -74,18 +73,6 @@ requires = [ "meson-python>=0.13.1", "pybind11>=2.6,!=2.13.3", "setuptools_scm>=7", - - # Comments on numpy build requirement range: - # - # 1. >=2.0.x is the numpy requirement for wheel builds for distribution - # on PyPI - building against 2.x yields wheels that are also - # ABI-compatible with numpy 1.x at runtime. - # 2. Note that building against numpy 1.x works fine too - users and - # redistributors can do this by installing the numpy version they like - # and disabling build isolation. - # 3. The <2.3 upper bound is for matching the numpy deprecation policy, - # it should not be loosened. - "numpy>=2.0.0rc1,<2.3", ] [tool.meson-python.args] diff --git a/requirements/dev/build-requirements.txt b/requirements/dev/build-requirements.txt index 0861a11c9ee5..b5cb6acdb279 100644 --- a/requirements/dev/build-requirements.txt +++ b/requirements/dev/build-requirements.txt @@ -1,4 +1,3 @@ pybind11!=2.13.3 meson-python -numpy<2.1.0 setuptools-scm diff --git a/requirements/testing/mypy.txt b/requirements/testing/mypy.txt index 0b65050b52de..aa20581ee69b 100644 --- a/requirements/testing/mypy.txt +++ b/requirements/testing/mypy.txt @@ -18,7 +18,6 @@ contourpy>=1.0.1 cycler>=0.10 fonttools>=4.22.0 kiwisolver>=1.3.1 -numpy>=1.19 packaging>=20.0 pillow>=8 pyparsing>=2.3.1 diff --git a/src/_backend_agg.h b/src/_backend_agg.h index a0147f9832c3..b14eb3f9e565 100644 --- a/src/_backend_agg.h +++ b/src/_backend_agg.h @@ -1234,11 +1234,11 @@ inline void RendererAgg::draw_gouraud_triangles(GCAgg &gc, ColorArray &colors, agg::trans_affine &trans) { - if (points.shape(0) && !check_trailing_shape(points, "points", 3, 2)) { - throw py::error_already_set(); + if (points.shape(0)) { + check_trailing_shape(points, "points", 3, 2); } - if (colors.shape(0) && !check_trailing_shape(colors, "colors", 3, 4)) { - throw py::error_already_set(); + if (colors.shape(0)) { + check_trailing_shape(colors, "colors", 3, 4); } if (points.shape(0) != colors.shape(0)) { throw py::value_error( diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp index fb241b217fe9..bfc8584d688d 100644 --- a/src/_backend_agg_wrapper.cpp +++ b/src/_backend_agg_wrapper.cpp @@ -2,10 +2,8 @@ #include #include #include "mplutils.h" -#include "numpy_cpp.h" #include "py_converters.h" #include "_backend_agg.h" -#include "py_converters_11.h" namespace py = pybind11; using namespace pybind11::literals; @@ -189,14 +187,6 @@ PyRendererAgg_draw_gouraud_triangles(RendererAgg *self, PYBIND11_MODULE(_backend_agg, m) { - auto ia = [m]() -> const void* { - import_array(); - return &m; - }; - if (ia() == NULL) { - throw py::error_already_set(); - } - py::class_(m, "RendererAgg", py::buffer_protocol()) .def(py::init(), "width"_a, "height"_a, "dpi"_a) diff --git a/src/_image_wrapper.cpp b/src/_image_wrapper.cpp index 856dcf4ea3ce..0095f52e5997 100644 --- a/src/_image_wrapper.cpp +++ b/src/_image_wrapper.cpp @@ -2,7 +2,7 @@ #include #include "_image_resample.h" -#include "py_converters_11.h" +#include "py_converters.h" namespace py = pybind11; using namespace pybind11::literals; diff --git a/src/_path.h b/src/_path.h index 693862c7a829..f5c06e4a6a15 100644 --- a/src/_path.h +++ b/src/_path.h @@ -18,7 +18,6 @@ #include "path_converters.h" #include "_backend_agg_basic_types.h" -#include "numpy_cpp.h" const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3 }; @@ -1004,7 +1003,7 @@ void convert_path_to_polygons(PathIterator &path, template void -__cleanup_path(VertexSource &source, std::vector &vertices, std::vector &codes) +__cleanup_path(VertexSource &source, std::vector &vertices, std::vector &codes) { unsigned code; double x, y; @@ -1012,7 +1011,7 @@ __cleanup_path(VertexSource &source, std::vector &vertices, std::vector< code = source.vertex(&x, &y); vertices.push_back(x); vertices.push_back(y); - codes.push_back((npy_uint8)code); + codes.push_back(static_cast(code)); } while (code != agg::path_cmd_stop); } diff --git a/src/_path_wrapper.cpp b/src/_path_wrapper.cpp index 13431601e5af..7a7cb343b8e7 100644 --- a/src/_path_wrapper.cpp +++ b/src/_path_wrapper.cpp @@ -7,14 +7,11 @@ #include #include -#include "numpy_cpp.h" - #include "_path.h" #include "_backend_agg_basic_types.h" #include "py_adaptors.h" #include "py_converters.h" -#include "py_converters_11.h" namespace py = pybind11; using namespace pybind11::literals; @@ -184,9 +181,7 @@ Py_affine_transform(py::array_t(); - if(!check_trailing_shape(vertices, "vertices", 2)) { - throw py::error_already_set(); - } + check_trailing_shape(vertices, "vertices", 2); py::ssize_t dims[] = { vertices.shape(0), 2 }; py::array_t result(dims); @@ -267,7 +262,7 @@ Py_cleanup_path(mpl::PathIterator path, agg::trans_affine trans, bool remove_nan bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2); std::vector vertices; - std::vector codes; + std::vector codes; cleanup_path(path, trans, remove_nans, do_clip, clip_rect, snap_mode, stroke_width, *simplify, return_curves, sketch, vertices, codes); @@ -375,14 +370,6 @@ Py_is_sorted_and_has_non_nan(py::object obj) PYBIND11_MODULE(_path, m) { - auto ia = [m]() -> const void* { - import_array(); - return &m; - }; - if (ia() == NULL) { - throw py::error_already_set(); - } - m.def("point_in_path", &Py_point_in_path, "x"_a, "y"_a, "radius"_a, "path"_a, "trans"_a); m.def("points_in_path", &Py_points_in_path, diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 9791dc7e2e06..4358646beede 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -4,7 +4,6 @@ #include #include "ft2font.h" -#include "numpy/arrayobject.h" #include #include @@ -955,14 +954,6 @@ PyFT2Font_fname(PyFT2Font *self) PYBIND11_MODULE(ft2font, m) { - auto ia = [m]() -> const void* { - import_array(); - return &m; - }; - if (ia() == NULL) { - throw py::error_already_set(); - } - if (FT_Init_FreeType(&_ft2Library)) { // initialize library throw std::runtime_error("Could not initialize the freetype2 library"); } diff --git a/src/meson.build b/src/meson.build index 6f1869cc6ca4..d2bc9e4afccd 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,42 +1,3 @@ -# NumPy include directory - needed in all submodules -# The try-except is needed because when things are split across drives on Windows, there -# is no relative path and an exception gets raised. There may be other such cases, so add -# a catch-all and switch to an absolute path. Relative paths are needed when for example -# a virtualenv is placed inside the source tree; Meson rejects absolute paths to places -# inside the source tree. -# For cross-compilation it is often not possible to run the Python interpreter in order -# to retrieve numpy's include directory. It can be specified in the cross file instead: -# -# [properties] -# numpy-include-dir = /abspath/to/host-pythons/site-packages/numpy/core/include -# -# This uses the path as is, and avoids running the interpreter. -incdir_numpy = meson.get_external_property('numpy-include-dir', 'not-given') -if incdir_numpy == 'not-given' - incdir_numpy = run_command(py3, - [ - '-c', - '''import os -import numpy as np -try: - incdir = os.path.relpath(np.get_include()) -except Exception: - incdir = np.get_include() -print(incdir)''' - ], - check: true - ).stdout().strip() -endif -numpy_dep = declare_dependency( - compile_args: [ - '-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION', - # Allow NumPy's printf format specifiers in C++. - '-D__STDC_FORMAT_MACROS=1', - ], - include_directories: include_directories(incdir_numpy), - dependencies: py3_dep, -) - # For cross-compilation it is often not possible to run the Python interpreter in order # to retrieve the platform-specific /dev/null. It can be specified in the cross file # instead: @@ -73,12 +34,10 @@ extension_data = { '_backend_agg': { 'subdir': 'matplotlib/backends', 'sources': files( - 'py_converters.cpp', - 'py_converters_11.cpp', '_backend_agg.cpp', '_backend_agg_wrapper.cpp', ), - 'dependencies': [agg_dep, numpy_dep, freetype_dep, pybind11_dep], + 'dependencies': [agg_dep, freetype_dep, pybind11_dep], }, '_c_internal_utils': { 'subdir': 'matplotlib', @@ -92,10 +51,9 @@ extension_data = { 'sources': files( 'ft2font.cpp', 'ft2font_wrapper.cpp', - 'py_converters.cpp', ), 'dependencies': [ - freetype_dep, pybind11_dep, numpy_dep, agg_dep.partial_dependency(includes: true), + freetype_dep, pybind11_dep, agg_dep.partial_dependency(includes: true), ], 'cpp_args': [ '-DFREETYPE_BUILD_TYPE="@0@"'.format( @@ -107,7 +65,7 @@ extension_data = { 'subdir': 'matplotlib', 'sources': files( '_image_wrapper.cpp', - 'py_converters_11.cpp', + 'py_converters.cpp', ), 'dependencies': [ pybind11_dep, @@ -118,11 +76,9 @@ extension_data = { '_path': { 'subdir': 'matplotlib', 'sources': files( - 'py_converters.cpp', - 'py_converters_11.cpp', '_path_wrapper.cpp', ), - 'dependencies': [numpy_dep, agg_dep, pybind11_dep], + 'dependencies': [agg_dep, pybind11_dep], }, '_qhull': { 'subdir': 'matplotlib', @@ -162,16 +118,7 @@ extension_data = { } foreach ext, kwargs : extension_data - # Ensure that PY_ARRAY_UNIQUE_SYMBOL is uniquely defined for each extension. - unique_array_api = '-DPY_ARRAY_UNIQUE_SYMBOL=MPL_@0@_ARRAY_API'.format(ext.replace('.', '_')) - additions = { - 'c_args': [unique_array_api] + kwargs.get('c_args', []), - 'cpp_args': [unique_array_api] + kwargs.get('cpp_args', []), - } - py3.extension_module( - ext, - install: true, - kwargs: kwargs + additions) + py3.extension_module(ext, install: true, kwargs: kwargs) endforeach if get_option('macosx') and host_machine.system() == 'darwin' diff --git a/src/mplutils.h b/src/mplutils.h index b7a80a84429c..95d9a2d9eb90 100644 --- a/src/mplutils.h +++ b/src/mplutils.h @@ -48,22 +48,6 @@ enum { CLOSEPOLY = 0x4f }; -inline int prepare_and_add_type(PyTypeObject *type, PyObject *module) -{ - if (PyType_Ready(type)) { - return -1; - } - char const* ptr = strrchr(type->tp_name, '.'); - if (!ptr) { - PyErr_SetString(PyExc_ValueError, "tp_name should be a qualified name"); - return -1; - } - if (PyModule_AddObject(module, ptr + 1, (PyObject *)type)) { - return -1; - } - return 0; -} - #ifdef __cplusplus // not for macosx.m // Check that array has shape (N, d1) or (N, d1, d2). We cast d1, d2 to longs // so that we don't need to access the NPY_INTP_FMT macro here. @@ -71,51 +55,44 @@ inline int prepare_and_add_type(PyTypeObject *type, PyObject *module) #include namespace py = pybind11; +using namespace pybind11::literals; template -inline bool check_trailing_shape(T array, char const* name, long d1) +inline void check_trailing_shape(T array, char const* name, long d1) { if (array.ndim() != 2) { - PyErr_Format(PyExc_ValueError, - "Expected 2-dimensional array, got %ld", - array.ndim()); - return false; + throw py::value_error( + "Expected 2-dimensional array, got %d"_s.format(array.ndim())); } if (array.size() == 0) { // Sometimes things come through as atleast_2d, etc., but they're empty, so // don't bother enforcing the trailing shape. - return true; + return; } if (array.shape(1) != d1) { - PyErr_Format(PyExc_ValueError, - "%s must have shape (N, %ld), got (%ld, %ld)", - name, d1, array.shape(0), array.shape(1)); - return false; + throw py::value_error( + "%s must have shape (N, %d), got (%d, %d)"_s.format( + name, d1, array.shape(0), array.shape(1))); } - return true; } template -inline bool check_trailing_shape(T array, char const* name, long d1, long d2) +inline void check_trailing_shape(T array, char const* name, long d1, long d2) { if (array.ndim() != 3) { - PyErr_Format(PyExc_ValueError, - "Expected 3-dimensional array, got %ld", - array.ndim()); - return false; + throw py::value_error( + "Expected 3-dimensional array, got %d"_s.format(array.ndim())); } if (array.size() == 0) { // Sometimes things come through as atleast_3d, etc., but they're empty, so // don't bother enforcing the trailing shape. - return true; + return; } if (array.shape(1) != d1 || array.shape(2) != d2) { - PyErr_Format(PyExc_ValueError, - "%s must have shape (N, %ld, %ld), got (%ld, %ld, %ld)", - name, d1, d2, array.shape(0), array.shape(1), array.shape(2)); - return false; + throw py::value_error( + "%s must have shape (N, %d, %d), got (%d, %d, %d)"_s.format( + name, d1, d2, array.shape(0), array.shape(1), array.shape(2))); } - return true; } /* In most cases, code should use safe_first_shape(obj) instead of obj.shape(0), since diff --git a/src/numpy_cpp.h b/src/numpy_cpp.h deleted file mode 100644 index 6b7446337bb7..000000000000 --- a/src/numpy_cpp.h +++ /dev/null @@ -1,582 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#ifndef MPL_NUMPY_CPP_H -#define MPL_NUMPY_CPP_H -#define PY_SSIZE_T_CLEAN -/*************************************************************************** - * This file is based on original work by Mark Wiebe, available at: - * - * http://github.com/mwiebe/numpy-cpp - * - * However, the needs of matplotlib wrappers, such as treating an - * empty array as having the correct dimensions, have made this rather - * matplotlib-specific, so it's no longer compatible with the - * original. - */ - -#ifdef _POSIX_C_SOURCE -# undef _POSIX_C_SOURCE -#endif -#ifndef _AIX -#ifdef _XOPEN_SOURCE -# undef _XOPEN_SOURCE -#endif -#endif - -// Prevent multiple conflicting definitions of swab from stdlib.h and unistd.h -#if defined(__sun) || defined(sun) -#if defined(_XPG4) -#undef _XPG4 -#endif -#if defined(_XPG3) -#undef _XPG3 -#endif -#endif - -#include -#include - -#include "py_exceptions.h" - -#include - -namespace numpy -{ - -// Type traits for the NumPy types -template -struct type_num_of; - -/* Be careful with bool arrays as python has sizeof(npy_bool) == 1, but it is - * not always the case that sizeof(bool) == 1. Using the array_view_accessors - * is always fine regardless of sizeof(bool), so do this rather than using - * array.data() and pointer arithmetic which will not work correctly if - * sizeof(bool) != 1. */ -template <> struct type_num_of -{ - enum { - value = NPY_BOOL - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_BYTE - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_UBYTE - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_SHORT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_USHORT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_INT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_UINT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_LONG - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_ULONG - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_LONGLONG - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_ULONGLONG - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_FLOAT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_DOUBLE - }; -}; -#if NPY_LONGDOUBLE != NPY_DOUBLE -template <> -struct type_num_of -{ - enum { - value = NPY_LONGDOUBLE - }; -}; -#endif -template <> -struct type_num_of -{ - enum { - value = NPY_CFLOAT - }; -}; -template <> -struct type_num_of > -{ - enum { - value = NPY_CFLOAT - }; -}; -template <> -struct type_num_of -{ - enum { - value = NPY_CDOUBLE - }; -}; -template <> -struct type_num_of > -{ - enum { - value = NPY_CDOUBLE - }; -}; -#if NPY_CLONGDOUBLE != NPY_CDOUBLE -template <> -struct type_num_of -{ - enum { - value = NPY_CLONGDOUBLE - }; -}; -template <> -struct type_num_of > -{ - enum { - value = NPY_CLONGDOUBLE - }; -}; -#endif -template <> -struct type_num_of -{ - enum { - value = NPY_OBJECT - }; -}; -template -struct type_num_of -{ - enum { - value = type_num_of::value - }; -}; -template -struct type_num_of -{ - enum { - value = type_num_of::value - }; -}; - -template -struct is_const -{ - enum { - value = false - }; -}; -template -struct is_const -{ - enum { - value = true - }; -}; - -namespace detail -{ -template