scikit-base 0.12.0__py3-none-any.whl → 0.12.2__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: scikit-base
3
- Version: 0.12.0
3
+ Version: 0.12.2
4
4
  Summary: Base classes for sklearn-like parametric objects
5
5
  Author-email: sktime developers <sktime.toolbox@gmail.com>
6
6
  Maintainer: Franz Király
@@ -58,28 +58,14 @@ Classifier: Programming Language :: Python :: 3.13
58
58
  Requires-Python: <3.14,>=3.9
59
59
  Description-Content-Type: text/markdown
60
60
  License-File: LICENSE
61
- Provides-Extra: all_extras
61
+ Provides-Extra: all-extras
62
62
  Requires-Dist: numpy; extra == "all-extras"
63
63
  Requires-Dist: pandas; extra == "all-extras"
64
- Provides-Extra: binder
65
- Requires-Dist: jupyter; extra == "binder"
66
64
  Provides-Extra: dev
67
65
  Requires-Dist: scikit-learn>=0.24.0; extra == "dev"
68
66
  Requires-Dist: pre-commit; extra == "dev"
69
67
  Requires-Dist: pytest; extra == "dev"
70
68
  Requires-Dist: pytest-cov; extra == "dev"
71
- Provides-Extra: docs
72
- Requires-Dist: jupyter; extra == "docs"
73
- Requires-Dist: myst-parser; extra == "docs"
74
- Requires-Dist: nbsphinx>=0.8.6; extra == "docs"
75
- Requires-Dist: numpydoc; extra == "docs"
76
- Requires-Dist: pydata-sphinx-theme; extra == "docs"
77
- Requires-Dist: sphinx-issues<6.0.0; extra == "docs"
78
- Requires-Dist: sphinx-gallery<0.19.0; extra == "docs"
79
- Requires-Dist: sphinx-panels; extra == "docs"
80
- Requires-Dist: sphinx-design<0.7.0; extra == "docs"
81
- Requires-Dist: Sphinx!=7.2.0,<9.0.0; extra == "docs"
82
- Requires-Dist: tabulate; extra == "docs"
83
69
  Provides-Extra: linters
84
70
  Requires-Dist: mypy; extra == "linters"
85
71
  Requires-Dist: isort; extra == "linters"
@@ -95,6 +81,20 @@ Requires-Dist: pandas-vet; extra == "linters"
95
81
  Requires-Dist: flake8-print; extra == "linters"
96
82
  Requires-Dist: pep8-naming; extra == "linters"
97
83
  Requires-Dist: doc8; extra == "linters"
84
+ Provides-Extra: binder
85
+ Requires-Dist: jupyter; extra == "binder"
86
+ Provides-Extra: docs
87
+ Requires-Dist: jupyter; extra == "docs"
88
+ Requires-Dist: myst-parser; extra == "docs"
89
+ Requires-Dist: nbsphinx>=0.8.6; extra == "docs"
90
+ Requires-Dist: numpydoc; extra == "docs"
91
+ Requires-Dist: pydata-sphinx-theme; extra == "docs"
92
+ Requires-Dist: sphinx-issues<6.0.0; extra == "docs"
93
+ Requires-Dist: sphinx-gallery<0.20.0; extra == "docs"
94
+ Requires-Dist: sphinx-panels; extra == "docs"
95
+ Requires-Dist: sphinx-design<0.7.0; extra == "docs"
96
+ Requires-Dist: Sphinx!=7.2.0,<9.0.0; extra == "docs"
97
+ Requires-Dist: tabulate; extra == "docs"
98
98
  Provides-Extra: test
99
99
  Requires-Dist: pytest; extra == "test"
100
100
  Requires-Dist: coverage; extra == "test"
@@ -104,6 +104,7 @@ Requires-Dist: numpy; extra == "test"
104
104
  Requires-Dist: scipy; extra == "test"
105
105
  Requires-Dist: pandas; extra == "test"
106
106
  Requires-Dist: scikit-learn>=0.24.0; extra == "test"
107
+ Dynamic: license-file
107
108
 
108
109
  <a href="https://skbase.readthedocs.io/en/latest/"><img src="https://github.com/sktime/skbase/blob/main/docs/source/images/skbase-logo-with-name.png" width="175" align="right" /></a>
109
110
 
@@ -114,7 +115,7 @@ Requires-Dist: scikit-learn>=0.24.0; extra == "test"
114
115
  `skbase` provides base classes for creating scikit-learn-like parametric objects,
115
116
  along with tools to make it easier to build your own packages that follow these design patterns.
116
117
 
