Skip to content

Allow sequences of pydantic models #6

@pypae

Description

@pypae

If we want to allow sequences of pydantic models, things get unreadable quite fast.

Let's look at an example of a nested pydantic model with a sequence of another model:
pets.py

from typing import Optional, List

import typer

import pydantic


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


class Person(pydantic.BaseModel):
    name: str
    age: Optional[float] = None
    pets: List[Pet]


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


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

The script could be called like this:

$ python pets.py --person.name Jeff --person.pets.name Lassie --person.pets.species dog

If we want to add multiple pets, we can just supply --person.pets.name and person.pets.species multiple times.

$ python pets.py --person.name Jeff --person.pets.name Lassie --person.pets.species dog --person.pets.name Nala --person.pets.species cat

We don't explicitly state which pet names and species belong together and have to rely on the correct order of parameters. In my opinion this is potentially confusing for the CLI user and may lead to bugs.

Potential Solution

To make the mapping more explicit, we could allow to enable typer.Option lists to be indexed.
Like for nested pydantic models, I suggest sticking to the syntax traefik uses for lists. I.e. entrypoints.<name>.http.tls.domains[n].main

Indexed lists could be implemented independently of this PR and should work for all lists.
I suggest adding an indexed flag on typer.Option like shown in the example below.

indexed_list.py

from typing import List

import typer

def main(indexed_list: List[int] = typer.Option(..., indexed=True)):
    print(indexed_list)

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

This would then produce the following help text:

$ python indexed_list.py --help
                                                                                
 Usage: indexed_list.py [OPTIONS]                                    
                                                                    
╭─ Options ────────────────────────────────────────────────────────╮
│ *  --indexed-list[n]        INTEGER   [default: None] [required] │
│    --help                             Show this message and      │
│                                       exit.                      │
╰──────────────────────────────────────────────────────────────────╯

And could be used like this:

$ python indexed_list.py --indexed-list[1] 0 --indexed-list[0] 1 --indexed-list[2] 2
[1, 0, 2]

Note how the order of the input parameters doesn't matter anymore because the indices are given explicitly.

Notes on Implementation

Implementing this might not be trivial, but I think it could be possible by forwarding unknown options as described in the click docs.

Edit: This might actually be easier using token normalization.

Originally posted by @pypae in fastapi/typer#803 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions