Skip to content

Commit 7269f19

Browse files
mvdocclaude
andauthored
Move dev/unused dependencies to optional extras (#4)
* Move dev/unused dependencies to optional extras Remove ruff, pytest, pydeface, jupyterlab, ipywidgets, h5py, pillow, and lxml from runtime dependencies. None are imported by the package source code. ruff and pytest are moved to [dev] optional extras with correct version pins. The rest are dropped entirely. Closes #3 (partially) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix typo, remove dead code, add node_modules to gitignore - Fix "fmriprepp" -> "fmriprep" typo in utils.py docstrings - Remove unreachable Python 3.8 fallback in io.py (min is 3.9) - Add node_modules/ to .gitignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add minimal CI workflow Runs lint (ruff) and tests (pytest) on Python 3.9-3.12 via GitHub Actions on pushes and PRs to main/master. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix lint errors in QA scripts - Remove unused loop variables (B007) - Remove unused local variable (F841) - Fix line-too-long errors (E501) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Drop Python 3.9 support (EOL), require >=3.10 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add strict= to zip() calls and update uv.lock for Python >=3.10 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix remaining zip() strict= lint errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Use strict=True in zip() calls to catch length mismatches Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ae8fe0d commit 7269f19

13 files changed

Lines changed: 147 additions & 3225 deletions

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.10", "3.11", "3.12"]
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- uses: astral-sh/setup-uv@v4
19+
with:
20+
version: "latest"
21+
22+
- name: Set up Python ${{ matrix.python-version }}
23+
run: uv python install ${{ matrix.python-version }}
24+
25+
- name: Install dependencies
26+
run: uv sync --extra dev
27+
28+
- name: Lint
29+
run: uv run ruff check src/ scripts/qa/
30+
31+
- name: Test
32+
run: uv run pytest tests/ -q

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,6 @@ private
7272

7373
# NFS hanging stuff
7474
.nfs*
75+
76+
# Node
77+
node_modules/

pyproject.toml

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,12 @@ authors = [
1313
]
1414
readme = "README.md"
1515
license = {file = "LICENSE"}
16-
requires-python = ">=3.9"
16+
requires-python = ">=3.10"
1717
classifiers = [
1818
"Development Status :: 3 - Alpha",
1919
"Intended Audience :: Science/Research",
2020
"License :: OSI Approved :: BSD License",
2121
"Programming Language :: Python :: 3",
22-
"Programming Language :: Python :: 3.9",
2322
"Programming Language :: Python :: 3.10",
2423
"Programming Language :: Python :: 3.11",
2524
"Programming Language :: Python :: 3.12",
@@ -34,26 +33,18 @@ dependencies = [
3433
"nibabel>=3.0.0",
3534
"nilearn>=0.6.0",
3635
"scikit-learn>=0.22.0",
37-
"jupyterlab>=3.0.0",
38-
"ipywidgets>=7.5.0",
39-
"h5py>=2.10.0",
4036
"joblib>=1.0.0",
41-
"pillow>=7.0.0",
42-
"lxml>=4.5.0",
43-
"ruff>=0.12.12",
4437
"tqdm>=4.67.1",
4538
"jinja2>=3.0.0",
4639
"pyyaml>=6.0",
4740
"pybids>=0.16.0",
4841
"pycortex>=1.0.0",
49-
"pytest>=8.4.2",
50-
"pydeface>=2.0.2",
5142
]
5243

5344
[project.optional-dependencies]
5445
dev = [
55-
"pytest>=6.0",
56-
"ruff>=0.1.0",
46+
"pytest>=8.4.2",
47+
"ruff>=0.12.12",
5748
]
5849

5950
[project.urls]
@@ -71,7 +62,7 @@ hyperface = ["assets/*.yaml", "templates/*.html"]
7162

7263
[tool.ruff]
7364
line-length = 88
74-
target-version = "py39"
65+
target-version = "py310"
7566
# Exclude archival code that should not be modified
7667
exclude = [
7768
"scripts/presentation/**", # Legacy Python 2 stimulus presentation code

scripts/qa/print-tsnr-summary.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,18 @@ def format_task_summary(task_stats: dict) -> str:
8585
f" Number of subjects: {n_subjects}",
8686
"",
8787
" tSNR (temporal signal-to-noise ratio):",
88-
f" Mean across subjects: {np.mean(mean_tsnrs):.1f} ± {np.std(mean_tsnrs):.1f}",
88+
f" Mean across subjects: {np.mean(mean_tsnrs):.1f}"
89+
f" ± {np.std(mean_tsnrs):.1f}",
8990
f" Median across subjects: {np.median(mean_tsnrs):.1f}",
9091
f" Min: {np.min(mean_tsnrs):.1f}",
9192
f" Max: {np.max(mean_tsnrs):.1f}",
9293
"",
9394
" Paper-ready text:",
94-
f" The mean tSNR across subjects was {np.mean(mean_tsnrs):.1f} ± {np.std(mean_tsnrs):.1f} "
95-
f"(median {np.median(mean_tsnrs):.1f}, min {np.min(mean_tsnrs):.1f}, max {np.max(mean_tsnrs):.1f}).",
95+
f" The mean tSNR across subjects was "
96+
f"{np.mean(mean_tsnrs):.1f} ± {np.std(mean_tsnrs):.1f} "
97+
f"(median {np.median(mean_tsnrs):.1f}, "
98+
f"min {np.min(mean_tsnrs):.1f}, "
99+
f"max {np.max(mean_tsnrs):.1f}).",
96100
"",
97101
]
98102

scripts/qa/qa-plot-accuracy.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def plot_accuracy_figure(
134134

135135
# Bar plot for mean accuracy
136136
x_positions = np.arange(len(subjects))
137-
bars = ax.bar(
137+
ax.bar(
138138
x_positions,
139139
mean_accuracies,
140140
color=PRIMARY_COLOR,
@@ -145,7 +145,7 @@ def plot_accuracy_figure(
145145
)
146146

147147
# Scatter plot for individual run values
148-
for i, (x_pos, values) in enumerate(zip(x_positions, all_run_values)):
148+
for x_pos, values in zip(x_positions, all_run_values, strict=True):
149149
jitter = np.random.uniform(-0.15, 0.15, len(values))
150150
ax.scatter(
151151
[x_pos + j for j in jitter],
@@ -205,7 +205,7 @@ def format_accuracy_summary(accuracy_data: dict[str, dict[str, int]]) -> str:
205205
subject_means = []
206206
perfect_subjects = 0
207207

208-
for subject, runs in accuracy_data.items():
208+
for _subject, runs in accuracy_data.items():
209209
values = list(runs.values())
210210
all_values.extend(values)
211211
subject_means.append(np.mean(values))
@@ -247,7 +247,8 @@ def format_accuracy_summary(accuracy_data: dict[str, dict[str, int]]) -> str:
247247
"",
248248
"Paper-ready text:",
249249
f" Participants achieved a mean accuracy of {np.mean(subject_means):.1f}% "
250-
f"(median {np.median(subject_means):.1f}%, min {np.min(subject_means):.1f}%, "
250+
f"(median {np.median(subject_means):.1f}%, "
251+
f"min {np.min(subject_means):.1f}%, "
251252
f"max {np.max(subject_means):.1f}%) on the visual memory task. "
252253
f"{perfect_subjects} out of {n_subjects} participants achieved "
253254
f"100% accuracy across all runs.",

scripts/qa/qa-plot-isc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def print_summary_stats(isc_data: list[np.ndarray], subject_ids: list[str]) -> N
101101
print(header)
102102
print("-" * 60)
103103

104-
for subject_id, isc in zip(subject_ids, isc_data):
104+
for subject_id, isc in zip(subject_ids, isc_data, strict=True):
105105
valid = isc[np.isfinite(isc)]
106106
vals = [valid.mean(), np.median(valid), valid.std(), valid.min(), valid.max()]
107107
row = f"{subject_id:<15} " + " ".join(f"{v:>8.4f}" for v in vals)
@@ -173,7 +173,7 @@ def main():
173173

174174
# Create individual subject surface plots
175175
print("\nCreating individual subject surface plots...")
176-
for subject_id, isc in zip(subject_ids, isc_data):
176+
for subject_id, isc in zip(subject_ids, isc_data, strict=True):
177177
subject_path = figures_dir / f"{subject_id}_desc-isc_{plot_type}.png"
178178
create_fsaverage6_plot(
179179
isc,

scripts/qa/qa-plot-motion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ def create_fd_violin_plots_by_session(
219219
print(" Creating FD violin plots by session...")
220220

221221
session_data: dict[str | None, list[str]] = {}
222-
for confounds_file, session in zip(confounds_files, sessions):
222+
for confounds_file, session in zip(confounds_files, sessions, strict=True):
223223
session_data.setdefault(session, []).append(confounds_file)
224224

225225
for session, session_files in session_data.items():

scripts/qa/qa-plot-participant-datasets.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ def main():
2929

3030
# Create binary matrix
3131
n_participants = len(df)
32-
datasets = ["hyperface", "budapest", "identity_decoding"]
3332
matrix = np.zeros((n_participants, 3), dtype=int)
3433

3534
# All participants have hyperface

scripts/qa/qa-save-isc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def load_and_process_subject(
6565
events = load_events(subject, task="visualmemory", data_dir=data_dir)
6666

6767
processed_runs = []
68-
for data, evt in zip(responses, events):
68+
for data, evt in zip(responses, events, strict=True):
6969
n_trs = data.shape[0]
7070
clip_mask = get_clip_tr_mask(evt, n_trs, tr=tr)
7171
data_clips = data[clip_mask]
@@ -111,7 +111,7 @@ def main():
111111
n_trs_list = [d.shape[0] for d in subjects_data]
112112
if len(set(n_trs_list)) > 1:
113113
print(f"\nERROR: Inconsistent TR counts: {set(n_trs_list)}")
114-
for subj_id, n_trs in zip(subject_ids, n_trs_list):
114+
for subj_id, n_trs in zip(subject_ids, n_trs_list, strict=True):
115115
print(f" {subj_id}: {n_trs}")
116116
return 1
117117

scripts/qa/stimuli/plot-behavioral-ratings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def plot_factor_proportions(df, output_path):
9090

9191
total_n = len(df)
9292

93-
for ax, (factor, config) in zip(axes, FACTOR_CONFIG.items()):
93+
for ax, (factor, config) in zip(axes, FACTOR_CONFIG.items(), strict=True):
9494
# Get counts in specified order
9595
counts = df[factor].value_counts()
9696
order = [lvl for lvl in config["order"] if lvl in counts.index]
@@ -106,7 +106,7 @@ def plot_factor_proportions(df, output_path):
106106
)
107107

108108
# Add percentage labels above bars
109-
for i, (count, bar) in enumerate(zip(counts.values, bars)):
109+
for count, bar in zip(counts.values, bars, strict=True):
110110
pct = 100 * count / total_n
111111
ax.text(
112112
bar.get_x() + bar.get_width() / 2,

0 commit comments

Comments
 (0)