Giter Club home page Giter Club logo

Comments (28)

mirestrepo avatar mirestrepo commented on May 13, 2024 15

@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 <typer.models.OptionInfo object at 0x119787650> instead of the default value. I suppose if I don't use the typer.Option as a default it should work, but then I can take advantage of the --help etc

from typer.

justinpinkney avatar justinpinkney commented on May 13, 2024 11

What about in the case I'm using the typer.Option("default") syntax, now my function doesn't receive "default" as the default value, but instead a typer.models.OptionInfo object

Is there a way to deal with this?

from typer.

ojomio avatar ojomio commented on May 13, 2024 10

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 (

from typer.

rec avatar rec commented on May 13, 2024 6

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.

from typer.

Spenhouet avatar Spenhouet commented on May 13, 2024 4

@tiangolo I agree that this issue should be reopened.
If one uses typer.Option the function also requires that and not the actual type.

from typer.

jonasjancarik avatar jonasjancarik commented on May 13, 2024 4

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 ...

from typer.

starwalker00 avatar starwalker00 commented on May 13, 2024 4

+1 for this ! Still not a dead topic imo.

from typer.

rec avatar rec commented on May 13, 2024 3

from typer.

rec avatar rec commented on May 13, 2024 3

After thinking about the above for quite a while, I wrote a decorator that takes typer functions and automatically creates a callable dataclass with the same signature and proper defaults, which in many ways is even better, because you can easily write methods and properties.

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.

from typer.

rec avatar rec commented on May 13, 2024 3

If @tiangolo pointed me to the right place, I could easily emit a pull request to add this to typer.

from typer.

tiangolo avatar tiangolo commented on May 13, 2024 2

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.

from typer.

rec avatar rec commented on May 13, 2024 1

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.

from typer.

AbeHandler avatar AbeHandler commented on May 13, 2024 1

I agree this is an issue which should be fixed

from typer.

tiangolo avatar tiangolo commented on May 13, 2024 1

I want to support and recommend Annotated, that will alleviate a lot of these problems.

from typer.

rec avatar rec commented on May 13, 2024 1

Hello again! This is fixed in dtyper 2.0.0 which I just released.

The major version number increase is because typer.Option arguments are now keyword-only, when before they were positional or keyword.

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 Arguments -> positional or keyword, Options -> keyword only is the clearest conceptual solution but open to arguments.


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.

from typer.

github-actions avatar github-actions commented on May 13, 2024

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

from typer.

justinpinkney avatar justinpinkney commented on May 13, 2024

@justinpinkney Did you ever figure out a way to get the defaults correctly? Thanks in advance! My use case is to call the suctions directly from the tests, by for every option that has a default I get something like <typer.models.OptionInfo object at 0x119787650> instead of the default value. I suppose if I don't use the typer.Option as a default it should work, but then I can take advantage of the --help etc

My problem exactly, didn't find a good way around this.

from typer.

ojomio avatar ojomio commented on May 13, 2024

Yes, so we literally write two functions - one with typer.Option and friends, which calls the second one, which only contains 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

from typer.

sherifattia avatar sherifattia commented on May 13, 2024

The suggested workarounds make the code very ugly. It would be awesome if Typer can fix this

from typer.

nacitar avatar nacitar commented on May 13, 2024

Indeed, this shouldn't be closed. It's the biggest blemish this otherwise somewhat elegant library has.

from typer.

lainisourgod avatar lainisourgod commented on May 13, 2024

@rec thank you! just putting @dtyper.function over my command works. Except one issue.

In typer you can require user to provide some option/argument by setting default to ellipsis (...). Usually you can put requried options in the mix with optional. E.g.

@dtyper.function
@app.command()
def main(
    city: str = Option("New York"),
    name: str = Option(...),
)

Now this code is invalid, emitting ValueError: non-default argument follows default argument emitting from lib inspect.

from typer.

rec avatar rec commented on May 13, 2024

Now this code is invalid, emitting ValueError: non-default argument follows default argument emitting from lib inspect.

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!

from typer.

mirestrepo avatar mirestrepo commented on May 13, 2024

@tiangolo Do you mind elaborating more on how we can leverage Annotated in this case? Thank you!

from typer.

tiangolo avatar tiangolo commented on May 13, 2024

@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)

from typer.

lainisourgod avatar lainisourgod commented on May 13, 2024

@tiangolo hi! do you intend to implement this feature in package? i mean natively running typer commands from python code as usual functions?

from typer.

rec avatar rec commented on May 13, 2024

The code to do it is here and here.

from typer.

glass-ships avatar glass-ships commented on May 13, 2024

+1 for this! any plans to implement?

from typer.

trymzet avatar trymzet commented on May 13, 2024

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/

from typer.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.