Skip to content

Commit

Permalink
Merge branch 'main' into fix-copy-arg-array
Browse files Browse the repository at this point in the history
  • Loading branch information
mtsokol committed Apr 2, 2024
2 parents 1814aa8 + 4517378 commit 4140c67
Show file tree
Hide file tree
Showing 42 changed files with 505 additions and 288 deletions.
7 changes: 1 addition & 6 deletions .github/workflows/emscripten.yml
Expand Up @@ -23,7 +23,7 @@ jobs:
# To enable this workflow on a fork, comment out:
if: github.repository == 'numpy/numpy'
env:
PYODIDE_VERSION: 0.25.0
PYODIDE_VERSION: 0.25.1
# PYTHON_VERSION and EMSCRIPTEN_VERSION are determined by PYODIDE_VERSION.
# The appropriate versions can be found in the Pyodide repodata.json
# "info" field, or in Makefile.envs:
Expand Down Expand Up @@ -69,11 +69,6 @@ jobs:
with open(env_file, "a") as myfile:
myfile.write(f"PYODIDE_BUILD_PATH={pyodide_build_path}\n")
- name: Apply patch(es) for pyodide-build installation
run: |
ls -a ${{ env.PYODIDE_BUILD_PATH }}
patch -d "${{ env.PYODIDE_BUILD_PATH }}" -p1 < tools/ci/emscripten/0001-do-not-set-meson-environment-variable-pyodide-gh-4502.patch
- name: Build NumPy for Pyodide
run: |
pyodide build -Cbuild-dir=build -Csetup-args="--cross-file=$PWD/tools/ci/emscripten/emscripten.meson.cross" -Csetup-args="-Dblas=none" -Csetup-args="-Dlapack=none"
Expand Down
21 changes: 21 additions & 0 deletions doc/source/numpy_2_0_migration_guide.rst
Expand Up @@ -425,3 +425,24 @@ The :ref:`copy keyword behavior changes <copy-keyword-changes-2.0>` in
older NumPy versions as well. If ``copy`` keyword is considered in
the ``__array__`` method implementation, then for ``copy=True`` always
return a new copy.


Writing numpy-version-dependent code
------------------------------------

It should be fairly rare to have to write code that explicitly branches on the
``numpy`` version - in most cases, code can be rewritten to be compatible with
1.x and 2.0 at the same time. However, if it is necessary, here is a suggested
code pattern to use, using `numpy.lib.NumpyVersion`::

# example with AxisError, which is no longer available in
# the main namespace in 2.0, and not available in the
# `exceptions` namespace in <1.25.0 (example uses <2.0.0b1
# for illustrative purposes):
if np.lib.NumpyVersion(np.__version__) >= '2.0.0b1':
from numpy.exceptions import AxisError
else:
from numpy import AxisError

This pattern will work correctly including with NumPy release candidates, which
is important during the 2.0.0 release period.
24 changes: 24 additions & 0 deletions doc/source/reference/c-api/types-and-structures.rst
Expand Up @@ -906,6 +906,30 @@ PyArray_DTypeMeta and PyArrayDTypeMeta_Spec
of functions in the DType API. Slot IDs must be one of the
DType slot IDs enumerated in :ref:`dtype-slots`.
Exposed DTypes classes (``PyArray_DTypeMeta`` objects)
------------------------------------------------------
For use with promoters, NumPy exposes a number of Dtypes following the
pattern ``PyArray_<Name>DType`` corresponding to those found in `np.dtypes`.
Additionally, the three DTypes, ``PyArray_PyLongDType``,
``PyArray_PyFloatDType``, ``PyArray_PyComplexDType`` correspond to the
Python scalar values. These cannot be used in all places, but do allow
for example the common dtype operation and implementing promotion with them
may be necessary.
Further, the following abstract DTypes are defined which cover both the
builtin NumPy ones and the python ones, and users can in principle subclass
from them (this does not inherit any DType specific functionality):
* ``PyArray_IntAbstractDType``
* ``PyArray_FloatAbstractDType``
* ``PyArray_ComplexAbstractDType``
.. warning::
As of NumPy 2.0, the *only* valid use for these DTypes is registering a
promoter conveniently to e.g. match "any integers" (and subclass checks).
Because of this, they are not exposed to Python.
PyUFunc_Type and PyUFuncObject
------------------------------
Expand Down
2 changes: 2 additions & 0 deletions doc/source/reference/module_structure.rst
Expand Up @@ -35,6 +35,7 @@ Special-purpose namespaces
- :ref:`numpy.emath <routines.emath>` - mathematical functions with automatic domain
- :ref:`numpy.lib <routines.lib>` - utilities & functionality which do not fit the main namespace
- :ref:`numpy.rec <routines.rec>` - record arrays (largely superseded by dataframe libraries)
- :ref:`numpy.version <routines.version>` - small module with more detailed version info

