diff --git a/.coveragerc b/.coveragerc index f65ec5e..dd05c95 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,4 +2,8 @@ branch = True omit = */_version.py - */tests/* + +[paths] +source = + pydra/tasks + /**/pydra/tasks diff --git a/.editorconfig b/.editorconfig index d056e98..00699be 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,11 @@ insert_final_newline = true indent_size = 4 indent_style = space +# Use 4-space indentation for TOML. +[*.toml] +indent_size = 4 +indent_style = space + # Use 2-space indentation for YAML. [*.{yml,yaml}] indent_size = 2 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index dc05d30..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: publish - -on: - # Trigger this workflow when a release is created. - release: - types: - - published - -permissions: - # Allow checkout of the project. - contents: read - -env: - # Default Python version on which jobs are run. - DEFAULT_PYTHON_VERSION: '3.9' - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: ${{ env.DEFAULT_PYTHON_VERSION }} - - name: Install build frontend - run: python -m pip install build - - name: Build distribution package - run: python -m build - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 979871c..47984d1 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -6,28 +6,41 @@ name: Python package # Set once env: SUBPACKAGE: nipype1 + FSLCONDA: https://fsl.fmrib.ox.ac.uk/fsldownloads/fslconda/public/ + NO_ET: 1 # etelemetry causes order-of-magnitude slowdowns on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] + release: + types: + - published + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash -el {0} jobs: devcheck: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.7', '3.11' ] # Check oldest and newest versions - pip-flags: [ '', '--editable' ] + python-version: ["3.8", "3.12"] # Check oldest and newest versions + pip-flags: ["", "--editable"] pydra: - - 'pydra' - - '--editable git+https://github.com/nipype/pydra.git#egg=pydra' + - "pydra" + - "--editable git+https://github.com/nipype/pydra.git#egg=pydra" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install build dependencies @@ -47,19 +60,24 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11' ] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: conda-incubator/setup-miniconda@v3 with: python-version: ${{ matrix.python-version }} - - name: Set up NeuroDebian - run: bash <(wget -q -O- http://neuro.debian.net/_files/neurodebian-travis.sh) + mamba-version: "*" + channels: ${{ env.FSLCONDA }},conda-forge,defaults + channel-priority: true - name: Install FSL - run: sudo apt-get install -y fsl - - name: Install build dependencies + run: | + mamba install fsl-avwutils + mamba env config vars set FSLDIR="$CONDA_PREFIX" FSLOUTPUTTYPE="NIFTI_GZ" + # Hack because we're not doing a full FSL install + echo "6.0.7.9" > $CONDA_PREFIX/etc/fslversion + - name: Upgrade pip run: | python -m pip install --upgrade pip - name: Install task package @@ -69,34 +87,32 @@ jobs: python -c "import pydra as m; print(f'{m.__name__} {m.__version__} @ {m.__file__}')" - name: Test with pytest run: | - source /etc/fsl/fsl.sh - pytest -sv --doctest-modules pydra/tasks/$SUBPACKAGE \ - --cov pydra.tasks.$SUBPACKAGE --cov-report xml - - uses: codecov/codecov-action@v1 + pytest -sv --doctest-modules --pyargs pydra.tasks.$SUBPACKAGE \ + --cov pydra.tasks.$SUBPACKAGE --cov-report xml --cov-report term-missing + - uses: codecov/codecov-action@v4 if: ${{ always() }} + with: + token: ${{ secrets.CODECOV_TOKEN }} - deploy: - needs: [ devcheck, test ] + build: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ '3.9' ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install build tools - run: python -m pip install build twine - - name: Build source and wheel distributions - run: python -m build - - name: Check distributions - run: twine check dist/* - - uses: actions/upload-artifact@v2 + - uses: hynek/build-and-inspect-python-package@v2 + + deploy: + needs: [build, devcheck, test] + runs-on: ubuntu-latest + permissions: + id-token: write + if: github.repository_owner == 'nipype' && github.event.action == 'published' + environment: Publish + steps: + - uses: actions/download-artifact@v4 with: - name: distributions - path: dist/ + name: Packages + path: dist + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index ef50f11..4dc2815 100644 --- a/.gitignore +++ b/.gitignore @@ -129,4 +129,4 @@ dmypy.json .pyre/ # Generated by flit_scm -_versions.py +_version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1061012..7a0fcb1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,13 +2,13 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 24.3.0 hooks: - id: black diff --git a/CHANGELOG.md b/CHANGELOG.md index ecf0c47..b8e31e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ All notable changes to this project will be documented in this file. +## [0.3.0] - 2024-03-22 + +### Changed + +* Pydra 0.23+ compatibility +* Drop Python 3.7 compatibility, test Python 3.12 +* Updates to CI workflows + +## [0.2.2] – 2022-11-22 + +### Changed + +- Set Nipype developers as maintainers. + ## [0.2.1] – 2022-11-22 ### Added @@ -31,6 +45,8 @@ All notable changes to this project will be documented in this file. - Initial release for PyPI +[0.3.0]: https://github.com/nipype/pydra-nipype1/compare/0.2.2...0.3.0 +[0.2.2]: https://github.com/nipype/pydra-nipype1/compare/0.2.1...0.2.2 [0.2.1]: https://github.com/nipype/pydra-nipype1/compare/0.2.0...0.2.1 [0.2.0]: https://github.com/nipype/pydra-nipype1/compare/0.1.0...0.2.0 [0.1.0]: https://github.com/nipype/pydra-nipype1/releases/tag/0.1.0 diff --git a/codecov.yml b/codecov.yml index bbf806c..f45bbe4 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,10 +1,7 @@ coverage: range: "50...100" ignore: # files and folders that will be removed during processing - - "**/tests" - "**/_version.py" - - "setup.py" - - "versioneer.py" status: project: default: diff --git a/pydra/tasks/nipype1/__init__.py b/pydra/tasks/nipype1/__init__.py index d786431..94ee228 100644 --- a/pydra/tasks/nipype1/__init__.py +++ b/pydra/tasks/nipype1/__init__.py @@ -2,10 +2,11 @@ >>> from pydra import ShellCommandTask >>> import pydra.tasks.nipype1 """ + try: from ._version import __version__ -except ImportError: - pass +except ImportError: # pragma: no cover + __version__ = "0+unknown" from .utils import Nipype1Task diff --git a/pydra/tasks/nipype1/tests/__init__.py b/pydra/tasks/nipype1/tests/__init__.py index e69de29..6e98041 100644 --- a/pydra/tasks/nipype1/tests/__init__.py +++ b/pydra/tasks/nipype1/tests/__init__.py @@ -0,0 +1,18 @@ +import sys +from atexit import register +from contextlib import ExitStack +from functools import lru_cache + + +if sys.version_info < (3, 9): + from importlib_resources import as_file, files +else: + from importlib.resources import as_file, files + +_stack = ExitStack() +register(_stack.close) + + +@lru_cache +def load_resource(anchor, *parts) -> str: + return str(_stack.enter_context(as_file(files(anchor).joinpath(*parts)))) diff --git a/pydra/tasks/nipype1/tests/test_nipype1task.py b/pydra/tasks/nipype1/tests/test_nipype1task.py index 9a17c6e..5ceeb16 100644 --- a/pydra/tasks/nipype1/tests/test_nipype1task.py +++ b/pydra/tasks/nipype1/tests/test_nipype1task.py @@ -1,6 +1,6 @@ import pytest import shutil -from pkg_resources import resource_filename +from . import load_resource from nipype.interfaces import fsl import nipype.interfaces.utility as nutil @@ -12,7 +12,7 @@ def test_isolation(tmp_path): in_file = tmp_path / "orig/tpms_msk.nii.gz" in_file.parent.mkdir() - shutil.copyfile(resource_filename("nipype", "testing/data/tpms_msk.nii.gz"), in_file) + shutil.copyfile(load_resource("nipype", "testing/data/tpms_msk.nii.gz"), in_file) out_dir = tmp_path / "output" out_dir.mkdir() diff --git a/pydra/tasks/nipype1/utils.py b/pydra/tasks/nipype1/utils.py index 0be31c4..913403b 100644 --- a/pydra/tasks/nipype1/utils.py +++ b/pydra/tasks/nipype1/utils.py @@ -11,7 +11,7 @@ def traitedspec_to_specinfo(traitedspec): return pydra.specs.SpecInfo( name="Inputs", fields=[ - (name, attrs.field(metadata={"help_string": trait.desc})) + (name, attrs.field(metadata={"help_string": trait.desc}, type=ty.Any)) for name, trait in traitedspec.traits().items() if name in trait_names ], @@ -27,11 +27,11 @@ class Nipype1Task(pydra.engine.task.TaskBase): in Pydra Task outputs. >>> import pytest - >>> from pkg_resources import resource_filename + >>> from pydra.tasks.nipype1.tests import load_resource >>> from nipype.interfaces import fsl >>> if fsl.Info.version() is None: ... pytest.skip() - >>> img = resource_filename('nipype', 'testing/data/tpms_msk.nii.gz') + >>> img = load_resource('nipype', 'testing/data/tpms_msk.nii.gz') >>> from pydra.tasks.nipype1.utils import Nipype1Task >>> thresh = Nipype1Task(fsl.Threshold()) @@ -68,7 +68,7 @@ def __init__( ) self.output_spec = traitedspec_to_specinfo(interface._outputs()) - def _run_task(self): + def _run_task(self, environment=None): inputs = attrs.asdict(self.inputs, filter=lambda a, v: v is not attrs.NOTHING) node = nipype.Node(self._interface, base_dir=self.output_dir, name=self.name) node.inputs.trait_set(**inputs) diff --git a/pyproject.toml b/pyproject.toml index e630f8a..7fe2461 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "flit_scm:buildapi" name = "pydra-nipype1" description = "Tools for importing nipype 1.x interfaces into Pydra" readme = "README.md" -requires-python = ">=3.7" +requires-python = ">=3.8" dependencies = [ "pydra >=0.6.2", "nipype", @@ -17,7 +17,7 @@ authors = [ {name = "Chris Markiewicz", email = "markiewicz@stanford.edu"}, ] maintainers = [ - {name = "Ghislain Vaillant", email = "ghislain.vaillant@icm-institute.org"}, + {name = "Nipype developers", email = "neuroimaging@python.org"}, ] keywords = [ "pydra",