Skip to content

Commit 2d18f12

Browse files
h-vetinariPingviinituutti
authored andcommitted
TST: move .str-test to strings.py & parametrize it; precursor to pandas-dev#23582 (pandas-dev#23777)
1 parent 08f4e8f commit 2d18f12

File tree

2 files changed

+112
-76
lines changed

2 files changed

+112
-76
lines changed

pandas/tests/series/test_api.py

-76
Original file line numberDiff line numberDiff line change
@@ -602,82 +602,6 @@ def f():
602602
ordered=True))
603603
tm.assert_series_equal(result, expected)
604604

605-
def test_str_accessor_api_for_categorical(self):
606-
# https://github.com/pandas-dev/pandas/issues/10661
607-
from pandas.core.strings import StringMethods
608-
s = Series(list('aabb'))
609-
s = s + " " + s
610-
c = s.astype('category')
611-
assert isinstance(c.str, StringMethods)
612-
613-
# str functions, which need special arguments
614-
special_func_defs = [
615-
('cat', (list("zyxw"),), {"sep": ","}),
616-
('center', (10,), {}),
617-
('contains', ("a",), {}),
618-
('count', ("a",), {}),
619-
('decode', ("UTF-8",), {}),
620-
('encode', ("UTF-8",), {}),
621-
('endswith', ("a",), {}),
622-
('extract', ("([a-z]*) ",), {"expand": False}),
623-
('extract', ("([a-z]*) ",), {"expand": True}),
624-
('extractall', ("([a-z]*) ",), {}),
625-
('find', ("a",), {}),
626-
('findall', ("a",), {}),
627-
('index', (" ",), {}),
628-
('ljust', (10,), {}),
629-
('match', ("a"), {}), # deprecated...
630-
('normalize', ("NFC",), {}),
631-
('pad', (10,), {}),
632-
('partition', (" ",), {"expand": False}), # not default
633-
('partition', (" ",), {"expand": True}), # default
634-
('repeat', (3,), {}),
635-
('replace', ("a", "z"), {}),
636-
('rfind', ("a",), {}),
637-
('rindex', (" ",), {}),
638-
('rjust', (10,), {}),
639-
('rpartition', (" ",), {"expand": False}), # not default
640-
('rpartition', (" ",), {"expand": True}), # default
641-
('slice', (0, 1), {}),
642-
('slice_replace', (0, 1, "z"), {}),
643-
('split', (" ",), {"expand": False}), # default
644-
('split', (" ",), {"expand": True}), # not default
645-
('startswith', ("a",), {}),
646-
('wrap', (2,), {}),
647-
('zfill', (10,), {})
648-
]
649-
_special_func_names = [f[0] for f in special_func_defs]
650-
651-
# * get, join: they need a individual elements of type lists, but
652-
# we can't make a categorical with lists as individual categories.
653-
# -> `s.str.split(" ").astype("category")` will error!
654-
# * `translate` has different interfaces for py2 vs. py3
655-
_ignore_names = ["get", "join", "translate"]
656-
657-
str_func_names = [f for f in dir(s.str) if not (
658-
f.startswith("_") or
659-
f in _special_func_names or
660-
f in _ignore_names)]
661-
662-
func_defs = [(f, (), {}) for f in str_func_names]
663-
func_defs.extend(special_func_defs)
664-
665-
for func, args, kwargs in func_defs:
666-
res = getattr(c.str, func)(*args, **kwargs)
667-
exp = getattr(s.str, func)(*args, **kwargs)
668-
669-
if isinstance(res, DataFrame):
670-
tm.assert_frame_equal(res, exp)
671-
else:
672-
tm.assert_series_equal(res, exp)
673-
674-
invalid = Series([1, 2, 3]).astype('category')
675-
msg = "Can only use .str accessor with string"
676-
677-
with pytest.raises(AttributeError, match=msg):
678-
invalid.str
679-
assert not hasattr(invalid, 'str')
680-
681605
def test_dt_accessor_api_for_categorical(self):
682606
# https://github.com/pandas-dev/pandas/issues/10661
683607
from pandas.core.indexes.accessors import Properties

pandas/tests/test_strings.py

+112
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,98 @@ def assert_series_or_index_equal(left, right):
2626
assert_index_equal(left, right)
2727

2828

