From 7884a8c9f5f5c6657413dbeaa59ad969280d38ea Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Sat, 23 Jan 2016 15:58:58 +0100 Subject: [PATCH 1/4] ENH: Add stacklevel to all (or almost all) our function calls --- numpy/_import_tools.py | 2 +- numpy/core/_methods.py | 5 +++-- numpy/core/fromnumeric.py | 2 +- numpy/core/numeric.py | 10 +++++---- numpy/core/setup.py | 2 +- numpy/core/setup_common.py | 2 +- numpy/core/tests/test_deprecations.py | 2 +- numpy/ctypeslib.py | 2 +- numpy/distutils/command/config.py | 2 +- numpy/distutils/core.py | 15 +++++++++----- numpy/distutils/cpuinfo.py | 2 +- numpy/distutils/extension.py | 2 +- numpy/distutils/fcompiler/gnu.py | 2 +- numpy/distutils/misc_util.py | 2 +- numpy/distutils/system_info.py | 26 +++++++++++------------ numpy/lib/format.py | 4 ++-- numpy/lib/function_base.py | 30 +++++++++++++-------------- numpy/lib/nanfunctions.py | 18 ++++++++-------- numpy/lib/npyio.py | 6 +++--- numpy/lib/polynomial.py | 2 +- numpy/lib/utils.py | 6 +++--- numpy/linalg/linalg.py | 4 ++-- numpy/ma/core.py | 7 ++++--- numpy/ma/extras.py | 2 +- numpy/ma/mrecords.py | 2 +- numpy/polynomial/chebyshev.py | 2 +- numpy/polynomial/hermite.py | 2 +- numpy/polynomial/hermite_e.py | 2 +- numpy/polynomial/laguerre.py | 2 +- numpy/polynomial/legendre.py | 2 +- numpy/polynomial/polynomial.py | 2 +- pavement.py | 2 +- setup.py | 2 +- 33 files changed, 92 insertions(+), 83 deletions(-) diff --git a/numpy/_import_tools.py b/numpy/_import_tools.py index eda0284fe187..18ac78d29314 100644 --- a/numpy/_import_tools.py +++ b/numpy/_import_tools.py @@ -166,7 +166,7 @@ def __call__(self,*packages, **options): # 2014-10-29, 1.10 warnings.warn('pkgload and PackageLoader are obsolete ' 'and will be removed in a future version of numpy', - DeprecationWarning) + DeprecationWarning, stacklevel=2) frame = self.parent_frame self.info_modules = {} if options.get('force', False): diff --git a/numpy/core/_methods.py b/numpy/core/_methods.py index 5fc2bc4450d3..54e26754106b 100644 --- a/numpy/core/_methods.py +++ b/numpy/core/_methods.py @@ -56,7 +56,7 @@ def _mean(a, axis=None, dtype=None, out=None, keepdims=False): rcount = _count_reduce_items(arr, axis) # Make this warning show up first if rcount == 0: - warnings.warn("Mean of empty slice.", RuntimeWarning) + warnings.warn("Mean of empty slice.", RuntimeWarning, stacklevel=2) # Cast bool, unsigned int, and int to float64 by default if dtype is None and issubclass(arr.dtype.type, (nt.integer, nt.bool_)): @@ -79,7 +79,8 @@ def _var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): rcount = _count_reduce_items(arr, axis) # Make this warning show up on top. if ddof >= rcount: - warnings.warn("Degrees of freedom <= 0 for slice", RuntimeWarning) + warnings.warn("Degrees of freedom <= 0 for slice", RuntimeWarning, + stacklevel=2) # Cast bool, unsigned int, and int to float64 by default if dtype is None and issubclass(arr.dtype.type, (nt.integer, nt.bool_)): diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index d07c5c08b429..99173d105fb6 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -2647,7 +2647,7 @@ def rank(a): warnings.warn( "`rank` is deprecated; use the `ndim` attribute or function instead. " "To find the rank of a matrix see `numpy.linalg.matrix_rank`.", - VisibleDeprecationWarning) + VisibleDeprecationWarning, stacklevel=2) try: return a.ndim except AttributeError: diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 8db4e1302e21..81ed0178a08e 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -1181,7 +1181,8 @@ def alterdot(): """ # 2014-08-13, 1.10 - warnings.warn("alterdot no longer does anything.", DeprecationWarning) + warnings.warn("alterdot no longer does anything.", + DeprecationWarning, stacklevel=2) def restoredot(): @@ -1205,7 +1206,8 @@ def restoredot(): """ # 2014-08-13, 1.10 - warnings.warn("restoredot no longer does anything.", DeprecationWarning) + warnings.warn("restoredot no longer does anything.", + DeprecationWarning, stacklevel=2) def tensordot(a, b, axes=2): @@ -2260,8 +2262,8 @@ def warn_if_insufficient(width, binwdith): if width is not None and width < binwidth: warnings.warn( "Insufficient bit width provided. This behavior " - "will raise an error in the future.", DeprecationWarning - ) + "will raise an error in the future.", DeprecationWarning, + stacklevel=3) if num == 0: return '0' * (width or 1) diff --git a/numpy/core/setup.py b/numpy/core/setup.py index c6c1f6c03b0a..bec35848042b 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -179,7 +179,7 @@ def check_complex(config, mathlibs): try: if os.uname()[0] == "Interix": - warnings.warn("Disabling broken complex support. See #1365") + warnings.warn("Disabling broken complex support. See #1365", stacklevel=2) return priv, pub except: # os.uname not available on all platforms. blanket except ugly but safe diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py index ba7521e3043b..90b5929999dc 100644 --- a/numpy/core/setup_common.py +++ b/numpy/core/setup_common.py @@ -94,7 +94,7 @@ def check_api_version(apiversion, codegen_dir): ) warnings.warn(msg % (apiversion, curapi_hash, apiversion, api_hash, __file__), - MismatchCAPIWarning) + MismatchCAPIWarning, stacklevel=2) # Mandatory functions: if not found, fail the build MANDATORY_FUNCS = ["sin", "cos", "tan", "sinh", "cosh", "tanh", "fabs", "floor", "ceil", "sqrt", "log10", "log", "exp", "asin", diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index e03edb2ea268..47870581a021 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -641,7 +641,7 @@ def test_assert_deprecated(self): lambda: None) def foo(): - warnings.warn("foo", category=DeprecationWarning) + warnings.warn("foo", category=DeprecationWarning, stacklevel=2) test_case_instance.assert_deprecated(foo) test_case_instance.tearDown() diff --git a/numpy/ctypeslib.py b/numpy/ctypeslib.py index 36bcf2764577..bf34ce38b8fc 100644 --- a/numpy/ctypeslib.py +++ b/numpy/ctypeslib.py @@ -119,7 +119,7 @@ def load_library(libname, loader_path): if ctypes.__version__ < '1.0.1': import warnings warnings.warn("All features of ctypes interface may not work " \ - "with ctypes < 1.0.1") + "with ctypes < 1.0.1", stacklevel=2) ext = os.path.splitext(libname)[1] if not ext: diff --git a/numpy/distutils/command/config.py b/numpy/distutils/command/config.py index ea1cd3e24af8..e43fb631beb9 100644 --- a/numpy/distutils/command/config.py +++ b/numpy/distutils/command/config.py @@ -431,7 +431,7 @@ def get_output(self, body, headers=None, include_dirs=None, "use it anymore, and avoid configuration checks \n" \ "involving running executable on the target machine.\n" \ "+++++++++++++++++++++++++++++++++++++++++++++++++\n", - DeprecationWarning) + DeprecationWarning, stacklevel=2) from distutils.ccompiler import CompileError, LinkError self._check_compiler() exitcode, output = 255, '' diff --git a/numpy/distutils/core.py b/numpy/distutils/core.py index 3f0fd464a0d3..d9e125368909 100644 --- a/numpy/distutils/core.py +++ b/numpy/distutils/core.py @@ -176,18 +176,21 @@ def _check_append_library(libraries, item): if item[1] is libitem[1]: return warnings.warn("[0] libraries list contains %r with" - " different build_info" % (item[0],)) + " different build_info" % (item[0],), + stacklevel=2) break else: if item==libitem[0]: warnings.warn("[1] libraries list contains %r with" - " no build_info" % (item[0],)) + " no build_info" % (item[0],), + stacklevel=2) break else: if is_sequence(item): if item[0]==libitem: warnings.warn("[2] libraries list contains %r with" - " no build_info" % (item[0],)) + " no build_info" % (item[0],), + stacklevel=2) break else: if item==libitem: @@ -201,10 +204,12 @@ def _check_append_ext_library(libraries, lib_name, build_info): if item[1] is build_info: return warnings.warn("[3] libraries list contains %r with" - " different build_info" % (lib_name,)) + " different build_info" % (lib_name,), + stacklevel=2) break elif item==lib_name: warnings.warn("[4] libraries list contains %r with" - " no build_info" % (lib_name,)) + " no build_info" % (lib_name,), + stacklevel=2) break libraries.append((lib_name, build_info)) diff --git a/numpy/distutils/cpuinfo.py b/numpy/distutils/cpuinfo.py index 020f2c02fee6..03860c1517cc 100644 --- a/numpy/distutils/cpuinfo.py +++ b/numpy/distutils/cpuinfo.py @@ -117,7 +117,7 @@ def __init__(self): fo = open('/proc/cpuinfo') except EnvironmentError: e = get_exception() - warnings.warn(str(e), UserWarning) + warnings.warn(str(e), UserWarning, stacklevel=2) else: for line in fo: name_value = [s.strip() for s in line.split(':', 1)] diff --git a/numpy/distutils/extension.py b/numpy/distutils/extension.py index ebb263bd165f..935f3eec9254 100644 --- a/numpy/distutils/extension.py +++ b/numpy/distutils/extension.py @@ -63,7 +63,7 @@ def __init__ ( if isinstance(self.swig_opts, basestring): import warnings msg = "swig_opts is specified as a string instead of a list" - warnings.warn(msg, SyntaxWarning) + warnings.warn(msg, SyntaxWarning, stacklevel=2) self.swig_opts = self.swig_opts.split() # Python 2.3 distutils new features diff --git a/numpy/distutils/fcompiler/gnu.py b/numpy/distutils/fcompiler/gnu.py index 9697aa8dba44..fd49db49216b 100644 --- a/numpy/distutils/fcompiler/gnu.py +++ b/numpy/distutils/fcompiler/gnu.py @@ -137,7 +137,7 @@ def get_flags_linker_so(self): os.environ['MACOSX_DEPLOYMENT_TARGET'] = target if target == '10.3': s = 'Env. variable MACOSX_DEPLOYMENT_TARGET set to 10.3' - warnings.warn(s) + warnings.warn(s, stacklevel=2) opt.extend(['-undefined', 'dynamic_lookup', '-bundle']) else: diff --git a/numpy/distutils/misc_util.py b/numpy/distutils/misc_util.py index 2cd1853329dc..0070678d3a7f 100644 --- a/numpy/distutils/misc_util.py +++ b/numpy/distutils/misc_util.py @@ -2206,7 +2206,7 @@ def default_config_dict(name = None, parent_name = None, local_path=None): 'deprecated default_config_dict(%r,%r,%r)' % (name, parent_name, local_path, name, parent_name, local_path, - )) + ), stacklevel=2) c = Configuration(name, parent_name, local_path) return c.todict() diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py index c3a62464bb72..286154e8fb98 100644 --- a/numpy/distutils/system_info.py +++ b/numpy/distutils/system_info.py @@ -573,7 +573,7 @@ def get_info(self, notfound_action=0): if notfound_action: if not self.has_info(): if notfound_action == 1: - warnings.warn(self.notfounderror.__doc__) + warnings.warn(self.notfounderror.__doc__, stacklevel=2) elif notfound_action == 2: raise self.notfounderror(self.notfounderror.__doc__) else: @@ -642,7 +642,7 @@ def get_paths(self, section, key): ret = [] for d in dirs: if len(d) > 0 and not os.path.isdir(d): - warnings.warn('Specified path %s is invalid.' % d) + warnings.warn('Specified path %s is invalid.' % d, stacklevel=2) continue if d not in ret: @@ -1104,7 +1104,7 @@ def calc_info(self): Could not find lapack library within the ATLAS installation. ********************************************************************* """ - warnings.warn(message) + warnings.warn(message, stacklevel=2) self.set_info(**info) return @@ -1135,7 +1135,7 @@ def calc_info(self): numpy/INSTALL.txt. ********************************************************************* """ % (lapack_lib, sz / 1024) - warnings.warn(message) + warnings.warn(message, stacklevel=2) else: info['language'] = 'f77' @@ -1420,7 +1420,7 @@ def get_atlas_version(**config): when building extension libraries that use ATLAS. Make sure that -lgfortran is used for C++ extensions. ***************************************************** -""") +""", stacklevel=2) dict_append(info, language='f90', define_macros=[('ATLAS_REQUIRES_GFORTRAN', None)]) except Exception: # failed to get version from file -- maybe on Windows @@ -1531,7 +1531,7 @@ def calc_info(self): info = atlas_info else: - warnings.warn(AtlasNotFoundError.__doc__) + warnings.warn(AtlasNotFoundError.__doc__, stacklevel=2) need_blas = 1 need_lapack = 1 dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)]) @@ -1542,10 +1542,10 @@ def calc_info(self): if lapack_info: dict_append(info, **lapack_info) else: - warnings.warn(LapackNotFoundError.__doc__) + warnings.warn(LapackNotFoundError.__doc__, stacklevel=2) lapack_src_info = get_info('lapack_src') if not lapack_src_info: - warnings.warn(LapackSrcNotFoundError.__doc__) + warnings.warn(LapackSrcNotFoundError.__doc__, stacklevel=2) return dict_append(info, libraries=[('flapack_src', lapack_src_info)]) @@ -1554,10 +1554,10 @@ def calc_info(self): if blas_info: dict_append(info, **blas_info) else: - warnings.warn(BlasNotFoundError.__doc__) + warnings.warn(BlasNotFoundError.__doc__, stacklevel=2) blas_src_info = get_info('blas_src') if not blas_src_info: - warnings.warn(BlasSrcNotFoundError.__doc__) + warnings.warn(BlasSrcNotFoundError.__doc__, stacklevel=2) return dict_append(info, libraries=[('fblas_src', blas_src_info)]) @@ -1634,7 +1634,7 @@ def calc_info(self): if atlas_info: info = atlas_info else: - warnings.warn(AtlasNotFoundError.__doc__) + warnings.warn(AtlasNotFoundError.__doc__, stacklevel=2) need_blas = 1 dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)]) @@ -1643,10 +1643,10 @@ def calc_info(self): if blas_info: dict_append(info, **blas_info) else: - warnings.warn(BlasNotFoundError.__doc__) + warnings.warn(BlasNotFoundError.__doc__, stacklevel=2) blas_src_info = get_info('blas_src') if not blas_src_info: - warnings.warn(BlasSrcNotFoundError.__doc__) + warnings.warn(BlasSrcNotFoundError.__doc__, stacklevel=2) return dict_append(info, libraries=[('fblas_src', blas_src_info)]) diff --git a/numpy/lib/format.py b/numpy/lib/format.py index e62677bd86eb..633aee67574e 100644 --- a/numpy/lib/format.py +++ b/numpy/lib/format.py @@ -556,7 +556,7 @@ def write_array(fp, array, version=None, allow_pickle=True, pickle_kwargs=None): # this warning can be removed when 1.9 has aged enough if version != (2, 0) and used_ver == (2, 0): warnings.warn("Stored array in format 2.0. It can only be" - "read by NumPy >= 1.9", UserWarning) + "read by NumPy >= 1.9", UserWarning, stacklevel=2) if array.itemsize == 0: buffersize = 0 @@ -759,7 +759,7 @@ def open_memmap(filename, mode='r+', dtype=None, shape=None, # this warning can be removed when 1.9 has aged enough if version != (2, 0) and used_ver == (2, 0): warnings.warn("Stored array in format 2.0. It can only be" - "read by NumPy >= 1.9", UserWarning) + "read by NumPy >= 1.9", UserWarning, stacklevel=2) offset = fp.tell() finally: fp.close() diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index a866ed767c78..b74e04028210 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1383,7 +1383,7 @@ def select(condlist, choicelist, default=0): # 2014-02-24, 1.9 warnings.warn("select with an empty condition list is not possible" "and will be deprecated", - DeprecationWarning) + DeprecationWarning, stacklevel=2) return np.asarray(default)[()] choicelist = [np.asarray(choice) for choice in choicelist] @@ -1418,7 +1418,7 @@ def select(condlist, choicelist, default=0): msg = "select condlists containing integer ndarrays is deprecated " \ "and will be removed in the future. Use `.astype(bool)` to " \ "convert to bools." - warnings.warn(msg, DeprecationWarning) + warnings.warn(msg, DeprecationWarning, stacklevel=2) if choicelist[0].ndim == 0: # This may be common, so avoid the call. @@ -2692,7 +2692,7 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, fact = w_sum - ddof*sum(w*aweights)/w_sum if fact <= 0: - warnings.warn("Degrees of freedom <= 0 for slice", RuntimeWarning) + warnings.warn("Degrees of freedom <= 0 for slice", RuntimeWarning, stacklevel=2) fact = 0.0 X -= avg[:, None] @@ -2766,7 +2766,7 @@ def corrcoef(x, y=None, rowvar=1, bias=np._NoValue, ddof=np._NoValue): if bias is not np._NoValue or ddof is not np._NoValue: # 2015-03-15, 1.10 warnings.warn('bias and ddof have no effect and are deprecated', - DeprecationWarning) + DeprecationWarning, stacklevel=2) c = cov(x, y, rowvar) try: d = diag(c) @@ -3774,7 +3774,7 @@ def _median(a, axis=None, out=None, overwrite_input=False): if rout.ndim == 0: if n == True: warnings.warn("Invalid value encountered in median", - RuntimeWarning) + RuntimeWarning, stacklevel=3) if out is not None: out[...] = a.dtype.type(np.nan) rout = out @@ -3783,7 +3783,7 @@ def _median(a, axis=None, out=None, overwrite_input=False): elif np.count_nonzero(n.ravel()) > 0: warnings.warn("Invalid value encountered in median for" + " %d results" % np.count_nonzero(n.ravel()), - RuntimeWarning) + RuntimeWarning, stacklevel=3) rout[n] = np.nan return rout else: @@ -4039,7 +4039,7 @@ def _percentile(a, q, axis=None, out=None, if np.any(n): warnings.warn("Invalid value encountered in percentile", - RuntimeWarning) + RuntimeWarning, stacklevel=3) if zerod: if ap.ndim == 1: if out is not None: @@ -4403,7 +4403,7 @@ def delete(arr, obj, axis=None): # 2013-09-24, 1.9 warnings.warn( "in the future the special handling of scalars will be removed " - "from delete and raise an error", DeprecationWarning) + "from delete and raise an error", DeprecationWarning, stacklevel=2) if wrap: return wrap(arr) else: @@ -4470,7 +4470,7 @@ def delete(arr, obj, axis=None): if obj.dtype == bool: warnings.warn( "in the future insert will treat boolean arrays and array-likes " - "as boolean index instead of casting it to integer", FutureWarning) + "as boolean index instead of casting it to integer", FutureWarning, stacklevel=2) obj = obj.astype(intp) if isinstance(_obj, (int, long, integer)): # optimization for a single value @@ -4498,7 +4498,7 @@ def delete(arr, obj, axis=None): # 2013-09-24, 1.9 warnings.warn( "using a non-integer array as obj in delete will result in an " - "error in the future", DeprecationWarning) + "error in the future", DeprecationWarning, stacklevel=2) obj = obj.astype(intp) keep = ones(N, dtype=bool) @@ -4509,13 +4509,13 @@ def delete(arr, obj, axis=None): warnings.warn( "in the future out of bounds indices will raise an error " "instead of being ignored by `numpy.delete`.", - DeprecationWarning) + DeprecationWarning, stacklevel=2) obj = obj[inside_bounds] positive_indices = obj >= 0 if not positive_indices.all(): warnings.warn( "in the future negative indices will not be ignored by " - "`numpy.delete`.", FutureWarning) + "`numpy.delete`.", FutureWarning, stacklevel=2) obj = obj[positive_indices] keep[obj, ] = False @@ -4642,7 +4642,7 @@ def insert(arr, obj, values, axis=None): # 2013-09-24, 1.9 warnings.warn( "in the future the special handling of scalars will be removed " - "from insert and raise an error", DeprecationWarning) + "from insert and raise an error", DeprecationWarning, stacklevel=2) arr = arr.copy(order=arrorder) arr[...] = values if wrap: @@ -4664,7 +4664,7 @@ def insert(arr, obj, values, axis=None): warnings.warn( "in the future insert will treat boolean arrays and " "array-likes as a boolean index instead of casting it to " - "integer", FutureWarning) + "integer", FutureWarning, stacklevel=2) indices = indices.astype(intp) # Code after warning period: #if obj.ndim != 1: @@ -4714,7 +4714,7 @@ def insert(arr, obj, values, axis=None): # 2013-09-24, 1.9 warnings.warn( "using a non-integer array as obj in insert will result in an " - "error in the future", DeprecationWarning) + "error in the future", DeprecationWarning, stacklevel=2) indices = indices.astype(intp) indices[indices < 0] += N diff --git a/numpy/lib/nanfunctions.py b/numpy/lib/nanfunctions.py index c2fc92ebf76b..7f7aea9bc042 100644 --- a/numpy/lib/nanfunctions.py +++ b/numpy/lib/nanfunctions.py @@ -236,7 +236,7 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): # Fast, but not safe for subclasses of ndarray res = np.fmin.reduce(a, axis=axis, out=out, **kwargs) if np.isnan(res).any(): - warnings.warn("All-NaN axis encountered", RuntimeWarning) + warnings.warn("All-NaN axis encountered", RuntimeWarning, stacklevel=2) else: # Slow, but safe for subclasses of ndarray a, mask = _replace_nan(a, +np.inf) @@ -248,7 +248,7 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): mask = np.all(mask, axis=axis, **kwargs) if np.any(mask): res = _copyto(res, np.nan, mask) - warnings.warn("All-NaN axis encountered", RuntimeWarning) + warnings.warn("All-NaN axis encountered", RuntimeWarning, stacklevel=2) return res @@ -343,7 +343,7 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): # Fast, but not safe for subclasses of ndarray res = np.fmax.reduce(a, axis=axis, out=out, **kwargs) if np.isnan(res).any(): - warnings.warn("All-NaN slice encountered", RuntimeWarning) + warnings.warn("All-NaN slice encountered", RuntimeWarning, stacklevel=2) else: # Slow, but safe for subclasses of ndarray a, mask = _replace_nan(a, -np.inf) @@ -355,7 +355,7 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): mask = np.all(mask, axis=axis, **kwargs) if np.any(mask): res = _copyto(res, np.nan, mask) - warnings.warn("All-NaN axis encountered", RuntimeWarning) + warnings.warn("All-NaN axis encountered", RuntimeWarning, stacklevel=2) return res @@ -821,7 +821,7 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): isbad = (cnt == 0) if isbad.any(): - warnings.warn("Mean of empty slice", RuntimeWarning) + warnings.warn("Mean of empty slice", RuntimeWarning, stacklevel=2) # NaN is the only possible bad value, so no further # action is needed to handle bad results. return avg @@ -835,7 +835,7 @@ def _nanmedian1d(arr1d, overwrite_input=False): c = np.isnan(arr1d) s = np.where(c)[0] if s.size == arr1d.size: - warnings.warn("All-NaN slice encountered", RuntimeWarning) + warnings.warn("All-NaN slice encountered", RuntimeWarning, stacklevel=3) return np.nan elif s.size == 0: return np.median(arr1d, overwrite_input=overwrite_input) @@ -887,7 +887,7 @@ def _nanmedian_small(a, axis=None, out=None, overwrite_input=False): a = np.ma.masked_array(a, np.isnan(a)) m = np.ma.median(a, axis=axis, overwrite_input=overwrite_input) for i in range(np.count_nonzero(m.mask.ravel())): - warnings.warn("All-NaN slice encountered", RuntimeWarning) + warnings.warn("All-NaN slice encountered", RuntimeWarning, stacklevel=3) if out is not None: out[...] = m.filled(np.nan) return out @@ -1161,7 +1161,7 @@ def _nanpercentile1d(arr1d, q, overwrite_input=False, interpolation='linear'): c = np.isnan(arr1d) s = np.where(c)[0] if s.size == arr1d.size: - warnings.warn("All-NaN slice encountered", RuntimeWarning) + warnings.warn("All-NaN slice encountered", RuntimeWarning, stacklevel=3) if q.ndim == 0: return np.nan else: @@ -1317,7 +1317,7 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): isbad = (dof <= 0) if np.any(isbad): - warnings.warn("Degrees of freedom <= 0 for slice.", RuntimeWarning) + warnings.warn("Degrees of freedom <= 0 for slice.", RuntimeWarning, stacklevel=2) # NaN, inf, or negative numbers are all possible bad # values, so explicitly replace them with NaN. var = _copyto(var, np.nan, isbad) diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 534aa695cbdc..0b2fdfaba88e 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -944,7 +944,7 @@ def split_line(line): # End of lines reached first_line = '' first_vals = [] - warnings.warn('loadtxt: Empty input file: "%s"' % fname) + warnings.warn('loadtxt: Empty input file: "%s"' % fname, stacklevel=2) N = len(usecols or first_vals) dtype_types, packing = flatten_dtype(dtype) @@ -1542,7 +1542,7 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, # return an empty array if the datafile is empty first_line = asbytes('') first_values = [] - warnings.warn('genfromtxt: Empty input file: "%s"' % fname) + warnings.warn('genfromtxt: Empty input file: "%s"' % fname, stacklevel=2) # Should we take the first values as names ? if names is True: @@ -1827,7 +1827,7 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, raise ValueError(errmsg) # Issue a warning ? else: - warnings.warn(errmsg, ConversionWarning) + warnings.warn(errmsg, ConversionWarning, stacklevel=2) # Strip the last skip_footer data if skip_footer > 0: diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index d178ba4b307e..81c72749a6a9 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -588,7 +588,7 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): # warn on rank reduction, which indicates an ill conditioned matrix if rank != order and not full: msg = "Polyfit may be poorly conditioned" - warnings.warn(msg, RankWarning) + warnings.warn(msg, RankWarning, stacklevel=2) if full: return c, resids, rank, s, rcond diff --git a/numpy/lib/utils.py b/numpy/lib/utils.py index a2191468f709..133704d131ea 100644 --- a/numpy/lib/utils.py +++ b/numpy/lib/utils.py @@ -96,7 +96,7 @@ def __call__(self, func, *args, **kwargs): def newfunc(*args,**kwds): """`arrayrange` is deprecated, use `arange` instead!""" - warnings.warn(depdoc, DeprecationWarning) + warnings.warn(depdoc, DeprecationWarning, stacklevel=2) return func(*args, **kwds) newfunc = _set_function_name(newfunc, old_name) @@ -152,7 +152,7 @@ def deprecate(*args, **kwargs): >>> olduint(6) /usr/lib/python2.5/site-packages/numpy/lib/utils.py:114: DeprecationWarning: uint32 is deprecated - warnings.warn(str1, DeprecationWarning) + warnings.warn(str1, DeprecationWarning, stacklevel=2) 6 """ @@ -1016,7 +1016,7 @@ class SafeEval(object): def __init__(self): # 2014-10-15, 1.10 warnings.warn("SafeEval is deprecated in 1.10 and will be removed.", - DeprecationWarning) + DeprecationWarning, stacklevel=2) def visit(self, node): cls = node.__class__ diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py index db50a5eec628..a05b0ee8e7bc 100644 --- a/numpy/linalg/linalg.py +++ b/numpy/linalg/linalg.py @@ -737,12 +737,12 @@ def qr(a, mode='reduced'): msg = "".join(( "The 'full' option is deprecated in favor of 'reduced'.\n", "For backward compatibility let mode default.")) - warnings.warn(msg, DeprecationWarning) + warnings.warn(msg, DeprecationWarning, stacklevel=2) mode = 'reduced' elif mode in ('e', 'economic'): # 2013-04-01, 1.8 msg = "The 'economic' option is deprecated.", - warnings.warn(msg, DeprecationWarning) + warnings.warn(msg, DeprecationWarning, stacklevel=2) mode = 'economic' else: raise ValueError("Unrecognized mode '%s'" % mode) diff --git a/numpy/ma/core.py b/numpy/ma/core.py index b1e48c156708..1bf41b3d812e 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -3210,7 +3210,8 @@ def __getitem__(self, indx): "of fill_value at 0. Discarding " "heterogeneous fill_value and setting " "all to {fv!s}.".format(indx=indx, - fv=dout._fill_value[0])) + fv=dout._fill_value[0]), + stacklevel=2) dout._fill_value = dout._fill_value.flat[0] dout._isfield = True # Update the mask if needed @@ -4195,7 +4196,7 @@ def __float__(self): raise TypeError("Only length-1 arrays can be converted " "to Python scalars") elif self._mask: - warnings.warn("Warning: converting a masked element to nan.") + warnings.warn("Warning: converting a masked element to nan.", stacklevel=2) return np.nan return float(self.item()) @@ -6909,7 +6910,7 @@ def rank(obj): # 2015-04-12, 1.10.0 warnings.warn( "`rank` is deprecated; use the `ndim` function instead. ", - np.VisibleDeprecationWarning) + np.VisibleDeprecationWarning, stacklevel=2) return np.ndim(getdata(obj)) rank.__doc__ = np.rank.__doc__ diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py index aefdd4e49a47..a05ea476b9de 100644 --- a/numpy/ma/extras.py +++ b/numpy/ma/extras.py @@ -1344,7 +1344,7 @@ def corrcoef(x, y=None, rowvar=True, bias=np._NoValue, allow_masked=True, msg = 'bias and ddof have no effect and are deprecated' if bias is not np._NoValue or ddof is not np._NoValue: # 2015-03-15, 1.10 - warnings.warn(msg, DeprecationWarning) + warnings.warn(msg, DeprecationWarning, stacklevel=2) # Get the data (x, xnotmask, rowvar) = _covhelper(x, y, rowvar, allow_masked) # Compute the covariance matrix diff --git a/numpy/ma/mrecords.py b/numpy/ma/mrecords.py index 6c9a4ea73770..6b7acf469a70 100644 --- a/numpy/ma/mrecords.py +++ b/numpy/ma/mrecords.py @@ -743,7 +743,7 @@ def fromtextfile(fname, delimitor=None, commentchar='#', missingchar='', if len(vartypes) != nfields: msg = "Attempting to %i dtypes for %i fields!" msg += " Reverting to default." - warnings.warn(msg % (len(vartypes), nfields)) + warnings.warn(msg % (len(vartypes), nfields), stacklevel=2) vartypes = _guessvartypes(_variables[0]) # Construct the descriptor. diff --git a/numpy/polynomial/chebyshev.py b/numpy/polynomial/chebyshev.py index 2537bea32d43..dd346fbda155 100644 --- a/numpy/polynomial/chebyshev.py +++ b/numpy/polynomial/chebyshev.py @@ -1782,7 +1782,7 @@ def chebfit(x, y, deg, rcond=None, full=False, w=None): # warn on rank reduction if rank != order and not full: msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning) + warnings.warn(msg, pu.RankWarning, stacklevel=2) if full: return c, [resids, rank, s, rcond] diff --git a/numpy/polynomial/hermite.py b/numpy/polynomial/hermite.py index e234c8e2319c..06d68668f700 100644 --- a/numpy/polynomial/hermite.py +++ b/numpy/polynomial/hermite.py @@ -1558,7 +1558,7 @@ def hermfit(x, y, deg, rcond=None, full=False, w=None): # warn on rank reduction if rank != order and not full: msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning) + warnings.warn(msg, pu.RankWarning, stacklevel=2) if full: return c, [resids, rank, s, rcond] diff --git a/numpy/polynomial/hermite_e.py b/numpy/polynomial/hermite_e.py index 8fdcbf6662b7..a846b167c0a7 100644 --- a/numpy/polynomial/hermite_e.py +++ b/numpy/polynomial/hermite_e.py @@ -1555,7 +1555,7 @@ def hermefit(x, y, deg, rcond=None, full=False, w=None): # warn on rank reduction if rank != order and not full: msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning) + warnings.warn(msg, pu.RankWarning, stacklevel=2) if full: return c, [resids, rank, s, rcond] diff --git a/numpy/polynomial/laguerre.py b/numpy/polynomial/laguerre.py index d459551ae894..60728c5f997c 100644 --- a/numpy/polynomial/laguerre.py +++ b/numpy/polynomial/laguerre.py @@ -1557,7 +1557,7 @@ def lagfit(x, y, deg, rcond=None, full=False, w=None): # warn on rank reduction if rank != order and not full: msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning) + warnings.warn(msg, pu.RankWarning, stacklevel=2) if full: return c, [resids, rank, s, rcond] diff --git a/numpy/polynomial/legendre.py b/numpy/polynomial/legendre.py index 54e9895db95f..ec6c2f8bf5c7 100644 --- a/numpy/polynomial/legendre.py +++ b/numpy/polynomial/legendre.py @@ -1585,7 +1585,7 @@ def legfit(x, y, deg, rcond=None, full=False, w=None): # warn on rank reduction if rank != order and not full: msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning) + warnings.warn(msg, pu.RankWarning, stacklevel=2) if full: return c, [resids, rank, s, rcond] diff --git a/numpy/polynomial/polynomial.py b/numpy/polynomial/polynomial.py index c310d659d77d..3ed6b67a474f 100644 --- a/numpy/polynomial/polynomial.py +++ b/numpy/polynomial/polynomial.py @@ -1493,7 +1493,7 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None): # warn on rank reduction if rank != order and not full: msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning) + warnings.warn(msg, pu.RankWarning, stacklevel=2) if full: return c, [resids, rank, s, rcond] diff --git a/pavement.py b/pavement.py index d02cc48c5437..015e802ef7dc 100644 --- a/pavement.py +++ b/pavement.py @@ -495,7 +495,7 @@ def dmg(options): user = os.path.join(options.doc.destdir_pdf, "userguide.pdf") if (not os.path.exists(ref)) or (not os.path.exists(user)): import warnings - warnings.warn("Docs need to be built first! Can't find them.") + warnings.warn("Docs need to be built first! Can't find them.", stacklevel=2) # Build the mpkg package call_task("clean") diff --git a/setup.py b/setup.py index 8b8863255896..981746ff941f 100755 --- a/setup.py +++ b/setup.py @@ -329,7 +329,7 @@ def parse_setuppy_commands(): # If we got here, we didn't detect what setup.py command was given import warnings warnings.warn("Unrecognized setuptools command, proceeding with " - "generating Cython sources and expanding templates") + "generating Cython sources and expanding templates", stacklevel=2) return True From 4ec016784b02f69347c4429829b9eb8225ace5ee Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Fri, 29 Jan 2016 01:17:55 +0100 Subject: [PATCH 2/4] TST: Add tests for stacklevel in warnings and "ignore" filters. Enforces that stacklevel is used in warnings.warn and "ignore" is not used in filterwarnings or simplefilter for most of numpy. --- numpy/distutils/command/egg_info.py | 2 +- numpy/ma/tests/test_core.py | 8 +-- numpy/tests/test_warnings.py | 86 +++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 numpy/tests/test_warnings.py diff --git a/numpy/distutils/command/egg_info.py b/numpy/distutils/command/egg_info.py index 972a27df3846..7176f9212e3b 100644 --- a/numpy/distutils/command/egg_info.py +++ b/numpy/distutils/command/egg_info.py @@ -10,7 +10,7 @@ def run(self): import warnings warnings.warn("`build_src` is being run, this may lead to missing " "files in your sdist! See numpy issue gh-7127 for " - "details", UserWarning) + "details", UserWarning, stacklevel=2) # We need to ensure that build_src has been executed in order to give # setuptools' egg_info command real filenames instead of functions which diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index cbab5ad5b042..7cac90628bf0 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -2450,12 +2450,12 @@ def test_inplace_division_scalar_type(self): x /= t(2) assert_equal(x, y) except (DeprecationWarning, TypeError) as e: - warnings.warn(str(e)) + warnings.warn(str(e), stacklevel=1) try: xm /= t(2) assert_equal(xm, y) except (DeprecationWarning, TypeError) as e: - warnings.warn(str(e)) + warnings.warn(str(e), stacklevel=1) if issubclass(t, np.integer): assert_equal(len(sup.log), 2, "Failed on type=%s." % t) @@ -2485,7 +2485,7 @@ def test_inplace_division_array_type(self): x /= a assert_equal(x, y / a) except (DeprecationWarning, TypeError) as e: - warnings.warn(str(e)) + warnings.warn(str(e), stacklevel=1) try: xm /= a assert_equal(xm, y / a) @@ -2494,7 +2494,7 @@ def test_inplace_division_array_type(self): mask_or(mask_or(m, a.mask), (a == t(0))) ) except (DeprecationWarning, TypeError) as e: - warnings.warn(str(e)) + warnings.warn(str(e), stacklevel=1) if issubclass(t, np.integer): assert_equal(len(sup.log), 2, "Failed on type=%s." % t) diff --git a/numpy/tests/test_warnings.py b/numpy/tests/test_warnings.py new file mode 100644 index 000000000000..dcae60a81a07 --- /dev/null +++ b/numpy/tests/test_warnings.py @@ -0,0 +1,86 @@ +""" +Tests which scan for certain occurances in the code, they may not find +all of these occurances but should catch almost all. +""" + + +from __future__ import division, absolute_import, print_function + + +import sys +if sys.version_info >= (3, 4): + from pathlib import Path + import ast + import tokenize + import numpy + from numpy.testing import run_module_suite + from numpy.testing.decorators import slow + + + class ParseCall(ast.NodeVisitor): + def __init__(self): + self.ls = [] + + def visit_Attribute(self, node): + ast.NodeVisitor.generic_visit(self, node) + self.ls.append(node.attr) + + def visit_Name(self, node): + self.ls.append(node.id) + + + class FindFuncs(ast.NodeVisitor): + def __init__(self, filename): + super().__init__() + self.__filename = filename + + def visit_Call(self, node): + p = ParseCall() + p.visit(node.func) + ast.NodeVisitor.generic_visit(self, node) + + if p.ls[-1] == 'simplefilter' or p.ls[-1] == 'filterwarnings': + if node.args[0].s == "ignore": + raise AssertionError( + "ignore filter should not be used; found in " + "{} on line {}".format(self.__filename, node.lineno)) + + if p.ls[-1] == 'warn' and ( + len(p.ls) == 1 or p.ls[-2] == 'warnings'): + + if "testing/tests/test_warnings.py" is self.__filename: + # This file + return + + # See if stacklevel exists: + if len(node.args) == 3: + return + args = {kw.arg for kw in node.keywords} + if "stacklevel" in args: + return + raise AssertionError( + "warnings should have an appropriate stacklevel; found in " + "{} on line {}".format(self.__filename, node.lineno)) + + + @slow + def test_warning_calls(): + # combined "ignore" and stacklevel error + base = Path(numpy.__file__).parent + + for path in base.rglob("*.py"): + if base / "testing" in path.parents: + continue + if path == base / "__init__.py": + continue + if path == base / "random" / "__init__.py": + continue + # use tokenize to auto-detect encoding on systems where no + # default encoding is defined (e.g. LANG='C') + with tokenize.open(str(path)) as file: + tree = ast.parse(file.read()) + FindFuncs(path).visit(tree) + + + if __name__ == "__main__": + run_module_suite() From 2dc070d3b7c1aa5d6e0b3509078c358192627132 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Sat, 30 Jan 2016 12:06:17 +0100 Subject: [PATCH 3/4] TST: Make one 3.5 test run the full test suit. --- .travis.yml | 2 +- tools/travis-test.sh | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fee1e72ee262..7d503b8c575c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,7 +61,7 @@ matrix: - python: 2.7 env: USE_WHEEL=1 - python: 3.5 - env: USE_WHEEL=1 + env: USE_WHEEL=1 RUN_FULL_TESTS=1 - python: 3.5 env: USE_SDIST=1 - python: 2.7 diff --git a/tools/travis-test.sh b/tools/travis-test.sh index 6ed51ee910e9..be9830e6ed5e 100755 --- a/tools/travis-test.sh +++ b/tools/travis-test.sh @@ -117,7 +117,11 @@ run_test() INSTALLDIR=$($PYTHON -c \ "import os; import numpy; print(os.path.dirname(numpy.__file__))") export PYTHONWARNINGS=default - $PYTHON ../tools/test-installed-numpy.py + if [ -n "$RUN_FULL_TESTS" ]; then + $PYTHON ../tools/test-installed-numpy.py --mode=full + else + $PYTHON ../tools/test-installed-numpy.py + fi if [ -n "$USE_ASV" ]; then pushd ../benchmarks $PYTHON `which asv` machine --machine travis From 68ea0c792db6bac435752e45d8681f7f1283453d Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Thu, 1 Sep 2016 10:20:05 +0200 Subject: [PATCH 4/4] DOC: Add warning stacklevel increase to the release notes --- doc/release/1.12.0-notes.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/release/1.12.0-notes.rst b/doc/release/1.12.0-notes.rst index ef8131e898da..f7f3b4e555ec 100644 --- a/doc/release/1.12.0-notes.rst +++ b/doc/release/1.12.0-notes.rst @@ -319,6 +319,19 @@ is regular quicksort but changing to a heapsort when not enough progress is made. This retains the good quicksort performance while changing the worst case runtime from ``O(N^2)`` to ``O(N*log(N))``. +stacklevel of warnings increased +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The stacklevel for python based warnings was increased so that most warnings +will report the offending line of the user code instead of the line the +warning itself is given. Passing of stacklevel is now tested to ensure that +new warnings will recieve the ``stacklevel`` argument. + +This causes warnings with the "default" or "module" filter to be shown once +for every offending user code line or user module instead of only once. On +python versions before 3.4, this can cause warnings to appear that were falsly +ignored before, which may be surprising especially in test suits. + + Deprecations ============