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.
Files changed (60) hide show
  1. docs/source/conf.py +299 -299
  2. {scikit_base-0.4.6.dist-info → scikit_base-0.5.1.dist-info}/LICENSE +29 -29
  3. {scikit_base-0.4.6.dist-info → scikit_base-0.5.1.dist-info}/METADATA +160 -159
  4. scikit_base-0.5.1.dist-info/RECORD +58 -0
  5. {scikit_base-0.4.6.dist-info → scikit_base-0.5.1.dist-info}/WHEEL +1 -1
  6. scikit_base-0.5.1.dist-info/top_level.txt +5 -0
  7. {scikit_base-0.4.6.dist-info → scikit_base-0.5.1.dist-info}/zip-safe +1 -1
  8. skbase/__init__.py +14 -14
  9. skbase/_exceptions.py +31 -31
  10. skbase/_nopytest_tests.py +35 -35
  11. skbase/base/__init__.py +20 -20
  12. skbase/base/_base.py +1249 -1249
  13. skbase/base/_meta.py +883 -871
  14. skbase/base/_pretty_printing/__init__.py +11 -11
  15. skbase/base/_pretty_printing/_object_html_repr.py +392 -392
  16. skbase/base/_pretty_printing/_pprint.py +412 -412
  17. skbase/base/_tagmanager.py +217 -217
  18. skbase/lookup/__init__.py +31 -31
  19. skbase/lookup/_lookup.py +1009 -1009
  20. skbase/lookup/tests/__init__.py +2 -2
  21. skbase/lookup/tests/test_lookup.py +991 -991
  22. skbase/testing/__init__.py +12 -12
  23. skbase/testing/test_all_objects.py +852 -856
  24. skbase/testing/utils/__init__.py +5 -5
  25. skbase/testing/utils/_conditional_fixtures.py +209 -209
  26. skbase/testing/utils/_dependencies.py +15 -15
  27. skbase/testing/utils/deep_equals.py +15 -15
  28. skbase/testing/utils/inspect.py +30 -30
  29. skbase/testing/utils/tests/__init__.py +2 -2
  30. skbase/testing/utils/tests/test_check_dependencies.py +49 -49
  31. skbase/testing/utils/tests/test_deep_equals.py +66 -66
  32. skbase/tests/__init__.py +2 -2
  33. skbase/tests/conftest.py +273 -273
  34. skbase/tests/mock_package/__init__.py +5 -5
  35. skbase/tests/mock_package/test_mock_package.py +74 -74
  36. skbase/tests/test_base.py +1202 -1202
  37. skbase/tests/test_baseestimator.py +130 -130
  38. skbase/tests/test_exceptions.py +23 -23
  39. skbase/tests/test_meta.py +170 -131
  40. skbase/utils/__init__.py +21 -21
  41. skbase/utils/_check.py +53 -53
  42. skbase/utils/_iter.py +238 -238
  43. skbase/utils/_nested_iter.py +180 -180
  44. skbase/utils/_utils.py +91 -91
  45. skbase/utils/deep_equals.py +358 -358
  46. skbase/utils/dependencies/__init__.py +11 -11
  47. skbase/utils/dependencies/_dependencies.py +253 -253
  48. skbase/utils/tests/__init__.py +4 -4
  49. skbase/utils/tests/test_check.py +24 -24
  50. skbase/utils/tests/test_iter.py +127 -127
  51. skbase/utils/tests/test_nested_iter.py +84 -84
  52. skbase/utils/tests/test_utils.py +37 -37
  53. skbase/validate/__init__.py +22 -22
  54. skbase/validate/_named_objects.py +403 -403
  55. skbase/validate/_types.py +345 -345
  56. skbase/validate/tests/__init__.py +2 -2
  57. skbase/validate/tests/test_iterable_named_objects.py +200 -200
  58. skbase/validate/tests/test_type_validations.py +370 -370
  59. scikit_base-0.4.6.dist-info/RECORD +0 -58
  60. 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
+ )