Skip to content

Commit fe62764

Browse files
pditommasoclaude
andauthored
Add support for comma-separated file paths in ProcessEntryHandler (#6434) [ci fast]
- Add parseFileInput() helper method to handle comma-separated file paths - Support both String and GString inputs for file parameters - Return List<Path> for multiple files, single Path for one file - Handle whitespace trimming and empty string filtering - Maintain backward compatibility with existing single file behavior - Add comprehensive unit tests using Spock parameterized testing Usage: - Single file: --input file.txt (returns Path object) - Multiple files: --input "file1.txt,file2.txt,file3.txt" (returns List<Path>) - With whitespace: --input " file1.txt , file2.txt " (handles gracefully) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Claude <[email protected]>
1 parent abbef79 commit fe62764

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

modules/nextflow/src/main/groovy/nextflow/script/ProcessEntryHandler.groovy

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,27 @@ class ProcessEntryHandler {
340340
return complexParams
341341
}
342342

343+
/**
344+
* Parses file input handling comma-separated values.
345+
* If the input contains commas, splits and returns a list of files.
346+
* Otherwise returns a single file object.
347+
*
348+
* @param fileInput String representation of file path(s)
349+
* @return Single file object or List of file objects
350+
*/
351+
protected Object parseFileInput(String fileInput) {
352+
if (fileInput.contains(',')) {
353+
// Split by comma, trim whitespace, and convert each to a file
354+
return fileInput.tokenize(',')
355+
.collect { it.trim() }
356+
.findAll { !it.isEmpty() }
357+
.collect { Nextflow.file(it) }
358+
} else {
359+
// Single file case - existing behavior
360+
return Nextflow.file(fileInput)
361+
}
362+
}
363+
343364
/**
344365
* Gets the appropriate value for an input definition, handling type conversion.
345366
*
@@ -360,8 +381,8 @@ class ProcessEntryHandler {
360381
switch( paramType ) {
361382
case 'path':
362383
case 'file':
363-
if( paramValue instanceof String ) {
364-
return Nextflow.file(paramValue)
384+
if( paramValue instanceof String || paramValue instanceof GString ) {
385+
return parseFileInput(paramValue.toString())
365386
}
366387
return paramValue
367388

@@ -376,4 +397,4 @@ class ProcessEntryHandler {
376397
return paramValue
377398
}
378399
}
379-
}
400+
}

modules/nextflow/src/test/groovy/nextflow/script/ProcessEntryHandlerTest.groovy

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package nextflow.script
1818

19+
import java.nio.file.Path
1920
import nextflow.Session
2021
import spock.lang.Specification
2122

@@ -209,4 +210,43 @@ class ProcessEntryHandlerTest extends Specification {
209210
// tupleElements[1] should be a Path object (mocked)
210211
tupleElements[1].toString().contains('file.fa')
211212
}
212-
}
213+
214+
def 'should parse file input correctly' () {
215+
given:
216+
def session = Mock(Session)
217+
def script = Mock(BaseScript)
218+
def meta = Mock(ScriptMeta)
219+
def handler = new ProcessEntryHandler(script, session, meta)
220+
221+
expect:
222+
handler.parseFileInput(INPUT) == EXPECTED_FILES
223+
224+
where:
225+
INPUT | EXPECTED_FILES
226+
'/path/to/file.txt' | Path.of('/path/to/file.txt')
227+
and:
228+
'/path/to/file1.txt,/path/to/file2.txt,/path/to/file3.txt' | [Path.of('/path/to/file1.txt'), Path.of('/path/to/file2.txt'), Path.of('/path/to/file3.txt')]
229+
' /path/to/file1.txt , /path/to/file2.txt , /path/to/file3.txt ' | [Path.of('/path/to/file1.txt'), Path.of('/path/to/file2.txt'), Path.of('/path/to/file3.txt')]
230+
'/path/to/file1.txt,,/path/to/file2.txt, ,/path/to/file3.txt' | [Path.of('/path/to/file1.txt'), Path.of('/path/to/file2.txt'), Path.of('/path/to/file3.txt')]
231+
'file1.txt,file2.txt' | [Path.of('file1.txt').toAbsolutePath(), Path.of('file2.txt').toAbsolutePath()]
232+
}
233+
234+
def 'should handle file input with GString' () {
235+
given:
236+
def session = Mock(Session)
237+
def script = Mock(BaseScript)
238+
def meta = Mock(ScriptMeta)
239+
def handler = new ProcessEntryHandler(script, session, meta)
240+
def inputDef = [name: 'input', type: 'file']
241+
def complexParams = [input: "${'/path/to/file1.txt'},${'/path/to/file2.txt'}"]
242+
243+
when:
244+
def result = handler.getValueForInput(inputDef, complexParams)
245+
246+
then:
247+
result instanceof List
248+
result.size() == 2
249+
result[0].toString().contains('file1.txt')
250+
result[1].toString().contains('file2.txt')
251+
}
252+
}

0 commit comments

Comments
 (0)