Skip to content

Commit 47acae9

Browse files
Merge pull request #167 from sccn/copilot/fix-d7f0a9a7-e10b-4c4d-bef8-9d03c3f086f8
Fix Python 3.10 compatibility in features module
2 parents 1514663 + 59791ec commit 47acae9

File tree

6 files changed

+81
-4
lines changed

6 files changed

+81
-4
lines changed

eegdash/features/extractors.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
from abc import ABC, abstractmethod
24
from collections.abc import Callable
35
from functools import partial

eegdash/features/feature_bank/complexity.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ def preprocess(self, x, m=2, r=0.2, l=1):
3636
counts_m = np.empty((*x.shape[:-1], (x.shape[-1] - m + 1) // l))
3737
counts_mp1 = np.empty((*x.shape[:-1], (x.shape[-1] - m) // l))
3838
for i in np.ndindex(x.shape[:-1]):
39-
counts_m[*i, :] = _channel_app_samp_entropy_counts(x[i], m, rr[i], l)
40-
counts_mp1[*i, :] = _channel_app_samp_entropy_counts(x[i], m + 1, rr[i], l)
39+
counts_m[i + (slice(None),)] = _channel_app_samp_entropy_counts(
40+
x[i], m, rr[i], l
41+
)
42+
counts_mp1[i + (slice(None),)] = _channel_app_samp_entropy_counts(
43+
x[i], m + 1, rr[i], l
44+
)
4145
return counts_m, counts_mp1
4246

4347

@@ -62,7 +66,7 @@ def complexity_sample_entropy(counts_m, counts_mp1):
6266
def complexity_svd_entropy(x, m=10, tau=1):
6367
x_emb = np.empty((*x.shape[:-1], (x.shape[-1] - m + 1) // tau, m))
6468
for i in np.ndindex(x.shape[:-1]):
65-
x_emb[*i, :, :] = _create_embedding(x[i], m, tau)
69+
x_emb[i + (slice(None), slice(None))] = _create_embedding(x[i], m, tau)
6670
s = np.linalg.svdvals(x_emb)
6771
s /= s.sum(axis=-1, keepdims=True)
6872
return -np.sum(s * np.log(s), axis=-1)

eegdash/features/feature_bank/dimensionality.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def dimensionality_higuchi_fractal_dim(x, k_max=10, eps=1e-7):
2626
for i in np.ndindex(x.shape[:-1]):
2727
for k in range(1, k_max + 1):
2828
for m in range(k):
29-
L_km[m] = np.mean(np.abs(np.diff(x[*i, m:], n=k)))
29+
L_km[m] = np.mean(np.abs(np.diff(x[i + (slice(m, None),)], n=k)))
3030
L_k[k - 1] = (N - 1) * np.sum(L_km[:k]) / (k**3)
3131
L_k = np.maximum(L_k, eps)
3232
hfd[i] = np.linalg.lstsq(log_k, np.log(L_k))[0][0]

eegdash/features/inspect.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import inspect
24
from collections.abc import Callable
35

eegdash/features/serialization.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
77
"""
88

9+
from __future__ import annotations
10+
911
from pathlib import Path
1012

1113
import pandas as pd

tests/test_features.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""Test for features module Python 3.10+ compatibility."""
2+
3+
import pytest
4+
5+
6+
def test_import_features_module():
7+
"""Test that the features module can be imported without syntax errors.
8+
9+
This test ensures Python 3.10+ compatibility by verifying that:
10+
1. Type annotations with list[], type[], and | syntax work (via __future__ imports)
11+
2. No Python 3.11+ exclusive syntax is used (like *unpacking in subscripts)
12+
"""
13+
try:
14+
import eegdash.features
15+
16+
assert eegdash.features is not None
17+
except SyntaxError as e:
18+
pytest.fail(f"SyntaxError when importing eegdash.features: {e}")
19+
except ImportError as e:
20+
pytest.fail(f"ImportError when importing eegdash.features: {e}")
21+
22+
23+
def test_import_features_submodules():
24+
"""Test that all features submodules can be imported."""
25+
submodules = [
26+
"eegdash.features.inspect",
27+
"eegdash.features.extractors",
28+
"eegdash.features.serialization",
29+
"eegdash.features.datasets",
30+
"eegdash.features.decorators",
31+
"eegdash.features.feature_bank",
32+
"eegdash.features.feature_bank.complexity",
33+
"eegdash.features.feature_bank.dimensionality",
34+
"eegdash.features.feature_bank.signal",
35+
"eegdash.features.feature_bank.spectral",
36+
"eegdash.features.feature_bank.connectivity",
37+
"eegdash.features.feature_bank.csp",
38+
]
39+
40+
for module_name in submodules:
41+
try:
42+
__import__(module_name)
43+
except SyntaxError as e:
44+
pytest.fail(f"SyntaxError when importing {module_name}: {e}")
45+
except ImportError:
46+
# Some imports might fail due to missing dependencies, that's ok
47+
# We only care about SyntaxError
48+
pass
49+
50+
51+
def test_features_basic_functionality():
52+
"""Test basic features module functionality."""
53+
from eegdash.features import (
54+
get_all_feature_extractors,
55+
get_all_feature_kinds,
56+
get_all_features,
57+
)
58+
59+
# These should return lists without errors
60+
features = get_all_features()
61+
assert isinstance(features, list)
62+
63+
extractors = get_all_feature_extractors()
64+
assert isinstance(extractors, list)
65+
66+
kinds = get_all_feature_kinds()
67+
assert isinstance(kinds, list)

0 commit comments

Comments
 (0)