junifer 0.0.4.dev831__py3-none-any.whl → 0.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. junifer/__init__.py +17 -0
  2. junifer/_version.py +2 -2
  3. junifer/api/__init__.py +4 -1
  4. junifer/api/cli.py +91 -1
  5. junifer/api/decorators.py +9 -0
  6. junifer/api/functions.py +56 -10
  7. junifer/api/parser.py +3 -0
  8. junifer/api/queue_context/__init__.py +4 -1
  9. junifer/api/queue_context/gnu_parallel_local_adapter.py +16 -6
  10. junifer/api/queue_context/htcondor_adapter.py +16 -5
  11. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +41 -12
  12. junifer/api/queue_context/tests/test_htcondor_adapter.py +48 -15
  13. junifer/api/res/afni/run_afni_docker.sh +1 -1
  14. junifer/api/res/ants/run_ants_docker.sh +1 -1
  15. junifer/api/res/freesurfer/mri_binarize +3 -0
  16. junifer/api/res/freesurfer/mri_mc +3 -0
  17. junifer/api/res/freesurfer/mri_pretess +3 -0
  18. junifer/api/res/freesurfer/mris_convert +3 -0
  19. junifer/api/res/freesurfer/run_freesurfer_docker.sh +61 -0
  20. junifer/api/res/fsl/run_fsl_docker.sh +1 -1
  21. junifer/api/res/{run_conda.sh → run_conda.bash} +1 -1
  22. junifer/api/res/run_conda.zsh +23 -0
  23. junifer/api/res/run_venv.bash +22 -0
  24. junifer/api/res/{run_venv.sh → run_venv.zsh} +1 -1
  25. junifer/api/tests/test_api_utils.py +4 -2
  26. junifer/api/tests/test_cli.py +83 -0
  27. junifer/api/tests/test_functions.py +27 -2
  28. junifer/configs/__init__.py +1 -1
  29. junifer/configs/juseless/__init__.py +4 -1
  30. junifer/configs/juseless/datagrabbers/__init__.py +10 -1
  31. junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +4 -3
  32. junifer/configs/juseless/datagrabbers/camcan_vbm.py +3 -0
  33. junifer/configs/juseless/datagrabbers/ixi_vbm.py +4 -3
  34. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +1 -3
  35. junifer/configs/juseless/datagrabbers/ucla.py +12 -9
  36. junifer/configs/juseless/datagrabbers/ukb_vbm.py +3 -0
  37. junifer/data/__init__.py +21 -1
  38. junifer/data/coordinates.py +10 -19
  39. junifer/data/masks/ukb/UKB_15K_GM_template.nii.gz +0 -0
  40. junifer/data/masks.py +58 -87
  41. junifer/data/parcellations.py +14 -3
  42. junifer/data/template_spaces.py +4 -1
  43. junifer/data/tests/test_masks.py +26 -37
  44. junifer/data/utils.py +3 -0
  45. junifer/datagrabber/__init__.py +18 -1
  46. junifer/datagrabber/aomic/__init__.py +3 -0
  47. junifer/datagrabber/aomic/id1000.py +70 -37
  48. junifer/datagrabber/aomic/piop1.py +69 -36
  49. junifer/datagrabber/aomic/piop2.py +71 -38
  50. junifer/datagrabber/aomic/tests/test_id1000.py +44 -100
  51. junifer/datagrabber/aomic/tests/test_piop1.py +65 -108
  52. junifer/datagrabber/aomic/tests/test_piop2.py +45 -102
  53. junifer/datagrabber/base.py +13 -6
  54. junifer/datagrabber/datalad_base.py +13 -1
  55. junifer/datagrabber/dmcc13_benchmark.py +36 -53
  56. junifer/datagrabber/hcp1200/__init__.py +3 -0
  57. junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -0
  58. junifer/datagrabber/hcp1200/hcp1200.py +4 -1
  59. junifer/datagrabber/multiple.py +45 -6
  60. junifer/datagrabber/pattern.py +170 -62
  61. junifer/datagrabber/pattern_datalad.py +25 -12
  62. junifer/datagrabber/pattern_validation_mixin.py +388 -0
  63. junifer/datagrabber/tests/test_datalad_base.py +4 -4
  64. junifer/datagrabber/tests/test_dmcc13_benchmark.py +46 -19
  65. junifer/datagrabber/tests/test_multiple.py +161 -84
  66. junifer/datagrabber/tests/test_pattern.py +45 -0
  67. junifer/datagrabber/tests/test_pattern_datalad.py +4 -4
  68. junifer/datagrabber/tests/test_pattern_validation_mixin.py +249 -0
  69. junifer/datareader/__init__.py +4 -1
  70. junifer/datareader/default.py +95 -43
  71. junifer/external/BrainPrint/brainprint/__init__.py +4 -0
  72. junifer/external/BrainPrint/brainprint/_version.py +3 -0
  73. junifer/external/BrainPrint/brainprint/asymmetry.py +91 -0
  74. junifer/external/BrainPrint/brainprint/brainprint.py +441 -0
  75. junifer/external/BrainPrint/brainprint/surfaces.py +258 -0
  76. junifer/external/BrainPrint/brainprint/utils/__init__.py +1 -0
  77. junifer/external/BrainPrint/brainprint/utils/_config.py +112 -0
  78. junifer/external/BrainPrint/brainprint/utils/utils.py +188 -0
  79. junifer/external/__init__.py +1 -1
  80. junifer/external/nilearn/__init__.py +5 -1
  81. junifer/external/nilearn/junifer_connectivity_measure.py +483 -0
  82. junifer/external/nilearn/junifer_nifti_spheres_masker.py +23 -9
  83. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +1089 -0
  84. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +76 -1
  85. junifer/markers/__init__.py +23 -1
  86. junifer/markers/base.py +68 -28
  87. junifer/markers/brainprint.py +459 -0
  88. junifer/markers/collection.py +10 -2
  89. junifer/markers/complexity/__init__.py +10 -0
  90. junifer/markers/complexity/complexity_base.py +26 -43
  91. junifer/markers/complexity/hurst_exponent.py +3 -0
  92. junifer/markers/complexity/multiscale_entropy_auc.py +3 -0
  93. junifer/markers/complexity/perm_entropy.py +3 -0
  94. junifer/markers/complexity/range_entropy.py +3 -0
  95. junifer/markers/complexity/range_entropy_auc.py +3 -0
  96. junifer/markers/complexity/sample_entropy.py +3 -0
  97. junifer/markers/complexity/tests/test_hurst_exponent.py +11 -3
  98. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +11 -3
  99. junifer/markers/complexity/tests/test_perm_entropy.py +11 -3
  100. junifer/markers/complexity/tests/test_range_entropy.py +11 -3
  101. junifer/markers/complexity/tests/test_range_entropy_auc.py +11 -3
  102. junifer/markers/complexity/tests/test_sample_entropy.py +11 -3
  103. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +11 -3
  104. junifer/markers/complexity/weighted_perm_entropy.py +3 -0
  105. junifer/markers/ets_rss.py +27 -42
  106. junifer/markers/falff/__init__.py +3 -0
  107. junifer/markers/falff/_afni_falff.py +5 -2
  108. junifer/markers/falff/_junifer_falff.py +3 -0
  109. junifer/markers/falff/falff_base.py +20 -46
  110. junifer/markers/falff/falff_parcels.py +56 -27
  111. junifer/markers/falff/falff_spheres.py +60 -29
  112. junifer/markers/falff/tests/test_falff_parcels.py +39 -23
  113. junifer/markers/falff/tests/test_falff_spheres.py +39 -23
  114. junifer/markers/functional_connectivity/__init__.py +9 -0
  115. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +63 -60
  116. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +45 -32
  117. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +49 -36
  118. junifer/markers/functional_connectivity/functional_connectivity_base.py +71 -70
  119. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +34 -25
  120. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +40 -30
  121. junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +11 -7
  122. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +27 -7
  123. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +28 -12
  124. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +35 -11
  125. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +36 -62
  126. junifer/markers/parcel_aggregation.py +47 -61
  127. junifer/markers/reho/__init__.py +3 -0
  128. junifer/markers/reho/_afni_reho.py +5 -2
  129. junifer/markers/reho/_junifer_reho.py +4 -1
  130. junifer/markers/reho/reho_base.py +8 -27
  131. junifer/markers/reho/reho_parcels.py +28 -17
  132. junifer/markers/reho/reho_spheres.py +27 -18
  133. junifer/markers/reho/tests/test_reho_parcels.py +8 -3
  134. junifer/markers/reho/tests/test_reho_spheres.py +8 -3
  135. junifer/markers/sphere_aggregation.py +43 -59
  136. junifer/markers/temporal_snr/__init__.py +3 -0
  137. junifer/markers/temporal_snr/temporal_snr_base.py +23 -32
  138. junifer/markers/temporal_snr/temporal_snr_parcels.py +9 -6
  139. junifer/markers/temporal_snr/temporal_snr_spheres.py +9 -6
  140. junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +6 -3
  141. junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +6 -3
  142. junifer/markers/tests/test_brainprint.py +58 -0
  143. junifer/markers/tests/test_collection.py +9 -8
  144. junifer/markers/tests/test_ets_rss.py +15 -9
  145. junifer/markers/tests/test_markers_base.py +17 -18
  146. junifer/markers/tests/test_parcel_aggregation.py +93 -32
  147. junifer/markers/tests/test_sphere_aggregation.py +72 -19
  148. junifer/onthefly/__init__.py +4 -1
  149. junifer/onthefly/read_transform.py +3 -0
  150. junifer/pipeline/__init__.py +9 -1
  151. junifer/pipeline/pipeline_step_mixin.py +21 -4
  152. junifer/pipeline/registry.py +3 -0
  153. junifer/pipeline/singleton.py +3 -0
  154. junifer/pipeline/tests/test_registry.py +1 -1
  155. junifer/pipeline/update_meta_mixin.py +3 -0
  156. junifer/pipeline/utils.py +67 -1
  157. junifer/pipeline/workdir_manager.py +3 -0
  158. junifer/preprocess/__init__.py +10 -2
  159. junifer/preprocess/base.py +6 -3
  160. junifer/preprocess/confounds/__init__.py +3 -0
  161. junifer/preprocess/confounds/fmriprep_confound_remover.py +47 -60
  162. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +72 -113
  163. junifer/preprocess/smoothing/__init__.py +9 -0
  164. junifer/preprocess/smoothing/_afni_smoothing.py +119 -0
  165. junifer/preprocess/smoothing/_fsl_smoothing.py +116 -0
  166. junifer/preprocess/smoothing/_nilearn_smoothing.py +69 -0
  167. junifer/preprocess/smoothing/smoothing.py +174 -0
  168. junifer/preprocess/smoothing/tests/test_smoothing.py +94 -0
  169. junifer/preprocess/warping/__init__.py +3 -0
  170. junifer/preprocess/warping/_ants_warper.py +3 -0
  171. junifer/preprocess/warping/_fsl_warper.py +3 -0
  172. junifer/stats.py +4 -1
  173. junifer/storage/__init__.py +9 -1
  174. junifer/storage/base.py +40 -1
  175. junifer/storage/hdf5.py +71 -9
  176. junifer/storage/pandas_base.py +3 -0
  177. junifer/storage/sqlite.py +3 -0
  178. junifer/storage/tests/test_hdf5.py +82 -10
  179. junifer/storage/utils.py +9 -0
  180. junifer/testing/__init__.py +4 -1
  181. junifer/testing/datagrabbers.py +13 -6
  182. junifer/testing/tests/test_partlycloudytesting_datagrabber.py +7 -7
  183. junifer/testing/utils.py +3 -0
  184. junifer/utils/__init__.py +13 -2
  185. junifer/utils/fs.py +3 -0
  186. junifer/utils/helpers.py +32 -1
  187. junifer/utils/logging.py +33 -4
  188. junifer/utils/tests/test_logging.py +8 -0
  189. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/METADATA +17 -16
  190. junifer-0.0.5.dist-info/RECORD +275 -0
  191. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/WHEEL +1 -1
  192. junifer/datagrabber/tests/test_datagrabber_utils.py +0 -218
  193. junifer/datagrabber/utils.py +0 -230
  194. junifer/preprocess/ants/__init__.py +0 -4
  195. junifer/preprocess/ants/ants_apply_transforms_warper.py +0 -185
  196. junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +0 -56
  197. junifer/preprocess/bold_warper.py +0 -265
  198. junifer/preprocess/fsl/__init__.py +0 -4
  199. junifer/preprocess/fsl/apply_warper.py +0 -179
  200. junifer/preprocess/fsl/tests/test_apply_warper.py +0 -45
  201. junifer/preprocess/tests/test_bold_warper.py +0 -159
  202. junifer-0.0.4.dev831.dist-info/RECORD +0 -257
  203. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/AUTHORS.rst +0 -0
  204. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/LICENSE.md +0 -0
  205. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/entry_points.txt +0 -0
  206. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/top_level.txt +0 -0
