typer
typer copied to clipboard
[FEATURE] Support for common pydantic types
It will be great to add support for common pydantic types, namely EmailStr
and HttpUrl
. Possible?
The obvious use case is to pass and emails and HTTP URLs strings as command line parameters and being validated automagically - similar to what FastAPI does.
P.S. @tiangolo many thanks for creating another gem!
I have immediate usecases for pydantic.FilePath
, pydantic.DirectoryPath
, and the pydantic.con-
types (conint
, confloat
, ...)
Hello, I encountered this issue while trying to use email, IP addresses and urls as parameters.
I did some experiments so I'm sharing thoughts:
- Currently
Typer
doesn't depend onpydantic
, and supporting pydantic native types would add an extra dependency that not everyone may want - Besides Pydantic, users may want to use other kinds of data types that may just be subclasses of
str
,int
or whatever plain data type. - Pydantic types are not consistent in the way you may do conversion to them. For instance,
EmailStr.validate
takes a simple value argument, and will return a regularstr
, whileAnyHttpUrl
will requireFieldInfo
andBaseConfig
arguments on top.
My experiment was therefore not opinionated on supporting pydantic
but rather supporting "any kind of str or int subclass".
I changed get_click_type
's
if annotated == str:
return click.STRING
to a later
elif issubclass(annotated, str):
return click.STRING
It is important to defer it as one of the latest cases, because it is not uncommon to have subclasses of Enum to be also subclasses of int
or str
see StrEnum coming in 3.11, which we want to still fall into click.CHOICE
.
With this change in mind, we've got half of the original request: we can annotate our code with the actual expected data. What's missing is the automagical conversion to the expected type when the callback gets executed.
To me, it's not a big deal to wrap it with @pydantic.validate_arguments
decorator to get it done, and it avoids an unnecessary dependency.
All that being said, I would like to thank you @tiangolo for the great and inspirational work you do with your libraries ❤️ , and I'd really be eager to contribute if you accept my proposal of simplified implementation of this ticket.
Hi I am trying to pass a URL as a parameter, hence check it is valid so this feature would be great. Any update on it? Thanks!
Hello,
I've made a recent attempt to progress on the topic in order to propose a pull request.
Unfortunately, I've discovered that some pydantic types, such as EmailStr
or HttpUrl
, which were previously subclasses of str
are no longer so in pydantic 2.
However, I've therefore dug a little bit further and realised that you may specify click_type
in the parameter_info
Consider the following snippet:
app = Typer()
EmailArg = Annotated[EmailStr, Argument(click_type=click.STRING)]
@app.command("something_with_email")
@validate_arguments
def something(user_email: EmailArg):
print(user_email)
if __name__=='__main__':
app()
It's enough IMO to do the job, and if you want to go even further, it's possible to even plug a callback function to the argument to catch a pydantic error and return a typer Exception
def validate_email(ctx: Context, value: str):
if ctx.resilient_parsing: # handling autocompletion
return
try:
return EmailStr._validate(value, None) # this one is a bit hacky but just for the sake of demo
except ValueError as exc:
raise BadParameter(str(exc)) from exc
EmailArg = Annotated[EmailStr, Argument(click_type=click.STRING, callback=validate_email)]
@app.command("something_with_email")
def something(user_email: EmailArg):
print(user_email)
however there are hacky parts here and there in code above, which I'd be delighted to not have to put in my production code, but I think we'd need a strong statement from @tiangolo or core team of this repository whether we want to go or not into adding pydantic as strong dependency for typer.
Update on this ticket, I've found some free time to work a bit on a tryout for pydantic types integration while still keeping it an optional dependency. I'll write some more tests, and polish a bit the code before I submit something.
Note for people coming here from Google who really want pydantic
support: There are extensions/alternatives to typer
that enable pydantic
types for Python CLIs. Here's a quick overview from most to least similar to typer
:
pydantic-typer
-
Note: I created
pydantic-typer
for personal use. - A thin wrapper around
typer
that addresses this issue as well as the issue described in Typer issue #111. - Fully compatible with
typer
; just replacetyper.run
withpydantic_typer.run
. Tested against the entiretyper
test suite.
feud
Feud is essentially a wrapper around Click that takes classes and functions with type hints and intelligently 'compiles' them into a ready-to-use Click generated CLI.
The author compares it to typer
here:
Typer is a more complete library for building CLIs overall, but currently lacks support for more complex types, such as those offered by Pydantic.
cyclopts
Cyclopts is what you thought Typer was. Cyclopts includes information from docstrings, supports more complex types (even Unions and Literals!), and includes proper validation support.
There is a detailed comparison between typer
and cyclopts
. Two main differences are:
-
cyclopts
uses theAnnotation
syntax exclusively, allowing mostcyclopts
functions to be callable from regular Python code as well. In contrast,typer
also allows definingOption
s andArgument
s using proxy default values for function parameters. -
cyclopts
doesn’t useclick
but implements its own parser.