117
- :rocket: Version 0.12.0 is now available. Check out our
118
+ :rocket: Version 0.12.2 is now available. Check out our
118
119
  [release notes](https://skbase.readthedocs.io/en/latest/changelog.html).
119
120
 
120
121
  | Overview | |
@@ -160,3 +161,13 @@ or, if you want to install with the maximum set of dependencies, use:
160
161
  ```bash
161
162
  pip install scikit-base[all_extras]
162
163
  ```
164
+
165
+ ## Contributors ✨
166
+
167
+ This project follows the
168
+ [all-contributors](https://github.com/all-contributors/all-contributors) specification.
169
+ Contributions of any kind welcome!
170
+
171
+ Thanks go to these wonderful people:
172
+
173
+ [skbase contributors](https://github.com/sktime/skbase/graphs/contributors)
@@ -1,12 +1,13 @@
1
1
  docs/source/conf.py,sha256=kFc-4qkb0ZGD5cDej5KPJhMePp9kpVu6ZqFoF0fgovg,9951
2
- skbase/__init__.py,sha256=WEOqt1h0pUasRLXHL63vuu7eUGzR-CzdszMmunJyH70,346
2
+ scikit_base-0.12.2.dist-info/licenses/LICENSE,sha256=W2h8EYZ_G_mvCmCmXTTYqv66QF5NgSMbzLYJdk8qHVg,1525
3
+ skbase/__init__.py,sha256=5SckxWhIw301-BYxKlAns_hbBTHaoKcxx7u8_3OVml0,346
3
4
  skbase/_exceptions.py,sha256=asAhMbBeMwRBU_HDPFzwVCz8sb9_itG_6JVq3v_RZv8,1100
4
- skbase/_nopytest_tests.py,sha256=npL5pibSgCpulEGw0NqLKcG0majh6xcdW5A4Zibf78s,1077
5
+ skbase/_nopytest_tests.py,sha256=NnFa4WPrjxUCcBvIlkCh7q-4WfMFVErSEPMK4OJPFtY,1078
5
6
  skbase/base/__init__.py,sha256=5ZLlwJeyfKDA1lAylBJgZd3t5JY25xsgQB4waQnroa8,751
6
- skbase/base/_base.py,sha256=frYe-ycOLR8t-swr_ROwBTgFIFC2JEGV15yfqS0BJ5s,65942
7
+ skbase/base/_base.py,sha256=4U87g1P7MFSvd5_6uNZXTXXJX8zcy8yHP1U5p1J-pHQ,66020
7
8
  skbase/base/_clone_base.py,sha256=u-uw9mOLUf0QKxvM4ibeClYRTSf7wwcKDvAoiuh0Y-Q,5281
8
9
  skbase/base/_clone_plugins.py,sha256=61_FqlE0oCDFymFtzrSSWlbm_yg5ugCyFnhNLF2MdSo,6693
9
- skbase/base/_meta.py,sha256=VY6_R2tE885j-GTDuzLFyho5i382jOni5lkR_ykPZqo,38815
10
+ skbase/base/_meta.py,sha256=vW6f4rf64ijJ7fj0CVfoAui6nC1ujTSd_gtuAcC8d9g,39073
10
11
  skbase/base/_tagmanager.py,sha256=nKoiIC1yXFFSpN5ljWbMrwA-pwlbxsljgKuUywh1MR4,7289
11
12
  skbase/base/_pretty_printing/__init__.py,sha256=bVuKnwafn8c2q2AGJ9BOu9cmu-xBjiOxHf1hxjm8K2A,492
12
13
  skbase/base/_pretty_printing/_object_html_repr.py,sha256=jvng-RT2JH4RElJkYBNdfu-lRKzlqZeBgqsNl2kNDKM,11677
@@ -18,13 +19,13 @@ skbase/lookup/_lookup.py,sha256=COZhLXRVZUdisoiS53J1LZylyjlM8TX-P9erEp6bk9I,4302
18
19
  skbase/lookup/tests/__init__.py,sha256=MVqGlWsUV-gQ4qzW_TqE3UmKO9IQ9mwdDlsIHaGt3bc,68
19
20
  skbase/lookup/tests/test_lookup.py,sha256=kAgsGyp4EYrXZnqezya-PI14m9mm8-ePoR0Wf-Cu-oo,39782
20
21
  skbase/testing/__init__.py,sha256=OdwR-aEU2KzGrU-O0gtNSMNGmF2mtgBmjAnMzcgwe6w,351
21
- skbase/testing/test_all_objects.py,sha256=YoG4Ogg8X9etZoGhPhcwzLTzBCq6GyOncEIRo0qR1Og,36373
22
+ skbase/testing/test_all_objects.py,sha256=WCdpQ0cYxeAoBkmT1Dh-iDeHdbgqZlTB6SOBQLDLV7I,36372
22
23
  skbase/testing/utils/__init__.py,sha256=kaLuqQwJsCunRWsUb1JwTVG-iqXbzdUobuYHNHsBlQQ,113
23
24
  skbase/testing/utils/_conditional_fixtures.py,sha256=QwI7K28Lsy6RAkDP94goo8uWWvMzKKNOmXRFtc9RNtI,9890
24
25
  skbase/testing/utils/inspect.py,sha256=e6F7AIuDhBTpgK8KKmiuwxeggrMjC7DHuSAKA1jOU2A,761
25
26
  skbase/tests/__init__.py,sha256=d2_OTTnt0GX5otQsBuNAb1evg8C5Fi0JjqK2VsfMtXU,37
26
- skbase/tests/conftest.py,sha256=mTtbBiKzmC2dRF3vir_Kh1EmcAhkxNfNsmxIvbmcfos,10723
27
- skbase/tests/test_base.py,sha256=TDnXM805Ak50DgrG-tzRzcZy9CagpCtq-jIdRzKS0PY,51553
27
+ skbase/tests/conftest.py,sha256=pHzQlpGJatKlGc80WtMitgPeHiaiYIkXzUEXkJIvnGs,10757
28
+ skbase/tests/test_base.py,sha256=DQzJFtGc7gFOyPRc3b-LfAtFONI4BntanKBicm85rws,49439
28
29
  skbase/tests/test_baseestimator.py,sha256=fuzpwxjYzyl-Vrte1va4AWdbYElhWnED8W10236Xprc,4731
29
30
  skbase/tests/test_exceptions.py,sha256=wOdk7Gp8pvbhucna3_9FxTk9xFLjC9XNsGsVabQLYEE,629
30
31
  skbase/tests/test_meta.py,sha256=TTZW_BlEbirLjeEQCV1x3IYCf6V2ULJ_KfyVHgs0wkU,5662
@@ -40,15 +41,15 @@ skbase/utils/stderr_mute.py,sha256=VGMAjYgEjl-T-cFEzGJp_ry2iNR8wYLKL9SDhT8OZ7s,2
40
41
  skbase/utils/stdout_mute.py,sha256=XeeNst0oN2D77x85N0pQsBv_iYj6gtlliNS7WadwypQ,2046
41
42
  skbase/utils/deep_equals/__init__.py,sha256=1II3GWV1c1s43y62IidMiTjjyOnE9MFysQ5AKCXMB2g,235
42
43
  skbase/utils/deep_equals/_common.py,sha256=O0ODPJGwdq6G-KdeGoHgyote53tNcxu3y2jHvej3bdQ,1273
43
- skbase/utils/deep_equals/_deep_equals.py,sha256=DT6nE0p1IGsLb82h3JJu24_nWeNE2HI46eL2qPlqxbo,19151
44
+ skbase/utils/deep_equals/_deep_equals.py,sha256=zKJx6xPUOHCYrqJh322TA9BW2c10gLgmbrHqKW6siqk,19225
44
45
  skbase/utils/dependencies/__init__.py,sha256=cCUa_P-RiDs4pW6cw51uYeoBMaMa9iycwiFkwqkIizc,419
45
- skbase/utils/dependencies/_dependencies.py,sha256=muUbqw4vmmn6YvkugIhlaqGKgW8pSermnhvn5DvahQs,20763
46
+ skbase/utils/dependencies/_dependencies.py,sha256=6G1wnNoLj7tXPJA0Da1inBiOryUYoJDuzTdVOodIJYA,22368
46
47
  skbase/utils/dependencies/_import.py,sha256=PoaZE6WiCTp-vuvrkrM6EO2wWvX6owanQ0uESFhqLtQ,802
47
48
  skbase/utils/dependencies/tests/__init__.py,sha256=UqE6wenG-HffjT2Z974OLzmXG-M8PNOP9nUnNfqtfT4,74
48
- skbase/utils/dependencies/tests/test_check_dependencies.py,sha256=uxAC3gr4VWTlgctN90pnT1ra_UYkPxQHEla-IljX-n0,2238
49
+ skbase/utils/dependencies/tests/test_check_dependencies.py,sha256=uIgAO2xkTlmKYH-4_38Asba7590QTzHkyDrDkFqoQss,4169
49
50
  skbase/utils/tests/__init__.py,sha256=YfvP5lpCrTC_6SIakU7jBBdqYyuqE07nZ56ZYKTs3f0,165
50
51
  skbase/utils/tests/test_check.py,sha256=rMxaQtKegaKZPGjocNB9ntMwMIAq5-7SmNZuFsWFGZE,754
51
- skbase/utils/tests/test_deep_equals.py,sha256=WdWpaUPi8m_kzP2IbQcPdfWmerEDVd-AaBuGiG_aPcE,3848
52
+ skbase/utils/tests/test_deep_equals.py,sha256=VVsNAfiGC3GOG_9qtsrWR6Z4d6WwRy_HhE4n-Sv3Lgo,3868
52
53
  skbase/utils/tests/test_iter.py,sha256=XIJPZ3QjVR5szj5oNS9DBwum6WXRGHSAiC0O9MW4maY,4918
53
54
  skbase/utils/tests/test_nested_iter.py,sha256=lZF9jiU_6xw1dOo2QrrVF96Pw8ThutQuVlRspIgNy80,2230
54
55
  skbase/utils/tests/test_random_state.py,sha256=XW1KIFy2S-MQjlx4lUdP8K-w1N9eEUWa7PP_Yve7d78,3934
@@ -60,9 +61,8 @@ skbase/validate/_types.py,sha256=riVEVlj8ipErZX07OVbzv6zdGKssfegHyMr8XwaBm6M,121
60
61
  skbase/validate/tests/__init__.py,sha256=wunQBy6rajyrDymKvuFVajsBjj90VP5IFey5b6ZIRCk,70
61
62
  skbase/validate/tests/test_iterable_named_objects.py,sha256=NaEwdmtQJJy4GXMSh9ULOaR4ua7R11BcE6Khz5RKWUk,7438
62
63
  skbase/validate/tests/test_type_validations.py,sha256=oIysbDxRlbBMcCOrDMW6MM6VqhhMWJxNP6NO9Id9Q5g,14133
63
- scikit_base-0.12.0.dist-info/LICENSE,sha256=W2h8EYZ_G_mvCmCmXTTYqv66QF5NgSMbzLYJdk8qHVg,1525
64
- scikit_base-0.12.0.dist-info/METADATA,sha256=4pzEkNaQD8FLpiTLpKs2CVQ-kBZyuUnsRP_9a1tDmZw,8487
65
- scikit_base-0.12.0.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
66
- scikit_base-0.12.0.dist-info/top_level.txt,sha256=FbRMsZcP-O6pMLGZpxA5pQ-ClfRzoB6Yr-hTViYqwT0,57
67
- scikit_base-0.12.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
68
- scikit_base-0.12.0.dist-info/RECORD,,
64
+ scikit_base-0.12.2.dist-info/METADATA,sha256=2ists-o7LlPIz2vgdnkdftBDCtNhdXBHqGahd8yV0iI,8794
65
+ scikit_base-0.12.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
66
+ scikit_base-0.12.2.dist-info/top_level.txt,sha256=FbRMsZcP-O6pMLGZpxA5pQ-ClfRzoB6Yr-hTViYqwT0,57
67
+ scikit_base-0.12.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
68
+ scikit_base-0.12.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.5.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
skbase/__init__.py CHANGED
@@ -6,4 +6,4 @@
6
6
  The included functionality makes it easy to reuse scikit-learn and
7
7
  sktime design principles in your project.
8
8
  """
9
- __version__: str = "0.12.0"
9
+ __version__: str = "0.12.2"
skbase/_nopytest_tests.py CHANGED
@@ -7,7 +7,7 @@ from skbase.lookup import all_objects
7
7
 
8
8
  MODULES_TO_IGNORE = ("tests", "testing", "dependencies", "all")
9
9
 
10
- # all_objectscrawls all modules excepting pytest test files
10
+ # all_objects crawls all modules excepting pytest test files
11
11
  # if it encounters an unisolated import, it will throw an exception
12
12
  results = all_objects(modules_to_ignore=MODULES_TO_IGNORE)
13
13
 
skbase/base/_base.py CHANGED
@@ -1169,13 +1169,19 @@ class BaseObject(_FlagManager):
1169
1169
  class TagAliaserMixin:
1170
1170
  """Mixin class for tag aliasing and deprecation of old tags.
1171
1171
 
1172
- To deprecate tags, add the TagAliaserMixin to BaseObject or BaseEstimator.
1173
- alias_dict contains the deprecated tags, and supports removal and renaming.
1174
- For removal, add an entry "old_tag_name": ""
1175
- For renaming, add an entry "old_tag_name": "new_tag_name"
1176
- deprecate_dict contains the version number of renaming or removal.
1177
- the keys in deprecate_dict should be the same as in alias_dict.
1178
- values in deprecate_dict should be strings, the version of removal/renaming.
1172
+ To deprecate tags, add the ``TagAliaserMixin`` to ``BaseObject``
1173
+ or ``BaseEstimator``.
1174
+
1175
+ ``alias_dict`` contains the deprecated tags, and supports removal and renaming.
1176
+
1177
+ * For removal, add an entry ``"old_tag_name": ""``
1178
+ * For renaming, add an entry ``"old_tag_name": "new_tag_name"``
1179
+
1180
+ ``deprecate_dict`` contains the version number of renaming or removal.
1181
+
1182
+ * The keys in ``deprecate_dict`` should be the same as in alias_dict.
1183
+ * Values in ``deprecate_dict`` should be strings, the version of
1184
+ removal/renaming, in PEP 440 format, e.g., ``"1.0.0"``.
1179
1185
 
1180
1186
  The class will ensure that new tags alias old tags and vice versa, during
1181
1187
  the deprecation period. Informative warnings will be raised whenever the
skbase/base/_meta.py CHANGED
@@ -360,6 +360,7 @@ class _MetaObjectMixin:
360
360
  cls_type=None,
361
361
  allow_dict=False,
362
362
  allow_mix=True,
363
+ allow_empty=False,
363
364
  clone=True,
364
365
  ):
365
366
  """Check that objects is a list of objects or sequence of named objects.
@@ -373,10 +374,14 @@ class _MetaObjectMixin:
373
374
  Name of checked attribute in error messages.
374
375
  cls_type : class or tuple of classes, default=BaseEstimator.
375
376
  class(es) that all objects are checked to be an instance of.
377
+ allow_dict : bool, default=False
378
+ Whether ``objs`` can be a dictionary mapping str names to objects.
376
379
  allow_mix : bool, default=True
377
- Whether mix of objects and (str, objects) is allowed in `objs.`
380
+ Whether mix of objects and (str, objects) is allowed in ``objs``.
381
+ allow_empty : bool, default=False
382
+ Whether ``objs`` can be empty.
378
383
  clone : bool, default=True
379
- Whether objects or named objects in `objs` are returned as clones
384
+ Whether objects or named objects in ``objs`` are returned as clones
380
385
  (True) or references (False).
381
386
 
382
387
  Returns
@@ -421,7 +426,7 @@ class _MetaObjectMixin:
421
426
 
422
427
  if (
423
428
  objs is None
424
- or len(objs) == 0
429
+ or (not allow_empty and len(objs) == 0)
425
430
  or not (isinstance(objs, list) or (allow_dict and isinstance(objs, dict)))
426
431
  ):
427
432
  raise TypeError(msg)
@@ -226,7 +226,7 @@ class BaseFixtureGenerator:
226
226
  @pytest.fixture(scope="function")
227
227
  def object_instance(self, request):
228
228
  """object_instance fixture definition for indirect use."""
229
- # esetimator_instance is cloned at the start of every test
229
+ # estimator_instance is cloned at the start of every test
230
230
  return request.param.clone()
231
231
 
232
232
 
skbase/tests/conftest.py CHANGED
@@ -272,6 +272,7 @@ SKBASE_FUNCTIONS_BY_MODULE.update(
272
272
  "_get_pkg_version",
273
273
  "_get_installed_packages",
274
274
  "_normalize_requirement",
275
+ "_normalize_version",
275
276
  "_raise_at_severity",
276
277
  ),
277
278
  "skbase.utils.random_state": (
skbase/tests/test_base.py CHANGED
@@ -51,10 +51,7 @@ __all__ = [
51
51
  "test_clone",
52
52
  "test_clone_2",
53
53
  "test_clone_raises_error_for_nonconforming_objects",
54
- "test_clone_param_is_none",
55
- "test_clone_empty_array",
56
- "test_clone_sparse_matrix",
57
- "test_clone_nan",
54
+ "test_clone_none_and_empty_array_nan_sparse_matrix",
58
55
  "test_clone_estimator_types",
59
56
  "test_clone_class_rather_than_instance_raises_error",
60
57
  "test_clone_sklearn_composite",
@@ -1025,75 +1022,30 @@ def test_nested_config_after_clone_tags(clone_config):
1025
1022
  not _check_soft_dependencies("scikit-learn", severity="none"),
1026
1023
  reason="skip test if sklearn is not available",
1027
1024
  ) # sklearn is part of the dev dependency set, test should be executed with that
1028
- def test_clone_param_is_none(fixture_class_parent: Type[Parent]):
1029
- """Test clone with keyword parameter set to None."""
1030
- from sklearn.base import clone
1031
-
1032
- base_obj = fixture_class_parent(c=None)
1033
- new_base_obj = clone(base_obj)
1034
- new_base_obj2 = base_obj.clone()
1035
- assert base_obj.c is new_base_obj.c
1036
- assert base_obj.c is new_base_obj2.c
1037
-
1038
-
1039
- @pytest.mark.skipif(
1040
- not _check_soft_dependencies("scikit-learn", severity="none"),
1041
- reason="skip test if sklearn is not available",
1042
- ) # sklearn is part of the dev dependency set, test should be executed with that
1043
- def test_clone_empty_array(fixture_class_parent: Type[Parent]):
1044
- """Test clone with keyword parameter is scipy sparse matrix.
1045
-
1046
- This test is based on scikit-learn regression test to make sure clone
1047
- works with default parameter set to scipy sparse matrix.
1048
- """
1049
- from sklearn.base import clone
1050
-
1051
- # Regression test for cloning estimators with empty arrays
1052
- base_obj = fixture_class_parent(c=np.array([]))
1053
- new_base_obj = clone(base_obj)
1054
- new_base_obj2 = base_obj.clone()
1055
- np.testing.assert_array_equal(base_obj.c, new_base_obj.c)
1056
- np.testing.assert_array_equal(base_obj.c, new_base_obj2.c)
1057
-
1058
-
1059
- @pytest.mark.skipif(
1060
- not _check_soft_dependencies("scikit-learn", severity="none"),
1061
- reason="skip test if sklearn is not available",
1062
- ) # sklearn is part of the dev dependency set, test should be executed with that
1063
- def test_clone_sparse_matrix(fixture_class_parent: Type[Parent]):
1064
- """Test clone with keyword parameter is scipy sparse matrix.
1065
-
1066
- This test is based on scikit-learn regression test to make sure clone
1067
- works with default parameter set to scipy sparse matrix.
1068
- """
1069
- from sklearn.base import clone
1070
-
1071
- base_obj = fixture_class_parent(c=sp.csr_matrix(np.array([[0]])))
1072
- new_base_obj = clone(base_obj)
1073
- new_base_obj2 = base_obj.clone()
1074
- np.testing.assert_array_equal(base_obj.c, new_base_obj.c)
1075
- np.testing.assert_array_equal(base_obj.c, new_base_obj2.c)
1076
-
1077
-
1078
- @pytest.mark.skipif(
1079
- not _check_soft_dependencies("scikit-learn", severity="none"),
1080
- reason="skip test if sklearn is not available",
1081
- ) # sklearn is part of the dev dependency set, test should be executed with that
1082
- def test_clone_nan(fixture_class_parent: Type[Parent]):
1083
- """Test clone with keyword parameter is np.nan.
1084
-
1085
- This test is based on scikit-learn regression test to make sure clone
1086
- works with default parameter set to np.nan.
1087
- """
1025
+ @pytest.mark.parametrize(
1026
+ "c_value",
1027
+ [
1028
+ None,
1029
+ np.array([]),
1030
+ sp.csr_matrix(np.array([[0]])),
1031
+ np.nan,
1032
+ ],
1033
+ )
1034
+ def test_clone_none_and_empty_array_nan_sparse_matrix(
1035
+ fixture_class_parent: Type[Parent], c_value
1036
+ ):
1088
1037
  from sklearn.base import clone
1089
1038
 
1090
- # Regression test for cloning estimators with default parameter as np.nan
1091
- base_obj = fixture_class_parent(c=np.nan)
1039
+ base_obj = fixture_class_parent(c=c_value)
1092
1040
  new_base_obj = clone(base_obj)
1093
1041
  new_base_obj2 = base_obj.clone()
1094
1042
 
1095
- assert base_obj.c is new_base_obj.c
1096
- assert base_obj.c is new_base_obj2.c
1043
+ if isinstance(base_obj.c, (np.ndarray, type(sp.csr_matrix(np.array([[0]]))))):
1044
+ np.testing.assert_array_equal(base_obj.c, new_base_obj.c)
1045
+ np.testing.assert_array_equal(base_obj.c, new_base_obj2.c)
1046
+ else:
1047
+ assert base_obj.c is new_base_obj.c
1048
+ assert base_obj.c is new_base_obj2.c
1097
1049
 
1098
1050
 
1099
1051
  def test_clone_estimator_types(fixture_class_parent: Type[Parent]):
@@ -267,6 +267,7 @@ def _pandas_equals(x, y, return_msg=False, deep_equals=None):
267
267
  return ret(
268
268
  False, f".dtypes, x.dtypes = {x.dtypes} != y.dtypes = {y.dtypes}"
269
269
  )
270
+ return ret(x.equals(y), "index.equals, x = {} != y = {}", [x, y])
270
271
  else:
271
272
  raise RuntimeError(
272
273
  f"Unexpected type of pandas object in _pandas_equals: type(x)={type(x)},"
@@ -13,10 +13,10 @@ from packaging.version import InvalidVersion, Version
13
13
 
14
14
  def _check_soft_dependencies(
15
15
  *packages,
16
- package_import_alias="deprecated",
17
16
  severity="error",
18
17
  obj=None,
19
18
  msg=None,
19
+ normalize_reqs=True,
20
20
  ):
21
21
  """Check if required soft dependencies are installed and raise error or warning.
22
22
 
@@ -53,6 +53,16 @@ def _check_soft_dependencies(
53
53
  msg : str, or None, default=None
54
54
  if str, will override the error message or warning shown with msg
55
55
 
56
+ normalize_reqs : bool, default=True
57
+ whether to normalize the requirement strings before checking them,
58
+ by removing build metadata from versions.
59
+ If set True, pre, post, and dev versions are removed from all version strings.
60
+
61
+ Example if True:
62
+ requirement "my_pkg==2.3.4.post1" will be normalized to "my_pkg==2.3.4";
63
+ an actual version "my_pkg==2.3.4.post1" will be considered compatible with
64
+ "my_pkg==2.3.4". If False, the this situation would raise an error.
65
+
56
66
  Raises
57
67
  ------
58
68
  InvalidRequirement
@@ -98,7 +108,8 @@ def _check_soft_dependencies(
98
108
  for package in packages:
99
109
  try:
100
110
  req = Requirement(package)
101
- req = _normalize_requirement(req)
111
+ if normalize_reqs:
112
+ req = _normalize_requirement(req)
102
113
  except InvalidRequirement:
103
114
  msg_version = (
104
115
  f"wrong format for package requirement string, "
@@ -112,6 +123,8 @@ def _check_soft_dependencies(
112
123
  package_version_req = req.specifier
113
124
 
114
125
  pkg_env_version = _get_pkg_version(package_name)
126
+ if normalize_reqs:
127
+ pkg_env_version = _normalize_version(pkg_env_version)
115
128
 
116
129
  # if package not present, make the user aware of installation reqs
117
130
  if pkg_env_version is None:
@@ -129,7 +142,7 @@ def _check_soft_dependencies(
129
142
  )
130
143
  msg = msg + (
131
144
  f"Please run: `pip install {package}` to "
132
- f"install the {package} package. "
145
+ f"install the {package!r} package. "
133
146
  )
134
147
  # if msg is not None, none of the above is executed,
135
148
  # so if msg is passed it overrides the default messages
@@ -223,7 +236,9 @@ def _get_pkg_version(package_name):
223
236
  return pkg_env_version
224
237
 
225
238
 
226
- def _check_python_version(obj, package=None, msg=None, severity="error"):
239
+ def _check_python_version(
240
+ obj, package=None, msg=None, severity="error", prereleases=True
241
+ ):
227
242
  """Check if system python version is compatible with requirements of obj.
228
243
 
229
244
  Parameters
@@ -246,6 +261,13 @@ def _check_python_version(obj, package=None, msg=None, severity="error"):
246
261
  * "none" - does not raise exception or warning
247
262
  function returns False if one of packages is not installed, otherwise True
248
263
 
264
+ prereleases: str, default = True
265
+ Whether prerelease versions are considered compatible.
266
+ If True, allows prerelease versions to be considered compatible.
267
+ If False, always considers prerelease versions as incompatible, i.e., always
268
+ raises error, warning, or returns False, if the system python version is a
269
+ prerelease.
270
+
249
271
  Returns
250
272
  -------
251
273
  compatible : bool, whether obj is compatible with system python version
@@ -263,7 +285,7 @@ def _check_python_version(obj, package=None, msg=None, severity="error"):
263
285
  return True
264
286
 
265
287
  try:
266
- est_specifier = SpecifierSet(est_specifier_tag)
288
+ est_specifier = SpecifierSet(est_specifier_tag, prereleases=prereleases)
267
289
  except InvalidSpecifier:
268
290
  msg_version = (
269
291
  f"wrong format for python_version tag, "
@@ -290,6 +312,9 @@ def _check_python_version(obj, package=None, msg=None, severity="error"):
290
312
  f" but system python version is {sys.version}."
291
313
  )
292
314
 
315
+ if "rc" in sys_version:
316
+ msg += " This is due to the release candidate status of your system Python."
317
+
293
318
  if package is not None:
294
319
  msg += (
295
320
  f" This is due to python version requirements of the {package} package."
@@ -309,7 +334,7 @@ def _check_env_marker(obj, package=None, msg=None, severity="error"):
309
334
  package : str, default = None
310
335
  if given, will be used in error message as package name
311
336
  msg : str, optional, default = default message (msg below)
312
- error message to be returned in the `ModuleNotFoundError`, overrides default
337
+ error message to be returned in the ``ModuleNotFoundError``, overrides default
313
338
 
314
339
  severity : str, "error" (default), "warning", "none"
315
340
  whether the check should raise an error, a warning, or nothing
@@ -427,13 +452,10 @@ def _check_estimator_deps(obj, msg=None, severity="error"):
427
452
  compatible = compatible and _check_env_marker(obj, severity=severity)
428
453
 
429
454
  pkg_deps = obj.get_class_tag("python_dependencies", None)
430
- pck_alias = obj.get_class_tag("python_dependencies_alias", None)
431
455
  if pkg_deps is not None and not isinstance(pkg_deps, list):
432
456
  pkg_deps = [pkg_deps]
433
457
  if pkg_deps is not None:
434
- pkg_deps_ok = _check_soft_dependencies(
435
- *pkg_deps, severity=severity, obj=obj, package_import_alias=pck_alias
436
- )
458
+ pkg_deps_ok = _check_soft_dependencies(*pkg_deps, severity=severity, obj=obj)
437
459
  compatible = compatible and pkg_deps_ok
438
460
 
439
461
  return compatible
@@ -456,12 +478,9 @@ def _normalize_requirement(req):
456
478
  # Process each specifier in the requirement
457
479
  normalized_specs = []
458
480
  for spec in req.specifier:
459
- # Parse the version and remove the build metadata
460
- spec_v = Version(spec.version)
461
- version_wo_build_metadata = f"{spec_v.major}.{spec_v.minor}.{spec_v.micro}"
462
-
463
481
  # Create a new specifier without the build metadata
464
- normalized_spec = Specifier(f"{spec.operator}{version_wo_build_metadata}")
482
+ normalized_version = _normalize_version(spec.version)
483
+ normalized_spec = Specifier(f"{spec.operator}{normalized_version}")
465
484
  normalized_specs.append(normalized_spec)
466
485
 
467
486
  # Reconstruct the specifier set
@@ -473,6 +492,29 @@ def _normalize_requirement(req):
473
492
  return normalized_req
474
493
 
475
494
 
495
+ def _normalize_version(version):
496
+ """Normalize version string by removing build metadata.
497
+
498
+ Parameters
499
+ ----------
500
+ version : packaging.version.Version
501
+ version object to normalize, e.g., Version("1.2.3+foobar")
502
+
503
+ Returns
504
+ -------
505
+ normalized_version : packaging.version.Version
506
+ normalized version object with build metadata removed, e.g., Version("1.2.3")
507
+ """
508
+ if version is None:
509
+ return None
510
+ if not isinstance(version, Version):
511
+ version_obj = Version(version)
512
+ else:
513
+ version_obj = version
514
+ normalized_version = f"{version_obj.major}.{version_obj.minor}.{version_obj.micro}"
515
+ return normalized_version
516
+
517
+
476
518
  def _raise_at_severity(
477
519
  msg,
478
520
  severity,
@@ -1,9 +1,11 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Tests for _check_soft_dependencies utility."""
3
+ from unittest.mock import patch
4
+
3
5
  import pytest
4
6
  from packaging.requirements import InvalidRequirement
5
7
 
6
- from skbase.utils.dependencies._dependencies import _check_soft_dependencies
8
+ from skbase.utils.dependencies import _check_python_version, _check_soft_dependencies
7
9
 
8
10
 
9
11
  def test_check_soft_deps():
@@ -47,3 +49,53 @@ def test_check_soft_deps():
47
49
  assert _check_soft_dependencies(
48
50
  ("pytest", "!!numpy<~><>0.1.0"), severity="none"
49
51
  )
52
+
53
+
54
+ @patch("skbase.utils.dependencies._dependencies.sys")
55
+ @pytest.mark.parametrize(
56
+ "mock_release_version, prereleases, expect_exception",
57
+ [
58
+ (True, True, False),
59
+ (True, False, True),
60
+ (False, False, False),
61
+ (False, True, False),
62
+ ],
63
+ )
64
+ def test_check_python_version(
65
+ mock_sys, mock_release_version, prereleases, expect_exception
66
+ ):
67
+ from skbase.base import BaseObject
68
+
69
+ if mock_release_version:
70
+ mock_sys.version = "3.8.1rc"
71
+ else:
72
+ mock_sys.version = "3.8.1"
73
+
74
+ class DummyObjectClass(BaseObject):
75
+ _tags = {
76
+ "python_version": ">=3.7.1", # PEP 440 version specifier, e.g., ">=3.7"
77
+ "python_dependencies": None, # PEP 440 dependency strs, e.g., "pandas>=1.0"
78
+ "env_marker": None, # PEP 508 environment marker, e.g., "os_name=='posix'"
79
+ }
80
+ """Define dummy class to test set_tags."""
81
+
82
+ dummy_object_instance = DummyObjectClass()
83
+
84
+ try:
85
+ _check_python_version(dummy_object_instance, prereleases=prereleases)
86
+ except ModuleNotFoundError as exception:
87
+ expected_msg = (
88
+ f"{type(dummy_object_instance).__name__} requires python version "
89
+ f"to be {dummy_object_instance.get_tags()['python_version']}, "
90
+ f"but system python version is {mock_sys.version}. "
91
+ "This is due to the release candidate status of your system Python."
92
+ )
93
+
94
+ if not expect_exception or exception.msg != expected_msg:
95
+ # Throw Error since exception is not expected or has not the correct message
96
+ raise AssertionError(
97
+ "ModuleNotFoundError should be NOT raised by:",
98
+ f"\n\t - mock_release_version: {mock_release_version},",
99
+ f"\n\t - prereleases: {prereleases},",
100
+ f"\nERROR MESSAGE: {exception.msg}",
101
+ ) from exception
@@ -12,7 +12,7 @@ EXAMPLES = [
12
12
  42,
13
13
  [],
14
14
  ((((())))),
15
- [([([([()])])])],
15
+ [[[[()]]]],
16
16
  3.5,
17
17
  4.2,
18
18
  ]
@@ -56,6 +56,7 @@ if _check_soft_dependencies("pandas", severity="none"):
56
56
  pd.Index([1, 2, 3]),
57
57
  pd.Index([2, 3, 4]),
58
58
  pd.Index([2, 3, 4, 6]),
59
+ pd.Index([None]),
59
60
  ]
60
61
 
61
62
  # nested DataFrame example