junifer/__init__.py CHANGED
@@ -20,3 +20,20 @@ from . import (
20
20
  onthefly,
21
21
  )
22
22
  from ._version import __version__
23
+
24
+
25
+ __all__ = [
26
+ "api",
27
+ "configs",
28
+ "data",
29
+ "datagrabber",
30
+ "datareader",
31
+ "markers",
32
+ "pipeline",
33
+ "preprocess",
34
+ "stats",
35
+ "storage",
36
+ "utils",
37
+ "external",
38
+ "onthefly",
39
+ ]
junifer/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.0.4.dev831'
16
- __version_tuple__ = version_tuple = (0, 0, 4, 'dev831')
15
+ __version__ = version = '0.0.5'
16
+ __version_tuple__ = version_tuple = (0, 0, 5)
junifer/api/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- """Provide imports for api sub-package."""
1
+ """Public API and CLI components."""
2
2
 
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
@@ -7,3 +7,6 @@
7
7
  from . import decorators
8
8
  from .cli import cli
9
9
  from .functions import collect, queue, run
10
+
11
+
12
+ __all__ = ["decorators", "cli", "collect", "queue", "run"]
junifer/api/cli.py CHANGED
@@ -8,7 +8,7 @@ import pathlib
8
8
  import subprocess
9
9
  import sys
