Skip to content

Commit 0a5e747

Browse files
gfyoungjreback
authored andcommitted
REF/TST: Add more pytest idiom to util/test_util (#24141)
Also breaks up test_util into multiple test modules by the function or method tested.
1 parent 809c0f4 commit 0a5e747

8 files changed

+587
-505
lines changed
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
4+
from pandas.util._decorators import deprecate_kwarg
5+
6+
import pandas.util.testing as tm
7+
8+
9+
@deprecate_kwarg("old", "new")
10+
def _f1(new=False):
11+
return new
12+
13+
14+
_f2_mappings = {"yes": True, "no": False}
15+
16+
17+
@deprecate_kwarg("old", "new", _f2_mappings)
18+
def _f2(new=False):
19+
return new
20+
21+
22+
def _f3_mapping(x):
23+
return x + 1
24+
25+
26+
@deprecate_kwarg("old", "new", _f3_mapping)
27+
def _f3(new=0):
28+
return new
29+
30+
31+
@pytest.mark.parametrize("key,klass", [
32+
("old", FutureWarning),
33+
("new", None)
34+
])
35+
def test_deprecate_kwarg(key, klass):
36+
x = 78
37+
38+
with tm.assert_produces_warning(klass):
39+
assert _f1(**{key: x}) == x
40+
41+
42+
@pytest.mark.parametrize("key", list(_f2_mappings.keys()))
43+
def test_dict_deprecate_kwarg(key):
44+
with tm.assert_produces_warning(FutureWarning):
45+
assert _f2(old=key) == _f2_mappings[key]
46+
47+
48+
@pytest.mark.parametrize("key", ["bogus", 12345, -1.23])
49+
def test_missing_deprecate_kwarg(key):
50+
with tm.assert_produces_warning(FutureWarning):
51+
assert _f2(old=key) == key
52+
53+
54+
@pytest.mark.parametrize("x", [1, -1.4, 0])
55+
def test_callable_deprecate_kwarg(x):
56+
with tm.assert_produces_warning(FutureWarning):
57+
assert _f3(old=x) == _f3_mapping(x)
58+
59+
60+
def test_callable_deprecate_kwarg_fail():
61+
msg = "((can only|cannot) concatenate)|(must be str)|(Can't convert)"
62+
63+
with pytest.raises(TypeError, match=msg):
64+
_f3(old="hello")
65+
66+
67+
def test_bad_deprecate_kwarg():
68+
msg = "mapping from old to new argument values must be dict or callable!"
69+
70+
with pytest.raises(TypeError, match=msg):
71+
@deprecate_kwarg("old", "new", 0)
72+
def f4(new=None):
73+
return new
74+
75+
76+
@deprecate_kwarg("old", None)
77+
def _f4(old=True, unchanged=True):
78+
return old, unchanged
79+
80+
81+
@pytest.mark.parametrize("key", ["old", "unchanged"])
82+
def test_deprecate_keyword(key):
83+
x = 9
84+
85+
if key == "old":
86+
klass = FutureWarning
87+
expected = (x, True)
88+
else:
89+
klass = None
90+
expected = (True, x)
91+
92+
with tm.assert_produces_warning(klass):
93+
assert _f4(**{key: x}) == expected

pandas/tests/util/test_locale.py

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# -*- coding: utf-8 -*-
2+
import codecs
3+
import locale
4+
import os
5+
6+
import pytest
7+
8+
from pandas.compat import is_platform_windows
9+
10+
import pandas.core.common as com
11+
import pandas.util.testing as tm
12+
13+
_all_locales = tm.get_locales() or []
14+
_current_locale = locale.getlocale()
15+
16+
# Don't run any of these tests if we are on Windows or have no locales.
17+
pytestmark = pytest.mark.skipif(is_platform_windows() or not _all_locales,
18+
reason="Need non-Windows and locales")
19+
20+
_skip_if_only_one_locale = pytest.mark.skipif(
21+
len(_all_locales) <= 1, reason="Need multiple locales for meaningful test")
22+
23+
24+
def test_can_set_locale_valid_set():
25+
# Can set the default locale.
26+
assert tm.can_set_locale("")
27+
28+
29+
def test_can_set_locale_invalid_set():
30+
# Cannot set an invalid locale.
31+
assert not tm.can_set_locale("non-existent_locale")
32+
33+
34+
def test_can_set_locale_invalid_get(monkeypatch):
35+
# see gh-22129
36+
#
37+
# In some cases, an invalid locale can be set,
38+
# but a subsequent getlocale() raises a ValueError.
39+
40+
def mock_get_locale():
41+
raise ValueError()
42+
43+
with monkeypatch.context() as m:
44+
m.setattr(locale, "getlocale", mock_get_locale)
45+
assert not tm.can_set_locale("")
46+
47+
48+
def test_get_locales_at_least_one():
49+
# see gh-9744
50+
assert len(_all_locales) > 0
51+
52+
53+
@_skip_if_only_one_locale
54+
def test_get_locales_prefix():
55+
first_locale = _all_locales[0]
56+
assert len(tm.get_locales(prefix=first_locale[:2])) > 0
57+
58+
59+
@_skip_if_only_one_locale
60+
def test_set_locale():
61+
if com._all_none(_current_locale):
62+
# Not sure why, but on some Travis runs with pytest,
63+
# getlocale() returned (None, None).
64+
pytest.skip("Current locale is not set.")
65+
66+
locale_override = os.environ.get("LOCALE_OVERRIDE", None)
67+
68+
if locale_override is None:
69+
lang, enc = "it_CH", "UTF-8"
70+
elif locale_override == "C":
71+
lang, enc = "en_US", "ascii"
72+
else:
73+
lang, enc = locale_override.split(".")
74+
75+
enc = codecs.lookup(enc).name
76+
new_locale = lang, enc
77+
78+
if not tm.can_set_locale(new_locale):
79+
msg = "unsupported locale setting"
80+
81+
with pytest.raises(locale.Error, match=msg):
82+
with tm.set_locale(new_locale):
83+
pass
84+
else:
85+
with tm.set_locale(new_locale) as normalized_locale:
86+
new_lang, new_enc = normalized_locale.split(".")
87+
new_enc = codecs.lookup(enc).name
88+
89+
normalized_locale = new_lang, new_enc
90+
assert normalized_locale == new_locale
91+
92+
# Once we exit the "with" statement, locale should be back to what it was.
93+
current_locale = locale.getlocale()
94+
assert current_locale == _current_locale

pandas/tests/util/test_move.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# -*- coding: utf-8 -*-
2+
import sys
3+
from uuid import uuid4
4+
5+
import pytest
6+
7+
from pandas.compat import PY3, intern
8+
from pandas.util._move import BadMove, move_into_mutable_buffer, stolenbuf
9+
10+
11+
def test_cannot_create_instance_of_stolen_buffer():
12+
# Stolen buffers need to be created through the smart constructor
13+
# "move_into_mutable_buffer," which has a bunch of checks in it.
14+
15+
msg = "cannot create 'pandas.util._move.stolenbuf' instances"
16+
with pytest.raises(TypeError, match=msg):
17+
stolenbuf()
18+
19+
20+
def test_more_than_one_ref():
21+
# Test case for when we try to use "move_into_mutable_buffer"
22+
# when the object being moved has other references.
23+
24+
b = b"testing"
25+
26+
with pytest.raises(BadMove) as e:
27+
def handle_success(type_, value, tb):
28+
assert value.args[0] is b
29+
return type(e).handle_success(e, type_, value, tb) # super
30+
31+
e.handle_success = handle_success
32+
move_into_mutable_buffer(b)
33+
34+
35+
def test_exactly_one_ref():
36+
# Test case for when the object being moved has exactly one reference.
37+
38+
b = b"testing"
39+
40+
# We need to pass an expression on the stack to ensure that there are
41+
# not extra references hanging around. We cannot rewrite this test as
42+
# buf = b[:-3]
43+
# as_stolen_buf = move_into_mutable_buffer(buf)
44+
# because then we would have more than one reference to buf.
45+
as_stolen_buf = move_into_mutable_buffer(b[:-3])
46+
47+
# Materialize as byte-array to show that it is mutable.
48+
assert bytearray(as_stolen_buf) == b"test"
49+
50+
51+
@pytest.mark.skipif(PY3, reason="bytes objects cannot be interned in PY3")
52+
def test_interned():
53+
salt = uuid4().hex
54+
55+
def make_string():
56+
# We need to actually create a new string so that it has refcount
57+
# one. We use a uuid so that we know the string could not already
58+
# be in the intern table.
59+
return "".join(("testing: ", salt))
60+
61+
# This should work, the string has one reference on the stack.
62+
move_into_mutable_buffer(make_string())
63+
refcount = [None] # nonlocal
64+
65+
def ref_capture(ob):
66+
# Subtract two because those are the references owned by this frame:
67+
# 1. The local variables of this stack frame.
68+
# 2. The python data stack of this stack frame.
69+
refcount[0] = sys.getrefcount(ob) - 2
70+
return ob
71+
72+
with pytest.raises(BadMove, match="testing"):
73+
# If we intern the string, it will still have one reference. Now,
74+
# it is in the intern table, so if other people intern the same
75+
# string while the mutable buffer holds the first string they will
76+
# be the same instance.
77+
move_into_mutable_buffer(ref_capture(intern(make_string()))) # noqa
78+
79+
assert refcount[0] == 1

pandas/tests/util/test_safe_import.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# -*- coding: utf-8 -*-
2+
import sys
3+
import types
4+
5+
import pytest
6+
7+
import pandas.util._test_decorators as td
8+
9+
10+
@pytest.mark.parametrize("name", ["foo", "hello123"])
11+
def test_safe_import_non_existent(name):
12+
assert not td.safe_import(name)
13+
14+
15+
def test_safe_import_exists():
16+
assert td.safe_import("pandas")
17+
18+
19+
@pytest.mark.parametrize("min_version,valid", [
20+
("0.0.0", True),
21+
("99.99.99", False)
22+
])
23+
def test_safe_import_versions(min_version, valid):
24+
result = td.safe_import("pandas", min_version=min_version)
25+
result = result if valid else not result
26+
assert result
27+
28+
29+
@pytest.mark.parametrize("min_version,valid", [
30+
(None, False),
31+
("1.0", True),
32+
("2.0", False)
33+
])
34+
def test_safe_import_dummy(monkeypatch, min_version, valid):
35+
mod_name = "hello123"
36+
37+
mod = types.ModuleType(mod_name)
38+
mod.__version__ = "1.5"
39+
40+
if min_version is not None:
41+
monkeypatch.setitem(sys.modules, mod_name, mod)
42+
43+
result = td.safe_import(mod_name, min_version=min_version)
44+
result = result if valid else not result
45+
assert result

0 commit comments

Comments
 (0)