11"""Tests for the new stats, multivariate, ML, DL, and validation modules."""
22# ruff: noqa: E501
3+ import sys
34import numpy as np
45import pandas as pd
56import pytest
67from sklearn .datasets import make_classification
78from sklearn .ensemble import RandomForestClassifier
89
910
11+ # ── Optional dependency availability (evaluated at collection time) ───────────
12+
13+ # torch
14+ try :
15+ import torch as _torch_check # noqa: F401
16+ _TORCH_AVAILABLE = True
17+ except ImportError :
18+ _TORCH_AVAILABLE = False
19+
20+ _skip_no_torch = pytest .mark .skipif (not _TORCH_AVAILABLE , reason = "torch not installed" )
21+
22+ # xgboost — instantiate XGBClassifier to force libxgboost.dylib load.
23+ # This catches the macOS libomp.dylib missing error at collection time
24+ # rather than letting the test crash at runtime.
25+ _XGB_SKIP_REASON : str = ""
26+ if sys .version_info >= (3 , 14 ):
27+ _XGB_AVAILABLE = False
28+ _XGB_SKIP_REASON = "xgboost 3.x segfaults under CPython 3.14 (upstream C-API incompatibility)"
29+ else :
30+ try :
31+ from xgboost import XGBClassifier as _XGBCheck
32+ _XGBCheck () # forces dylib load → catches missing libomp on macOS
33+ _XGB_AVAILABLE = True
34+ except Exception as _e :
35+ _XGB_AVAILABLE = False
36+ _XGB_SKIP_REASON = f"xgboost not available: { _e } "
37+
38+ _skip_no_xgb = pytest .mark .skipif (not _XGB_AVAILABLE , reason = _XGB_SKIP_REASON )
39+
40+
1041# ── shared fixtures ──────────────────────────────────────────────────────────
1142
1243@pytest .fixture
@@ -343,12 +374,8 @@ def test_svm_fit(self, X_y_binary):
343374 clf = MLClassifier (X , y , model = "svm" , random_state = 0 ).fit (cv = 3 )
344375 assert len (clf .predict (X )) == len (X )
345376
346- @pytest .mark .skipif (
347- __import__ ("sys" ).version_info >= (3 , 14 ),
348- reason = "xgboost 3.x segfaults under CPython 3.14 (upstream C-API incompatibility)" ,
349- )
377+ @_skip_no_xgb
350378 def test_xgb_fit (self , X_y_binary ):
351- pytest .importorskip ("xgboost" , reason = "xgboost not installed" )
352379 from metbit .ml .classifiers import MLClassifier
353380 X , y = X_y_binary
354381 clf = MLClassifier (X , y , model = "xgb" , random_state = 0 ).fit (cv = 3 )
@@ -363,15 +390,6 @@ def test_elasticnet_fit(self, X_y_binary):
363390
364391# ── dl/models ────────────────────────────────────────────────────────────────
365392
366- try :
367- import torch as _torch_check # noqa: F401
368- _TORCH_AVAILABLE = True
369- except ImportError :
370- _TORCH_AVAILABLE = False
371-
372- _skip_no_torch = pytest .mark .skipif (not _TORCH_AVAILABLE , reason = "torch not installed" )
373-
374-
375393@_skip_no_torch
376394class TestSpectralAutoencoder :
377395 def test_fit_encode (self , X_y_binary ):
0 commit comments