10
10
  from pathlib import Path
11
- from typing import Dict, List, Tuple, Union
11
+ from typing import Dict, List, Optional, Tuple, Union
12
12
 
13
13
  import click
14
14
  import pandas as pd
@@ -20,6 +20,7 @@ from ..utils.logging import (
20
20
  warn_with_log,
21
21
  )
22
22
  from .functions import collect as api_collect
23
+ from .functions import list_elements as api_list_elements
23
24
  from .functions import queue as api_queue
24
25
  from .functions import reset as api_reset
25
26
  from .functions import run as api_run
@@ -451,6 +452,69 @@ def reset(
451
452
  api_reset(config)
452
453
 
453
454
 
455
+ @cli.command()
456
+ @click.argument(
457
+ "filepath",
458
+ type=click.Path(
459
+ exists=True, readable=True, dir_okay=False, path_type=pathlib.Path
460
+ ),
461
+ )
462
+ @click.option("--element", type=str, multiple=True)
463
+ @click.option(
464
+ "-o",
465
+ "--output-file",
466
+ type=click.Path(dir_okay=False, writable=True, path_type=pathlib.Path),
467
+ )
468
+ @click.option(
469
+ "-v",
470
+ "--verbose",
471
+ type=click.UNPROCESSED,
472
+ callback=_validate_verbose,
473
+ default="info",
474
+ )
475
+ def list_elements(
476
+ filepath: click.Path,
477
+ element: Tuple[str],
478
+ output_file: Optional[click.Path],
479
+ verbose: Union[str, int],
480
+ ) -> None:
481
+ """Element listing command for CLI.
482
+
483
+ \f
484
+
485
+ Parameters
486
+ ----------
487
+ filepath : click.Path
488
+ The filepath to the configuration file.
489
+ element : tuple of str
490
+ The element to operate on.
491
+ output_file : click.Path or None
492
+ The path to write the output to. If not None, writing to
493
+ stdout is not performed.
494
+ verbose : click.Choice
495
+ The verbosity level: warning, info or debug (default "info").
496
+
497
+ """
498
+ configure_logging(level=verbose)
499
+ # Parse YAML
500
+ config = parse_yaml(filepath) # type: ignore
501
+ # Fetch datagrabber
502
+ datagrabber = config["datagrabber"]
503
+ # Parse elements
504
+ elements = _parse_elements(element, config)
505
+ # Perform operation
506
+ listed_elements = api_list_elements(
507
+ datagrabber=datagrabber,
508
+ elements=elements,
509
+ )
510
+ # Check if output file is provided
511
+ if output_file is not None:
512
+ output_file.touch()
513
+ output_file.write_text(listed_elements)
514
+ else:
515
+ click.secho(listed_elements, fg="blue")
516
+
517
+
454
518
  @cli.group()
455
519
  def setup() -> None: # pragma: no cover
456
520
  """Configure commands for Junifer."""
@@ -524,3 +588,29 @@ def ants_docker() -> None: # pragma: no cover
524
588
  export PATH="$PATH:{ants_wrappers_path}"
525
589
  """
526
590
  click.secho(msg, fg="blue")
591
+
592
+
593
+ @setup.command("freesurfer-docker")
594
+ def freesurfer_docker() -> None: # pragma: no cover
595
+ """Configure FreeSurfer-Docker wrappers."""
596
+ import junifer
597
+
598
+ pkg_path = Path(junifer.__path__[0]) # type: ignore
599
+ fs_wrappers_path = pkg_path / "api" / "res" / "freesurfer"
600
+ msg = f"""
601
+ Installation instructions for FreeSurfer-Docker wrappers:
602
+
603
+ 1. Install Docker: https://docs.docker.com/get-docker/
604
+
605
+ 2. Get the FreeSurfer-Docker image by running this on the command line:
606
+
607
+ docker pull freesurfer/freesurfer
608
+
609
+ 3. Get license from: https://surfer.nmr.mgh.harvard.edu/registration.html .
610
+ You can skip this step if you already have one.
611
+
612
+ 4. Add this line to the ~/.bashrc or ~/.zshrc file:
613
+
614
+ export PATH="$PATH:{fs_wrappers_path}"
615
+ """
616
+ click.secho(msg, fg="blue")
junifer/api/decorators.py CHANGED
@@ -10,6 +10,15 @@ from typing import Type
10
10
  from ..pipeline.registry import register
11
11
 
12
12
 
13
+ __all__ = [
14
+ "register_datagrabber",
15
+ "register_datareader",
16
+ "register_preprocessor",
17
+ "register_marker",
18
+ "register_storage",
19
+ ]
20
+
21
+
13
22
  def register_datagrabber(klass: Type) -> Type:
14
23
  """Register DataGrabber.
15
24
 
junifer/api/functions.py CHANGED
@@ -5,6 +5,7 @@
5
5
  # Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  # License: AGPL
7
7
 
8
+ import os
8
9
  import shutil
9
10
  import typing
10
11
  from pathlib import Path
@@ -22,6 +23,9 @@ from .queue_context import GnuParallelLocalAdapter, HTCondorAdapter
22
23
  from .utils import yaml
23
24
 
24
25
 
26
+ __all__ = ["run", "collect", "queue", "reset", "list_elements"]
27
+
28
+
25
29
  def _get_datagrabber(datagrabber_config: Dict) -> BaseDataGrabber:
26
30
  """Get DataGrabber.
27
31
 
@@ -91,7 +95,7 @@ def run(
91
95
  datagrabber : dict
92
96
  DataGrabber to use. Must have a key ``kind`` with the kind of
93
97
  DataGrabber to use. All other keys are passed to the DataGrabber
94
- init function.
98
+ constructor.
95
99
  markers : list of dict
96
100
  List of markers to extract. Each marker is a dict with at least two
97
101
  keys: ``name`` and ``kind``. The ``name`` key is used to name the
@@ -101,11 +105,11 @@ def run(
101
105
  storage : dict
102
106
  Storage to use. Must have a key ``kind`` with the kind of
103
107
  storage to use. All other keys are passed to the storage
104
- init function.
108
+ constructor.
105
109
  preprocessors : list of dict, optional
106
110
  List of preprocessors to use. Each preprocessor is a dict with at
107
111
  least a key ``kind`` specifying the preprocessor to use. All other keys
108
- are passed to the preprocessor init function (default None).
112
+ are passed to the preprocessor constructor (default None).
109
113
  elements : str or tuple or list of str or tuple, optional
110
114
  Element(s) to process. Will be used to index the DataGrabber
111
115
  (default None).
@@ -187,7 +191,7 @@ def collect(storage: Dict) -> None:
187
191
  storage : dict
188
192
  Storage to use. Must have a key ``kind`` with the kind of
189
193
  storage to use. All other keys are passed to the storage
190
- init function.
194
+ constructor.
191
195
 
192
196
  """
193
197
  storage_params = storage.copy()
@@ -339,12 +343,12 @@ def reset(config: Dict) -> None:
339
343
  storage = config["storage"]
340
344
  storage_uri = Path(storage["uri"])
341
345
  logger.info(f"Deleting {storage_uri.resolve()!s}")
342
- # Delete storage; will be str
346
+ # Delete storage
343
347
  if storage_uri.exists():
344
- # Delete files in the directory
345
- for file in storage_uri.iterdir():
348
+ # Delete files in the job storage directory
349
+ for file in storage_uri.parent.iterdir():
346
350
  file.unlink(missing_ok=True)
347
- # Remove directory
351
+ # Remove job storage directory
348
352
  storage_uri.parent.rmdir()
349
353
 
350
354
  # Fetch job name (if present)
@@ -359,5 +363,47 @@ def reset(config: Dict) -> None:
359
363
  if job_dir.exists():
360
364
  # Remove files and directories
361
365
  shutil.rmtree(job_dir)
362
- # Remove directory
363
- job_dir.parent.rmdir()
366
+ # Remove parent directory (if empty)
367
+ if not next(os.scandir(job_dir.parent), None):
368
+ job_dir.parent.rmdir()
369
+
370
+
371
+ def list_elements(
372
+ datagrabber: Dict,
373
+ elements: Union[str, List[Union[str, Tuple]], Tuple, None] = None,
374
+ ) -> str:
375
+ """List elements of the datagrabber filtered using `elements`.
376
+
377
+ Parameters
378
+ ----------
379
+ datagrabber : dict
380
+ DataGrabber to index. Must have a key ``kind`` with the kind of
381
+ DataGrabber to use. All other keys are passed to the DataGrabber
382
+ constructor.
383
+ elements : str or tuple or list of str or tuple, optional
384
+ Element(s) to filter using. Will be used to index the DataGrabber
385
+ (default None).
386
+
387
+ """
388
+ # Get datagrabber to use
389
+ datagrabber_object = _get_datagrabber(datagrabber)
390
+
391
+ # Fetch elements
392
+ raw_elements_to_list = []
393
+ with datagrabber_object:
394
+ if elements is not None:
395
+ for element in datagrabber_object.filter(elements):
396
+ raw_elements_to_list.append(element)
397
+ else:
398
+ for element in datagrabber_object:
399
+ raw_elements_to_list.append(element)
400
+
401
+ elements_to_list = []
402
+ for element in raw_elements_to_list:
403
+ # Stringify elements if tuple for operation
404
+ str_element = (
405
+ ",".join(element) if isinstance(element, tuple) else element
406
+ )
407
+ elements_to_list.append(str_element)
408
+
409
+ return "\n".join(elements_to_list)
junifer/api/parser.py CHANGED
@@ -14,6 +14,9 @@ from ..utils.logging import logger, raise_error
14
14
  from .utils import yaml
15
15
 
16
16
 
17
+ __all__ = ["parse_yaml"]
18
+
19
+
17
20
  def parse_yaml(filepath: Union[str, Path]) -> Dict:
18
21
  """Parse YAML.
19
22
 
@@ -1,4 +1,4 @@
1
- """Provide imports for queue context sub-package."""
1
+ """Context adapters for queueing."""
2
2
 
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
@@ -6,3 +6,6 @@
6
6
  from .queue_context_adapter import QueueContextAdapter
7
7
  from .htcondor_adapter import HTCondorAdapter
8
8
  from .gnu_parallel_local_adapter import GnuParallelLocalAdapter
9
+
10
+
11
+ __all__ = ["QueueContextAdapter", "HTCondorAdapter", "GnuParallelLocalAdapter"]
@@ -43,7 +43,8 @@ class GnuParallelLocalAdapter(QueueContextAdapter):
43
43
  Raises
44
44
  ------
45
45
  ValueError
46
- If``env`` is invalid.
46
+ If ``env.kind`` is invalid or
47
+ if ``env.shell`` is invalid.
47
48
 
48
49
  See Also
49
50
  --------
@@ -110,13 +111,22 @@ class GnuParallelLocalAdapter(QueueContextAdapter):
110
111
  f"must be one of {valid_env_kinds}"
111
112
  )
112
113
  else:
114
+ # Check shell
115
+ shell = env.get("shell", "bash")
116
+ valid_shells = ["bash", "zsh"]
117
+ if shell not in valid_shells:
118
+ raise_error(
119
+ f"Invalid value for `env.shell`: {shell}, "
120
+ f"must be one of {valid_shells}"
121
+ )
122
+ self._shell = shell
113
123
  # Set variables
114
124
  if env["kind"] == "local":
115
125
  # No virtual environment
116
126
  self._executable = "junifer"
117
127
  self._arguments = ""
118
128
  else:
119
- self._executable = f"run_{env['kind']}.sh"
129
+ self._executable = f"run_{env['kind']}.{self._shell}"
120
130
  self._arguments = f"{env['name']} junifer"
121
131
  self._exec_path = self._job_dir / self._executable
122
132
 
@@ -135,7 +145,7 @@ class GnuParallelLocalAdapter(QueueContextAdapter):
135
145
  def pre_run(self) -> str:
136
146
  """Return pre-run commands."""
137
147
  fixed = (
138
- "#!/usr/bin/env bash\n\n"
148
+ f"#!/usr/bin/env {self._shell}\n\n"
139
149
  "# This script is auto-generated by junifer.\n\n"
140
150
  "# Force datalad to run in non-interactive mode\n"
141
151
  "DATALAD_UI_INTERACTIVE=false\n"
@@ -146,7 +156,7 @@ class GnuParallelLocalAdapter(QueueContextAdapter):
146
156
  def run(self) -> str:
147
157
  """Return run commands."""
148
158
  return (
149
- "#!/usr/bin/env bash\n\n"
159
+ f"#!/usr/bin/env {self._shell}\n\n"
150
160
  "# This script is auto-generated by junifer.\n\n"
151
161
  "# Run pre_run.sh\n"
152
162
  f"sh {self._pre_run_path.resolve()!s}\n\n"
@@ -166,7 +176,7 @@ class GnuParallelLocalAdapter(QueueContextAdapter):
166
176
  def pre_collect(self) -> str:
167
177
  """Return pre-collect commands."""
168
178
  fixed = (
169
- "#!/usr/bin/env bash\n\n"
179
+ f"#!/usr/bin/env {self._shell}\n\n"
170
180
  "# This script is auto-generated by junifer.\n"
171
181
  )
172
182
  var = self._pre_collect or ""
@@ -175,7 +185,7 @@ class GnuParallelLocalAdapter(QueueContextAdapter):
175
185
  def collect(self) -> str:
176
186
  """Return collect commands."""
177
187
  return (
178
- "#!/usr/bin/env bash\n\n"
188
+ f"#!/usr/bin/env {self._shell}\n\n"
179
189
  "# This script is auto-generated by junifer.\n\n"
180
190
  "# Run pre_collect.sh\n"
181
191
  f"sh {self._pre_collect_path.resolve()!s}\n\n"
@@ -126,7 +126,8 @@ class HTCondorAdapter(QueueContextAdapter):
126
126
  Raises
127
127
  ------
128
128
  ValueError
129
- If ``env.kind`` is invalid.
129
+ If ``env.kind`` is invalid or
130
+ if ``env.shell`` is invalid.
130
131
 
131
132
  """
132
133
  # Set env related variables
@@ -140,13 +141,22 @@ class HTCondorAdapter(QueueContextAdapter):
140
141
  f"must be one of {valid_env_kinds}"
141
142
  )
