question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Repeating options with sub-options/arguments

See original GitHub issue

I would like to add an option multiple times and add sub-options/parameters to it. In my scenario, I have a viewer application and, among other options and one positional argument, I would like to add datasets, somewhat like this:

viewer <positional-argument> \
    --add-dataset --container=c1 --dataset=d1 \
    --add-dataset --dataset=<d2 --type=label \
    --fallback-container=fbc \ # use this as fallback, if no container is specified 
    [other options]

For this call, I would add two datasets to my container:

  • d1 in container c1 (type is auto-detected from meta data)
  • d2 in container fbc as label dataset

As far as I can tell, this is not (yet) supported in picocli (please correct me if I am wrong), and my - kind of hackish way - to achieve that, is to add an Option

@CommandLine.Option(names = arrayOf("--add-dataset"), arity = "+")
var addDatasetStrings: List<String> = mutableListOf()

that collects greedily all Strings that follow that option into a list. Users need to append a split-char after the last option of each --add-dataset (I chose _ here). I then split the list at _, and parse each of the sublists with a separate parser. This works well for me but it has a few downsides, as far as I can tell:

  • I do not think it is possible to have overlapping arguments between the main arguments/options and the options/arguments for --add-dataset.
  • Need delimiter string (I used _)
  • help message for sub option (--add-dataset) not included automatically

I added my (kotlin) code and an invocation example at the very end of this comment for reference.

I think that this would be a useful addition. There are several issues that found that are related but not the same in my understanding:

Out of these, #454 seems to be the most closely related issues. For my real-world use case (viewer application with one positional argument, and more options), I could turn the one positional argument into an option and #454 could be a working solution for me. For anything that requires positional arguments, I would be concerned that the positional arguments might clash with the subcommands.

import org.apache.commons.lang3.builder.ToStringBuilder
import picocli.CommandLine
import java.util.concurrent.Callable

@CommandLine.Command

class AddDataset(val fallbackContainer: String?) : Callable<AddDataset> {

	@CommandLine.Option(names = arrayOf("--container"))
	var container: String? = null

	@CommandLine.Option(names = arrayOf("--dataset"), required=true)
	var dataset: String? = null

	@CommandLine.Option(names = arrayOf("--help", "-h"),  usageHelp = true)
	var helpRequested = false

	override fun call(): AddDataset {
		return this
	}

	override fun toString(): String {
		return ToStringBuilder(this)
				.append("container", container?: fallbackContainer)
				.append("dataset", dataset)
				.toString()
	}

}


class Args : Callable<List<AddDataset>> {

	@CommandLine.Option(names = arrayOf("--default-container"))
	var defaultContainer: String? = null

	@CommandLine.Option(names = arrayOf("--add-dataset"), arity = "+")
	var addDatasetStrings: List<String> = mutableListOf()

	@CommandLine.Option(names = arrayOf("--help", "-h"),  usageHelp = true)
	var helpRequested = false

	override fun call(): List<AddDataset> {

		val indices = addDatasetStrings
				.withIndex()
				.filter { it.value.equals("_", ignoreCase = true) }
				.map { it.index }

		val subLists = mutableListOf<List<String>>()
		var nextStartingIndex = 0
		for (i in 0 until indices.size) {
			subLists.add(addDatasetStrings.subList(nextStartingIndex, indices[i]))
			nextStartingIndex = indices[i] + 1
		}

		return subLists.map { CommandLine.call(AddDataset(defaultContainer), *it.toTypedArray()) }
	}

}

fun main(argv: Array<String>) {

	val args = Args()
	val datasets = CommandLine.call(args, *argv)
	println(datasets)

}
$ Command --add-dataset --container=123 --dataset=456 _ --default-container=abracadabra --add-dataset --dataset=789 _
[AddDataset@5b87ed94[container=123,dataset=456], AddDataset@6e0e048a[container=abracadabra,dataset=789]]

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:14 (9 by maintainers)

github_iconTop GitHub Comments

3reactions
remkopcommented, Mar 30, 2019

Picocli 4.0.0-alpha-1 has been released which includes support for repeating composite groups. See https://picocli.info/#_argument_groups for details.

Please try this and provide feedback. We can still make changes.

What do you think of the annotations API? What about the programmatic API? Does it work as expected? Are the input validation error messages correct and clear? Is the documentation clear and complete? Anything you want to change or improve? Any other feedback?

2reactions
remkopcommented, Apr 1, 2019

Thanks for raising this! I fixed one arg group validation bug in master, but found another one. Added a failing test but not able to fix it yet, I suspect the validation logic needs to be restructured.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Repeating arguments specified in options list - docopt
I'm aware that having repeating optional arguments is possible, I would just really prefer to specify so inside the options description ...
Read more >
Insert Repeating Items into a List - YouTube
We have a few T-Shirt options that we want repeated for each T-shirt in our list. For example, each T-shirt has the same...
Read more >
Repeating Data - Support : Knowledge Base
Define the section of the template to output for each repeat. This uses the special {{!REPEATSTART}} and {{!REPEATEND}} placeholders. These options give you...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found