Skip to content

Commit 0ba7b16

Browse files
TomAugspurgeraeltanawy
authored andcommitted
TST: Fail on warning (pandas-dev#22699)
1 parent d64c0a8 commit 0ba7b16

File tree

106 files changed

+2677
-2298
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+2677
-2298
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ matrix:
6464
# In allow_failures
6565
- dist: trusty
6666
env:
67-
- JOB="3.6, NumPy dev" ENV_FILE="ci/travis-36-numpydev.yaml" TEST_ARGS="--skip-slow --skip-network" PANDAS_TESTING_MODE="deprecate"
67+
- JOB="3.7, NumPy dev" ENV_FILE="ci/travis-37-numpydev.yaml" TEST_ARGS="--skip-slow --skip-network -W error" PANDAS_TESTING_MODE="deprecate"
6868
addons:
6969
apt:
7070
packages:
@@ -79,7 +79,7 @@ matrix:
7979
- JOB="3.6, slow" ENV_FILE="ci/travis-36-slow.yaml" SLOW=true
8080
- dist: trusty
8181
env:
82-
- JOB="3.6, NumPy dev" ENV_FILE="ci/travis-36-numpydev.yaml" TEST_ARGS="--skip-slow --skip-network" PANDAS_TESTING_MODE="deprecate"
82+
- JOB="3.7, NumPy dev" ENV_FILE="ci/travis-37-numpydev.yaml" TEST_ARGS="--skip-slow --skip-network -W error" PANDAS_TESTING_MODE="deprecate"
8383
addons:
8484
apt:
8585
packages:

ci/travis-36-numpydev.yaml renamed to ci/travis-37-numpydev.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: pandas
22
channels:
33
- defaults
44
dependencies:
5-
- python=3.6*
5+
- python=3.7*
66
- pytz
77
- Cython>=0.28.2
88
# universal

doc/source/contributing.rst

+57
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,14 @@ Otherwise, you need to do it manually:
632632
warnings.warn('Use new_func instead.', FutureWarning, stacklevel=2)
633633
new_func()
634634
635+
You'll also need to
636+
637+
1. write a new test that asserts a warning is issued when calling with the deprecated argument
638+
2. Update all of pandas existing tests and code to use the new argument
639+
640+
See :ref:`contributing.warnings` for more.
641+
642+
635643
.. _contributing.ci:
636644

637645
Testing With Continuous Integration
@@ -859,6 +867,55 @@ preferred if the inputs or logic are simple, with Hypothesis tests reserved
859867
for cases with complex logic or where there are too many combinations of
860868
options or subtle interactions to test (or think of!) all of them.
861869

870+
.. _contributing.warnings:
871+
872+
Testing Warnings
873+
~~~~~~~~~~~~~~~~
874+
875+
By default, one of pandas CI workers will fail if any unhandled warnings are emitted.
876+
877+
If your change involves checking that a warning is actually emitted, use
878+
``tm.assert_produces_warning(ExpectedWarning)``.
879+
880+
881+
.. code-block:: python
882+
883+
with tm.assert_prodcues_warning(FutureWarning):
884+
df.some_operation()
885+
886+
We prefer this to the ``pytest.warns`` context manager because ours checks that the warning's
887+
stacklevel is set correctly. The stacklevel is what ensure the *user's* file name and line number
888+
is printed in the warning, rather than something internal to pandas. It represents the number of
889+
function calls from user code (e.g. ``df.some_operation()``) to the function that actually emits
890+
the warning. Our linter will fail the build if you use ``pytest.warns`` in a test.
891+
892+
If you have a test that would emit a warning, but you aren't actually testing the
893+
warning itself (say because it's going to be removed in the future, or because we're
894+
matching a 3rd-party library's behavior), then use ``pytest.mark.filterwarnings`` to
895+
ignore the error.
896+
897+
.. code-block:: python
898+
899+
@pytest.mark.filterwarnings("ignore:msg:category")
900+
def test_thing(self):
901+
...
902+
903+
If the test generates a warning of class ``category`` whose message starts
904+
with ``msg``, the warning will be ignored and the test will pass.
905+
906+
If you need finer-grained control, you can use Python's usual
907+
`warnings module <https://docs.python.org/3/library/warnings.html>`__
908+
to control whether a warning is ignored / raised at different places within
909+
a single test.
910+
911+
.. code-block:: python
912+
913+
with warch.catch_warnings():
914+
warnings.simplefilter("ignore", FutureWarning)
915+
# Or use warnings.filterwarnings(...)
916+
917+
Alternatively, consider breaking up the unit test.
918+
862919

863920
Running the test suite
864921
----------------------

pandas/compat/__init__.py

+12
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import struct
3939
import inspect
4040
from collections import namedtuple
41+
import collections
4142

4243
PY2 = sys.version_info[0] == 2
4344
PY3 = sys.version_info[0] >= 3
@@ -135,6 +136,11 @@ def lfilter(*args, **kwargs):
135136

136137
from importlib import reload
137138
reload = reload
139+
Hashable = collections.abc.Hashable
140+
Iterable = collections.abc.Iterable
141+
Mapping = collections.abc.Mapping
142+
Sequence = collections.abc.Sequence
143+
Sized = collections.abc.Sized
138144

139145
else:
140146
# Python 2
@@ -190,6 +196,12 @@ def get_range_parameters(data):
190196

191197
reload = builtins.reload
192198

199+
Hashable = collections.Hashable
200+
Iterable = collections.Iterable
201+
Mapping = collections.Mapping
202+
Sequence = collections.Sequence
203+
Sized = collections.Sized
204+
193205
if PY2:
194206
def iteritems(obj, **kw):
195207
return obj.iteritems(**kw)

pandas/compat/chainmap_impl.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
from collections import MutableMapping
1+
import sys
2+
3+
PY3 = sys.version_info[0] >= 3
4+
5+
if PY3:
6+
from collections.abc import MutableMapping
7+
else:
8+
from collections import MutableMapping
29

310
try:
411
from thread import get_ident

pandas/core/algorithms.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
intended for public consumption
44
"""
55
from __future__ import division
6-
from warnings import warn, catch_warnings
6+
from warnings import warn, catch_warnings, simplefilter
77
from textwrap import dedent
88

99
import numpy as np
@@ -91,7 +91,8 @@ def _ensure_data(values, dtype=None):
9191

9292
# ignore the fact that we are casting to float
9393
# which discards complex parts
94-
with catch_warnings(record=True):
94+
with catch_warnings():
95+
simplefilter("ignore", np.ComplexWarning)
9596
values = ensure_float64(values)
9697
return values, 'float64', 'float64'
9798

pandas/core/arrays/datetimelike.py

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def cmp_method(self, other):
5959
# numpy will show a DeprecationWarning on invalid elementwise
6060
# comparisons, this will raise in the future
6161
with warnings.catch_warnings(record=True):
62+
warnings.filterwarnings("ignore", "elementwise", FutureWarning)
6263
with np.errstate(all='ignore'):
6364
result = op(self.values, np.asarray(other))
6465

pandas/core/arrays/integer.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from pandas._libs.lib import infer_dtype
77
from pandas.util._decorators import cache_readonly
8-
from pandas.compat import u, range
8+
from pandas.compat import u, range, string_types
99
from pandas.compat import set_function_name
1010

1111
from pandas.core.dtypes.cast import astype_nansafe
@@ -147,6 +147,11 @@ def coerce_to_array(values, dtype, mask=None, copy=False):
147147
dtype = values.dtype
148148

149149
if dtype is not None:
150+
if (isinstance(dtype, string_types) and
151+
(dtype.startswith("Int") or dtype.startswith("UInt"))):
152+
# Avoid DeprecationWarning from NumPy about np.dtype("Int64")
153+
# https://github.com/numpy/numpy/pull/7476
154+
dtype = dtype.lower()
150155
if not issubclass(type(dtype), _IntegerDtype):
151156
try:
152157
dtype = _dtypes[str(np.dtype(dtype))]
@@ -507,7 +512,8 @@ def cmp_method(self, other):
507512

508513
# numpy will show a DeprecationWarning on invalid elementwise
509514
# comparisons, this will raise in the future
510-
with warnings.catch_warnings(record=True):
515+
with warnings.catch_warnings():
516+
warnings.filterwarnings("ignore", "elementwise", FutureWarning)
511517
with np.errstate(all='ignore'):
512518
result = op(self._data, other)
513519

pandas/core/common.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ def standardize_mapping(into):
356356
return partial(
357357
collections.defaultdict, into.default_factory)
358358
into = type(into)
359-
if not issubclass(into, collections.Mapping):
359+
if not issubclass(into, compat.Mapping):
360360
raise TypeError('unsupported type: {into}'.format(into=into))
361361
elif into == collections.defaultdict:
362362
raise TypeError(

pandas/core/computation/eval.py

+1
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ def eval(expr, parser='pandas', engine=None, truediv=True,
323323
# to use a non-numeric indexer
324324
try:
325325
with warnings.catch_warnings(record=True):
326+
# TODO: Filter the warnings we actually care about here.
326327
target[assigner] = ret
327328
except (TypeError, IndexError):
328329
raise ValueError("Cannot assign expression output to target")

pandas/core/dtypes/inference.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
""" basic inference routines """
22

3-
import collections
43
import re
54
import numpy as np
6-
from collections import Iterable
75
from numbers import Number
6+
from pandas import compat
87
from pandas.compat import (PY2, string_types, text_type,
98
string_and_binary_types, re_type)
109
from pandas._libs import lib
@@ -112,7 +111,7 @@ def _iterable_not_string(obj):
112111
False
113112
"""
114113

115-
return (isinstance(obj, collections.Iterable) and
114+
return (isinstance(obj, compat.Iterable) and
116115
not isinstance(obj, string_types))
117116

118117

@@ -284,7 +283,7 @@ def is_list_like(obj):
284283
False
285284
"""
286285

287-
return (isinstance(obj, Iterable) and
286+
return (isinstance(obj, compat.Iterable) and
288287
# we do not count strings/unicode/bytes as list-like
289288
not isinstance(obj, string_and_binary_types) and
290289
# exclude zero-dimensional numpy arrays, effectively scalars

pandas/core/frame.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,9 @@ def __init__(self, data=None, index=None, columns=None, dtype=None,
417417
copy=copy)
418418

419419
# For data is list-like, or Iterable (will consume into list)
420-
elif (isinstance(data, collections.Iterable)
420+
elif (isinstance(data, compat.Iterable)
421421
and not isinstance(data, string_and_binary_types)):
422-
if not isinstance(data, collections.Sequence):
422+
if not isinstance(data, compat.Sequence):
423423
data = list(data)
424424
if len(data) > 0:
425425
if is_list_like(data[0]) and getattr(data[0], 'ndim', 1) == 1:
@@ -7640,7 +7640,7 @@ def _to_arrays(data, columns, coerce_float=False, dtype=None):
76407640
if isinstance(data[0], (list, tuple)):
76417641
return _list_to_arrays(data, columns, coerce_float=coerce_float,
76427642
dtype=dtype)
7643-
elif isinstance(data[0], collections.Mapping):
7643+
elif isinstance(data[0], compat.Mapping):
76447644
return _list_of_dict_to_arrays(data, columns,
76457645
coerce_float=coerce_float, dtype=dtype)
76467646
elif isinstance(data[0], Series):

pandas/core/groupby/generic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@ def aggregate(self, func_or_funcs, *args, **kwargs):
758758
if isinstance(func_or_funcs, compat.string_types):
759759
return getattr(self, func_or_funcs)(*args, **kwargs)
760760

761-
if isinstance(func_or_funcs, collections.Iterable):
761+
if isinstance(func_or_funcs, compat.Iterable):
762762
# Catch instances of lists / tuples
763763
# but not the class list / tuple itself.
764764
ret = self._aggregate_multiple_funcs(func_or_funcs,

pandas/core/indexes/base.py

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ def cmp_method(self, other):
9898
# numpy will show a DeprecationWarning on invalid elementwise
9999
# comparisons, this will raise in the future
100100
with warnings.catch_warnings(record=True):
101+
warnings.filterwarnings("ignore", "elementwise", FutureWarning)
101102
with np.errstate(all='ignore'):
102103
result = op(self.values, np.asarray(other))
103104

pandas/core/internals/blocks.py

+1
Original file line numberDiff line numberDiff line change
@@ -3490,6 +3490,7 @@ def _putmask_smart(v, m, n):
34903490

34913491
# we ignore ComplexWarning here
34923492
with warnings.catch_warnings(record=True):
3493+
warnings.simplefilter("ignore", np.ComplexWarning)
34933494
nn_at = nn.astype(v.dtype)
34943495

34953496
# avoid invalid dtype comparisons

pandas/core/series.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
# pylint: disable=E1101,E1103
77
# pylint: disable=W0703,W0622,W0613,W0201
88

9-
import collections
109
import warnings
1110
from textwrap import dedent
1211

@@ -240,8 +239,8 @@ def __init__(self, data=None, index=None, dtype=None, name=None,
240239
raise TypeError("{0!r} type is unordered"
241240
"".format(data.__class__.__name__))
242241
# If data is Iterable but not list-like, consume into list.
243-
elif (isinstance(data, collections.Iterable)
244-
and not isinstance(data, collections.Sized)):
242+
elif (isinstance(data, compat.Iterable)
243+
and not isinstance(data, compat.Sized)):
245244
data = list(data)
246245
else:
247246

pandas/core/window.py

+2
Original file line numberDiff line numberDiff line change
@@ -2387,11 +2387,13 @@ def dataframe_from_int_dict(data, frame_template):
23872387
if not arg2.columns.is_unique:
23882388
raise ValueError("'arg2' columns are not unique")
23892389
with warnings.catch_warnings(record=True):
2390+
warnings.simplefilter("ignore", RuntimeWarning)
23902391
X, Y = arg1.align(arg2, join='outer')
23912392
X = X + 0 * Y
23922393
Y = Y + 0 * X
23932394

23942395
with warnings.catch_warnings(record=True):
2396+
warnings.simplefilter("ignore", RuntimeWarning)
23952397
res_columns = arg1.columns.union(arg2.columns)
23962398
for col in res_columns:
23972399
if col in X and col in Y:

pandas/io/common.py

+2
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,8 @@ def _get_handle(path_or_buf, mode, encoding=None, compression=None,
386386
# ZIP Compression
387387
elif compression == 'zip':
388388
zf = BytesZipFile(path_or_buf, mode)
389+
# Ensure the container is closed as well.
390+
handles.append(zf)
389391
if zf.mode == 'w':
390392
f = zf
391393
elif zf.mode == 'r':

pandas/io/html.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
import os
77
import re
88
import numbers
9-
import collections
109

1110
from distutils.version import LooseVersion
1211

1312
from pandas.core.dtypes.common import is_list_like
1413
from pandas.errors import EmptyDataError
1514
from pandas.io.common import _is_url, urlopen, _validate_header_arg
1615
from pandas.io.parsers import TextParser
16+
from pandas import compat
1717
from pandas.compat import (lrange, lmap, u, string_types, iteritems,
1818
raise_with_traceback, binary_type)
1919
from pandas import Series
@@ -859,7 +859,7 @@ def _validate_flavor(flavor):
859859
flavor = 'lxml', 'bs4'
860860
elif isinstance(flavor, string_types):
861861
flavor = flavor,
862-
elif isinstance(flavor, collections.Iterable):
862+
elif isinstance(flavor, compat.Iterable):
863863
if not all(isinstance(flav, string_types) for flav in flavor):
864864
raise TypeError('Object of type {typ!r} is not an iterable of '
865865
'strings'

pandas/io/pickle.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ def try_read(path, encoding=None):
160160
# GH 6899
161161
try:
162162
with warnings.catch_warnings(record=True):
163-
# We want to silencce any warnings about, e.g. moved modules.
163+
# We want to silence any warnings about, e.g. moved modules.
164+
warnings.simplefilter("ignore", Warning)
164165
return read_wrapper(lambda f: pkl.load(f))
165166
except Exception:
166167
# reg/patched pickle

0 commit comments

Comments
 (0)