https://github.com/fulcrumgenomics/sopt
An argument parsing library for Scala
https://github.com/fulcrumgenomics/sopt
argument-parsing scala
Last synced: 4 months ago
JSON representation
An argument parsing library for Scala
- Host: GitHub
- URL: https://github.com/fulcrumgenomics/sopt
- Owner: fulcrumgenomics
- License: mit
- Created: 2017-05-08T14:03:51.000Z (over 8 years ago)
- Default Branch: main
- Last Pushed: 2025-04-30T18:32:33.000Z (8 months ago)
- Last Synced: 2025-04-30T18:44:56.487Z (8 months ago)
- Topics: argument-parsing, scala
- Language: Scala
- Homepage:
- Size: 234 KB
- Stars: 10
- Watchers: 9
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
[](https://github.com/fulcrumgenomics/sopt/actions?query=workflow%3A%22unit+tests%22)
[](https://codecov.io/github/fulcrumgenomics/sopt?branch=main)
[](https://maven-badges.herokuapp.com/maven-central/com.fulcrumgenomics/sopt_2.12)
[](http://javadoc.io/doc/com.fulcrumgenomics/sopt_2.12)
[](https://github.com/fulcrumgenomics/sopt/blob/main/LICENSE)
[](http://www.scala-lang.org/)
# sopt - Scala Option Parsing Library
_sopt_ is a scala library for command line option parsing with minimal dependencies. It is designed for toolkits that have multiple "commands" such as [dagr](https://github.com/fulcrumgenomics/dagr) and [fgbio](https://github.com/fulcrumgenomics/fgbio). The latest API documentation can be found [here](http://javadoc.io/doc/com.fulcrumgenomics/sopt_2.12).
[Visit us at Fulcrum Genomics](https://www.fulcrumgenomics.com) to learn more about how we can power your Bioinformatics with sopt and beyond.
_sopt_ has the following high level features:
- Support for GNU/posix style argument names and conventions
- Camel case names in scala are auto-translated to GNU style options (e.g. `inputFile -> input-file`)
- Argument configuration via annotations on scala classes
- Reflectively builds typed strongly typed argument values for most simple types:
- Primitive types like `Int` and `Double`
- Any type that has a constructor from `String` or a companion `apply(String)`
- Option types, e.g. `Option[Int]` or `Option[String]`, etc.
- Collection types, e.g. `Seq[Int]` or `Set[String`], etc.
- Typedefs used for argument types and will be used in usage & documentation
- Argument file support similar to Python's Argparse
- Command usage and argument documentation in [GitHub flavored MarkDown](https://guides.github.com/features/mastering-markdown/), formatted appropriately for the terminal
- Terminal output using ANSI escape sequences to color and highlight usage
- APIs to retrieve command and argument metadata, e.g. to create offline documentation
## Getting Started
To use sopt you will need to add it to your build. For sbt this looks like:
```scala
libraryDependencies += "com.fulcrumgenomics" %% "sopt" % "1.1.0"
```
You'll then need the following:
1. A `trait` which all your commands or tools will extend
1. One or more command classes
1. An `object` with a main method that invokes `sopt` to parse the command line
### A Short Example
The following is a minimal example of a use of sopt.
```scala
package example
import com.fulcrumgenomics.sopt._
import com.fulcrumgenomics.sopt.Sopt._
import example.Types._
/** All command classes exposed on the command-line will extend or mix-in this trait. */
trait Tool { def execute(): Unit }
object Types {
type Name = String
}
/** An example command. */
@clp(description=
"""
|An example program that greets people. The `greeting` argument is optional
|since it has a default.
""")
class Greet(
@arg(flag='g', doc="The greeting.") val greeting: String = "Hello",
@arg(flag='n', doc="Someone's name.") val name: Name
) extends Tool {
override def execute(): Unit = System.out.println(s"$greeting $name!")
}
/** The main class that invokes sopt. */
object Main {
def main(args: Array[String]): Unit = {
val commands = Sopt.find[Tool](packages=Seq("example"))
Sopt.parseCommand[Tool](name="example-kit", args=args, commands=commands) match {
case Failure(usage) =>
System.err.print(usage())
System.exit(1)
case CommandSuccess(tool) =>
tool.execute()
System.exit(0)
}
}
}
```
The `Tool` trait is not required, but it is generally useful to have a trait which is implemented by all commands, so that not only can they have a common method (ex. `execute()`) that can be invoked to _do something_, the available commands can be found using the `Tool` trait and `Sopt.find`. Alternatively you could invoke `Sopt.parseCommand[AnyRef]` directly! To do so, you would need to manually construct the list of commands instead of searching for subclasses with `Sopt.find[AnyRef](packages)`.
In this example, the `Main` class is implemented only once and can act as a dispatcher to any number of commands that implement `Tool`. It also acts as a central point to add consistent behaviour amongst tools, for example printing out the date and time at the start and end of execution.
Running the `Main` class from the command line yields the following output:
```
USAGE: example-kit [command] [arguments]
Version: 1.0
-----------------------------------------------------------------------------
Available Sub-Commands:
-----------------------------------------------------------------------------
Clps: Various command line programs.
GreetingCommand An example program that greets people.
-----------------------------------------------------------------------------
No sub-command given.
```
Running the `Main` class and specifying the `Greet` command without any arguments produces the following output:
```
USAGE: Greet [arguments]
Version: 0.2.0-SNAPSHOT
------------------------------------------------------------------------------------------------
An example program that greets people. The greeting argument is optional since it has a default.
Greet Required Arguments:
------------------------------------------------------------------------------------------------
-n String, --name=Name Someone's name.
Greet Optional Arguments:
------------------------------------------------------------------------------------------------
-h [true|false], --help[=true|false]
Display the help message. Default: false.
-g String, --greeting=String The greeting. Default: Hello.
--version[=true|false] Display the version number for this tool. Default: false.
Error: Argument 'name' is required.
```
## Documentation
API documentation for all versions can be viewed on [javadoc.io](http://www.javadoc.io/doc/com.fulcrumgenomics/sopt_2.12/0.2.0).
## Argument Naming & Formatting
Each argument may have a long name and optionally a short name.
### Short Names
Short names are single-character names. Arguments with short names may be formatted on the command line as follows:
- `-`
- `-=`
- `- `
### Long names
Long names may be of any length and are generally specified as one or more lower-case words separated with hyphens (e.g. `--input-files`). Long names may be used on the command line with the following formats:
- `--=`
- `-- `
Name must be `[A-Za-z0-9?][-A-Za-Z0-9?]*`
The following is not supported:
- `--`
due to the prefix support described below.
### Prefixes
Prefixes of option names are supported, as long as the a prefix is unambiguous, and either a ` `(whitespace) or `=` delimiter is used.
In a command with no other options beginning with `foo` the following are equivalent:
- `--foobar `
- `--fooba `
- `--foob `
- `--foo `
But if we have another option, e.g. `--football `, then using `--foo ` will cause a `DuplicateOptionNameException` to be thrown.
## Argument Types
### Flags
Flags are arguments with a boolean (true or false) value. The value may be ommitted, in which case it is interpreted as if `true` were provided. The following are all equivalent:
- `-x`
- `-xtrue`
- `-x=true`
- `-xT`
- `-x=T`
`Yes` and `Y` are also interpreted as `true`; `No` and `N` are interpreted as `false`. Both are case insensitive.
### Single-value arguments
Single value arguments may only be specified once on the command line. If the argument has a default value, the value from the command line will override it. Single-value arguments may be specified using long or short names, in the following ways:
- `-x `
- `-x`
- `-x=`
- `--extra `
- `--extra=`
### Multi-value arguments
Arguments that accept multiple values may be specified up to the maximum number of times specified by the tool. If an argument has a default value, the value(s) specified on the command line replace the default value (i.e. they do not add to it).
Multiple values can specified via multiple name/value pairs, e.g.:
- `--input=foo.txt --input=bar.txt --input=whee.txt`
- `--input foo.txt --input bar.txt --input whee.txt`
or with multiple values following a single argument name:
- `--input foo.txt bar.txt whee.txt`
- `--input *.txt`
### Optional arguments
Arguments are optional on the command line if any of the following are true:
- The argument has a default value
- The argument's type is `Option[_]`
- The argument's type is a collection type (e.g. `Seq`) and `minElements` is set to 0
`Option` and collection arguments that are optional, but have default values may be cleared by specifying the special argument value `:none:`.
### Argument files
Argument files are modeled after the implementation in [Python's Argparse library](https://docs.python.org/3/library/argparse.html). In short:
- Any argument prefixed with `@` is expected to be an argument file (e.g. `-foo=bar @whee.txt` species one parameter on the command line, and then a file called `whee.txt`)
- The arguments present in an argument file are substituted into the command line at the point where the argument file is specified
- Each line in the argument file is treated as a single token or argument
The latter is especially useful when passing many arguments that include spaces or special characters. For example the following file:
```
--funny-strings
Hello World!
I'm a shooting *star*
```
is equivalent to `--funny-strings 'Hello World!' 'I\'m a shooting *star*'`.