-
-
Notifications
You must be signed in to change notification settings - Fork 672
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
running typer cli command from python code #106
Comments
A function in a Typer app is not modified by Typer, it's still a normal function. So, you should be able to call it directly as you normally would, just make sure you pass all the parameters to it. |
Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues. |
What about in the case I'm using the Is there a way to deal with this? |
@justinpinkney Did you ever figure out a way to get the defaults correctly? Thanks in advance! My use case is to call the functions directly from the tests, but for every option that has a default I get something like |
My problem exactly, didn't find a good way around this. |
Can this issue be re-opened? It seems to be a relevant one. Right now if we want to call a function from code we have to write duplicate functions just for the Typer CLI with Typer defaults and counterparts with regular python defaults - very ugly ( |
I am experiencing exactly the same issue. The problem is hidden in this statement above: "A function in a Typer app is not modified by Typer, it's still a normal function. So, you should be able to call it directly as you normally would, just make sure you pass all the parameters to it." (Italics mine.) I want to call a Typer function which takes seven parameters, only one of which I want to modify. So I need to pass in seven default arguments to get the one real argument in, and if I ever change any default, I now have to change it not just in the code but at every call site. I can see how this would be fixed in Typer. It wouldn't be trivial, you'd have to use inspection on the function, and then decorate it based on that. |
Yes, so we literally write two functions - one with This way we get The only problem is - you know - there are two functions How a workaround for the problem we all have may look like- |
Exactly!!
I could do this without too much effort, but it would require the Typer
author(s) to want it :-), and to give it a pretty thorough review, as it
would be a fairly big change.
Another way, less violent, would be instead to park the wrapped, directly
callable function on a .call property on the original function, so current
behavior would not be changed.
…On Sat, Sep 4, 2021 at 9:09 AM Yury Paykov ***@***.***> wrote:
Yes, so we literally write *two* functions - one with typer.Option and
friends, which calls *the second one*, which only contain plain default
values.
This way we get --help , type casting, enum checking at runtime and all
what typer offers and still have a way to call the original function
The only problem is - you know - there are two functions
How a workaround for the problem we all have may look like-
Typer.add_command automatically generates a plain version of decorated
function and make it available through -say- .original attribute
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#106 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAB53MRWWHG7JFCJJ55MQKLUAHA2PANCNFSM4NIJ6DYA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
--
/t
PGP Key: ***@***.***
*https://tom.ritchford.com <https://tom.ritchford.com>*
*https://tom.swirly.com <https://tom.swirly.com>*
|
The suggested workarounds make the code very ugly. It would be awesome if Typer can fix this |
Indeed, this shouldn't be closed. It's the biggest blemish this otherwise somewhat elegant library has. |
@tiangolo I agree that this issue should be reopened. |
After thinking about the above for quite a while, I wrote a decorator that takes typer functions and automatically creates a callable The code is not long: dcommand.py. Here's a test that shows how to use it. I'm using this in production in a proprietary project and it's worked very well to reduce clutter and repetition, and to share code between typer and non-typer sides - and it lets me (more or less) unit test my typer commands, which is great. I could easily productionize it into a separate pypi module, or prepare it for release into typer if there were any interest. |
I released this, with one more piece of functionality, as a PyPi module. https://pypi.org/project/dtyper/ It's marked as beta, but I have complete test coverage and it's in use in a production project so far without issue. |
That looks great, I'll try it out. For reference, I've been using this decorator function to get the defaults: import inspect
def set_default_values(function):
def wrapper(**kwargs):
# first, check if any of the kwargs is not included in the signature of the function and raise an error if so
for kwarg in kwargs:
if kwarg not in inspect.signature(function).parameters:
raise ValueError(f'{kwarg} is not a valid argument for {function.__name__}')
new_kwargs = {}
for default_kwarg in inspect.signature(function).parameters.values():
if default_kwarg.name in kwargs.keys():
new_kwargs[default_kwarg.name] = kwargs[default_kwarg.name]
else:
if isinstance(default_kwarg.default, typer.models.OptionInfo):
new_kwargs[default_kwarg.name] = default_kwarg.default.default
else:
new_kwargs[default_kwarg.name] = default_kwarg
return function(**new_kwargs)
if __name__ == '__main__':
return function()
else:
return wrapper
@set_default_values
@app.command ... |
I agree this is an issue which should be fixed |
@rec thank you! just putting In typer you can require user to provide some option/argument by setting default to ellipsis ( @dtyper.function
@app.command()
def main(
city: str = Option("New York"),
name: str = Option(...),
) Now this code is invalid, emitting |
I want to support and recommend |
Arg, I am so sorry I just saw this comment from last week a few moments ago!!! I guess it was part of this thread so I didn't look closely enough. I filed an issue against the project rec/dtyper#4 and I will fix this perhaps tonight but otherwise tomorrow. Sorry again! |
Hello again! This is fixed in dtyper 2.0.0 which I just released. The major version number increase is because While this is technically a breaking change, it's unlikely that any real world code will be affected by this, and it fixes that bug rec/dtyper#4. Named arguments are always preferred in general anyway. I think Thanks for finding this, I even had a unit test testing the wrong behavior! If you file bugs right on https://github.com/rec/dtyper/issues, I'll be less likely to miss them. |
@tiangolo Do you mind elaborating more on how we can leverage |
@mirestrepo I haven't implemented it yet, but it will look more or less like this: @app.command()
def main(
city: Annotated[str, Option("New York")],
):
print(city) |
Running the config command from run-tests will trigger fastapi/typer#106 and we only have an object but not the default string. Also using correct path to determine if new config needs to be generated before running unit tests.
@tiangolo hi! do you intend to implement this feature in package? i mean natively running typer commands from python code as usual functions? |
If @tiangolo pointed me to the right place, I could easily emit a pull request to add this to |
+1 for this! any plans to implement? |
+1 for this ! Still not a dead topic imo. |
It seems the solution proposed by Sebastián was implemented in the mean time and is reflected in the docs -- see https://typer.tiangolo.com/tutorial/options/help/ |
- Solves wrong type for optional parameters fastapi/typer#106 - Add testcases for --no-kernel option
- Solves wrong type for optional parameters fastapi/typer#106 - Add testcases for --no-kernel option
- Solves wrong type for optional parameters fastapi/typer#106 - Add testcases for --no-kernel option
- Solves wrong type for optional parameters fastapi/typer#106 - Add testcases for --no-kernel option
How can i trigger/run typer cli specific command from python code. I guess i need to emulate command line arguments some where but not sure. Can you please help on this?
Cli:
python main.py goodbye --formal Camila
From Code:
i want to run specific command based on my need. how do i pass command line arguments, some thing like:
typer_app( "goodbye --formal Camila" )
The text was updated successfully, but these errors were encountered: