diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 7c688ac9..4aaa5cf4 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -113,3 +113,40 @@ jobs: with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} + + + trigger_post_release: + needs: [deploy] + runs-on: ubuntu-latest + strategy: + matrix: + repo: + - afni + - ants + - fsl + - freesurfer + env: + SECRET: "" + steps: + - name: Get secret for corresponding repo + id: get_secret + run: | + if [ "${{ matrix.repo }}" == "afni" ]; then + SECRET="${{ secrets.AFNI_PAT }}" + elif [ "${{ matrix.repo }}" == "ants" ]; then + SECRET="${{ secrets.ANTS_PAT }}" + elif [ "${{ matrix.repo }}" == "fsl" ]; then + SECRET="${{ secrets.FSL_PAT }}" + elif [ "${{ matrix.repo }}" == "freesurfer" ]; then + SECRET="${{ secrets.FREESURFER_PAT }}" + fi + echo "SECRET=$SECRET" >> $GITHUB_ENV + + - name: Trigger post-release on downstream repos + if: github.event_name == 'release' && env.SECRET + run: | + curl -XPOST -u "${{ env.SECRET }}" -H "Accept: application/vnd.github.everest-preview+json" \ + "https://api.github.com/repos/nipype/pydra-${{ matrix.repo }}/dispatches" \ + -d '{"event_type": "create-post-release"}' + env: + PAT: ${{ env.SECRET }} \ No newline at end of file diff --git a/conftest.py b/conftest.py index 9ec78aba..b53e22a8 100644 --- a/conftest.py +++ b/conftest.py @@ -58,7 +58,7 @@ def pytest_internalerror(excinfo): raise excinfo.value def pytest_configure(config): - config.option.capture = 'no' # allow print statements to show up in the console + config.option.capture = "no" # allow print statements to show up in the console config.option.log_cli = True # show log messages in the console config.option.log_level = "INFO" # set the log level to INFO @@ -70,8 +70,3 @@ def pytest_configure(config): @pytest.fixture def catch_cli_exceptions(): return CATCH_CLI_EXCEPTIONS - - -def show_cli_trace(result): - "Used in testing to show traceback of CLI output" - return "".join(traceback.format_exception(*result.exc_info)) diff --git a/example-specs/task/nipype/afni/center_mass.yaml b/example-specs/task/nipype/afni/center_mass.yaml index 35e82749..a6981eb8 100644 --- a/example-specs/task/nipype/afni/center_mass.yaml +++ b/example-specs/task/nipype/afni/center_mass.yaml @@ -91,7 +91,7 @@ tests: automask: # type=bool|default=False: Generate the mask automatically set_cm: - # type=tuple|default=(, , ): After computing the center of mass, set the origin fields in the header so that the center of mass will be at (x,y,z) in DICOM coords. + # type=tuple|default=None: After computing the center of mass, set the origin fields in the header so that the center of mass will be at (x,y,z) in DICOM coords. local_ijk: # type=bool|default=False: Output values as (i,j,k) in local orientation roi_vals: diff --git a/example-specs/task/nipype/afni/dot.yaml b/example-specs/task/nipype/afni/dot.yaml index 25211895..7c21b4fd 100644 --- a/example-specs/task/nipype/afni/dot.yaml +++ b/example-specs/task/nipype/afni/dot.yaml @@ -85,7 +85,7 @@ tests: mask: # type=file|default=: Use this dataset as a mask mrange: - # type=tuple|default=(, ): Means to further restrict the voxels from 'mset' so thatonly those mask values within this range (inclusive) willbe used. + # type=tuple|default=None: Means to further restrict the voxels from 'mset' so thatonly those mask values within this range (inclusive) willbe used. demean: # type=bool|default=False: Remove the mean from each volume prior to computing the correlation docor: diff --git a/example-specs/task/nipype/afni/one_d_tool_py.yaml b/example-specs/task/nipype/afni/one_d_tool_py.yaml index d7ae5324..bfd7bdeb 100644 --- a/example-specs/task/nipype/afni/one_d_tool_py.yaml +++ b/example-specs/task/nipype/afni/one_d_tool_py.yaml @@ -82,7 +82,7 @@ tests: show_censor_count: # type=bool|default=False: display the total number of censored TRs Note : if input is a valid xmat.1D dataset, then the count will come from the header. Otherwise the input is assumed to be a binary censorfile, and zeros are simply counted. censor_motion: - # type=tuple|default=(, ): Tuple of motion limit and outfile prefix. need to also set set_nruns -r set_run_lengths + # type=tuple|default=None: Tuple of motion limit and outfile prefix. need to also set set_nruns -r set_run_lengths censor_prev_TR: # type=bool|default=False: for each censored TR, also censor previous show_trs_uncensored: diff --git a/example-specs/task/nipype/afni/qwarp.yaml b/example-specs/task/nipype/afni/qwarp.yaml index f04d886b..0ed6ee9d 100644 --- a/example-specs/task/nipype/afni/qwarp.yaml +++ b/example-specs/task/nipype/afni/qwarp.yaml @@ -193,7 +193,7 @@ tests: wball: # type=list|default=[]: "``-wball x y z r f`` Enhance automatic weight from '-useweight' by a factor of 1+f\*Gaussian(FWHM=r) centered in the base image at DICOM coordinates (x,y,z) and with radius 'r'. The goal of this option is to try and make the alignment better in a specific part of the brain. Example: -wball 0 14 6 30 40 to emphasize the thalamic area (in MNI/Talairach space). * The 'r' parameter must be positive! * The 'f' parameter must be between 1 and 100 (inclusive). * '-wball' does nothing if you input your own weight with the '-weight' option. * '-wball' does change the binary weight created by the '-noweight' option. * You can only use '-wball' once in a run of 3dQwarp. **The effect of '-wball' is not dramatic.** The example above makes the average brain image across a collection of subjects a little sharper in the thalamic area, which might have some small value. If you care enough about alignment to use '-wball', then you should examine the results from 3dQwarp for each subject, to see if the alignments are good enough for your purposes. wmask: - # type=tuple|default=(, ): Similar to '-wball', but here, you provide a dataset 'ws' that indicates where to increase the weight. * The 'ws' dataset must be on the same 3D grid as the base dataset. * 'ws' is treated as a mask -- it only matters where it is nonzero -- otherwise, the values inside are not used. * After 'ws' comes the factor 'f' by which to increase the automatically computed weight. Where 'ws' is nonzero, the weighting will be multiplied by (1+f). * As with '-wball', the factor 'f' should be between 1 and 100. + # type=tuple|default=None: Similar to '-wball', but here, you provide a dataset 'ws' that indicates where to increase the weight. * The 'ws' dataset must be on the same 3D grid as the base dataset. * 'ws' is treated as a mask -- it only matters where it is nonzero -- otherwise, the values inside are not used. * After 'ws' comes the factor 'f' by which to increase the automatically computed weight. Where 'ws' is nonzero, the weighting will be multiplied by (1+f). * As with '-wball', the factor 'f' should be between 1 and 100. out_weight_file: # type=file|default=: Write the weight volume to disk as a dataset blur: diff --git a/example-specs/task/nipype/afni/qwarp_plus_minus.yaml b/example-specs/task/nipype/afni/qwarp_plus_minus.yaml index f01c13cf..47f3e26b 100644 --- a/example-specs/task/nipype/afni/qwarp_plus_minus.yaml +++ b/example-specs/task/nipype/afni/qwarp_plus_minus.yaml @@ -129,7 +129,7 @@ tests: wball: # type=list|default=[]: "``-wball x y z r f`` Enhance automatic weight from '-useweight' by a factor of 1+f\*Gaussian(FWHM=r) centered in the base image at DICOM coordinates (x,y,z) and with radius 'r'. The goal of this option is to try and make the alignment better in a specific part of the brain. Example: -wball 0 14 6 30 40 to emphasize the thalamic area (in MNI/Talairach space). * The 'r' parameter must be positive! * The 'f' parameter must be between 1 and 100 (inclusive). * '-wball' does nothing if you input your own weight with the '-weight' option. * '-wball' does change the binary weight created by the '-noweight' option. * You can only use '-wball' once in a run of 3dQwarp. **The effect of '-wball' is not dramatic.** The example above makes the average brain image across a collection of subjects a little sharper in the thalamic area, which might have some small value. If you care enough about alignment to use '-wball', then you should examine the results from 3dQwarp for each subject, to see if the alignments are good enough for your purposes. wmask: - # type=tuple|default=(, ): Similar to '-wball', but here, you provide a dataset 'ws' that indicates where to increase the weight. * The 'ws' dataset must be on the same 3D grid as the base dataset. * 'ws' is treated as a mask -- it only matters where it is nonzero -- otherwise, the values inside are not used. * After 'ws' comes the factor 'f' by which to increase the automatically computed weight. Where 'ws' is nonzero, the weighting will be multiplied by (1+f). * As with '-wball', the factor 'f' should be between 1 and 100. + # type=tuple|default=None: Similar to '-wball', but here, you provide a dataset 'ws' that indicates where to increase the weight. * The 'ws' dataset must be on the same 3D grid as the base dataset. * 'ws' is treated as a mask -- it only matters where it is nonzero -- otherwise, the values inside are not used. * After 'ws' comes the factor 'f' by which to increase the automatically computed weight. Where 'ws' is nonzero, the weighting will be multiplied by (1+f). * As with '-wball', the factor 'f' should be between 1 and 100. out_weight_file: # type=file|default=: Write the weight volume to disk as a dataset blur: diff --git a/example-specs/task/nipype/afni/t_corr_map.yaml b/example-specs/task/nipype/afni/t_corr_map.yaml index 65e3a897..bafdfb92 100644 --- a/example-specs/task/nipype/afni/t_corr_map.yaml +++ b/example-specs/task/nipype/afni/t_corr_map.yaml @@ -164,7 +164,7 @@ tests: polort: # type=int|default=0: bandpass: - # type=tuple|default=(, ): + # type=tuple|default=None: regress_out_timeseries: # type=file|default=: blur_fwhm: diff --git a/example-specs/task/nipype/freesurfer/robust_template.yaml b/example-specs/task/nipype/freesurfer/robust_template.yaml index 55609f66..df30f9a5 100644 --- a/example-specs/task/nipype/freesurfer/robust_template.yaml +++ b/example-specs/task/nipype/freesurfer/robust_template.yaml @@ -6,7 +6,7 @@ # Docs # ---- # construct an unbiased robust template for longitudinal volumes -# +# # Examples # -------- # >>> from nipype.interfaces.freesurfer import RobustTemplate @@ -23,26 +23,26 @@ # >>> template.inputs.out_file = 'T1.nii' # >>> template.cmdline #doctest: # 'mri_robust_template --satit --average 0 --fixtp --mov structural.nii functional.nii --inittp 1 --noit --template T1.nii --subsample 200' -# +# # >>> template.inputs.transform_outputs = ['structural.lta', # ... 'functional.lta'] # >>> template.inputs.scaled_intensity_outputs = ['structural-iscale.txt', # ... 'functional-iscale.txt'] # >>> template.cmdline #doctest: +ELLIPSIS # 'mri_robust_template --satit --average 0 --fixtp --mov structural.nii functional.nii --inittp 1 --noit --template T1.nii --iscaleout .../structural-iscale.txt .../functional-iscale.txt --subsample 200 --lta .../structural.lta .../functional.lta' -# +# # >>> template.inputs.transform_outputs = True # >>> template.inputs.scaled_intensity_outputs = True # >>> template.cmdline #doctest: +ELLIPSIS # 'mri_robust_template --satit --average 0 --fixtp --mov structural.nii functional.nii --inittp 1 --noit --template T1.nii --iscaleout .../is1.txt .../is2.txt --subsample 200 --lta .../tp1.lta .../tp2.lta' -# +# # >>> template.run() #doctest: +SKIP -# +# # References # ---------- # [https://surfer.nmr.mgh.harvard.edu/fswiki/mri_robust_template] -# -# +# +# task_name: RobustTemplate nipype_name: RobustTemplate nipype_module: nipype.interfaces.freesurfer.longitudinal @@ -52,11 +52,11 @@ inputs: rename: # dict[str, str] - fields to rename in the Pydra interface types: - # dict[str, type] - override inferred types (use "mime-like" string for file-format types, - # e.g. 'medimage/nifti-gz'). For most fields the type will be correctly inferred - # from the nipype interface, but you may want to be more specific, particularly - # for file types, where specifying the format also specifies the file that will be - # passed to the field in the automatically generated unittests. + # dict[str, type] - override inferred types (use "mime-like" string for file-format types, + # e.g. 'medimage/nifti-gz'). For most fields the type will be correctly inferred + # from the nipype interface, but you may want to be more specific, particularly + # for file types, where specifying the format also specifies the file that will be + # passed to the field in the automatically generated unittests. in_files: medimage/nifti1+list-of # type=inputmultiobject|default=[]: input movable volumes to be aligned to common mean/median template in_intensity_scales: generic/file+list-of @@ -79,18 +79,18 @@ outputs: rename: # dict[str, str] - fields to rename in the Pydra interface types: - # dict[str, type] - override inferred types (use "mime-like" string for file-format types, - # e.g. 'medimage/nifti-gz'). For most fields the type will be correctly inferred - # from the nipype interface, but you may want to be more specific, particularly - # for file types, where specifying the format also specifies the file that will be - # passed to the field in the automatically generated unittests. + # dict[str, type] - override inferred types (use "mime-like" string for file-format types, + # e.g. 'medimage/nifti-gz'). For most fields the type will be correctly inferred + # from the nipype interface, but you may want to be more specific, particularly + # for file types, where specifying the format also specifies the file that will be + # passed to the field in the automatically generated unittests. out_file: medimage/nifti1 # type=file: output template volume (final mean/median image) # type=file|default='mri_robust_template_out.mgz': output template volume (final mean/median image) scaled_intensity_outputs: text/text-file+list-of # type=outputmultiobject: output final intensity scales # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) - transform_outputs: medimage-freesurfer/lta+list-of + transform_outputs: '[generic/file,medimage-freesurfer/lta]+list-of' # type=outputmultiobject: output xform files from moving to template # type=traitcompound|default=[None]: output xforms to template (for each input) callables: @@ -101,206 +101,206 @@ outputs: requirements: # dict[str, list[str]] - input fields that are required to be provided for the output field to be present tests: - - inputs: - # dict[str, str] - values to provide to inputs fields in the task initialisation - # (if not specified, will try to choose a sensible value) - in_files: - # type=inputmultiobject|default=[]: input movable volumes to be aligned to common mean/median template - out_file: - # type=file: output template volume (final mean/median image) - # type=file|default='mri_robust_template_out.mgz': output template volume (final mean/median image) - auto_detect_sensitivity: - # type=bool|default=False: auto-detect good sensitivity (recommended for head or full brain scans) - outlier_sensitivity: - # type=float|default=0.0: set outlier sensitivity manually (e.g. "--sat 4.685" ). Higher values mean less sensitivity. - transform_outputs: - # type=outputmultiobject: output xform files from moving to template - # type=traitcompound|default=[None]: output xforms to template (for each input) - intensity_scaling: - # type=bool|default=False: allow also intensity scaling (default off) - scaled_intensity_outputs: - # type=outputmultiobject: output final intensity scales - # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) - subsample_threshold: - # type=int|default=0: subsample if dim > # on all axes (default no subs.) - average_metric: - # type=enum|default='median'|allowed['mean','median']: construct template from: 0 Mean, 1 Median (default) - initial_timepoint: - # type=int|default=0: use TP# for spacial init (default random), 0: no init - fixed_timepoint: - # type=bool|default=False: map everything to init TP# (init TP is not resampled) - no_iteration: - # type=bool|default=False: do not iterate, just create first template - initial_transforms: - # type=inputmultiobject|default=[]: use initial transforms (lta) on source - in_intensity_scales: - # type=inputmultiobject|default=[]: use initial intensity scales - num_threads: - # type=int|default=0: allows for specifying more threads - subjects_dir: - # type=directory|default=: subjects directory - args: - # type=str|default='': Additional parameters to the command - environ: - # type=dict|default={}: Environment variables - imports: - # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item - # consisting of 'module', 'name', and optionally 'alias' keys - expected_outputs: - # dict[str, str] - expected values for selected outputs, noting that tests will typically - # be terminated before they complete for time-saving reasons, and therefore - # these values will be ignored, when running in CI - timeout: 10 - # int - the value to set for the timeout in the generated test, - # after which the test will be considered to have been initialised - # successfully. Set to 0 to disable the timeout (warning, this could - # lead to the unittests taking a very long time to complete) - xfail: true - # bool - whether the unittest is expected to fail or not. Set to false - # when you are satisfied with the edits you have made to this file - - inputs: - # dict[str, str] - values to provide to inputs fields in the task initialisation - # (if not specified, will try to choose a sensible value) - in_files: - # type=inputmultiobject|default=[]: input movable volumes to be aligned to common mean/median template - auto_detect_sensitivity: "True" - # type=bool|default=False: auto-detect good sensitivity (recommended for head or full brain scans) - average_metric: '"mean"' - # type=enum|default='median'|allowed['mean','median']: construct template from: 0 Mean, 1 Median (default) - initial_timepoint: "1" - # type=int|default=0: use TP# for spacial init (default random), 0: no init - fixed_timepoint: "True" - # type=bool|default=False: map everything to init TP# (init TP is not resampled) - no_iteration: "True" - # type=bool|default=False: do not iterate, just create first template - subsample_threshold: "200" - # type=int|default=0: subsample if dim > # on all axes (default no subs.) - out_file: '"T1.nii"' - # type=file: output template volume (final mean/median image) - # type=file|default='mri_robust_template_out.mgz': output template volume (final mean/median image) - imports: - # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item - # consisting of 'module', 'name', and optionally 'alias' keys - expected_outputs: - # dict[str, str] - expected values for selected outputs, noting that tests will typically - # be terminated before they complete for time-saving reasons, and therefore - # these values will be ignored, when running in CI - timeout: 10 - # int - the value to set for the timeout in the generated test, - # after which the test will be considered to have been initialised - # successfully. Set to 0 to disable the timeout (warning, this could - # lead to the unittests taking a very long time to complete) - xfail: true - # bool - whether the unittest is expected to fail or not. Set to false - # when you are satisfied with the edits you have made to this file - - inputs: - # dict[str, str] - values to provide to inputs fields in the task initialisation - # (if not specified, will try to choose a sensible value) - transform_outputs: '["structural.lta","functional.lta"]' - # type=outputmultiobject: output xform files from moving to template - # type=traitcompound|default=[None]: output xforms to template (for each input) - scaled_intensity_outputs: '["structural-iscale.txt","functional-iscale.txt"]' - # type=outputmultiobject: output final intensity scales - # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) - imports: - # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item - # consisting of 'module', 'name', and optionally 'alias' keys - expected_outputs: - # dict[str, str] - expected values for selected outputs, noting that tests will typically - # be terminated before they complete for time-saving reasons, and therefore - # these values will be ignored, when running in CI - timeout: 10 - # int - the value to set for the timeout in the generated test, - # after which the test will be considered to have been initialised - # successfully. Set to 0 to disable the timeout (warning, this could - # lead to the unittests taking a very long time to complete) - xfail: true - # bool - whether the unittest is expected to fail or not. Set to false - # when you are satisfied with the edits you have made to this file - - inputs: - # dict[str, str] - values to provide to inputs fields in the task initialisation - # (if not specified, will try to choose a sensible value) - transform_outputs: "True" - # type=outputmultiobject: output xform files from moving to template - # type=traitcompound|default=[None]: output xforms to template (for each input) - scaled_intensity_outputs: "True" - # type=outputmultiobject: output final intensity scales - # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) - imports: - # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item - # consisting of 'module', 'name', and optionally 'alias' keys - expected_outputs: - # dict[str, str] - expected values for selected outputs, noting that tests will typically - # be terminated before they complete for time-saving reasons, and therefore - # these values will be ignored, when running in CI - timeout: 10 - # int - the value to set for the timeout in the generated test, - # after which the test will be considered to have been initialised - # successfully. Set to 0 to disable the timeout (warning, this could - # lead to the unittests taking a very long time to complete) - xfail: true - # bool - whether the unittest is expected to fail or not. Set to false - # when you are satisfied with the edits you have made to this file +- inputs: + # dict[str, str] - values to provide to inputs fields in the task initialisation + # (if not specified, will try to choose a sensible value) + in_files: + # type=inputmultiobject|default=[]: input movable volumes to be aligned to common mean/median template + out_file: + # type=file: output template volume (final mean/median image) + # type=file|default='mri_robust_template_out.mgz': output template volume (final mean/median image) + auto_detect_sensitivity: + # type=bool|default=False: auto-detect good sensitivity (recommended for head or full brain scans) + outlier_sensitivity: + # type=float|default=0.0: set outlier sensitivity manually (e.g. "--sat 4.685" ). Higher values mean less sensitivity. + transform_outputs: + # type=outputmultiobject: output xform files from moving to template + # type=traitcompound|default=[None]: output xforms to template (for each input) + intensity_scaling: + # type=bool|default=False: allow also intensity scaling (default off) + scaled_intensity_outputs: + # type=outputmultiobject: output final intensity scales + # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) + subsample_threshold: + # type=int|default=0: subsample if dim > # on all axes (default no subs.) + average_metric: + # type=enum|default='median'|allowed['mean','median']: construct template from: 0 Mean, 1 Median (default) + initial_timepoint: + # type=int|default=0: use TP# for spacial init (default random), 0: no init + fixed_timepoint: + # type=bool|default=False: map everything to init TP# (init TP is not resampled) + no_iteration: + # type=bool|default=False: do not iterate, just create first template + initial_transforms: + # type=inputmultiobject|default=[]: use initial transforms (lta) on source + in_intensity_scales: + # type=inputmultiobject|default=[]: use initial intensity scales + num_threads: + # type=int|default=0: allows for specifying more threads + subjects_dir: + # type=directory|default=: subjects directory + args: + # type=str|default='': Additional parameters to the command + environ: + # type=dict|default={}: Environment variables + imports: + # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item + # consisting of 'module', 'name', and optionally 'alias' keys + expected_outputs: + # dict[str, str] - expected values for selected outputs, noting that tests will typically + # be terminated before they complete for time-saving reasons, and therefore + # these values will be ignored, when running in CI + timeout: 10 + # int - the value to set for the timeout in the generated test, + # after which the test will be considered to have been initialised + # successfully. Set to 0 to disable the timeout (warning, this could + # lead to the unittests taking a very long time to complete) + xfail: true + # bool - whether the unittest is expected to fail or not. Set to false + # when you are satisfied with the edits you have made to this file +- inputs: + # dict[str, str] - values to provide to inputs fields in the task initialisation + # (if not specified, will try to choose a sensible value) + in_files: + # type=inputmultiobject|default=[]: input movable volumes to be aligned to common mean/median template + auto_detect_sensitivity: 'True' + # type=bool|default=False: auto-detect good sensitivity (recommended for head or full brain scans) + average_metric: '"mean"' + # type=enum|default='median'|allowed['mean','median']: construct template from: 0 Mean, 1 Median (default) + initial_timepoint: '1' + # type=int|default=0: use TP# for spacial init (default random), 0: no init + fixed_timepoint: 'True' + # type=bool|default=False: map everything to init TP# (init TP is not resampled) + no_iteration: 'True' + # type=bool|default=False: do not iterate, just create first template + subsample_threshold: '200' + # type=int|default=0: subsample if dim > # on all axes (default no subs.) + out_file: '"T1.nii"' + # type=file: output template volume (final mean/median image) + # type=file|default='mri_robust_template_out.mgz': output template volume (final mean/median image) + imports: + # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item + # consisting of 'module', 'name', and optionally 'alias' keys + expected_outputs: + # dict[str, str] - expected values for selected outputs, noting that tests will typically + # be terminated before they complete for time-saving reasons, and therefore + # these values will be ignored, when running in CI + timeout: 10 + # int - the value to set for the timeout in the generated test, + # after which the test will be considered to have been initialised + # successfully. Set to 0 to disable the timeout (warning, this could + # lead to the unittests taking a very long time to complete) + xfail: true + # bool - whether the unittest is expected to fail or not. Set to false + # when you are satisfied with the edits you have made to this file +- inputs: + # dict[str, str] - values to provide to inputs fields in the task initialisation + # (if not specified, will try to choose a sensible value) + transform_outputs: '["structural.lta","functional.lta"]' + # type=outputmultiobject: output xform files from moving to template + # type=traitcompound|default=[None]: output xforms to template (for each input) + scaled_intensity_outputs: '["structural-iscale.txt","functional-iscale.txt"]' + # type=outputmultiobject: output final intensity scales + # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) + imports: + # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item + # consisting of 'module', 'name', and optionally 'alias' keys + expected_outputs: + # dict[str, str] - expected values for selected outputs, noting that tests will typically + # be terminated before they complete for time-saving reasons, and therefore + # these values will be ignored, when running in CI + timeout: 10 + # int - the value to set for the timeout in the generated test, + # after which the test will be considered to have been initialised + # successfully. Set to 0 to disable the timeout (warning, this could + # lead to the unittests taking a very long time to complete) + xfail: true + # bool - whether the unittest is expected to fail or not. Set to false + # when you are satisfied with the edits you have made to this file +- inputs: + # dict[str, str] - values to provide to inputs fields in the task initialisation + # (if not specified, will try to choose a sensible value) + transform_outputs: 'True' + # type=outputmultiobject: output xform files from moving to template + # type=traitcompound|default=[None]: output xforms to template (for each input) + scaled_intensity_outputs: 'True' + # type=outputmultiobject: output final intensity scales + # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) + imports: + # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item + # consisting of 'module', 'name', and optionally 'alias' keys + expected_outputs: + # dict[str, str] - expected values for selected outputs, noting that tests will typically + # be terminated before they complete for time-saving reasons, and therefore + # these values will be ignored, when running in CI + timeout: 10 + # int - the value to set for the timeout in the generated test, + # after which the test will be considered to have been initialised + # successfully. Set to 0 to disable the timeout (warning, this could + # lead to the unittests taking a very long time to complete) + xfail: true + # bool - whether the unittest is expected to fail or not. Set to false + # when you are satisfied with the edits you have made to this file doctests: - - cmdline: - # str - the expected cmdline output - inputs: - # dict[str, str] - name-value pairs for inputs to be provided to the doctest. - # If the field is of file-format type and the value is None, then the - # '.mock()' method of the corresponding class is used instead. - in_files: - # type=inputmultiobject|default=[]: input movable volumes to be aligned to common mean/median template - auto_detect_sensitivity: "True" - # type=bool|default=False: auto-detect good sensitivity (recommended for head or full brain scans) - average_metric: '"mean"' - # type=enum|default='median'|allowed['mean','median']: construct template from: 0 Mean, 1 Median (default) - initial_timepoint: "1" - # type=int|default=0: use TP# for spacial init (default random), 0: no init - fixed_timepoint: "True" - # type=bool|default=False: map everything to init TP# (init TP is not resampled) - no_iteration: "True" - # type=bool|default=False: do not iterate, just create first template - subsample_threshold: "200" - # type=int|default=0: subsample if dim > # on all axes (default no subs.) - out_file: '"T1.nii"' - # type=file: output template volume (final mean/median image) - # type=file|default='mri_robust_template_out.mgz': output template volume (final mean/median image) - imports: - # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item - # consisting of 'module', 'name', and optionally 'alias' keys - directive: - # str - any doctest directive to place on the cmdline call, e.g. # doctest: +ELLIPSIS - - cmdline: - # str - the expected cmdline output - inputs: - # dict[str, str] - name-value pairs for inputs to be provided to the doctest. - # If the field is of file-format type and the value is None, then the - # '.mock()' method of the corresponding class is used instead. - transform_outputs: '["structural.lta","functional.lta"]' - # type=outputmultiobject: output xform files from moving to template - # type=traitcompound|default=[None]: output xforms to template (for each input) - scaled_intensity_outputs: '["structural-iscale.txt","functional-iscale.txt"]' - # type=outputmultiobject: output final intensity scales - # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) - imports: - # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item - # consisting of 'module', 'name', and optionally 'alias' keys - directive: - # str - any doctest directive to place on the cmdline call, e.g. # doctest: +ELLIPSIS - - cmdline: - # str - the expected cmdline output - inputs: - # dict[str, str] - name-value pairs for inputs to be provided to the doctest. - # If the field is of file-format type and the value is None, then the - # '.mock()' method of the corresponding class is used instead. - transform_outputs: "True" - # type=outputmultiobject: output xform files from moving to template - # type=traitcompound|default=[None]: output xforms to template (for each input) - scaled_intensity_outputs: "True" - # type=outputmultiobject: output final intensity scales - # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) - imports: - # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item - # consisting of 'module', 'name', and optionally 'alias' keys - directive: - # str - any doctest directive to place on the cmdline call, e.g. # doctest: +ELLIPSIS +- cmdline: + # str - the expected cmdline output + inputs: + # dict[str, str] - name-value pairs for inputs to be provided to the doctest. + # If the field is of file-format type and the value is None, then the + # '.mock()' method of the corresponding class is used instead. + in_files: + # type=inputmultiobject|default=[]: input movable volumes to be aligned to common mean/median template + auto_detect_sensitivity: 'True' + # type=bool|default=False: auto-detect good sensitivity (recommended for head or full brain scans) + average_metric: '"mean"' + # type=enum|default='median'|allowed['mean','median']: construct template from: 0 Mean, 1 Median (default) + initial_timepoint: '1' + # type=int|default=0: use TP# for spacial init (default random), 0: no init + fixed_timepoint: 'True' + # type=bool|default=False: map everything to init TP# (init TP is not resampled) + no_iteration: 'True' + # type=bool|default=False: do not iterate, just create first template + subsample_threshold: '200' + # type=int|default=0: subsample if dim > # on all axes (default no subs.) + out_file: '"T1.nii"' + # type=file: output template volume (final mean/median image) + # type=file|default='mri_robust_template_out.mgz': output template volume (final mean/median image) + imports: + # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item + # consisting of 'module', 'name', and optionally 'alias' keys + directive: + # str - any doctest directive to place on the cmdline call, e.g. # doctest: +ELLIPSIS +- cmdline: + # str - the expected cmdline output + inputs: + # dict[str, str] - name-value pairs for inputs to be provided to the doctest. + # If the field is of file-format type and the value is None, then the + # '.mock()' method of the corresponding class is used instead. + transform_outputs: '["structural.lta","functional.lta"]' + # type=outputmultiobject: output xform files from moving to template + # type=traitcompound|default=[None]: output xforms to template (for each input) + scaled_intensity_outputs: '["structural-iscale.txt","functional-iscale.txt"]' + # type=outputmultiobject: output final intensity scales + # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) + imports: + # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item + # consisting of 'module', 'name', and optionally 'alias' keys + directive: + # str - any doctest directive to place on the cmdline call, e.g. # doctest: +ELLIPSIS +- cmdline: + # str - the expected cmdline output + inputs: + # dict[str, str] - name-value pairs for inputs to be provided to the doctest. + # If the field is of file-format type and the value is None, then the + # '.mock()' method of the corresponding class is used instead. + transform_outputs: 'True' + # type=outputmultiobject: output xform files from moving to template + # type=traitcompound|default=[None]: output xforms to template (for each input) + scaled_intensity_outputs: 'True' + # type=outputmultiobject: output final intensity scales + # type=traitcompound|default=[None]: final intensity scales (will activate --iscale) + imports: + # list[nipype2pydra.task.base.importstatement] - list import statements required by the test, with each list item + # consisting of 'module', 'name', and optionally 'alias' keys + directive: + # str - any doctest directive to place on the cmdline call, e.g. # doctest: +ELLIPSIS diff --git a/nipype2pydra/cli/pkg_gen.py b/nipype2pydra/cli/pkg_gen.py index fa516892..168d4cb7 100644 --- a/nipype2pydra/cli/pkg_gen.py +++ b/nipype2pydra/cli/pkg_gen.py @@ -18,12 +18,17 @@ NipypeInterface, gen_fileformats_module, gen_fileformats_extras_module, + gen_fileformats_extras_tests, ) from nipype2pydra.cli.base import cli DEFAULT_INTERFACE_SPEC = ( - Path(__file__).parent.parent / "pkg_gen" / "resources" / "specs" / "nipype-interfaces-to-import.yaml" + Path(__file__).parent.parent + / "pkg_gen" + / "resources" + / "specs" + / "nipype-interfaces-to-import.yaml" ) @@ -33,7 +38,11 @@ @click.argument("output_dir", type=click.Path(path_type=Path)) @click.option("--work-dir", type=click.Path(path_type=Path), default=None) @click.option("--task-template", type=click.Path(path_type=Path), default=None) -@click.option("--packages-to-import", type=click.Path(path_type=Path), default=DEFAULT_INTERFACE_SPEC) +@click.option( + "--packages-to-import", + type=click.Path(path_type=Path), + default=DEFAULT_INTERFACE_SPEC, +) @click.option("--single-interface", type=str, nargs=2, default=None) @click.option( "--example-packages", @@ -118,9 +127,7 @@ def pkg_gen( parsed = NipypeInterface.parse(nipype_interface, pkg, base_package) spec_name = to_snake_case(interface) - yaml_spec = ( - parsed.generate_yaml_spec() - ) + yaml_spec = parsed.generate_yaml_spec() unmatched_formats.extend(parsed.unmatched_formats) ambiguous_formats.extend(parsed.ambiguous_formats) pkg_formats.update(parsed.pkg_formats) @@ -157,6 +164,19 @@ def pkg_gen( ) as f: f.write(gen_fileformats_extras_module(pkg, pkg_formats)) + tests_dir = ( + pkg_dir + / "related-packages" + / "fileformats-extras" + / "fileformats" + / "extras" + / f"medimage_{pkg}" + / "tests" + ) + tests_dir.mkdir() + with open(tests_dir / "test_generate_sample_data.py", "w") as f: + f.write(gen_fileformats_extras_tests(pkg, pkg_formats)) + sp.check_call("git init", shell=True, cwd=pkg_dir) sp.check_call("git add --all", shell=True, cwd=pkg_dir) sp.check_call( diff --git a/nipype2pydra/pkg_gen/__init__.py b/nipype2pydra/pkg_gen/__init__.py index cf3d6e14..eafc1d4b 100644 --- a/nipype2pydra/pkg_gen/__init__.py +++ b/nipype2pydra/pkg_gen/__init__.py @@ -12,6 +12,7 @@ from warnings import warn import requests from operator import itemgetter +from traits.trait_type import TraitType import yaml import black.parsing import fileformats.core @@ -161,7 +162,12 @@ def parse( if inpt_name in ("trait_added", "trait_modified"): continue inpt_desc = inpt.desc.replace("\n", " ") if inpt.desc else "" - inpt_mdata = f"type={type(inpt.trait_type).__name__.lower()}|default={inpt.default!r}" + input_default = inpt.default + if isinstance(input_default, tuple) and isinstance( + input_default[0], TraitType + ): + input_default = None + inpt_mdata = f"type={type(inpt.trait_type).__name__.lower()}|default={input_default!r}" if isinstance(inpt.trait_type, nipype.interfaces.base.core.traits.Enum): inpt_mdata += f"|allowed[{','.join(sorted(repr(v) for v in inpt.trait_type.values))}]" parsed.input_helps[inpt_name] = f"{inpt_mdata}: {inpt_desc}" @@ -635,9 +641,34 @@ def copy_ignore(_, names): gh_workflows_dir / "ci-cd.yaml", ) + related_pkgs_dir = pkg_dir / "related-packages" + shutil.copytree(TEMPLATES_DIR / "related-packages", related_pkgs_dir) + os.rename(related_pkgs_dir / "conftest_.py", related_pkgs_dir / "conftest.py") + # Add modified README os.unlink(pkg_dir / "README.md") - shutil.copy(TEMPLATES_DIR / "README.rst", pkg_dir / "README.rst") + with open(TEMPLATES_DIR / "README.rst") as f: + readme_rst = f.read() + readme_rst = readme_rst.replace("=" * 31, "=" * (23 + len(pkg))) + with open(pkg_dir / "README.rst", "w") as f: + f.write(readme_rst) + + fileformat_readme_path = related_pkgs_dir / "fileformats" / "README.rst" + with open(fileformat_readme_path) as f: + ff_readme_rst = f.read() + ff_readme_rst = ff_readme_rst.replace("=" * 29, "=" * (21 + len(pkg))) + with open(fileformat_readme_path, "w") as f: + f.write(ff_readme_rst) + + fileformat_extras_readme_path = ( + related_pkgs_dir / "fileformats-extras" / "README.rst" + ) + with open(fileformat_extras_readme_path) as f: + ffe_readme_rst = f.read() + ffe_readme_rst = ffe_readme_rst.replace("=" * 36, "=" * (28 + len(pkg))) + with open(fileformat_extras_readme_path, "w") as f: + f.write(ffe_readme_rst) + with open(pkg_dir / "pyproject.toml") as f: pyproject_toml = f.read() pyproject_toml = pyproject_toml.replace("README.md", "README.rst") @@ -685,7 +716,7 @@ def copy_ignore(_, names): # Replace "CHANGEME" string with pkg name for fspath in pkg_dir.glob("**/*"): - if fspath.is_dir(): + if fspath.is_dir() or fspath.suffix in (".pyc", ".pyo", ".pyd"): continue with open(fspath) as f: contents = f.read() @@ -807,7 +838,7 @@ def gen_fileformats_extras_module(pkg: str, pkg_formats: ty.Set[str]): from pathlib import Path import typing as ty from random import Random -from fileformats.core import FileSet +from fileformats.core import FileSet, SampleFileGenerator """ code_str += f"from fileformats.medimage_{pkg} import (\n" for ext in pkg_formats: @@ -819,12 +850,29 @@ def gen_fileformats_extras_module(pkg: str, pkg_formats: ty.Set[str]): code_str += f""" @FileSet.generate_sample_data.register -def gen_sample_{frmt.lower()}_data({frmt.lower()}: {frmt}, dest_dir: Path, seed: ty.Union[int, Random] = 0, stem: ty.Optional[str] = None) -> ty.Iterable[Path]: +def gen_sample_{frmt.lower()}_data({frmt.lower()}: {frmt}, generator: SampleFileGenerator) -> ty.Iterable[Path]: raise NotImplementedError """ return code_str +def gen_fileformats_extras_tests(pkg: str, pkg_formats: ty.Set[str]): + code_str = f"import pytest\nfrom fileformats.medimage_{pkg} import (\n" + for ext in pkg_formats: + frmt = ext2format_name(ext) + code_str += f" {frmt},\n" + code_str += ")\n\n" + for ext in pkg_formats: + frmt = ext2format_name(ext) + code_str += f""" + +@pytest.mark.xfail(reason="generate_sample_data not implemented") +def test_generate_sample_{frmt.lower()}_data(): + assert isinstance({frmt}.sample(), {frmt}) +""" + return code_str + + def get_callable_sources( nipype_interface, ) -> ty.Tuple[ty.Set[str], ty.List[str], ty.Set[str], ty.Set[ty.Tuple[str, str]]]: diff --git a/nipype2pydra/pkg_gen/resources/templates/README.rst b/nipype2pydra/pkg_gen/resources/templates/README.rst index d9af9070..085d5396 100644 --- a/nipype2pydra/pkg_gen/resources/templates/README.rst +++ b/nipype2pydra/pkg_gen/resources/templates/README.rst @@ -2,10 +2,10 @@ Pydra task package for CHANGEME =============================== -.. image:: https://github.com/nipype/pydra-CHANGEME/actions/workflows/pythonpackage.yaml/badge.svg - :target: https://github.com/nipype/pydra-CHANGEME/actions/workflows/pythonpackage.yaml -.. .. image:: https://codecov.io/gh/nipype/pydra-CHANGEME/branch/main/graph/badge.svg?token=UIS0OGPST7 -.. :target: https://codecov.io/gh/nipype/pydra-CHANGEME +.. image:: https://github.com/nipype/pydra-CHANGEME/actions/workflows/ci-cd.yaml/badge.svg + :target: https://github.com/nipype/pydra-CHANGEME/actions/workflows/ci-cd.yaml +.. image:: https://codecov.io/gh/nipype/pydra-CHANGEME/branch/main/graph/badge.svg?token=UIS0OGPST7 + :target: https://codecov.io/gh/nipype/pydra-CHANGEME .. image:: https://img.shields.io/pypi/pyversions/pydra-CHANGEME.svg :target: https://pypi.python.org/pypi/pydra-CHANGEME/ :alt: Supported Python versions @@ -71,6 +71,14 @@ Contributing to this package Developer installation ~~~~~~~~~~~~~~~~~~~~~~ +Install the `fileformats `__ packages +corresponding to AFNI specific file formats + + +.. code-block:: + + $ pip install -e ./related-packages/fileformats[dev] + $ pip install -e ./related-packages/fileformats-extras[dev] Install repo in developer mode from the source directory and install pre-commit to ensure consistent code-style and quality. @@ -78,7 +86,7 @@ ensure consistent code-style and quality. .. code-block:: $ pip install -e .[test,dev] -$ pre-commit install + $ pre-commit install Next install the requirements for running the auto-conversion script and generate the Pydra task interfaces from their Nipype counterparts @@ -93,7 +101,8 @@ The run the conversion script to convert Nipype interfaces to Pydra $ nipype-auto-conv/generate -## Methodology +Methodology +~~~~~~~~~~~ The development of this package is expected to have two phases diff --git a/nipype2pydra/pkg_gen/resources/templates/gh_workflows/ci-cd.yaml b/nipype2pydra/pkg_gen/resources/templates/gh_workflows/ci-cd.yaml index 4d7e8644..783e364b 100644 --- a/nipype2pydra/pkg_gen/resources/templates/gh_workflows/ci-cd.yaml +++ b/nipype2pydra/pkg_gen/resources/templates/gh_workflows/ci-cd.yaml @@ -21,12 +21,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Revert version to most recent tag on upstream update if: github.event_name == 'repository_dispatch' run: git checkout $(git tag -l | tail -n 1 | awk -F post '{print $1}') - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 - name: Install build dependencies run: python -m pip install --upgrade pip - name: Install requirements @@ -50,7 +50,7 @@ jobs: - '--editable git+https://github.com/nipype/pydra.git#egg=pydra' steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Revert version to most recent tag on upstream update if: github.event_name == 'repository_dispatch' run: git checkout $(git tag -l | tail -n 1 | awk -F post '{print $1}') @@ -63,7 +63,7 @@ jobs: run: | sed -i '/\/pydra\/tasks\/CHANGEME\/auto/d' .gitignore - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install build dependencies @@ -90,12 +90,12 @@ jobs: matrix: python-version: ['3.8', '3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Revert version to most recent tag on upstream update if: github.event_name == 'repository_dispatch' run: git checkout $(git tag -l | tail -n 1 | awk -F post '{print $1}') - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install build dependencies @@ -149,7 +149,7 @@ jobs: source $FREESURFER_HOME/SetUpFreeSurfer.sh echo $FREESURFER_LICENCE > $FREESURFER_HOME/license.txt export PATH=$FREESURFER_HOME/bin:$PATH - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Revert version to most recent tag on upstream update if: github.event_name == 'repository_dispatch' run: git checkout $(git tag -l | tail -n 1 | awk -F post '{print $1}') @@ -162,7 +162,7 @@ jobs: run: | sed -i '/\/src\/pydra\/tasks\/CHANGEME\/auto/d' .gitignore - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install build dependencies @@ -187,12 +187,12 @@ jobs: needs: [devcheck, test] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install build tools @@ -219,12 +219,12 @@ jobs: needs: [deploy-fileformats] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install build tools @@ -251,7 +251,7 @@ jobs: needs: [deploy-fileformats-extras] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 @@ -270,7 +270,7 @@ jobs: git commit -am"added auto-generated version to make new tag for package version" git tag ${TAG}post${POST} - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install build tools diff --git a/nipype2pydra/pkg_gen/resources/templates/related-packages/conftest_.py b/nipype2pydra/pkg_gen/resources/templates/related-packages/conftest_.py new file mode 100644 index 00000000..2a703c05 --- /dev/null +++ b/nipype2pydra/pkg_gen/resources/templates/related-packages/conftest_.py @@ -0,0 +1,37 @@ +import os +import logging +from pathlib import Path +import tempfile +import pytest + +# Set DEBUG logging for unittests + +log_level = logging.WARNING + +logger = logging.getLogger("fileformats") +logger.setLevel(log_level) + +sch = logging.StreamHandler() +sch.setLevel(log_level) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +sch.setFormatter(formatter) +logger.addHandler(sch) + + +# For debugging in IDE's don't catch raised exceptions and let the IDE +# break at it +if os.getenv("_PYTEST_RAISE", "0") != "0": + + @pytest.hookimpl(tryfirst=True) + def pytest_exception_interact(call): + raise call.excinfo.value + + @pytest.hookimpl(tryfirst=True) + def pytest_internalerror(excinfo): + raise excinfo.value + + +@pytest.fixture +def work_dir(): + work_dir = tempfile.mkdtemp() + return Path(work_dir) diff --git a/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/LICENSE b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/LICENSE new file mode 100644 index 00000000..e00bcb30 --- /dev/null +++ b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/LICENSE @@ -0,0 +1,13 @@ + Copyright 2021 Nipype developers + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/README.rst b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/README.rst new file mode 100644 index 00000000..4aa206c0 --- /dev/null +++ b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/README.rst @@ -0,0 +1,32 @@ +FileFormats-medimage-CHANGEME Extras +==================================== + +.. image:: https://github.com/nipype/pydra-freesurfer/actions/workflows/ci-cd.yaml/badge.svg + :target: https://github.com/nipype/pydra-freesurfer/actions/workflows/ci-cd.yaml + + +This is a extras module for the `fileformats-medimage-CHANGEME `__ +fileformats extension package, which provides additional functionality to format classes (i.e. aside +from basic identification and validation), such as conversion tools, metadata parsers, +sample data generators, etc... + + +Quick Installation +------------------ + +This extension can be installed for Python 3 using *pip*:: + + $ pip3 install fileformats-medimage-CHANGEME-extras + +This will install the package, base packages, and any other dependencies required to +implement the extra functionality. + +License +------- + +This work is licensed under a +`Creative Commons Attribution 4.0 International License `_ + +.. image:: https://i.creativecommons.org/l/by/4.0/88x31.png + :target: http://creativecommons.org/licenses/by/4.0/ + :alt: Creative Commons Attribution 4.0 International License diff --git a/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/fileformats/extras/medimage_CHANGEME/__init__.py b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/fileformats/extras/medimage_CHANGEME/__init__.py new file mode 100644 index 00000000..8dee4bf8 --- /dev/null +++ b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/fileformats/extras/medimage_CHANGEME/__init__.py @@ -0,0 +1 @@ +from ._version import __version__ diff --git a/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/pyproject.toml b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/pyproject.toml new file mode 100644 index 00000000..fbb1237e --- /dev/null +++ b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats-extras/pyproject.toml @@ -0,0 +1,87 @@ +[build-system] +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" + +[project] +name = "fileformats-medimage-CHANGEME-extras" +description = "Extensions to add functionality to tool-specific *fileformats* classes" +readme = "README.rst" +requires-python = ">=3.8" +dependencies = [ + "fileformats", + "fileformats-medimage-CHANGEME", + "pydra >= 0.23.0a" +] +license = {file = "LICENSE"} +authors = [ + {name = "Thomas G. Close", email = "tom.g.close@gmail.com"}, +] +maintainers = [ + {name = "Thomas G. Close", email = "tom.g.close@gmail.com"}, +] +keywords = [ + "file formats", + "data", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: Apache Software License", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Scientific/Engineering", +] +dynamic = ["version"] + +[project.optional-dependencies] +dev = [ + "black", + "pre-commit", + "codespell", + "flake8", + "flake8-pyproject", +] +test = [ + "pytest >=6.2.5", + "pytest-env>=0.6.2", + "pytest-cov>=2.12.1", + "codecov", +] + +converters = [ +] + +[project.urls] +repository = "https://github.com/nipype/pydra-CHANGEME" + +[tool.hatch.version] +source = "vcs" +raw-options = { root = "../.." } + +[tool.hatch.build.hooks.vcs] +version-file = "fileformats/extras/medimage_CHANGEME/_version.py" + +[tool.hatch.build.targets.wheel] +packages = ["fileformats"] + +[tool.black] +target-version = ['py38'] +exclude = "fileformats/extras/medimage_CHANGEME/_version.py" + +[tool.codespell] +ignore-words = ".codespell-ignorewords" + +[tool.flake8] +doctests = true +per-file-ignores = [ + "__init__.py:F401" +] +max-line-length = 88 +select = "C,E,F,W,B,B950" +extend-ignore = ['E203', 'E501', 'E129'] diff --git a/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/LICENSE b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/LICENSE new file mode 100644 index 00000000..e00bcb30 --- /dev/null +++ b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/LICENSE @@ -0,0 +1,13 @@ + Copyright 2021 Nipype developers + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/README.rst b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/README.rst new file mode 100644 index 00000000..1d07e924 --- /dev/null +++ b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/README.rst @@ -0,0 +1,28 @@ +FileFormats-medimage-CHANGEME +============================= + +.. image:: https://github.com/nipype/pydra-CHANGEME/actions/workflows/ci-cd.yml/badge.svg + :target: https://github.com/nipype/pydra-CHANGEME/actions/workflows/ci-cd.yml + +This is an extension module of the `fileformats `__ +package for defining file formats that are specific to the CHANGME software toolkit. + + +Quick Installation +------------------ + +This extension can be installed for Python 3 using *pip*:: + + $ pip3 install fileformats-medimage-CHANGEME + +This will install the format extensions and dependent base packages. + +License +------- + +This work is licensed under a +`Creative Commons Attribution 4.0 International License `_ + +.. image:: https://i.creativecommons.org/l/by/4.0/88x31.png + :target: http://creativecommons.org/licenses/by/4.0/ + :alt: Creative Commons Attribution 4.0 International License diff --git a/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/fileformats/medimage_CHANGEME/__init__.py b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/fileformats/medimage_CHANGEME/__init__.py new file mode 100644 index 00000000..8dee4bf8 --- /dev/null +++ b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/fileformats/medimage_CHANGEME/__init__.py @@ -0,0 +1 @@ +from ._version import __version__ diff --git a/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/pyproject.toml b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/pyproject.toml new file mode 100644 index 00000000..b070d1f8 --- /dev/null +++ b/nipype2pydra/pkg_gen/resources/templates/related-packages/fileformats/pyproject.toml @@ -0,0 +1,84 @@ +[build-system] +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" + +[project] +name = "fileformats-medimage-CHANGEME" +description = "Classes for representing different file formats in Python classes for use in type hinting in data workflows" +readme = "README.rst" +requires-python = ">=3.8" +dependencies = [ + "fileformats", + "fileformats-medimage" +] +license = {file = "LICENSE"} +authors = [ + {name = "Thomas G. Close", email = "tom.g.close@gmail.com"}, +] +maintainers = [ + {name = "Thomas G. Close", email = "tom.g.close@gmail.com"}, +] +keywords = [ + "file formats", + "data", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: Apache Software License", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Scientific/Engineering", +] +dynamic = ["version"] + +[project.optional-dependencies] +dev = [ + "black", + "pre-commit", + "codespell", + "flake8", + "flake8-pyproject", +] +test = [ + "pytest >=6.2.5", + "pytest-env>=0.6.2", + "pytest-cov>=2.12.1", + "codecov", + "fileformats-medimage-CHANGME-extras", +] + +[project.urls] +repository = "https://github.com/nipype/pydra-CHANGEME" + +[tool.hatch.version] +source = "vcs" +raw-options = { root = "../.." } + +[tool.hatch.build.hooks.vcs] +version-file = "fileformats/medimage_CHANGEME/_version.py" + +[tool.hatch.build.targets.wheel] +packages = ["fileformats"] + +[tool.black] +target-version = ['py38'] +exclude = "fileformats/medimage_CHANGEME/_version.py" + +[tool.codespell] +ignore-words = ".codespell-ignorewords" + +[tool.flake8] +doctests = true +per-file-ignores = [ + "__init__.py:F401" +] +max-line-length = 88 +select = "C,E,F,W,B,B950" +extend-ignore = ['E203', 'E501', 'E129'] diff --git a/nipype2pydra/task/base.py b/nipype2pydra/task/base.py index 736aff0a..9a610866 100644 --- a/nipype2pydra/task/base.py +++ b/nipype2pydra/task/base.py @@ -913,6 +913,8 @@ def create_doctests(self, input_fields, nonstd_types): else: if isinstance(val, str): val = f'"{val}"' + if val is None and is_fileset(tp): + val = f"{tp.__name__}.mock()" if val is not attrs.NOTHING: doctest_str += f" >>> task.inputs.{nm} = {val}\n" doctest_str += " >>> task.cmdline\n" diff --git a/nipype2pydra/task/tests/test_task.py b/nipype2pydra/task/tests/test_task.py index a2ff8e1f..7de438f3 100644 --- a/nipype2pydra/task/tests/test_task.py +++ b/nipype2pydra/task/tests/test_task.py @@ -2,7 +2,7 @@ import yaml import pytest import logging -from conftest import show_cli_trace +from nipype2pydra.utils import show_cli_trace from traceback import format_exc from nipype2pydra.cli.task import task as task_cli from nipype2pydra.utils import add_to_sys_path, add_exc_note, INBUILT_NIPYPE_TRAIT_NAMES diff --git a/nipype2pydra/tests/test_pkg_gen.py b/nipype2pydra/tests/test_pkg_gen.py index 61c99bb6..e09e3366 100644 --- a/nipype2pydra/tests/test_pkg_gen.py +++ b/nipype2pydra/tests/test_pkg_gen.py @@ -1,5 +1,5 @@ from nipype2pydra.cli.pkg_gen import pkg_gen -from conftest import show_cli_trace +from nipype2pydra.utils import show_cli_trace def test_pkg_gen(cli_runner, tmp_path):