Skip to content
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

[QUESTION] why replace "_" with "-" in command name? #341

Open
7 tasks done
bloodynumen opened this issue Nov 19, 2021 · 14 comments
Open
7 tasks done

[QUESTION] why replace "_" with "-" in command name? #341

bloodynumen opened this issue Nov 19, 2021 · 14 comments
Labels
question Question or problem

Comments

@bloodynumen
Copy link

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the Typer documentation, with the integrated search.
  • I already searched in Google "How to X in Typer" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to Typer but to Click.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

from typing import Optional

import typer

app = typer.Typer()


@app.command()
def hello_test(name: Optional[str] = None):
    if name:
        typer.echo(f"Hello {name}")
    else:
        typer.echo("Hello World!")

if __name__ == "__main__":
    app()

Description

expect: python main.py hello_test --name world
actually: python main.py hello-test --name world

I found the "_" in command name is replaced with "-".
source code https://github.com/tiangolo/typer/blob/master/typer/main.py#L390

why do this?

Thank u

Operating System

macOS

Operating System Details

No response

Typer Version

0.4.0

Python Version

3.9.7

Additional Context

No response

@bloodynumen bloodynumen added the question Question or problem label Nov 19, 2021
@daddycocoaman
Copy link
Contributor

This is probably just a design choice based around natural CLI convention. You're usually gonna find dashes in commands in any CLI, not underscores. If you want to keep the underscore, you can just specify the command name manually in the app.command().

@mbopfNIH
Copy link

mbopfNIH commented May 23, 2022

This type of undocumented behavior is why I have decided not to use Typer. I like a lot of things about the package, but it's not worth the learning curve of digging through the code when something doesn't work as expected.

@targhs
Copy link
Contributor

targhs commented Jun 10, 2022

I guess that helps

@manueldeprada
Copy link

manueldeprada commented Jul 31, 2022

In fact, this is something that comes from the "click" package which typer uses as a base. If it helps, it comes from here:

https://github.com/pallets/click/blob/dc918b48fb9006be683a684b42cc7496ad649b83/src/click/core.py#L2633

@Timmmm
Copy link

Timmmm commented Aug 21, 2023

Yeah this is pretty annoying. Is there any way to disable this behaviour? There doesn't seem to be anything in typer.Option(). In fact I couldn't find any reference documentation at all...?

@manueldeprada
Copy link

Yeah this is pretty annoying. Is there any way to disable this behaviour? There doesn't seem to be anything in typer.Option(). In fact I couldn't find any reference documentation at all...?

I don’t think so, since it comes from click. I switched to Python Fire by google.

@Timmmm
Copy link

Timmmm commented Aug 21, 2023

Ok this comes from get_command_name().

You can fix it like this:

typer.main.get_command_name = lambda name: name

I suggest an annotation on def main() to allow you to configure whether or not this happens.

Also note for bool options you'll get --no-foo_bar which is a bit weird but whatever.

@Timmmm
Copy link

Timmmm commented Aug 21, 2023

I switched to Python Fire by google.

Ah I hadn't heard of that. Had a look but it seems like it has a slightly different focus and also doesn't support type hints which is like 90% of the reason for using Typer. I'll stick with my get_command_name hack for now I reckon.

@manueldeprada
Copy link

Ok this comes from get_command_name().

You can fix it like this:

typer.main.get_command_name = lambda name: name

I suggest an annotation on def main() to allow you to configure whether or not this happens.

Also note for bool options you'll get --no-foo_bar which is a bit weird but whatever.

Hmm interesting. Can you check if it also works for option names? I mean like --cool_option gets transformed into cool-option? Thanks :)

@Timmmm
Copy link

Timmmm commented Aug 22, 2023

Hmm interesting. Can you check if it also works for option names? I mean like --cool_option gets transformed into cool-option? Thanks :)

Yep that's how I'm using it.

#!/usr/bin/env python3

import typer
from typing_extensions import Annotated

typer.main.get_command_name = lambda name: name

def main(foo_bar: Annotated[int, typer.Option(help="Do a thing.")]):
    return 0

if __name__ == "__main__":
    typer.run(main)
❯ ./test.py --help
                                                                                                             
 Usage: test.py [OPTIONS]                                                                                    
                                                                                                             
╭─ Options ─────────────────────────────────────────────────────────────────────────────────────────────────╮
│ *  --foo_bar        INTEGER  Do a thing. [default: None] [required]                                       │
│    --help                    Show this message and exit.                                                  │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯

@VladikaAndrii
Copy link

Хм цікаво. Чи можете ви перевірити, чи це також працює для назв параметрів? Я маю на увазі, як --cool_option перетворюється на cool-option? Дякую :)

Так, я цим користуюся.

#!/usr/bin/env python3

import typer
from typing_extensions import Annotated

typer.main.get_command_name = lambda name: name

def main(foo_bar: Annotated[int, typer.Option(help="Do a thing.")]):
    return 0

if __name__ == "__main__":
    typer.run(main)
❯ ./test.py --help
                                                                                                             
 Usage: test.py [OPTIONS]                                                                                    
                                                                                                             
╭─ Options ─────────────────────────────────────────────────────────────────────────────────────────────────╮
│ *  --foo_bar        INTEGER  Do a thing. [default: None] [required]                                       │
│    --help                    Show this message and exit.                                                  │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯

Thanks, you saved me a lot of time!

@jaraco
Copy link

jaraco commented Aug 21, 2024

Personally, I prefer the dash convention because it allows for less typing. On many (most?) keyboard layouts, - is a single keystroke, whereas _ is a shifted keystroke, requiring three motions in coordination versus one.

@Timmmm
Copy link

Timmmm commented Aug 21, 2024

Yeah that's an advantage for sure. I guess it's up to you whether it outweighs the disadvantage of making the code harder to follow.

Another disadvantage of using _ I've found is that for bool arguments Typer generates --no-foo_bar which is just weird!

@gajop-ptx
Copy link

There are plenty cases where this automatic replacement of "_" with "-" does more harm than good.

I am often the user of my CLI programs, and not being able to directly copy-paste (or programatically generate) use cases from the API is annoying, but most of all, this hinders search / refactoring capability.

CLI programs are, for the most part, refactored by text search & replacement, and it's annoying to think about underscore/dash in these scenarios.

This

typer.main.get_command_name = lambda name: name

can suffice for now, but a more reliable (proper API) option is necessary, as this might break at any time, since it's not part of the API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Question or problem
Projects
None yet
Development

No branches or pull requests

9 participants