-
I want to extend the CLI app with custom data types not supported by Typer/Click. Right now, the Typer app raises an error on encountering an unsupported type. The solution you would likeWe could add a method to the Typer object to register custom data types along with a function to convert terminal input string to an object of the desired type. A possible API could be: app = typer.Typer()
app.register_custom_type(_type, _deserializer) Here is the monkey-patched solution I use right now without providing a import datetime
from typing import Any, Callable, Optional
import typer
import click
_get_click_type = typer.main.get_click_type
REGISTERED_TYPES = {
datetime.date: lambda string: datetime.datetime.strptime(string, '%Y-%m-%d').date()
}
class TyperCustomParam(click.ParamType):
name = 'CustomParameter'
def __init__(self, typ: type, deserializer: Optional[Callable] = None):
self.name = typ.__name__
self._deserializer = typ if deserializer is None else deserializer
def convert(self, value, param, ctx):
try:
return self._deserializer(value)
except Exception as E:
self.fail(
f"couldn't serialize {value} to an instance of type {self.name}, error: {E}"
)
def supersede_get_click_type(
*, annotation: Any, parameter_info: typer.main.ParameterInfo
) -> click.ParamType:
if annotation in REGISTERED_TYPES:
return TyperCustomParam(annotation, REGISTERED_TYPES[annotation])
else:
return _get_click_type(annotation=annotation, parameter_info=parameter_info)
typer.main.get_click_type = supersede_get_click_type AlternativesAlternatives would be to either modify the original routine or create a wrapper function to accept a Click supported datatype and convert later. Letting users create custom types directly is preferred since that lets us create CLIs without touching existing code. Thank you for looking into this. Similar to FastAPI, typer is a really nice library. |
Beta Was this translation helpful? Give feedback.
Replies: 10 comments
-
I need this feature (actually for |
Beta Was this translation helpful? Give feedback.
-
Hey @jackric, the method used above requires a global container, which makes me uneasy. You could probably use this snippet in your code and import the |
Beta Was this translation helpful? Give feedback.
-
@ananis25 I think a nice approach would be to register custom types with a new function - either on import typer
typer.register_type(datetime.date, lambda string: datetime.datetime.strptime(string, '%Y-%m-%d').date()) import typer
app = typer.Typer()
app.register_type(datetime.date, lambda string: datetime.datetime.strptime(string, '%Y-%m-%d').date())
|
Beta Was this translation helpful? Give feedback.
-
If I understand correctly, the above snippet does exactly this, isn't it? I'd leave it to the maintainers to see if they really want to add this. |
Beta Was this translation helpful? Give feedback.
-
I agree with the solution proposed by @jackric. There are lots of "wrapper types" that can be called on a string input and give an augmented object that is still a string. Many more are just variants of already handled types. So maybe if Typer did just try to call the annotated type by default, it would be a quick and easy way to solve many problems with not so much work. -- I think it should at least be possible to skip an unhandled type with a mere warning. To let users properly annotate their functions without having to use some crazy black magic. |
Beta Was this translation helpful? Give feedback.
-
One thing that is missing when registering custom types (or at least not explicitly clear) is the usage when I just want to add a converter to an existing type. For example - parsing a hexadecimal number. I want to extend the parsing, but the result type is still a number. The options I see for this are either: @app.command()
def command(number: int = Typer.Argument(converter=lambda x: int(x, 0))) Or class HexInt(int): pass
# Fill in registering custom type
@app.command()
def command(number: HexInt) I am not sure what I think about either. |
Beta Was this translation helpful? Give feedback.
-
My 2 cents: declaring the |
Beta Was this translation helpful? Give feedback.
-
I wrote a PR that implements this, please let me know what you think: #443 |
Beta Was this translation helpful? Give feedback.
-
+1 would love to see @paulo-raca 's PR merged. Thanks! |
Beta Was this translation helpful? Give feedback.
-
Thanks all! Support for this was added in #583, the new docs are here: https://typer.tiangolo.com/tutorial/parameter-types/custom-types/, it's available in Typer |
Beta Was this translation helpful? Give feedback.
Thanks all! Support for this was added in #583, the new docs are here: https://typer.tiangolo.com/tutorial/parameter-types/custom-types/, it's available in Typer
0.8.0
, just released. 🎉