scikit-base 0.4.6__py3-none-any.whl → 0.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs/source/conf.py +299 -299
- {scikit_base-0.4.6.dist-info → scikit_base-0.5.1.dist-info}/LICENSE +29 -29
- {scikit_base-0.4.6.dist-info → scikit_base-0.5.1.dist-info}/METADATA +160 -159
- scikit_base-0.5.1.dist-info/RECORD +58 -0
- {scikit_base-0.4.6.dist-info → scikit_base-0.5.1.dist-info}/WHEEL +1 -1
- scikit_base-0.5.1.dist-info/top_level.txt +5 -0
- {scikit_base-0.4.6.dist-info → scikit_base-0.5.1.dist-info}/zip-safe +1 -1
- skbase/__init__.py +14 -14
- skbase/_exceptions.py +31 -31
- skbase/_nopytest_tests.py +35 -35
- skbase/base/__init__.py +20 -20
- skbase/base/_base.py +1249 -1249
- skbase/base/_meta.py +883 -871
- skbase/base/_pretty_printing/__init__.py +11 -11
- skbase/base/_pretty_printing/_object_html_repr.py +392 -392
- skbase/base/_pretty_printing/_pprint.py +412 -412
- skbase/base/_tagmanager.py +217 -217
- skbase/lookup/__init__.py +31 -31
- skbase/lookup/_lookup.py +1009 -1009
- skbase/lookup/tests/__init__.py +2 -2
- skbase/lookup/tests/test_lookup.py +991 -991
- skbase/testing/__init__.py +12 -12
- skbase/testing/test_all_objects.py +852 -856
- skbase/testing/utils/__init__.py +5 -5
- skbase/testing/utils/_conditional_fixtures.py +209 -209
- skbase/testing/utils/_dependencies.py +15 -15
- skbase/testing/utils/deep_equals.py +15 -15
- skbase/testing/utils/inspect.py +30 -30
- skbase/testing/utils/tests/__init__.py +2 -2
- skbase/testing/utils/tests/test_check_dependencies.py +49 -49
- skbase/testing/utils/tests/test_deep_equals.py +66 -66
- skbase/tests/__init__.py +2 -2
- skbase/tests/conftest.py +273 -273
- skbase/tests/mock_package/__init__.py +5 -5
- skbase/tests/mock_package/test_mock_package.py +74 -74
- skbase/tests/test_base.py +1202 -1202
- skbase/tests/test_baseestimator.py +130 -130
- skbase/tests/test_exceptions.py +23 -23
- skbase/tests/test_meta.py +170 -131
- skbase/utils/__init__.py +21 -21
- skbase/utils/_check.py +53 -53
- skbase/utils/_iter.py +238 -238
- skbase/utils/_nested_iter.py +180 -180
- skbase/utils/_utils.py +91 -91
- skbase/utils/deep_equals.py +358 -358
- skbase/utils/dependencies/__init__.py +11 -11
- skbase/utils/dependencies/_dependencies.py +253 -253
- skbase/utils/tests/__init__.py +4 -4
- skbase/utils/tests/test_check.py +24 -24
- skbase/utils/tests/test_iter.py +127 -127
- skbase/utils/tests/test_nested_iter.py +84 -84
- skbase/utils/tests/test_utils.py +37 -37
- skbase/validate/__init__.py +22 -22
- skbase/validate/_named_objects.py +403 -403
- skbase/validate/_types.py +345 -345
- skbase/validate/tests/__init__.py +2 -2
- skbase/validate/tests/test_iterable_named_objects.py +200 -200
- skbase/validate/tests/test_type_validations.py +370 -370
- scikit_base-0.4.6.dist-info/RECORD +0 -58
- scikit_base-0.4.6.dist-info/top_level.txt +0 -2
@@ -1,370 +1,370 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# copyright: skbase developers, BSD-3-Clause License (see LICENSE file)
|
3
|
-
"""Tests of the functionality for validating iterables of named objects.
|
4
|
-
|
5
|
-
tests in this module include:
|
6
|
-
|
7
|
-
- test_check_type_output
|
8
|
-
- test_check_type_raises_error_if_expected_type_is_wrong_format
|
9
|
-
- test_is_sequence_output
|
10
|
-
- test_is_sequence_with_seq_of_class_and_instance_input
|
11
|
-
- test_check_sequence_output
|
12
|
-
- test_check_sequence_scalar_input_coercion
|
13
|
-
- test_check_sequence_with_seq_of_class_and_instance_input
|
14
|
-
- test_convert_scalar_seq_type_input_to_tuple_raises_error
|
15
|
-
"""
|
16
|
-
import collections
|
17
|
-
|
18
|
-
import numpy as np
|
19
|
-
import pytest
|
20
|
-
|
21
|
-
from skbase.base import BaseEstimator, BaseObject
|
22
|
-
from skbase.validate import check_sequence, check_type, is_sequence
|
23
|
-
from skbase.validate._types import _convert_scalar_seq_type_input_to_tuple
|
24
|
-
|
25
|
-
__author__ = ["RNKuhns"]
|
26
|
-
|
27
|
-
|
28
|
-
@pytest.fixture
|
29
|
-
def fixture_object_instance():
|
30
|
-
"""Pytest fixture of BaseObject instance."""
|
31
|
-
return BaseObject()
|
32
|
-
|
33
|
-
|
34
|
-
@pytest.fixture
|
35
|
-
def fixture_estimator_instance():
|
36
|
-
"""Pytest fixture of BaseObject instance."""
|
37
|
-
return BaseEstimator()
|
38
|
-
|
39
|
-
|
40
|
-
def test_check_type_output(fixture_estimator_instance, fixture_object_instance):
|
41
|
-
"""Test check type returns expected output."""
|
42
|
-
assert check_type(7, expected_type=int) == 7
|
43
|
-
assert check_type(7.2, expected_type=float) == 7.2
|
44
|
-
assert check_type(7.2, expected_type=(float, int)) == 7.2
|
45
|
-
assert check_type("something", expected_type=str) == "something"
|
46
|
-
assert check_type(None, expected_type=str, allow_none=True) is None
|
47
|
-
assert check_type(["a", 7, fixture_object_instance], expected_type=list) == [
|
48
|
-
"a",
|
49
|
-
7,
|
50
|
-
fixture_object_instance,
|
51
|
-
]
|
52
|
-
assert (
|
53
|
-
check_type(fixture_estimator_instance, expected_type=BaseObject)
|
54
|
-
== fixture_estimator_instance
|
55
|
-
)
|
56
|
-
assert check_type(None, expected_type=int, allow_none=True) is None
|
57
|
-
|
58
|
-
with pytest.raises(TypeError, match=r"`input` should be type.*"):
|
59
|
-
check_type(7.2, expected_type=int)
|
60
|
-
|
61
|
-
with pytest.raises(TypeError, match=r"`input` should be type.*"):
|
62
|
-
check_type("something", expected_type=(int, float))
|
63
|
-
|
64
|
-
with pytest.raises(TypeError, match=r"`input` should be type.*"):
|
65
|
-
check_type(BaseEstimator, expected_type=BaseObject)
|
66
|
-
|
67
|
-
with pytest.raises(TypeError, match="^`input` should be.*"):
|
68
|
-
check_type("something", expected_type=int, allow_none=True)
|
69
|
-
|
70
|
-
# Verify optional use of issubclass instead of isinstance
|
71
|
-
assert (
|
72
|
-
check_type(BaseEstimator, expected_type=BaseObject, use_subclass=True)
|
73
|
-
== BaseEstimator
|
74
|
-
)
|
75
|
-
|
76
|
-
|
77
|
-
def test_check_type_raises_error_if_expected_type_is_wrong_format():
|
78
|
-
"""Test check_type raises an error if expected_type wrong format.
|
79
|
-
|
80
|
-
`expected_type` should be a type or tuple of types.
|
81
|
-
"""
|
82
|
-
with pytest.raises(TypeError, match="^`expected_type` should be.*"):
|
83
|
-
check_type(7, expected_type=11)
|
84
|
-
|
85
|
-
with pytest.raises(TypeError, match="^`expected_type` should be.*"):
|
86
|
-
check_type(7, expected_type=[int])
|
87
|
-
|
88
|
-
with pytest.raises(TypeError, match="^`expected_type` should be.*"):
|
89
|
-
check_type(None, expected_type=[int])
|
90
|
-
|
91
|
-
|
92
|
-
def test_is_sequence_output():
|
93
|
-
"""Test is_sequence returns expected output.
|
94
|
-
|
95
|
-
This excludes test of class and class instance usage, which is included in
|
96
|
-
test_is_sequence_with_seq_of_class_and_instance_input.
|
97
|
-
"""
|
98
|
-
# Test simple example with no constraints on sequence_type or element_type
|
99
|
-
# True for any sequence
|
100
|
-
assert is_sequence([1, 2, 3]) is True
|
101
|
-
# But false for generators, since they are iterable but not sequences
|
102
|
-
assert is_sequence((c for c in [1, 2, 3])) is False
|
103
|
-
|
104
|
-
# Test use of sequence_type restriction
|
105
|
-
assert is_sequence([1, 2, 3, 4], sequence_type=list) is True
|
106
|
-
assert is_sequence([1, 2, 3, 4], sequence_type=tuple) is False
|
107
|
-
assert is_sequence((1, 2, 3, 4), sequence_type=list) is False
|
108
|
-
assert is_sequence((1, 2, 3, 4), sequence_type=tuple) is True
|
109
|
-
|
110
|
-
# Test use of element_type restriction
|
111
|
-
assert is_sequence([1, 2, 3], element_type=int) is True
|
112
|
-
assert is_sequence([1, 2, 3], element_type=float) is False
|
113
|
-
assert is_sequence([1, 2, 3, 4], sequence_type=list, element_type=int) is True
|
114
|
-
assert is_sequence([1, 2, 3, 4], sequence_type=tuple, element_type=int) is False
|
115
|
-
assert is_sequence([1, 2, 3, 4], sequence_type=list, element_type=float) is False
|
116
|
-
assert is_sequence([1, 2, 3, 4], sequence_type=tuple, element_type=float) is False
|
117
|
-
|
118
|
-
# Tests using different types
|
119
|
-
assert is_sequence("abc") is True # strings are iterable and sequences in Python
|
120
|
-
assert is_sequence([1, "something", 4.5]) is True
|
121
|
-
assert is_sequence([1, "something", 4.5], element_type=float) is False
|
122
|
-
assert (
|
123
|
-
is_sequence(
|
124
|
-
("a string", "or another string"), sequence_type=tuple, element_type=str
|
125
|
-
)
|
126
|
-
is True
|
127
|
-
)
|
128
|
-
|
129
|
-
# Test with 3rd party types works in default way via exact type
|
130
|
-
assert is_sequence([1.2, 4.7], element_type=np.float_) is False
|
131
|
-
assert is_sequence([np.float_(1.2), np.float_(4.7)], element_type=np.float_) is True
|
132
|
-
|
133
|
-
# np.nan is float, not int or np.float_
|
134
|
-
assert is_sequence([np.nan, 4.8], element_type=float) is True
|
135
|
-
assert is_sequence([np.nan, 4], element_type=int) is False
|
136
|
-
|
137
|
-
|
138
|
-
def test_is_sequence_with_seq_of_class_and_instance_input(
|
139
|
-
fixture_estimator_instance, fixture_object_instance
|
140
|
-
):
|
141
|
-
"""Test is_sequence returns expected value with sequence of classes as input."""
|
142
|
-
# Verify we can identify sequences of a given class type as valid sequences
|
143
|
-
input_seq = (fixture_estimator_instance, fixture_object_instance)
|
144
|
-
assert is_sequence(input_seq, element_type=BaseObject) is True
|
145
|
-
assert (
|
146
|
-
is_sequence(list(input_seq), sequence_type=list, element_type=BaseObject)
|
147
|
-
is True
|
148
|
-
)
|
149
|
-
# Verify we detect when list elements are not instances of valid class type
|
150
|
-
assert is_sequence([1, 2, 3], element_type=BaseObject) is False
|
151
|
-
|
152
|
-
# Verify we can identify sequences of class types as valid sequences of types
|
153
|
-
input_seq = (BaseObject, BaseEstimator)
|
154
|
-
assert is_sequence(input_seq, element_type=type) is True
|
155
|
-
assert is_sequence(list(input_seq), sequence_type=list, element_type=type) is True
|
156
|
-
# Verify we detect when list elements are not instances of valid types
|
157
|
-
assert is_sequence([1, 2, 3], element_type=BaseObject) is False
|
158
|
-
|
159
|
-
|
160
|
-
def test_check_sequence_output():
|
161
|
-
"""Test check_sequence returns expected output.
|
162
|
-
|
163
|
-
This excludes test of class and class instance usage, which is included in
|
164
|
-
test_check_sequence_with_seq_of_class_and_instance_input.
|
165
|
-
"""
|
166
|
-
# Test simple example with no constraints on sequence_type or element_type
|
167
|
-
# True for any sequence
|
168
|
-
assert check_sequence([1, 2, 3]) == [1, 2, 3]
|
169
|
-
assert check_sequence([1, "a", 3.4, False]) == [1, "a", 3.4, False]
|
170
|
-
# But false for generators, since they are iterable but not sequences
|
171
|
-
with pytest.raises(
|
172
|
-
TypeError,
|
173
|
-
match="Invalid sequence: Input sequence expected to be a a sequence.",
|
174
|
-
):
|
175
|
-
assert check_sequence((c for c in [1, 2, 3]))
|
176
|
-
|
177
|
-
# Test use of sequence_type restriction
|
178
|
-
assert check_sequence([1, 2, 3, 4], sequence_type=list) == [1, 2, 3, 4]
|
179
|
-
with pytest.raises(
|
180
|
-
TypeError,
|
181
|
-
match="Invalid sequence: Input sequence expected to be a tuple.",
|
182
|
-
):
|
183
|
-
check_sequence([1, 2, 3, 4], sequence_type=tuple)
|
184
|
-
with pytest.raises(
|
185
|
-
TypeError,
|
186
|
-
match="Invalid sequence: Input sequence expected to be a list.",
|
187
|
-
):
|
188
|
-
check_sequence((1, 2, 3, 4), sequence_type=list)
|
189
|
-
assert check_sequence((1, 2, 3, 4), sequence_type=tuple) == (1, 2, 3, 4)
|
190
|
-
assert check_sequence((1, 2, 3, 4), sequence_type=(list, tuple)) == (1, 2, 3, 4)
|
191
|
-
|
192
|
-
# Test use of element_type restriction
|
193
|
-
assert check_sequence([1, 2, 3], element_type=int) == [1, 2, 3]
|
194
|
-
assert check_sequence([1, 2, 3], element_type=(float, int)) == [1, 2, 3]
|
195
|
-
assert check_sequence([1, 2, False, "a", 3], element_type=(bool, str, int)) == [
|
196
|
-
1,
|
197
|
-
2,
|
198
|
-
False,
|
199
|
-
"a",
|
200
|
-
3,
|
201
|
-
]
|
202
|
-
|
203
|
-
with pytest.raises(
|
204
|
-
TypeError,
|
205
|
-
match="Invalid sequence: .*",
|
206
|
-
):
|
207
|
-
check_sequence([1, 2, 3], element_type=float)
|
208
|
-
with pytest.raises(
|
209
|
-
TypeError,
|
210
|
-
match="Invalid sequence: .*",
|
211
|
-
):
|
212
|
-
check_sequence([1, 2, 3, 4], sequence_type=tuple, element_type=int)
|
213
|
-
with pytest.raises(
|
214
|
-
TypeError,
|
215
|
-
match="Invalid sequence: .*",
|
216
|
-
):
|
217
|
-
check_sequence([1, 2, 3, 4], sequence_type=list, element_type=float)
|
218
|
-
with pytest.raises(
|
219
|
-
TypeError,
|
220
|
-
match="Invalid sequence: .*",
|
221
|
-
):
|
222
|
-
check_sequence([1, 2, 3, 4], sequence_type=tuple, element_type=float)
|
223
|
-
|
224
|
-
input_seq = [1, 2, 3, 4]
|
225
|
-
assert check_sequence(input_seq, sequence_type=list, element_type=int) == input_seq
|
226
|
-
|
227
|
-
# Tests using different types
|
228
|
-
# strings are iterable and sequences in Python
|
229
|
-
assert check_sequence("abc") == "abc"
|
230
|
-
assert check_sequence([1, "something", 4.5]) == [1, "something", 4.5]
|
231
|
-
with pytest.raises(
|
232
|
-
TypeError,
|
233
|
-
match="Invalid sequence: .*",
|
234
|
-
):
|
235
|
-
check_sequence([1, "something", 4.5], element_type=float)
|
236
|
-
|
237
|
-
assert check_sequence(
|
238
|
-
("a string", "or another string"), sequence_type=tuple, element_type=str
|
239
|
-
) == ("a string", "or another string")
|
240
|
-
|
241
|
-
# Test with 3rd party types works in default way via exact type
|
242
|
-
with pytest.raises(
|
243
|
-
TypeError,
|
244
|
-
match="Invalid sequence: .*",
|
245
|
-
):
|
246
|
-
check_sequence([1.2, 4.7], element_type=np.float_)
|
247
|
-
input_seq = [np.float_(1.2), np.float_(4.7)]
|
248
|
-
assert check_sequence(input_seq, element_type=np.float_) == input_seq
|
249
|
-
|
250
|
-
# np.nan is float, not int or np.float_
|
251
|
-
assert check_sequence([np.nan, 4.8], element_type=float) == [np.nan, 4.8]
|
252
|
-
assert check_sequence([np.nan, 4.8, 7], element_type=(float, int)) == [
|
253
|
-
np.nan,
|
254
|
-
4.8,
|
255
|
-
7,
|
256
|
-
]
|
257
|
-
with pytest.raises(
|
258
|
-
TypeError,
|
259
|
-
match="Invalid sequence: .*",
|
260
|
-
):
|
261
|
-
check_sequence([np.nan, 4], element_type=int)
|
262
|
-
|
263
|
-
# Check return type coercion to specified sequence type
|
264
|
-
input_seq = [1, 2, 3, 4]
|
265
|
-
assert check_sequence(
|
266
|
-
input_seq, sequence_type=list, element_type=int, coerce_output_type_to=tuple
|
267
|
-
) == tuple(input_seq)
|
268
|
-
|
269
|
-
|
270
|
-
def test_check_sequence_scalar_input_coercion():
|
271
|
-
"""Test check_sequence coerces scalar inputs to sequences as expected."""
|
272
|
-
assert check_sequence(7, element_type=int, coerce_scalar_input=True) == (7,)
|
273
|
-
assert check_sequence(
|
274
|
-
7, element_type=int, coerce_output_type_to=list, coerce_scalar_input=True
|
275
|
-
) == [7]
|
276
|
-
|
277
|
-
# Note that single strings treated as scalars for this purpose
|
278
|
-
assert check_sequence(
|
279
|
-
"some string", element_type=str, coerce_scalar_input=True
|
280
|
-
) == ("some string",)
|
281
|
-
|
282
|
-
# coercion takes into account allowed sequence_types
|
283
|
-
assert check_sequence(
|
284
|
-
7, element_type=int, sequence_type=list, coerce_scalar_input=True
|
285
|
-
) == [7]
|
286
|
-
# If more than one sequence_type allowed then the first is used for coercion
|
287
|
-
assert check_sequence(
|
288
|
-
7, element_type=int, sequence_type=(list, tuple), coerce_scalar_input=True
|
289
|
-
) == [7]
|
290
|
-
# Output type conversion overrides input type coercion to specified sequence_type
|
291
|
-
assert check_sequence(
|
292
|
-
7,
|
293
|
-
element_type=int,
|
294
|
-
sequence_type=list,
|
295
|
-
coerce_output_type_to=tuple,
|
296
|
-
coerce_scalar_input=True,
|
297
|
-
) == (7,)
|
298
|
-
|
299
|
-
# Still raise an error if element type is not expected
|
300
|
-
with pytest.raises(
|
301
|
-
TypeError,
|
302
|
-
match="Invalid sequence: .*",
|
303
|
-
):
|
304
|
-
check_sequence(
|
305
|
-
7,
|
306
|
-
sequence_type=list,
|
307
|
-
element_type=float,
|
308
|
-
coerce_scalar_input=True,
|
309
|
-
)
|
310
|
-
|
311
|
-
|
312
|
-
def test_check_sequence_with_seq_of_class_and_instance_input(
|
313
|
-
fixture_estimator_instance, fixture_object_instance
|
314
|
-
):
|
315
|
-
"""Test check_sequence returns expected value with sequence of classes as input."""
|
316
|
-
# Verify we can identify sequences of a given class type as valid sequences
|
317
|
-
input_seq = (fixture_estimator_instance, fixture_object_instance)
|
318
|
-
assert check_sequence(input_seq, element_type=BaseObject) == input_seq
|
319
|
-
assert check_sequence(
|
320
|
-
list(input_seq), sequence_type=list, element_type=BaseObject
|
321
|
-
) == list(input_seq)
|
322
|
-
|
323
|
-
with pytest.raises(
|
324
|
-
TypeError,
|
325
|
-
match="Invalid sequence: .*",
|
326
|
-
):
|
327
|
-
check_sequence(list(input_seq), sequence_type=tuple, element_type=BaseObject)
|
328
|
-
with pytest.raises(
|
329
|
-
TypeError,
|
330
|
-
match="Invalid sequence: .*",
|
331
|
-
):
|
332
|
-
# Verify we detect when list elements are not instances of valid class type
|
333
|
-
check_sequence([1, 2, 3], element_type=BaseObject)
|
334
|
-
|
335
|
-
# Verify we can identify sequences of class types as valid sequences of types
|
336
|
-
input_seq = (BaseObject, BaseEstimator)
|
337
|
-
assert check_sequence(input_seq, element_type=type) == input_seq
|
338
|
-
assert check_sequence(
|
339
|
-
list(input_seq), sequence_type=list, element_type=type
|
340
|
-
) == list(input_seq)
|
341
|
-
with pytest.raises(
|
342
|
-
TypeError,
|
343
|
-
match="Invalid sequence: .*",
|
344
|
-
):
|
345
|
-
# Verify we detect when list elements are not instances of valid types
|
346
|
-
check_sequence([1, 2, 3], element_type=BaseObject)
|
347
|
-
|
348
|
-
|
349
|
-
def test_convert_scalar_seq_type_input_to_tuple_raises_error():
|
350
|
-
"""Test _convert_scalar_seq_type_input_to_tuple raises error."""
|
351
|
-
# Raises because 7 is not a type
|
352
|
-
with pytest.raises(
|
353
|
-
TypeError, match=r"`type_input` should be a type or tuple of types."
|
354
|
-
):
|
355
|
-
_convert_scalar_seq_type_input_to_tuple(7)
|
356
|
-
|
357
|
-
# Test error message uses input_name
|
358
|
-
with pytest.raises(
|
359
|
-
TypeError, match=r"`some_input` should be a type or tuple of types."
|
360
|
-
):
|
361
|
-
_convert_scalar_seq_type_input_to_tuple(7, input_name="some_input")
|
362
|
-
|
363
|
-
# Raises error because dict is a type but not a subclass of type_input_subclass
|
364
|
-
with pytest.raises(
|
365
|
-
TypeError, match=r"`type_input` should be a type or tuple of types."
|
366
|
-
):
|
367
|
-
_convert_scalar_seq_type_input_to_tuple(
|
368
|
-
dict,
|
369
|
-
type_input_subclass=collections.abc.Sequence,
|
370
|
-
)
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# copyright: skbase developers, BSD-3-Clause License (see LICENSE file)
|
3
|
+
"""Tests of the functionality for validating iterables of named objects.
|
4
|
+
|
5
|
+
tests in this module include:
|
6
|
+
|
7
|
+
- test_check_type_output
|
8
|
+
- test_check_type_raises_error_if_expected_type_is_wrong_format
|
9
|
+
- test_is_sequence_output
|
10
|
+
- test_is_sequence_with_seq_of_class_and_instance_input
|
11
|
+
- test_check_sequence_output
|
12
|
+
- test_check_sequence_scalar_input_coercion
|
13
|
+
- test_check_sequence_with_seq_of_class_and_instance_input
|
14
|
+
- test_convert_scalar_seq_type_input_to_tuple_raises_error
|
15
|
+
"""
|
16
|
+
import collections
|
17
|
+
|
18
|
+
import numpy as np
|
19
|
+
import pytest
|
20
|
+
|
21
|
+
from skbase.base import BaseEstimator, BaseObject
|
22
|
+
from skbase.validate import check_sequence, check_type, is_sequence
|
23
|
+
from skbase.validate._types import _convert_scalar_seq_type_input_to_tuple
|
24
|
+
|
25
|
+
__author__ = ["RNKuhns"]
|
26
|
+
|
27
|
+
|
28
|
+
@pytest.fixture
|
29
|
+
def fixture_object_instance():
|
30
|
+
"""Pytest fixture of BaseObject instance."""
|
31
|
+
return BaseObject()
|
32
|
+
|
33
|
+
|
34
|
+
@pytest.fixture
|
35
|
+
def fixture_estimator_instance():
|
36
|
+
"""Pytest fixture of BaseObject instance."""
|
37
|
+
return BaseEstimator()
|
38
|
+
|
39
|
+
|
40
|
+
def test_check_type_output(fixture_estimator_instance, fixture_object_instance):
|
41
|
+
"""Test check type returns expected output."""
|
42
|
+
assert check_type(7, expected_type=int) == 7
|
43
|
+
assert check_type(7.2, expected_type=float) == 7.2
|
44
|
+
assert check_type(7.2, expected_type=(float, int)) == 7.2
|
45
|
+
assert check_type("something", expected_type=str) == "something"
|
46
|
+
assert check_type(None, expected_type=str, allow_none=True) is None
|
47
|
+
assert check_type(["a", 7, fixture_object_instance], expected_type=list) == [
|
48
|
+
"a",
|
49
|
+
7,
|
50
|
+
fixture_object_instance,
|
51
|
+
]
|
52
|
+
assert (
|
53
|
+
check_type(fixture_estimator_instance, expected_type=BaseObject)
|
54
|
+
== fixture_estimator_instance
|
55
|
+
)
|
56
|
+
assert check_type(None, expected_type=int, allow_none=True) is None
|
57
|
+
|
58
|
+
with pytest.raises(TypeError, match=r"`input` should be type.*"):
|
59
|
+
check_type(7.2, expected_type=int)
|
60
|
+
|
61
|
+
with pytest.raises(TypeError, match=r"`input` should be type.*"):
|
62
|
+
check_type("something", expected_type=(int, float))
|
63
|
+
|
64
|
+
with pytest.raises(TypeError, match=r"`input` should be type.*"):
|
65
|
+
check_type(BaseEstimator, expected_type=BaseObject)
|
66
|
+
|
67
|
+
with pytest.raises(TypeError, match="^`input` should be.*"):
|
68
|
+
check_type("something", expected_type=int, allow_none=True)
|
69
|
+
|
70
|
+
# Verify optional use of issubclass instead of isinstance
|
71
|
+
assert (
|
72
|
+
check_type(BaseEstimator, expected_type=BaseObject, use_subclass=True)
|
73
|
+
== BaseEstimator
|
74
|
+
)
|
75
|
+
|
76
|
+
|
77
|
+
def test_check_type_raises_error_if_expected_type_is_wrong_format():
|
78
|
+
"""Test check_type raises an error if expected_type wrong format.
|
79
|
+
|
80
|
+
`expected_type` should be a type or tuple of types.
|
81
|
+
"""
|
82
|
+
with pytest.raises(TypeError, match="^`expected_type` should be.*"):
|
83
|
+
check_type(7, expected_type=11)
|
84
|
+
|
85
|
+
with pytest.raises(TypeError, match="^`expected_type` should be.*"):
|
86
|
+
check_type(7, expected_type=[int])
|
87
|
+
|
88
|
+
with pytest.raises(TypeError, match="^`expected_type` should be.*"):
|
89
|
+
check_type(None, expected_type=[int])
|
90
|
+
|
91
|
+
|
92
|
+
def test_is_sequence_output():
|
93
|
+
"""Test is_sequence returns expected output.
|
94
|
+
|
95
|
+
This excludes test of class and class instance usage, which is included in
|
96
|
+
test_is_sequence_with_seq_of_class_and_instance_input.
|
97
|
+
"""
|
98
|
+
# Test simple example with no constraints on sequence_type or element_type
|
99
|
+
# True for any sequence
|
100
|
+
assert is_sequence([1, 2, 3]) is True
|
101
|
+
# But false for generators, since they are iterable but not sequences
|
102
|
+
assert is_sequence((c for c in [1, 2, 3])) is False
|
103
|
+
|
104
|
+
# Test use of sequence_type restriction
|
105
|
+
assert is_sequence([1, 2, 3, 4], sequence_type=list) is True
|
106
|
+
assert is_sequence([1, 2, 3, 4], sequence_type=tuple) is False
|
107
|
+
assert is_sequence((1, 2, 3, 4), sequence_type=list) is False
|
108
|
+
assert is_sequence((1, 2, 3, 4), sequence_type=tuple) is True
|
109
|
+
|
110
|
+
# Test use of element_type restriction
|
111
|
+
assert is_sequence([1, 2, 3], element_type=int) is True
|
112
|
+
assert is_sequence([1, 2, 3], element_type=float) is False
|
113
|
+
assert is_sequence([1, 2, 3, 4], sequence_type=list, element_type=int) is True
|
114
|
+
assert is_sequence([1, 2, 3, 4], sequence_type=tuple, element_type=int) is False
|
115
|
+
assert is_sequence([1, 2, 3, 4], sequence_type=list, element_type=float) is False
|
116
|
+
assert is_sequence([1, 2, 3, 4], sequence_type=tuple, element_type=float) is False
|
117
|
+
|
118
|
+
# Tests using different types
|
119
|
+
assert is_sequence("abc") is True # strings are iterable and sequences in Python
|
120
|
+
assert is_sequence([1, "something", 4.5]) is True
|
121
|
+
assert is_sequence([1, "something", 4.5], element_type=float) is False
|
122
|
+
assert (
|
123
|
+
is_sequence(
|
124
|
+
("a string", "or another string"), sequence_type=tuple, element_type=str
|
125
|
+
)
|
126
|
+
is True
|
127
|
+
)
|
128
|
+
|
129
|
+
# Test with 3rd party types works in default way via exact type
|
130
|
+
assert is_sequence([1.2, 4.7], element_type=np.float_) is False
|
131
|
+
assert is_sequence([np.float_(1.2), np.float_(4.7)], element_type=np.float_) is True
|
132
|
+
|
133
|
+
# np.nan is float, not int or np.float_
|
134
|
+
assert is_sequence([np.nan, 4.8], element_type=float) is True
|
135
|
+
assert is_sequence([np.nan, 4], element_type=int) is False
|
136
|
+
|
137
|
+
|
138
|
+
def test_is_sequence_with_seq_of_class_and_instance_input(
|
139
|
+
fixture_estimator_instance, fixture_object_instance
|
140
|
+
):
|
141
|
+
"""Test is_sequence returns expected value with sequence of classes as input."""
|
142
|
+
# Verify we can identify sequences of a given class type as valid sequences
|
143
|
+
input_seq = (fixture_estimator_instance, fixture_object_instance)
|
144
|
+
assert is_sequence(input_seq, element_type=BaseObject) is True
|
145
|
+
assert (
|
146
|
+
is_sequence(list(input_seq), sequence_type=list, element_type=BaseObject)
|
147
|
+
is True
|
148
|
+
)
|
149
|
+
# Verify we detect when list elements are not instances of valid class type
|
150
|
+
assert is_sequence([1, 2, 3], element_type=BaseObject) is False
|
151
|
+
|
152
|
+
# Verify we can identify sequences of class types as valid sequences of types
|
153
|
+
input_seq = (BaseObject, BaseEstimator)
|
154
|
+
assert is_sequence(input_seq, element_type=type) is True
|
155
|
+
assert is_sequence(list(input_seq), sequence_type=list, element_type=type) is True
|
156
|
+
# Verify we detect when list elements are not instances of valid types
|
157
|
+
assert is_sequence([1, 2, 3], element_type=BaseObject) is False
|
158
|
+
|
159
|
+
|
160
|
+
def test_check_sequence_output():
|
161
|
+
"""Test check_sequence returns expected output.
|
162
|
+
|
163
|
+
This excludes test of class and class instance usage, which is included in
|
164
|
+
test_check_sequence_with_seq_of_class_and_instance_input.
|
165
|
+
"""
|
166
|
+
# Test simple example with no constraints on sequence_type or element_type
|
167
|
+
# True for any sequence
|
168
|
+
assert check_sequence([1, 2, 3]) == [1, 2, 3]
|
169
|
+
assert check_sequence([1, "a", 3.4, False]) == [1, "a", 3.4, False]
|
170
|
+
# But false for generators, since they are iterable but not sequences
|
171
|
+
with pytest.raises(
|
172
|
+
TypeError,
|
173
|
+
match="Invalid sequence: Input sequence expected to be a a sequence.",
|
174
|
+
):
|
175
|
+
assert check_sequence((c for c in [1, 2, 3]))
|
176
|
+
|
177
|
+
# Test use of sequence_type restriction
|
178
|
+
assert check_sequence([1, 2, 3, 4], sequence_type=list) == [1, 2, 3, 4]
|
179
|
+
with pytest.raises(
|
180
|
+
TypeError,
|
181
|
+
match="Invalid sequence: Input sequence expected to be a tuple.",
|
182
|
+
):
|
183
|
+
check_sequence([1, 2, 3, 4], sequence_type=tuple)
|
184
|
+
with pytest.raises(
|
185
|
+
TypeError,
|
186
|
+
match="Invalid sequence: Input sequence expected to be a list.",
|
187
|
+
):
|
188
|
+
check_sequence((1, 2, 3, 4), sequence_type=list)
|
189
|
+
assert check_sequence((1, 2, 3, 4), sequence_type=tuple) == (1, 2, 3, 4)
|
190
|
+
assert check_sequence((1, 2, 3, 4), sequence_type=(list, tuple)) == (1, 2, 3, 4)
|
191
|
+
|
192
|
+
# Test use of element_type restriction
|
193
|
+
assert check_sequence([1, 2, 3], element_type=int) == [1, 2, 3]
|
194
|
+
assert check_sequence([1, 2, 3], element_type=(float, int)) == [1, 2, 3]
|
195
|
+
assert check_sequence([1, 2, False, "a", 3], element_type=(bool, str, int)) == [
|
196
|
+
1,
|
197
|
+
2,
|
198
|
+
False,
|
199
|
+
"a",
|
200
|
+
3,
|
201
|
+
]
|
202
|
+
|
203
|
+
with pytest.raises(
|
204
|
+
TypeError,
|
205
|
+
match="Invalid sequence: .*",
|
206
|
+
):
|
207
|
+
check_sequence([1, 2, 3], element_type=float)
|
208
|
+
with pytest.raises(
|
209
|
+
TypeError,
|
210
|
+
match="Invalid sequence: .*",
|
211
|
+
):
|
212
|
+
check_sequence([1, 2, 3, 4], sequence_type=tuple, element_type=int)
|
213
|
+
with pytest.raises(
|
214
|
+
TypeError,
|
215
|
+
match="Invalid sequence: .*",
|
216
|
+
):
|
217
|
+
check_sequence([1, 2, 3, 4], sequence_type=list, element_type=float)
|
218
|
+
with pytest.raises(
|
219
|
+
TypeError,
|
220
|
+
match="Invalid sequence: .*",
|
221
|
+
):
|
222
|
+
check_sequence([1, 2, 3, 4], sequence_type=tuple, element_type=float)
|
223
|
+
|
224
|
+
input_seq = [1, 2, 3, 4]
|
225
|
+
assert check_sequence(input_seq, sequence_type=list, element_type=int) == input_seq
|
226
|
+
|
227
|
+
# Tests using different types
|
228
|
+
# strings are iterable and sequences in Python
|
229
|
+
assert check_sequence("abc") == "abc"
|
230
|
+
assert check_sequence([1, "something", 4.5]) == [1, "something", 4.5]
|
231
|
+
with pytest.raises(
|
232
|
+
TypeError,
|
233
|
+
match="Invalid sequence: .*",
|
234
|
+
):
|
235
|
+
check_sequence([1, "something", 4.5], element_type=float)
|
236
|
+
|
237
|
+
assert check_sequence(
|
238
|
+
("a string", "or another string"), sequence_type=tuple, element_type=str
|
239
|
+
) == ("a string", "or another string")
|
240
|
+
|
241
|
+
# Test with 3rd party types works in default way via exact type
|
242
|
+
with pytest.raises(
|
243
|
+
TypeError,
|
244
|
+
match="Invalid sequence: .*",
|
245
|
+
):
|
246
|
+
check_sequence([1.2, 4.7], element_type=np.float_)
|
247
|
+
input_seq = [np.float_(1.2), np.float_(4.7)]
|
248
|
+
assert check_sequence(input_seq, element_type=np.float_) == input_seq
|
249
|
+
|
250
|
+
# np.nan is float, not int or np.float_
|
251
|
+
assert check_sequence([np.nan, 4.8], element_type=float) == [np.nan, 4.8]
|
252
|
+
assert check_sequence([np.nan, 4.8, 7], element_type=(float, int)) == [
|
253
|
+
np.nan,
|
254
|
+
4.8,
|
255
|
+
7,
|
256
|
+
]
|
257
|
+
with pytest.raises(
|
258
|
+
TypeError,
|
259
|
+
match="Invalid sequence: .*",
|
260
|
+
):
|
261
|
+
check_sequence([np.nan, 4], element_type=int)
|
262
|
+
|
263
|
+
# Check return type coercion to specified sequence type
|
264
|
+
input_seq = [1, 2, 3, 4]
|
265
|
+
assert check_sequence(
|
266
|
+
input_seq, sequence_type=list, element_type=int, coerce_output_type_to=tuple
|
267
|
+
) == tuple(input_seq)
|
268
|
+
|
269
|
+
|
270
|
+
def test_check_sequence_scalar_input_coercion():
|
271
|
+
"""Test check_sequence coerces scalar inputs to sequences as expected."""
|
272
|
+
assert check_sequence(7, element_type=int, coerce_scalar_input=True) == (7,)
|
273
|
+
assert check_sequence(
|
274
|
+
7, element_type=int, coerce_output_type_to=list, coerce_scalar_input=True
|
275
|
+
) == [7]
|
276
|
+
|
277
|
+
# Note that single strings treated as scalars for this purpose
|
278
|
+
assert check_sequence(
|
279
|
+
"some string", element_type=str, coerce_scalar_input=True
|
280
|
+
) == ("some string",)
|
281
|
+
|
282
|
+
# coercion takes into account allowed sequence_types
|
283
|
+
assert check_sequence(
|
284
|
+
7, element_type=int, sequence_type=list, coerce_scalar_input=True
|
285
|
+
) == [7]
|
286
|
+
# If more than one sequence_type allowed then the first is used for coercion
|
287
|
+
assert check_sequence(
|
288
|
+
7, element_type=int, sequence_type=(list, tuple), coerce_scalar_input=True
|
289
|
+
) == [7]
|
290
|
+
# Output type conversion overrides input type coercion to specified sequence_type
|
291
|
+
assert check_sequence(
|
292
|
+
7,
|
293
|
+
element_type=int,
|
294
|
+
sequence_type=list,
|
295
|
+
coerce_output_type_to=tuple,
|
296
|
+
coerce_scalar_input=True,
|
297
|
+
) == (7,)
|
298
|
+
|
299
|
+
# Still raise an error if element type is not expected
|
300
|
+
with pytest.raises(
|
301
|
+
TypeError,
|
302
|
+
match="Invalid sequence: .*",
|
303
|
+
):
|
304
|
+
check_sequence(
|
305
|
+
7,
|
306
|
+
sequence_type=list,
|
307
|
+
element_type=float,
|
308
|
+
coerce_scalar_input=True,
|
309
|
+
)
|
310
|
+
|
311
|
+
|
312
|
+
def test_check_sequence_with_seq_of_class_and_instance_input(
|
313
|
+
fixture_estimator_instance, fixture_object_instance
|
314
|
+
):
|
315
|
+
"""Test check_sequence returns expected value with sequence of classes as input."""
|
316
|
+
# Verify we can identify sequences of a given class type as valid sequences
|
317
|
+
input_seq = (fixture_estimator_instance, fixture_object_instance)
|
318
|
+
assert check_sequence(input_seq, element_type=BaseObject) == input_seq
|
319
|
+
assert check_sequence(
|
320
|
+
list(input_seq), sequence_type=list, element_type=BaseObject
|
321
|
+
) == list(input_seq)
|
322
|
+
|
323
|
+
with pytest.raises(
|
324
|
+
TypeError,
|
325
|
+
match="Invalid sequence: .*",
|
326
|
+
):
|
327
|
+
check_sequence(list(input_seq), sequence_type=tuple, element_type=BaseObject)
|
328
|
+
with pytest.raises(
|
329
|
+
TypeError,
|
330
|
+
match="Invalid sequence: .*",
|
331
|
+
):
|
332
|
+
# Verify we detect when list elements are not instances of valid class type
|
333
|
+
check_sequence([1, 2, 3], element_type=BaseObject)
|
334
|
+
|
335
|
+
# Verify we can identify sequences of class types as valid sequences of types
|
336
|
+
input_seq = (BaseObject, BaseEstimator)
|
337
|
+
assert check_sequence(input_seq, element_type=type) == input_seq
|
338
|
+
assert check_sequence(
|
339
|
+
list(input_seq), sequence_type=list, element_type=type
|
340
|
+
) == list(input_seq)
|
341
|
+
with pytest.raises(
|
342
|
+
TypeError,
|
343
|
+
match="Invalid sequence: .*",
|
344
|
+
):
|
345
|
+
# Verify we detect when list elements are not instances of valid types
|
346
|
+
check_sequence([1, 2, 3], element_type=BaseObject)
|
347
|
+
|
348
|
+
|
349
|
+
def test_convert_scalar_seq_type_input_to_tuple_raises_error():
|
350
|
+
"""Test _convert_scalar_seq_type_input_to_tuple raises error."""
|
351
|
+
# Raises because 7 is not a type
|
352
|
+
with pytest.raises(
|
353
|
+
TypeError, match=r"`type_input` should be a type or tuple of types."
|
354
|
+
):
|
355
|
+
_convert_scalar_seq_type_input_to_tuple(7)
|
356
|
+
|
357
|
+
# Test error message uses input_name
|
358
|
+
with pytest.raises(
|
359
|
+
TypeError, match=r"`some_input` should be a type or tuple of types."
|
360
|
+
):
|
361
|
+
_convert_scalar_seq_type_input_to_tuple(7, input_name="some_input")
|
362
|
+
|
363
|
+
# Raises error because dict is a type but not a subclass of type_input_subclass
|
364
|
+
with pytest.raises(
|
365
|
+
TypeError, match=r"`type_input` should be a type or tuple of types."
|
366
|
+
):
|
367
|
+
_convert_scalar_seq_type_input_to_tuple(
|
368
|
+
dict,
|
369
|
+
type_input_subclass=collections.abc.Sequence,
|
370
|
+
)
|