-
Notifications
You must be signed in to change notification settings - Fork 0
Data Types
Typically, you don't want numbers, booleans, lists, represented as just strings. Arc provides type conversions that will convert the input to your desired type before passing it on to the command. If you've used Flask before, it's URL converters work in essentially the same way. If no type is specified, the input will be left as a string
Check examples/converters.py for full examples
Arc supports type conversion to these builtin types:
intfloatboolbyteslistsettuple
from arc import CLI
cli = CLI()
@cli.subcommand()
def number_type(number: int):
'''Prints the type of a number'''
print(type(number))
cli()$ python3 example.py number_type number=5
<class 'int'>
from arc import CLI
cli = CLI()
@cli.subcommand()
def float_type(number: float):
'''Prints the type of a float'''
print(type(number))
cli()$ python3 example.py float_type number=5.3
<class 'float'>
from arc import CLI
cli = CLI()
@cli.subcommand()
def list_type(numbers: list):
'''Prints the type of a float'''
print(type(number))
print(numbers)
cli()$ python3 example.py list_type numbers=1,2,3
<class 'list'>
["1", "2", "3"]
Collection Types can also be typed generically, to enable each element to be converted. Replace list with list[int] in the example above and then each item will be a integer, rather than a string. This works for set and tuple as well.
Bool types doen't require a value and simple test for presence. By default, flags are considered False when not passed, and True when passed. This can be swapped by providing the arguments with the default value of True
from arc import CLI
cli = CLI()
@cli.subcommand()
def hello(name, reverse: bool):
'''Command that prints greets someone'''
if reverse:
name = name[::-1]
print(f"Hello, {name}!")
cli()$ python3 example.py hello --name Sean
Hello, Sean!
$ python3 example.py hello --name Sean --reverse
Hello, naeS!
Note that flags are actually just syntatic suguar for arguments, so writing:
$ python3 example.py hello --name Sean --reverse true
Would have the same result
The union type is used to specify that an argument has two or more possible types.
from typing import Union
from arc import CLI
cli = CLI()
@cli.subcommand()
def union_type(number: Union[int, str]):
'''Prints the type of a float'''
print(type(number))
cli()$ python3 example.py union_type number=5.3
<class 'int'>
$ python3 example.py union_type number=somestring
<class 'str'>
Arc will attempt to convert to each type within the union, from left to right, and the first one to succeed will be passed to the command. If none succeed, an error will be raised.
The Path conversion doesn't perform any validation, but it just offers the convenience of working with path objects rather than strings.
from pathlib import Path
from arc import CLI
cli = CLI()
@cli.command()
def paint(path: Path):
print(path.as_posix())
cli()You can use the Python standard library Enum to define a specific subset of acceptable values to an argument.
from enum import Enum
from arc import CLI
cli = CLI()
class Color(Enum):
RED = "red"
YELLOW = "yellow"
GREEN = "green"
@cli.command()
def paint(color: Color):
if color == Color.RED:
print("You painted the walls the bloodiest of reds")
elif color == Color.YELLOW:
print("You painted the walls the most fabulous yellow")
else:
print("You painted the walls the deepest of blues")
cli()$ python3 example.py paint color=red
You painted the walls the bloodiest of reds
Since red is a defined value on the Color enumeration.
The literal type can also be used to define a limited subset of values that are acceptable.
from typing import Literal
from arc import CLI
cli = CLI()
@cli.command()
def pick_pain(color: Literal["red", "green", "blue", "yellow"]):
print(f"You picked {color}")
cli()A file type will take in a file path and return a file handler object
from arc import CLI
from arc.types import File
cli = CLI()
@cli.command()
def file_test(file=File[File.READ]):
# file will be of custom type File, but has the
# same interface as the TextIOWrapper returned by open()
print(file.readlines())The format for a File types is File[<FILE_MODE>] with the available file modes being:
- READ
- WRITE
- APPEND
- CREATE
Arc should handle the file cleanup process, however if you want to control when the file closes, you can with file.close(). Additionally, the file object implements __enter__ and __exit__ so can be used as a context manager.
from arc import CLI
from arc.types import File
cli = CLI()
@cli.command()
def file_test(file: File[File.READ]):
print(file.readlines())
file.close()
# OR
with file as f:
print(f.readlines())
# With either approach, file will defintely be closed at this point
# unless something goes horribly wrong, once the function finishes execution ARC will close the file for you though.The range type can assure that the user's input is within a specific value range
from typing import Literal
from arc import CLI
from arc.types import Range
cli = CLI()
@cli.command()
def range_test(value: Range[Literal[1], Litearl[10]]):
print(value)
print(type(value))
print(val + 2)The above example will only allows values between 1 and 10 (like the builtin this is inclusive on the minimum end and exclusive on the maximum end). While the type of value will be Range, Range is a subclass of int and so can be treated as such.
There are a couple of useful methods to note on the Range.
-
range- returns a builtinrange()with the provided minimums and maximums -
range_with_picked- Generator that returns a tuple pair of the value and whether or not that value was the one picked by the user.
Acts like a pathlib.Path object, but validates that the path exists first
from arc import CLI
from arc.types import ValidPath
cli = CLI()
@cli.command()
def paint(path: ValidPath):
print(path.as_posix())
cli()Arc also allows you to use it's converter functionality when gathering user input from within a command
from arc import CLI
from arc.convert.input import convert_to_int
cli = CLI()
@cli.subcommand("example")
def example():
cool_number = convert_to_int("Please enter a number: ")
print(type(cool_number)) # '<class : int>'
cli()Note that all convereters in Config.converter will have a function associated with it. This includes all custom converters. The functions will be named convert_to_<indicator>
Note that if the command defines a custom converter, it's respective input function will need to be imported after it is added to Config.converter otherwise it doesn't yet exist to import.