diff options
-rw-r--r-- | .lgtm.yml | 9 | ||||
-rw-r--r-- | numpy/core/_internal.py | 4 | ||||
-rw-r--r-- | numpy/core/records.py | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/buffer.c | 21 | ||||
-rw-r--r-- | numpy/core/src/multiarray/multiarraymodule.c | 2 | ||||
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 5 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 115 | ||||
-rw-r--r-- | numpy/core/tests/test_scalarbuffer.py | 8 | ||||
-rw-r--r-- | numpy/lib/_datasource.py | 8 | ||||
-rw-r--r-- | numpy/lib/polynomial.py | 6 | ||||
-rw-r--r-- | numpy/lib/tests/test__datasource.py | 23 | ||||
-rw-r--r-- | numpy/polynomial/_polybase.py | 4 | ||||
-rwxr-xr-x | tools/travis-test.sh | 24 |
13 files changed, 167 insertions, 66 deletions
diff --git a/.lgtm.yml b/.lgtm.yml new file mode 100644 index 000000000..c0a9cf59a --- /dev/null +++ b/.lgtm.yml @@ -0,0 +1,9 @@ +path_classifiers: + library: + - tools + generated: + # The exports defined in __init__.py are defined in the Cython module + # np.random.mtrand. By excluding this file we suppress a number of + # "undefined export" alerts + - numpy/random/__init__.py + diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py index 1cf89aab0..ce7ef7060 100644 --- a/numpy/core/_internal.py +++ b/numpy/core/_internal.py @@ -9,7 +9,7 @@ from __future__ import division, absolute_import, print_function import re import sys -from numpy.compat import basestring +from numpy.compat import basestring, unicode from .multiarray import dtype, array, ndarray try: import ctypes @@ -294,7 +294,7 @@ def _newnames(datatype, order): """ oldnames = datatype.names nameslist = list(oldnames) - if isinstance(order, str): + if isinstance(order, (str, unicode)): order = [order] seen = set() if isinstance(order, (list, tuple)): diff --git a/numpy/core/records.py b/numpy/core/records.py index 612d39322..a483871ba 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -42,7 +42,7 @@ import warnings from . import numeric as sb from . import numerictypes as nt -from numpy.compat import isfileobj, bytes, long +from numpy.compat import isfileobj, bytes, long, unicode from .arrayprint import get_printoptions # All of the functions allow formats to be a dtype @@ -174,7 +174,7 @@ class format_parser(object): if (names): if (type(names) in [list, tuple]): pass - elif isinstance(names, str): + elif isinstance(names, (str, unicode)): names = names.split(',') else: raise NameError("illegal input names %s" % repr(names)) diff --git a/numpy/core/src/multiarray/buffer.c b/numpy/core/src/multiarray/buffer.c index 0325f3c6a..d549a3029 100644 --- a/numpy/core/src/multiarray/buffer.c +++ b/numpy/core/src/multiarray/buffer.c @@ -175,6 +175,14 @@ _is_natively_aligned_at(PyArray_Descr *descr, return 1; } +/* + * Fill in str with an appropriate PEP 3118 format string, based on + * descr. For structured dtypes, calls itself recursively. Each call extends + * str at offset then updates offset, and uses descr->byteorder, (and + * possibly the byte order in obj) to determine the byte-order char. + * + * Returns 0 for success, -1 for failure + */ static int _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, PyObject* obj, Py_ssize_t *offset, @@ -195,8 +203,8 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, PyObject *item, *subarray_tuple; Py_ssize_t total_count = 1; Py_ssize_t dim_size; + Py_ssize_t old_offset; char buf[128]; - int old_offset; int ret; if (PyTuple_Check(descr->subarray->shape)) { @@ -230,15 +238,15 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, return ret; } else if (PyDataType_HASFIELDS(descr)) { - int base_offset = *offset; + Py_ssize_t base_offset = *offset; _append_str(str, "T{"); for (k = 0; k < PyTuple_GET_SIZE(descr->names); ++k) { PyObject *name, *item, *offset_obj, *tmp; PyArray_Descr *child; char *p; - Py_ssize_t len; - int new_offset; + Py_ssize_t len, new_offset; + int ret; name = PyTuple_GET_ITEM(descr->names, k); item = PyDict_GetItem(descr->fields, name); @@ -266,8 +274,11 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, } /* Insert child item */ - _buffer_format_string(child, str, obj, offset, + ret = _buffer_format_string(child, str, obj, offset, active_byteorder); + if (ret < 0) { + return -1; + } /* Insert field name */ #if defined(NPY_PY3K) diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index 6e57f1d6d..dc571dfcb 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -2020,7 +2020,7 @@ array_fromstring(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds if (DEPRECATE( "The binary mode of fromstring is deprecated, as it behaves " "surprisingly on unicode inputs. Use frombuffer instead") < 0) { - Py_DECREF(descr); + Py_XDECREF(descr); return NULL; } } diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 5d66d963f..10ef16800 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -518,3 +518,8 @@ class TestPositiveOnNonNumerical(_DeprecationTestCase): # 2018-06-28, 1.16.0 def test_positive_on_non_number(self): self.assert_deprecated(operator.pos, args=(np.array('foo'),)) + +class TestFromstring(_DeprecationTestCase): + # 2017-10-19, 1.14 + def test_fromstring(self): + self.assert_deprecated(np.fromstring, args=('\x00'*80,)) diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 1511f5b6b..d751657db 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -4748,55 +4748,72 @@ class TestRecord(object): # Error raised when multiple fields have the same name assert_raises(ValueError, test_assign) - if sys.version_info[0] >= 3: - def test_bytes_fields(self): - # Bytes are not allowed in field names and not recognized in titles - # on Py3 - assert_raises(TypeError, np.dtype, [(b'a', int)]) - assert_raises(TypeError, np.dtype, [(('b', b'a'), int)]) - - dt = np.dtype([((b'a', 'b'), int)]) - assert_raises(TypeError, dt.__getitem__, b'a') - - x = np.array([(1,), (2,), (3,)], dtype=dt) - assert_raises(IndexError, x.__getitem__, b'a') - - y = x[0] - assert_raises(IndexError, y.__getitem__, b'a') - - def test_multiple_field_name_unicode(self): - def test_assign_unicode(): - dt = np.dtype([("\u20B9", "f8"), - ("B", "f8"), - ("\u20B9", "f8")]) - - # Error raised when multiple fields have the same name(unicode included) - assert_raises(ValueError, test_assign_unicode) - - else: - def test_unicode_field_titles(self): - # Unicode field titles are added to field dict on Py2 - title = u'b' - dt = np.dtype([((title, 'a'), int)]) - dt[title] - dt['a'] - x = np.array([(1,), (2,), (3,)], dtype=dt) - x[title] - x['a'] - y = x[0] - y[title] - y['a'] - - def test_unicode_field_names(self): - # Unicode field names are converted to ascii on Python 2: - encodable_name = u'b' - assert_equal(np.dtype([(encodable_name, int)]).names[0], b'b') - assert_equal(np.dtype([(('a', encodable_name), int)]).names[0], b'b') - - # But raises UnicodeEncodeError if it can't be encoded: - nonencodable_name = u'\uc3bc' - assert_raises(UnicodeEncodeError, np.dtype, [(nonencodable_name, int)]) - assert_raises(UnicodeEncodeError, np.dtype, [(('a', nonencodable_name), int)]) + @pytest.mark.skipif(sys.version_info[0] < 3, reason="Not Python 3") + def test_bytes_fields(self): + # Bytes are not allowed in field names and not recognized in titles + # on Py3 + assert_raises(TypeError, np.dtype, [(b'a', int)]) + assert_raises(TypeError, np.dtype, [(('b', b'a'), int)]) + + dt = np.dtype([((b'a', 'b'), int)]) + assert_raises(TypeError, dt.__getitem__, b'a') + + x = np.array([(1,), (2,), (3,)], dtype=dt) + assert_raises(IndexError, x.__getitem__, b'a') + + y = x[0] + assert_raises(IndexError, y.__getitem__, b'a') + + @pytest.mark.skipif(sys.version_info[0] < 3, reason="Not Python 3") + def test_multiple_field_name_unicode(self): + def test_assign_unicode(): + dt = np.dtype([("\u20B9", "f8"), + ("B", "f8"), + ("\u20B9", "f8")]) + + # Error raised when multiple fields have the same name(unicode included) + assert_raises(ValueError, test_assign_unicode) + + @pytest.mark.skipif(sys.version_info[0] >= 3, reason="Not Python 2") + def test_unicode_field_titles(self): + # Unicode field titles are added to field dict on Py2 + title = u'b' + dt = np.dtype([((title, 'a'), int)]) + dt[title] + dt['a'] + x = np.array([(1,), (2,), (3,)], dtype=dt) + x[title] + x['a'] + y = x[0] + y[title] + y['a'] + + @pytest.mark.skipif(sys.version_info[0] >= 3, reason="Not Python 2") + def test_unicode_field_names(self): + # Unicode field names are converted to ascii on Python 2: + encodable_name = u'b' + assert_equal(np.dtype([(encodable_name, int)]).names[0], b'b') + assert_equal(np.dtype([(('a', encodable_name), int)]).names[0], b'b') + + # But raises UnicodeEncodeError if it can't be encoded: + nonencodable_name = u'\uc3bc' + assert_raises(UnicodeEncodeError, np.dtype, [(nonencodable_name, int)]) + assert_raises(UnicodeEncodeError, np.dtype, [(('a', nonencodable_name), int)]) + + def test_fromarrays_unicode(self): + # A single name string provided to fromarrays() is allowed to be unicode + # on both Python 2 and 3: + x = np.core.records.fromarrays([[0], [1]], names=u'a,b', formats=u'i4,i4') + assert_equal(x['a'][0], 0) + assert_equal(x['b'][0], 1) + + def test_unicode_order(self): + # Test that we can sort with order as a unicode field name in both Python 2 and + # 3: + name = u'b' + x = np.array([1, 3, 2], dtype=[(name, int)]) + x.sort(order=name) + assert_equal(x[u'b'], np.array([1, 2, 3])) def test_field_names(self): # Test unicode and 8-bit / byte strings can be used diff --git a/numpy/core/tests/test_scalarbuffer.py b/numpy/core/tests/test_scalarbuffer.py index 6d57a5014..9e75fec3e 100644 --- a/numpy/core/tests/test_scalarbuffer.py +++ b/numpy/core/tests/test_scalarbuffer.py @@ -5,7 +5,7 @@ import sys import numpy as np import pytest -from numpy.testing import assert_, assert_equal +from numpy.testing import assert_, assert_equal, assert_raises # PEP3118 format strings for native (standard alignment and byteorder) types scalars_and_codes = [ @@ -77,3 +77,9 @@ class TestScalarPEP3118(object): mv_a = memoryview(a) assert_equal(mv_x.itemsize, mv_a.itemsize) assert_equal(mv_x.format, mv_a.format) + + def test_invalid_buffer_format(self): + # datetime64 cannot be used in a buffer yet + dt = np.dtype([('a', int), ('b', 'M8[s]')]) + a = np.empty(1, dt) + assert_raises((ValueError, BufferError), memoryview, a[0]) diff --git a/numpy/lib/_datasource.py b/numpy/lib/_datasource.py index 6f1295f09..ab00b1444 100644 --- a/numpy/lib/_datasource.py +++ b/numpy/lib/_datasource.py @@ -37,6 +37,7 @@ from __future__ import division, absolute_import, print_function import os import sys +import warnings import shutil import io @@ -85,9 +86,10 @@ def _python2_bz2open(fn, mode, encoding, newline): if "t" in mode: # BZ2File is missing necessary functions for TextIOWrapper - raise ValueError("bz2 text files not supported in python2") - else: - return bz2.BZ2File(fn, mode) + warnings.warn("Assuming latin1 encoding for bz2 text file in Python2", + RuntimeWarning, stacklevel=5) + mode = mode.replace("t", "") + return bz2.BZ2File(fn, mode) def _python2_gzipopen(fn, mode, encoding, newline): """ Wrapper to open gzip in text mode. diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index 0e691f56e..9f3b84732 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -396,7 +396,11 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): Fit a polynomial ``p(x) = p[0] * x**deg + ... + p[deg]`` of degree `deg` to points `(x, y)`. Returns a vector of coefficients `p` that minimises - the squared error. + the squared error in the order `deg`, `deg-1`, ... `0`. + + The `Polynomial.fit <numpy.polynomial.polynomial.Polynomial.fit>` class + method is recommended for new code as it is more stable numerically. See + the documentation of the method for more information. Parameters ---------- diff --git a/numpy/lib/tests/test__datasource.py b/numpy/lib/tests/test__datasource.py index 32812990c..70fff3bb0 100644 --- a/numpy/lib/tests/test__datasource.py +++ b/numpy/lib/tests/test__datasource.py @@ -2,11 +2,14 @@ from __future__ import division, absolute_import, print_function import os import sys +import pytest from tempfile import mkdtemp, mkstemp, NamedTemporaryFile from shutil import rmtree -from numpy.testing import assert_, assert_equal, assert_raises, SkipTest import numpy.lib._datasource as datasource +from numpy.testing import ( + assert_, assert_equal, assert_raises, assert_warns, SkipTest + ) if sys.version_info[0] >= 3: import urllib.request as urllib_request @@ -161,6 +164,24 @@ class TestDataSourceOpen(object): fp.close() assert_equal(magic_line, result) + @pytest.mark.skipif(sys.version_info[0] >= 3, reason="Python 2 only") + def test_Bz2File_text_mode_warning(self): + try: + import bz2 + except ImportError: + # We don't have the bz2 capabilities to test. + raise SkipTest + # Test datasource's internal file_opener for BZip2 files. + filepath = os.path.join(self.tmpdir, 'foobar.txt.bz2') + fp = bz2.BZ2File(filepath, 'w') + fp.write(magic_line) + fp.close() + with assert_warns(RuntimeWarning): + fp = self.ds.open(filepath, 'rt') + result = fp.readline() + fp.close() + assert_equal(magic_line, result) + class TestDataSourceExists(object): def setup(self): diff --git a/numpy/polynomial/_polybase.py b/numpy/polynomial/_polybase.py index 9f4d30e53..ccbf30bda 100644 --- a/numpy/polynomial/_polybase.py +++ b/numpy/polynomial/_polybase.py @@ -860,7 +860,9 @@ class ABCPolyBase(object): ------- new_series : series A series that represents the least squares fit to the data and - has the domain specified in the call. + has the domain and window specified in the call. If the + coefficients for the unscaled and unshifted basis polynomials are + of interest, do ``new_series.convert().coef``. [resid, rank, sv, rcond] : list These values are only returned if `full` = True diff --git a/tools/travis-test.sh b/tools/travis-test.sh index 11863b5fe..97b192813 100755 --- a/tools/travis-test.sh +++ b/tools/travis-test.sh @@ -125,6 +125,22 @@ run_test() fi if [ -n "$RUN_COVERAGE" ]; then + # move back up to the source dir because we want to execute + # gcov on the source files after the tests have gone through + # the code paths + cd .. + + # execute gcov on source files + find . -name '*.gcno' -type f -exec gcov -pb {} + + + # move the C line coverage report files to the same path + # as the Python report data + mv *.gcov empty + + # move back to the previous path for good measure + # as the Python coverage data is there + cd empty + # Upload coverage files to codecov bash <(curl -s https://codecov.io/bash) -X gcov -X coveragepy fi @@ -153,6 +169,14 @@ if [ -n "$USE_WHEEL" ] && [ $# -eq 0 ]; then $PIP install -U virtualenv # ensure some warnings are not issued export CFLAGS=$CFLAGS" -Wno-sign-compare -Wno-unused-result" + # adjust gcc flags if C coverage requested + if [ -n "$RUN_COVERAGE" ]; then + export NPY_DISTUTILS_APPEND_FLAGS=1 + export CC='gcc --coverage' + export F77='gfortran --coverage' + export F90='gfortran --coverage' + export LDFLAGS='--coverage' + fi $PYTHON setup.py bdist_wheel # Make another virtualenv to install into virtualenv --python=`which $PYTHON` venv-for-wheel |