Click: Environment variable name generation from command name is broken

Created on 8 Mar 2019  ·  6Comments  ·  Source: pallets/click

When command names contain a - character, automatic environment variable name generation is broken

#!/usr/bin/env python3

import click

@click.group()
@click.option('--debug/--no-debug')
def cli(debug):
    click.echo('Debug mode is %s' % ('on' if debug else 'off'))

@click.command(name="greet-me")
@click.option('--username', show_envvar=True)
def greet(username):
    click.echo('Hello %s!' % username)

if __name__ == '__main__':
    cli.add_command(greet)
    cli(auto_envvar_prefix='GREETER')
$ pipenv run python ./test.py greet-me  --help
Loading .env environment variables…
Debug mode is off
Usage: test.py greet-me [OPTIONS]

Options:
  --username TEXT  [env var: GREETER_GREET-ME_USERNAME]
  --help           Show this message and exit.

This seems to be solved by changing core.py:329 to

       # If there is no envvar prefix yet, but the parent has one and
        # the command on this level has a name, we can expand the envvar
        # prefix automatically.
        if auto_envvar_prefix is None:
            if parent is not None \
               and parent.auto_envvar_prefix is not None and \
               self.info_name is not None:
                auto_envvar_prefix = '%s_%s' % (parent.auto_envvar_prefix,
                                           self.info_name.upper().replace("-", "_"))

I'm not opening a pull request though as I'm not familiar with click's codebase and

  • I'm not sure the modification above is general enough as there are other instances of <something>.name.upper() found in core.py
  • I'm not sure replacing - with _ covers all the tricky cases

Most helpful comment

This issue is causing me problems as well. Thank you @gpakosz for submitting a fix!

All 6 comments

In fact, it also happens when not setting the command name explicitly but letting click renaming function names with underscore to command names.

#!/usr/bin/env python3

import click

@click.group()
@click.option('--debug/--no-debug')
def cli(debug):
    click.echo('Debug mode is %s' % ('on' if debug else 'off'))

@cli.command()
@click.option('--username', show_envvar=True)
def greet_me(username, foo):
    click.echo('Hello %s!' % username)

if __name__ == '__main__':
    cli(auto_envvar_prefix='GREETER')
$ pipenv run python ./test.py greet-me --help
Loading .env environment variables…
Debug mode is off
Usage: test.py greet-me [OPTIONS]

Options:
  --username TEXT  [env var: GREETER_GREET-ME_USERNAME]
  --help           Show this message and exit.

This issue is causing me problems as well. Thank you @gpakosz for submitting a fix!

@davidism can this be added to the next milestone by chance?

Stumbled over this today as well. Maybe Command Aliases might be a workaround until #1261 is merged?

Unfortunately, Command Aliases can not be used as a workaround here (at least not how I am doing it):

import click


# this Group allows specifying both "greet_me" and "greet-me"
# (https://click.palletsprojects.com/en/7.x/advanced/?highlight=alias#command-aliases)
class AliasedGroup(click.Group):
    def get_command(self, ctx, cmd_name):
        rv = click.Group.get_command(self, ctx, cmd_name)
        if rv is not None:
            return rv
        matches = [
            x
            for x in self.list_commands(ctx)
            if x in (cmd_name, cmd_name.replace("-", "_"))
        ]
        if not matches:
            return None
        elif len(matches) == 1:
            return click.Group.get_command(self, ctx, matches[0])


@click.group(
    cls=AliasedGroup,
    # setting the auto_envvar_prefix here works better with
    # setuptools/console_scripts
    context_settings={"auto_envvar_prefix": "GREETER"},
)
def cli():
    click.echo("click version {}".format(click.__version__))


@cli.command(name="greet_me")  # force the name to include the underscore
@click.option("--username", show_envvar=True)
def greet_me(username):
    click.echo("Hello %s!" % username)


if __name__ == "__main__":
    cli()

This can be invoked both with greet_me and greet-me. However, using the dash still results in the broken environment variable:

python3 -m test greet_me --help
click version 7.0
Usage: test.py greet_me [OPTIONS]

Options:
  --username TEXT  [env var: GREETER_GREET_ME_USERNAME]
  --help           Show this message and exit.
python3 -m test greet-me --help
click version 7.0
Usage: test.py greet-me [OPTIONS]

Options:
  --username TEXT  [env var: GREETER_GREET-ME_USERNAME]
  --help           Show this message and exit.

Was this page helpful?
0 / 5 - 0 ratings