diff options
135 files changed, 2302 insertions, 1044 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index e055739e5..86470e086 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,6 +21,8 @@ jobs: python3 -m venv venv . venv/bin/activate pip install cython sphinx matplotlib + sudo apt-get update + sudo apt-get install -y graphviz - run: name: build numpy @@ -54,8 +56,51 @@ jobs: # path: doc/neps/_build/html/ # destination: neps - - deploy: + - add_ssh_keys: + fingerprints: + - "9f:8c:e5:3f:53:40:0b:ee:c9:c3:0f:fd:0f:3c:cc:55" + + - run: + name: deploy devdocs command: | if [ "${CIRCLE_BRANCH}" == "master" ]; then - echo "Deploying on master" + touch doc/build/html/.nojekyll + + ./tools/ci/push_docs_to_repo.py doc/build/html \ + git@github.com:numpy/devdocs.git \ + --committer "numpy-circleci-bot" \ + --email "numpy-circleci-bot@nomail" \ + --message "Docs build of $CIRCLE_SHA1" \ + --force + else + echo "Not on the master branch; skipping deployment" + fi + + - add_ssh_keys: + fingerprints: + - "11:fb:19:69:80:3a:6d:37:9c:d1:ac:20:17:cd:c8:17" + + - run: + name: select SSH key for neps repo + command: | + cat <<\EOF > ~/.ssh/config + Host github.com + IdentitiesOnly yes + IdentityFile /home/circleci/.ssh/id_rsa_11fb1969803a6d379cd1ac2017cdc817 + EOF + + - run: + name: deploy neps + command: | + if [ "${CIRCLE_BRANCH}" == "master" ]; then + touch doc/neps/_build/html/.nojekyll + + ./tools/ci/push_docs_to_repo.py doc/neps/_build/html \ + git@github.com:numpy/neps.git \ + --committer "numpy-circleci-bot" \ + --email "numpy-circleci-bot@nomail" \ + --message "Docs build of $CIRCLE_SHA1" \ + --force + else + echo "Not on the master branch; skipping deployment" fi diff --git a/.travis.yml b/.travis.yml index fca0c632d..168a7a385 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,19 +35,18 @@ python: - 3.4 - 3.5 - 3.6 + - 3.7-dev matrix: include: - - python: 2.7 - env: USE_CHROOT=1 ARCH=i386 DIST=artful PYTHON=2.7 + - python: 3.6 + env: USE_CHROOT=1 ARCH=i386 DIST=artful PYTHON=3.6 sudo: true - dist: trusty addons: apt: packages: - debootstrap - python: 3.4 env: USE_DEBUG=1 - dist: trusty addons: apt: packages: @@ -57,19 +56,21 @@ matrix: - python3-dev - python3-nose - python3-setuptools - - python: 3.5 + - python: 3.6 env: USE_WHEEL=1 RUN_FULL_TESTS=1 - - python: 3.5 - env: USE_SDIST=1 - python: 2.7 + env: USE_WHEEL=1 RUN_FULL_TESTS=1 PYTHON_OPTS="-3 -OO" + - python: 3.6 + env: USE_SDIST=1 + - python: 3.6 env: - PYTHONOPTIMIZE=2 - USE_ASV=1 - - python: 2.7 - env: NPY_RELAXED_STRIDES_CHECKING=0 PYTHON_OPTS="-3 -OO" - - python: 2.7 + - python: 3.5 + env: NPY_RELAXED_STRIDES_CHECKING=0 + - python: 3.6 env: USE_WHEEL=1 NPY_RELAXED_STRIDES_DEBUG=1 - - python: 2.7 + - python: 3.6 env: - BLAS=None - LAPACK=None diff --git a/doc/Makefile b/doc/Makefile index d414d26d7..667dbef29 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,7 +1,7 @@ # Makefile for Sphinx documentation # -PYVER = 2.7 +PYVER = 3.6 PYTHON = python$(PYVER) # You can set these variables from the command line. diff --git a/doc/changelog/1.14.2-changelog.rst b/doc/changelog/1.14.2-changelog.rst new file mode 100644 index 000000000..fae815c8e --- /dev/null +++ b/doc/changelog/1.14.2-changelog.rst @@ -0,0 +1,22 @@ + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Allan Haldane +* Charles Harris +* Eric Wieser +* Pauli Virtanen + +Pull requests merged +==================== + +A total of 5 pull requests were merged for this release. + +* `#10674 <https://github.com/numpy/numpy/pull/10674>`__: BUG: Further back-compat fix for subclassed array repr +* `#10725 <https://github.com/numpy/numpy/pull/10725>`__: BUG: dragon4 fractional output mode adds too many trailing zeros +* `#10726 <https://github.com/numpy/numpy/pull/10726>`__: BUG: Fix f2py generated code to work on PyPy +* `#10727 <https://github.com/numpy/numpy/pull/10727>`__: BUG: Fix missing NPY_VISIBILITY_HIDDEN on npy_longdouble_to_PyLong +* `#10729 <https://github.com/numpy/numpy/pull/10729>`__: DOC: Create 1.14.2 notes and changelog. diff --git a/doc/neps/.gitignore b/doc/neps/.gitignore new file mode 100644 index 000000000..04163f707 --- /dev/null +++ b/doc/neps/.gitignore @@ -0,0 +1 @@ +index.rst diff --git a/doc/neps/Makefile b/doc/neps/Makefile index 2d1a063de..3c023ae9b 100644 --- a/doc/neps/Makefile +++ b/doc/neps/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = -W SPHINXBUILD = sphinx-build SPHINXPROJ = NumPyEnhancementProposals SOURCEDIR = . @@ -12,9 +12,12 @@ BUILDDIR = _build help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile +.PHONY: help Makefile index + +index: + python tools/build_index.py # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile +%: Makefile index @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/neps/index.rst b/doc/neps/index.rst.tmpl index 8a2df4078..8d0c5da77 100644 --- a/doc/neps/index.rst +++ b/doc/neps/index.rst.tmpl @@ -14,9 +14,11 @@ Meta-NEPs (NEPs about NEPs or Processes) .. toctree:: :maxdepth: 1 - nep-0000 - nep-template +{% for nep, tags in neps.items() if tags['Type'] == 'Process' %} + NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> +{% endfor %} + nep-template Accepted NEPs, implementation in progress ----------------------------------------- @@ -24,8 +26,9 @@ Accepted NEPs, implementation in progress .. toctree:: :maxdepth: 1 - nep-0014-dropping-python2.7-proposal - +{% for nep, tags in neps.items() if tags['Status'] == 'Accepted' %} + NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> +{% endfor %} Implemented NEPs ---------------- @@ -33,12 +36,9 @@ Implemented NEPs .. toctree:: :maxdepth: 1 - nep-0001-npy-format - nep-0005-generalized-ufuncs - nep-0007-datetime-proposal - nep-0010-new-iterator-ufunc - nep-0013-ufunc-overrides - +{% for nep, tags in neps.items() if tags['Status'] == 'Final' %} + NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> +{% endfor %} Defunct NEPs ------------ @@ -46,11 +46,6 @@ Defunct NEPs .. toctree:: :maxdepth: 1 - nep-0002-warnfix - nep-0003-math_config_clean - nep-0004-datetime-proposal3 - nep-0006-newbugtracker - nep-0008-groupby_additions - nep-0009-structured_array_extensions - nep-0011-deferred-ufunc-evaluation - nep-0012-missing-data +{% for nep, tags in neps.items() if tags['Status'] == 'Deferred' %} + NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> +{% endfor %} diff --git a/doc/neps/nep-0000.rst b/doc/neps/nep-0000.rst index ae8603c62..a7d6d7115 100644 --- a/doc/neps/nep-0000.rst +++ b/doc/neps/nep-0000.rst @@ -1,6 +1,6 @@ -======================= -NEP Purpose and Process -======================= +=================== +Purpose and Process +=================== :Author: Jarrod Millman <millman@berkeley.edu> :Status: Draft @@ -121,9 +121,12 @@ updated accordingly. In addition to updating the status field, at the very least the ``Resolution`` header should be added with a link to the relevant post in the mailing list archives. -NEPs can also be ``Replaced`` by a different NEP, rendering the original -obsolete. Process NEPs may also have a status of -``Active`` if they are never meant to be completed. E.g. NEP 0 (this NEP). +NEPs can also be ``Superseded`` by a different NEP, rendering the +original obsolete. The ``Replaced-By`` and ``Replaces`` headers +should be added to the original and new NEPs respectively. + +Process NEPs may also have a status of ``Active`` if they are never +meant to be completed, e.g. NEP 0 (this NEP). Maintenance diff --git a/doc/neps/nep-0001-npy-format.rst b/doc/neps/nep-0001-npy-format.rst index 3f12e1bf1..2057aed83 100644 --- a/doc/neps/nep-0001-npy-format.rst +++ b/doc/neps/nep-0001-npy-format.rst @@ -2,10 +2,9 @@ A Simple File Format for NumPy Arrays ===================================== -Author: Robert Kern <robert.kern@gmail.com> -Status: Draft -Created: 20-Dec-2007 - +:Author: Robert Kern <robert.kern@gmail.com> +:Status: Final +:Created: 20-Dec-2007 Abstract -------- diff --git a/doc/neps/nep-0002-warnfix.rst b/doc/neps/nep-0002-warnfix.rst index 4b0a2a56e..60dc885b2 100644 --- a/doc/neps/nep-0002-warnfix.rst +++ b/doc/neps/nep-0002-warnfix.rst @@ -5,6 +5,7 @@ A proposal to build numpy without warning with a big set of warning flags :Author: David Cournapeau :Contact: david@ar.media.kyoto-u.ac.jp :Date: 2008-09-04 +:Status: Deferred Executive summary ================= @@ -20,13 +21,13 @@ Warning flags ============= Each compiler detects a different set of potential errors. The baseline will -be gcc -Wall -W -Wextra. Ideally, a complete set would be nice: +be gcc -Wall -W -Wextra. Ideally, a complete set would be nice:: --W -Wall -Wextra -Wstrict-prototypes -Wmissing-prototypes -Waggregate-return --Wcast-align -Wcast-qual -Wnested-externs -Wshadow -Wbad-function-cast --Wwrite-strings " + -W -Wall -Wextra -Wstrict-prototypes -Wmissing-prototypes -Waggregate-return + -Wcast-align -Wcast-qual -Wnested-externs -Wshadow -Wbad-function-cast + -Wwrite-strings " -Intel compiler, VS with /W3 /Wall, Sun compilers have extra warnings too. +Intel compiler, VS with ``/W3 /Wall``, Sun compilers have extra warnings too. Kind of warnings ================ @@ -46,27 +47,29 @@ solve it is to tag the function argument with a macro NPY_UNUSED. This macro uses compiler specific code to tag the variable, and mangle it such as it is not possible to use it accidentally once it is tagged. -The code to apply compiler specific option could be: +The code to apply compiler specific option could be:: -#if defined(__GNUC__) - #define __COMP_NPY_UNUSED __attribute__ ((__unused__)) -# elif defined(__ICC) - #define __COMP_NPY_UNUSED __attribute__ ((__unused__)) -#else - #define __COMP_NPY_UNUSED -#endif + #if defined(__GNUC__) + #define __COMP_NPY_UNUSED __attribute__ ((__unused__)) + # elif defined(__ICC) + #define __COMP_NPY_UNUSED __attribute__ ((__unused__)) + #else + #define __COMP_NPY_UNUSED + #endif -The variable mangling would be: +The variable mangling would be:: -#define NPY_UNUSED(x) (__NPY_UNUSED_TAGGED ## x) __COMP_NPY_UNUSED + #define NPY_UNUSED(x) (__NPY_UNUSED_TAGGED ## x) __COMP_NPY_UNUSED -When applied to a variable, one would get: +When applied to a variable, one would get:: -int foo(int * NPY_UNUSED(dummy)) + int foo(int * NPY_UNUSED(dummy)) expanded to -int foo(int * __NPY_UNUSED_TAGGEDdummy __COMP_NPY_UNUSED) +:: + + int foo(int * __NPY_UNUSED_TAGGEDdummy __COMP_NPY_UNUSED) Thus avoiding any accidental use of the variable. The mangling is pure C, and thuse portable. The per-variable warning disabling is compiler specific. diff --git a/doc/neps/nep-0003-math_config_clean.rst b/doc/neps/nep-0003-math_config_clean.rst index 27c0adfa1..5af907437 100644 --- a/doc/neps/nep-0003-math_config_clean.rst +++ b/doc/neps/nep-0003-math_config_clean.rst @@ -5,6 +5,7 @@ Cleaning the math configuration of numpy.core :Author: David Cournapeau :Contact: david@ar.media.kyoto-u.ac.jp :Date: 2008-09-04 +:Status: Deferred Executive summary ================= diff --git a/doc/neps/nep-0004-datetime-proposal3.rst b/doc/neps/nep-0004-datetime-proposal3.rst index fcfb39e54..46d8e314b 100644 --- a/doc/neps/nep-0004-datetime-proposal3.rst +++ b/doc/neps/nep-0004-datetime-proposal3.rst @@ -7,7 +7,7 @@ :Author: Ivan Vilata i Balaguer :Contact: ivan@selidor.net :Date: 2008-07-30 - +:Status: Deferred Executive summary ================= diff --git a/doc/neps/nep-0005-generalized-ufuncs.rst b/doc/neps/nep-0005-generalized-ufuncs.rst index 98e436990..54b2b370e 100644 --- a/doc/neps/nep-0005-generalized-ufuncs.rst +++ b/doc/neps/nep-0005-generalized-ufuncs.rst @@ -2,6 +2,8 @@ Generalized Universal Functions =============================== +:Status: Final + There is a general need for looping over not only functions on scalars but also over functions on vectors (or arrays), as explained on http://scipy.org/scipy/numpy/wiki/GeneralLoopingFunctions. We propose diff --git a/doc/neps/nep-0006-newbugtracker.rst b/doc/neps/nep-0006-newbugtracker.rst index 5af633552..2b9344ed0 100644 --- a/doc/neps/nep-0006-newbugtracker.rst +++ b/doc/neps/nep-0006-newbugtracker.rst @@ -3,6 +3,7 @@ Replacing Trac with a different bug tracker =========================================== :Author: David Cournapeau, Stefan van der Walt +:Status: Deferred Some release managers of both numpy and scipy are becoming more and more dissatisfied with the current development workflow, in particular for bug diff --git a/doc/neps/nep-0007-datetime-proposal.rst b/doc/neps/nep-0007-datetime-proposal.rst index 76c361f4f..72d48d244 100644 --- a/doc/neps/nep-0007-datetime-proposal.rst +++ b/doc/neps/nep-0007-datetime-proposal.rst @@ -5,6 +5,7 @@ :Author: Travis Oliphant :Contact: oliphant@enthought.com :Date: 2009-06-09 +:Status: Final Revised only slightly from the third proposal by @@ -14,7 +15,6 @@ Revised only slightly from the third proposal by :Contact: ivan@selidor.net :Date: 2008-07-30 - Executive summary ================= diff --git a/doc/neps/nep-0008-groupby_additions.rst b/doc/neps/nep-0008-groupby_additions.rst index a86bdd642..fa02f2f9c 100644 --- a/doc/neps/nep-0008-groupby_additions.rst +++ b/doc/neps/nep-0008-groupby_additions.rst @@ -5,6 +5,7 @@ :Author: Travis Oliphant :Contact: oliphant@enthought.com :Date: 2010-04-27 +:Status: Deferred Executive summary @@ -22,9 +23,9 @@ Example Use Case ================ Suppose you have a NumPy structured array containing information about the number of purchases at several stores over multiple days. To be clear, the -structured array data-type is: +structured array data-type is:: -dt = [('year', i2), ('month', i1), ('day', i1), ('time', float), + dt = [('year', i2), ('month', i1), ('day', i1), ('time', float), ('store', i4), ('SKU', 'S6'), ('number', i4)] Suppose there is a 1-d NumPy array of this data-type and you would like @@ -98,14 +99,5 @@ reduceby:: Functions proposed ================== -segment:: - - -edges:: - - -.. Local Variables: -.. mode: rst -.. coding: utf-8 -.. fill-column: 72 -.. End: +- segment +- edges diff --git a/doc/neps/nep-0009-structured_array_extensions.rst b/doc/neps/nep-0009-structured_array_extensions.rst index a4248362c..695d0d516 100644 --- a/doc/neps/nep-0009-structured_array_extensions.rst +++ b/doc/neps/nep-0009-structured_array_extensions.rst @@ -2,6 +2,8 @@ Structured array extensions =========================== +:Status: Deferred + 1. Create with-style context that makes "named-columns" available as names in the namespace. with np.columns(array): diff --git a/doc/neps/nep-0010-new-iterator-ufunc.rst b/doc/neps/nep-0010-new-iterator-ufunc.rst index 7a9e7627c..7b388a974 100644 --- a/doc/neps/nep-0010-new-iterator-ufunc.rst +++ b/doc/neps/nep-0010-new-iterator-ufunc.rst @@ -5,6 +5,7 @@ Optimizing Iterator/UFunc Performance :Author: Mark Wiebe <mwwiebe@gmail.com> :Content-Type: text/x-rst :Created: 25-Nov-2010 +:Status: Final ***************** Table of Contents diff --git a/doc/neps/nep-0011-deferred-ufunc-evaluation.rst b/doc/neps/nep-0011-deferred-ufunc-evaluation.rst index b00c0dd2d..5f5de3518 100644 --- a/doc/neps/nep-0011-deferred-ufunc-evaluation.rst +++ b/doc/neps/nep-0011-deferred-ufunc-evaluation.rst @@ -5,6 +5,7 @@ Deferred UFunc Evaluation :Author: Mark Wiebe <mwwiebe@gmail.com> :Content-Type: text/x-rst :Created: 30-Nov-2010 +:Status: Deferred ******** Abstract diff --git a/doc/neps/nep-0012-missing-data.rst b/doc/neps/nep-0012-missing-data.rst index 00a6034f4..1553339f4 100644 --- a/doc/neps/nep-0012-missing-data.rst +++ b/doc/neps/nep-0012-missing-data.rst @@ -6,6 +6,7 @@ Missing Data Functionality in NumPy :Copyright: Copyright 2011 by Enthought, Inc :License: CC By-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0/) :Date: 2011-06-23 +:Status: Deferred ***************** Table of Contents diff --git a/doc/neps/nep-0013-ufunc-overrides.rst b/doc/neps/nep-0013-ufunc-overrides.rst index 90869e1ac..c97b69023 100644 --- a/doc/neps/nep-0013-ufunc-overrides.rst +++ b/doc/neps/nep-0013-ufunc-overrides.rst @@ -1,5 +1,3 @@ -.. _neps.ufunc-overrides: - ================================= A Mechanism for Overriding Ufuncs ================================= @@ -19,6 +17,8 @@ A Mechanism for Overriding Ufuncs :Author: Stephan Hoyer :Date: 2017-03-31 +:Status: Final + Executive summary ================= @@ -154,8 +154,8 @@ Here: - *ufunc* is the ufunc object that was called. - *method* is a string indicating how the Ufunc was called, either ``"__call__"`` to indicate it was called directly, or one of its - :ref:`methods<ufuncs.methods>`: ``"reduce"``, ``"accumulate"``, - ``"reduceat"``, ``"outer"``, or ``"at"``. + methods: ``"reduce"``, ``"accumulate"``, ``"reduceat"``, ``"outer"``, + or ``"at"``. - *inputs* is a tuple of the input arguments to the ``ufunc`` - *kwargs* contains any optional or keyword arguments passed to the function. This includes any ``out`` arguments, which are always diff --git a/doc/neps/nep-0014-dropping-python2.7-proposal.rst b/doc/neps/nep-0014-dropping-python2.7-proposal.rst index 3cfe50bd0..6cfd4707f 100644 --- a/doc/neps/nep-0014-dropping-python2.7-proposal.rst +++ b/doc/neps/nep-0014-dropping-python2.7-proposal.rst @@ -2,6 +2,9 @@ Plan for dropping Python 2.7 support ==================================== +:Status: Accepted +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2017-November/077419.html + The Python core team plans to stop supporting Python 2 in 2020. The NumPy project has supported both Python 2 and Python 3 in parallel since 2010, and has found that supporting Python 2 is an increasing burden on our limited diff --git a/doc/neps/nep-template.rst b/doc/neps/nep-template.rst index d51ad3688..56b06049e 100644 --- a/doc/neps/nep-template.rst +++ b/doc/neps/nep-template.rst @@ -4,7 +4,7 @@ NEP Template and Instructions :Author: <list of authors' real names and optionally, email addresses> :Status: <Draft | Active | Accepted | Deferred | Rejected | Withdrawn | Final | Superseded> -:Type: <Standards Track | Informational | Process> +:Type: <Standards Track | Process> :Created: <date created on, in yyyy-mm-dd format> diff --git a/doc/neps/tools/build_index.py b/doc/neps/tools/build_index.py new file mode 100644 index 000000000..65225c995 --- /dev/null +++ b/doc/neps/tools/build_index.py @@ -0,0 +1,99 @@ +""" +Scan the directory of nep files and extract their metadata. The +metadata is passed to Jinja for filling out `index.rst.tmpl`. +""" + +import os +import sys +import jinja2 +import glob +import re + + +def render(tpl_path, context): + path, filename = os.path.split(tpl_path) + return jinja2.Environment( + loader=jinja2.FileSystemLoader(path or './') + ).get_template(filename).render(context) + +def nep_metadata(): + ignore = ('nep-template.rst') + sources = sorted(glob.glob(r'nep-*.rst')) + sources = [s for s in sources if not s in ignore] + + meta_re = r':([a-zA-Z\-]*): (.*)' + + neps = {} + print('Loading metadata for:') + for source in sources: + print(f' - {source}') + nr = int(re.match(r'nep-([0-9]{4}).*\.rst', source).group(1)) + + with open(source) as f: + lines = f.readlines() + tags = [re.match(meta_re, line) for line in lines] + tags = [match.groups() for match in tags if match is not None] + tags = {tag[0]: tag[1] for tag in tags} + + # We could do a clever regexp, but for now just assume the title is + # the second line of the document + tags['Title'] = lines[1].strip() + tags['Filename'] = source + + + if tags['Status'] in ('Accepted', 'Rejected', 'Withdrawn'): + if not 'Resolution' in tags: + raise RuntimeError( + f'NEP {nr} is Accepted/Rejected/Withdrawn but ' + 'has no Resolution tag' + ) + + neps[nr] = tags + + # Now that we have all of the NEP metadata, do some global consistency + # checks + + for nr, tags in neps.items(): + if tags['Status'] == 'Superseded': + if not 'Replaced-By' in tags: + raise RuntimeError( + f'NEP {nr} has been Superseded, but has no Replaced-By tag' + ) + + replaced_by = int(tags['Replaced-By']) + replacement_nep = neps[replaced_by] + + if not 'Replaces' in replacement_nep: + raise RuntimeError( + f'NEP {nr} is superseded by {replaced_by}, but that NEP has ' + f"no Replaces tag." + ) + + if not int(replacement_nep['Replaces']) == nr: + raise RuntimeError( + f'NEP {nr} is superseded by {replaced_by}, but that NEP has a ' + f"Replaces tag of `{replacement_nep['Replaces']}`." + ) + + if 'Replaces' in tags: + replaced_nep = int(tags['Replaces']) + replaced_nep_tags = neps[replaced_nep] + if not replaced_nep_tags['Status'] == 'Superseded': + raise RuntimeError( + f'NEP {nr} replaces {replaced_nep}, but that NEP has not ' + f'been set to Superseded' + ) + + return {'neps': neps} + + +infile = 'index.rst.tmpl' +outfile = 'index.rst' + +meta = nep_metadata() + +print(f'Compiling {infile} -> {outfile}') +index = render(infile, meta) + +with open(outfile, 'w') as f: + f.write(index) diff --git a/doc/release/1.14.2-notes.rst b/doc/release/1.14.2-notes.rst new file mode 100644 index 000000000..3f47cb5f5 --- /dev/null +++ b/doc/release/1.14.2-notes.rst @@ -0,0 +1,40 @@ +========================== +NumPy 1.14.2 Release Notes +========================== + +This is a bugfix release for some bugs reported following the 1.14.1 release. The major +problems dealt with are as follows. + +* Residual bugs in the new array printing functionality. +* Regression resulting in a relocation problem with shared library. +* Improved PyPy compatibility. + +The Python versions supported in this release are 2.7 and 3.4 - 3.6. The Python +3.6 wheels available from PIP are built with Python 3.6.2 and should be +compatible with all previous versions of Python 3.6. The source releases were +cythonized with Cython 0.26.1, which is known to **not** support the upcoming +Python 3.7 release. People who wish to run Python 3.7 should check out the +NumPy repo and try building with the, as yet, unreleased master branch of +Cython. + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Allan Haldane +* Charles Harris +* Eric Wieser +* Pauli Virtanen + +Pull requests merged +==================== + +A total of 5 pull requests were merged for this release. + +* `#10674 <https://github.com/numpy/numpy/pull/10674>`__: BUG: Further back-compat fix for subclassed array repr +* `#10725 <https://github.com/numpy/numpy/pull/10725>`__: BUG: dragon4 fractional output mode adds too many trailing zeros +* `#10726 <https://github.com/numpy/numpy/pull/10726>`__: BUG: Fix f2py generated code to work on PyPy +* `#10727 <https://github.com/numpy/numpy/pull/10727>`__: BUG: Fix missing NPY_VISIBILITY_HIDDEN on npy_longdouble_to_PyLong +* `#10729 <https://github.com/numpy/numpy/pull/10729>`__: DOC: Create 1.14.2 notes and changelog. diff --git a/doc/release/1.15.0-notes.rst b/doc/release/1.15.0-notes.rst index 4d19a953d..2a8c57351 100644 --- a/doc/release/1.15.0-notes.rst +++ b/doc/release/1.15.0-notes.rst @@ -19,9 +19,11 @@ New functions for the scope of the ``with`` block:: >>> with np.printoptions(precision=2): - ... print(np.array([2.0])) / 3 + ... print(np.array([2.0]) / 3) [0.67] + * `np.histogram_bin_edges`, a function to get the edges of the bins used by a histogram + without needing to calculate the histogram. Deprecations ============ @@ -44,6 +46,15 @@ Future Changes Compatibility notes =================== +``np.ma.notmasked_contiguous`` and ``np.ma.flatnotmasked_contiguous`` always return lists +----------------------------------------------------------------------------------------- +This was always the documented behavior, but in reality the result used to be +any of slice, None, or list. + +All downstream users seem to use detect the `None` result from +``flatnotmasked_contiguous`` and replace it with ``[]``. +These callers will continue to work as before. + C API changes ============= @@ -71,6 +82,10 @@ Creating a full iOS-compatible NumPy package requires building for the 5 architectures supported by iOS (i386, x86_64, armv7, armv7s and arm64), and combining these 5 compiled builds products into a single "fat" binary. +Build system +------------ +Added experimental support for the 64-bit RISC-V architecture. + Improvements ============ @@ -154,5 +169,19 @@ This change makes it easier to write Python 2/3 compatible code using ``from __future__ import unicode_literals``, which previously would cause string literal field names to raise a TypeError in Python 2. +``sort`` functions accept ``kind='stable'`` +------------------------------------------- +Up until now, to perform a stable sort on the data, the user must do:: + + >>> np.sort([5, 2, 6, 2, 1], kind='mergesort') + [1, 2, 2, 5, 6] + +because merge sort is the only stable sorting algorithm available in +NumPy. However, having kind='mergesort' does not make it explicit that +the user wants to perform a stable sort thus harming the readability. + +This change allows the user to specify kind='stable' thus clarifying +the intent. + Changes ======= diff --git a/doc/source/dev/development_environment.rst b/doc/source/dev/development_environment.rst index e6df9803c..f4c6f3ec7 100644 --- a/doc/source/dev/development_environment.rst +++ b/doc/source/dev/development_environment.rst @@ -203,7 +203,7 @@ typically packaged as ``python-dbg``) is highly recommended. .. _DebuggingWithGdb: https://wiki.python.org/moin/DebuggingWithGdb -.. _tox: http://tox.testrun.org +.. _tox: https://tox.readthedocs.io/ .. _virtualenv: http://www.virtualenv.org/ diff --git a/doc/source/release.rst b/doc/source/release.rst index 0bb759dce..224436b82 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -3,6 +3,7 @@ Release Notes ************* .. include:: ../release/1.15.0-notes.rst +.. include:: ../release/1.14.2-notes.rst .. include:: ../release/1.14.1-notes.rst .. include:: ../release/1.14.0-notes.rst .. include:: ../release/1.13.3-notes.rst diff --git a/doc/source/user/building.rst b/doc/source/user/building.rst index b98f89c2d..76eb48487 100644 --- a/doc/source/user/building.rst +++ b/doc/source/user/building.rst @@ -132,6 +132,8 @@ Supplying additional compiler flags Additional compiler flags can be supplied by setting the ``OPT``, ``FOPT`` (for Fortran), and ``CC`` environment variables. +When providing options that should improve the performance of the code ensure +that you also set ``-DNDEBUG`` so that debugging code is not executed. Building with ATLAS support diff --git a/doc/summarize.py b/doc/summarize.py index dbadb30b3..cfce2713e 100755 --- a/doc/summarize.py +++ b/doc/summarize.py @@ -8,7 +8,12 @@ Show a summary about which NumPy functions are documented and which are not. from __future__ import division, absolute_import, print_function import os, glob, re, sys, inspect, optparse -import collections +try: + # Accessing collections abstract classes from collections + # has been deprecated since Python 3.3 + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc sys.path.append(os.path.join(os.path.dirname(__file__), 'sphinxext')) from sphinxext.phantom_import import import_phantom_module @@ -136,7 +141,7 @@ def get_undocumented(documented, module, module_name=None, skip=[]): if full_name in skip: continue if full_name.startswith('numpy.') and full_name[6:] in skip: continue - if not (inspect.ismodule(obj) or isinstance(obj, collections.Callable) or inspect.isclass(obj)): + if not (inspect.ismodule(obj) or isinstance(obj, collections_abc.Callable) or inspect.isclass(obj)): continue if full_name not in documented: diff --git a/numpy/__init__.py b/numpy/__init__.py index db99294bc..db7bc0368 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -150,7 +150,6 @@ else: # no-one else in the world is using it (though I hope not) from .testing import Tester, _numpy_tester test = _numpy_tester().test - bench = _numpy_tester().bench # Allow distributors to run custom init code from . import _distributor_init diff --git a/numpy/add_newdocs.py b/numpy/add_newdocs.py index 38da1f0b3..0a7b2b13d 100644 --- a/numpy/add_newdocs.py +++ b/numpy/add_newdocs.py @@ -3060,9 +3060,17 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('size', """ Number of elements in the array. - Equivalent to ``np.prod(a.shape)``, i.e., the product of the array's + Equal to ``np.prod(a.shape)``, i.e., the product of the array's dimensions. + Notes + ----- + `a.size` returns a standard arbitrary precision Python integer. This + may not be the case with other methods of obtaining the same value + (like the suggested ``np.prod(a.shape)``, which returns an instance + of ``np.int_``), and may be relevant if the value is used further in + calculations that may overflow a fixed size integer type. + Examples -------- >>> x = np.zeros((3, 5, 2), dtype=np.complex128) @@ -4486,7 +4494,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('sort', axis : int, optional Axis along which to sort. Default is -1, which means sort along the last axis. - kind : {'quicksort', 'mergesort', 'heapsort'}, optional + kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional Sorting algorithm. Default is 'quicksort'. order : str or list of str, optional When `a` is an array with fields defined, this argument specifies diff --git a/numpy/compat/setup.py b/numpy/compat/setup.py index 26161f330..882857428 100644 --- a/numpy/compat/setup.py +++ b/numpy/compat/setup.py @@ -1,10 +1,10 @@ -#!/usr/bin/env python from __future__ import division, print_function - def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration + config = Configuration('compat', parent_package, top_path) + config.add_data_dir('tests') return config if __name__ == '__main__': diff --git a/numpy/conftest.py b/numpy/conftest.py index 15985a75b..ce985d079 100644 --- a/numpy/conftest.py +++ b/numpy/conftest.py @@ -8,7 +8,7 @@ import pytest import numpy import importlib -from numpy.core.multiarray_tests import get_fpu_mode +from numpy.core._multiarray_tests import get_fpu_mode _old_fpu_mode = None @@ -21,7 +21,7 @@ def pytest_itemcollected(item): Check FPU precision mode was not changed during test collection. The clumsy way we do it here is mainly necessary because numpy - still uses yield tests, which can execute code at test collection + still uses yield tests, which can execute code at test collection time. """ global _old_fpu_mode diff --git a/numpy/core/__init__.py b/numpy/core/__init__.py index 6db484de4..264324503 100644 --- a/numpy/core/__init__.py +++ b/numpy/core/__init__.py @@ -73,7 +73,6 @@ __all__ += einsumfunc.__all__ from numpy.testing import _numpy_tester test = _numpy_tester().test -bench = _numpy_tester().bench # Make it possible so that ufuncs can be pickled # Here are the loading and unloading functions diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index ebcf864ea..1d3550e06 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -966,7 +966,7 @@ def make_arrays(funcdict): for vt in t.simd: code2list.append(textwrap.dedent("""\ #ifdef HAVE_ATTRIBUTE_TARGET_{ISA} - if (NPY_CPU_SUPPORTS_{ISA}) {{ + if (npy_cpu_supports("{ISA}")) {{ {fname}_functions[{idx}] = {type}_{fname}_{isa}; }} #endif @@ -1073,7 +1073,7 @@ def make_code(funcdict, filename): Please make changes to the code generator program (%s) **/ - + #include "cpuid.h" %s static int diff --git a/numpy/core/code_generators/ufunc_docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py index 75dee7084..c7e5cf600 100644 --- a/numpy/core/code_generators/ufunc_docstrings.py +++ b/numpy/core/code_generators/ufunc_docstrings.py @@ -18,23 +18,36 @@ def get(name): return docdict.get(name) # common parameter text to all ufuncs -_params_text = textwrap.dedent(""" - out : ndarray, None, or tuple of ndarray and None, optional - A location into which the result is stored. If provided, it must have - a shape that the inputs broadcast to. If not provided or `None`, - a freshly-allocated array is returned. A tuple (possible only as a - keyword argument) must have length equal to the number of outputs. - where : array_like, optional - Values of True indicate to calculate the ufunc at that position, values - of False indicate to leave the value in the output alone. - **kwargs - For other keyword-only arguments, see the - :ref:`ufunc docs <ufuncs.kwargs>`. -""").strip() +subst = { + 'PARAMS': textwrap.dedent(""" + out : ndarray, None, or tuple of ndarray and None, optional + A location into which the result is stored. If provided, it must have + a shape that the inputs broadcast to. If not provided or `None`, + a freshly-allocated array is returned. A tuple (possible only as a + keyword argument) must have length equal to the number of outputs. + where : array_like, optional + Values of True indicate to calculate the ufunc at that position, values + of False indicate to leave the value in the output alone. + **kwargs + For other keyword-only arguments, see the + :ref:`ufunc docs <ufuncs.kwargs>`. + """).strip(), + 'OUT_SCALAR_1': "This is a scalar if `x` is a scalar.", + 'OUT_SCALAR_2': "This is a scalar if both `x1` and `x2` are scalars.", +} def add_newdoc(place, name, doc): doc = textwrap.dedent(doc).strip() - doc = doc.replace('$PARAMS', _params_text) + + if name[0] != '_': + if '\nx :' in doc: + assert '$OUT_SCALAR_1' in doc, "in {}".format(name) + elif '\nx2 :' in doc or '\nx1, x2 :' in doc: + assert '$OUT_SCALAR_2' in doc, "in {}".format(name) + else: + assert False, "Could not detect number of inputs in {}".format(name) + for k, v in subst.items(): + doc = doc.replace('$' + k, v) docdict['.'.join((place, name))] = doc @@ -57,6 +70,7 @@ add_newdoc('numpy.core.umath', 'absolute', An ndarray containing the absolute value of each element in `x`. For complex input, ``a + ib``, the absolute value is :math:`\\sqrt{ a^2 + b^2 }`. + $OUT_SCALAR_1 Examples -------- @@ -97,8 +111,8 @@ add_newdoc('numpy.core.umath', 'add', Returns ------- add : ndarray or scalar - The sum of `x1` and `x2`, element-wise. Returns a scalar if - both `x1` and `x2` are scalars. + The sum of `x1` and `x2`, element-wise. + $OUT_SCALAR_2 Notes ----- @@ -134,9 +148,8 @@ add_newdoc('numpy.core.umath', 'arccos', ------- angle : ndarray The angle of the ray intersecting the unit circle at the given - `x`-coordinate in radians [0, pi]. If `x` is a scalar then a - scalar is returned, otherwise an array of the same shape as `x` - is returned. + `x`-coordinate in radians [0, pi]. + $OUT_SCALAR_1 See Also -------- @@ -194,6 +207,7 @@ add_newdoc('numpy.core.umath', 'arccosh', ------- arccosh : ndarray Array of the same shape as `x`. + $OUT_SCALAR_1 See Also -------- @@ -244,8 +258,8 @@ add_newdoc('numpy.core.umath', 'arcsin', ------- angle : ndarray The inverse sine of each element in `x`, in radians and in the - closed interval ``[-pi/2, pi/2]``. If `x` is a scalar, a scalar - is returned, otherwise an array. + closed interval ``[-pi/2, pi/2]``. + $OUT_SCALAR_1 See Also -------- @@ -296,8 +310,9 @@ add_newdoc('numpy.core.umath', 'arcsinh', Returns ------- - out : ndarray + out : ndarray or scalar Array of the same shape as `x`. + $OUT_SCALAR_1 Notes ----- @@ -342,10 +357,10 @@ add_newdoc('numpy.core.umath', 'arctan', Returns ------- - out : ndarray + out : ndarray or scalar Out has the same shape as `x`. Its real part is in ``[-pi/2, pi/2]`` (``arctan(+/-inf)`` returns ``+/-pi/2``). - It is a scalar if `x` is a scalar. + $OUT_SCALAR_1 See Also -------- @@ -424,6 +439,7 @@ add_newdoc('numpy.core.umath', 'arctan2', ------- angle : ndarray Array of angles in radians, in the range ``[-pi, pi]``. + $OUT_SCALAR_2 See Also -------- @@ -490,8 +506,9 @@ add_newdoc('numpy.core.umath', 'arctanh', Returns ------- - out : ndarray + out : ndarray or scalar Array of the same shape as `x`. + $OUT_SCALAR_1 See Also -------- @@ -543,8 +560,9 @@ add_newdoc('numpy.core.umath', 'bitwise_and', Returns ------- - out : array_like + out : ndarray or scalar Result. + $OUT_SCALAR_2 See Also -------- @@ -595,8 +613,9 @@ add_newdoc('numpy.core.umath', 'bitwise_or', Returns ------- - out : array_like + out : ndarray or scalar Result. + $OUT_SCALAR_2 See Also -------- @@ -652,8 +671,9 @@ add_newdoc('numpy.core.umath', 'bitwise_xor', Returns ------- - out : array_like + out : ndarray or scalar Result. + $OUT_SCALAR_2 See Also -------- @@ -703,6 +723,7 @@ add_newdoc('numpy.core.umath', 'ceil', ------- y : ndarray or scalar The ceiling of each element in `x`, with `float` dtype. + $OUT_SCALAR_1 See Also -------- @@ -734,6 +755,7 @@ add_newdoc('numpy.core.umath', 'trunc', ------- y : ndarray or scalar The truncated value of each element in `x`. + $OUT_SCALAR_1 See Also -------- @@ -768,6 +790,7 @@ add_newdoc('numpy.core.umath', 'conjugate', ------- y : ndarray The complex conjugate of `x`, with same dtype as `y`. + $OUT_SCALAR_1 Examples -------- @@ -795,6 +818,7 @@ add_newdoc('numpy.core.umath', 'cos', ------- y : ndarray The corresponding cosine values. + $OUT_SCALAR_1 Notes ----- @@ -838,8 +862,9 @@ add_newdoc('numpy.core.umath', 'cosh', Returns ------- - out : ndarray + out : ndarray or scalar Output array of same shape as `x`. + $OUT_SCALAR_1 Examples -------- @@ -870,6 +895,7 @@ add_newdoc('numpy.core.umath', 'degrees', y : ndarray of floats The corresponding degree values; if `out` was supplied this is a reference to it. + $OUT_SCALAR_1 See Also -------- @@ -905,6 +931,7 @@ add_newdoc('numpy.core.umath', 'rad2deg', ------- y : ndarray The corresponding angle in degrees. + $OUT_SCALAR_1 See Also -------- @@ -946,8 +973,9 @@ add_newdoc('numpy.core.umath', 'heaviside', Returns ------- - out : ndarray + out : ndarray or scalar The output array, element-wise Heaviside step function of `x1`. + $OUT_SCALAR_2 Notes ----- @@ -981,8 +1009,8 @@ add_newdoc('numpy.core.umath', 'divide', Returns ------- y : ndarray or scalar - The quotient ``x1/x2``, element-wise. Returns a scalar if - both ``x1`` and ``x2`` are scalars. + The quotient ``x1/x2``, element-wise. + $OUT_SCALAR_2 See Also -------- @@ -1050,7 +1078,8 @@ add_newdoc('numpy.core.umath', 'equal', Returns ------- out : ndarray or bool - Output array of bools, or a single bool if x1 and x2 are scalars. + Output array of bools. + $OUT_SCALAR_2 See Also -------- @@ -1081,8 +1110,9 @@ add_newdoc('numpy.core.umath', 'exp', Returns ------- - out : ndarray + out : ndarray or scalar Output array, element-wise exponential of `x`. + $OUT_SCALAR_1 See Also -------- @@ -1145,8 +1175,9 @@ add_newdoc('numpy.core.umath', 'exp2', Returns ------- - out : ndarray + out : ndarray or scalar Element-wise 2 to the power `x`. + $OUT_SCALAR_1 See Also -------- @@ -1177,8 +1208,9 @@ add_newdoc('numpy.core.umath', 'expm1', Returns ------- - out : ndarray + out : ndarray or scalar Element-wise exponential minus one: ``out = exp(x) - 1``. + $OUT_SCALAR_1 See Also -------- @@ -1222,6 +1254,7 @@ add_newdoc('numpy.core.umath', 'fabs', ------- y : ndarray or scalar The absolute values of `x`, the returned values are always floats. + $OUT_SCALAR_1 See Also -------- @@ -1253,6 +1286,7 @@ add_newdoc('numpy.core.umath', 'floor', ------- y : ndarray or scalar The floor of each element in `x`. + $OUT_SCALAR_1 See Also -------- @@ -1291,7 +1325,7 @@ add_newdoc('numpy.core.umath', 'floor_divide', ------- y : ndarray y = floor(`x1`/`x2`) - + $OUT_SCALAR_2 See Also -------- @@ -1322,15 +1356,16 @@ add_newdoc('numpy.core.umath', 'fmod', Parameters ---------- x1 : array_like - Dividend. + Dividend. x2 : array_like - Divisor. + Divisor. $PARAMS Returns ------- y : array_like - The remainder of the division of `x1` by `x2`. + The remainder of the division of `x1` by `x2`. + $OUT_SCALAR_2 See Also -------- @@ -1381,7 +1416,8 @@ add_newdoc('numpy.core.umath', 'greater', Returns ------- out : bool or ndarray of bool - Array of bools, or a single bool if `x1` and `x2` are scalars. + Array of bools. + $OUT_SCALAR_2 See Also @@ -1417,7 +1453,8 @@ add_newdoc('numpy.core.umath', 'greater_equal', Returns ------- out : bool or ndarray of bool - Array of bools, or a single bool if `x1` and `x2` are scalars. + Array of bools. + $OUT_SCALAR_2 See Also -------- @@ -1449,6 +1486,7 @@ add_newdoc('numpy.core.umath', 'hypot', ------- z : ndarray The hypotenuse of the triangle(s). + $OUT_SCALAR_2 Examples -------- @@ -1489,8 +1527,9 @@ add_newdoc('numpy.core.umath', 'invert', Returns ------- - out : array_like + out : ndarray or scalar Result. + $OUT_SCALAR_1 See Also -------- @@ -1562,15 +1601,9 @@ add_newdoc('numpy.core.umath', 'isfinite', Returns ------- y : ndarray, bool - For scalar input, the result is a new boolean with value True - if the input is finite; otherwise the value is False (input is - either positive infinity, negative infinity or Not a Number). - - For array input, the result is a boolean array with the same - dimensions as the input and the values are True if the - corresponding element of the input is finite; otherwise the values - are False (element is either positive infinity, negative infinity - or Not a Number). + True where ``x`` is not positive infinity, negative infinity, + or NaN; false otherwise. + $OUT_SCALAR_1 See Also -------- @@ -1628,18 +1661,8 @@ add_newdoc('numpy.core.umath', 'isinf', Returns ------- y : bool (scalar) or boolean ndarray - For scalar input, the result is a new boolean with value True if - the input is positive or negative infinity; otherwise the value is - False. - - For array input, the result is a boolean array with the same shape - as the input and the values are True where the corresponding - element of the input is positive or negative infinity; elsewhere - the values are False. If a second argument was supplied the result - is stored there. If the type of that array is a numeric type the - result is represented as zeros and ones, if the type is boolean - then as False and True, respectively. The return value `y` is then - a reference to that array. + True where ``x`` is positive or negative infinity, false otherwise. + $OUT_SCALAR_1 See Also -------- @@ -1687,13 +1710,8 @@ add_newdoc('numpy.core.umath', 'isnan', Returns ------- y : ndarray or bool - For scalar input, the result is a new boolean with value True if - the input is NaN; otherwise the value is False. - - For array input, the result is a boolean array of the same - dimensions as the input and the values are True if the - corresponding element of the input is NaN; otherwise the values are - False. + True where ``x`` is NaN, false otherwise. + $OUT_SCALAR_1 See Also -------- @@ -1728,13 +1746,8 @@ add_newdoc('numpy.core.umath', 'isnat', Returns ------- y : ndarray or bool - For scalar input, the result is a new boolean with value True if - the input is NaT; otherwise the value is False. - - For array input, the result is a boolean array of the same - dimensions as the input and the values are True if the - corresponding element of the input is NaT; otherwise the values are - False. + True where ``x`` is NaT, false otherwise. + $OUT_SCALAR_1 See Also -------- @@ -1771,6 +1784,7 @@ add_newdoc('numpy.core.umath', 'left_shift', ------- out : array of integer type Return `x1` with bits shifted `x2` times to the left. + $OUT_SCALAR_2 See Also -------- @@ -1807,7 +1821,8 @@ add_newdoc('numpy.core.umath', 'less', Returns ------- out : bool or ndarray of bool - Array of bools, or a single bool if `x1` and `x2` are scalars. + Array of bools. + $OUT_SCALAR_2 See Also -------- @@ -1835,7 +1850,8 @@ add_newdoc('numpy.core.umath', 'less_equal', Returns ------- out : bool or ndarray of bool - Array of bools, or a single bool if `x1` and `x2` are scalars. + Array of bools. + $OUT_SCALAR_2 See Also -------- @@ -1866,6 +1882,7 @@ add_newdoc('numpy.core.umath', 'log', ------- y : ndarray The natural logarithm of `x`, element-wise. + $OUT_SCALAR_1 See Also -------- @@ -1914,6 +1931,7 @@ add_newdoc('numpy.core.umath', 'log10', y : ndarray The logarithm to the base 10 of `x`, element-wise. NaNs are returned where x is negative. + $OUT_SCALAR_1 See Also -------- @@ -1961,6 +1979,7 @@ add_newdoc('numpy.core.umath', 'log2', ------- y : ndarray Base-2 logarithm of `x`. + $OUT_SCALAR_1 See Also -------- @@ -2015,6 +2034,7 @@ add_newdoc('numpy.core.umath', 'logaddexp', ------- result : ndarray Logarithm of ``exp(x1) + exp(x2)``. + $OUT_SCALAR_2 See Also -------- @@ -2056,6 +2076,7 @@ add_newdoc('numpy.core.umath', 'logaddexp2', ------- result : ndarray Base-2 logarithm of ``2**x1 + 2**x2``. + $OUT_SCALAR_2 See Also -------- @@ -2093,6 +2114,7 @@ add_newdoc('numpy.core.umath', 'log1p', ------- y : ndarray Natural logarithm of `1 + x`, element-wise. + $OUT_SCALAR_1 See Also -------- @@ -2146,6 +2168,7 @@ add_newdoc('numpy.core.umath', 'logical_and', y : ndarray or bool Boolean result with the same shape as `x1` and `x2` of the logical AND operation on corresponding elements of `x1` and `x2`. + $OUT_SCALAR_2 See Also -------- @@ -2180,6 +2203,7 @@ add_newdoc('numpy.core.umath', 'logical_not', y : bool or ndarray of bool Boolean result with the same shape as `x` of the NOT operation on elements of `x`. + $OUT_SCALAR_1 See Also -------- @@ -2214,6 +2238,7 @@ add_newdoc('numpy.core.umath', 'logical_or', y : ndarray or bool Boolean result with the same shape as `x1` and `x2` of the logical OR operation on elements of `x1` and `x2`. + $OUT_SCALAR_2 See Also -------- @@ -2250,6 +2275,7 @@ add_newdoc('numpy.core.umath', 'logical_xor', Boolean result of the logical XOR operation applied to the elements of `x1` and `x2`; the shape is determined by whether or not broadcasting of one or both arrays was required. + $OUT_SCALAR_2 See Also -------- @@ -2295,8 +2321,8 @@ add_newdoc('numpy.core.umath', 'maximum', Returns ------- y : ndarray or scalar - The maximum of `x1` and `x2`, element-wise. Returns scalar if - both `x1` and `x2` are scalars. + The maximum of `x1` and `x2`, element-wise. + $OUT_SCALAR_2 See Also -------- @@ -2354,8 +2380,8 @@ add_newdoc('numpy.core.umath', 'minimum', Returns ------- y : ndarray or scalar - The minimum of `x1` and `x2`, element-wise. Returns scalar if - both `x1` and `x2` are scalars. + The minimum of `x1` and `x2`, element-wise. + $OUT_SCALAR_2 See Also -------- @@ -2413,8 +2439,8 @@ add_newdoc('numpy.core.umath', 'fmax', Returns ------- y : ndarray or scalar - The maximum of `x1` and `x2`, element-wise. Returns scalar if - both `x1` and `x2` are scalars. + The maximum of `x1` and `x2`, element-wise. + $OUT_SCALAR_2 See Also -------- @@ -2471,8 +2497,8 @@ add_newdoc('numpy.core.umath', 'fmin', Returns ------- y : ndarray or scalar - The minimum of `x1` and `x2`, element-wise. Returns scalar if - both `x1` and `x2` are scalars. + The minimum of `x1` and `x2`, element-wise. + $OUT_SCALAR_2 See Also -------- @@ -2525,8 +2551,10 @@ add_newdoc('numpy.core.umath', 'modf', ------- y1 : ndarray Fractional part of `x`. + $OUT_SCALAR_1 y2 : ndarray Integral part of `x`. + $OUT_SCALAR_1 Notes ----- @@ -2560,7 +2588,8 @@ add_newdoc('numpy.core.umath', 'multiply', ------- y : ndarray The product of `x1` and `x2`, element-wise. Returns a scalar if - both `x1` and `x2` are scalars. + both `x1` and `x2` are scalars. + $OUT_SCALAR_2 Notes ----- @@ -2594,6 +2623,7 @@ add_newdoc('numpy.core.umath', 'negative', ------- y : ndarray or scalar Returned array or scalar: `y = -x`. + $OUT_SCALAR_1 Examples -------- @@ -2617,6 +2647,7 @@ add_newdoc('numpy.core.umath', 'positive', ------- y : ndarray or scalar Returned array or scalar: `y = +x`. + $OUT_SCALAR_1 Notes ----- @@ -2632,14 +2663,15 @@ add_newdoc('numpy.core.umath', 'not_equal', Parameters ---------- x1, x2 : array_like - Input arrays. + Input arrays. $PARAMS Returns ------- not_equal : ndarray bool, scalar bool - For each element in `x1, x2`, return True if `x1` is not equal - to `x2` and False otherwise. + For each element in `x1, x2`, return True if `x1` is not equal + to `x2` and False otherwise. + $OUT_SCALAR_2 See Also @@ -2688,6 +2720,7 @@ add_newdoc('numpy.core.umath', 'power', ------- y : ndarray The bases in `x1` raised to the exponents in `x2`. + $OUT_SCALAR_2 See Also -------- @@ -2746,6 +2779,7 @@ add_newdoc('numpy.core.umath', 'float_power', ------- y : ndarray The bases in `x1` raised to the exponents in `x2`. + $OUT_SCALAR_2 See Also -------- @@ -2793,6 +2827,7 @@ add_newdoc('numpy.core.umath', 'radians', ------- y : ndarray The corresponding radian values. + $OUT_SCALAR_1 See Also -------- @@ -2829,6 +2864,7 @@ add_newdoc('numpy.core.umath', 'deg2rad', ------- y : ndarray The corresponding angle in radians. + $OUT_SCALAR_1 See Also -------- @@ -2864,6 +2900,7 @@ add_newdoc('numpy.core.umath', 'reciprocal', ------- y : ndarray Return array. + $OUT_SCALAR_1 Notes ----- @@ -2914,7 +2951,7 @@ add_newdoc('numpy.core.umath', 'remainder', ------- y : ndarray The element-wise remainder of the quotient ``floor_divide(x1, x2)``. - Returns a scalar if both `x1` and `x2` are scalars. + $OUT_SCALAR_2 See Also -------- @@ -2959,8 +2996,10 @@ add_newdoc('numpy.core.umath', 'divmod', ------- out1 : ndarray Element-wise quotient resulting from floor division. + $OUT_SCALAR_2 out2 : ndarray Element-wise remainder from floor division. + $OUT_SCALAR_2 See Also -------- @@ -2996,6 +3035,7 @@ add_newdoc('numpy.core.umath', 'right_shift', ------- out : ndarray, int Return `x1` with bits shifted `x2` times to the right. + $OUT_SCALAR_2 See Also -------- @@ -3031,6 +3071,7 @@ add_newdoc('numpy.core.umath', 'rint', ------- out : ndarray or scalar Output array is same shape and type as `x`. + $OUT_SCALAR_1 See Also -------- @@ -3059,13 +3100,14 @@ add_newdoc('numpy.core.umath', 'sign', Parameters ---------- x : array_like - Input values. + Input values. $PARAMS Returns ------- y : ndarray - The sign of `x`. + The sign of `x`. + $OUT_SCALAR_1 Notes ----- @@ -3098,6 +3140,7 @@ add_newdoc('numpy.core.umath', 'signbit', ------- result : ndarray of bool Output array, or reference to `out` if that was supplied. + $OUT_SCALAR_1 Examples -------- @@ -3126,8 +3169,9 @@ add_newdoc('numpy.core.umath', 'copysign', Returns ------- - out : array_like + out : ndarray or scalar The values of `x1` with the sign of `x2`. + $OUT_SCALAR_2 Examples -------- @@ -3159,8 +3203,9 @@ add_newdoc('numpy.core.umath', 'nextafter', Returns ------- - out : array_like + out : ndarray or scalar The next representable values of `x1` in the direction of `x2`. + $OUT_SCALAR_2 Examples -------- @@ -3184,8 +3229,9 @@ add_newdoc('numpy.core.umath', 'spacing', Returns ------- - out : array_like - The spacing of values of `x1`. + out : ndarray or scalar + The spacing of values of `x`. + $OUT_SCALAR_1 Notes ----- @@ -3217,6 +3263,7 @@ add_newdoc('numpy.core.umath', 'sin', ------- y : array_like The sine of each element of x. + $OUT_SCALAR_1 See Also -------- @@ -3277,6 +3324,7 @@ add_newdoc('numpy.core.umath', 'sinh', ------- y : ndarray The corresponding hyperbolic sine values. + $OUT_SCALAR_1 Notes ----- @@ -3330,6 +3378,7 @@ add_newdoc('numpy.core.umath', 'sqrt', negative reals are calculated). If all of the elements in `x` are real, so is `y`, with negative elements returning ``nan``. If `out` was provided, `y` is a reference to it. + $OUT_SCALAR_1 See Also -------- @@ -3374,6 +3423,7 @@ add_newdoc('numpy.core.umath', 'cbrt', An array of the same shape as `x`, containing the cube cube-root of each element in `x`. If `out` was provided, `y` is a reference to it. + $OUT_SCALAR_1 Examples @@ -3395,9 +3445,9 @@ add_newdoc('numpy.core.umath', 'square', Returns ------- - out : ndarray + out : ndarray or scalar Element-wise `x*x`, of the same shape and dtype as `x`. - Returns scalar if `x` is a scalar. + $OUT_SCALAR_1 See Also -------- @@ -3425,8 +3475,8 @@ add_newdoc('numpy.core.umath', 'subtract', Returns ------- y : ndarray - The difference of `x1` and `x2`, element-wise. Returns a scalar if - both `x1` and `x2` are scalars. + The difference of `x1` and `x2`, element-wise. + $OUT_SCALAR_2 Notes ----- @@ -3455,13 +3505,14 @@ add_newdoc('numpy.core.umath', 'tan', Parameters ---------- x : array_like - Input array. + Input array. $PARAMS Returns ------- y : ndarray - The corresponding tangent values. + The corresponding tangent values. + $OUT_SCALAR_1 Notes ----- @@ -3509,6 +3560,7 @@ add_newdoc('numpy.core.umath', 'tanh', ------- y : ndarray The corresponding hyperbolic tangent values. + $OUT_SCALAR_1 Notes ----- @@ -3561,8 +3613,8 @@ add_newdoc('numpy.core.umath', 'true_divide', Returns ------- - out : ndarray - Result is scalar if both inputs are scalar, ndarray otherwise. + out : ndarray or scalar + $OUT_SCALAR_2 Notes ----- @@ -3614,9 +3666,12 @@ add_newdoc('numpy.core.umath', 'frexp', Returns ------- - (mantissa, exponent) : tuple of ndarrays, (float, int) - `mantissa` is a float array with values between -1 and 1. - `exponent` is an int array which represents the exponent of 2. + mantissa : ndarray + Floating values between -1 and 1. + $OUT_SCALAR_1 + exponent : ndarray + Integer exponents of 2. + $OUT_SCALAR_1 See Also -------- @@ -3659,6 +3714,7 @@ add_newdoc('numpy.core.umath', 'ldexp', ------- y : ndarray or scalar The result of ``x1 * 2**x2``. + $OUT_SCALAR_2 See Also -------- @@ -3695,6 +3751,7 @@ add_newdoc('numpy.core.umath', 'gcd', ------- y : ndarray or scalar The greatest common divisor of the absolute value of the inputs + $OUT_SCALAR_2 See Also -------- @@ -3724,6 +3781,7 @@ add_newdoc('numpy.core.umath', 'lcm', ------- y : ndarray or scalar The lowest common multiple of the absolute value of the inputs + $OUT_SCALAR_2 See Also -------- diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 10256c3c0..63279ffb3 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -764,7 +764,7 @@ def sort(a, axis=-1, kind='quicksort', order=None): axis : int or None, optional Axis along which to sort. If None, the array is flattened before sorting. The default is -1, which sorts along the last axis. - kind : {'quicksort', 'mergesort', 'heapsort'}, optional + kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional Sorting algorithm. Default is 'quicksort'. order : str or list of str, optional When `a` is an array with fields defined, this argument specifies @@ -794,13 +794,13 @@ def sort(a, axis=-1, kind='quicksort', order=None): order. The three available algorithms have the following properties: - =========== ======= ============= ============ ======= - kind speed worst case work space stable - =========== ======= ============= ============ ======= + =========== ======= ============= ============ ======== + kind speed worst case work space stable + =========== ======= ============= ============ ======== 'quicksort' 1 O(n^2) 0 no 'mergesort' 2 O(n*log(n)) ~n/2 yes 'heapsort' 3 O(n*log(n)) 0 no - =========== ======= ============= ============ ======= + =========== ======= ============= ============ ======== All the sort algorithms make temporary copies of the data when sorting along any but the last axis. Consequently, sorting along @@ -829,6 +829,10 @@ def sort(a, axis=-1, kind='quicksort', order=None): heapsort when it does not make enough progress. This makes its worst case O(n*log(n)). + 'stable' automatically choses the best stable sorting algorithm + for the data type being sorted. It is currently mapped to + merge sort. + Examples -------- >>> a = np.array([[1,4],[3,1]]) @@ -886,7 +890,7 @@ def argsort(a, axis=-1, kind='quicksort', order=None): axis : int or None, optional Axis along which to sort. The default is -1 (the last axis). If None, the flattened array is used. - kind : {'quicksort', 'mergesort', 'heapsort'}, optional + kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional Sorting algorithm. order : str or list of str, optional When `a` is an array with fields defined, this argument specifies diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h index b7634a930..5faff4385 100644 --- a/numpy/core/include/numpy/npy_common.h +++ b/numpy/core/include/numpy/npy_common.h @@ -101,22 +101,6 @@ #endif #endif -#ifdef HAVE___BUILTIN_CPU_SUPPORTS - #ifdef HAVE_ATTRIBUTE_TARGET_AVX2 - #define NPY_CPU_SUPPORTS_AVX2 __builtin_cpu_supports("avx2") - #else - #define NPY_CPU_SUPPORTS_AVX2 0 - #endif - #ifdef HAVE_ATTRIBUTE_TARGET_AVX - #define NPY_CPU_SUPPORTS_AVX __builtin_cpu_supports("avx") - #else - #define NPY_CPU_SUPPORTS_AVX 0 - #endif -#else - #define NPY_CPU_SUPPORTS_AVX 0 - #define NPY_CPU_SUPPORTS_AVX2 0 -#endif - #if defined(_MSC_VER) #define NPY_INLINE __inline #elif defined(__GNUC__) diff --git a/numpy/core/include/numpy/npy_cpu.h b/numpy/core/include/numpy/npy_cpu.h index 84653ea18..106ffa450 100644 --- a/numpy/core/include/numpy/npy_cpu.h +++ b/numpy/core/include/numpy/npy_cpu.h @@ -17,6 +17,7 @@ * NPY_CPU_SH_BE * NPY_CPU_ARCEL * NPY_CPU_ARCEB + * NPY_CPU_RISCV64 */ #ifndef _NPY_CPUARCH_H_ #define _NPY_CPUARCH_H_ @@ -82,6 +83,8 @@ #define NPY_CPU_ARCEL #elif defined(__arc__) && defined(__BIG_ENDIAN__) #define NPY_CPU_ARCEB +#elif defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64 + #define NPY_CPU_RISCV64 #else #error Unknown CPU, please report this to numpy maintainers with \ information about your platform (OS, CPU and compiler) diff --git a/numpy/core/include/numpy/npy_endian.h b/numpy/core/include/numpy/npy_endian.h index 1a42121db..649bdb0a6 100644 --- a/numpy/core/include/numpy/npy_endian.h +++ b/numpy/core/include/numpy/npy_endian.h @@ -46,7 +46,8 @@ || defined(NPY_CPU_SH_LE) \ || defined(NPY_CPU_MIPSEL) \ || defined(NPY_CPU_PPC64LE) \ - || defined(NPY_CPU_ARCEL) + || defined(NPY_CPU_ARCEL) \ + || defined(NPY_CPU_RISCV64) #define NPY_BYTE_ORDER NPY_LITTLE_ENDIAN #elif defined(NPY_CPU_PPC) \ || defined(NPY_CPU_SPARC) \ diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 1f249ae6c..d154206c5 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -1,6 +1,11 @@ from __future__ import division, absolute_import, print_function -import collections +try: + # Accessing collections abstract classes from collections + # has been deprecated since Python 3.3 + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc import itertools import operator import sys @@ -1084,7 +1089,7 @@ def outer(a, b, out=None): References ---------- - .. [1] : G. H. Golub and C. F. van Loan, *Matrix Computations*, 3rd + .. [1] : G. H. Golub and C. F. Van Loan, *Matrix Computations*, 3rd ed., Baltimore, MD, Johns Hopkins University Press, 1996, pg. 8. @@ -2758,8 +2763,8 @@ def seterrcall(func): {'over': 'log', 'divide': 'log', 'invalid': 'log', 'under': 'log'} """ - if func is not None and not isinstance(func, collections.Callable): - if not hasattr(func, 'write') or not isinstance(func.write, collections.Callable): + if func is not None and not isinstance(func, collections_abc.Callable): + if not hasattr(func, 'write') or not isinstance(func.write, collections_abc.Callable): raise ValueError("Only callable can be used as callback") pyvals = umath.geterrobj() old = geterrcall() diff --git a/numpy/core/setup.py b/numpy/core/setup.py index 11b1acb07..15f6e1522 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -888,6 +888,7 @@ def configuration(parent_package='',top_path=None): join('src', 'umath', 'loops.c.src'), join('src', 'umath', 'ufunc_object.c'), join('src', 'umath', 'extobj.c'), + join('src', 'umath', 'cpuid.c'), join('src', 'umath', 'scalarmath.c.src'), join('src', 'umath', 'ufunc_type_resolution.c'), join('src', 'umath', 'override.c'), @@ -924,29 +925,29 @@ def configuration(parent_package='',top_path=None): # umath_tests module # ####################################################################### - config.add_extension('umath_tests', - sources=[join('src', 'umath', 'umath_tests.c.src')]) + config.add_extension('_umath_tests', + sources=[join('src', 'umath', '_umath_tests.c.src')]) ####################################################################### # custom rational dtype module # ####################################################################### - config.add_extension('test_rational', - sources=[join('src', 'umath', 'test_rational.c.src')]) + config.add_extension('_rational_tests', + sources=[join('src', 'umath', '_rational_tests.c.src')]) ####################################################################### # struct_ufunc_test module # ####################################################################### - config.add_extension('struct_ufunc_test', - sources=[join('src', 'umath', 'struct_ufunc_test.c.src')]) + config.add_extension('_struct_ufunc_tests', + sources=[join('src', 'umath', '_struct_ufunc_tests.c.src')]) ####################################################################### # multiarray_tests module # ####################################################################### - config.add_extension('multiarray_tests', - sources=[join('src', 'multiarray', 'multiarray_tests.c.src'), + config.add_extension('_multiarray_tests', + sources=[join('src', 'multiarray', '_multiarray_tests.c.src'), join('src', 'private', 'mem_overlap.c')], depends=[join('src', 'private', 'mem_overlap.h'), join('src', 'private', 'npy_extint128.h')], @@ -956,8 +957,8 @@ def configuration(parent_package='',top_path=None): # operand_flag_tests module # ####################################################################### - config.add_extension('operand_flag_tests', - sources=[join('src', 'umath', 'operand_flag_tests.c.src')]) + config.add_extension('_operand_flag_tests', + sources=[join('src', 'umath', '_operand_flag_tests.c.src')]) config.add_data_dir('tests') config.add_data_dir('tests/data') diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py index 1fe953910..f36d61f55 100644 --- a/numpy/core/setup_common.py +++ b/numpy/core/setup_common.py @@ -146,6 +146,7 @@ OPTIONAL_INTRINSICS = [("__builtin_isnan", '5.'), "stdio.h", "LINK_AVX"), ("__asm__ volatile", '"vpand %ymm1, %ymm2, %ymm3"', "stdio.h", "LINK_AVX2"), + ("__asm__ volatile", '"xgetbv"', "stdio.h", "XGETBV"), ] # function attributes diff --git a/numpy/core/src/multiarray/multiarray_tests.c.src b/numpy/core/src/multiarray/_multiarray_tests.c.src index d63349560..afc6db1aa 100644 --- a/numpy/core/src/multiarray/multiarray_tests.c.src +++ b/numpy/core/src/multiarray/_multiarray_tests.c.src @@ -1867,7 +1867,7 @@ static PyMethodDef Multiarray_TestsMethods[] = { #if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - "multiarray_tests", + "_multiarray_tests", NULL, -1, Multiarray_TestsMethods, @@ -1880,11 +1880,11 @@ static struct PyModuleDef moduledef = { #if defined(NPY_PY3K) #define RETVAL m -PyMODINIT_FUNC PyInit_multiarray_tests(void) +PyMODINIT_FUNC PyInit__multiarray_tests(void) #else #define RETVAL PyMODINIT_FUNC -initmultiarray_tests(void) +init_multiarray_tests(void) #endif { PyObject *m; @@ -1892,7 +1892,7 @@ initmultiarray_tests(void) #if defined(NPY_PY3K) m = PyModule_Create(&moduledef); #else - m = Py_InitModule("multiarray_tests", Multiarray_TestsMethods); + m = Py_InitModule("_multiarray_tests", Multiarray_TestsMethods); #endif if (m == NULL) { return RETVAL; @@ -1900,7 +1900,7 @@ initmultiarray_tests(void) import_array(); if (PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, - "cannot load umath_tests module."); + "cannot load _multiarray_tests module."); } return RETVAL; } diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index e8aa19416..5e6804a5c 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -998,11 +998,25 @@ VOID_setitem(PyObject *op, void *input, void *vap) * undiscerning case: It interprets any object as a buffer * and reads as many bytes as possible, padding with 0. */ +#if defined(NPY_PY3K) + { + Py_buffer view; + + if (PyObject_GetBuffer(op, &view, PyBUF_SIMPLE) < 0) { + return -1; + } + memcpy(ip, view.buf, PyArray_MIN(view.len, itemsize)); + if (itemsize > view.len) { + memset(ip + view.len, 0, itemsize - view.len); + } + PyBuffer_Release(&view); + } +#else { const void *buffer; Py_ssize_t buflen; - res = PyObject_AsReadBuffer(op, &buffer, &buflen); - if (res == -1) { + + if (PyObject_AsReadBuffer(op, &buffer, &buflen) < 0) { return -1; } memcpy(ip, buffer, PyArray_MIN(buflen, itemsize)); @@ -1010,6 +1024,7 @@ VOID_setitem(PyObject *op, void *input, void *vap) memset(ip + buflen, 0, itemsize - buflen); } } +#endif return 0; } diff --git a/numpy/core/src/multiarray/buffer.c b/numpy/core/src/multiarray/buffer.c index 4aa25a196..21dbdefd6 100644 --- a/numpy/core/src/multiarray/buffer.c +++ b/numpy/core/src/multiarray/buffer.c @@ -15,6 +15,7 @@ #include "common.h" #include "numpyos.h" #include "arrayobject.h" +#include "scalartypes.h" /************************************************************************* **************** Implement Buffer Protocol **************************** @@ -176,7 +177,7 @@ _is_natively_aligned_at(PyArray_Descr *descr, static int _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, - PyArrayObject* arr, Py_ssize_t *offset, + PyObject* obj, Py_ssize_t *offset, char *active_byteorder) { int k; @@ -223,7 +224,7 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, Py_DECREF(subarray_tuple); old_offset = *offset; - ret = _buffer_format_string(descr->subarray->base, str, arr, offset, + ret = _buffer_format_string(descr->subarray->base, str, obj, offset, active_byteorder); *offset = old_offset + (*offset - old_offset) * total_count; return ret; @@ -265,7 +266,7 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, } /* Insert child item */ - _buffer_format_string(child, str, arr, offset, + _buffer_format_string(child, str, obj, offset, active_byteorder); /* Insert field name */ @@ -302,6 +303,7 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, } else { int is_standard_size = 1; + int is_natively_aligned; int is_native_only_type = (descr->type_num == NPY_LONGDOUBLE || descr->type_num == NPY_CLONGDOUBLE); if (sizeof(npy_longlong) != 8) { @@ -312,8 +314,16 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, *offset += descr->elsize; - if (descr->byteorder == '=' && - _is_natively_aligned_at(descr, arr, *offset)) { + if (PyArray_IsScalar(obj, Generic)) { + /* scalars are always natively aligned */ + is_natively_aligned = 1; + } + else { + is_natively_aligned = _is_natively_aligned_at(descr, + (PyArrayObject*)obj, *offset); + } + + if (descr->byteorder == '=' && is_natively_aligned) { /* Prefer native types, to cater for Cython */ is_standard_size = 0; if (*active_byteorder != '@') { @@ -448,43 +458,61 @@ static PyObject *_buffer_info_cache = NULL; /* Fill in the info structure */ static _buffer_info_t* -_buffer_info_new(PyArrayObject *arr) +_buffer_info_new(PyObject *obj) { _buffer_info_t *info; _tmp_string_t fmt = {NULL, 0, 0}; int k; + PyArray_Descr *descr = NULL; + int err = 0; info = malloc(sizeof(_buffer_info_t)); if (info == NULL) { goto fail; } - /* Fill in format */ - if (_buffer_format_string(PyArray_DESCR(arr), &fmt, arr, NULL, NULL) != 0) { - free(fmt.s); - goto fail; - } - _append_char(&fmt, '\0'); - info->format = fmt.s; - - /* Fill in shape and strides */ - info->ndim = PyArray_NDIM(arr); - - if (info->ndim == 0) { + if (PyArray_IsScalar(obj, Generic)) { + descr = PyArray_DescrFromScalar(obj); + if (descr == NULL) { + goto fail; + } + info->ndim = 0; info->shape = NULL; info->strides = NULL; } else { - info->shape = malloc(sizeof(Py_ssize_t) * PyArray_NDIM(arr) * 2 + 1); - if (info->shape == NULL) { - goto fail; + PyArrayObject * arr = (PyArrayObject *)obj; + descr = PyArray_DESCR(arr); + /* Fill in shape and strides */ + info->ndim = PyArray_NDIM(arr); + + if (info->ndim == 0) { + info->shape = NULL; + info->strides = NULL; } - info->strides = info->shape + PyArray_NDIM(arr); - for (k = 0; k < PyArray_NDIM(arr); ++k) { - info->shape[k] = PyArray_DIMS(arr)[k]; - info->strides[k] = PyArray_STRIDES(arr)[k]; + else { + info->shape = malloc(sizeof(Py_ssize_t) * PyArray_NDIM(arr) * 2 + 1); + if (info->shape == NULL) { + goto fail; + } + info->strides = info->shape + PyArray_NDIM(arr); + for (k = 0; k < PyArray_NDIM(arr); ++k) { + info->shape[k] = PyArray_DIMS(arr)[k]; + info->strides[k] = PyArray_STRIDES(arr)[k]; + } } + Py_INCREF(descr); + } + + /* Fill in format */ + err = _buffer_format_string(descr, &fmt, obj, NULL, NULL); + Py_DECREF(descr); + if (err != 0) { + free(fmt.s); + goto fail; } + _append_char(&fmt, '\0'); + info->format = fmt.s; return info; @@ -530,7 +558,7 @@ _buffer_info_free(_buffer_info_t *info) /* Get buffer info from the global dictionary */ static _buffer_info_t* -_buffer_get_info(PyObject *arr) +_buffer_get_info(PyObject *obj) { PyObject *key = NULL, *item_list = NULL, *item = NULL; _buffer_info_t *info = NULL, *old_info = NULL; @@ -543,13 +571,13 @@ _buffer_get_info(PyObject *arr) } /* Compute information */ - info = _buffer_info_new((PyArrayObject*)arr); + info = _buffer_info_new(obj); if (info == NULL) { return NULL; } /* Check if it is identical with an old one; reuse old one, if yes */ - key = PyLong_FromVoidPtr((void*)arr); + key = PyLong_FromVoidPtr((void*)obj); if (key == NULL) { goto fail; } @@ -627,9 +655,8 @@ _buffer_clear_info(PyObject *arr) } /* - * Retrieving buffers + * Retrieving buffers for ndarray */ - static int array_getbuffer(PyObject *obj, Py_buffer *view, int flags) { @@ -751,6 +778,62 @@ fail: return -1; } +/* + * Retrieving buffers for scalars + */ +int +gentype_getbuffer(PyObject *self, Py_buffer *view, int flags) +{ + _buffer_info_t *info = NULL; + PyArray_Descr *descr = NULL; + int elsize; + + if (flags & PyBUF_WRITABLE) { + PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly"); + goto fail; + } + + /* Fill in information */ + info = _buffer_get_info(self); + if (info == NULL) { + PyErr_SetString(PyExc_BufferError, + "could not get scalar buffer information"); + goto fail; + } + + view->ndim = info->ndim; + view->shape = info->shape; + view->strides = info->strides; + + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { + view->format = info->format; + } else { + view->format = NULL; + } + + descr = PyArray_DescrFromScalar(self); + view->buf = (void *)scalar_value(self, descr); + elsize = descr->elsize; +#ifndef Py_UNICODE_WIDE + if (descr->type_num == NPY_UNICODE) { + elsize >>= 1; + } +#endif + view->len = elsize; + view->itemsize = elsize; + + Py_DECREF(descr); + + view->readonly = 1; + view->suboffsets = NULL; + view->obj = self; + Py_INCREF(self); + return 0; + +fail: + view->obj = NULL; + return -1; +} /* * NOTE: for backward compatibility (esp. with PyArg_ParseTuple("s#", ...)) diff --git a/numpy/core/src/multiarray/buffer.h b/numpy/core/src/multiarray/buffer.h index d2ea01b34..d5da8f440 100644 --- a/numpy/core/src/multiarray/buffer.h +++ b/numpy/core/src/multiarray/buffer.h @@ -9,4 +9,7 @@ _array_dealloc_buffer_info(PyArrayObject *self); NPY_NO_EXPORT PyArray_Descr* _descriptor_from_pep3118_format(char *s); +NPY_NO_EXPORT int +gentype_getbuffer(PyObject *obj, Py_buffer *view, int flags); + #endif diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c index 10efdc4c8..f191f8db4 100644 --- a/numpy/core/src/multiarray/common.c +++ b/numpy/core/src/multiarray/common.c @@ -305,7 +305,8 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, memset(&buffer_view, 0, sizeof(Py_buffer)); if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_FORMAT|PyBUF_STRIDES) == 0 || - PyObject_GetBuffer(obj, &buffer_view, PyBUF_FORMAT) == 0) { + PyObject_GetBuffer(obj, &buffer_view, + PyBUF_FORMAT|PyBUF_SIMPLE) == 0) { PyErr_Clear(); dtype = _descriptor_from_pep3118_format(buffer_view.format); @@ -633,8 +634,12 @@ NPY_NO_EXPORT npy_bool _IsWriteable(PyArrayObject *ap) { PyObject *base=PyArray_BASE(ap); +#if defined(NPY_PY3K) + Py_buffer view; +#else void *dummy; Py_ssize_t n; +#endif /* If we own our own data, then no-problem */ if ((base == NULL) || (PyArray_FLAGS(ap) & NPY_ARRAY_OWNDATA)) { @@ -668,9 +673,18 @@ _IsWriteable(PyArrayObject *ap) if (PyString_Check(base)) { return NPY_TRUE; } +#if defined(NPY_PY3K) + if (PyObject_GetBuffer(base, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) { + PyErr_Clear(); + return NPY_FALSE; + } + PyBuffer_Release(&view); +#else if (PyObject_AsWriteBuffer(base, &dummy, &n) < 0) { + PyErr_Clear(); return NPY_FALSE; } +#endif return NPY_TRUE; } diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index 56bd65eed..5ee385c46 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -1402,7 +1402,11 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) { PyObject *obj; PyObject *str; + #if (PY_VERSION_HEX >= 0x030700A2) + const char *docstr; + #else char *docstr; + #endif static char *msg = "already has a docstring"; PyObject *tp_dict = PyArrayDescr_Type.tp_dict; PyObject *myobj; diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c index 2bb1cbfc1..7e92e5991 100644 --- a/numpy/core/src/multiarray/conversion_utils.c +++ b/numpy/core/src/multiarray/conversion_utils.c @@ -165,10 +165,12 @@ PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf) } #if defined(NPY_PY3K) - if (PyObject_GetBuffer(obj, &view, PyBUF_ANY_CONTIGUOUS|PyBUF_WRITABLE) != 0) { + if (PyObject_GetBuffer(obj, &view, + PyBUF_ANY_CONTIGUOUS|PyBUF_WRITABLE|PyBUF_SIMPLE) != 0) { PyErr_Clear(); buf->flags &= ~NPY_ARRAY_WRITEABLE; - if (PyObject_GetBuffer(obj, &view, PyBUF_ANY_CONTIGUOUS) != 0) { + if (PyObject_GetBuffer(obj, &view, + PyBUF_ANY_CONTIGUOUS|PyBUF_SIMPLE) != 0) { return NPY_FAIL; } } @@ -177,8 +179,10 @@ PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf) buf->len = (npy_intp) view.len; /* - * XXX: PyObject_AsWriteBuffer does also this, but it is unsafe, as there is - * no strict guarantee that the buffer sticks around after being released. + * In Python 3 both of the deprecated functions PyObject_AsWriteBuffer and + * PyObject_AsReadBuffer that this code replaces release the buffer. It is + * up to the object that supplies the buffer to guarantee that the buffer + * sticks around after the release. */ PyBuffer_Release(&view); @@ -415,6 +419,10 @@ PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind) else if (str[0] == 'm' || str[0] == 'M') { *sortkind = NPY_MERGESORT; } + else if (str[0] == 's' || str[0] == 'S') { + /* mergesort is the only stable sorting method in numpy */ + *sortkind = NPY_MERGESORT; + } else { PyErr_Format(PyExc_ValueError, "%s is an unrecognized kind of sort", diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 3d6b161b1..0eba077da 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -729,13 +729,16 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it, /* PEP 3118 buffer interface */ if (PyObject_CheckBuffer(obj) == 1) { memset(&buffer_view, 0, sizeof(Py_buffer)); - if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_STRIDES) == 0 || - PyObject_GetBuffer(obj, &buffer_view, PyBUF_ND) == 0) { + if (PyObject_GetBuffer(obj, &buffer_view, + PyBUF_STRIDES|PyBUF_SIMPLE) == 0 || + PyObject_GetBuffer(obj, &buffer_view, + PyBUF_ND|PyBUF_SIMPLE) == 0) { int nd = buffer_view.ndim; + if (nd < *maxndim) { *maxndim = nd; } - for (i=0; i<*maxndim; i++) { + for (i = 0; i < *maxndim; i++) { d[i] = buffer_view.shape[i]; } PyBuffer_Release(&buffer_view); @@ -756,6 +759,7 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it, e = PyArray_LookupSpecial_OnInstance(obj, "__array_struct__"); if (e != NULL) { int nd = -1; + if (NpyCapsule_Check(e)) { PyArrayInterface *inter; inter = (PyArrayInterface *)NpyCapsule_AsVoidPtr(e); @@ -2187,7 +2191,11 @@ PyArray_FromInterface(PyObject *origin) PyArrayObject *ret; PyArray_Descr *dtype = NULL; char *data = NULL; +#if defined(NPY_PY3K) + Py_buffer view; +#else Py_ssize_t buffer_len; +#endif int res, i, n; npy_intp dims[NPY_MAXDIMS], strides[NPY_MAXDIMS]; int dataflags = NPY_ARRAY_BEHAVED; @@ -2217,7 +2225,7 @@ PyArray_FromInterface(PyObject *origin) if (PyUnicode_Check(attr)) { PyObject *tmp = PyUnicode_AsASCIIString(attr); if (tmp == NULL) { - goto fail; + goto fail; } attr = tmp; } @@ -2251,7 +2259,7 @@ PyArray_FromInterface(PyObject *origin) dtype = new_dtype; } } - + #if defined(NPY_PY3K) Py_DECREF(attr); /* Pairs with the unicode handling above */ #endif @@ -2335,6 +2343,25 @@ PyArray_FromInterface(PyObject *origin) else { base = origin; } +#if defined(NPY_PY3K) + if (PyObject_GetBuffer(base, &view, + PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) { + PyErr_Clear(); + if (PyObject_GetBuffer(base, &view, + PyBUF_SIMPLE) < 0) { + goto fail; + } + dataflags &= ~NPY_ARRAY_WRITEABLE; + } + data = (char *)view.buf; + /* + * In Python 3 both of the deprecated functions PyObject_AsWriteBuffer and + * PyObject_AsReadBuffer that this code replaces release the buffer. It is + * up to the object that supplies the buffer to guarantee that the buffer + * sticks around after the release. + */ + PyBuffer_Release(&view); +#else res = PyObject_AsWriteBuffer(base, (void **)&data, &buffer_len); if (res < 0) { PyErr_Clear(); @@ -2345,6 +2372,7 @@ PyArray_FromInterface(PyObject *origin) } dataflags &= ~NPY_ARRAY_WRITEABLE; } +#endif /* Get offset number from interface specification */ attr = PyDict_GetItemString(origin, "offset"); if (attr) { @@ -3480,6 +3508,9 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, { PyArrayObject *ret; char *data; +#if defined(NPY_PY3K) + Py_buffer view; +#endif Py_ssize_t ts; npy_intp s, n; int itemsize; @@ -3519,6 +3550,26 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, Py_INCREF(buf); } +#if defined(NPY_PY3K) + if (PyObject_GetBuffer(buf, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) { + writeable = 0; + PyErr_Clear(); + if (PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE) < 0) { + Py_DECREF(buf); + Py_DECREF(type); + return NULL; + } + } + data = (char *)view.buf; + ts = view.len; + /* + * In Python 3 both of the deprecated functions PyObject_AsWriteBuffer and + * PyObject_AsReadBuffer that this code replaces release the buffer. It is + * up to the object that supplies the buffer to guarantee that the buffer + * sticks around after the release. + */ + PyBuffer_Release(&view); +#else if (PyObject_AsWriteBuffer(buf, (void *)&data, &ts) == -1) { writeable = 0; PyErr_Clear(); @@ -3528,6 +3579,7 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, return NULL; } } +#endif if ((offset < 0) || (offset > ts)) { PyErr_Format(PyExc_ValueError, diff --git a/numpy/core/src/multiarray/datetime_strings.c b/numpy/core/src/multiarray/datetime_strings.c index 96cb66b95..4f9d8fa41 100644 --- a/numpy/core/src/multiarray/datetime_strings.c +++ b/numpy/core/src/multiarray/datetime_strings.c @@ -374,7 +374,7 @@ parse_iso_8601_datetime(char *str, Py_ssize_t len, } /* Leading '-' sign for negative year */ - if (*substr == '-') { + if (*substr == '-' || *substr == '+') { ++substr; --sublen; } diff --git a/numpy/core/src/multiarray/einsum.c.src b/numpy/core/src/multiarray/einsum.c.src index 7db606194..5dbc30aa9 100644 --- a/numpy/core/src/multiarray/einsum.c.src +++ b/numpy/core/src/multiarray/einsum.c.src @@ -1829,9 +1829,10 @@ parse_operand_subscripts(char *subscripts, int length, break; } else { - PyErr_SetString(PyExc_ValueError, + PyErr_Format(PyExc_ValueError, "einstein sum subscripts string contains a " - "'.' that is not part of an ellipsis ('...')"); + "'.' that is not part of an ellipsis ('...') in " + "operand %d", iop); return 0; } @@ -1888,6 +1889,12 @@ parse_operand_subscripts(char *subscripts, int length, return 0; } } + else if (label == '.') { + PyErr_Format(PyExc_ValueError, + "einstein sum subscripts string contains a " + "'.' that is not part of an ellipsis ('...') in " + "operand %d", iop); + } else if (label != ' ') { PyErr_Format(PyExc_ValueError, "invalid subscript '%c' in einstein sum " @@ -2011,7 +2018,8 @@ parse_output_subscripts(char *subscripts, int length, else { PyErr_SetString(PyExc_ValueError, "einstein sum subscripts string contains a " - "'.' that is not part of an ellipsis ('...')"); + "'.' that is not part of an ellipsis ('...') " + "in the output"); return -1; } @@ -2037,8 +2045,15 @@ parse_output_subscripts(char *subscripts, int length, if (i > 0) { for (i = 0; i < length; ++i) { label = subscripts[i]; + if (label == '.') { + PyErr_SetString(PyExc_ValueError, + "einstein sum subscripts string contains a " + "'.' that is not part of an ellipsis ('...') " + "in the output"); + return -1; + } /* A label for an axis */ - if (label != '.' && label != ' ') { + else if (label != ' ') { if (idim < ndim_left) { out_labels[idim++] = label; } @@ -2049,12 +2064,6 @@ parse_output_subscripts(char *subscripts, int length, return -1; } } - else { - PyErr_SetString(PyExc_ValueError, - "einstein sum subscripts string contains a " - "'.' that is not part of an ellipsis ('...')"); - return -1; - } } } @@ -2185,7 +2194,7 @@ get_combined_dims_view(PyArrayObject *op, int iop, char *labels) { npy_intp new_strides[NPY_MAXDIMS]; npy_intp new_dims[NPY_MAXDIMS]; - int i, idim, ndim, icombine, combineoffset, label; + int idim, ndim, icombine, combineoffset; int icombinemap[NPY_MAXDIMS]; PyArrayObject *ret = NULL; @@ -2205,7 +2214,7 @@ get_combined_dims_view(PyArrayObject *op, int iop, char *labels) * The char type may be either signed or unsigned, we * need it to be signed here. */ - label = (signed char)labels[idim]; + int label = (signed char)labels[idim]; /* If this label says to merge axes, get the actual label */ if (label < 0) { combineoffset = label; @@ -2225,7 +2234,7 @@ get_combined_dims_view(PyArrayObject *op, int iop, char *labels) } else { /* Update the combined axis dimensions and strides */ - i = idim + combineoffset; + int i = icombinemap[idim + combineoffset]; if (combineoffset < 0 && new_dims[i] != 0 && new_dims[i] != PyArray_DIM(op, idim)) { PyErr_Format(PyExc_ValueError, @@ -2235,7 +2244,6 @@ get_combined_dims_view(PyArrayObject *op, int iop, char *labels) (int)PyArray_DIM(op, idim)); return NULL; } - i = icombinemap[i]; new_dims[i] = PyArray_DIM(op, idim); new_strides[i] += PyArray_STRIDE(op, idim); } diff --git a/numpy/core/src/multiarray/flagsobject.c b/numpy/core/src/multiarray/flagsobject.c index d3dcc934f..a78bedccb 100644 --- a/numpy/core/src/multiarray/flagsobject.c +++ b/numpy/core/src/multiarray/flagsobject.c @@ -575,7 +575,7 @@ arrayflags_getitem(PyArrayFlagsObject *self, PyObject *ind) return arrayflags_fortran_get(self); } break; - case 14: + case 15: if (strncmp(key, "WRITEBACKIFCOPY", n) == 0) { return arrayflags_writebackifcopy_get(self); } diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c index 825363f19..86e6e7a2f 100644 --- a/numpy/core/src/multiarray/getset.c +++ b/numpy/core/src/multiarray/getset.c @@ -106,8 +106,12 @@ array_strides_set(PyArrayObject *self, PyObject *obj) npy_intp offset = 0; npy_intp lower_offset = 0; npy_intp upper_offset = 0; +#if defined(NPY_PY3K) + Py_buffer view; +#else Py_ssize_t buf_len; char *buf; +#endif if (obj == NULL) { PyErr_SetString(PyExc_AttributeError, @@ -132,12 +136,21 @@ array_strides_set(PyArrayObject *self, PyObject *obj) * Get the available memory through the buffer interface on * PyArray_BASE(new) or if that fails from the current new */ - if (PyArray_BASE(new) && PyObject_AsReadBuffer(PyArray_BASE(new), - (const void **)&buf, - &buf_len) >= 0) { +#if defined(NPY_PY3K) + if (PyArray_BASE(new) && + PyObject_GetBuffer(PyArray_BASE(new), &view, PyBUF_SIMPLE) >= 0) { + offset = PyArray_BYTES(self) - (char *)view.buf; + numbytes = view.len + offset; + PyBuffer_Release(&view); + } +#else + if (PyArray_BASE(new) && + PyObject_AsReadBuffer(PyArray_BASE(new), (const void **)&buf, + &buf_len) >= 0) { offset = PyArray_BYTES(self) - buf; numbytes = buf_len + offset; } +#endif else { PyErr_Clear(); offset_bounds_from_strides(PyArray_ITEMSIZE(new), PyArray_NDIM(new), @@ -328,6 +341,9 @@ array_data_set(PyArrayObject *self, PyObject *op) void *buf; Py_ssize_t buf_len; int writeable=1; +#if defined(NPY_PY3K) + Py_buffer view; +#endif /* 2016-19-02, 1.12 */ int ret = DEPRECATE("Assigning the 'data' attribute is an " @@ -342,18 +358,38 @@ array_data_set(PyArrayObject *self, PyObject *op) "Cannot delete array data"); return -1; } +#if defined(NPY_PY3K) + if (PyObject_GetBuffer(op, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) { + writeable = 0; + PyErr_Clear(); + if (PyObject_GetBuffer(op, &view, PyBUF_SIMPLE) < 0) { + return -1; + } + } + buf = view.buf; + buf_len = view.len; + /* + * In Python 3 both of the deprecated functions PyObject_AsWriteBuffer and + * PyObject_AsReadBuffer that this code replaces release the buffer. It is + * up to the object that supplies the buffer to guarantee that the buffer + * sticks around after the release. + */ + PyBuffer_Release(&view); +#else if (PyObject_AsWriteBuffer(op, &buf, &buf_len) < 0) { + PyErr_Clear(); writeable = 0; if (PyObject_AsReadBuffer(op, (const void **)&buf, &buf_len) < 0) { + PyErr_Clear(); PyErr_SetString(PyExc_AttributeError, - "object does not have single-segment " \ - "buffer interface"); + "object does not have single-segment buffer interface"); return -1; } } +#endif if (!PyArray_ISONESEGMENT(self)) { - PyErr_SetString(PyExc_AttributeError, "cannot set single-" \ - "segment buffer for discontiguous array"); + PyErr_SetString(PyExc_AttributeError, + "cannot set single-segment buffer for discontiguous array"); return -1; } if (PyArray_NBYTES(self) > buf_len) { diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c index cd88ab76b..004af8a70 100644 --- a/numpy/core/src/multiarray/methods.c +++ b/numpy/core/src/multiarray/methods.c @@ -2305,7 +2305,7 @@ array_setflags(PyArrayObject *self, PyObject *args, PyObject *kwds) } else { PyErr_SetString(PyExc_ValueError, - "cannot set aligned flag of mis-"\ + "cannot set aligned flag of mis-" "aligned array to True"); return NULL; } @@ -2315,7 +2315,7 @@ array_setflags(PyArrayObject *self, PyObject *args, PyObject *kwds) if (PyObject_IsTrue(uic)) { fa->flags = flagback; PyErr_SetString(PyExc_ValueError, - "cannot set WRITEBACKIFCOPY " \ + "cannot set WRITEBACKIFCOPY " "flag to True"); return NULL; } diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index 3e17ec040..9df635dee 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -28,6 +28,7 @@ #include "npy_import.h" #include "dragon4.h" #include "npy_longdouble.h" +#include "buffer.h" #include <stdlib.h> @@ -1842,6 +1843,9 @@ static PyObject * gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args)) { PyObject *ret = NULL, *obj = NULL, *mod = NULL; +#if defined(NPY_PY3K) + Py_buffer view; +#endif const char *buffer; Py_ssize_t buflen; @@ -1850,18 +1854,35 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args)) if (ret == NULL) { return NULL; } + #if defined(NPY_PY3K) if (PyArray_IsScalar(self, Unicode)) { /* Unicode on Python 3 does not expose the buffer interface */ buffer = PyUnicode_AS_DATA(self); buflen = PyUnicode_GET_DATA_SIZE(self); } - else -#endif + else if (PyObject_GetBuffer(self, &view, PyBUF_SIMPLE) >= 0) { + buffer = view.buf; + buflen = view.len; + /* + * In Python 3 both of the deprecated functions PyObject_AsWriteBuffer and + * PyObject_AsReadBuffer that this code replaces release the buffer. It is + * up to the object that supplies the buffer to guarantee that the buffer + * sticks around after the release. + */ + PyBuffer_Release(&view); + } + else { + Py_DECREF(ret); + return NULL; + } +#else if (PyObject_AsReadBuffer(self, (const void **)&buffer, &buflen)<0) { Py_DECREF(ret); return NULL; } +#endif + mod = PyImport_ImportModule("numpy.core.multiarray"); if (mod == NULL) { return NULL; @@ -2506,22 +2527,6 @@ gentype_getcharbuf(PyObject *self, Py_ssize_t segment, constchar **ptrptr) } #endif /* !defined(NPY_PY3K) */ - -static int -gentype_getbuffer(PyObject *self, Py_buffer *view, int flags) -{ - Py_ssize_t len; - void *buf; - - /* FIXME: XXX: the format is not implemented! -- this needs more work */ - - len = gentype_getreadbuf(self, 0, &buf); - return PyBuffer_FillInfo(view, self, buf, len, 1, flags); -} - -/* releasebuffer is not needed */ - - static PyBufferProcs gentype_as_buffer = { #if !defined(NPY_PY3K) gentype_getreadbuf, /* bf_getreadbuffer*/ diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c index 29c122bd3..05c24d6da 100644 --- a/numpy/core/src/multiarray/shape.c +++ b/numpy/core/src/multiarray/shape.c @@ -1078,7 +1078,7 @@ build_shape_string(npy_intp n, npy_intp *vals) * WARNING: If an axis flagged for removal has a shape equal to zero, * the array will point to invalid memory. The caller must * validate this! - * If an axis flagged for removal has a shape larger then one, + * If an axis flagged for removal has a shape larger than one, * the aligned flag (and in the future the contiguous flags), * may need explicit update. * (check also NPY_RELAXED_STRIDES_CHECKING) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index ea784ec5b..cf427dad8 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -1526,9 +1526,13 @@ const npy_float SQRT_MIN = 1.0842022e-19f; const npy_double SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */ #endif #if @precision@ == 3 +#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE +const npy_longdouble SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */ +#else /* this is correct for 80 bit long doubles */ const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l; #endif +#endif /* Avoid underflow when y is small. */ if (y < SQRT_MIN) { return (x * x); diff --git a/numpy/core/src/umath/operand_flag_tests.c.src b/numpy/core/src/umath/_operand_flag_tests.c.src index 046c37595..551a9c632 100644 --- a/numpy/core/src/umath/operand_flag_tests.c.src +++ b/numpy/core/src/umath/_operand_flag_tests.c.src @@ -42,7 +42,7 @@ static void *data[1] = {NULL}; #if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - "operand_flag_tests", + "_operand_flag_tests", NULL, -1, TestMethods, @@ -53,11 +53,11 @@ static struct PyModuleDef moduledef = { }; #define RETVAL m -PyMODINIT_FUNC PyInit_operand_flag_tests(void) +PyMODINIT_FUNC PyInit__operand_flag_tests(void) { #else #define RETVAL -PyMODINIT_FUNC initoperand_flag_tests(void) +PyMODINIT_FUNC init_operand_flag_tests(void) { #endif PyObject *m = NULL; @@ -66,7 +66,7 @@ PyMODINIT_FUNC initoperand_flag_tests(void) #if defined(NPY_PY3K) m = PyModule_Create(&moduledef); #else - m = Py_InitModule("operand_flag_tests", TestMethods); + m = Py_InitModule("_operand_flag_tests", TestMethods); #endif if (m == NULL) { goto fail; @@ -92,7 +92,7 @@ PyMODINIT_FUNC initoperand_flag_tests(void) fail: if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, - "cannot load operand_flag_tests module."); + "cannot load _operand_flag_tests module."); } #if defined(NPY_PY3K) if (m) { diff --git a/numpy/core/src/umath/test_rational.c.src b/numpy/core/src/umath/_rational_tests.c.src index ffc92b732..9e74845df 100644 --- a/numpy/core/src/umath/test_rational.c.src +++ b/numpy/core/src/umath/_rational_tests.c.src @@ -1129,7 +1129,7 @@ PyMethodDef module_methods[] = { #if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - "test_rational", + "_rational_tests", NULL, -1, module_methods, @@ -1142,10 +1142,10 @@ static struct PyModuleDef moduledef = { #if defined(NPY_PY3K) #define RETVAL m -PyMODINIT_FUNC PyInit_test_rational(void) { +PyMODINIT_FUNC PyInit__rational_tests(void) { #else #define RETVAL -PyMODINIT_FUNC inittest_rational(void) { +PyMODINIT_FUNC init_rational_tests(void) { #endif PyObject *m = NULL; @@ -1295,7 +1295,7 @@ PyMODINIT_FUNC inittest_rational(void) { #if defined(NPY_PY3K) m = PyModule_Create(&moduledef); #else - m = Py_InitModule("test_rational", module_methods); + m = Py_InitModule("_rational_tests", module_methods); #endif if (!m) { @@ -1397,7 +1397,7 @@ PyMODINIT_FUNC inittest_rational(void) { fail: if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, - "cannot load test_rational module."); + "cannot load _rational_tests module."); } #if defined(NPY_PY3K) if (m) { diff --git a/numpy/core/src/umath/struct_ufunc_test.c.src b/numpy/core/src/umath/_struct_ufunc_tests.c.src index 9a6318f47..b831d5c2a 100644 --- a/numpy/core/src/umath/struct_ufunc_test.c.src +++ b/numpy/core/src/umath/_struct_ufunc_tests.c.src @@ -56,7 +56,7 @@ static void add_uint64_triplet(char **args, npy_intp *dimensions, #if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - "struct_ufunc_test", + "_struct_ufunc_tests", NULL, -1, StructUfuncTestMethods, @@ -68,9 +68,9 @@ static struct PyModuleDef moduledef = { #endif #if defined(NPY_PY3K) -PyMODINIT_FUNC PyInit_struct_ufunc_test(void) +PyMODINIT_FUNC PyInit__struct_ufunc_tests(void) #else -PyMODINIT_FUNC initstruct_ufunc_test(void) +PyMODINIT_FUNC init_struct_ufunc_tests(void) #endif { PyObject *m, *add_triplet, *d; @@ -81,7 +81,7 @@ PyMODINIT_FUNC initstruct_ufunc_test(void) #if defined(NPY_PY3K) m = PyModule_Create(&moduledef); #else - m = Py_InitModule("struct_ufunc_test", StructUfuncTestMethods); + m = Py_InitModule("_struct_ufunc_tests", StructUfuncTestMethods); #endif if (m == NULL) { diff --git a/numpy/core/src/umath/umath_tests.c.src b/numpy/core/src/umath/_umath_tests.c.src index 8d9009a1a..120ce0332 100644 --- a/numpy/core/src/umath/umath_tests.c.src +++ b/numpy/core/src/umath/_umath_tests.c.src @@ -360,7 +360,7 @@ static PyMethodDef UMath_TestsMethods[] = { #if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - "umath_tests", + "_umath_tests", NULL, -1, UMath_TestsMethods, @@ -373,11 +373,11 @@ static struct PyModuleDef moduledef = { #if defined(NPY_PY3K) #define RETVAL m -PyMODINIT_FUNC PyInit_umath_tests(void) +PyMODINIT_FUNC PyInit__umath_tests(void) #else #define RETVAL PyMODINIT_FUNC -initumath_tests(void) +init_umath_tests(void) #endif { PyObject *m; @@ -387,7 +387,7 @@ initumath_tests(void) #if defined(NPY_PY3K) m = PyModule_Create(&moduledef); #else - m = Py_InitModule("umath_tests", UMath_TestsMethods); + m = Py_InitModule("_umath_tests", UMath_TestsMethods); #endif if (m == NULL) return RETVAL; @@ -406,7 +406,7 @@ initumath_tests(void) if (PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, - "cannot load umath_tests module."); + "cannot load _umath_tests module."); } return RETVAL; diff --git a/numpy/core/src/umath/cpuid.c b/numpy/core/src/umath/cpuid.c new file mode 100644 index 000000000..912d51eeb --- /dev/null +++ b/numpy/core/src/umath/cpuid.c @@ -0,0 +1,56 @@ +#define _UMATHMODULE +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include <Python.h> + +#include "npy_config.h" + +#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API +#define NO_IMPORT_ARRAY + +#include "cpuid.h" + +#define XCR_XFEATURE_ENABLED_MASK 0x0 +#define XSTATE_SSE 0x2 +#define XSTATE_YMM 0x4 + +/* + * verify the OS supports avx instructions + * it can be disabled in some OS, e.g. with the nosavex boot option of linux + */ +static NPY_INLINE +int os_avx_support(void) +{ +#if HAVE_XGETBV + /* + * use bytes for xgetbv to avoid issues with compiler not knowing the + * instruction + */ + unsigned int eax, edx; + unsigned int ecx = XCR_XFEATURE_ENABLED_MASK; + __asm__("xgetbv" : "=a" (eax), "=d" (edx) : "c" (ecx)); + return (eax & (XSTATE_SSE | XSTATE_YMM)) == (XSTATE_SSE | XSTATE_YMM); +#else + return 0; +#endif +} + + +/* + * Primitive cpu feature detect function + * Currently only supports checking for avx on gcc compatible compilers. + */ +NPY_NO_EXPORT int +npy_cpu_supports(const char * feature) +{ +#ifdef HAVE___BUILTIN_CPU_SUPPORTS + if (strcmp(feature, "avx2") == 0) { + return __builtin_cpu_supports("avx2") && os_avx_support(); + } + else if (strcmp(feature, "avx") == 0) { + return __builtin_cpu_supports("avx") && os_avx_support(); + } +#endif + + return 0; +} diff --git a/numpy/core/src/umath/cpuid.h b/numpy/core/src/umath/cpuid.h new file mode 100644 index 000000000..33702ed41 --- /dev/null +++ b/numpy/core/src/umath/cpuid.h @@ -0,0 +1,9 @@ +#ifndef _NPY_PRIVATE__CPUID_H_ +#define _NPY_PRIVATE__CPUID_H_ + +#include <numpy/ndarraytypes.h> /* for NPY_NO_EXPORT */ + +NPY_NO_EXPORT int +npy_cpu_supports(const char * feature); + +#endif diff --git a/numpy/core/src/umath/scalarmath.c.src b/numpy/core/src/umath/scalarmath.c.src index 7b424cc74..6e1fb1ee8 100644 --- a/numpy/core/src/umath/scalarmath.c.src +++ b/numpy/core/src/umath/scalarmath.c.src @@ -274,9 +274,6 @@ static void * #type = npy_float, npy_double, npy_longdouble# * #c = f, , l# */ -static @type@ (*_basic_@name@_sqrt)(@type@); -static @type@ (*_basic_@name@_fmod)(@type@, @type@); - #define @name@_ctype_add(a, b, outp) *(outp) = (a) + (b) #define @name@_ctype_subtract(a, b, outp) *(outp) = (a) - (b) #define @name@_ctype_multiply(a, b, outp) *(outp) = (a) * (b) @@ -306,9 +303,6 @@ static void /**end repeat**/ -static npy_half (*_basic_half_sqrt)(npy_half); -static npy_half (*_basic_half_fmod)(npy_half, npy_half); - #define half_ctype_add(a, b, outp) *(outp) = \ npy_float_to_half(npy_half_to_float(a) + npy_half_to_float(b)) #define half_ctype_subtract(a, b, outp) *(outp) = \ @@ -1693,49 +1687,6 @@ get_functions(PyObject * mm) _basic_clongdouble_pow = funcdata[j + 5]; Py_DECREF(obj); - /* Get the sqrt functions */ - obj = PyObject_GetAttrString(mm, "sqrt"); - if (obj == NULL) { - goto fail; - } - funcdata = ((PyUFuncObject *)obj)->data; - signatures = ((PyUFuncObject *)obj)->types; - /* - * sqrt ufunc is specialized for double and float loops in - * generate_umath.py, the first to go into FLOAT/DOUBLE_sqrt - * they have the same signature as the scalar variants so we need to skip - * over them - * also skip float16 copy placed before - */ - i = 6; - j = 3; - while (signatures[i] != NPY_FLOAT) { - i += 2; j++; - } - _basic_half_sqrt = funcdata[j - 1]; - _basic_float_sqrt = funcdata[j]; - _basic_double_sqrt = funcdata[j + 1]; - _basic_longdouble_sqrt = funcdata[j + 2]; - Py_DECREF(obj); - - /* Get the fmod functions */ - obj = PyObject_GetAttrString(mm, "fmod"); - if (obj == NULL) { - goto fail; - } - funcdata = ((PyUFuncObject *)obj)->data; - signatures = ((PyUFuncObject *)obj)->types; - i = 0; - j = 0; - while (signatures[i] != NPY_FLOAT) { - i += 3; - j++; - } - _basic_half_fmod = funcdata[j - 1]; - _basic_float_fmod = funcdata[j]; - _basic_double_fmod = funcdata[j + 1]; - _basic_longdouble_fmod = funcdata[j + 2]; - Py_DECREF(obj); return ret = 0; fail: diff --git a/numpy/core/tests/_locales.py b/numpy/core/tests/_locales.py new file mode 100644 index 000000000..28eebb14d --- /dev/null +++ b/numpy/core/tests/_locales.py @@ -0,0 +1,76 @@ +"""Provide class for testing in French locale + +""" +from __future__ import division, absolute_import, print_function + +import sys +import locale + +from numpy.testing import SkipTest + +__ALL__ = ['CommaDecimalPointLocale'] + + +def find_comma_decimal_point_locale(): + """See if platform has a decimal point as comma locale. + + Find a locale that uses a comma instead of a period as the + decimal point. + + Returns + ------- + old_locale: str + Locale when the function was called. + new_locale: {str, None) + First French locale found, None if none found. + + """ + if sys.platform == 'win32': + locales = ['FRENCH'] + else: + locales = ['fr_FR', 'fr_FR.UTF-8', 'fi_FI', 'fi_FI.UTF-8'] + + old_locale = locale.getlocale(locale.LC_NUMERIC) + new_locale = None + try: + for loc in locales: + try: + locale.setlocale(locale.LC_NUMERIC, loc) + new_locale = loc + break + except locale.Error: + pass + finally: + locale.setlocale(locale.LC_NUMERIC, locale=old_locale) + return old_locale, new_locale + + +class CommaDecimalPointLocale(object): + """Sets LC_NUMERIC to a locale with comma as decimal point. + + Classes derived from this class have setup and teardown methods that run + tests with locale.LC_NUMERIC set to a locale where commas (',') are used as + the decimal point instead of periods ('.'). On exit the locale is restored + to the initial locale. It also serves as context manager with the same + effect. If no such locale is available, it raises SkipTest in both cases. + + .. versionadded:: 1.15.0 + + """ + (cur_locale, tst_locale) = find_comma_decimal_point_locale() + + def setup(self): + if self.tst_locale is None: + raise SkipTest("No French locale available") + locale.setlocale(locale.LC_NUMERIC, locale=self.tst_locale) + + def teardown(self): + locale.setlocale(locale.LC_NUMERIC, locale=self.cur_locale) + + def __enter__(self): + if self.tst_locale is None: + raise SkipTest("No French locale available") + locale.setlocale(locale.LC_NUMERIC, locale=self.tst_locale) + + def __exit__(self, type, value, traceback): + locale.setlocale(locale.LC_NUMERIC, locale=self.cur_locale) diff --git a/numpy/core/tests/test_arrayprint.py b/numpy/core/tests/test_arrayprint.py index 309df8545..6c2403e67 100644 --- a/numpy/core/tests/test_arrayprint.py +++ b/numpy/core/tests/test_arrayprint.py @@ -5,7 +5,7 @@ import sys, gc import numpy as np from numpy.testing import ( - run_module_suite, assert_, assert_equal, assert_raises, assert_warns, dec + run_module_suite, assert_, assert_equal, assert_raises, assert_warns, dec, ) import textwrap @@ -388,6 +388,7 @@ class TestArray2String(object): "[ 'xxxxx']" ) + @dec._needs_refcount def test_refcount(self): # make sure we do not hold references to the array due to a recursive # closure (gh-10620) diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index 638994aee..ba291737a 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -1255,12 +1255,19 @@ class TestDateTime(object): # Allow space instead of 'T' between date and time assert_equal(np.array(['1980-02-29T01:02:03'], np.dtype('M8[s]')), np.array(['1980-02-29 01:02:03'], np.dtype('M8[s]'))) + # Allow positive years + assert_equal(np.array(['+1980-02-29T01:02:03'], np.dtype('M8[s]')), + np.array(['+1980-02-29 01:02:03'], np.dtype('M8[s]'))) # Allow negative years assert_equal(np.array(['-1980-02-29T01:02:03'], np.dtype('M8[s]')), np.array(['-1980-02-29 01:02:03'], np.dtype('M8[s]'))) # UTC specifier with assert_warns(DeprecationWarning): assert_equal( + np.array(['+1980-02-29T01:02:03'], np.dtype('M8[s]')), + np.array(['+1980-02-29 01:02:03Z'], np.dtype('M8[s]'))) + with assert_warns(DeprecationWarning): + assert_equal( np.array(['-1980-02-29T01:02:03'], np.dtype('M8[s]')), np.array(['-1980-02-29 01:02:03Z'], np.dtype('M8[s]'))) # Time zone offset diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index fe0c7cc5f..2c2900e6c 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -429,7 +429,7 @@ class TestNonNumericConjugate(_DeprecationTestCase): class TestNPY_CHAR(_DeprecationTestCase): # 2017-05-03, 1.13.0 def test_npy_char_deprecation(self): - from numpy.core.multiarray_tests import npy_char_deprecation + from numpy.core._multiarray_tests import npy_char_deprecation self.assert_deprecated(npy_char_deprecation) assert_(npy_char_deprecation() == 'S1') @@ -440,11 +440,11 @@ class Test_UPDATEIFCOPY(_DeprecationTestCase): WRITEBACKIFCOPY instead """ def test_npy_updateifcopy_deprecation(self): - from numpy.core.multiarray_tests import npy_updateifcopy_deprecation + from numpy.core._multiarray_tests import npy_updateifcopy_deprecation arr = np.arange(9).reshape(3, 3) v = arr.T self.assert_deprecated(npy_updateifcopy_deprecation, args=(v,)) - + class TestDatetimeEvent(_DeprecationTestCase): # 2017-08-11, 1.14.0 diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 2f997b4f7..c924e6f43 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -5,7 +5,7 @@ import sys import operator import numpy as np -from numpy.core.test_rational import rational +from numpy.core._rational_tests import rational from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_raises, dec diff --git a/numpy/core/tests/test_einsum.py b/numpy/core/tests/test_einsum.py index 9bd85fdb9..406b10eab 100644 --- a/numpy/core/tests/test_einsum.py +++ b/numpy/core/tests/test_einsum.py @@ -1,5 +1,7 @@ from __future__ import division, absolute_import, print_function +import itertools + import numpy as np from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_array_equal, @@ -791,6 +793,12 @@ class TestEinSum(object): self.optimize_compare('dba,ead,cad->bce') self.optimize_compare('aef,fbc,dca->bde') + def test_combined_views_mapping(self): + # gh-10792 + a = np.arange(9).reshape(1, 1, 3, 1, 3) + b = np.einsum('bbcdc->d', a) + assert_equal(b, [12]) + class TestEinSumPath(object): def build_operands(self, string, size_dict=global_size_dict): @@ -918,6 +926,13 @@ class TestEinSumPath(object): opt = np.einsum(*path_test, optimize=exp_path) assert_almost_equal(noopt, opt) + def test_spaces(self): + #gh-10794 + arr = np.array([[1]]) + for sp in itertools.product(['', ' '], repeat=4): + # no error for any spacing + np.einsum('{}...a{}->{}...a{}'.format(*sp), arr) + if __name__ == "__main__": run_module_suite() diff --git a/numpy/core/tests/test_extint128.py b/numpy/core/tests/test_extint128.py index d87585dcf..31786124d 100644 --- a/numpy/core/tests/test_extint128.py +++ b/numpy/core/tests/test_extint128.py @@ -6,7 +6,7 @@ import contextlib import operator import numpy as np -import numpy.core.multiarray_tests as mt +import numpy.core._multiarray_tests as mt from numpy.compat import long from numpy.testing import assert_raises, assert_equal, dec diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py index cd4ea611a..082ecb496 100644 --- a/numpy/core/tests/test_indexing.py +++ b/numpy/core/tests/test_indexing.py @@ -6,7 +6,7 @@ import functools import operator import numpy as np -from numpy.core.multiarray_tests import array_indexing +from numpy.core._multiarray_tests import array_indexing from itertools import product from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_raises, @@ -14,20 +14,6 @@ from numpy.testing import ( ) -try: - cdll = None - if hasattr(sys, 'gettotalrefcount'): - try: - cdll = np.ctypeslib.load_library('multiarray_d', np.core.multiarray.__file__) - except OSError: - pass - if cdll is None: - cdll = np.ctypeslib.load_library('multiarray', np.core.multiarray.__file__) - _HAS_CTYPE = True -except ImportError: - _HAS_CTYPE = False - - class TestIndexing(object): def test_index_no_floats(self): a = np.array([[[5]]]) @@ -513,7 +499,7 @@ class TestIndexing(object): arro = np.zeros((4, 4)) arr = arro[::-1, ::-1] - slices = [slice(None), [0, 1, 2, 3]] + slices = (slice(None), [0, 1, 2, 3]) arr[slices] = 10 assert_array_equal(arr, 10.) @@ -622,7 +608,7 @@ class TestSubclasses(object): assert_array_equal(new_s.finalize_status, new_s) assert_array_equal(new_s.old, s) - @dec.skipif(not HAS_REFCOUNT) + @dec._needs_refcount def test_slice_decref_getsetslice(self): # See gh-10066, a temporary slice object should be discarted. # This test is only really interesting on Python 2 since diff --git a/numpy/core/tests/test_longdouble.py b/numpy/core/tests/test_longdouble.py index 625d40c1b..7cd5b04d8 100644 --- a/numpy/core/tests/test_longdouble.py +++ b/numpy/core/tests/test_longdouble.py @@ -7,7 +7,7 @@ from numpy.testing import ( run_module_suite, assert_, assert_equal, dec, assert_raises, assert_array_equal, temppath, ) -from .test_print import in_foreign_locale +from ._locales import CommaDecimalPointLocale LD_INFO = np.finfo(np.longdouble) longdouble_longer_than_double = (LD_INFO.eps < np.finfo(np.double).eps) @@ -50,25 +50,12 @@ def test_bytes(): np.longdouble(b"1.2") -@in_foreign_locale -def test_fromstring_foreign_repr(): - f = 1.234 - a = np.fromstring(repr(f), dtype=float, sep=" ") - assert_equal(a[0], f) - - @dec.knownfailureif(string_to_longdouble_inaccurate, "Need strtold_l") def test_repr_roundtrip_bytes(): o = 1 + LD_INFO.eps assert_equal(np.longdouble(repr(o).encode("ascii")), o) -@in_foreign_locale -def test_repr_roundtrip_foreign(): - o = 1.5 - assert_equal(o, np.longdouble(repr(o))) - - def test_bogus_string(): assert_raises(ValueError, np.longdouble, "spam") assert_raises(ValueError, np.longdouble, "1.0 flub") @@ -83,18 +70,6 @@ def test_fromstring(): err_msg="reading '%s'" % s) -@in_foreign_locale -def test_fromstring_best_effort_float(): - assert_equal(np.fromstring("1,234", dtype=float, sep=" "), - np.array([1.])) - - -@in_foreign_locale -def test_fromstring_best_effort(): - assert_equal(np.fromstring("1,234", dtype=np.longdouble, sep=" "), - np.array([1.])) - - def test_fromstring_bogus(): assert_equal(np.fromstring("1. 2. 3. flop 4.", dtype=float, sep=" "), np.array([1., 2., 3.])) @@ -155,26 +130,6 @@ class TestFileBased(object): assert_equal(res, self.tgt) -@in_foreign_locale -def test_fromstring_foreign(): - s = "1.234" - a = np.fromstring(s, dtype=np.longdouble, sep=" ") - assert_equal(a[0], np.longdouble(s)) - - -@in_foreign_locale -def test_fromstring_foreign_sep(): - a = np.array([1, 2, 3, 4]) - b = np.fromstring("1,2,3,4,", dtype=np.longdouble, sep=",") - assert_array_equal(a, b) - - -@in_foreign_locale -def test_fromstring_foreign_value(): - b = np.fromstring("1,234", dtype=np.longdouble, sep=" ") - assert_array_equal(b[0], 1) - - # Conversions long double -> string @@ -207,6 +162,43 @@ def test_array_repr(): raise ValueError("precision loss creating arrays") assert_(repr(a) != repr(b)) +# +# Locale tests: scalar types formatting should be independent of the locale +# + +class TestCommaDecimalPointLocale(CommaDecimalPointLocale): + + def test_repr_roundtrip_foreign(self): + o = 1.5 + assert_equal(o, np.longdouble(repr(o))) + + def test_fromstring_foreign_repr(self): + f = 1.234 + a = np.fromstring(repr(f), dtype=float, sep=" ") + assert_equal(a[0], f) + + def test_fromstring_best_effort_float(self): + assert_equal(np.fromstring("1,234", dtype=float, sep=" "), + np.array([1.])) + + def test_fromstring_best_effort(self): + assert_equal(np.fromstring("1,234", dtype=np.longdouble, sep=" "), + np.array([1.])) + + def test_fromstring_foreign(self): + s = "1.234" + a = np.fromstring(s, dtype=np.longdouble, sep=" ") + assert_equal(a[0], np.longdouble(s)) + + def test_fromstring_foreign_sep(self): + a = np.array([1, 2, 3, 4]) + b = np.fromstring("1,2,3,4,", dtype=np.longdouble, sep=",") + assert_array_equal(a, b) + + def test_fromstring_foreign_value(self): + b = np.fromstring("1,234", dtype=np.longdouble, sep=" ") + assert_array_equal(b[0], 1) + if __name__ == "__main__": run_module_suite() diff --git a/numpy/core/tests/test_mem_overlap.py b/numpy/core/tests/test_mem_overlap.py index 9c17ed210..92baa0896 100644 --- a/numpy/core/tests/test_mem_overlap.py +++ b/numpy/core/tests/test_mem_overlap.py @@ -7,8 +7,8 @@ import numpy as np from numpy.testing import (run_module_suite, assert_, assert_raises, assert_equal, assert_array_equal, assert_allclose, dec) -from numpy.core.multiarray_tests import solve_diophantine, internal_overlap -from numpy.core import umath_tests +from numpy.core._multiarray_tests import solve_diophantine, internal_overlap +from numpy.core import _umath_tests from numpy.lib.stride_tricks import as_strided from numpy.compat import long @@ -749,7 +749,7 @@ class TestUFunc(object): def test_unary_gufunc_fuzz(self): shapes = [7, 13, 8, 21, 29, 32] - gufunc = umath_tests.euclidean_pdist + gufunc = _umath_tests.euclidean_pdist rng = np.random.RandomState(1234) diff --git a/numpy/core/tests/test_memmap.py b/numpy/core/tests/test_memmap.py index 1cd09ab21..c00867ce1 100644 --- a/numpy/core/tests/test_memmap.py +++ b/numpy/core/tests/test_memmap.py @@ -126,7 +126,7 @@ class TestMemmap(object): def test_indexing_drops_references(self): fp = memmap(self.tmpfp, dtype=self.dtype, mode='w+', shape=self.shape) - tmp = fp[[(1, 2), (2, 3)]] + tmp = fp[(1, 2), (2, 3)] if isinstance(tmp, memmap): assert_(tmp._mmap is not fp._mmap) diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 43bfb0635..d861da4b6 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -1,6 +1,11 @@ from __future__ import division, absolute_import, print_function -import collections +try: + # Accessing collections abstract classes from collections + # has been deprecated since Python 3.3 + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc import tempfile import sys import shutil @@ -18,22 +23,17 @@ if sys.version_info[0] >= 3: else: import __builtin__ as builtins from decimal import Decimal -from unittest import TestCase import numpy as np from numpy.compat import strchar, unicode -from numpy.core.tests.test_print import in_foreign_locale -from numpy.core.multiarray_tests import ( - test_neighborhood_iterator, test_neighborhood_iterator_oob, - test_pydatamem_seteventhook_start, test_pydatamem_seteventhook_end, - test_inplace_increment, get_buffer_info, test_as_c_array, - ) +import numpy.core._multiarray_tests as _multiarray_tests from numpy.testing import ( run_module_suite, assert_, assert_raises, assert_warns, assert_equal, assert_almost_equal, assert_array_equal, assert_raises_regex, assert_array_almost_equal, assert_allclose, IS_PYPY, HAS_REFCOUNT, assert_array_less, runstring, dec, SkipTest, temppath, suppress_warnings ) +from ._locales import CommaDecimalPointLocale # Need to test an object that does not fully implement math interface from datetime import timedelta, datetime @@ -101,8 +101,10 @@ class TestFlags(object): assert_equal(self.a.flags.updateifcopy, False) with assert_warns(DeprecationWarning): assert_equal(self.a.flags['U'], False) + assert_equal(self.a.flags['UPDATEIFCOPY'], False) assert_equal(self.a.flags.writebackifcopy, False) assert_equal(self.a.flags['X'], False) + assert_equal(self.a.flags['WRITEBACKIFCOPY'], False) def test_string_align(self): @@ -189,7 +191,7 @@ class TestAttributes(object): assert_equal(isinstance(numpy_int, int), True) # ... and fast-path checks on C-API level should also work - from numpy.core.multiarray_tests import test_int_subclass + from numpy.core._multiarray_tests import test_int_subclass assert_equal(test_int_subclass(numpy_int), True) def test_stridesattr(self): @@ -3340,7 +3342,7 @@ class TestTemporaryElide(object): # def incref_elide(a): # d = input.copy() # refcount 1 # return d, d + d # PyNumber_Add without increasing refcount - from numpy.core.multiarray_tests import incref_elide + from numpy.core._multiarray_tests import incref_elide d = np.ones(100000) orig, res = incref_elide(d) d + d @@ -3355,7 +3357,7 @@ class TestTemporaryElide(object): # # def incref_elide_l(d): # return l[4] + l[4] # PyNumber_Add without increasing refcount - from numpy.core.multiarray_tests import incref_elide_l + from numpy.core._multiarray_tests import incref_elide_l # padding with 1 makes sure the object on the stack is not overwritten l = [1, 1, 1, 1, np.ones(100000)] res = incref_elide_l(l) @@ -3434,7 +3436,7 @@ class TestTemporaryElide(object): class TestCAPI(object): def test_IsPythonScalar(self): - from numpy.core.multiarray_tests import IsPythonScalar + from numpy.core._multiarray_tests import IsPythonScalar assert_(IsPythonScalar(b'foobar')) assert_(IsPythonScalar(1)) assert_(IsPythonScalar(2**80)) @@ -4471,14 +4473,15 @@ class TestIO(object): assert_equal(s, '1.51,2.00,3.51,4.00') def test_locale(self): - in_foreign_locale(self.test_numbers)() - in_foreign_locale(self.test_nan)() - in_foreign_locale(self.test_inf)() - in_foreign_locale(self.test_counted_string)() - in_foreign_locale(self.test_ascii)() - in_foreign_locale(self.test_malformed)() - in_foreign_locale(self.test_tofile_sep)() - in_foreign_locale(self.test_tofile_format)() + with CommaDecimalPointLocale(): + self.test_numbers() + self.test_nan() + self.test_inf() + self.test_counted_string() + self.test_ascii() + self.test_malformed() + self.test_tofile_sep() + self.test_tofile_format() class TestFromBuffer(object): @@ -5350,8 +5353,6 @@ class TestDot(object): class MatmulCommon(object): """Common tests for '@' operator and numpy.matmul. - Do not derive from TestCase to avoid nose running it. - """ # Should work with these types. Will want to add # "O" at some point @@ -5804,24 +5805,24 @@ class TestNeighborhoodIter(object): np.array([[0, 0, 0], [0, 1, 0]], dtype=dt), np.array([[0, 0, 1], [0, 2, 3]], dtype=dt), np.array([[0, 1, 0], [2, 3, 0]], dtype=dt)] - l = test_neighborhood_iterator(x, [-1, 0, -1, 1], x[0], - NEIGH_MODE['zero']) + l = _multiarray_tests.test_neighborhood_iterator( + x, [-1, 0, -1, 1], x[0], NEIGH_MODE['zero']) assert_array_equal(l, r) r = [np.array([[1, 1, 1], [1, 0, 1]], dtype=dt), np.array([[1, 1, 1], [0, 1, 1]], dtype=dt), np.array([[1, 0, 1], [1, 2, 3]], dtype=dt), np.array([[0, 1, 1], [2, 3, 1]], dtype=dt)] - l = test_neighborhood_iterator(x, [-1, 0, -1, 1], x[0], - NEIGH_MODE['one']) + l = _multiarray_tests.test_neighborhood_iterator( + x, [-1, 0, -1, 1], x[0], NEIGH_MODE['one']) assert_array_equal(l, r) r = [np.array([[4, 4, 4], [4, 0, 1]], dtype=dt), np.array([[4, 4, 4], [0, 1, 4]], dtype=dt), np.array([[4, 0, 1], [4, 2, 3]], dtype=dt), np.array([[0, 1, 4], [2, 3, 4]], dtype=dt)] - l = test_neighborhood_iterator(x, [-1, 0, -1, 1], 4, - NEIGH_MODE['constant']) + l = _multiarray_tests.test_neighborhood_iterator( + x, [-1, 0, -1, 1], 4, NEIGH_MODE['constant']) assert_array_equal(l, r) def test_simple2d(self): @@ -5836,8 +5837,8 @@ class TestNeighborhoodIter(object): np.array([[0, 1, 1], [0, 1, 1]], dtype=dt), np.array([[0, 0, 1], [2, 2, 3]], dtype=dt), np.array([[0, 1, 1], [2, 3, 3]], dtype=dt)] - l = test_neighborhood_iterator(x, [-1, 0, -1, 1], x[0], - NEIGH_MODE['mirror']) + l = _multiarray_tests.test_neighborhood_iterator( + x, [-1, 0, -1, 1], x[0], NEIGH_MODE['mirror']) assert_array_equal(l, r) def test_mirror2d(self): @@ -5851,15 +5852,18 @@ class TestNeighborhoodIter(object): # Test padding with constant values x = np.linspace(1, 5, 5).astype(dt) r = [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 0]] - l = test_neighborhood_iterator(x, [-1, 1], x[0], NEIGH_MODE['zero']) + l = _multiarray_tests.test_neighborhood_iterator( + x, [-1, 1], x[0], NEIGH_MODE['zero']) assert_array_equal(l, r) r = [[1, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 1]] - l = test_neighborhood_iterator(x, [-1, 1], x[0], NEIGH_MODE['one']) + l = _multiarray_tests.test_neighborhood_iterator( + x, [-1, 1], x[0], NEIGH_MODE['one']) assert_array_equal(l, r) r = [[x[4], 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, x[4]]] - l = test_neighborhood_iterator(x, [-1, 1], x[4], NEIGH_MODE['constant']) + l = _multiarray_tests.test_neighborhood_iterator( + x, [-1, 1], x[4], NEIGH_MODE['constant']) assert_array_equal(l, r) def test_simple_float(self): @@ -5873,7 +5877,8 @@ class TestNeighborhoodIter(object): x = np.linspace(1, 5, 5).astype(dt) r = np.array([[2, 1, 1, 2, 3], [1, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 5], [3, 4, 5, 5, 4]], dtype=dt) - l = test_neighborhood_iterator(x, [-2, 2], x[1], NEIGH_MODE['mirror']) + l = _multiarray_tests.test_neighborhood_iterator( + x, [-2, 2], x[1], NEIGH_MODE['mirror']) assert_([i.dtype == dt for i in l]) assert_array_equal(l, r) @@ -5888,7 +5893,8 @@ class TestNeighborhoodIter(object): x = np.linspace(1, 5, 5).astype(dt) r = np.array([[4, 5, 1, 2, 3], [5, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 1], [3, 4, 5, 1, 2]], dtype=dt) - l = test_neighborhood_iterator(x, [-2, 2], x[0], NEIGH_MODE['circular']) + l = _multiarray_tests.test_neighborhood_iterator( + x, [-2, 2], x[0], NEIGH_MODE['circular']) assert_array_equal(l, r) def test_circular(self): @@ -5911,8 +5917,8 @@ class TestStackedNeighborhoodIter(object): np.array([3], dtype=dt), np.array([0], dtype=dt), np.array([0], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-2, 4], NEIGH_MODE['zero'], - [0, 0], NEIGH_MODE['zero']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-2, 4], NEIGH_MODE['zero'], [0, 0], NEIGH_MODE['zero']) assert_array_equal(l, r) r = [np.array([1, 0, 1], dtype=dt), @@ -5920,8 +5926,8 @@ class TestStackedNeighborhoodIter(object): np.array([1, 2, 3], dtype=dt), np.array([2, 3, 0], dtype=dt), np.array([3, 0, 1], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-1, 3], NEIGH_MODE['zero'], - [-1, 1], NEIGH_MODE['one']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-1, 3], NEIGH_MODE['zero'], [-1, 1], NEIGH_MODE['one']) assert_array_equal(l, r) # 2nd simple, 1d test: stacking 2 neigh iterators, mixing const padding and @@ -5935,8 +5941,8 @@ class TestStackedNeighborhoodIter(object): np.array([1, 2, 3], dtype=dt), np.array([2, 3, 3], dtype=dt), np.array([3, 3, 0], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-1, 3], NEIGH_MODE['mirror'], - [-1, 1], NEIGH_MODE['zero']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-1, 3], NEIGH_MODE['mirror'], [-1, 1], NEIGH_MODE['zero']) assert_array_equal(l, r) # Stacking mirror on top of zero @@ -5946,8 +5952,8 @@ class TestStackedNeighborhoodIter(object): np.array([0, 1, 2], dtype=dt), np.array([1, 2, 3], dtype=dt), np.array([2, 3, 0], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-1, 3], NEIGH_MODE['zero'], - [-2, 0], NEIGH_MODE['mirror']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-1, 3], NEIGH_MODE['zero'], [-2, 0], NEIGH_MODE['mirror']) assert_array_equal(l, r) # Stacking mirror on top of zero: 2nd @@ -5957,8 +5963,8 @@ class TestStackedNeighborhoodIter(object): np.array([2, 3, 0], dtype=dt), np.array([3, 0, 0], dtype=dt), np.array([0, 0, 3], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-1, 3], NEIGH_MODE['zero'], - [0, 2], NEIGH_MODE['mirror']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-1, 3], NEIGH_MODE['zero'], [0, 2], NEIGH_MODE['mirror']) assert_array_equal(l, r) # Stacking mirror on top of zero: 3rd @@ -5968,8 +5974,8 @@ class TestStackedNeighborhoodIter(object): np.array([0, 1, 2, 3, 0], dtype=dt), np.array([1, 2, 3, 0, 0], dtype=dt), np.array([2, 3, 0, 0, 3], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-1, 3], NEIGH_MODE['zero'], - [-2, 2], NEIGH_MODE['mirror']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-1, 3], NEIGH_MODE['zero'], [-2, 2], NEIGH_MODE['mirror']) assert_array_equal(l, r) # 3rd simple, 1d test: stacking 2 neigh iterators, mixing const padding and @@ -5983,8 +5989,8 @@ class TestStackedNeighborhoodIter(object): np.array([1, 2, 3], dtype=dt), np.array([2, 3, 1], dtype=dt), np.array([3, 1, 0], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-1, 3], NEIGH_MODE['circular'], - [-1, 1], NEIGH_MODE['zero']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-1, 3], NEIGH_MODE['circular'], [-1, 1], NEIGH_MODE['zero']) assert_array_equal(l, r) # Stacking mirror on top of zero @@ -5994,8 +6000,8 @@ class TestStackedNeighborhoodIter(object): np.array([0, 1, 2], dtype=dt), np.array([1, 2, 3], dtype=dt), np.array([2, 3, 0], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-1, 3], NEIGH_MODE['zero'], - [-2, 0], NEIGH_MODE['circular']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-1, 3], NEIGH_MODE['zero'], [-2, 0], NEIGH_MODE['circular']) assert_array_equal(l, r) # Stacking mirror on top of zero: 2nd @@ -6005,8 +6011,8 @@ class TestStackedNeighborhoodIter(object): np.array([2, 3, 0], dtype=dt), np.array([3, 0, 0], dtype=dt), np.array([0, 0, 1], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-1, 3], NEIGH_MODE['zero'], - [0, 2], NEIGH_MODE['circular']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-1, 3], NEIGH_MODE['zero'], [0, 2], NEIGH_MODE['circular']) assert_array_equal(l, r) # Stacking mirror on top of zero: 3rd @@ -6016,8 +6022,8 @@ class TestStackedNeighborhoodIter(object): np.array([0, 1, 2, 3, 0], dtype=dt), np.array([1, 2, 3, 0, 0], dtype=dt), np.array([2, 3, 0, 0, 1], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [-1, 3], NEIGH_MODE['zero'], - [-2, 2], NEIGH_MODE['circular']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [-1, 3], NEIGH_MODE['zero'], [-2, 2], NEIGH_MODE['circular']) assert_array_equal(l, r) # 4th simple, 1d test: stacking 2 neigh iterators, but with lower iterator @@ -6028,24 +6034,24 @@ class TestStackedNeighborhoodIter(object): # array x = np.array([1, 2, 3], dtype=dt) r = [np.array([1, 2, 3, 0], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [1, 1], NEIGH_MODE['zero'], - [-1, 2], NEIGH_MODE['zero']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [1, 1], NEIGH_MODE['zero'], [-1, 2], NEIGH_MODE['zero']) assert_array_equal(l, r) # Stacking mirror on top of zero, first neighborhood strictly inside the # array x = np.array([1, 2, 3], dtype=dt) r = [np.array([1, 2, 3, 3], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [1, 1], NEIGH_MODE['zero'], - [-1, 2], NEIGH_MODE['mirror']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [1, 1], NEIGH_MODE['zero'], [-1, 2], NEIGH_MODE['mirror']) assert_array_equal(l, r) # Stacking mirror on top of zero, first neighborhood strictly inside the # array x = np.array([1, 2, 3], dtype=dt) r = [np.array([1, 2, 3, 1], dtype=dt)] - l = test_neighborhood_iterator_oob(x, [1, 1], NEIGH_MODE['zero'], - [-1, 2], NEIGH_MODE['circular']) + l = _multiarray_tests.test_neighborhood_iterator_oob( + x, [1, 1], NEIGH_MODE['zero'], [-1, 2], NEIGH_MODE['circular']) assert_array_equal(l, r) class TestWarnings(object): @@ -6429,7 +6435,9 @@ class TestNewBufferProtocol(object): def test_export_flags(self): # Check SIMPLE flag, see also gh-3613 (exception should be BufferError) - assert_raises(ValueError, get_buffer_info, np.arange(5)[::2], ('SIMPLE',)) + assert_raises(ValueError, + _multiarray_tests.get_buffer_info, + np.arange(5)[::2], ('SIMPLE',)) def test_padding(self): for j in range(8): @@ -6485,10 +6493,12 @@ class TestNewBufferProtocol(object): arr = np.ones((1, 10)) if arr.flags.f_contiguous: - shape, strides = get_buffer_info(arr, ['F_CONTIGUOUS']) + shape, strides = _multiarray_tests.get_buffer_info( + arr, ['F_CONTIGUOUS']) assert_(strides[0] == 8) arr = np.ones((10, 1), order='F') - shape, strides = get_buffer_info(arr, ['C_CONTIGUOUS']) + shape, strides = _multiarray_tests.get_buffer_info( + arr, ['C_CONTIGUOUS']) assert_(strides[-1] == 8) def test_out_of_order_fields(self): @@ -6630,26 +6640,26 @@ def test_scalar_element_deletion(): class TestMemEventHook(object): def test_mem_seteventhook(self): # The actual tests are within the C code in - # multiarray/multiarray_tests.c.src - test_pydatamem_seteventhook_start() + # multiarray/_multiarray_tests.c.src + _multiarray_tests.test_pydatamem_seteventhook_start() # force an allocation and free of a numpy array # needs to be larger then limit of small memory cacher in ctors.c a = np.zeros(1000) del a gc.collect() - test_pydatamem_seteventhook_end() + _multiarray_tests.test_pydatamem_seteventhook_end() class TestMapIter(object): def test_mapiter(self): # The actual tests are within the C code in - # multiarray/multiarray_tests.c.src + # multiarray/_multiarray_tests.c.src a = np.arange(12).reshape((3, 4)).astype(float) index = ([1, 1, 2, 0], [0, 0, 2, 3]) vals = [50, 50, 30, 16] - test_inplace_increment(a, index, vals) + _multiarray_tests.test_inplace_increment(a, index, vals) assert_equal(a, [[0.00, 1., 2.0, 19.], [104., 5., 6.0, 7.0], [8.00, 9., 40., 11.]]) @@ -6657,24 +6667,24 @@ class TestMapIter(object): b = np.arange(6).astype(float) index = (np.array([1, 2, 0]),) vals = [50, 4, 100.1] - test_inplace_increment(b, index, vals) + _multiarray_tests.test_inplace_increment(b, index, vals) assert_equal(b, [100.1, 51., 6., 3., 4., 5.]) class TestAsCArray(object): def test_1darray(self): array = np.arange(24, dtype=np.double) - from_c = test_as_c_array(array, 3) + from_c = _multiarray_tests.test_as_c_array(array, 3) assert_equal(array[3], from_c) def test_2darray(self): array = np.arange(24, dtype=np.double).reshape(3, 8) - from_c = test_as_c_array(array, 2, 4) + from_c = _multiarray_tests.test_as_c_array(array, 2, 4) assert_equal(array[2, 4], from_c) def test_3darray(self): array = np.arange(24, dtype=np.double).reshape(2, 3, 4) - from_c = test_as_c_array(array, 1, 2, 3) + from_c = _multiarray_tests.test_as_c_array(array, 1, 2, 3) assert_equal(array[1, 2, 3], from_c) @@ -6992,7 +7002,7 @@ class TestHashing(object): def test_collections_hashable(self): x = np.array([]) - assert_(not isinstance(x, collections.Hashable)) + assert_(not isinstance(x, collections_abc.Hashable)) class TestArrayPriority(object): @@ -7168,7 +7178,7 @@ class TestCTypes(object): _internal.ctypes = ctypes -class TestWritebackIfCopy(TestCase): +class TestWritebackIfCopy(object): # all these tests use the WRITEBACKIFCOPY mechanism def test_argmax_with_out(self): mat = np.eye(5) @@ -7234,7 +7244,7 @@ class TestWritebackIfCopy(TestCase): assert_equal(b, np.array([[15, 18, 21], [42, 54, 66], [69, 90, 111]])) def test_view_assign(self): - from numpy.core.multiarray_tests import npy_create_writebackifcopy, npy_resolve + from numpy.core._multiarray_tests import npy_create_writebackifcopy, npy_resolve arr = np.arange(9).reshape(3, 3).T arr_wb = npy_create_writebackifcopy(arr) assert_(arr_wb.flags.writebackifcopy) @@ -7304,7 +7314,7 @@ def test_equal_override(): def test_npymath_complex(): # Smoketest npymath functions - from numpy.core.multiarray_tests import ( + from numpy.core._multiarray_tests import ( npy_cabs, npy_carg) funcs = {npy_cabs: np.absolute, @@ -7323,7 +7333,7 @@ def test_npymath_complex(): def test_npymath_real(): # Smoketest npymath functions - from numpy.core.multiarray_tests import ( + from numpy.core._multiarray_tests import ( npy_log10, npy_cosh, npy_sinh, npy_tan, npy_tanh) funcs = {npy_log10: np.log10, diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py index f3f8706b5..9d8f4f06a 100644 --- a/numpy/core/tests/test_nditer.py +++ b/numpy/core/tests/test_nditer.py @@ -4,8 +4,8 @@ import sys import warnings import numpy as np +import numpy.core._multiarray_tests as _multiarray_tests from numpy import array, arange, nditer, all -from numpy.core.multiarray_tests import test_nditer_too_large from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_array_equal, assert_raises, assert_warns, dec, HAS_REFCOUNT, suppress_warnings @@ -33,7 +33,7 @@ def iter_iterindices(i): i.iternext() return ret -@dec.skipif(not HAS_REFCOUNT, "python does not have sys.getrefcount") +@dec._needs_refcount def test_iter_refcount(): # Make sure the iterator doesn't leak @@ -2697,18 +2697,19 @@ def test_iter_too_large_with_multiindex(): # arrays are now too large to be broadcast. The different modes test # different nditer functionality with or without GIL. for mode in range(6): - assert_raises(ValueError, test_nditer_too_large, arrays, -1, mode) + with assert_raises(ValueError): + _multiarray_tests.test_nditer_too_large(arrays, -1, mode) # but if we do nothing with the nditer, it can be constructed: - test_nditer_too_large(arrays, -1, 7) + _multiarray_tests.test_nditer_too_large(arrays, -1, 7) # When an axis is removed, things should work again (half the time): for i in range(num): for mode in range(6): # an axis with size 1024 is removed: - test_nditer_too_large(arrays, i*2, mode) + _multiarray_tests.test_nditer_too_large(arrays, i*2, mode) # an axis with size 1 is removed: - assert_raises(ValueError, test_nditer_too_large, - arrays, i*2 + 1, mode) + with assert_raises(ValueError): + _multiarray_tests.test_nditer_too_large(arrays, i*2 + 1, mode) if __name__ == "__main__": diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 7c012e9e8..d7cdd77f1 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -12,7 +12,7 @@ from numpy.random import rand, randint, randn from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_raises, assert_raises_regex, assert_array_equal, assert_almost_equal, - assert_array_almost_equal, dec, HAS_REFCOUNT, suppress_warnings + assert_array_almost_equal, dec, suppress_warnings ) @@ -2092,7 +2092,7 @@ class TestCreationFuncs(object): self.check_function(np.full, 0) self.check_function(np.full, 1) - @dec.skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") + @dec._needs_refcount def test_for_reference_leak(self): # Make sure we have an object for reference dim = 1 diff --git a/numpy/core/tests/test_print.py b/numpy/core/tests/test_print.py index 6ebb4733c..f432711a9 100644 --- a/numpy/core/tests/test_print.py +++ b/numpy/core/tests/test_print.py @@ -2,12 +2,14 @@ from __future__ import division, absolute_import, print_function import sys import locale +import contextlib import nose import numpy as np from numpy.testing import ( - run_module_suite, assert_, assert_equal, SkipTest + run_module_suite, assert_, assert_equal, SkipTest, dec ) +from ._locales import CommaDecimalPointLocale if sys.version_info[0] >= 3: @@ -201,46 +203,21 @@ def test_scalar_format(): (fmat, repr(val), repr(valtype), str(e))) +# # Locale tests: scalar types formatting should be independent of the locale -def in_foreign_locale(func): - """ - Swap LC_NUMERIC locale to one in which the decimal point is ',' and not '.' - If not possible, raise SkipTest +# - """ - if sys.platform == 'win32': - locales = ['FRENCH'] - else: - locales = ['fr_FR', 'fr_FR.UTF-8', 'fi_FI', 'fi_FI.UTF-8'] +class TestCommaDecimalPointLocale(CommaDecimalPointLocale): + + def test_locale_single(self): + assert_equal(str(np.float32(1.2)), str(float(1.2))) + + def test_locale_double(self): + assert_equal(str(np.double(1.2)), str(float(1.2))) + + def test_locale_longdouble(self): + assert_equal(str(np.longdouble('1.2')), str(float(1.2))) - def wrapper(*args, **kwargs): - curloc = locale.getlocale(locale.LC_NUMERIC) - try: - for loc in locales: - try: - locale.setlocale(locale.LC_NUMERIC, loc) - break - except locale.Error: - pass - else: - raise SkipTest("Skipping locale test, because " - "French locale not found") - return func(*args, **kwargs) - finally: - locale.setlocale(locale.LC_NUMERIC, locale=curloc) - return nose.tools.make_decorator(func)(wrapper) - -@in_foreign_locale -def test_locale_single(): - assert_equal(str(np.float32(1.2)), str(float(1.2))) - -@in_foreign_locale -def test_locale_double(): - assert_equal(str(np.double(1.2)), str(float(1.2))) - -@in_foreign_locale -def test_locale_longdouble(): - assert_equal(str(np.longdouble('1.2')), str(float(1.2))) if __name__ == "__main__": run_module_suite() diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py index d5423b1f1..8e261d226 100644 --- a/numpy/core/tests/test_records.py +++ b/numpy/core/tests/test_records.py @@ -1,7 +1,12 @@ from __future__ import division, absolute_import, print_function import sys -import collections +try: + # Accessing collections abstract classes from collections + # has been deprecated since Python 3.3 + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc import pickle import warnings import textwrap @@ -252,7 +257,7 @@ class TestFromrecords(object): assert_array_equal(ra['shape'], [['A', 'B', 'C']]) ra.field = 5 assert_array_equal(ra['field'], [[5, 5, 5]]) - assert_(isinstance(ra.field, collections.Callable)) + assert_(isinstance(ra.field, collections_abc.Callable)) def test_fromrecords_with_explicit_dtype(self): a = np.rec.fromrecords([(1, 'a'), (2, 'bbb')], diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py index a3b011454..016b720f3 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -1424,7 +1424,7 @@ class TestRegression(object): x[x.nonzero()] = x.ravel()[:1] assert_(x[0, 1] == x[0, 0]) - @dec.skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") + @dec._needs_refcount def test_structured_arrays_with_objects2(self): # Ticket #1299 second test stra = 'aaaa' @@ -1537,7 +1537,7 @@ class TestRegression(object): y = np.add(x, x, x) assert_equal(id(x), id(y)) - @dec.skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") + @dec._needs_refcount def test_take_refcount(self): # ticket #939 a = np.arange(16, dtype=float) @@ -1937,7 +1937,7 @@ class TestRegression(object): a = np.empty((100000000,), dtype='i1') del a - @dec.skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") + @dec._needs_refcount def test_ufunc_reduce_memoryleak(self): a = np.arange(6) acnt = sys.getrefcount(a) @@ -2167,7 +2167,7 @@ class TestRegression(object): assert_equal(uf(a), ()) assert_array_equal(a, [[3, 2, 1], [5, 4], [9, 7, 8, 6]]) - @dec.skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") + @dec._needs_refcount def test_leak_in_structured_dtype_comparison(self): # gh-6250 recordtype = np.dtype([('a', np.float64), diff --git a/numpy/core/tests/test_scalarbuffer.py b/numpy/core/tests/test_scalarbuffer.py new file mode 100644 index 000000000..cd887f2fb --- /dev/null +++ b/numpy/core/tests/test_scalarbuffer.py @@ -0,0 +1,84 @@ +""" +Test scalar buffer interface adheres to PEP 3118 +""" +import sys +import numpy as np +from numpy.testing import run_module_suite, assert_, assert_equal, dec + +# PEP3118 format strings for native (standard alignment and byteorder) types +scalars_and_codes = [ + (np.bool_, '?'), + (np.byte, 'b'), + (np.short, 'h'), + (np.intc, 'i'), + (np.int_, 'l'), + (np.longlong, 'q'), + (np.ubyte, 'B'), + (np.ushort, 'H'), + (np.uintc, 'I'), + (np.uint, 'L'), + (np.ulonglong, 'Q'), + (np.half, 'e'), + (np.single, 'f'), + (np.double, 'd'), + (np.longdouble, 'g'), + (np.csingle, 'Zf'), + (np.cdouble, 'Zd'), + (np.clongdouble, 'Zg'), +] + + +class TestScalarPEP3118(object): + skip_if_no_buffer_interface = dec.skipif(sys.version_info.major < 3, + "scalars do not implement buffer interface in Python 2") + + @skip_if_no_buffer_interface + def test_scalar_match_array(self): + for scalar, _ in scalars_and_codes: + x = scalar() + a = np.array([], dtype=np.dtype(scalar)) + mv_x = memoryview(x) + mv_a = memoryview(a) + assert_equal(mv_x.format, mv_a.format) + + @skip_if_no_buffer_interface + def test_scalar_dim(self): + for scalar, _ in scalars_and_codes: + x = scalar() + mv_x = memoryview(x) + assert_equal(mv_x.itemsize, np.dtype(scalar).itemsize) + assert_equal(mv_x.ndim, 0) + assert_equal(mv_x.shape, ()) + assert_equal(mv_x.strides, ()) + assert_equal(mv_x.suboffsets, ()) + + @skip_if_no_buffer_interface + def test_scalar_known_code(self): + for scalar, code in scalars_and_codes: + x = scalar() + mv_x = memoryview(x) + assert_equal(mv_x.format, code) + + @skip_if_no_buffer_interface + def test_void_scalar_structured_data(self): + dt = np.dtype([('name', np.unicode_, 16), ('grades', np.float64, (2,))]) + x = np.array(('ndarray_scalar', (1.2, 3.0)), dtype=dt)[()] + assert_(isinstance(x, np.void)) + mv_x = memoryview(x) + expected_size = 16 * np.dtype((np.unicode_, 1)).itemsize + expected_size += 2 * np.dtype((np.float64, 1)).itemsize + assert_equal(mv_x.itemsize, expected_size) + assert_equal(mv_x.ndim, 0) + assert_equal(mv_x.shape, ()) + assert_equal(mv_x.strides, ()) + assert_equal(mv_x.suboffsets, ()) + + # check scalar format string against ndarray format string + a = np.array([('Sarah', (8.0, 7.0)), ('John', (6.0, 7.0))], dtype=dt) + assert_(isinstance(a, np.ndarray)) + mv_a = memoryview(a) + assert_equal(mv_x.itemsize, mv_a.itemsize) + assert_equal(mv_x.format, mv_a.format) + +if __name__ == "__main__": + run_module_suite() diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index 7e1bfbdbe..576ecaf0d 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -4,9 +4,9 @@ import warnings import itertools import numpy as np -import numpy.core.umath_tests as umt -import numpy.core.operand_flag_tests as opflag_tests -from numpy.core.test_rational import rational, test_add, test_add_rationals +import numpy.core._umath_tests as umt +import numpy.core._operand_flag_tests as opflag_tests +import numpy.core._rational_tests as _rational_tests from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_raises, assert_array_equal, assert_almost_equal, assert_array_almost_equal, @@ -42,8 +42,9 @@ class TestUfunc(object): assert_(pickle.loads(pickle.dumps(np.sin)) is np.sin) # Check that ufunc not defined in the top level numpy namespace such as - # numpy.core.test_rational.test_add can also be pickled - assert_(pickle.loads(pickle.dumps(test_add)) is test_add) + # numpy.core._rational_tests.test_add can also be pickled + res = pickle.loads(pickle.dumps(_rational_tests.test_add)) + assert_(res is _rational_tests.test_add) def test_pickle_withstring(self): import pickle @@ -625,7 +626,7 @@ class TestUfunc(object): assert_array_equal(c, (a * b).sum(0)) c = in1d(a, b, axes=[0, 2]) assert_array_equal(c, (a.transpose(1, 2, 0) * b).sum(-1)) - # Check errors for inproperly constructed axes arguments. + # Check errors for improperly constructed axes arguments. # should have list. assert_raises(TypeError, in1d, a, b, axes=-1) # needs enough elements @@ -670,7 +671,7 @@ class TestUfunc(object): d = mm(a, b, out=c, axes=[(-2, -1), (-2, -1), (3, 0)]) assert_(c is d) assert_array_equal(c, np.matmul(a, b).transpose(3, 0, 1, 2)) - # Check errors for inproperly constructed axes arguments. + # Check errors for improperly constructed axes arguments. # wrong argument assert_raises(TypeError, mm, a, b, axis=1) # axes should be list @@ -1143,15 +1144,17 @@ class TestUfunc(object): a = np.array([0, 1, 2], dtype='i8') b = np.array([0, 1, 2], dtype='i8') - c = np.empty(3, dtype=rational) + c = np.empty(3, dtype=_rational_tests.rational) # Output must be specified so numpy knows what # ufunc signature to look for - result = test_add(a, b, c) - assert_equal(result, np.array([0, 2, 4], dtype=rational)) + result = _rational_tests.test_add(a, b, c) + target = np.array([0, 2, 4], dtype=_rational_tests.rational) + assert_equal(result, target) # no output type should raise TypeError - assert_raises(TypeError, test_add, a, b) + with assert_raises(TypeError): + _rational_tests.test_add(a, b) def test_operand_flags(self): a = np.arange(16, dtype='l').reshape(4, 4) @@ -1167,7 +1170,7 @@ class TestUfunc(object): assert_equal(a, 10) def test_struct_ufunc(self): - import numpy.core.struct_ufunc_test as struct_ufunc + import numpy.core._struct_ufunc_tests as struct_ufunc a = np.array([(1, 2, 3)], dtype='u8,u8,u8') b = np.array([(1, 2, 3)], dtype='u8,u8,u8') @@ -1176,20 +1179,30 @@ class TestUfunc(object): assert_equal(result, np.array([(2, 4, 6)], dtype='u8,u8,u8')) def test_custom_ufunc(self): - a = np.array([rational(1, 2), rational(1, 3), rational(1, 4)], - dtype=rational) - b = np.array([rational(1, 2), rational(1, 3), rational(1, 4)], - dtype=rational) - - result = test_add_rationals(a, b) - expected = np.array([rational(1), rational(2, 3), rational(1, 2)], - dtype=rational) + a = np.array( + [_rational_tests.rational(1, 2), + _rational_tests.rational(1, 3), + _rational_tests.rational(1, 4)], + dtype=_rational_tests.rational) + b = np.array( + [_rational_tests.rational(1, 2), + _rational_tests.rational(1, 3), + _rational_tests.rational(1, 4)], + dtype=_rational_tests.rational) + + result = _rational_tests.test_add_rationals(a, b) + expected = np.array( + [_rational_tests.rational(1), + _rational_tests.rational(2, 3), + _rational_tests.rational(1, 2)], + dtype=_rational_tests.rational) assert_equal(result, expected) def test_custom_ufunc_forced_sig(self): # gh-9351 - looking for a non-first userloop would previously hang - assert_raises(TypeError, - np.multiply, rational(1), 1, signature=(rational, int, None)) + with assert_raises(TypeError): + np.multiply(_rational_tests.rational(1), 1, + signature=(_rational_tests.rational, int, None)) def test_custom_array_like(self): diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index ac97b8b0d..fe7768e53 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -7,7 +7,7 @@ import fnmatch import itertools import numpy.core.umath as ncu -from numpy.core import umath_tests as ncu_tests +from numpy.core import _umath_tests as ncu_tests import numpy as np from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_raises, @@ -24,10 +24,10 @@ def on_powerpc(): class _FilterInvalids(object): - def setUp(self): + def setup(self): self.olderr = np.seterr(invalid='ignore') - def tearDown(self): + def teardown(self): np.seterr(**self.olderr) diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py index fb3b6577c..1b098a120 100644 --- a/numpy/core/tests/test_umath_complex.py +++ b/numpy/core/tests/test_umath_complex.py @@ -351,10 +351,10 @@ class TestCsqrt(object): # cuts first) class TestCpow(object): - def setUp(self): + def setup(self): self.olderr = np.seterr(invalid='ignore') - def tearDown(self): + def teardown(self): np.seterr(**self.olderr) def test_simple(self): @@ -391,10 +391,10 @@ class TestCpow(object): assert_almost_equal(n_r[i], p_r[i], err_msg='Loop %d\n' % i) class TestCabs(object): - def setUp(self): + def setup(self): self.olderr = np.seterr(invalid='ignore') - def tearDown(self): + def teardown(self): np.seterr(**self.olderr) def test_simple(self): diff --git a/numpy/distutils/__init__.py b/numpy/distutils/__init__.py index 0450334ff..d5921b399 100644 --- a/numpy/distutils/__init__.py +++ b/numpy/distutils/__init__.py @@ -17,7 +17,7 @@ try: # Normally numpy is installed if the above import works, but an interrupted # in-place build could also have left a __config__.py. In that case the # next import may still fail, so keep it inside the try block. - from numpy.testing.nosetester import _numpy_tester + from numpy.testing import _numpy_tester test = _numpy_tester().test except ImportError: pass @@ -26,7 +26,7 @@ except ImportError: def customized_fcompiler(plat=None, compiler=None): from numpy.distutils.fcompiler import new_fcompiler c = new_fcompiler(plat=plat, compiler=compiler) - c.customize() + c.customize() return c def customized_ccompiler(plat=None, compiler=None): diff --git a/numpy/distutils/tests/test_system_info.py b/numpy/distutils/tests/test_system_info.py index 50befa15b..c6a9b9915 100644 --- a/numpy/distutils/tests/test_system_info.py +++ b/numpy/distutils/tests/test_system_info.py @@ -160,7 +160,7 @@ class TestSystemInfoReading(object): self.c_temp1 = site_and_parse(get_class('temp1'), self._sitecfg) self.c_temp2 = site_and_parse(get_class('temp2'), self._sitecfg) - def tearDown(self): + def teardown(self): # Do each removal separately try: shutil.rmtree(self._dir1) diff --git a/numpy/doc/subclassing.py b/numpy/doc/subclassing.py index 467e31cea..3be3d94b3 100644 --- a/numpy/doc/subclassing.py +++ b/numpy/doc/subclassing.py @@ -562,7 +562,7 @@ pass on to ``A.__array_ufunc__``, the ``super`` call in ``A`` would go to Prior to numpy 1.13, the behaviour of ufuncs could only be tuned using ``__array_wrap__`` and ``__array_prepare__``. These two allowed one to -change the output type of a ufunc, but, in constrast to +change the output type of a ufunc, but, in contrast to ``__array_ufunc__``, did not allow one to make any changes to the inputs. It is hoped to eventually deprecate these, but ``__array_wrap__`` is also used by other numpy functions and methods, such as ``squeeze``, so at the diff --git a/numpy/f2py/__init__.py b/numpy/f2py/__init__.py index 250c4322b..86cc45b42 100644 --- a/numpy/f2py/__init__.py +++ b/numpy/f2py/__init__.py @@ -71,4 +71,3 @@ def compile(source, from numpy.testing import _numpy_tester test = _numpy_tester().test -bench = _numpy_tester().bench diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py index dc560f98e..19ce8c145 100755 --- a/numpy/f2py/crackfortran.py +++ b/numpy/f2py/crackfortran.py @@ -612,7 +612,7 @@ multilinepattern = re.compile( def split_by_unquoted(line, characters): """ Splits the line into (line[:i], line[i:]), - where i is the index of first occurence of one of the characters + where i is the index of first occurrence of one of the characters not within quotes, or len(line) if no such index exists """ assert not (set('"\'') & set(characters)), "cannot split by unquoted quotes" diff --git a/numpy/fft/__init__.py b/numpy/fft/__init__.py index 72d61a728..d1716bd4b 100644 --- a/numpy/fft/__init__.py +++ b/numpy/fft/__init__.py @@ -8,4 +8,3 @@ from .helper import * from numpy.testing import _numpy_tester test = _numpy_tester().test -bench = _numpy_tester().bench diff --git a/numpy/fft/fftpack.py b/numpy/fft/fftpack.py index bd116b9cb..e17e1cb34 100644 --- a/numpy/fft/fftpack.py +++ b/numpy/fft/fftpack.py @@ -69,13 +69,13 @@ def _raw_fft(a, n=None, axis=-1, init_function=fftpack.cffti, if s[axis] > n: index = [slice(None)]*len(s) index[axis] = slice(0, n) - a = a[index] + a = a[tuple(index)] else: index = [slice(None)]*len(s) index[axis] = slice(0, s[axis]) s[axis] = n z = zeros(s, a.dtype.char) - z[index] = a + z[tuple(index)] = a a = z if axis != -1: diff --git a/numpy/fft/helper.py b/numpy/fft/helper.py index 1a1266e12..729121f31 100644 --- a/numpy/fft/helper.py +++ b/numpy/fft/helper.py @@ -5,7 +5,10 @@ Discrete Fourier Transforms - helper.py from __future__ import division, absolute_import, print_function import collections -import threading +try: + import threading +except ImportError: + import dummy_threading as threading from numpy.compat import integer_types from numpy.core import integer, empty, arange, asarray, roll diff --git a/numpy/lib/__init__.py b/numpy/lib/__init__.py index cc05232a2..0c2e6dfab 100644 --- a/numpy/lib/__init__.py +++ b/numpy/lib/__init__.py @@ -48,4 +48,3 @@ __all__ += histograms.__all__ from numpy.testing import _numpy_tester test = _numpy_tester().test -bench = _numpy_tester().bench diff --git a/numpy/lib/arraypad.py b/numpy/lib/arraypad.py index cdc354a02..daaa68d06 100644 --- a/numpy/lib/arraypad.py +++ b/numpy/lib/arraypad.py @@ -1346,9 +1346,9 @@ def pad(array, pad_width, mode, **kwargs): # Create a new padded array rank = list(range(narray.ndim)) total_dim_increase = [np.sum(pad_width[i]) for i in rank] - offset_slices = [slice(pad_width[i][0], - pad_width[i][0] + narray.shape[i]) - for i in rank] + offset_slices = tuple( + slice(pad_width[i][0], pad_width[i][0] + narray.shape[i]) + for i in rank) new_shape = np.array(narray.shape) + total_dim_increase newmat = np.zeros(new_shape, narray.dtype) diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 422a87322..8440be52e 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1,6 +1,11 @@ from __future__ import division, absolute_import, print_function -import collections +try: + # Accessing collections abstact classes from collections + # has been deprecated since Python 3.3 + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc import re import sys import warnings @@ -179,21 +184,18 @@ def flip(m, axis): >>> A array([[[0, 1], [2, 3]], - [[4, 5], [6, 7]]]) >>> flip(A, 0) array([[[4, 5], [6, 7]], - [[0, 1], [2, 3]]]) >>> flip(A, 1) array([[[2, 3], [0, 1]], - [[6, 7], [4, 5]]]) @@ -547,7 +549,7 @@ def piecewise(x, condlist, funclist, *args, **kw): y = zeros(x.shape, x.dtype) for k in range(n): item = funclist[k] - if not isinstance(item, collections.Callable): + if not isinstance(item, collections_abc.Callable): y[condlist[k]] = item else: vals = x[condlist[k]] @@ -970,7 +972,7 @@ def gradient(f, *varargs, **kwargs): slice4[axis] = slice(2, None) if uniform_spacing: - out[slice1] = (f[slice4] - f[slice2]) / (2. * ax_dx) + out[tuple(slice1)] = (f[tuple(slice4)] - f[tuple(slice2)]) / (2. * ax_dx) else: dx1 = ax_dx[0:-1] dx2 = ax_dx[1:] @@ -982,7 +984,7 @@ def gradient(f, *varargs, **kwargs): shape[axis] = -1 a.shape = b.shape = c.shape = shape # 1D equivalent -- out[1:-1] = a * f[:-2] + b * f[1:-1] + c * f[2:] - out[slice1] = a * f[slice2] + b * f[slice3] + c * f[slice4] + out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)] # Numerical differentiation: 1st order edges if edge_order == 1: @@ -991,14 +993,14 @@ def gradient(f, *varargs, **kwargs): slice3[axis] = 0 dx_0 = ax_dx if uniform_spacing else ax_dx[0] # 1D equivalent -- out[0] = (f[1] - f[0]) / (x[1] - x[0]) - out[slice1] = (f[slice2] - f[slice3]) / dx_0 + out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_0 slice1[axis] = -1 slice2[axis] = -1 slice3[axis] = -2 dx_n = ax_dx if uniform_spacing else ax_dx[-1] # 1D equivalent -- out[-1] = (f[-1] - f[-2]) / (x[-1] - x[-2]) - out[slice1] = (f[slice2] - f[slice3]) / dx_n + out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_n # Numerical differentiation: 2nd order edges else: @@ -1017,7 +1019,7 @@ def gradient(f, *varargs, **kwargs): b = (dx1 + dx2) / (dx1 * dx2) c = - dx1 / (dx2 * (dx1 + dx2)) # 1D equivalent -- out[0] = a * f[0] + b * f[1] + c * f[2] - out[slice1] = a * f[slice2] + b * f[slice3] + c * f[slice4] + out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)] slice1[axis] = -1 slice2[axis] = -3 @@ -1034,7 +1036,7 @@ def gradient(f, *varargs, **kwargs): b = - (dx2 + dx1) / (dx1 * dx2) c = (2. * dx2 + dx1) / (dx2 * (dx1 + dx2)) # 1D equivalent -- out[-1] = a * f[-3] + b * f[-2] + c * f[-1] - out[slice1] = a * f[slice2] + b * f[slice3] + c * f[slice4] + out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)] outvals.append(out) @@ -1372,6 +1374,7 @@ def unwrap(p, discont=pi, axis=-1): dd = diff(p, axis=axis) slice1 = [slice(None, None)]*nd # full slices slice1[axis] = slice(1, None) + slice1 = tuple(slice1) ddmod = mod(dd + pi, 2*pi) - pi _nx.copyto(ddmod, pi, where=(ddmod == -pi) & (dd > 0)) ph_correct = ddmod - dd @@ -2296,7 +2299,7 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, else: X_T = (X*w).T c = dot(X, X_T.conj()) - c *= 1. / np.float64(fact) + c *= np.true_divide(1, fact) return c.squeeze() @@ -3354,6 +3357,7 @@ def _median(a, axis=None, out=None, overwrite_input=False): indexer[axis] = slice(index, index+1) else: indexer[axis] = slice(index-1, index+1) + indexer = tuple(indexer) # Check if the array contains any nan's if np.issubdtype(a.dtype, np.inexact) and sz > 0: @@ -3473,6 +3477,33 @@ def percentile(a, q, axis=None, out=None, array([ 7., 2.]) >>> assert not np.all(a == b) + The different types of interpolation can be visualized graphically: + + ..plot:: + import matplotlib.pyplot as plt + + a = np.arange(4) + p = np.linspace(0, 100, 6001) + ax = plt.gca() + lines = [ + ('linear', None) + ('higher', '--') + ('lower', '--') + ('nearest', '-.') + ('midpoint', '-.') + ] + for interpolation, style in lines: + ax.plot( + p, np.percentile(a, p, interpolation=interpolation), + label=interpolation, linestyle=style) + ax.set( + title='Interpolation methods for list: ' + str(a), + xlabel='Percentile', + ylabel='List item returned', + yticks=a) + ax.legend() + plt.show() + """ q = np.true_divide(q, 100.0) # handles the asarray for us too if not _quantile_is_valid(q): @@ -3719,12 +3750,12 @@ def trapz(y, x=None, dx=1.0, axis=-1): slice1[axis] = slice(1, None) slice2[axis] = slice(None, -1) try: - ret = (d * (y[slice1] + y[slice2]) / 2.0).sum(axis) + ret = (d * (y[tuple(slice1)] + y[tuple(slice2)]) / 2.0).sum(axis) except ValueError: # Operations didn't work, cast to ndarray d = np.asarray(d) y = np.asarray(y) - ret = add.reduce(d * (y[slice1]+y[slice2])/2.0, axis) + ret = add.reduce(d * (y[tuple(slice1)]+y[tuple(slice2)])/2.0, axis) return ret @@ -4013,7 +4044,7 @@ def delete(arr, obj, axis=None): pass else: slobj[axis] = slice(None, start) - new[slobj] = arr[slobj] + new[tuple(slobj)] = arr[tuple(slobj)] # copy end chunck if stop == N: pass @@ -4021,7 +4052,7 @@ def delete(arr, obj, axis=None): slobj[axis] = slice(stop-numtodel, None) slobj2 = [slice(None)]*ndim slobj2[axis] = slice(stop, None) - new[slobj] = arr[slobj2] + new[tuple(slobj)] = arr[tuple(slobj2)] # copy middle pieces if step == 1: pass @@ -4031,9 +4062,9 @@ def delete(arr, obj, axis=None): slobj[axis] = slice(start, stop-numtodel) slobj2 = [slice(None)]*ndim slobj2[axis] = slice(start, stop) - arr = arr[slobj2] + arr = arr[tuple(slobj2)] slobj2[axis] = keep - new[slobj] = arr[slobj2] + new[tuple(slobj)] = arr[tuple(slobj2)] if wrap: return wrap(new) else: @@ -4060,11 +4091,11 @@ def delete(arr, obj, axis=None): newshape[axis] -= 1 new = empty(newshape, arr.dtype, arrorder) slobj[axis] = slice(None, obj) - new[slobj] = arr[slobj] + new[tuple(slobj)] = arr[tuple(slobj)] slobj[axis] = slice(obj, None) slobj2 = [slice(None)]*ndim slobj2[axis] = slice(obj+1, None) - new[slobj] = arr[slobj2] + new[tuple(slobj)] = arr[tuple(slobj2)] else: if obj.size == 0 and not isinstance(_obj, np.ndarray): obj = obj.astype(intp) @@ -4096,7 +4127,7 @@ def delete(arr, obj, axis=None): keep[obj, ] = False slobj[axis] = keep - new = arr[slobj] + new = arr[tuple(slobj)] if wrap: return wrap(new) @@ -4267,13 +4298,13 @@ def insert(arr, obj, values, axis=None): newshape[axis] += numnew new = empty(newshape, arr.dtype, arrorder) slobj[axis] = slice(None, index) - new[slobj] = arr[slobj] + new[tuple(slobj)] = arr[tuple(slobj)] slobj[axis] = slice(index, index+numnew) - new[slobj] = values + new[tuple(slobj)] = values slobj[axis] = slice(index+numnew, None) slobj2 = [slice(None)] * ndim slobj2[axis] = slice(index, None) - new[slobj] = arr[slobj2] + new[tuple(slobj)] = arr[tuple(slobj2)] if wrap: return wrap(new) return new @@ -4302,8 +4333,8 @@ def insert(arr, obj, values, axis=None): slobj2 = [slice(None)]*ndim slobj[axis] = indices slobj2[axis] = old_mask - new[slobj] = values - new[slobj2] = arr + new[tuple(slobj)] = values + new[tuple(slobj2)] = arr if wrap: return wrap(new) diff --git a/numpy/lib/histograms.py b/numpy/lib/histograms.py index ccc6c0616..aa067a431 100644 --- a/numpy/lib/histograms.py +++ b/numpy/lib/histograms.py @@ -8,7 +8,7 @@ import operator import numpy as np from numpy.compat.py3k import basestring -__all__ = ['histogram', 'histogramdd'] +__all__ = ['histogram', 'histogramdd', 'histogram_bin_edges'] def _hist_bin_sqrt(x): @@ -348,10 +348,9 @@ def _search_sorted_inclusive(a, v): )) -def histogram(a, bins=10, range=None, normed=False, weights=None, - density=None): +def histogram_bin_edges(a, bins=10, range=None, weights=None): r""" - Compute the histogram of a set of data. + Function to calculate only the edges of the bins used by the `histogram` function. Parameters ---------- @@ -363,9 +362,7 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, sequence, it defines the bin edges, including the rightmost edge, allowing for non-uniform bin widths. - .. versionadded:: 1.11.0 - - If `bins` is a string from the list below, `histogram` will use + If `bins` is a string from the list below, `histogram_bin_edges` will use the method chosen to calculate the optimal bin width and consequently the number of bins (see `Notes` for more detail on the estimators) from the data that falls within the requested @@ -412,57 +409,24 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, computation as well. While bin width is computed to be optimal based on the actual data within `range`, the bin count will fill the entire range including portions containing no data. - normed : bool, optional - This keyword is deprecated in NumPy 1.6.0 due to confusing/buggy - behavior. It will be removed in NumPy 2.0.0. Use the ``density`` - keyword instead. If ``False``, the result will contain the - number of samples in each bin. If ``True``, the result is the - value of the probability *density* function at the bin, - normalized such that the *integral* over the range is 1. Note - that this latter behavior is known to be buggy with unequal bin - widths; use ``density`` instead. + weights : array_like, optional An array of weights, of the same shape as `a`. Each value in `a` only contributes its associated weight towards the bin count - (instead of 1). If `density` is True, the weights are - normalized, so that the integral of the density over the range - remains 1. - density : bool, optional - If ``False``, the result will contain the number of samples in - each bin. If ``True``, the result is the value of the - probability *density* function at the bin, normalized such that - the *integral* over the range is 1. Note that the sum of the - histogram values will not be equal to 1 unless bins of unity - width are chosen; it is not a probability *mass* function. - - Overrides the ``normed`` keyword if given. + (instead of 1). This is currently not used by any of the bin estimators, + but may be in the future. Returns ------- - hist : array - The values of the histogram. See `density` and `weights` for a - description of the possible semantics. bin_edges : array of dtype float - Return the bin edges ``(length(hist)+1)``. - + The edges to pass into `histogram` See Also -------- - histogramdd, bincount, searchsorted, digitize + histogram Notes ----- - All but the last (righthand-most) bin is half-open. In other words, - if `bins` is:: - - [1, 2, 3, 4] - - then the first bin is ``[1, 2)`` (including 1, but excluding 2) and - the second ``[2, 3)``. The last bin, however, is ``[3, 4]``, which - *includes* 4. - - .. versionadded:: 1.11.0 - The methods to estimate the optimal number of bins are well founded in literature, and are inspired by the choices R provides for histogram visualisation. Note that having the number of bins @@ -533,6 +497,134 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, Examples -------- + >>> arr = np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]) + >>> np.histogram_bin_edges(arr, bins='auto', range=(0, 1)) + array([0. , 0.25, 0.5 , 0.75, 1. ]) + >>> np.histogram_bin_edges(arr, bins=2) + array([0. , 2.5, 5. ]) + + For consistency with histogram, an array of pre-computed bins is + passed through unmodified: + + >>> np.histogram_bin_edges(arr, [1, 2]) + array([1, 2]) + + This function allows one set of bins to be computed, and reused across + multiple histograms: + + >>> shared_bins = np.histogram_bin_edges(arr, bins='auto') + >>> shared_bins + array([0., 1., 2., 3., 4., 5.]) + + >>> group_id = np.array([0, 1, 1, 0, 1, 1, 0, 1, 1]) + >>> hist_0, _ = np.histogram(arr[group_id == 0], bins=shared_bins) + >>> hist_1, _ = np.histogram(arr[group_id == 1], bins=shared_bins) + + >>> hist_0; hist_1 + array([1, 1, 0, 1, 0]) + array([2, 0, 1, 1, 2]) + + Which gives more easily comparable results than using separate bins for + each histogram: + + >>> hist_0, bins_0 = np.histogram(arr[group_id == 0], bins='auto') + >>> hist_1, bins_1 = np.histogram(arr[group_id == 1], bins='auto') + >>> hist_0; hist1 + array([1, 1, 1]) + array([2, 1, 1, 2]) + >>> bins_0; bins_1 + array([0., 1., 2., 3.]) + array([0. , 1.25, 2.5 , 3.75, 5. ]) + + """ + a, weights = _ravel_and_check_weights(a, weights) + bin_edges, _ = _get_bin_edges(a, bins, range, weights) + return bin_edges + + +def histogram(a, bins=10, range=None, normed=False, weights=None, + density=None): + r""" + Compute the histogram of a set of data. + + Parameters + ---------- + a : array_like + Input data. The histogram is computed over the flattened array. + bins : int or sequence of scalars or str, optional + If `bins` is an int, it defines the number of equal-width + bins in the given range (10, by default). If `bins` is a + sequence, it defines the bin edges, including the rightmost + edge, allowing for non-uniform bin widths. + + .. versionadded:: 1.11.0 + + If `bins` is a string, it defines the method used to calculate the + optimal bin width, as defined by `histogram_bin_edges`. + + range : (float, float), optional + The lower and upper range of the bins. If not provided, range + is simply ``(a.min(), a.max())``. Values outside the range are + ignored. The first element of the range must be less than or + equal to the second. `range` affects the automatic bin + computation as well. While bin width is computed to be optimal + based on the actual data within `range`, the bin count will fill + the entire range including portions containing no data. + normed : bool, optional + + .. deprecated:: 1.6.0 + + This keyword is deprecated in NumPy 1.6.0 due to confusing/buggy + behavior. It will be removed in NumPy 2.0.0. Use the ``density`` + keyword instead. If ``False``, the result will contain the + number of samples in each bin. If ``True``, the result is the + value of the probability *density* function at the bin, + normalized such that the *integral* over the range is 1. Note + that this latter behavior is known to be buggy with unequal bin + widths; use ``density`` instead. + weights : array_like, optional + An array of weights, of the same shape as `a`. Each value in + `a` only contributes its associated weight towards the bin count + (instead of 1). If `density` is True, the weights are + normalized, so that the integral of the density over the range + remains 1. + density : bool, optional + If ``False``, the result will contain the number of samples in + each bin. If ``True``, the result is the value of the + probability *density* function at the bin, normalized such that + the *integral* over the range is 1. Note that the sum of the + histogram values will not be equal to 1 unless bins of unity + width are chosen; it is not a probability *mass* function. + + Overrides the ``normed`` keyword if given. + + Returns + ------- + hist : array + The values of the histogram. See `density` and `weights` for a + description of the possible semantics. + bin_edges : array of dtype float + Return the bin edges ``(length(hist)+1)``. + + + See Also + -------- + histogramdd, bincount, searchsorted, digitize, histogram_bin_edges + + Notes + ----- + All but the last (righthand-most) bin is half-open. In other words, + if `bins` is:: + + [1, 2, 3, 4] + + then the first bin is ``[1, 2)`` (including 1, but excluding 2) and + the second ``[2, 3)``. The last bin, however, is ``[3, 4]``, which + *includes* 4. + + + Examples + -------- >>> np.histogram([1, 2, 1], bins=[0, 1, 2, 3]) (array([0, 2, 1]), array([0, 1, 2, 3])) >>> np.histogram(np.arange(4), bins=np.arange(5), density=True) @@ -860,7 +952,7 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None): ni[i], ni[j] = ni[j], ni[i] # Remove outliers (indices 0 and -1 for each dimension). - core = D*[slice(1, -1)] + core = D*(slice(1, -1),) hist = hist[core] # Normalize if normed is True diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 959574594..0f338d781 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1104,7 +1104,7 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, nshape = list(X.shape) pos = nshape[0] nshape[0] += len(x) - X.resize(nshape) + X.resize(nshape, refcheck=False) X[pos:, ...] = x finally: if fown: diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index 49b450175..c28257c6d 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -11,7 +11,7 @@ from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_array_equal, assert_almost_equal, assert_array_almost_equal, assert_raises, assert_allclose, assert_array_max_ulp, assert_warns, assert_raises_regex, - dec, suppress_warnings, HAS_REFCOUNT, + dec, suppress_warnings, ) import numpy.lib.function_base as nfb from numpy.random import rand @@ -1752,7 +1752,9 @@ class TestCov(object): def test_complex(self): x = np.array([[1, 2, 3], [1j, 2j, 3j]]) - assert_allclose(cov(x), np.array([[1., -1.j], [1.j, 1.]])) + res = np.array([[1., -1.j], [1.j, 1.]]) + assert_allclose(cov(x), res) + assert_allclose(cov(x, aweights=np.ones(3)), res) def test_xy(self): x = np.array([[1, 2, 3]]) @@ -2141,7 +2143,7 @@ class TestBincount(object): "must not be negative", lambda: np.bincount(x, minlength=-1)) - @dec.skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") + @dec._needs_refcount def test_dtype_reference_leaks(self): # gh-6805 intp_refcount = sys.getrefcount(np.dtype(np.intp)) diff --git a/numpy/lib/tests/test_histograms.py b/numpy/lib/tests/test_histograms.py index a2c684a20..4f7af214c 100644 --- a/numpy/lib/tests/test_histograms.py +++ b/numpy/lib/tests/test_histograms.py @@ -2,7 +2,7 @@ from __future__ import division, absolute_import, print_function import numpy as np -from numpy.lib.histograms import histogram, histogramdd +from numpy.lib.histograms import histogram, histogramdd, histogram_bin_edges from numpy.testing import ( run_module_suite, assert_, assert_equal, assert_array_equal, assert_almost_equal, assert_array_almost_equal, assert_raises, @@ -346,6 +346,20 @@ class TestHistogram(object): self.do_precision(np.single, np.longdouble) self.do_precision(np.double, np.longdouble) + def test_histogram_bin_edges(self): + hist, e = histogram([1, 2, 3, 4], [1, 2]) + edges = histogram_bin_edges([1, 2, 3, 4], [1, 2]) + assert_array_equal(edges, e) + + arr = np.array([0., 0., 0., 1., 2., 3., 3., 4., 5.]) + hist, e = histogram(arr, bins=30, range=(-0.5, 5)) + edges = histogram_bin_edges(arr, bins=30, range=(-0.5, 5)) + assert_array_equal(edges, e) + + hist, e = histogram(arr, bins='auto', range=(0, 1)) + edges = histogram_bin_edges(arr, bins='auto', range=(0, 1)) + assert_array_equal(edges, e) + class TestHistogramOptimBinNums(object): """ diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py index ae40cf73b..5d7dc32dd 100644 --- a/numpy/lib/tests/test_io.py +++ b/numpy/lib/tests/test_io.py @@ -22,7 +22,7 @@ from numpy.ma.testutils import assert_equal from numpy.testing import ( run_module_suite, assert_warns, assert_, SkipTest, assert_raises_regex, assert_raises, assert_allclose, - assert_array_equal, temppath, tempdir, dec, IS_PYPY, suppress_warnings + assert_array_equal, temppath, tempdir, dec, IS_PYPY, suppress_warnings, ) @@ -599,11 +599,11 @@ class LoadTxtBase(object): class TestLoadTxt(LoadTxtBase): loadfunc = staticmethod(np.loadtxt) - def setUp(self): + def setup(self): # lower chunksize for testing self.orig_chunk = np.lib.npyio._loadtxt_chunksize np.lib.npyio._loadtxt_chunksize = 1 - def tearDown(self): + def teardown(self): np.lib.npyio._loadtxt_chunksize = self.orig_chunk def test_record(self): @@ -2364,6 +2364,7 @@ def test_npzfile_dict(): assert_('x' in z.keys()) +@dec._needs_refcount def test_load_refcount(): # Check that objects returned by np.load are directly freed based on # their refcount, rather than needing the gc to collect them. diff --git a/numpy/lib/tests/test_stride_tricks.py b/numpy/lib/tests/test_stride_tricks.py index 0599324d7..475119481 100644 --- a/numpy/lib/tests/test_stride_tricks.py +++ b/numpy/lib/tests/test_stride_tricks.py @@ -1,7 +1,7 @@ from __future__ import division, absolute_import, print_function import numpy as np -from numpy.core.test_rational import rational +from numpy.core._rational_tests import rational from numpy.testing import ( run_module_suite, assert_equal, assert_array_equal, assert_raises, assert_ diff --git a/numpy/linalg/__init__.py b/numpy/linalg/__init__.py index 2537926c5..1510a8448 100644 --- a/numpy/linalg/__init__.py +++ b/numpy/linalg/__init__.py @@ -52,4 +52,3 @@ from .linalg import * from numpy.testing import _numpy_tester test = _numpy_tester().test -bench = _numpy_tester().bench diff --git a/numpy/ma/__init__.py b/numpy/ma/__init__.py index fbefc47a4..0689f2932 100644 --- a/numpy/ma/__init__.py +++ b/numpy/ma/__init__.py @@ -53,4 +53,3 @@ __all__ += extras.__all__ from numpy.testing import _numpy_tester test = _numpy_tester().test -bench = _numpy_tester().bench diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 698d39cda..91cf8ed0f 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -26,6 +26,7 @@ import sys import operator import warnings import textwrap +import re from functools import reduce if sys.version_info[0] >= 3: @@ -132,19 +133,17 @@ def doc_note(initialdoc, note): if note is None: return initialdoc - # FIXME: disable this function for the moment until we figure out what to - # do with it. Currently it may result in duplicate Notes sections or Notes - # sections in the wrong place - return initialdoc + notesplit = re.split(r'\n\s*?Notes\n\s*?-----', initialdoc) - newdoc = """ - %s - - Notes + notedoc = """\ +Notes ----- - %s - """ - return newdoc % (initialdoc, note) + %s""" % note + + if len(notesplit) > 1: + notedoc = '\n\n ' + notedoc + '\n' + + return ''.join(notesplit[:1] + [notedoc] + notesplit[1:]) def get_object_signature(obj): @@ -5318,7 +5317,7 @@ class MaskedArray(ndarray): originally intended. Until then, the axis should be given explicitly when ``arr.ndim > 1``, to avoid a FutureWarning. - kind : {'quicksort', 'mergesort', 'heapsort'}, optional + kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional Sorting algorithm. order : list, optional When `a` is an array with fields defined, this argument specifies @@ -5469,7 +5468,7 @@ class MaskedArray(ndarray): axis : int, optional Axis along which to sort. If None, the array is flattened before sorting. The default is -1, which sorts along the last axis. - kind : {'quicksort', 'mergesort', 'heapsort'}, optional + kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional Sorting algorithm. Default is 'quicksort'. order : list, optional When `a` is a structured array, this argument specifies which fields @@ -5538,6 +5537,7 @@ class MaskedArray(ndarray): else: idx = list(np.ix_(*[np.arange(x) for x in self.shape])) idx[axis] = sidx + idx = tuple(idx) self[...] = self[idx] diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py index 99f5234d1..8272dced9 100644 --- a/numpy/ma/extras.py +++ b/numpy/ma/extras.py @@ -724,6 +724,7 @@ def _median(a, axis=None, out=None, overwrite_input=False): # as median (which is mean of empty slice = nan) indexer = [slice(None)] * asorted.ndim indexer[axis] = slice(0, 0) + indexer = tuple(indexer) return np.ma.mean(asorted[indexer], axis=axis, out=out) if asorted.ndim == 1: @@ -1619,7 +1620,10 @@ def flatnotmasked_contiguous(a): Returns ------- slice_list : list - A sorted sequence of slices (start index, end index). + A sorted sequence of `slice` objects (start index, end index). + + ..versionchanged:: 1.15.0 + Now returns an empty list instead of None for a fully masked array See Also -------- @@ -1634,7 +1638,7 @@ def flatnotmasked_contiguous(a): -------- >>> a = np.ma.arange(10) >>> np.ma.flatnotmasked_contiguous(a) - slice(0, 10, None) + [slice(0, 10, None)] >>> mask = (a < 3) | (a > 8) | (a == 5) >>> a[mask] = np.ma.masked @@ -1644,13 +1648,13 @@ def flatnotmasked_contiguous(a): >>> np.ma.flatnotmasked_contiguous(a) [slice(3, 5, None), slice(6, 9, None)] >>> a[:] = np.ma.masked - >>> print(np.ma.flatnotmasked_edges(a)) - None + >>> np.ma.flatnotmasked_contiguous(a) + [] """ m = getmask(a) if m is nomask: - return slice(0, a.size, None) + return [slice(0, a.size)] i = 0 result = [] for (k, g) in itertools.groupby(m.ravel()): @@ -1658,7 +1662,7 @@ def flatnotmasked_contiguous(a): if not k: result.append(slice(i, i + n)) i += n - return result or None + return result def notmasked_contiguous(a, axis=None): """ @@ -1670,7 +1674,8 @@ def notmasked_contiguous(a, axis=None): The input array. axis : int, optional Axis along which to perform the operation. - If None (default), applies to a flattened version of the array. + If None (default), applies to a flattened version of the array, and this + is the same as `flatnotmasked_contiguous`. Returns ------- @@ -1678,6 +1683,8 @@ def notmasked_contiguous(a, axis=None): A list of slices (start and end indexes) of unmasked indexes in the array. + If the input is 2d and axis is specified, the result is a list of lists. + See Also -------- flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges, @@ -1689,17 +1696,35 @@ def notmasked_contiguous(a, axis=None): Examples -------- - >>> a = np.arange(9).reshape((3, 3)) + >>> a = np.arange(12).reshape((3, 4)) >>> mask = np.zeros_like(a) - >>> mask[1:, 1:] = 1 - + >>> mask[1:, :-1] = 1; mask[0, 1] = 1; mask[-1, 0] = 0 >>> ma = np.ma.array(a, mask=mask) + >>> ma + masked_array( + data=[[0, --, 2, 3], + [--, --, --, 7], + [8, --, --, 11]], + mask=[[False, True, False, False], + [ True, True, True, False], + [False, True, True, False]], + fill_value=999999) >>> np.array(ma[~ma.mask]) - array([0, 1, 2, 3, 6]) + array([ 0, 2, 3, 7, 8, 11]) >>> np.ma.notmasked_contiguous(ma) - [slice(0, 4, None), slice(6, 7, None)] + [slice(0, 1, None), slice(2, 4, None), slice(7, 9, None), slice(11, 12, None)] + + >>> np.ma.notmasked_contiguous(ma, axis=0) + [[slice(0, 1, None), slice(2, 3, None)], # column broken into two segments + [], # fully masked column + [slice(0, 1, None)], + [slice(0, 3, None)]] + >>> np.ma.notmasked_contiguous(ma, axis=1) + [[slice(0, 1, None), slice(2, 4, None)], # row broken into two segments + [slice(3, 4, None)], + [slice(0, 1, None), slice(3, 4, None)]] """ a = asarray(a) nd = a.ndim @@ -1716,7 +1741,7 @@ def notmasked_contiguous(a, axis=None): # for i in range(a.shape[other]): idx[other] = i - result.append(flatnotmasked_contiguous(a[idx]) or None) + result.append(flatnotmasked_contiguous(a[tuple(idx)])) return result diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 4c6bb2b42..ff1e087b5 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -3200,6 +3200,12 @@ class TestMaskedArrayMethods(object): assert_equal(sortedx._data, [1, 2, -2, -1, 0]) assert_equal(sortedx._mask, [1, 1, 0, 0, 0]) + def test_stable_sort(self): + x = array([1, 2, 3, 1, 2, 3], dtype=np.uint8) + expected = array([0, 3, 1, 4, 2, 5]) + computed = argsort(x, kind='stable') + assert_equal(computed, expected) + def test_argsort_matches_sort(self): x = array([1, 4, 2, 3], mask=[0, 1, 0, 0], dtype=np.uint8) diff --git a/numpy/ma/tests/test_extras.py b/numpy/ma/tests/test_extras.py index d1c1aa63e..95319eb65 100644 --- a/numpy/ma/tests/test_extras.py +++ b/numpy/ma/tests/test_extras.py @@ -128,7 +128,10 @@ class TestGeneric(object): a = arange(10) # No mask test = flatnotmasked_contiguous(a) - assert_equal(test, slice(0, a.size)) + assert_equal(test, [slice(0, a.size)]) + # mask of all false + a.mask = np.zeros(10, dtype=bool) + assert_equal(test, [slice(0, a.size)]) # Some mask a[(a < 3) | (a > 8) | (a == 5)] = masked test = flatnotmasked_contiguous(a) @@ -136,7 +139,7 @@ class TestGeneric(object): # a[:] = masked test = flatnotmasked_contiguous(a) - assert_equal(test, None) + assert_equal(test, []) class TestAverage(object): @@ -368,23 +371,32 @@ class TestNotMasked(object): a = masked_array(np.arange(24).reshape(3, 8), mask=[[0, 0, 0, 0, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 1, 0], ]) + [0, 0, 0, 0, 0, 0, 1, 0]]) tmp = notmasked_contiguous(a, None) - assert_equal(tmp[-1], slice(23, 24, None)) - assert_equal(tmp[-2], slice(16, 22, None)) - assert_equal(tmp[-3], slice(0, 4, None)) - # + assert_equal(tmp, [ + slice(0, 4, None), + slice(16, 22, None), + slice(23, 24, None) + ]) + tmp = notmasked_contiguous(a, 0) - assert_(len(tmp[-1]) == 1) - assert_(tmp[-2] is None) - assert_equal(tmp[-3], tmp[-1]) - assert_(len(tmp[0]) == 2) + assert_equal(tmp, [ + [slice(0, 1, None), slice(2, 3, None)], + [slice(0, 1, None), slice(2, 3, None)], + [slice(0, 1, None), slice(2, 3, None)], + [slice(0, 1, None), slice(2, 3, None)], + [slice(2, 3, None)], + [slice(2, 3, None)], + [], + [slice(2, 3, None)] + ]) # tmp = notmasked_contiguous(a, 1) - assert_equal(tmp[0][-1], slice(0, 4, None)) - assert_(tmp[1] is None) - assert_equal(tmp[2][-1], slice(7, 8, None)) - assert_equal(tmp[2][-2], slice(0, 6, None)) + assert_equal(tmp, [ + [slice(0, 4, None)], + [], + [slice(0, 6, None), slice(7, 8, None)] + ]) class TestCompressFunctions(object): diff --git a/numpy/ma/testutils.py b/numpy/ma/testutils.py index a95c170c8..c4ee639ed 100644 --- a/numpy/ma/testutils.py +++ b/numpy/ma/testutils.py @@ -14,7 +14,7 @@ from numpy import ndarray, float_ import numpy.core.umath as umath import numpy.testing from numpy.testing import ( - TestCase, assert_, assert_allclose, assert_array_almost_equal_nulp, + assert_, assert_allclose, assert_array_almost_equal_nulp, assert_raises, build_err_msg, run_module_suite ) from .core import mask_or, getmask, masked_array, nomask, masked, filled @@ -31,6 +31,7 @@ __all__masked = [ # have mistakenly included them from this file. SciPy is one. That is # unfortunate, as some of these functions are not intended to work with # masked arrays. But there was no way to tell before. +from unittest import TestCase __some__from_testing = [ 'TestCase', 'assert_', 'assert_allclose', 'assert_array_almost_equal_nulp', 'assert_raises', 'run_module_suite', diff --git a/numpy/matrixlib/__init__.py b/numpy/matrixlib/__init__.py index 11dce2928..95713580d 100644 --- a/numpy/matrixlib/__init__.py +++ b/numpy/matrixlib/__init__.py @@ -9,4 +9,3 @@ __all__ = defmatrix.__all__ from numpy.testing import _numpy_tester test = _numpy_tester().test -bench = _numpy_tester().bench diff --git a/numpy/matrixlib/tests/test_defmatrix.py b/numpy/matrixlib/tests/test_defmatrix.py index 77f262031..d5435a1a3 100644 --- a/numpy/matrixlib/tests/test_defmatrix.py +++ b/numpy/matrixlib/tests/test_defmatrix.py @@ -1,6 +1,11 @@ from __future__ import division, absolute_import, print_function -import collections +try: + # Accessing collections abstract classes from collections + # has been deprecated since Python 3.3 + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc import numpy as np from numpy import matrix, asmatrix, bmat @@ -302,7 +307,7 @@ class TestMatrixReturn(object): if attrib.startswith('_') or attrib in excluded_methods: continue f = getattr(a, attrib) - if isinstance(f, collections.Callable): + if isinstance(f, collections_abc.Callable): # reset contents of a a.astype('f8') a.fill(1.0) diff --git a/numpy/polynomial/__init__.py b/numpy/polynomial/__init__.py index ae5b1f078..f7cbc19b0 100644 --- a/numpy/polynomial/__init__.py +++ b/numpy/polynomial/__init__.py @@ -24,4 +24,3 @@ from .laguerre import Laguerre from numpy.testing import _numpy_tester test = _numpy_tester().test -bench = _numpy_tester().bench diff --git a/numpy/random/__init__.py b/numpy/random/__init__.py index 869818a22..409f50ec0 100644 --- a/numpy/random/__init__.py +++ b/numpy/random/__init__.py @@ -119,4 +119,3 @@ def __RandomState_ctor(): from numpy.testing import _numpy_tester test = _numpy_tester().test -bench = _numpy_tester().bench diff --git a/numpy/testing/__init__.py b/numpy/testing/__init__.py index 9485b455e..f4970b06b 100644 --- a/numpy/testing/__init__.py +++ b/numpy/testing/__init__.py @@ -9,7 +9,12 @@ from __future__ import division, absolute_import, print_function from unittest import TestCase -from . import decorators as dec -from .nosetester import run_module_suite, NoseTester as Tester, _numpy_tester -from .utils import * +from ._private.utils import * +from ._private import decorators as dec +from ._private.nosetester import ( + run_module_suite, NoseTester as Tester, _numpy_tester, + ) + +__all__ = _private.utils.__all__ + ['TestCase', 'run_module_suite'] + test = _numpy_tester().test diff --git a/numpy/testing/nose_tools/__init__.py b/numpy/testing/_private/__init__.py index e69de29bb..e69de29bb 100644 --- a/numpy/testing/nose_tools/__init__.py +++ b/numpy/testing/_private/__init__.py diff --git a/numpy/testing/nose_tools/decorators.py b/numpy/testing/_private/decorators.py index 243c0c8c1..60d3f968f 100644 --- a/numpy/testing/nose_tools/decorators.py +++ b/numpy/testing/_private/decorators.py @@ -15,12 +15,17 @@ function name, setup and teardown functions and so on - see """ from __future__ import division, absolute_import, print_function -import collections +try: + # Accessing collections abstract classes from collections + # has been deprecated since Python 3.3 + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc -from .utils import SkipTest, assert_warns +from .utils import SkipTest, assert_warns, HAS_REFCOUNT __all__ = ['slow', 'setastest', 'skipif', 'knownfailureif', 'deprecated', - 'parametrize',] + 'parametrize', '_needs_refcount',] def slow(t): @@ -126,7 +131,7 @@ def skipif(skip_condition, msg=None): import nose # Allow for both boolean or callable skip conditions. - if isinstance(skip_condition, collections.Callable): + if isinstance(skip_condition, collections_abc.Callable): skip_val = lambda: skip_condition() else: skip_val = lambda: skip_condition @@ -202,7 +207,7 @@ def knownfailureif(fail_condition, msg=None): msg = 'Test skipped due to known failure' # Allow for both boolean or callable known failure conditions. - if isinstance(fail_condition, collections.Callable): + if isinstance(fail_condition, collections_abc.Callable): fail_val = lambda: fail_condition() else: fail_val = lambda: fail_condition @@ -257,7 +262,7 @@ def deprecated(conditional=True): with assert_warns(DeprecationWarning): f(*args, **kwargs) - if isinstance(conditional, collections.Callable): + if isinstance(conditional, collections_abc.Callable): cond = conditional() else: cond = conditional @@ -283,3 +288,5 @@ def parametrize(vars, input): from .parameterized import parameterized return parameterized(input) + +_needs_refcount = skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") diff --git a/numpy/testing/nose_tools/noseclasses.py b/numpy/testing/_private/noseclasses.py index 9756b9b45..08dec0ca9 100644 --- a/numpy/testing/nose_tools/noseclasses.py +++ b/numpy/testing/_private/noseclasses.py @@ -325,7 +325,7 @@ class FPUModeCheckPlugin(Plugin): """ def prepareTestCase(self, test): - from numpy.core.multiarray_tests import get_fpu_mode + from numpy.core._multiarray_tests import get_fpu_mode def run(result): old_mode = get_fpu_mode() diff --git a/numpy/testing/nose_tools/nosetester.py b/numpy/testing/_private/nosetester.py index c2cf58377..c2cf58377 100644 --- a/numpy/testing/nose_tools/nosetester.py +++ b/numpy/testing/_private/nosetester.py diff --git a/numpy/testing/nose_tools/parameterized.py b/numpy/testing/_private/parameterized.py index 53e67517d..53e67517d 100644 --- a/numpy/testing/nose_tools/parameterized.py +++ b/numpy/testing/_private/parameterized.py diff --git a/numpy/testing/_private/pytesttester.py b/numpy/testing/_private/pytesttester.py new file mode 100644 index 000000000..6a92a52fd --- /dev/null +++ b/numpy/testing/_private/pytesttester.py @@ -0,0 +1,175 @@ +""" +Pytest test running. + +This module implements the ``test()`` function for NumPy modules. The usual +boiler plate for doing that is to put the following in the module +``__init__.py`` file:: + + from numpy.testing import PytestTester + test = PytestTester(__name__).test + del PytestTester + + +Warnings filtering and other runtime settings should be dealt with in the +``pytest.ini`` file in the numpy repo root. The behavior of the test depends on +whether or not that file is found as follows: + +* ``pytest.ini`` is present (develop mode) + All warnings except those explicily filtered out are raised as error. +* ``pytest.ini`` is absent (release mode) + DeprecationWarnings and PendingDeprecationWarnings are ignored, other + warnings are passed through. + +In practice, tests run from the numpy repo are run in develop mode. That +includes the standard ``python runtests.py`` invocation. + +""" +from __future__ import division, absolute_import, print_function + +import sys +import os + +__all__ = ['PytestTester'] + + +def _show_numpy_info(): + import numpy as np + + print("NumPy version %s" % np.__version__) + relaxed_strides = np.ones((10, 1), order="C").flags.f_contiguous + print("NumPy relaxed strides checking option:", relaxed_strides) + + +class PytestTester(object): + """ + Pytest test runner. + + This class is made available in ``numpy.testing``, and a test function + is typically added to a package's __init__.py like so:: + + from numpy.testing import PytestTester + test = PytestTester(__name__).test + del PytestTester + + Calling this test function finds and runs all tests associated with the + module and all its sub-modules. + + Attributes + ---------- + module_name : str + Full path to the package to test. + + Parameters + ---------- + module_name : module name + The name of the module to test. + + """ + def __init__(self, module_name): + self.module_name = module_name + + def test(self, label='fast', verbose=1, extra_argv=None, + doctests=False, coverage=False, timer=0, tests=None): + """ + Run tests for module using pytest. + + Parameters + ---------- + label : {'fast', 'full'}, optional + Identifies the tests to run. When set to 'fast', tests decorated + with `pytest.mark.slow` are skipped, when 'full', the slow marker + is ignored. + verbose : int, optional + Verbosity value for test outputs, in the range 1-3. Default is 1. + extra_argv : list, optional + List with any extra arguments to pass to pytests. + doctests : bool, optional + .. note:: Not supported + coverage : bool, optional + If True, report coverage of NumPy code. Default is False. + Requires installation of (pip) pytest-cov. + timer : int, optional + If > 0, report the time of the slowest `timer` tests. Default is 0. + + tests : test or list of tests + Tests to be executed with pytest '--pyargs' + + Returns + ------- + result : bool + Return True on success, false otherwise. + + Notes + ----- + Each NumPy module exposes `test` in its namespace to run all tests for it. + For example, to run all tests for numpy.lib: + + >>> np.lib.test() #doctest: +SKIP + + Examples + -------- + >>> result = np.lib.test() #doctest: +SKIP + Running unit tests for numpy.lib + ... + Ran 976 tests in 3.933s + + OK + + >>> result.errors #doctest: +SKIP + [] + >>> result.knownfail #doctest: +SKIP + [] + + """ + import pytest + + #FIXME This is no longer needed? Assume it was for use in tests. + # cap verbosity at 3, which is equivalent to the pytest '-vv' option + #from . import utils + #verbose = min(int(verbose), 3) + #utils.verbose = verbose + # + + module = sys.modules[self.module_name] + module_path = os.path.abspath(module.__path__[0]) + + # setup the pytest arguments + pytest_args = ['-l'] + + if doctests: + raise ValueError("Doctests not supported") + + if extra_argv: + pytest_args += list(extra_argv) + + if verbose > 1: + pytest_args += ["-" + "v"*(verbose - 1)] + else: + pytest_args += ["-q"] + + if coverage: + pytest_args += ["--cov=" + module_path] + + if label == "fast": + pytest_args += ["-m", "not slow"] + elif label != "full": + pytest_args += ["-m", label] + + if timer > 0: + pytest_args += ["--durations=%s" % timer] + + if tests is None: + tests = [self.module_name] + + pytest_args += ['--pyargs'] + list(tests) + + + # run tests. + _show_numpy_info() + + try: + code = pytest.main(pytest_args) + except SystemExit as exc: + code = exc.code + + return code == 0 diff --git a/numpy/testing/nose_tools/utils.py b/numpy/testing/_private/utils.py index 2d97b5c1e..507ecb1e2 100644 --- a/numpy/testing/nose_tools/utils.py +++ b/numpy/testing/_private/utils.py @@ -1156,10 +1156,54 @@ def rundocs(filename=None, raise_on_error=True): raise AssertionError("Some doctests failed:\n%s" % "\n".join(msg)) -def raises(*args,**kwargs): +def raises(*args): + """Decorator to check for raised exceptions. + + The decorated test function must raise one of the passed exceptions to + pass. If you want to test many assertions about exceptions in a single + test, you may want to use `assert_raises` instead. + + .. warning:: + This decorator is nose specific, do not use it if you are using a + different test framework. + + Parameters + ---------- + args : exceptions + The test passes if any of the passed exceptions is raised. + + Raises + ------ + AssertionError + + Examples + -------- + + Usage:: + + @raises(TypeError, ValueError) + def test_raises_type_error(): + raise TypeError("This test passes") + + @raises(Exception) + def test_that_fails_by_passing(): + pass + + """ nose = import_nose() - return nose.tools.raises(*args,**kwargs) + return nose.tools.raises(*args) +# +# assert_raises and assert_raises_regex are taken from unittest. +# +import unittest + + +class _Dummy(unittest.TestCase): + def nop(self): + pass + +_d = _Dummy('nop') def assert_raises(*args, **kwargs): """ @@ -1187,8 +1231,7 @@ def assert_raises(*args, **kwargs): """ __tracebackhide__ = True # Hide traceback for py.test - nose = import_nose() - return nose.tools.assert_raises(*args,**kwargs) + return _d.assertRaises(*args,**kwargs) def assert_raises_regex(exception_class, expected_regexp, *args, **kwargs): @@ -1212,13 +1255,12 @@ def assert_raises_regex(exception_class, expected_regexp, *args, **kwargs): """ __tracebackhide__ = True # Hide traceback for py.test - nose = import_nose() if sys.version_info.major >= 3: - funcname = nose.tools.assert_raises_regex + funcname = _d.assertRaisesRegex else: # Only present in Python 2.7, missing from unittest in 2.6 - funcname = nose.tools.assert_raises_regexp + funcname = _d.assertRaisesRegexp return funcname(exception_class, expected_regexp, *args, **kwargs) @@ -1577,7 +1619,9 @@ def integer_repr(x): """Return the signed-magnitude interpretation of the binary representation of x.""" import numpy as np - if x.dtype == np.float32: + if x.dtype == np.float16: + return _integer_repr(x, np.int16, np.int16(-2**15)) + elif x.dtype == np.float32: return _integer_repr(x, np.int32, np.int32(-2**31)) elif x.dtype == np.float64: return _integer_repr(x, np.int64, np.int64(-2**63)) diff --git a/numpy/testing/decorators.py b/numpy/testing/decorators.py index 21bcdd798..1988afd29 100644 --- a/numpy/testing/decorators.py +++ b/numpy/testing/decorators.py @@ -3,6 +3,9 @@ Back compatibility decorators module. It will import the appropriate set of tools """ -import os +import warnings -from .nose_tools.decorators import * +warnings.warn("Import from numpy.testing, not numpy.testing.decorators", + ImportWarning) + +from ._private.decorators import * diff --git a/numpy/testing/noseclasses.py b/numpy/testing/noseclasses.py index 144c4e7e4..dde62e825 100644 --- a/numpy/testing/noseclasses.py +++ b/numpy/testing/noseclasses.py @@ -2,4 +2,10 @@ Back compatibility noseclasses module. It will import the appropriate set of tools """ -from .nose_tools.noseclasses import *
\ No newline at end of file +import warnings + +warnings.warn("Import from numpy.testing, not numpy.testing.noseclasses", + ImportWarning) + +from ._private.noseclasses import * + diff --git a/numpy/testing/nosetester.py b/numpy/testing/nosetester.py index 949fae03e..772bff305 100644 --- a/numpy/testing/nosetester.py +++ b/numpy/testing/nosetester.py @@ -3,10 +3,12 @@ Back compatibility nosetester module. It will import the appropriate set of tools """ -import os +import warnings -from .nose_tools.nosetester import * +warnings.warn("Import from numpy.testing, not numpy.testing.nosetester", + ImportWarning) +from ._private.nosetester import * __all__ = ['get_package_name', 'run_module_suite', 'NoseTester', '_numpy_tester', 'get_package_name', 'import_nose', diff --git a/numpy/testing/pytest_tools/decorators.py b/numpy/testing/pytest_tools/decorators.py index 08a39e0c0..e72b1eb0b 100644 --- a/numpy/testing/pytest_tools/decorators.py +++ b/numpy/testing/pytest_tools/decorators.py @@ -12,12 +12,17 @@ function name, setup and teardown functions and so on. """ from __future__ import division, absolute_import, print_function -import collections +try: + # Accessing collections abstract classes from collections + # has been deprecated since Python 3.3 + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc -from .utils import SkipTest, assert_warns +from .utils import SkipTest, assert_warns, HAS_REFCOUNT __all__ = ['slow', 'setastest', 'skipif', 'knownfailureif', 'deprecated', - 'parametrize',] + 'parametrize', '_needs_refcount',] def slow(t): @@ -127,7 +132,7 @@ def skipif(skip_condition, msg=None): out = msg # Allow for both boolean or callable skip conditions. - if isinstance(skip_condition, collections.Callable): + if isinstance(skip_condition, collections_abc.Callable): skip_val = lambda: skip_condition() else: skip_val = lambda: skip_condition @@ -203,7 +208,7 @@ def knownfailureif(fail_condition, msg=None): msg = 'Test skipped due to known failure' # Allow for both boolean or callable known failure conditions. - if isinstance(fail_condition, collections.Callable): + if isinstance(fail_condition, collections_abc.Callable): fail_val = lambda: fail_condition() else: fail_val = lambda: fail_condition @@ -252,7 +257,7 @@ def deprecated(conditional=True): with assert_warns(DeprecationWarning): f(*args, **kwargs) - if isinstance(conditional, collections.Callable): + if isinstance(conditional, collections_abc.Callable): cond = conditional() else: cond = conditional @@ -276,3 +281,6 @@ def parametrize(vars, input): import pytest return pytest.mark.parametrize(vars, input) + + +_needs_refcount = skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") diff --git a/numpy/testing/pytest_tools/utils.py b/numpy/testing/pytest_tools/utils.py index 8a0eb8be3..9640d48c3 100644 --- a/numpy/testing/pytest_tools/utils.py +++ b/numpy/testing/pytest_tools/utils.py @@ -1619,7 +1619,9 @@ def integer_repr(x): """Return the signed-magnitude interpretation of the binary representation of x.""" import numpy as np - if x.dtype == np.float32: + if x.dtype == np.float16: + return _integer_repr(x, np.int16, np.int16(-2**15)) + elif x.dtype == np.float32: return _integer_repr(x, np.int32, np.int32(-2**31)) elif x.dtype == np.float64: return _integer_repr(x, np.int64, np.int64(-2**63)) diff --git a/numpy/testing/setup.py b/numpy/testing/setup.py index 5a0f977d9..b00e5e029 100755 --- a/numpy/testing/setup.py +++ b/numpy/testing/setup.py @@ -6,7 +6,7 @@ def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('testing', parent_package, top_path) - config.add_subpackage('nose_tools') + config.add_subpackage('_private') config.add_subpackage('pytest_tools') config.add_data_dir('tests') return config diff --git a/numpy/testing/tests/test_utils.py b/numpy/testing/tests/test_utils.py index 77fb974cf..d5c582ad3 100644 --- a/numpy/testing/tests/test_utils.py +++ b/numpy/testing/tests/test_utils.py @@ -16,7 +16,6 @@ from numpy.testing import ( clear_and_catch_warnings, suppress_warnings, run_module_suite, assert_string_equal, assert_, tempdir, temppath, ) -import unittest class _GenericTest(object): @@ -69,9 +68,9 @@ class _GenericTest(object): self._test_equal([1, 2, 3], (1, 2, 3)) -class TestArrayEqual(_GenericTest, unittest.TestCase): +class TestArrayEqual(_GenericTest): - def setUp(self): + def setup(self): self._assert_func = assert_array_equal def test_generic_rank1(self): @@ -149,10 +148,10 @@ class TestArrayEqual(_GenericTest, unittest.TestCase): with suppress_warnings() as sup: l = sup.record(FutureWarning, message="elementwise == ") self._test_not_equal(c, b) - assert_(len(l) == 1) + assert_equal(len(l), 1) -class TestBuildErrorMessage(unittest.TestCase): +class TestBuildErrorMessage(object): def test_build_err_msg_defaults(self): x = np.array([1.00001, 2.00002, 3.00003]) @@ -163,7 +162,7 @@ class TestBuildErrorMessage(unittest.TestCase): b = ('\nItems are not equal: There is a mismatch\n ACTUAL: array([' '1.00001, 2.00002, 3.00003])\n DESIRED: array([1.00002, ' '2.00003, 3.00004])') - self.assertEqual(a, b) + assert_equal(a, b) def test_build_err_msg_no_verbose(self): x = np.array([1.00001, 2.00002, 3.00003]) @@ -172,7 +171,7 @@ class TestBuildErrorMessage(unittest.TestCase): a = build_err_msg([x, y], err_msg, verbose=False) b = '\nItems are not equal: There is a mismatch' - self.assertEqual(a, b) + assert_equal(a, b) def test_build_err_msg_custom_names(self): x = np.array([1.00001, 2.00002, 3.00003]) @@ -183,7 +182,7 @@ class TestBuildErrorMessage(unittest.TestCase): b = ('\nItems are not equal: There is a mismatch\n FOO: array([' '1.00001, 2.00002, 3.00003])\n BAR: array([1.00002, 2.00003, ' '3.00004])') - self.assertEqual(a, b) + assert_equal(a, b) def test_build_err_msg_custom_precision(self): x = np.array([1.000000001, 2.00002, 3.00003]) @@ -194,12 +193,12 @@ class TestBuildErrorMessage(unittest.TestCase): b = ('\nItems are not equal: There is a mismatch\n ACTUAL: array([' '1.000000001, 2.00002 , 3.00003 ])\n DESIRED: array([' '1.000000002, 2.00003 , 3.00004 ])') - self.assertEqual(a, b) + assert_equal(a, b) class TestEqual(TestArrayEqual): - def setUp(self): + def setup(self): self._assert_func = assert_equal def test_nan_items(self): @@ -298,16 +297,16 @@ class TestEqual(TestArrayEqual): x: array([1, 2]) y: matrix([[1, 2]])""") try: - self.assertEqual(msg, msg_reference) + assert_equal(msg, msg_reference) except AssertionError: - self.assertEqual(msg2, msg_reference) + assert_equal(msg2, msg_reference) else: raise AssertionError("Did not raise") -class TestArrayAlmostEqual(_GenericTest, unittest.TestCase): +class TestArrayAlmostEqual(_GenericTest): - def setUp(self): + def setup(self): self._assert_func = assert_array_almost_equal def test_closeness(self): @@ -319,12 +318,12 @@ class TestArrayAlmostEqual(_GenericTest, unittest.TestCase): # test scalars self._assert_func(1.499999, 0.0, decimal=0) - self.assertRaises(AssertionError, + assert_raises(AssertionError, lambda: self._assert_func(1.5, 0.0, decimal=0)) # test arrays self._assert_func([1.499999], [0.0], decimal=0) - self.assertRaises(AssertionError, + assert_raises(AssertionError, lambda: self._assert_func([1.5], [0.0], decimal=0)) def test_simple(self): @@ -333,7 +332,7 @@ class TestArrayAlmostEqual(_GenericTest, unittest.TestCase): self._assert_func(x, y, decimal=3) self._assert_func(x, y, decimal=4) - self.assertRaises(AssertionError, + assert_raises(AssertionError, lambda: self._assert_func(x, y, decimal=5)) def test_nan(self): @@ -341,21 +340,21 @@ class TestArrayAlmostEqual(_GenericTest, unittest.TestCase): aone = np.array([1]) ainf = np.array([np.inf]) self._assert_func(anan, anan) - self.assertRaises(AssertionError, + assert_raises(AssertionError, lambda: self._assert_func(anan, aone)) - self.assertRaises(AssertionError, + assert_raises(AssertionError, lambda: self._assert_func(anan, ainf)) - self.assertRaises(AssertionError, + assert_raises(AssertionError, lambda: self._assert_func(ainf, anan)) def test_inf(self): a = np.array([[1., 2.], [3., 4.]]) b = a.copy() a[0, 0] = np.inf - self.assertRaises(AssertionError, + assert_raises(AssertionError, lambda: self._assert_func(a, b)) b[0, 0] = -np.inf - self.assertRaises(AssertionError, + assert_raises(AssertionError, lambda: self._assert_func(a, b)) def test_subclass(self): @@ -396,9 +395,9 @@ class TestArrayAlmostEqual(_GenericTest, unittest.TestCase): self._assert_func(a, a) -class TestAlmostEqual(_GenericTest, unittest.TestCase): +class TestAlmostEqual(_GenericTest): - def setUp(self): + def setup(self): self._assert_func = assert_almost_equal def test_closeness(self): @@ -410,30 +409,30 @@ class TestAlmostEqual(_GenericTest, unittest.TestCase): # test scalars self._assert_func(1.499999, 0.0, decimal=0) - self.assertRaises(AssertionError, - lambda: self._assert_func(1.5, 0.0, decimal=0)) + assert_raises(AssertionError, + lambda: self._assert_func(1.5, 0.0, decimal=0)) # test arrays self._assert_func([1.499999], [0.0], decimal=0) - self.assertRaises(AssertionError, - lambda: self._assert_func([1.5], [0.0], decimal=0)) + assert_raises(AssertionError, + lambda: self._assert_func([1.5], [0.0], decimal=0)) def test_nan_item(self): self._assert_func(np.nan, np.nan) - self.assertRaises(AssertionError, - lambda: self._assert_func(np.nan, 1)) - self.assertRaises(AssertionError, - lambda: self._assert_func(np.nan, np.inf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(np.inf, np.nan)) + assert_raises(AssertionError, + lambda: self._assert_func(np.nan, 1)) + assert_raises(AssertionError, + lambda: self._assert_func(np.nan, np.inf)) + assert_raises(AssertionError, + lambda: self._assert_func(np.inf, np.nan)) def test_inf_item(self): self._assert_func(np.inf, np.inf) self._assert_func(-np.inf, -np.inf) - self.assertRaises(AssertionError, - lambda: self._assert_func(np.inf, 1)) - self.assertRaises(AssertionError, - lambda: self._assert_func(-np.inf, np.inf)) + assert_raises(AssertionError, + lambda: self._assert_func(np.inf, 1)) + assert_raises(AssertionError, + lambda: self._assert_func(-np.inf, np.inf)) def test_simple_item(self): self._test_not_equal(1, 2) @@ -467,7 +466,7 @@ class TestAlmostEqual(_GenericTest, unittest.TestCase): self._assert_func(x, y, decimal=12) except AssertionError as e: # remove anything that's not the array string - self.assertEqual(str(e).split('%)\n ')[1], b) + assert_equal(str(e).split('%)\n ')[1], b) # with the default value of decimal digits, only the 3rd element differs # note that we only check for the formatting of the arrays themselves @@ -477,7 +476,7 @@ class TestAlmostEqual(_GenericTest, unittest.TestCase): self._assert_func(x, y) except AssertionError as e: # remove anything that's not the array string - self.assertEqual(str(e).split('%)\n ')[1], b) + assert_equal(str(e).split('%)\n ')[1], b) def test_matrix(self): # Matrix slicing keeps things 2-D, while array does not necessarily. @@ -509,9 +508,9 @@ class TestAlmostEqual(_GenericTest, unittest.TestCase): self._assert_func(a, a) -class TestApproxEqual(unittest.TestCase): +class TestApproxEqual(object): - def setUp(self): + def setup(self): self._assert_func = assert_approx_equal def test_simple_arrays(self): @@ -520,8 +519,8 @@ class TestApproxEqual(unittest.TestCase): self._assert_func(x, y, significant=5) self._assert_func(x, y, significant=6) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, y, significant=7)) + assert_raises(AssertionError, + lambda: self._assert_func(x, y, significant=7)) def test_simple_items(self): x = 1234.22 @@ -530,37 +529,31 @@ class TestApproxEqual(unittest.TestCase): self._assert_func(x, y, significant=4) self._assert_func(x, y, significant=5) self._assert_func(x, y, significant=6) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, y, significant=7)) + assert_raises(AssertionError, + lambda: self._assert_func(x, y, significant=7)) def test_nan_array(self): anan = np.array(np.nan) aone = np.array(1) ainf = np.array(np.inf) self._assert_func(anan, anan) - self.assertRaises(AssertionError, - lambda: self._assert_func(anan, aone)) - self.assertRaises(AssertionError, - lambda: self._assert_func(anan, ainf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(ainf, anan)) + assert_raises(AssertionError, lambda: self._assert_func(anan, aone)) + assert_raises(AssertionError, lambda: self._assert_func(anan, ainf)) + assert_raises(AssertionError, lambda: self._assert_func(ainf, anan)) def test_nan_items(self): anan = np.array(np.nan) aone = np.array(1) ainf = np.array(np.inf) self._assert_func(anan, anan) - self.assertRaises(AssertionError, - lambda: self._assert_func(anan, aone)) - self.assertRaises(AssertionError, - lambda: self._assert_func(anan, ainf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(ainf, anan)) + assert_raises(AssertionError, lambda: self._assert_func(anan, aone)) + assert_raises(AssertionError, lambda: self._assert_func(anan, ainf)) + assert_raises(AssertionError, lambda: self._assert_func(ainf, anan)) -class TestArrayAssertLess(unittest.TestCase): +class TestArrayAssertLess(object): - def setUp(self): + def setup(self): self._assert_func = assert_array_less def test_simple_arrays(self): @@ -568,100 +561,79 @@ class TestArrayAssertLess(unittest.TestCase): y = np.array([1.2, 2.3]) self._assert_func(x, y) - self.assertRaises(AssertionError, - lambda: self._assert_func(y, x)) + assert_raises(AssertionError, lambda: self._assert_func(y, x)) y = np.array([1.0, 2.3]) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, y)) - self.assertRaises(AssertionError, - lambda: self._assert_func(y, x)) + assert_raises(AssertionError, lambda: self._assert_func(x, y)) + assert_raises(AssertionError, lambda: self._assert_func(y, x)) def test_rank2(self): x = np.array([[1.1, 2.2], [3.3, 4.4]]) y = np.array([[1.2, 2.3], [3.4, 4.5]]) self._assert_func(x, y) - self.assertRaises(AssertionError, - lambda: self._assert_func(y, x)) + assert_raises(AssertionError, lambda: self._assert_func(y, x)) y = np.array([[1.0, 2.3], [3.4, 4.5]]) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, y)) - self.assertRaises(AssertionError, - lambda: self._assert_func(y, x)) + assert_raises(AssertionError, lambda: self._assert_func(x, y)) + assert_raises(AssertionError, lambda: self._assert_func(y, x)) def test_rank3(self): x = np.ones(shape=(2, 2, 2)) y = np.ones(shape=(2, 2, 2))+1 self._assert_func(x, y) - self.assertRaises(AssertionError, - lambda: self._assert_func(y, x)) + assert_raises(AssertionError, lambda: self._assert_func(y, x)) y[0, 0, 0] = 0 - self.assertRaises(AssertionError, - lambda: self._assert_func(x, y)) - self.assertRaises(AssertionError, - lambda: self._assert_func(y, x)) + assert_raises(AssertionError, lambda: self._assert_func(x, y)) + assert_raises(AssertionError, lambda: self._assert_func(y, x)) def test_simple_items(self): x = 1.1 y = 2.2 self._assert_func(x, y) - self.assertRaises(AssertionError, - lambda: self._assert_func(y, x)) + assert_raises(AssertionError, lambda: self._assert_func(y, x)) y = np.array([2.2, 3.3]) self._assert_func(x, y) - self.assertRaises(AssertionError, - lambda: self._assert_func(y, x)) + assert_raises(AssertionError, lambda: self._assert_func(y, x)) y = np.array([1.0, 3.3]) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, y)) + assert_raises(AssertionError, lambda: self._assert_func(x, y)) def test_nan_noncompare(self): anan = np.array(np.nan) aone = np.array(1) ainf = np.array(np.inf) self._assert_func(anan, anan) - self.assertRaises(AssertionError, - lambda: self._assert_func(aone, anan)) - self.assertRaises(AssertionError, - lambda: self._assert_func(anan, aone)) - self.assertRaises(AssertionError, - lambda: self._assert_func(anan, ainf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(ainf, anan)) + assert_raises(AssertionError, lambda: self._assert_func(aone, anan)) + assert_raises(AssertionError, lambda: self._assert_func(anan, aone)) + assert_raises(AssertionError, lambda: self._assert_func(anan, ainf)) + assert_raises(AssertionError, lambda: self._assert_func(ainf, anan)) def test_nan_noncompare_array(self): x = np.array([1.1, 2.2, 3.3]) anan = np.array(np.nan) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, anan)) - self.assertRaises(AssertionError, - lambda: self._assert_func(anan, x)) + assert_raises(AssertionError, lambda: self._assert_func(x, anan)) + assert_raises(AssertionError, lambda: self._assert_func(anan, x)) x = np.array([1.1, 2.2, np.nan]) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, anan)) - self.assertRaises(AssertionError, - lambda: self._assert_func(anan, x)) + assert_raises(AssertionError, lambda: self._assert_func(x, anan)) + assert_raises(AssertionError, lambda: self._assert_func(anan, x)) y = np.array([1.0, 2.0, np.nan]) self._assert_func(y, x) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, y)) + assert_raises(AssertionError, lambda: self._assert_func(x, y)) def test_inf_compare(self): aone = np.array(1) @@ -670,37 +642,27 @@ class TestArrayAssertLess(unittest.TestCase): self._assert_func(aone, ainf) self._assert_func(-ainf, aone) self._assert_func(-ainf, ainf) - self.assertRaises(AssertionError, - lambda: self._assert_func(ainf, aone)) - self.assertRaises(AssertionError, - lambda: self._assert_func(aone, -ainf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(ainf, ainf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(ainf, -ainf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(-ainf, -ainf)) + assert_raises(AssertionError, lambda: self._assert_func(ainf, aone)) + assert_raises(AssertionError, lambda: self._assert_func(aone, -ainf)) + assert_raises(AssertionError, lambda: self._assert_func(ainf, ainf)) + assert_raises(AssertionError, lambda: self._assert_func(ainf, -ainf)) + assert_raises(AssertionError, lambda: self._assert_func(-ainf, -ainf)) def test_inf_compare_array(self): x = np.array([1.1, 2.2, np.inf]) ainf = np.array(np.inf) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, ainf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(ainf, x)) - self.assertRaises(AssertionError, - lambda: self._assert_func(x, -ainf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(-x, -ainf)) - self.assertRaises(AssertionError, - lambda: self._assert_func(-ainf, -x)) + assert_raises(AssertionError, lambda: self._assert_func(x, ainf)) + assert_raises(AssertionError, lambda: self._assert_func(ainf, x)) + assert_raises(AssertionError, lambda: self._assert_func(x, -ainf)) + assert_raises(AssertionError, lambda: self._assert_func(-x, -ainf)) + assert_raises(AssertionError, lambda: self._assert_func(-ainf, -x)) self._assert_func(-ainf, x) -class TestRaises(unittest.TestCase): +class TestRaises(object): - def setUp(self): + def setup(self): class MyException(Exception): pass @@ -732,7 +694,7 @@ class TestRaises(unittest.TestCase): raise AssertionError("should have raised an AssertionError") -class TestWarns(unittest.TestCase): +class TestWarns(object): def test_warn(self): def f(): @@ -783,28 +745,27 @@ class TestWarns(unittest.TestCase): raise AssertionError("wrong warning caught by assert_warn") -class TestAssertAllclose(unittest.TestCase): +class TestAssertAllclose(object): def test_simple(self): x = 1e-3 y = 1e-9 assert_allclose(x, y, atol=1) - self.assertRaises(AssertionError, assert_allclose, x, y) + assert_raises(AssertionError, assert_allclose, x, y) a = np.array([x, y, x, y]) b = np.array([x, y, x, x]) assert_allclose(a, b, atol=1) - self.assertRaises(AssertionError, assert_allclose, a, b) + assert_raises(AssertionError, assert_allclose, a, b) b[-1] = y * (1 + 1e-8) assert_allclose(a, b) - self.assertRaises(AssertionError, assert_allclose, a, b, - rtol=1e-9) + assert_raises(AssertionError, assert_allclose, a, b, rtol=1e-9) assert_allclose(6, 10, rtol=0.5) - self.assertRaises(AssertionError, assert_allclose, 10, 6, rtol=0.5) + assert_raises(AssertionError, assert_allclose, 10, 6, rtol=0.5) def test_min_int(self): a = np.array([np.iinfo(np.int_).min], dtype=np.int_) @@ -819,7 +780,7 @@ class TestAssertAllclose(unittest.TestCase): msg = '' except AssertionError as exc: msg = exc.args[0] - self.assertTrue("mismatch 25.0%" in msg) + assert_("mismatch 25.0%" in msg) def test_equal_nan(self): a = np.array([np.nan]) @@ -830,8 +791,7 @@ class TestAssertAllclose(unittest.TestCase): def test_not_equal_nan(self): a = np.array([np.nan]) b = np.array([np.nan]) - self.assertRaises(AssertionError, assert_allclose, a, b, - equal_nan=False) + assert_raises(AssertionError, assert_allclose, a, b, equal_nan=False) def test_equal_nan_default(self): # Make sure equal_nan default behavior remains unchanged. (All @@ -845,7 +805,7 @@ class TestAssertAllclose(unittest.TestCase): assert_allclose(a, b) -class TestArrayAlmostEqualNulp(unittest.TestCase): +class TestArrayAlmostEqualNulp(object): def test_float64_pass(self): # The number of units of least precision @@ -873,13 +833,13 @@ class TestArrayAlmostEqualNulp(unittest.TestCase): eps = np.finfo(x.dtype).eps y = x + x*eps*nulp*2. - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - x, y, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + x, y, nulp) epsneg = np.finfo(x.dtype).epsneg y = x - x*epsneg*nulp*2. - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - x, y, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + x, y, nulp) def test_float32_pass(self): nulp = 5 @@ -903,13 +863,43 @@ class TestArrayAlmostEqualNulp(unittest.TestCase): eps = np.finfo(x.dtype).eps y = x + x*eps*nulp*2. - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - x, y, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + x, y, nulp) + + epsneg = np.finfo(x.dtype).epsneg + y = x - x*epsneg*nulp*2. + assert_raises(AssertionError, assert_array_almost_equal_nulp, + x, y, nulp) + + def test_float16_pass(self): + nulp = 5 + x = np.linspace(-4, 4, 10, dtype=np.float16) + x = 10**x + x = np.r_[-x, x] + + eps = np.finfo(x.dtype).eps + y = x + x*eps*nulp/2. + assert_array_almost_equal_nulp(x, y, nulp) + + epsneg = np.finfo(x.dtype).epsneg + y = x - x*epsneg*nulp/2. + assert_array_almost_equal_nulp(x, y, nulp) + + def test_float16_fail(self): + nulp = 5 + x = np.linspace(-4, 4, 10, dtype=np.float16) + x = 10**x + x = np.r_[-x, x] + + eps = np.finfo(x.dtype).eps + y = x + x*eps*nulp*2. + assert_raises(AssertionError, assert_array_almost_equal_nulp, + x, y, nulp) epsneg = np.finfo(x.dtype).epsneg y = x - x*epsneg*nulp*2. - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - x, y, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + x, y, nulp) def test_complex128_pass(self): nulp = 5 @@ -943,25 +933,25 @@ class TestArrayAlmostEqualNulp(unittest.TestCase): eps = np.finfo(x.dtype).eps y = x + x*eps*nulp*2. - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, x + y*1j, nulp) - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, y + x*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, x + y*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, y + x*1j, nulp) # The test condition needs to be at least a factor of sqrt(2) smaller # because the real and imaginary parts both change y = x + x*eps*nulp - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, y + y*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, y + y*1j, nulp) epsneg = np.finfo(x.dtype).epsneg y = x - x*epsneg*nulp*2. - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, x + y*1j, nulp) - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, y + x*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, x + y*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, y + x*1j, nulp) y = x - x*epsneg*nulp - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, y + y*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, y + y*1j, nulp) def test_complex64_pass(self): nulp = 5 @@ -993,26 +983,26 @@ class TestArrayAlmostEqualNulp(unittest.TestCase): eps = np.finfo(x.dtype).eps y = x + x*eps*nulp*2. - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, x + y*1j, nulp) - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, y + x*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, x + y*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, y + x*1j, nulp) y = x + x*eps*nulp - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, y + y*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, y + y*1j, nulp) epsneg = np.finfo(x.dtype).epsneg y = x - x*epsneg*nulp*2. - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, x + y*1j, nulp) - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, y + x*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, x + y*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, y + x*1j, nulp) y = x - x*epsneg*nulp - self.assertRaises(AssertionError, assert_array_almost_equal_nulp, - xi, y + y*1j, nulp) + assert_raises(AssertionError, assert_array_almost_equal_nulp, + xi, y + y*1j, nulp) -class TestULP(unittest.TestCase): +class TestULP(object): def test_equal(self): x = np.random.randn(10) @@ -1051,24 +1041,24 @@ class TestULP(unittest.TestCase): tiny = np.array([np.finfo(dt).tiny]) zero = np.array([np.PZERO]).astype(dt) nzero = np.array([np.NZERO]).astype(dt) - self.assertRaises(AssertionError, - lambda: assert_array_max_ulp(nan, inf, - maxulp=maxulp)) - self.assertRaises(AssertionError, - lambda: assert_array_max_ulp(nan, big, - maxulp=maxulp)) - self.assertRaises(AssertionError, - lambda: assert_array_max_ulp(nan, tiny, - maxulp=maxulp)) - self.assertRaises(AssertionError, - lambda: assert_array_max_ulp(nan, zero, - maxulp=maxulp)) - self.assertRaises(AssertionError, - lambda: assert_array_max_ulp(nan, nzero, - maxulp=maxulp)) - - -class TestStringEqual(unittest.TestCase): + assert_raises(AssertionError, + lambda: assert_array_max_ulp(nan, inf, + maxulp=maxulp)) + assert_raises(AssertionError, + lambda: assert_array_max_ulp(nan, big, + maxulp=maxulp)) + assert_raises(AssertionError, + lambda: assert_array_max_ulp(nan, tiny, + maxulp=maxulp)) + assert_raises(AssertionError, + lambda: assert_array_max_ulp(nan, zero, + maxulp=maxulp)) + assert_raises(AssertionError, + lambda: assert_array_max_ulp(nan, nzero, + maxulp=maxulp)) + + +class TestStringEqual(object): def test_simple(self): assert_string_equal("hello", "hello") assert_string_equal("hello\nmultiline", "hello\nmultiline") @@ -1080,22 +1070,32 @@ class TestStringEqual(unittest.TestCase): else: raise AssertionError("exception not raised") - self.assertRaises(AssertionError, - lambda: assert_string_equal("foo", "hello")) + assert_raises(AssertionError, + lambda: assert_string_equal("foo", "hello")) -def assert_warn_len_equal(mod, n_in_context, py3_n_in_context=None): +def assert_warn_len_equal(mod, n_in_context, py34=None, py37=None): mod_warns = mod.__warningregistry__ + num_warns = len(mod_warns) # Python 3.4 appears to clear any pre-existing warnings of the same type, # when raising warnings inside a catch_warnings block. So, there is a # warning generated by the tests within the context manager, but no # previous warnings. if 'version' in mod_warns: - if py3_n_in_context is None: - py3_n_in_context = n_in_context - assert_equal(len(mod_warns) - 1, py3_n_in_context) - else: - assert_equal(len(mod_warns), n_in_context) + # Python 3 adds a 'version' entry to the registry, + # do not count it. + num_warns -= 1 + + # Behavior of warnings is Python version dependent. Adjust the + # expected result to compensate. In particular, Python 3.7 does + # not make an entry for ignored warnings. + if sys.version_info[:2] >= (3, 7): + if py37 is not None: + n_in_context = py37 + elif sys.version_info[:2] >= (3, 4): + if py34 is not None: + n_in_context = py34 + assert_equal(num_warns, n_in_context) def _get_fresh_mod(): @@ -1104,6 +1104,8 @@ def _get_fresh_mod(): try: my_mod.__warningregistry__.clear() except AttributeError: + # will not have a __warningregistry__ unless warning has been + # raised in the module at some point pass return my_mod @@ -1117,21 +1119,23 @@ def test_clear_and_catch_warnings(): warnings.warn('Some warning') assert_equal(my_mod.__warningregistry__, {}) # Without specified modules, don't clear warnings during context + # Python 3.7 catch_warnings doesn't make an entry for 'ignore'. with clear_and_catch_warnings(): warnings.simplefilter('ignore') warnings.warn('Some warning') - assert_warn_len_equal(my_mod, 1) + assert_warn_len_equal(my_mod, 1, py37=0) # Confirm that specifying module keeps old warning, does not add new with clear_and_catch_warnings(modules=[my_mod]): warnings.simplefilter('ignore') warnings.warn('Another warning') - assert_warn_len_equal(my_mod, 1) + assert_warn_len_equal(my_mod, 1, py37=0) # Another warning, no module spec does add to warnings dict, except on # Python 3.4 (see comments in `assert_warn_len_equal`) + # Python 3.7 catch_warnings doesn't make an entry for 'ignore'. with clear_and_catch_warnings(): warnings.simplefilter('ignore') warnings.warn('Another warning') - assert_warn_len_equal(my_mod, 2, 1) + assert_warn_len_equal(my_mod, 2, py34=1, py37=0) def test_suppress_warnings_module(): @@ -1148,6 +1152,7 @@ def test_suppress_warnings_module(): np.apply_along_axis(warn, 0, [0]) # Test module based warning suppression: + assert_warn_len_equal(my_mod, 0) with suppress_warnings() as sup: sup.record(UserWarning) # suppress warning from other module (may have .pyc ending), @@ -1157,10 +1162,9 @@ def test_suppress_warnings_module(): warn_other_module() # Check that the suppression did test the file correctly (this module # got filtered) - assert_(len(sup.log) == 1) - assert_(sup.log[0].message.args[0] == "Some warning") - - assert_warn_len_equal(my_mod, 0) + assert_equal(len(sup.log), 1) + assert_equal(sup.log[0].message.args[0], "Some warning") + assert_warn_len_equal(my_mod, 0, py37=0) sup = suppress_warnings() # Will have to be changed if apply_along_axis is moved: sup.filter(module=my_mod) @@ -1174,11 +1178,11 @@ def test_suppress_warnings_module(): assert_warn_len_equal(my_mod, 0) # Without specified modules, don't clear warnings during context + # Python 3.7 does not add ignored warnings. with suppress_warnings(): warnings.simplefilter('ignore') warnings.warn('Some warning') - assert_warn_len_equal(my_mod, 1) - + assert_warn_len_equal(my_mod, 1, py37=0) def test_suppress_warnings_type(): # Initial state of module, no warnings @@ -1202,10 +1206,11 @@ def test_suppress_warnings_type(): assert_warn_len_equal(my_mod, 0) # Without specified modules, don't clear warnings during context + # Python 3.7 does not add ignored warnings. with suppress_warnings(): warnings.simplefilter('ignore') warnings.warn('Some warning') - assert_warn_len_equal(my_mod, 1) + assert_warn_len_equal(my_mod, 1, py37=0) def test_suppress_warnings_decorate_no_record(): @@ -1220,7 +1225,7 @@ def test_suppress_warnings_decorate_no_record(): warnings.simplefilter("always") warn(UserWarning) # should be supppressed warn(RuntimeWarning) - assert_(len(w) == 1) + assert_equal(len(w), 1) def test_suppress_warnings_record(): @@ -1234,10 +1239,10 @@ def test_suppress_warnings_record(): warnings.warn('Some other warning') warnings.warn('Some other warning 2') - assert_(len(sup.log) == 2) - assert_(len(log1) == 1) - assert_(len(log2) == 1) - assert_(log2[0].message.args[0] == 'Some other warning 2') + assert_equal(len(sup.log), 2) + assert_equal(len(log1), 1) + assert_equal(len(log2),1) + assert_equal(log2[0].message.args[0], 'Some other warning 2') # Do it again, with the same context to see if some warnings survived: with sup: @@ -1247,10 +1252,10 @@ def test_suppress_warnings_record(): warnings.warn('Some other warning') warnings.warn('Some other warning 2') - assert_(len(sup.log) == 2) - assert_(len(log1) == 1) - assert_(len(log2) == 1) - assert_(log2[0].message.args[0] == 'Some other warning 2') + assert_equal(len(sup.log), 2) + assert_equal(len(log1), 1) + assert_equal(len(log2), 1) + assert_equal(log2[0].message.args[0], 'Some other warning 2') # Test nested: with suppress_warnings() as sup: @@ -1259,8 +1264,8 @@ def test_suppress_warnings_record(): sup2.record(message='Some warning') warnings.warn('Some warning') warnings.warn('Some other warning') - assert_(len(sup2.log) == 1) - assert_(len(sup.log) == 1) + assert_equal(len(sup2.log), 1) + assert_equal(len(sup.log), 1) def test_suppress_warnings_forwarding(): @@ -1278,7 +1283,7 @@ def test_suppress_warnings_forwarding(): for i in range(2): warnings.warn("Some warning") - assert_(len(sup.log) == 2) + assert_equal(len(sup.log), 2) with suppress_warnings() as sup: sup.record() @@ -1287,7 +1292,7 @@ def test_suppress_warnings_forwarding(): warnings.warn("Some warning") warnings.warn("Some warning") - assert_(len(sup.log) == 2) + assert_equal(len(sup.log), 2) with suppress_warnings() as sup: sup.record() @@ -1297,7 +1302,7 @@ def test_suppress_warnings_forwarding(): warnings.warn("Some warning") warn_other_module() - assert_(len(sup.log) == 2) + assert_equal(len(sup.log), 2) with suppress_warnings() as sup: sup.record() @@ -1307,7 +1312,7 @@ def test_suppress_warnings_forwarding(): warnings.warn("Some other warning") warn_other_module() - assert_(len(sup.log) == 2) + assert_equal(len(sup.log), 2) def test_tempdir(): diff --git a/numpy/testing/utils.py b/numpy/testing/utils.py index a0218c4e6..3cd89e639 100644 --- a/numpy/testing/utils.py +++ b/numpy/testing/utils.py @@ -3,9 +3,12 @@ Back compatibility utils module. It will import the appropriate set of tools """ -import os +import warnings -from .nose_tools.utils import * +warnings.warn("Import from numpy.testing, not numpy.testing.utils", + ImportWarning) + +from ._private.utils import * __all__ = [ 'assert_equal', 'assert_almost_equal', 'assert_approx_equal', diff --git a/pytest.ini b/pytest.ini index d3d7142d4..e901beb8c 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,9 +1,21 @@ [pytest] +addopts = -l -q norecursedirs = doc tools numpy/linalg/lapack_lite numpy/core/code_generators doctest_optionflags = NORMALIZE_WHITESPACE -testpaths = numpy +filterwarnings = + error +# Filter out annoying import messages. + ignore:Not importing directory + ignore:numpy.dtype size changed + ignore:numpy.ufunc size changed +# Ignore python2.7 -3 warnings + ignore:sys\.exc_clear\(\) not supported in 3\.x:DeprecationWarning + ignore:in 3\.x, __setslice__:DeprecationWarning + ignore:in 3\.x, __getslice__:DeprecationWarning + ignore:buffer\(\) not supported in 3\.x:DeprecationWarning + ignore:CObject type is not supported in 3\.x:DeprecationWarning + ignore:comparing unequal types not supported in 3\.x:DeprecationWarning + ignore:the commands module has been removed in Python 3\.0:DeprecationWarning env = PYTHONHASHSEED=0 - -# addopts = --doctest-modules --ignore=numpy/f2py/__main__.py --ignore=numpy/core/cversions.py --ignore=numpy/ma/core.py --ignore=numpy/ma/version.py --ignore=numpy/testing/utils.py --ignore=numpy/testing/decorators.py @@ -219,7 +219,9 @@ def parse_setuppy_commands(): Return a boolean value for whether or not to run the build or not (avoid parsing Cython and template files if False). """ - if len(sys.argv) < 2: + args = sys.argv[1:] + + if not args: # User forgot to give an argument probably, let setuptools handle that. return True @@ -229,12 +231,9 @@ def parse_setuppy_commands(): '--contact-email', '--url', '--license', '--description', '--long-description', '--platforms', '--classifiers', '--keywords', '--provides', '--requires', '--obsoletes'] - # Add commands that do more than print info, but also don't need Cython and - # template parsing. - info_commands.extend(['egg_info', 'install_egg_info', 'rotate']) for command in info_commands: - if command in sys.argv[1:]: + if command in args: return False # Note that 'alias', 'saveopts' and 'setopt' commands also seem to work @@ -245,12 +244,12 @@ def parse_setuppy_commands(): 'bdist_wininst', 'bdist_msi', 'bdist_mpkg') for command in good_commands: - if command in sys.argv[1:]: + if command in args: return True # The following commands are supported, but we need to show more # useful messages to the user - if 'install' in sys.argv[1:]: + if 'install' in args: print(textwrap.dedent(""" Note: if you need reliable uninstall behavior, then install with pip instead of using `setup.py install`: @@ -262,7 +261,7 @@ def parse_setuppy_commands(): """)) return True - if '--help' in sys.argv[1:] or '-h' in sys.argv[1]: + if '--help' in args or '-h' in sys.argv[1]: print(textwrap.dedent(""" NumPy-specific help ------------------- @@ -280,6 +279,7 @@ def parse_setuppy_commands(): """)) return False + # The following commands aren't supported. They can only be executed when # the user explicitly adds a --force command-line argument. bad_commands = dict( @@ -322,12 +322,19 @@ def parse_setuppy_commands(): bad_commands[command] = "`setup.py %s` is not supported" % command for command in bad_commands.keys(): - if command in sys.argv[1:]: + if command in args: print(textwrap.dedent(bad_commands[command]) + "\nAdd `--force` to your command to use it anyway if you " "must (unsupported).\n") sys.exit(1) + # Commands that do more than print info, but also don't need Cython and + # template parsing. + other_commands = ['egg_info', 'install_egg_info', 'rotate'] + for command in other_commands: + if command in args: + return False + # If we got here, we didn't detect what setup.py command was given import warnings warnings.warn("Unrecognized setuptools command, proceeding with " diff --git a/tools/ci/push_docs_to_repo.py b/tools/ci/push_docs_to_repo.py new file mode 100755 index 000000000..a98966880 --- /dev/null +++ b/tools/ci/push_docs_to_repo.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +import argparse +import subprocess +import tempfile +import os +import sys +import shutil + + +parser = argparse.ArgumentParser( + description='Upload files to a remote repo, replacing existing content' +) +parser.add_argument('dir', help='directory of which content will be uploaded') +parser.add_argument('remote', help='remote to which content will be pushed') +parser.add_argument('--message', default='Commit bot upload', + help='commit message to use') +parser.add_argument('--committer', default='numpy-commit-bot', + help='Name of the git committer') +parser.add_argument('--email', default='numpy-commit-bot@nomail', + help='Email of the git committer') + +parser.add_argument( + '--force', action='store_true', + help='hereby acknowledge that remote repo content will be overwritten' +) +args = parser.parse_args() +args.dir = os.path.abspath(args.dir) + +if not os.path.exists(args.dir): + print('Content directory does not exist') + sys.exit(1) + + +def run(cmd, stdout=True): + pipe = None if stdout else subprocess.DEVNULL + try: + subprocess.check_call(cmd, stdout=pipe, stderr=pipe) + except subprocess.CalledProcessError: + print("\n! Error executing: `%s;` aborting" % ' '.join(cmd)) + sys.exit(1) + + +workdir = tempfile.mkdtemp() +os.chdir(workdir) + +run(['git', 'init']) +run(['git', 'remote', 'add', 'origin', args.remote]) +run(['git', 'config', '--local', 'user.name', args.committer]) +run(['git', 'config', '--local', 'user.email', args.email]) + +print('- committing new content: "%s"' % args.message) +run(['cp', '-R', os.path.join(args.dir, '.'), '.']) +run(['git', 'add', '.'], stdout=False) +run(['git', 'commit', '--allow-empty', '-m', args.message], stdout=False) + +print('- uploading as %s <%s>' % (args.committer, args.email)) +if args.force: + run(['git', 'push', 'origin', 'master', '--force']) +else: + print('\n!! No `--force` argument specified; aborting') + print('!! Before enabling that flag, make sure you know what it does\n') + sys.exit(1) + +shutil.rmtree(workdir) |