Legacy namespaces
=================
Expand Down Expand Up @@ -67,6 +68,7 @@ and/or this code is deprecated or isn't reliable.
numpy.emath <routines.emath>
numpy.lib <routines.lib>
numpy.rec <routines.rec>
numpy.version <routines.version>
numpy.char <routines.char>
numpy.distutils <distutils>
numpy.f2py <../f2py/index>
Expand Down
38 changes: 38 additions & 0 deletions doc/source/reference/routines.version.rst
@@ -0,0 +1,38 @@
.. currentmodule:: numpy.version

.. _routines.version:

*******************
Version information
*******************

The ``numpy.version`` submodule includes several constants that expose more
detailed information about the exact version of the installed ``numpy``
package:

.. data:: version

Version string for the installed package - matches ``numpy.__version__``.

.. data:: full_version

Version string - the same as ``numpy.version.version``.

.. data:: short_version

Version string without any local build identifiers.

.. rubric:: Examples

>>> np.__version__
'2.1.0.dev0+git20240319.2ea7ce0' # may vary
>>> np.version.short_version
'2.1.0.dev0' # may vary

.. data:: git_revision

String containing the git hash of the commit from which ``numpy`` was built.

.. data:: release

``True`` if this version is a ``numpy`` release, ``False`` if a dev version.
2 changes: 1 addition & 1 deletion doc/source/user/absolute_beginners.rst
Expand Up @@ -1525,7 +1525,7 @@ If you want to store a single ndarray object, store it as a .npy file using
save it as a .npz file using ``np.savez``. You can also save several arrays
into a single file in compressed npz format with `savez_compressed`.

It's easy to save and load and array with ``np.save()``. Just make sure to
It's easy to save and load an array with ``np.save()``. Just make sure to
specify the array you want to save and a file name. For example, if you create
this array::

Expand Down
28 changes: 28 additions & 0 deletions doc/source/user/troubleshooting-importerror.rst
Expand Up @@ -183,6 +183,34 @@ that usually works is to upgrade the NumPy version::

pip install numpy --upgrade


Downstream ImportError or AttributeError
========================================

If you see a message such as::

A module that was compiled using NumPy 1.x cannot be run in
NumPy 2.0.0 as it may crash. To support both 1.x and 2.x
versions of NumPy, modules must be compiled with NumPy 2.0.
Some module may need to rebuild instead e.g. with 'pybind11>=2.12'.

Either as an ``ImportError`` or with::

AttributeError: _ARRAY_API not found

Then you are using NumPy 2 together with a module that was build with NumPy 1.
NumPy 2 made some changes that require rebuilding such modules to avoid
possibly incorrect results or crashes.

As the error message suggests, the easiest solution is likely to downgrade
NumPy to `numpy<2`.
Alternatively, you can search the traceback (from the back) to find the first
line that isn't inside NumPy to see which module needs to be updated.

NumPy 2 was released in the first half of 2024 and especially smaller
modules downstream are expected need time to adapt and publish a new version.


Segfaults or crashes
====================

Expand Down
3 changes: 3 additions & 0 deletions numpy/_build_utils/gitversion.py
Expand Up @@ -70,6 +70,9 @@ def git_version(version):

