diff --git a/.context/research.md b/.context/research.md index 0f8b1067..a56d5bed 100644 --- a/.context/research.md +++ b/.context/research.md @@ -15,7 +15,7 @@ Track technical solutions, explored approaches, and references during developmen ## References -- [HED Specification](https://hed-specification.readthedocs.io) +- [HED Specification](https://www.hedtags.org/hed-specification) - [HED Python API Docs](https://www.hedtags.org/hed-python/) -- [HED Resources](https://www.hed-resources.org) +- [HED Resources](https://www.hedtags.org/hed-resources) - [BIDS Specification](https://bids-specification.readthedocs.io) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index bc044a83..917e9d69 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -50,7 +50,7 @@ jobs: 3. For suggestions: only mention if they clearly improve code quality Follow the project's code style: ruff formatting, Google-style docstrings, - 127-char line length, PEP 8. Check for correctness, test coverage, and + 120-char line length, PEP 8. Check for correctness, test coverage, and backward compatibility. Do not flag stylistic preferences or false positives. diff --git a/.github/workflows/links.yaml b/.github/workflows/links.yaml index 119631b1..529ca6ca 100644 --- a/.github/workflows/links.yaml +++ b/.github/workflows/links.yaml @@ -38,10 +38,9 @@ jobs: run: | sphinx-build -b html docs docs/_build/html - - name: Link Checker on built documentation + - name: Link Checker id: lychee uses: lycheeverse/lychee-action@v2.8.0 with: - # Check the built HTML files recursively (includes internal links) - args: --config lychee.toml --verbose --no-progress --max-redirects 10 'docs/_build/html/**/*.html' + args: --config lychee.toml --verbose --no-progress --max-redirects 10 'docs/_build/html/**/*.html' README.md RELEASE_GUIDE.md CONTRIBUTING.md fail: true diff --git a/.github/workflows/codespell.yaml b/.github/workflows/typos.yaml similarity index 80% rename from .github/workflows/codespell.yaml rename to .github/workflows/typos.yaml index d1f24dc6..f77f6b38 100644 --- a/.github/workflows/codespell.yaml +++ b/.github/workflows/typos.yaml @@ -1,5 +1,5 @@ --- -name: Codespell +name: Typos on: push: @@ -11,7 +11,7 @@ permissions: contents: read jobs: - codespell: + typos: name: Check for spelling errors runs-on: ubuntu-latest @@ -25,5 +25,5 @@ jobs: enable-cache: true cache-dependency-glob: "**/pyproject.toml" - - name: Run Codespell - run: uvx --with tomli codespell . + - name: Run typos + run: uvx typos diff --git a/.gitignore b/.gitignore index faa6ebb3..3e0e57e9 100644 --- a/.gitignore +++ b/.gitignore @@ -137,7 +137,6 @@ Desktop.ini # Project specific schema_cache_test/ hed_cache/ -spec_tests/hed-specification/tests spec_tests/*.json # GitHub Copilot instructions (project-specific) diff --git a/.rules/ci_cd.md b/.rules/ci_cd.md index 03930c04..189d0fdd 100644 --- a/.rules/ci_cd.md +++ b/.rules/ci_cd.md @@ -14,12 +14,11 @@ ### Code Quality -| Workflow | Triggers | Python | Description | -| ---------------- | ------------------------ | ------ | -------------------------------------------- | -| `ruff.yaml` | Push + PR to main | 3.12 | Ruff linter (>=0.8.0) | -| `black.yaml` | Push + PR (all branches) | 3.12 | Black formatter check (>=24.0.0) | -| `codespell.yaml` | Push + PR to main | 3.10 | Spelling check | -| `mdformat.yaml` | Push + PR to main | 3.12 | Markdown format check (docs/ and root \*.md) | +| Workflow | Triggers | Python | Description | +| --------------- | ----------------- | ------ | -------------------------------------------- | +| `ruff.yaml` | Push + PR to main | 3.12 | Ruff linter and formatter check (>=0.8.0) | +| `typos.yaml` | Push + PR to main | 3.10 | Spelling check (typos) | +| `mdformat.yaml` | Push + PR to main | 3.12 | Markdown format check (docs/ and root \*.md) | ### Documentation @@ -36,6 +35,6 @@ ## All CI Must Pass Before Merge - Tests across Python 3.10-3.14 -- Linting (ruff), formatting (black), spelling (codespell), markdown (mdformat) +- Linting and formatting (ruff check + ruff format), spelling (typos), markdown (mdformat) - Documentation builds successfully - Package installs correctly in fresh environment diff --git a/.rules/code_review.md b/.rules/code_review.md index 24be6968..849a2f23 100644 --- a/.rules/code_review.md +++ b/.rules/code_review.md @@ -2,10 +2,10 @@ ## Before Creating a PR -1. All tests pass: `pytest tests/ --cov` and `pytest spec_tests/` -2. Linting clean: `ruff check hed/ tests/` -3. Formatting clean: `black --check hed/ tests/` -4. Spelling clean: `codespell` +1. All tests pass: `python -m unittest discover tests -v` and `python -m unittest discover spec_tests -v` +2. Linting clean: `ruff check . --fix` +3. Formatting clean: `ruff format --check .` +4. Spelling clean: `typos` 5. New functionality has tests (real data, no mocks) ## PR Review with pr-review-toolkit diff --git a/.rules/git.md b/.rules/git.md index 706d435a..404ccb90 100644 --- a/.rules/git.md +++ b/.rules/git.md @@ -3,17 +3,17 @@ ## Branch Strategy - `stable`: Released versions on PyPI -- `main`/`master`: Most recent usable version -- `develop`: Experimental, evolving features +- `main`: Most recent usable version on `hed-standard/hed-python` - Feature branches: `feature/-short-description` -- PRs target `develop` branch +- Development happens on a personal fork of `hed-standard/hed-python` +- Push feature branches to the forked repository, then open a PR targeting `main` on `hed-standard/hed-python` ## Commits - Atomic, focused changes - Messages \<50 chars, no emojis - No AI attribution in commits or PRs -- Pre-commit hook runs ruff check + format on staged .py files +- Precommit: Should run ruff check and format and mdformat locally before to eliminate format errors ## Submodules @@ -26,20 +26,21 @@ Three submodules under `spec_tests/`, all tracking `main`: ## PR Process -1. Create issue (except for minor fixes) -2. Use `gh issue develop` to create branch from `develop` -3. Implement with atomic commits -4. Run tests: `pytest tests/ --cov` and `pytest spec_tests/` -5. Run linting: `ruff check hed/ tests/` and `black --check hed/ tests/` -6. Create PR targeting `develop` -7. Run `/review-pr` for code review -8. Address all critical/important findings -9. Merge when all CI (12 workflows) is green +01. Create issue on `hed-standard/hed-python` (except for minor fixes) +02. Create a feature branch on your fork: `git checkout -b feature/-short-description` +03. Implement with atomic commits +04. Run tests: `python -m unittest discover tests -v` and `python -m unittest discover spec_tests -v` +05. Run linting and formatting check: `ruff check hed/ tests/` and `ruff format --check hed/ tests/` +06. Push the feature branch to your fork: `git push origin feature/-short-description` +07. Open a PR from your fork's feature branch targeting `main` on `hed-standard/hed-python` +08. Run `/review-pr` for code review +09. Address all critical/important findings +10. Merge by a maintainer ## Release Process (see RELEASE_GUIDE.md) 1. Update CHANGELOG.md -2. Run code quality checks (black, ruff, codespell) +2. Run code quality checks (ruff check, ruff format, typos) 3. Run all tests (unit + spec) 4. Push CHANGELOG PR, merge to main 5. Create git tag (semantic versioning: MAJOR.MINOR.PATCH) diff --git a/.rules/python.md b/.rules/python.md index 846c20c6..9c16dc3d 100644 --- a/.rules/python.md +++ b/.rules/python.md @@ -2,7 +2,7 @@ ## Style -- PEP 8 compliant with 127-char line length (configured in pyproject.toml) +- PEP 8 compliant with 120-char line length (configured in pyproject.toml) - Google-style docstrings for all public classes and functions - Type hints where appropriate @@ -13,8 +13,8 @@ - Max McCabe complexity: 10 - Excludes: .git, .venv, __pycache__, build, dist, hed/\_version.py, spec_tests submodules - Per-file: F401 ignored in `__init__.py` (unused imports are re-exports) -- **black:** Format (`black hed/ tests/`); 127-char line, targets py310-py314 -- **codespell:** Spell checking; skips binary/data files, ignores domain-specific words (hed, parms, etc.) +- **ruff format:** Format (`ruff format hed/ tests/`); 120-char line, matches ruff lint config +- **typos:** Spell checking; skips binary/data files, ignores domain-specific words (hed, parms, etc.) ## Naming Conventions @@ -33,7 +33,7 @@ ## Dependencies - **Core:** click, click-option-group, pandas (\<3.0), numpy (>=2.0.2), openpyxl, defusedxml, inflect, semantic-version, portalocker -- **Dev:** ruff (>=0.8.0), codespell, black[jupyter], mdformat, mdformat-myst +- **Dev:** ruff (>=0.8.0), typos (>=1.29.0), mdformat, mdformat-myst - **Docs:** sphinx (\<10), furo, sphinx-copybutton, myst-parser, sphinx-autodoc-typehints, linkify-it-py - **Test:** coverage - **Examples:** jupyter, notebook, nbformat, nbconvert, ipykernel diff --git a/.rules/testing.md b/.rules/testing.md index 46c86831..903c1e47 100644 --- a/.rules/testing.md +++ b/.rules/testing.md @@ -2,11 +2,10 @@ ## Framework -- **unittest** is the project standard for writing tests -- **pytest** as the test runner (supports unittest natively) +- **unittest** is the project standard for writing and running tests - Test files mirror `hed/` package structure under `tests/` - Test file naming: `test_.py` -- No conftest.py or pytest fixtures; uses unittest's `setUp`/`setUpClass` +- Uses unittest's `setUp`/`setUpClass` (no pytest fixtures or conftest.py) ## Test Organization (60 test files) @@ -42,13 +41,10 @@ tests/ ```bash # All unit tests -pytest tests/ --cov - -# Unittest discovery (alternative) -python -m unittest discover tests +python -m unittest discover tests -v # Spec tests (require submodule init) -pytest spec_tests/ +python -m unittest discover spec_tests -v # Specific test file python -m unittest tests/models/test_hed_string.py @@ -57,7 +53,7 @@ python -m unittest tests/models/test_hed_string.py python -m unittest tests.models.test_hed_string.TestHedString.test_constructor # Notebook tests -pytest tests/test_notebooks.py +python -m unittest tests.test_notebooks ``` ## Test Patterns diff --git a/CHANGELOG.md b/CHANGELOG.md index 759c779a..b4c6a47f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,7 +121,7 @@ The main purpose of this release is to clean up the CLI for the hedtools and to - `hed/scripts/extract_tabular_summary.py` - Non-BIDS tabular summarization - `hed/scripts/script_utils.py` - Shared utility functions for scripts -**Script uilities:** +**Script utilities:** - Centralized logging setup across all scripts - Standardized validation result formatting diff --git a/CLAUDE.md b/CLAUDE.md index b37a3693..002dc037 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,23 +37,22 @@ examples/ - 10 Jupyter notebooks (validation, extraction, summarization wor ```bash # Tests -pytest tests/ --cov -python -m unittest discover tests +python -m unittest discover tests -v # Spec tests (require submodule init) -pytest spec_tests/ +python -m unittest discover spec_tests -v # Linting and formatting ruff check --fix --unsafe-fixes hed/ tests/ -black hed/ tests/ -codespell +ruff format hed/ tests/ +typos # Documentation cd docs && sphinx-build -b html . _build/html # CLI hedpy --help -hedpy validate string --hed-string "Event" --schema-version 8.3.0 +hedpy validate string --hed-string "Event" --schema-version 8.4.0 hedpy validate bids-dataset --bids-path /path/to/bids hedpy schema convert --input schema.xml --output schema.json ``` @@ -62,16 +61,16 @@ hedpy schema convert --input schema.xml --output schema.json ### Testing: Real Data Only -- unittest framework (project standard); pytest as runner +- unittest framework (project standard) for both writing and running tests - No mocks; real schemas, real HED strings, real files - Spec tests via git submodules for official HED test suites - Details: `.rules/testing.md` ### Code Style -- PEP 8; 127-char line length +- PEP 8; 120-char line length - Google-style docstrings for public APIs -- ruff (E, W, F, N, B, C4 rules), black for formatting, codespell for spelling +- ruff (E, W, F, N, B, C4 rules + ruff format for formatting), typos for spelling - Details: `.rules/python.md` ### Git and PRs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 430700a2..afb6a448 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -93,20 +93,17 @@ This project adheres to a code of conduct that we expect all contributors to fol We use several tools to maintain code quality: -- **black:** For automatic code formatting +- **ruff format:** For automatic code formatting ```bash # Check if code is formatted correctly - black --check . + ruff format --check . # Automatically format all code - black . + ruff format . # Format specific files or directories - black hed/ tests/ - - # Windows: Use --workers 1 if you encounter file I/O errors - black --workers 1 . + ruff format hed/ tests/ ``` - **ruff:** For linting, style checking, and import sorting @@ -121,10 +118,10 @@ We use several tools to maintain code quality: ruff check --fix hed/ tests/ ``` -- **codespell:** For spell checking +- **typos:** For spell checking ```bash - codespell + typos ``` ### Documentation Style diff --git a/README.md b/README.md index 53bf532e..a5163bf8 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ cd hed-python pip install -e . # Install with optional dependency groups -pip install -e ".[dev]" # Development tools (ruff, black, codespell) +pip install -e ".[dev]" # Development tools (ruff, typos) pip install -e ".[docs]" # Documentation tools (sphinx, furo) pip install -e ".[test]" # Testing tools (coverage) pip install -e ".[examples]" # Jupyter notebook support @@ -72,7 +72,7 @@ pip install -e ".[dev,docs,test,examples]" **Optional dependency groups:** -- `dev` - Code quality tools: ruff (linter), black (formatter), codespell, mdformat +- `dev` - Code quality tools: ruff (linter + formatter), typos, mdformat - `docs` - Documentation generation: sphinx, furo theme, myst-parser - `test` - Code coverage reporting: coverage - `examples` - Jupyter notebook support: jupyter, notebook, ipykernel @@ -180,39 +180,32 @@ cd docs sphinx-build -b html . _build/html ``` -To iew the built documentation open `docs/_build/html/index.html` in your browser +To view the built documentation open `docs/_build/html/index.html` in your browser -### Formatting with Black +### Code Formatting -This project uses [Black](https://black.readthedocs.io/) for consistent code formatting. +This project uses [ruff format](https://docs.astral.sh/ruff/formatter/) for consistent code formatting. ```bash # Check if code is properly formatted (without making changes) -black --check . +ruff format --check . # Check and show what would change -black --check --diff . +ruff format --check --diff . # Format all Python code in the repository -black . +ruff format . # Format specific files or directories -black hed/ -black tests/ +ruff format hed/ +ruff format tests/ ``` -**Windows Users:** If you encounter "I/O operation on closed file" errors, use the `--workers 1` flag: +**Configuration:** Formatter settings are in `pyproject.toml` under `[tool.ruff.format]` with a line length of 120 characters. -```bash -black --workers 1 --check . -black --workers 1 . -``` - -**Configuration:** Black settings are in `pyproject.toml` with a line length of 127 characters (matching our ruff configuration). +**Exclusions:** Ruff automatically excludes `.venv/`, `__pycache__/`, auto-generated files (`hed/_version.py`), and external repos (`spec_tests/hed-examples/`, `spec_tests/hed-schemas/`). -**Exclusions:** Black automatically excludes `.venv/`, `__pycache__/`, auto-generated files (`hed/_version.py`), and external repos (`spec_tests/hed-examples/`, `spec_tests/hed-specification/`). - -**CI integration:** All code is automatically checked for Black formatting in GitHub Actions. Run `black .` before committing to ensure your code passes CI checks. +**CI integration:** All code is automatically checked for formatting in GitHub Actions. Run `ruff format .` before committing to ensure your code passes CI checks. ## Related repositories @@ -303,3 +296,7 @@ HEDTools is licensed under the MIT License. See [LICENSE](LICENSE) for details. - GitHub issues: [https://github.com/hed-standard/hed-python/issues](https://github.com/hed-standard/hed-python/issues) - Questions or ideas: [HED discussions](https://github.com/orgs/hed-standard/discussions) - Contact: [hed-maintainers@gmail.com](mailto:hed-maintainers@gmail.com) + +## Funding + +Partial support for this project was provided by [NIH 1R01MH126700-01A1](https://reporter.nih.gov/project-details/10480619). diff --git a/RELEASE_GUIDE.md b/RELEASE_GUIDE.md index 76a16f6d..713205ba 100644 --- a/RELEASE_GUIDE.md +++ b/RELEASE_GUIDE.md @@ -95,14 +95,13 @@ Before releasing, ensure all code quality checks pass: ```bash # Run code formatter check -black --check . -# On Windows, use: black --workers 1 --check . +ruff format --check . # Run linter ruff check hed/ tests/ # Run spell checker -codespell +typos # Run all tests python -m unittest discover tests -v diff --git a/lychee.toml b/lychee.toml index c3115bbe..fc019131 100644 --- a/lychee.toml +++ b/lychee.toml @@ -34,7 +34,18 @@ user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/1 # Exclude patterns - regex patterns to exclude from checking # These are Sphinx theme template files with Jinja2 variables that shouldn't be checked exclude_path = [ - "webpack-macros\\.html$", - "sbt-webpack-macros\\.html$", - ".*-macros\\.html$" + 'webpack-macros\.html$', + 'sbt-webpack-macros\.html$', + '.*-macros\.html$', + 'spec_tests/hed-examples', + 'spec_tests/hed-schemas', + 'spec_tests/hed-tests', +] + +# Exclude specific URLs from checking (by regex) +exclude = [ + '^http://127\.0\.0\.', + '^http://localhost', + '^https://localhost', + '^file://', ] diff --git a/pyproject.toml b/pyproject.toml index c1b314ee..c20be443 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ dependencies = [ [project.optional-dependencies] dev = [ "ruff>=0.8.0", - "codespell>=2.2.0", + "typos>=1.29.0", "mdformat>=0.7.0", "mdformat-myst>=0.1.5", ] @@ -112,9 +112,35 @@ namespaces = false [tool.setuptools.package-data] hed = ["schema/schema_data/*.xml", "resources/*.png", "validator/data/*"] -[tool.codespell] -skip = '*.git,*.pdf,*.svg,deprecated,venv*,*.tsv,*.yaml,*.yml,*.json,*.rdf,*.jsonld,spec_tests,*.xml,*.mediawiki,*.omn,*.toml' -ignore-words-list = 'te,parms,assertIn,covert,hed,recuse,disjointness' +[tool.typos.files] +extend-exclude = [ + "*.pdf", + "*.svg", + "deprecated/", + "venv*/", + "*.tsv", + "*.yaml", + "*.yml", + "*.json", + "*.rdf", + "*.jsonld", + "spec_tests/", + "tests/", + "*.xml", + "*.mediawiki", + "*.omn", + "*.toml", +] + +[tool.typos.default.extend-words] +# Domain-specific terms and abbreviations that are not typos +te = "te" +parms = "parms" +assertIn = "assertIn" +covert = "covert" +hed = "hed" +recuse = "recuse" +disjointness = "disjointness" [tool.ruff] # Target Python 3.10+ @@ -123,19 +149,18 @@ line-length = 120 # Exclude common directories exclude = [ - ".git", - ".venv", - "venv", - "__pycache__", - "build", - "dist", - "*.egg-info", - "hed/_version.py", # Auto-generated by setuptools-scm - "spec_tests/hed-examples", - "spec_tests/hed-schemas", - "spec_tests/hed-specification", - "spec_tests/hed-tests", - ".status", + '.git', + '.venv', + 'venv', + '__pycache__', + 'build', + 'dist', + '*.egg-info', + 'hed/_version.py', # Auto-generated by setuptools-scm + 'spec_tests/hed-examples', + 'spec_tests/hed-schemas', + 'spec_tests/hed-tests', + '.status', ] [tool.ruff.lint] @@ -182,28 +207,8 @@ max-complexity = 10 known-first-party = ["hed"] [tool.ruff.format] -# Match black's defaults +# Ruff format settings quote-style = "double" indent-style = "space" skip-magic-trailing-comma = false line-ending = "auto" - -[tool.black] -line-length = 127 -target-version = ["py310", "py311", "py312", "py313", "py314"] -exclude = ''' -/( - \.git - | \.venv - | venv - | __pycache__ - | build - | dist - | \.eggs - | .*\.egg-info - | (\./)?hed/_version\.py - | spec_tests/hed-examples - | spec_tests/hed-specification - | \.status -)/ -'''