Skip to main content

Use PEP 593 annotations to define click options and arguments

Project description

dataclass-click

Structure your click arguments.

dataclass-click lets you move your user arguments from kwargs to dataclasses, keeping things self-contained.

Click is pretty simple to start with, but when your program gets complex, you find yourself repeating a lot of work. This is particularly true if you have command groups with shared arguments.

The idea of dataclass-click is to move the @option and @argument decorators off into annotations on dataclasses and pass dataclass objects instead of kwargs.

Simple Example

from dataclasses import dataclass
from pathlib import Path
from typing import Annotated

import click
from dataclass_click import argument, dataclass_click, option


@dataclass
class Config:
    # Auto-inferred type for built-in click types
    target: Annotated[Path, argument()]  
    
    # Auto-inferred option names
    foo: Annotated[float, option(required=True)]
    
    # Automatically map mismatched names
    bar: Annotated[int | None, option("--other")]


@click.command()
@dataclass_click(Config)
def main(config: Config):
    # All args neatly packaged.
    print(config.target, config.foo, config.bar)
    print(list(config.target.iterdir())) 

Inference Features

Name inference

The pythonic name for options and attributes is copied from the dataclass attribute name. Options can then be named whatever you want. If you don't specify an option name, it will be inferred for you:

from dataclasses import dataclass
from dataclass_click import option, argument
from typing import Annotated

@dataclass
class Config:
    foo: Annotated[str, argument()]
    bar: Annotated[str, option()]
    baz: Annotated[str, option("--bonno")]
Usage: main.py [OPTIONS] FOO

Options:
  --bonno TEXT
  --bar TEXT
  --help        Show this message and exit.

Type Inference (Extensible)

Out of the box...

data-class click can infer click parameter type (eg: option(type=click.INT)) from the attribute type. These types are static so if, like click.Path, you need arguments, you can always specify the type yourself like normal.

from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Annotated
from uuid import UUID

from dataclass_click import option


@dataclass
class Config:
    a: Annotated[str, option()]       # click.STRING
    b: Annotated[bool, option()]      # click.BOOL
    c: Annotated[int, option()]       # click.INT
    d: Annotated[float, option()]     # click.FLOAT
    e: Annotated[UUID, option()]      # click.UUID
    f: Annotated[datetime, option()]  # click.DateTime()
    g: Annotated[Path, option()]      # click.Path(path_type=Path)

Extending it...

Type inference is extensible so a program can add and even change the default click ParameterTypes associated with a Python data type. Eg: if you want to add a Decimal:

from decimal import Decimal

import click
import dataclass_click


class DecimalParameterType(click.ParamType):
    name = "integer"

    def convert(self, value, param, ctx):
        if isinstance(value, Decimal):
            return value
        try:
            return Decimal(value)
        except ValueError:
            self.fail(f"{value!r} is not a valid decimal number", param, ctx)


DECIMAL = DecimalParameterType()

# Extend type inference to add Decimal
dataclass_click.register_type_inference(Decimal, DECIMAL)

Required Inference

To avoid tricky mismatches between required options and optional attributes, dataclass-click will infer a field is required if neither default= nor required= are explicitly set and the attribute type hint is not optional:

from dataclasses import dataclass
from typing import Annotated

from dataclass_click import option

@dataclass
class Config:
    # Infer --foo is required because str is not optional
    foo: Annotated[str, option()]
    
    # Infer --bar is not required because str | None is optional
    bar: Annotated[str | None, option()]
    
    # No inference performed because required= is set.
    baz: Annotated[str | None, option(required=True)]
    
    # Infer not required because default= set.
    bob: Annotated[str, option(default="no")]
    
    # If you want to shoot yourself in the foot, you can!
    # Not inferred because required=False.  
    # Click will pass None even though the attribute is not optional
    bonno: Annotated[str, option(required=False)]  
Usage: main.py [OPTIONS]

Options:
  --bob TEXT
  --baz TEXT  [required]
  --bar TEXT
  --foo TEXT  [required]
  --bonno
  --help      Show this message and exit.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dataclass_click-1.0.4.tar.gz (7.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dataclass_click-1.0.4-py3-none-any.whl (8.6 kB view details)

Uploaded Python 3

File details

Details for the file dataclass_click-1.0.4.tar.gz.

File metadata

  • Download URL: dataclass_click-1.0.4.tar.gz
  • Upload date:
  • Size: 7.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dataclass_click-1.0.4.tar.gz
Algorithm Hash digest
SHA256 10e7de638dd9e68ae9abd5086f61d8ddee42b1873a70f5fd9fd2167856afac11
MD5 5e780f930f2609d6dcf93983f9f9a5bf
BLAKE2b-256 89825b6035efd90621771fa039960eab3e1ec7ff2a8625033272856843e8bd27

See more details on using hashes here.

Provenance

The following attestation bundles were made for dataclass_click-1.0.4.tar.gz:

Publisher: release.yaml on couling/dataclass-click

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dataclass_click-1.0.4-py3-none-any.whl.

File metadata

File hashes

Hashes for dataclass_click-1.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 a225d30c04e4abbdba411cc3d5ec0a2ea829e1dca6500afe5f87cc243e5ead72
MD5 ee878538e44b4aa291faf0be53ce4e7c
BLAKE2b-256 86dc38a94a2eb5f756724a6dc87a7aea38f7b747fe7b2e9daabc34a65e6cd9ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for dataclass_click-1.0.4-py3-none-any.whl:

Publisher: release.yaml on couling/dataclass-click

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page