29+
_any_string_method = [
30+
('cat', (), {'sep': ','}), # noqa: E241
31+
('cat', (Series(list('zyx')),), {'sep': ',', # noqa: E241
32+
'join': 'left'}),
33+
('center', (10,), {}), # noqa: E241
34+
('contains', ('a',), {}), # noqa: E241
35+
('count', ('a',), {}), # noqa: E241
36+
('decode', ('UTF-8',), {}), # noqa: E241
37+
('encode', ('UTF-8',), {}), # noqa: E241
38+
('endswith', ('a',), {}), # noqa: E241
39+
('extract', ('([a-z]*)',), {'expand': False}), # noqa: E241
40+
('extract', ('([a-z]*)',), {'expand': True}), # noqa: E241
41+
('extractall', ('([a-z]*)',), {}), # noqa: E241
42+
('find', ('a',), {}), # noqa: E241
43+
('findall', ('a',), {}), # noqa: E241
44+
('get', (0,), {}), # noqa: E241
45+
# because "index" (and "rindex") fail intentionally
46+
# if the string is not found, search only for empty string
47+
('index', ('',), {}), # noqa: E241
48+
('join', (',',), {}), # noqa: E241
49+
('ljust', (10,), {}), # noqa: E241
50+
('match', ('a',), {}), # noqa: E241
51+
('normalize', ('NFC',), {}), # noqa: E241
52+
('pad', (10,), {}), # noqa: E241
53+
('partition', (' ',), {'expand': False}), # noqa: E241
54+
('partition', (' ',), {'expand': True}), # noqa: E241
55+
('repeat', (3,), {}), # noqa: E241
56+
('replace', ('a', 'z',), {}), # noqa: E241
57+
('rfind', ('a',), {}), # noqa: E241
58+
('rindex', ('',), {}), # noqa: E241
59+
('rjust', (10,), {}), # noqa: E241
60+
('rpartition', (' ',), {'expand': False}), # noqa: E241
61+
('rpartition', (' ',), {'expand': True}), # noqa: E241
62+
('slice', (0, 1,), {}), # noqa: E241
63+
('slice_replace', (0, 1, 'z',), {}), # noqa: E241
64+
('split', (' ',), {'expand': False}), # noqa: E241
65+
('split', (' ',), {'expand': True}), # noqa: E241
66+
('startswith', ('a',), {}), # noqa: E241
67+
# translating unicode points of "a" to "d"
68+
('translate', ({97: 100},), {}), # noqa: E241
69+
('wrap', (2,), {}), # noqa: E241
70+
('zfill', (10,), {}) # noqa: E241
71+
] + list(zip([
72+
# methods without positional arguments: zip with empty tuple and empty dict
73+
'capitalize', 'cat', 'get_dummies',
74+
'isalnum', 'isalpha', 'isdecimal',
75+
'isdigit', 'islower', 'isnumeric',
76+
'isspace', 'istitle', 'isupper',
77+
'len', 'lower', 'lstrip', 'partition',
78+
'rpartition', 'rsplit', 'rstrip',
79+
'slice', 'slice_replace', 'split',
80+
'strip', 'swapcase', 'title', 'upper'
81+
], [()] * 100, [{}] * 100))
82+
ids, _, _ = zip(*_any_string_method) # use method name as fixture-id
83+
84+
85+
# test that the above list captures all methods of StringMethods
86+
missing_methods = {f for f in dir(strings.StringMethods)
87+
if not f.startswith('_')} - set(ids)
88+
assert not missing_methods
89+
90+
91+
@pytest.fixture(params=_any_string_method, ids=ids)
92+
def any_string_method(request):
93+
"""
94+
Fixture for all public methods of `StringMethods`
95+
96+
This fixture returns a tuple of the method name and sample arguments
97+
necessary to call the method.
98+
99+
Returns
100+
-------
101+
method_name : str
102+
The name of the method in `StringMethods`
103+
args : tuple
104+
Sample values for the positional arguments
105+
kwargs : dict
106+
Sample values for the keyword arguments
107+
108+
Examples
109+
--------
110+
>>> def test_something(any_string_method):
111+
... s = pd.Series(['a', 'b', np.nan, 'd'])
112+
...
113+
... method_name, args, kwargs = any_string_method
114+
... method = getattr(s.str, method_name)
115+
... # will not raise
116+
... method(*args, **kwargs)
117+
"""
118+
return request.param
119+
120+
29121
class TestStringMethods(object):
30122

31123
def test_api(self):
@@ -40,6 +132,26 @@ def test_api(self):
40132
invalid.str
41133
assert not hasattr(invalid, 'str')
42134

135+
def test_api_for_categorical(self, any_string_method):
136+
# https://github.com/pandas-dev/pandas/issues/10661
137+
s = Series(list('aabb'))
138+
s = s + " " + s
139+
c = s.astype('category')
140+
assert isinstance(c.str, strings.StringMethods)
141+
142+
method_name, args, kwargs = any_string_method
143+
144+
result = getattr(c.str, method_name)(*args, **kwargs)
145+
expected = getattr(s.str, method_name)(*args, **kwargs)
146+
147+
if isinstance(result, DataFrame):
148+
tm.assert_frame_equal(result, expected)
149+
elif isinstance(result, Series):
150+
tm.assert_series_equal(result, expected)
151+
else:
152+
# str.cat(others=None) returns string, for example
153+
assert result == expected
154+
43155
def test_iter(self):
44156
# GH3638
45157
strs = 'google', 'wikimedia', 'wikipedia', 'wikitravel'

0 commit comments

Comments
 (0)