Skip to content

make flag_value=None actually set flag value to None #2514

@SpaceshipOperations

Description

@SpaceshipOperations

Right now, click uses None as a sentinel for the flag_value keyword in the @click.option decorator, which triggers the default behavior where click tries to figure out the flag value by itself (either True or False). This has the unfortunate effect that you cannot tell click to use None as a flag value.

My command function has a keyword with the type bool | None, and I want my CLI to offer three flags, where one of them sets it to True, a second sets it to False, and a third sets it to None (which is also the default). Right now, there does not seem to be a straightforward way to do this in click.

Supporting None as a value can be implemented trivially by using some other sentinel that is guaranteed to be unique and unused by outsiders, which is a fairly common pattern in libraries where None is accepted as a possible/meaningful value for some keyword rather than to mean "this keyword was not specified". Example:

_unspecified = object()

# The `@click.option` decorator definition
def option(..., flag_value=_unspecified, ...):
    # ....
    if flag_value is _unspecified:
        # figure out flag value
    else:
        # accept flag value as-is (including if it's None)

Even better, the sentinel can be made a public feature by giving it a more meaningful name like this:

AUTO = object()

# The `@click.option` decorator definition
def option(..., flag_value=AUTO, ...):
    # ....
    if flag_value is AUTO:
        # figure out flag value
    else:
        # accept flag value as-is (including if it's None)

Then, users of the library can explicitly request an automatic value for the flag like this:

import click

@click.command
@click.option('-x', flag_value=click.AUTO)
def cmd(x: bool) -> None:
    ...

Note that I know I can make a non-flag option that accepts an argument and converts it to one of True, False or None, but the three flags approach is far more preferred and more appropriate for this use case. (And I suppose many other people's use cases as well.)

Also, while there might be some workarounds for this, like using any other arbitrary three constants as flag values, but having to do so instead of simply being able to supply None as a flag value is kind of awkward (especially when you take type annotations into account), so I think that in the grand scheme of things, it would probably be much better if click supported something elementary like this, rather than forcing the user to come up with awkward workarounds.

In any case, thank you in advance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions