girder-slicer-cli-web 5.0.19.dev14__tar.gz → 5.0.19.dev18__tar.gz

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 (74) hide show
  1. {girder_slicer_cli_web-5.0.19.dev14/girder_slicer_cli_web.egg-info → girder_slicer_cli_web-5.0.19.dev18}/PKG-INFO +3 -1
  2. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/README.rst +2 -0
  3. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18/girder_slicer_cli_web.egg-info}/PKG-INFO +3 -1
  4. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/girder_slicer_cli_web.egg-info/scm_version.json +2 -2
  5. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/girder_worker_plugin/direct_docker_run.py +25 -0
  6. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/prepare_task.py +65 -17
  7. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/rest_slicer_cli.py +3 -0
  8. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/MANIFEST.in +0 -0
  9. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/docs/worker_management.rst +0 -0
  10. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-average-color/.dockerignore +0 -0
  11. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-average-color/Dockerfile +0 -0
  12. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-average-color/README.rst +0 -0
  13. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-average-color/average_color/average_color.py +0 -0
  14. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-average-color/average_color/average_color.xml +0 -0
  15. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-average-color/cli_list.json +0 -0
  16. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-average-color/cli_list.py +0 -0
  17. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-girder-requests/Dockerfile +0 -0
  18. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-girder-requests/README.rst +0 -0
  19. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-girder-requests/cli_list.json +0 -0
  20. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-girder-requests/cli_list.py +0 -0
  21. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-girder-requests/girder_requests/girder_requests.py +0 -0
  22. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/example-girder-requests/girder_requests/girder_requests.xml +0 -0
  23. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/girder_slicer_cli_web.egg-info/SOURCES.txt +0 -0
  24. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/girder_slicer_cli_web.egg-info/dependency_links.txt +0 -0
  25. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/girder_slicer_cli_web.egg-info/entry_points.txt +0 -0
  26. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/girder_slicer_cli_web.egg-info/not-zip-safe +0 -0
  27. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/girder_slicer_cli_web.egg-info/requires.txt +0 -0
  28. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/girder_slicer_cli_web.egg-info/scm_file_list.json +0 -0
  29. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/girder_slicer_cli_web.egg-info/top_level.txt +0 -0
  30. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/pyproject.toml +0 -0
  31. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/setup.cfg +0 -0
  32. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/setup.py +0 -0
  33. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/__init__.py +0 -0
  34. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/cli_list_entrypoint.py +0 -0
  35. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/cli_utils.py +0 -0
  36. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/config.py +0 -0
  37. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/ctk_cli_adjustment.py +0 -0
  38. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/docker_resource.py +0 -0
  39. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/girder_plugin.py +0 -0
  40. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/girder_worker_plugin/__init__.py +0 -0
  41. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/girder_worker_plugin/cli_progress.py +0 -0
  42. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/image_job.py +0 -0
  43. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/models/__init__.py +0 -0
  44. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/models/docker_image.py +0 -0
  45. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/models/exceptions.py +0 -0
  46. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/models/json_to_xml.py +0 -0
  47. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/models/parser.py +0 -0
  48. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/models/schema.json +0 -0
  49. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/upload_slicer_cli_task.py +0 -0
  50. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/web_client/dist/girder-plugin-slicer-cli-web.umd.cjs +0 -0
  51. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/web_client/dist/style.css +0 -0
  52. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/slicer_cli_web/worker_tools.py +0 -0
  53. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/.dockerignore +0 -0
  54. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Dockerfile +0 -0
  55. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example1/Example1.json +0 -0
  56. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example1/Example1.py +0 -0
  57. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example1/Example1.xml +0 -0
  58. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example1/Example1.yaml +0 -0
  59. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example2/Example2.json +0 -0
  60. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example2/Example2.py +0 -0
  61. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example2/Example2.xml +0 -0
  62. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example2/Example2.yaml +0 -0
  63. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example3/Example3.json +0 -0
  64. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example3/Example3.py +0 -0
  65. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example3/Example3.xml +0 -0
  66. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/Example3/Example3.yaml +0 -0
  67. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/ExampleProgress/ExampleProgress.json +0 -0
  68. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/ExampleProgress/ExampleProgress.py +0 -0
  69. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/ExampleProgress/progress_helper.py +0 -0
  70. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/ExampleSubJob/ExampleSubJob.json +0 -0
  71. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/ExampleSubJob/ExampleSubJob.py +0 -0
  72. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/cli_list.json +0 -0
  73. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/small-docker/cli_list.py +0 -0
  74. {girder_slicer_cli_web-5.0.19.dev14 → girder_slicer_cli_web-5.0.19.dev18}/utils/xmltojson.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: girder-slicer-cli-web
3
- Version: 5.0.19.dev14
3
+ Version: 5.0.19.dev18
4
4
  Summary: A girder plugin for exposing slicer CLIs over the web
5
5
  Home-page: https://github.com/girder/slicer_cli_web
6
6
  Author: Kitware, Inc.
@@ -141,6 +141,8 @@ The XML must conform to the `Slicer Execution Schema <https://www.slicer.org/w/i
141
141
  ``multi`` allow multiple shapes, indicated by separating coordinates of each shape by -1,-1. Note that neither -1,-1 nor -2,-2 are allowed as coordinates within a shape -- to use those, specify them with decimals (e.g., -1.0,-1.0).
142
142
  The submit options will add suggestions on how the UI should handle changes. If present, the option to auto-run a job as soon as a valid shape is set should be present. ``autosubmit`` means this should always happen. ``submit`` or ``submitoff`` offers this as a setting but is default to not submit the job. ``submiton`` offers this as a setting and defaults to submitting the job.
143
143
 
144
+ - Girder-resource input types (``image``, ``file``, ``item``, ``directory``) can have a ``multiple`` property. If ``true``, the parameter accepts a comma-separated list of Girder IDs. Each resource is bound into the container separately (using direct read-only mounts when possible) and the CLI receives a single comma-separated list of container paths, in the submitted order. Inputs with ``multiple`` set are not available for batch processing.
145
+
144
146
  - Some input types (``image``, ``file``, ``item``, ``directory``) can have ``defaultNameMatch``, ``defaultPathMatch``, and ``defaultRelativePath`` properties. The first two are regular expressions designed to give a UI a value to match to prepopulate default values from files or paths that match the regex. ``defaultNameMatch`` is intended to match the final path element, whereas ``defaultPathMatch`` is used on the entire path as a combined string. ``defaultRelativePath`` is used to find a value that has a path relative to some base. In the Girder UI, this might be from an item.
145
147
 
146
148
  - Input types can have a ``datalist`` property. If this is present, when the CLI is first loaded or, possibly periodically after parameters have been changed, the CLI may be called with optional parameters. The CLI is expected to return a new-line separated list of values that can be used as recommended inputs. As an example, a ``string`` input might have a ``datalist`` of ``--enumerate-options``; the cli would be called with the existing parameters PLUS the extra parameter specified by ``datalist``. If the result is sensible, the input control would expose this list to the user. The ``datalist`` property is a json-encoded dictionary that overrides other parameters. This should override parameters that aren't needed to be resolved to produce the datalist (e.g., input and output files) as that will speed up the call. The CLI should respond to the modified call with a response that contains multiple ``<element>some text</element>`` values that will be the suggested data for the control.
@@ -95,6 +95,8 @@ The XML must conform to the `Slicer Execution Schema <https://www.slicer.org/w/i
95
95
  ``multi`` allow multiple shapes, indicated by separating coordinates of each shape by -1,-1. Note that neither -1,-1 nor -2,-2 are allowed as coordinates within a shape -- to use those, specify them with decimals (e.g., -1.0,-1.0).
96
96
  The submit options will add suggestions on how the UI should handle changes. If present, the option to auto-run a job as soon as a valid shape is set should be present. ``autosubmit`` means this should always happen. ``submit`` or ``submitoff`` offers this as a setting but is default to not submit the job. ``submiton`` offers this as a setting and defaults to submitting the job.
97
97
 
98
+ - Girder-resource input types (``image``, ``file``, ``item``, ``directory``) can have a ``multiple`` property. If ``true``, the parameter accepts a comma-separated list of Girder IDs. Each resource is bound into the container separately (using direct read-only mounts when possible) and the CLI receives a single comma-separated list of container paths, in the submitted order. Inputs with ``multiple`` set are not available for batch processing.
99
+
98
100
  - Some input types (``image``, ``file``, ``item``, ``directory``) can have ``defaultNameMatch``, ``defaultPathMatch``, and ``defaultRelativePath`` properties. The first two are regular expressions designed to give a UI a value to match to prepopulate default values from files or paths that match the regex. ``defaultNameMatch`` is intended to match the final path element, whereas ``defaultPathMatch`` is used on the entire path as a combined string. ``defaultRelativePath`` is used to find a value that has a path relative to some base. In the Girder UI, this might be from an item.
99
101
 
100
102
  - Input types can have a ``datalist`` property. If this is present, when the CLI is first loaded or, possibly periodically after parameters have been changed, the CLI may be called with optional parameters. The CLI is expected to return a new-line separated list of values that can be used as recommended inputs. As an example, a ``string`` input might have a ``datalist`` of ``--enumerate-options``; the cli would be called with the existing parameters PLUS the extra parameter specified by ``datalist``. If the result is sensible, the input control would expose this list to the user. The ``datalist`` property is a json-encoded dictionary that overrides other parameters. This should override parameters that aren't needed to be resolved to produce the datalist (e.g., input and output files) as that will speed up the call. The CLI should respond to the modified call with a response that contains multiple ``<element>some text</element>`` values that will be the suggested data for the control.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: girder-slicer-cli-web
3
- Version: 5.0.19.dev14
3
+ Version: 5.0.19.dev18
4
4
  Summary: A girder plugin for exposing slicer CLIs over the web
5
5
  Home-page: https://github.com/girder/slicer_cli_web
6
6
  Author: Kitware, Inc.
@@ -141,6 +141,8 @@ The XML must conform to the `Slicer Execution Schema <https://www.slicer.org/w/i
141
141
  ``multi`` allow multiple shapes, indicated by separating coordinates of each shape by -1,-1. Note that neither -1,-1 nor -2,-2 are allowed as coordinates within a shape -- to use those, specify them with decimals (e.g., -1.0,-1.0).
142
142
  The submit options will add suggestions on how the UI should handle changes. If present, the option to auto-run a job as soon as a valid shape is set should be present. ``autosubmit`` means this should always happen. ``submit`` or ``submitoff`` offers this as a setting but is default to not submit the job. ``submiton`` offers this as a setting and defaults to submitting the job.
143
143
 
144
+ - Girder-resource input types (``image``, ``file``, ``item``, ``directory``) can have a ``multiple`` property. If ``true``, the parameter accepts a comma-separated list of Girder IDs. Each resource is bound into the container separately (using direct read-only mounts when possible) and the CLI receives a single comma-separated list of container paths, in the submitted order. Inputs with ``multiple`` set are not available for batch processing.
145
+
144
146
  - Some input types (``image``, ``file``, ``item``, ``directory``) can have ``defaultNameMatch``, ``defaultPathMatch``, and ``defaultRelativePath`` properties. The first two are regular expressions designed to give a UI a value to match to prepopulate default values from files or paths that match the regex. ``defaultNameMatch`` is intended to match the final path element, whereas ``defaultPathMatch`` is used on the entire path as a combined string. ``defaultRelativePath`` is used to find a value that has a path relative to some base. In the Girder UI, this might be from an item.
145
147
 
146
148
  - Input types can have a ``datalist`` property. If this is present, when the CLI is first loaded or, possibly periodically after parameters have been changed, the CLI may be called with optional parameters. The CLI is expected to return a new-line separated list of values that can be used as recommended inputs. As an example, a ``string`` input might have a ``datalist`` of ``--enumerate-options``; the cli would be called with the existing parameters PLUS the extra parameter specified by ``datalist``. If the result is sensible, the input control would expose this list to the user. The ``datalist`` property is a json-encoded dictionary that overrides other parameters. This should override parameters that aren't needed to be resolved to produce the datalist (e.g., input and output files) as that will speed up the call. The CLI should respond to the modified call with a response that contains multiple ``<element>some text</element>`` values that will be the suggested data for the control.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tag": "5.0.18",
3
- "distance": 14,
4
- "node": "gacf9949d1ba44f30e05a9f368f663e613248289a",
3
+ "distance": 18,
4
+ "node": "gd52fa6a287ed58244e9fb22900d66ecedb0540c0",
5
5
  "dirty": false,
6
6
  "branch": "master",
7
7
  "node_date": "2026-10-01"
@@ -8,6 +8,7 @@ from girder_worker.docker.tasks import DockerTask, _docker_run
8
8
  from girder_worker.docker.transforms import BindMountVolume, ContainerStdOut
9
9
  from girder_worker.docker.transforms.girder import GirderFileIdToVolume
10
10
  from girder_worker.utils import _walk_obj
11
+ from girder_worker.utils.transform import Transform
11
12
  from girder_worker.utils.transforms.girder_io import GirderClientTransform
12
13
 
13
14
  from .cli_progress import CLIProgressCLIWriter
@@ -67,6 +68,27 @@ class DirectGirderFileIdToVolume(GirderFileIdToVolume):
67
68
  return super().transform(**kwargs)
68
69
 
69
70
 
71
+ class CommaJoinedVolumes(Transform):
72
+ """One container argument spanning several volume transforms: their
73
+ resolved container paths, comma-joined in order.
74
+ """
75
+
76
+ def __init__(self, volumes):
77
+ self._volumes = volumes
78
+
79
+ def transform(self, **kwargs):
80
+ return ','.join(str(volume.transform(**kwargs)) for volume in self._volumes)
81
+
82
+ def cleanup(self, **kwargs):
83
+ for volume in self._volumes:
84
+ volume.cleanup(**kwargs)
85
+
86
+ def _repr_model_(self):
87
+ return '<%s.%s: [%s]>' % (
88
+ self.__module__, self.__class__.__name__,
89
+ ', '.join(volume._repr_model_() for volume in self._volumes))
90
+
91
+
70
92
  class GirderApiUrl(GirderClientTransform):
71
93
  def transform(self, **kwargs):
72
94
  return self.gc.urlBase
@@ -85,6 +107,9 @@ def _resolve_direct_file_paths(args, kwargs):
85
107
  path = arg.resolve_direct_file_path()
86
108
  if path:
87
109
  extra_volumes.append(path)
110
+ elif isinstance(arg, CommaJoinedVolumes):
111
+ for volume in arg._volumes:
112
+ resolve(volume)
88
113
  return arg
89
114
  _walk_obj(args, resolve)
90
115
  _walk_obj(kwargs, resolve)
@@ -3,6 +3,7 @@ import logging
3
3
  import os
4
4
 
5
5
  import jinja2
6
+ from girder_plugin_worker.constants import PluginSettings
6
7
 
7
8
  from girder.api.rest import RestException
8
9
  from girder.constants import AccessType
@@ -21,8 +22,7 @@ FOLDER_SUFFIX = '_folder'
21
22
  logger = logging.getLogger(__name__)
22
23
 
23
24
 
24
- def _to_file_volume(param, model):
25
- from girder_plugin_worker.constants import PluginSettings
25
+ def _to_file_volume(param, model, filename=None, gc=None, direct_path=None):
26
26
  from girder_worker.docker.transforms.girder import (GirderFileIdToVolume,
27
27
  GirderFolderIdToVolume,
28
28
  GirderItemIdToVolume)
@@ -32,19 +32,53 @@ def _to_file_volume(param, model):
32
32
  girder_type = SLICER_TYPE_TO_GIRDER_MODEL_MAP[param.typ]
33
33
 
34
34
  if girder_type == 'folder':
35
- return GirderFolderIdToVolume(model['_id'], folder_name=model['name'])
35
+ return GirderFolderIdToVolume(model['_id'], folder_name=filename or model['name'],
36
+ gc=gc)
36
37
  elif girder_type == 'item':
37
- return GirderItemIdToVolume(model['_id'])
38
+ return GirderItemIdToVolume(model['_id'], gc=gc)
38
39
 
39
- if not Setting().get(PluginSettings.DIRECT_PATH):
40
- return GirderFileIdToVolume(model['_id'], filename=model['name'])
40
+ filename = filename or model['name']
41
+ if direct_path is None:
42
+ direct_path = Setting().get(PluginSettings.DIRECT_PATH)
43
+ if not direct_path:
44
+ return GirderFileIdToVolume(model['_id'], filename=filename, gc=gc)
41
45
 
42
46
  try:
43
47
  path = File().getLocalFilePath(model)
44
48
  return DirectGirderFileIdToVolume(model['_id'], direct_file_path=path,
45
- filename=model['name'])
49
+ filename=filename, gc=gc)
46
50
  except FilePathException:
47
- return GirderFileIdToVolume(model['_id'], filename=model['name'])
51
+ return GirderFileIdToVolume(model['_id'], filename=filename, gc=gc)
52
+
53
+
54
+ def _list_item_name(model):
55
+ # Id-prefix so same-named files in a list can't collide when mounted, and
56
+ # drop commas so a name can't corrupt the comma-joined path argument.
57
+ return ('%s_%s' % (model['_id'], model['name'])).replace(',', '_')
58
+
59
+
60
+ def _to_file_volume_arg(param, value):
61
+ from .girder_worker_plugin.direct_docker_run import CommaJoinedVolumes
62
+
63
+ if not isinstance(value, list):
64
+ return _to_file_volume(param, value)
65
+ # The direct-path setting is constant across the list; read it once.
66
+ direct_path = Setting().get(PluginSettings.DIRECT_PATH)
67
+ volumes = []
68
+ gc = None
69
+ for model in value:
70
+ volume = _to_file_volume(param, model, filename=_list_item_name(model),
71
+ gc=gc, direct_path=direct_path)
72
+ # Share the first volume's Girder client with the rest of the list so
73
+ # a large list does not mint one access token per file.
74
+ gc = volume.gc or gc
75
+ volumes.append(volume)
76
+ return CommaJoinedVolumes(volumes)
77
+
78
+
79
+ def _first_model(value):
80
+ """A possibly-multiple Girder input uses its first model as the primary."""
81
+ return value[0] if isinstance(value, list) else value
48
82
 
49
83
 
50
84
  def _to_girder_api(param, value):
@@ -59,7 +93,7 @@ def _to_girder_api(param, value):
59
93
  return value
60
94
 
61
95
 
62
- def _parseParamValue(param, value, user, token):
96
+ def _parseParamValue(param, value, user, token, primary_only=False):
63
97
  if isinstance(value, bytes):
64
98
  value = value.decode('utf8')
65
99
 
@@ -67,10 +101,21 @@ def _parseParamValue(param, value, user, token):
67
101
  if is_on_girder(param):
68
102
  girder_type = SLICER_TYPE_TO_GIRDER_MODEL_MAP[param.typ]
69
103
  curModel = ModelImporter.model(girder_type)
70
- loaded = curModel.load(value, level=AccessType.READ, user=user)
71
- if not loaded:
72
- raise RestException('Invalid %s id (%s).' % (curModel.name, str(value)))
73
- return loaded
104
+
105
+ def load(value):
106
+ loaded = curModel.load(value, level=AccessType.READ, user=user)
107
+ if not loaded:
108
+ raise RestException('Invalid %s id (%s).' % (curModel.name, str(value)))
109
+ return loaded
110
+
111
+ if getattr(param, 'multiple', None):
112
+ ids = [subvalue.strip() for subvalue in str(value).split(',')]
113
+ # Callers that only use the primary (first) model can skip loading
114
+ # the rest of the list.
115
+ if primary_only:
116
+ ids = ids[:1]
117
+ return [load(subvalue) for subvalue in ids]
118
+ return load(value)
74
119
 
75
120
  try:
76
121
  if param.isVector():
@@ -153,7 +198,7 @@ def _add_optional_input_param(param, args, user, token, templateParams):
153
198
 
154
199
  if is_on_girder(param):
155
200
  # Bindings
156
- container_args.append(_to_file_volume(param, value))
201
+ container_args.append(_to_file_volume_arg(param, value))
157
202
  elif is_girder_api(param):
158
203
  # Bindings
159
204
  container_args.append(_to_girder_api(param, value))
@@ -203,7 +248,7 @@ def _add_indexed_input_param(param, args, user, token, templateParams=None):
203
248
 
204
249
  if is_on_girder(param):
205
250
  # Bindings
206
- return _to_file_volume(param, value), value['name']
251
+ return _to_file_volume_arg(param, value), _first_model(value)['name']
207
252
  if is_girder_api(param):
208
253
  return _to_girder_api(param, value), value['name']
209
254
  value = _processTemplates(value, param, templateParams)
@@ -252,9 +297,11 @@ def _populateTemplateParams(params, user, token, index_params, opt_params, templ
252
297
  for param in index_params + opt_params:
253
298
  if param.identifier() in params:
254
299
  try:
255
- value = _parseParamValue(param, params[param.identifier()], user, token)
300
+ value = _parseParamValue(param, params[param.identifier()], user, token,
301
+ primary_only=True)
256
302
  except Exception:
257
303
  continue
304
+ value = _first_model(value)
258
305
  value = value.get('name') if isinstance(value, dict) else value
259
306
  if value:
260
307
  templateParams[f'parameter_{param.name}'] = value
@@ -304,7 +351,8 @@ def prepare_task(params, user, token, index_params, opt_params,
304
351
  and SLICER_TYPE_TO_GIRDER_MODEL_MAP[param.typ] != 'folder'):
305
352
  primary_input_name = name
306
353
  reference['userId'] = str(user['_id'])
307
- value = _parseParamValue(param, params[param.identifier()], user, token)
354
+ value = _first_model(_parseParamValue(
355
+ param, params[param.identifier()], user, token, primary_only=True))
308
356
  itemId = value['_id']
309
357
  if SLICER_TYPE_TO_GIRDER_MODEL_MAP[param.typ] == 'file':
310
358
  reference['fileId'] = str(value['_id'])
@@ -80,6 +80,9 @@ def _addInputParamToHandler(param, handlerDesc, required=True):
80
80
  'Girder ID of input %s (if batch input, this is a regex '
81
81
  'for item names) - %s: %s'
82
82
  % (param.typ, param.identifier(), param.description))
83
+ elif getattr(param, 'multiple', None):
84
+ desc = 'Comma separated Girder IDs of input %s - %s: %s' % (
85
+ param.typ, param.identifier(), param.description)
83
86
  else:
84
87
  desc = 'Girder ID of input %s - %s: %s' % (
85
88
  param.typ, param.identifier(), param.description)