Skip to main content

Typer extension to enable pydantic support

Project description

Pydantic Typer

PyPI - Version PyPI - Python Version Test Coverage

Typer extension to enable pydantic support

[!WARNING]
This package is still in early development and some things might not work as expected, or change between versions.


Table of Contents

Installation

pip install pydantic-typer

[!NOTE] pydantic-typer comes with pydantic and typer as dependencies, so you don't need to install anything else.

Usage

For general typer usage, please refer to the typer documentation.

All the code blocks below can be copied and used directly (they are tested Python files). To run any of the examples, copy the code to a file main.py, and run it:

python main.py

Basic Usage

Simply use pydantic_typer.run instead of typer.run to enable pydantic support:

import pydantic

import pydantic_typer


class User(pydantic.BaseModel):
    id: int
    name: str = "Jane Doe"


def main(num: int, user: User):
    print(num, type(num))
    print(user, type(user))


if __name__ == "__main__":
    pydantic_typer.run(main)

Usage with nested models

pydantic_typer.run also works with nested pydantic models:

from __future__ import annotations

from typing import Optional

import pydantic

import pydantic_typer


class Pet(pydantic.BaseModel):
    name: str
    species: str


class Person(pydantic.BaseModel):
    name: str
    age: Optional[float] = None  # noqa: UP007 typer does not support float | None yet, see https://github.com/tiangolo/typer/pull/548
    pet: Pet


def main(person: Person):
    print(person, type(person))


if __name__ == "__main__":
    pydantic_typer.run(main)

Use pydantic models with typer.Argument

You can annotate the parameters with typer.Argument to make all model fields CLI arguments:

from __future__ import annotations

import pydantic
import typer
from typing_extensions import Annotated

import pydantic_typer


class User(pydantic.BaseModel):
    id: int
    name: str


def main(num: Annotated[int, typer.Option()], user: Annotated[User, typer.Argument()]):
    print(num, type(num))
    print(user, type(user))


if __name__ == "__main__":
    pydantic_typer.run(main)
:bulb: You can also override annotations directly on the pydantic model fields:
from __future__ import annotations

import pydantic
import typer
from typing_extensions import Annotated

import pydantic_typer


class User(pydantic.BaseModel):
    id: Annotated[int, typer.Argument(metavar="THE_ID")]
    name: Annotated[str, typer.Option()]


def main(num: Annotated[int, typer.Option()], user: Annotated[User, typer.Argument()]):
    print(num, type(num))
    print(user, type(user))


if __name__ == "__main__":
    pydantic_typer.run(main)

Here, User is a typer.Argument, but we manually override the fields again:

  • We override the metavar of to User.id be THE_ID
  • And User.name to be a typer.Option

Use pydantic models in multiple commands

For larger typer apps, you can use pydantic_typer.Typer instead of annotating each command function individually to enable pydantic models on all commands:

from __future__ import annotations

import pydantic
import typer
from typing_extensions import Annotated

from pydantic_typer import Typer

app = Typer()


class User(pydantic.BaseModel):
    id: int
    name: Annotated[str, typer.Option()] = "John"


@app.command()
def hi(user: User):
    print(f"Hi {user}")


@app.command()
def bye(user: User):
    print(f"Bye {user}")


if __name__ == "__main__":
    app()

Use pydantic types

You can also annotate arguments with pydantic types and they will be validated:

import click
from pydantic import HttpUrl, conint

import pydantic_typer

EvenInt = conint(multiple_of=2)


def main(num: EvenInt, url: HttpUrl, ctx: click.Context):  # type: ignore
    print(num, type(num))
    print(url, type(url))


if __name__ == "__main__":
    pydantic_typer.run(main)

Pydantic types also work in lists and tuples:

from typing import List

import typer
from pydantic import AnyHttpUrl

import pydantic_typer


def main(urls: List[AnyHttpUrl] = typer.Option([], "--url")):
    typer.echo(f"urls: {urls}")


if __name__ == "__main__":
    pydantic_typer.run(main)

License

pydantic-typer is distributed under the terms of the MIT license.

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

pydantic_typer-0.0.6.tar.gz (331.7 kB view hashes)

Uploaded Source

Built Distribution

pydantic_typer-0.0.6-py3-none-any.whl (8.0 kB view hashes)

Uploaded Python 3

Supported by

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