Skip to content

Custom Types

Implment Your Own Type

By implmenting a simple protocol, your custom classes can be easily used as the type of an arc parameter.


When implementing your own types, you can make them compatible with arc by implementing the __convert__() class method. arc will call this with the input from the command line (or some other source), and you are expected to parse the input and return an instance of the type. For example,
import arc

class CustomType:
    def __init__(self, val: int):
        self.val = val

    def __str__(self):
        return f"CustomType(val={self.val})"

    def __convert__(cls, value: str):
        if value.isnumeric():
            return cls(int(value))
            raise arc.ConversionError(value, "must be an integer")

def main(foo: CustomType):

$ python 2
$ python string
USAGE [-h] [--] foo

invalid value for foo: must be an integer

Some notes about custom types:

In additon to value, you can also add the following arguments to the signature (in the given order, but the names don't need to match):

  • info: Description of the provided type. Instance of arc.types.TypeInfo
  • ctx: The current execution context. Instance of arc.context.Context

Context Managers

Any type that is considered a context manager (by contextlib.contextmanager()) will be opened before the command callback executes, and then closed after the command executes

import arc

class ContextManager:
    def __init__(self, value) -> None:
        self.value = value

    def __enter__(self):
        arc.print("Entering the Context")
        return self

    def __exit__(self, *args):
        arc.print("Exiting the Context")

    def __convert__(cls, value):
        return cls(value)

def command(val: ContextManager):

$ python 1
Entering the Context
<ContextManager object at 0x7ff0bd55aa10>
Exiting the Context


Note that the return value of __enter__() is important here, because it is the value that is passed to the command.

Type Aliases

Type aliases are the way in which arc implements support for builtin and standard library Python types, but can also be used for any type. You can use type aliases to provide support fo any third party library types. For example, supporting numpy arrays

import arc
from arc.types import Alias
import numpy as np

# Inherits from Alias. The 'of' parameter declares what types(s) this
# alias handles (can be a single type or tuple of types).
# Reads like a sentence: "NDArrayAlias is the Alias of np.ndarray"
class NDArrayAlias(Alias, of=np.ndarry):

    def __convert__(cls, value: str):
        return np.ndarray(value.split(","))

def main(array: np.ndarray):

$ python x,y,z
array(['x', 'y', 'z'], dtype='<U1')
All other principles about custom types hold for alias types.

Note that this is a simplified example, a more complete implementation would support the use of generics using numpy.typing

Generic Types