# For NumPy 2.0, this should only have one field: `version`
template = textwrap.dedent(f'''
"""
Module to expose more detailed version info for the installed `numpy`
"""
version = "{version}"
__version__ = version
full_version = version
Expand Down
5 changes: 5 additions & 0 deletions numpy/_core/code_generators/generate_numpy_api.py
Expand Up @@ -227,6 +227,7 @@ def do_generate_api(targets, sources):

# Check multiarray api indexes
multiarray_api_index = genapi.merge_api_dicts(multiarray_api)
unused_index_max = max(multiarray_api_index.get("__unused_indices__", 0))
genapi.check_api_dict(multiarray_api_index)

numpyapi_list = genapi.get_api_functions('NUMPY_API',
Expand Down Expand Up @@ -278,6 +279,10 @@ def do_generate_api(targets, sources):
init_list.append(api_item.array_api_define())
module_list.append(api_item.internal_define())

# In case we end with a "hole", append more NULLs
while len(init_list) <= unused_index_max:
init_list.append(" NULL")

# Write to header
s = h_template % ('\n'.join(module_list), '\n'.join(extension_list))
genapi.write_file(header_file, s)
Expand Down
4 changes: 3 additions & 1 deletion numpy/_core/code_generators/numpy_api.py
Expand Up @@ -94,6 +94,7 @@ def get_annotations():
# NOTE: The Slots 320-360 are defined in `_experimental_dtype_api.h`
# and filled explicitly outside the code generator as the metaclass
# makes them tricky to expose. (This may be refactored.)
# Slot 366, 367, 368 are the abstract DTypes
# End 2.0 API
}

Expand All @@ -107,7 +108,8 @@ def get_annotations():
103, 115, 117, 122, 163, 164, 171, 173, 197,
201, 202, 208, 219, 220, 221, 222, 223, 278,
291, 293, 294, 295, 301]
+ list(range(320, 361)) # range reserved DType class slots
# range/slots reserved DType classes (see _public_dtype_api_table.h):
+ list(range(320, 361)) + [366, 367, 368]
),
'PyArray_GetNDArrayCVersion': (0,),
# Unused slot 40, was `PyArray_SetNumericOps`
Expand Down
23 changes: 15 additions & 8 deletions numpy/_core/include/numpy/_public_dtype_api_table.h
Expand Up @@ -4,6 +4,9 @@
*
* These definitions are only relevant for the public API and we reserve
* the slots 320-360 in the API table generation for this (currently).
*
* TODO: This file should be consolidated with the API table generation
* (although not sure the current generation is worth preserving).
*/
#ifndef NUMPY_CORE_INCLUDE_NUMPY__PUBLIC_DTYPE_API_TABLE_H_
#define NUMPY_CORE_INCLUDE_NUMPY__PUBLIC_DTYPE_API_TABLE_H_
Expand Down Expand Up @@ -61,17 +64,21 @@
/* Object/Void */
#define PyArray_ObjectDType (*(PyArray_DTypeMeta *)(PyArray_API + 320)[33])
#define PyArray_VoidDType (*(PyArray_DTypeMeta *)(PyArray_API + 320)[34])
/* Abstract */
#define PyArray_PyIntAbstractDType \
(*(PyArray_DTypeMeta *)(PyArray_API + 320)[35])
#define PyArray_PyFloatAbstractDType \
(*(PyArray_DTypeMeta *)(PyArray_API + 320)[36])
#define PyArray_PyComplexAbstractDType \
(*(PyArray_DTypeMeta *)(PyArray_API + 320)[37])
/* Python types (used as markers for scalars) */
#define PyArray_PyLongDType (*(PyArray_DTypeMeta *)(PyArray_API + 320)[35])
#define PyArray_PyFloatDType (*(PyArray_DTypeMeta *)(PyArray_API + 320)[36])
#define PyArray_PyComplexDType (*(PyArray_DTypeMeta *)(PyArray_API + 320)[37])
/* Default integer type */
#define PyArray_DefaultIntDType (*(PyArray_DTypeMeta *)(PyArray_API + 320)[38])
/* New non-legacy DTypes follow in the order they were added */
#define PyArray_StringDType (*(PyArray_DTypeMeta *)(PyArray_API + 320)[39])
/* NOTE: offset 40 is free, after that a new range will need to be used */

/* NOTE: offset 40 is free */

/* Need to start with a larger offset again for the abstract classes: */
#define PyArray_IntAbstractDType (*(PyArray_DTypeMeta *)PyArray_API[366])
#define PyArray_FloatAbstractDType (*(PyArray_DTypeMeta *)PyArray_API[367])
#define PyArray_ComplexAbstractDType (*(PyArray_DTypeMeta *)PyArray_API[368])

#endif /* NPY_FEATURE_VERSION >= NPY_2_0_API_VERSION */

Expand Down
2 changes: 1 addition & 1 deletion numpy/_core/meson.build
Expand Up @@ -347,7 +347,7 @@ max_opt = {
'msvc': ['/O2'],
'intel-cl': ['/O3'],
}.get(compiler_id, ['-O3'])
max_opt = cc.has_multi_arguments(max_opt) ? max_opt : []
max_opt = cc.has_multi_arguments(max_opt) and get_option('buildtype') != 'debug' ? max_opt : []

# Optional GCC compiler builtins and their call arguments.
# If given, a required header and definition name (HAVE_ prepended)
Expand Down
2 changes: 1 addition & 1 deletion numpy/_core/numeric.py
Expand Up @@ -2142,7 +2142,7 @@ def base_repr(number, base=2, padding=0):
elif base < 2:
raise ValueError("Bases less than 2 not handled in base_repr.")

num = abs(number)
num = abs(int(number))
res = []
while num:
res.append(digits[num % base])
Expand Down

0 comments on commit 4140c67

Please sign in to comment.