Package: dtyper
🗝 Fix and improvetyper🗝What isdtyper, in one sentence?Usingimport dtyper as typerinstead ofimport typerwould make yourtyper.commands directly callable.(There's also a neat way to make dataclasses from typer commands, but that
would be two sentences.)Whydtyper?typeris a famously clear and useful system
for writing Python CLIs but it has two issues that people seem to run into a
lot:You can't call thetyper.commandfunctions it creates directly because
they have the wrong defaults.As you add more arguments to your CLI, there is no easy way to break up the
code sitting in one file without passing around long, verbose parameter lists.dtyperis a tiny, single-file library that adds to an existing installation
oftyperto solve these two problems without changing existing code at all.dtyper.commandexecutestyper.commandthen fixes the defaults.dtyper.functiondecorates an existingtyper.commandto have correct
defaults.dtyper.dataclassautomatically makes adataclassfrom
atyper.command.How to usedtyper?Install as usual withpoetry add dtyper,pip install dtyper, or your
favorite package manager.dtyperis a drop-in replacement fortyper- it copies alltypers
properties - so you can even writeimport dtyper as typerto experiment with it before deciding.dtyperhas two new functions thattyperdoesn't, and overrides atyperclass:@dtyper.functionis a decorator that takes atypercommand and returns
a callable function with the correct defaults.  It is unncessary if you usedtyper.Typer(below)@dtyper.dataclassis a decorator that takes an existingtyperordtypercommand and makes adataclassfrom it.dtyper.Typeris a class identical totyper.Typer, except it fixesTyper.commandfunctions so you can call them directly.None of thetyperfunctionality is changed to the slightest degree - addingdtyperwill not affect how your command line program runs at all.Example 1: usingdtyperinstead oftyperfrom dtyper import Argument, Option, Typer

app = Typer()

@app.command(help='test')
def get_keys(
    bucket: str = Argument(
        'buck', help='The bucket to use'
    ),

    keys: bool = Option(
        False, help='The keys to download'
    ),
):
    print(bucket, keys)You can callget_keys()from other code and get the right defaults.Without regulartyper, you sometimes get atyper.Argumentortyper.Optionin place of an expectedstrorbool.Example 2: a simpledtyper.dataclassHere's a simple CLI in one Python file with twoArguments and anOption:@command(help='test')
def get_keys(
    bucket: str = Argument(
        ..., help='The bucket to use'
    ),

    keys: str = Argument(
        'keys', help='The keys to download'
    ),

    pid: Optional[int] = Option(
        None, '--pid', '-p', help='process id, or None for this process'
    ),
):
    get_keys = GetKeys(**locals())
    print(get_keys.run())


@dtyper.dataclass(get_keys)
class GetKeys:
    site = 'https://www.some-websijt.nl'

    def run(self):
        return self.url, self.keys, self.pid

    def __post_init__(self):
        self.pid = self.pid or os.getpid()

    def url(self):
       return f'{self.site}/{self.url}/{self.pid}'Example: splitting a largetyper.commandinto multiple filesReal world CLIs frequently have dozens if not hundreds of commands, with
hundreds if not thousands of options, arguments, settings or command line
flags.The natural structure for this is the "big ball of mud", a popular anti-pattern
known to cause misery and suffering to maintainers.dtyper.dataclasscan split the user-facing definition of the API from its
implementation and then split that implementation over multiple files in a
natural and convenient way.The example has three Python files.interface.pycontains the Typer CLI definitions for this command.@command(help='test')
def big_calc(
    bucket: str = Argument(
        ..., help='The bucket to use'
    ),
    more: str = Argument(
        '', help='More information'
    ),
    enable_something: boolean = Option(
        False, help='Turn on one of many important parameters'
    ),
    # [dozens of parameters here]
):
    d = dict(locals())  # Capture all the command line arguments as a dict

    from .big_calc import BigCalc  # Lazy import to avoid a cycle

    bc = BigCalc(**d)
    bc.run()big_calc.pycontains thedtyper.dataclassimplementationfrom .interface import big_calc
from . import helper
import dtyper


@dtyper.dataclass(big_calc)
class BigCalc:
    def run(self):
       # Each argument in `big_calc` becomes a dataclass field
       print(self.bucket, self.more)
       print(self)  # dataclass gives you a nice output of all fields

       if helper.huge_thing(self) and self._etc():
          self.stuff()
          helper.more_stuff(self)
          ...

    def _etc(self):
       ...
       # Dozens more methods here perhaps!Some of the code is offloaded to helper files likehelper.py:def huge_thing(big_calc):
    if has_hole(big_calc.bucket):
       fix_it(big_calc.bucket, big_calc.more)

def more_stuff(big_calc):
    # even more codeAPI Documentation