8000 Use function both as API and click function? · Issue #1054 · pallets/click · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Use function both as API and click function? #1054

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

Open
michaelaye opened this issue Jun 23, 2018 · 7 comments
Open

Use function both as API and click function? #1054

michaelaye opened this issue Jun 23, 2018 · 7 comments
Assignees
Labels

Comments

@michaelaye
Copy link

I am trying to reduce boilerplate code and it was my idea to just decorate an existing function with click decorators to make it available via click. It's unclear to me from the documentation if this use case is supported. I also search the issues for the below error message and couldn't find anything related.

When I decorate a function with click, I am not able to use it as a function within Python:

Here's the function code:

@click.command()
@click.argument('datestr')
def nasa_date_to_iso(datestr):
    """Convert the day-number based NASA format to ISO.

    Parameters
    ----------
    datestr : str
        Date string in the form Y-j

    Returns
    -------
    Datestring in ISO standard yyyy-mm-ddTHH:MM:SS.MMMMMM
    """
    date = dt.datetime.strptime(datestr, nasa_date_format)
    click.echo(date.isoformat())
    return date.isoformat()

Simple enough, right?
I install it via setup.py like so:

    entry_points={
        "console_scripts": [
            'nasa_date_to_iso = planetpy.utils:nasa_date_to_iso',
        ]
    },

but when I call it within ipython I get this:

In [1]: from planetpy.utils import nasa_date_to_iso

In [2]: nasa_date_to_iso('2017-090')
Usage: ipython [OPTIONS] DATESTR

Error: Got unexpected extra arguments (0 1 7 - 0 9 0)
An exception has occurred, use %tb to see the full traceback.

SystemExit: 2

/Users/klay6683/miniconda3/envs/stable/lib/python3.6/site-packages/IPython/core/interactiveshell.py:2971: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
  warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)

So, is it not possible to have the same function both as a click-cli function and an python-internal API?

System: Py3.6 via conda on macOS 10.12.6, click version 6.7

@davidism
Copy link
Member

It's no longer a plain function, it's a command. You can run a command by calling it, which invokes its main method. Pass a list of args for the command to parse, and if you don't want it to exit, pass standalone_mode=False.

# will invoke Click pipeline
nasa_date_to_iso(['2017-090'], standalone_mode=False)
# equivalent
nasa_date_to_iso.main(['2017-090'], standalone_mode=False)

Assuming you know the given command is a direct wrapper for a function you wrote (and not a group or other type of command), you can get at the function with command.callback. However, calling it will just call the function, it won't invoke any of the Click pipeline for validation, callbacks, etc.

# won't invoke Click pipeline
nasa_date_to_iso.callback('2017-090')

@michaelaye
Copy link
Author

Hm, that's of course not optimal, because now I have to decide between adding a boilerplate function with a different name just for click or change all my Python-internal API calls to include the callback method. Both not ideal. :/

@davidism
Copy link
Member
def nasa_date_to_iso(value):
    ...

@click.command('nasa_date_to_iso')
def nasa_date_to_iso_command(value):
    click.echo(nasa_date_to_iso(value))

This would be pretty easy to write a decorator for to automate.

@michaelaye
Copy link
Author

Thank you, that looks like the smallest extra code possible. I was just hoping that that wouldn't be necessary. Thanks anyway!

@kootenpv
Copy link
kootenpv 8000 commented Feb 5, 2020

I would still call this a bad experience as a user. Why do we have to write the boilerplate? You have 5 args, you need to duplicate 5 args. If you go kwargs then you lose the signature.

@pallets pallets locked as resolved and limited conversation to collaborators Feb 5, 2020
@davidism
Copy link
Member
davidism commented Feb 5, 2020

A command is no longer a regular function. It may assume all sorts of other behaviors now, such as argument validation, the availability of the Click context, or that the function is being run interactively. Extracting a behavior into a common function that's used in multiple situations is a common and perfectly acceptable refactor.

@Rowlando13 Rowlando13 self-assigned this May 16, 2025
@Rowlando13
Copy link
Collaborator

I ran into this today. Obvious in retrospect, but probably worth an addition to docs faq.

@Rowlando13 Rowlando13 reopened this May 16, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants
0