From 1001426fb05a9b9aa0f52750e6da96405934f260 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Oct 2025 00:59:08 +0000 Subject: [PATCH 1/5] Initial plan From 3b1c1a51ec63784eed0ffc6d3f3f103c57e09e69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Oct 2025 01:12:25 +0000 Subject: [PATCH 2/5] Fix Python 3.10 compatibility by adding future annotations imports Co-authored-by: bruAristimunha <42702466+bruAristimunha@users.noreply.github.com> --- eegdash/features/extractors.py | 2 ++ eegdash/features/inspect.py | 2 ++ eegdash/features/serialization.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/eegdash/features/extractors.py b/eegdash/features/extractors.py index 5df28663..c0bb0759 100644 --- a/eegdash/features/extractors.py +++ b/eegdash/features/extractors.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABC, abstractmethod from collections.abc import Callable from functools import partial diff --git a/eegdash/features/inspect.py b/eegdash/features/inspect.py index 8e379b58..395a3326 100644 --- a/eegdash/features/inspect.py +++ b/eegdash/features/inspect.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import inspect from collections.abc import Callable diff --git a/eegdash/features/serialization.py b/eegdash/features/serialization.py index 5aeb1787..75ebdc34 100644 --- a/eegdash/features/serialization.py +++ b/eegdash/features/serialization.py @@ -6,6 +6,8 @@ """ +from __future__ import annotations + from pathlib import Path import pandas as pd From d9ccd27080a3849b68f6f6c44b981dbf8e9833bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Oct 2025 02:22:57 +0000 Subject: [PATCH 3/5] Fix Python 3.10 compatibility: replace *unpacking in subscripts Co-authored-by: bruAristimunha <42702466+bruAristimunha@users.noreply.github.com> --- eegdash/features/feature_bank/complexity.py | 6 +- .../features/feature_bank/dimensionality.py | 2 +- tests/test_features.py | 66 +++++++++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 tests/test_features.py diff --git a/eegdash/features/feature_bank/complexity.py b/eegdash/features/feature_bank/complexity.py index bd29f4c6..f85c5669 100644 --- a/eegdash/features/feature_bank/complexity.py +++ b/eegdash/features/feature_bank/complexity.py @@ -36,8 +36,8 @@ def preprocess(self, x, m=2, r=0.2, l=1): counts_m = np.empty((*x.shape[:-1], (x.shape[-1] - m + 1) // l)) counts_mp1 = np.empty((*x.shape[:-1], (x.shape[-1] - m) // l)) for i in np.ndindex(x.shape[:-1]): - counts_m[*i, :] = _channel_app_samp_entropy_counts(x[i], m, rr[i], l) - counts_mp1[*i, :] = _channel_app_samp_entropy_counts(x[i], m + 1, rr[i], l) + counts_m[i + (slice(None),)] = _channel_app_samp_entropy_counts(x[i], m, rr[i], l) + counts_mp1[i + (slice(None),)] = _channel_app_samp_entropy_counts(x[i], m + 1, rr[i], l) return counts_m, counts_mp1 @@ -62,7 +62,7 @@ def complexity_sample_entropy(counts_m, counts_mp1): def complexity_svd_entropy(x, m=10, tau=1): x_emb = np.empty((*x.shape[:-1], (x.shape[-1] - m + 1) // tau, m)) for i in np.ndindex(x.shape[:-1]): - x_emb[*i, :, :] = _create_embedding(x[i], m, tau) + x_emb[i + (slice(None), slice(None))] = _create_embedding(x[i], m, tau) s = np.linalg.svdvals(x_emb) s /= s.sum(axis=-1, keepdims=True) return -np.sum(s * np.log(s), axis=-1) diff --git a/eegdash/features/feature_bank/dimensionality.py b/eegdash/features/feature_bank/dimensionality.py index 0e8a535a..336744e4 100644 --- a/eegdash/features/feature_bank/dimensionality.py +++ b/eegdash/features/feature_bank/dimensionality.py @@ -26,7 +26,7 @@ def dimensionality_higuchi_fractal_dim(x, k_max=10, eps=1e-7): for i in np.ndindex(x.shape[:-1]): for k in range(1, k_max + 1): for m in range(k): - L_km[m] = np.mean(np.abs(np.diff(x[*i, m:], n=k))) + L_km[m] = np.mean(np.abs(np.diff(x[i + (slice(m, None),)], n=k))) L_k[k - 1] = (N - 1) * np.sum(L_km[:k]) / (k**3) L_k = np.maximum(L_k, eps) hfd[i] = np.linalg.lstsq(log_k, np.log(L_k))[0][0] diff --git a/tests/test_features.py b/tests/test_features.py new file mode 100644 index 00000000..67c4c537 --- /dev/null +++ b/tests/test_features.py @@ -0,0 +1,66 @@ +"""Test for features module Python 3.10+ compatibility.""" + +import pytest + + +def test_import_features_module(): + """Test that the features module can be imported without syntax errors. + + This test ensures Python 3.10+ compatibility by verifying that: + 1. Type annotations with list[], type[], and | syntax work (via __future__ imports) + 2. No Python 3.11+ exclusive syntax is used (like *unpacking in subscripts) + """ + try: + import eegdash.features + assert eegdash.features is not None + except SyntaxError as e: + pytest.fail(f"SyntaxError when importing eegdash.features: {e}") + except ImportError as e: + pytest.fail(f"ImportError when importing eegdash.features: {e}") + + +def test_import_features_submodules(): + """Test that all features submodules can be imported.""" + submodules = [ + "eegdash.features.inspect", + "eegdash.features.extractors", + "eegdash.features.serialization", + "eegdash.features.datasets", + "eegdash.features.decorators", + "eegdash.features.feature_bank", + "eegdash.features.feature_bank.complexity", + "eegdash.features.feature_bank.dimensionality", + "eegdash.features.feature_bank.signal", + "eegdash.features.feature_bank.spectral", + "eegdash.features.feature_bank.connectivity", + "eegdash.features.feature_bank.csp", + ] + + for module_name in submodules: + try: + __import__(module_name) + except SyntaxError as e: + pytest.fail(f"SyntaxError when importing {module_name}: {e}") + except ImportError as e: + # Some imports might fail due to missing dependencies, that's ok + # We only care about SyntaxError + pass + + +def test_features_basic_functionality(): + """Test basic features module functionality.""" + from eegdash.features import ( + get_all_features, + get_all_feature_extractors, + get_all_feature_kinds, + ) + + # These should return lists without errors + features = get_all_features() + assert isinstance(features, list) + + extractors = get_all_feature_extractors() + assert isinstance(extractors, list) + + kinds = get_all_feature_kinds() + assert isinstance(kinds, list) From 689e820260014574c96ca688f042cc2c7c3757ca Mon Sep 17 00:00:00 2001 From: bruAristimunha Date: Sun, 5 Oct 2025 19:03:29 +0200 Subject: [PATCH 4/5] pre-commit --- tests/test_features.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/test_features.py b/tests/test_features.py index 67c4c537..8a721ba1 100644 --- a/tests/test_features.py +++ b/tests/test_features.py @@ -5,13 +5,14 @@ def test_import_features_module(): """Test that the features module can be imported without syntax errors. - + This test ensures Python 3.10+ compatibility by verifying that: 1. Type annotations with list[], type[], and | syntax work (via __future__ imports) 2. No Python 3.11+ exclusive syntax is used (like *unpacking in subscripts) """ try: import eegdash.features + assert eegdash.features is not None except SyntaxError as e: pytest.fail(f"SyntaxError when importing eegdash.features: {e}") @@ -35,13 +36,13 @@ def test_import_features_submodules(): "eegdash.features.feature_bank.connectivity", "eegdash.features.feature_bank.csp", ] - + for module_name in submodules: try: __import__(module_name) except SyntaxError as e: pytest.fail(f"SyntaxError when importing {module_name}: {e}") - except ImportError as e: + except ImportError: # Some imports might fail due to missing dependencies, that's ok # We only care about SyntaxError pass @@ -50,17 +51,17 @@ def test_import_features_submodules(): def test_features_basic_functionality(): """Test basic features module functionality.""" from eegdash.features import ( - get_all_features, get_all_feature_extractors, get_all_feature_kinds, + get_all_features, ) - + # These should return lists without errors features = get_all_features() assert isinstance(features, list) - + extractors = get_all_feature_extractors() assert isinstance(extractors, list) - + kinds = get_all_feature_kinds() assert isinstance(kinds, list) From 59791eccc109fbf0e834ed68fe0a1307a98e4e72 Mon Sep 17 00:00:00 2001 From: bruAristimunha Date: Sun, 5 Oct 2025 19:05:35 +0200 Subject: [PATCH 5/5] pre-commit --- eegdash/features/feature_bank/complexity.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/eegdash/features/feature_bank/complexity.py b/eegdash/features/feature_bank/complexity.py index f85c5669..3aeef2fb 100644 --- a/eegdash/features/feature_bank/complexity.py +++ b/eegdash/features/feature_bank/complexity.py @@ -36,8 +36,12 @@ def preprocess(self, x, m=2, r=0.2, l=1): counts_m = np.empty((*x.shape[:-1], (x.shape[-1] - m + 1) // l)) counts_mp1 = np.empty((*x.shape[:-1], (x.shape[-1] - m) // l)) for i in np.ndindex(x.shape[:-1]): - counts_m[i + (slice(None),)] = _channel_app_samp_entropy_counts(x[i], m, rr[i], l) - counts_mp1[i + (slice(None),)] = _channel_app_samp_entropy_counts(x[i], m + 1, rr[i], l) + counts_m[i + (slice(None),)] = _channel_app_samp_entropy_counts( + x[i], m, rr[i], l + ) + counts_mp1[i + (slice(None),)] = _channel_app_samp_entropy_counts( + x[i], m + 1, rr[i], l + ) return counts_m, counts_mp1