142
143
  else:
144
+ # Check shell
145
+ shell = env.get("shell", "bash")
146
+ valid_shells = ["bash", "zsh"]
147
+ if shell not in valid_shells:
148
+ raise_error(
149
+ f"Invalid value for `env.shell`: {shell}, "
150
+ f"must be one of {valid_shells}"
151
+ )
152
+ self._shell = shell
143
153
  # Set variables
144
154
  if env["kind"] == "local":
145
155
  # No virtual environment
146
156
  self._executable = "junifer"
147
157
  self._arguments = ""
148
158
  else:
149
- self._executable = f"run_{env['kind']}.sh"
159
+ self._executable = f"run_{env['kind']}.{self._shell}"
150
160
  self._arguments = f"{env['name']} junifer"
151
161
  self._exec_path = self._job_dir / self._executable
152
162
 
@@ -181,7 +191,7 @@ class HTCondorAdapter(QueueContextAdapter):
181
191
  def pre_run(self) -> str:
182
192
  """Return pre-run commands."""
183
193
  fixed = (
184
- "#!/bin/bash\n\n"
194
+ f"#!/usr/bin/env {self._shell}\n\n"
185
195
  "# This script is auto-generated by junifer.\n\n"
186
196
  "# Force datalad to run in non-interactive mode\n"
187
197
  "DATALAD_UI_INTERACTIVE=false\n"
@@ -225,12 +235,13 @@ class HTCondorAdapter(QueueContextAdapter):
225
235
  def pre_collect(self) -> str:
226
236
  """Return pre-collect commands."""
227
237
  fixed = (
228
- "#!/bin/bash\n\n" "# This script is auto-generated by junifer.\n"
238
+ f"#!/usr/bin/env {self._shell}\n\n"
239
+ "# This script is auto-generated by junifer.\n"
229
240
  )
230
241
  var = self._pre_collect or ""
231
242
  # Add commands if collect="yes"
232
243
  if self._collect == "yes":
233
- var += 'if [ "${1}" == "4" ]; then\n' " exit 1\n" "fi\n"
244
+ var += 'if [ "${1}" == "4" ]; then\n exit 1\nfi\n'
234
245
  return fixed + "\n" + var
235
246
 
236
247
  def collect(self) -> str:
@@ -12,11 +12,11 @@ import pytest
12
12
  from junifer.api.queue_context import GnuParallelLocalAdapter
13
13
 
14
14
 
15
- def test_GnuParallelLocalAdapter_env_error() -> None:
15
+ def test_GnuParallelLocalAdapter_env_kind_error() -> None:
16
16
  """Test error for invalid env kind."""
17
17
  with pytest.raises(ValueError, match="Invalid value for `env.kind`"):
18
18
  GnuParallelLocalAdapter(
19
- job_name="check_env",
19
+ job_name="check_env_kind",
20
20
  job_dir=Path("."),
21
21
  yaml_config_path=Path("."),
22
22
  elements=["sub01"],
@@ -24,6 +24,18 @@ def test_GnuParallelLocalAdapter_env_error() -> None:
24
24
  )
25
25
 
26
26
 
27
+ def test_GnuParallelLocalAdapter_env_shell_error() -> None:
28
+ """Test error for invalid env shell."""
29
+ with pytest.raises(ValueError, match="Invalid value for `env.shell`"):
30
+ GnuParallelLocalAdapter(
31
+ job_name="check_env_shell",
32
+ job_dir=Path("."),
33
+ yaml_config_path=Path("."),
34
+ elements=["sub01"],
35
+ env={"kind": "conda", "shell": "fish"},
36
+ )
37
+
38
+
27
39
  @pytest.mark.parametrize(
28
40
  "elements, expected_text",
29
41
  [
@@ -55,14 +67,18 @@ def test_GnuParallelLocalAdapter_elements(
55
67
 
56
68
 
57
69
  @pytest.mark.parametrize(
58
- "pre_run, expected_text",
70
+ "pre_run, expected_text, shell",
59
71
  [
60
- (None, "# Force datalad"),
61
- ("# Check this out\n", "# Check this out"),
72
+ (None, "# Force datalad", "bash"),
73
+ (None, "# Force datalad", "zsh"),
74
+ ("# Check this out\n", "# Check this out", "bash"),
75
+ ("# Check this out\n", "# Check this out", "zsh"),
62
76
  ],
63
77
  )
64
78
  def test_GnuParallelLocalAdapter_pre_run(
65
- pre_run: Optional[str], expected_text: str
79
+ pre_run: Optional[str],
80
+ expected_text: str,
81
+ shell: str,
66
82
  ) -> None:
67
83
  """Test GnuParallelLocalAdapter pre_run().
68
84
 
@@ -72,6 +88,8 @@ def test_GnuParallelLocalAdapter_pre_run(
72
88
  The parametrized pre run text.
73
89
  expected_text : str
74
90
  The parametrized expected text.
91
+ shell : str
92
+ The parametrized expected shell.
75
93
 
76
94
  """
77
95
  adapter = GnuParallelLocalAdapter(
@@ -79,21 +97,26 @@ def test_GnuParallelLocalAdapter_pre_run(
79
97
  job_dir=Path("."),
80
98
  yaml_config_path=Path("."),
81
99
  elements=["sub01"],
100
+ env={"kind": "conda", "name": "junifer", "shell": shell},
82
101
  pre_run=pre_run,
83
102
  )
103
+ assert shell in adapter.pre_run()
84
104
  assert expected_text in adapter.pre_run()
85
105
 
86
106
 
87
107
  @pytest.mark.parametrize(
88
- "pre_collect, expected_text",
108
+ "pre_collect, expected_text, shell",
89
109
  [
90
- (None, "# This script"),
91
- ("# Check this out\n", "# Check this out"),
110
+ (None, "# This script", "bash"),
111
+ (None, "# This script", "zsh"),
112
+ ("# Check this out\n", "# Check this out", "bash"),
113
+ ("# Check this out\n", "# Check this out", "zsh"),
92
114
  ],
93
115
  )
94
116
  def test_GnuParallelLocalAdapter_pre_collect(
95
117
  pre_collect: Optional[str],
96
118
  expected_text: str,
119
+ shell: str,
97
120
  ) -> None:
98
121
  """Test GnuParallelLocalAdapter pre_collect().
99
122
 
@@ -103,6 +126,8 @@ def test_GnuParallelLocalAdapter_pre_collect(
103
126
  The parametrized pre collect text.
104
127
  expected_text : str
105
128
  The parametrized expected text.
129
+ shell : str
130
+ The parametrized expected shell.
106
131
 
107
132
  """
108
133
  adapter = GnuParallelLocalAdapter(
@@ -110,8 +135,10 @@ def test_GnuParallelLocalAdapter_pre_collect(
110
135
  job_dir=Path("."),
111
136
  yaml_config_path=Path("."),
112
137
  elements=["sub01"],
138
+ env={"kind": "venv", "name": "junifer", "shell": shell},
113
139
  pre_collect=pre_collect,
114
140
  )
141
+ assert shell in adapter.pre_collect()
115
142
  assert expected_text in adapter.pre_collect()
116
143
 
117
144
 
@@ -140,8 +167,10 @@ def test_GnuParallelLocalAdapter_collect() -> None:
140
167
  @pytest.mark.parametrize(
141
168
  "env",
142
169
  [
143
- {"kind": "conda", "name": "junifer"},
144
- {"kind": "venv", "name": "./junifer"},
170
+ {"kind": "conda", "name": "junifer", "shell": "bash"},
171
+ {"kind": "conda", "name": "junifer", "shell": "zsh"},
172
+ {"kind": "venv", "name": "./junifer", "shell": "bash"},
173
+ {"kind": "venv", "name": "./junifer", "shell": "zsh"},
145
174
  ],
146
175
  )
147
176
  def test_GnuParallelLocalAdapter_prepare(
@@ -177,7 +206,7 @@ def test_GnuParallelLocalAdapter_prepare(
177
206
  adapter.prepare()
178
207
 
179
208
  assert "GNU parallel" in caplog.text
180
- assert f"Copying run_{env['kind']}" in caplog.text
209
+ assert f"Copying run_{env['kind']}.{env['shell']}" in caplog.text
181
210
  assert "Writing pre_run.sh" in caplog.text
182
211
  assert "Writing run_test_prepare.sh" in caplog.text
183
212
  assert "Writing pre_collect.sh" in caplog.text