Skip to content

WIP: Require an automatic parameter name to be a Python identifier - #3827

Draft
kdeldycke wants to merge 1 commit into
pallets:mainfrom
kdeldycke:param-name-case-coverage
Draft

WIP: Require an automatic parameter name to be a Python identifier#3827
kdeldycke wants to merge 1 commit into
pallets:mainfrom
kdeldycke:param-name-case-coverage

Conversation

@kdeldycke

@kdeldycke kdeldycke commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

This PR changes the behavior of automatic naming:

  • An argument must produce a valid Python identifier. 0-file, foo.bar and foo bar are now refused.
  • expose_value=False no longer bypasses that check, on both arguments and options.
  • An argument or option requires at least one declaration that can't be empty. click.argument(expose_value=False) and click.option(expose_value=False) used to produce a "" parameter.
  • An awkward explicit option name is normalized: click.option("--in-file", "Input_File") names its parameter input_file.

What does not change:

  • The transformation itself: every - becomes _ and the result is lower cased.
  • An exposed option still raise if not producing a Python identifier, like click.option("--0-file").

How to migrate:

  • Give an option an explicit name: click.option("--0-file", "zero_file").
  • Rename an argument and pass metavar to keep the old display: click.argument("zero_file", metavar="0-FILE").

This PR shares the majority of the tests from #3866 which lockdown all other edge-cases and canonical naming behavior.

This PR is waiting for #3866 to be merged into the 8.x release cycle to be rebased on it.

Context

It is in the same vein as my previous PR at #3808 , and is also based on a collection of edge-cases I accumulated over the years. It also covers platform differences between Windows and Unix-like (see: #2483).

Note: this PR started as simple test coverage expansion but ended up implementing sanitizing fixes.

@kdeldycke kdeldycke added this to the 8.5.1 milestone Sep 1, 2026
@kdeldycke kdeldycke added docs Updates to documentation, readme, docstrings, typos tests Click's own test suite and CI workflows labels Sep 1, 2026
@davidism

davidism commented Sep 1, 2026

Copy link
Copy Markdown
Member

Both of these seem like bugs. I feel like the automatic names should always be normalized and pass str.isidentifier.

@kdeldycke
kdeldycke force-pushed the param-name-case-coverage branch from 254d9e4 to 49a5f5a Compare September 1, 2026 14:43
@kdeldycke

Copy link
Copy Markdown
Collaborator Author

Both of these seem like bugs. I feel like the automatic names should always be normalized and pass str.isidentifier.

OK cool, that was also my feeling. We don't want special treatments of the name of parameters, whatever their kind (options or arguments). I cannot find any reasons with the transformation should be different between the two kinds. So in the end yes, str.isidentifier should be the minimal check common to the two.

@kdeldycke kdeldycke changed the title Cover edge-cases of parameter's naming WIP: Cover edge-cases of parameter's naming Sep 1, 2026
@kdeldycke
kdeldycke marked this pull request as draft September 1, 2026 14:44
@kdeldycke
kdeldycke force-pushed the param-name-case-coverage branch from 49a5f5a to ad8601c Compare September 1, 2026 22:15
@kdeldycke kdeldycke changed the title WIP: Cover edge-cases of parameter's naming WIP: Require Argument's naming to be a Python identifier Sep 1, 2026
@kdeldycke kdeldycke changed the title WIP: Require Argument's naming to be a Python identifier WIP: Require an Argument name to be a Python identifier Sep 1, 2026
@kdeldycke

Copy link
Copy Markdown
Collaborator Author

Both of these seem like bugs. I feel like the automatic names should always be normalized and pass str.isidentifier.

@davidism Done. I did not pushed the normalization too much.

So there is one bothering difference between Argument and Option: the latter makes click.option("--in-file", "Input_File") names its parameter Input_File, while Argument forces it to input_file. Should I align both? I would tend to yes.

@kdeldycke kdeldycke changed the title WIP: Require an Argument name to be a Python identifier Require an Argument name to be a Python identifier Sep 1, 2026
@kdeldycke
kdeldycke force-pushed the param-name-case-coverage branch 3 times, most recently from 05c4439 to 3d4142f Compare September 2, 2026 11:03
@kdeldycke kdeldycke changed the title Require an Argument name to be a Python identifier Sanitize Argument and Option automatic naming Sep 2, 2026
@kdeldycke

Copy link
Copy Markdown
Collaborator Author

I just pushed the alignment and normalization of naming a bit more and updated the differences in the body of that PR.

@kdeldycke
kdeldycke force-pushed the param-name-case-coverage branch from 3d4142f to b4606f9 Compare September 2, 2026 12:43
@kdeldycke kdeldycke changed the title Sanitize Argument and Option automatic naming Require an automatic parameter name to be a Python identifier Sep 2, 2026
@kdeldycke
kdeldycke force-pushed the param-name-case-coverage branch from b4606f9 to ff79a3c Compare September 2, 2026 13:11
@kdeldycke
kdeldycke marked this pull request as ready for review September 2, 2026 13:29
@kdeldycke

Copy link
Copy Markdown
Collaborator Author

OK these were my final edits. You can review this PR.

@kdeldycke
kdeldycke force-pushed the param-name-case-coverage branch from ff79a3c to 6f9943e Compare September 2, 2026 13:32
Comment thread docs/arguments.md Outdated
Comment thread docs/options.md Outdated
Comment thread CHANGES.md Outdated
@davidism

davidism commented Sep 2, 2026

Copy link
Copy Markdown
Member

This needs to be targeted at 9.0 and listed high up in the changelog, the name normalization is a breaking change. Along with the keyword check I mention below, this could be significantly disruptive. We should probably issue a deprecation warning and accept names that are currently accepted, before applying the new rules.

It might be good for the TypeError to give explicit instructions: Argument {decls} tried to use 'name' but it is not a valid Python identifier. Add a valid name to the parameter declaration.

Overall I think the docs are a little too verbose about this. Giving a few examples and linking to isidentifier should be enough.

You bring up a good point about keywords. I think the intention of this code was clear: to only accept names that can be used in signatures rather than kwargs. We can also check that the name isn't a keyword with https://docs.python.org/3/library/keyword.html. I bet from and in are pretty common option names though, perhaps this is not worth it?

Comment thread src/click/core.py Outdated

@Rowlando13 Rowlando13 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @davidism. The docs are too verbose and these changes should go through normal deprecation process. Probably a best course of action is to make a pr against stable that has whatever tests you think we should have to lock in existing behavior. Then can merge stable into main. And you can make the new pr against main with the deprecations.

@kdeldycke

Copy link
Copy Markdown
Collaborator Author

Ahah agreed 100% about the verbosity of the docs. They accumulates all my changes and discovery through the process of producing this PR. It was a way for me to highlight how clunky the ID derivation is at the moment. My thinking being that if I can write the docs easily and make them flow naturally, it probably means the underlying concept and implementation is clean.

Ok for a 9.0.0 target then.

What I will do:

  • keep targeting this for a clean break for 9.0.0
  • propose another PR for 8.5.1/8.6.0 with proper detection of the edge-cases, deprecation warnings and migration documentation so the future break has time to diffuse in the ecosystem

@kdeldycke kdeldycke modified the milestones: 8.5.1, 9.0.0 Sep 8, 2026
@kdeldycke
kdeldycke marked this pull request as draft September 8, 2026 10:59
@kdeldycke kdeldycke mentioned this pull request Sep 8, 2026
@kdeldycke
kdeldycke changed the base branch from stable to main September 11, 2026 07:04
@kdeldycke kdeldycke changed the title Require an automatic parameter name to be a Python identifier WIP: Require an automatic parameter name to be a Python identifier Sep 11, 2026
@kdeldycke
kdeldycke force-pushed the param-name-case-coverage branch from 6f9943e to 127ed65 Compare September 11, 2026 08:54
@kdeldycke

Copy link
Copy Markdown
Collaborator Author

You bring up a good point about keywords. I think the intention of this code was clear: to only accept names that can be used in signatures rather than kwargs. We can also check that the name isn't a keyword with https://docs.python.org/3/library/keyword.html. I bet from and in are pretty common option names though, perhaps this is not worth it?

Problem is the list of Python keywords would prevent some options like --import or --from. Which is a hard constraint to impose a developer of a CLI. I prefer to not enforce checks too much here.

@kdeldycke

Copy link
Copy Markdown
Collaborator Author

OK so I think I am done with this PR and it now implements the ideal target behavior for 9.0.0. I will now create in the next few days a sibling PR for 8.5.1/8.6.0 that detects the changes and emits deprecation warnings. If I discover new stuff while producing this other PR I might bring some changes here.

@kdeldycke kdeldycke changed the title WIP: Require an automatic parameter name to be a Python identifier Require an automatic parameter name to be a Python identifier Sep 11, 2026
@kdeldycke
kdeldycke marked this pull request as ready for review September 11, 2026 09:14
kdeldycke added a commit that referenced this pull request Sep 11, 2026
Click 9.0 refuses such a name; warn a release ahead so the break can diffuse.
Prepares the ground for #3827, as promised in pull/3827#issuecomment-5583963038.
kdeldycke added a commit to kdeldycke/click that referenced this pull request Sep 11, 2026
Click 9.0 refuses such a name; warn a release ahead so the break can diffuse.
Prepares the ground for pallets#3827, as promised in pull/3827#issuecomment-5583963038.
@kdeldycke kdeldycke changed the title Require an automatic parameter name to be a Python identifier WIP: Require an automatic parameter name to be a Python identifier Sep 11, 2026
@kdeldycke
kdeldycke marked this pull request as draft September 11, 2026 13:34
@kdeldycke kdeldycke added the parsing Parsing, parameters, commands, chaining, context label Sep 11, 2026
kdeldycke added a commit to kdeldycke/click that referenced this pull request Sep 11, 2026
Prepares the ground for pallets#3827 and future 9.0.0 release
kdeldycke added a commit to kdeldycke/click that referenced this pull request Sep 11, 2026
Prepares the ground for pallets#3827 and future 9.0.0 release
@kdeldycke
kdeldycke force-pushed the param-name-case-coverage branch from 127ed65 to 0054c44 Compare September 11, 2026 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Updates to documentation, readme, docstrings, typos parsing Parsing, parameters, commands, chaining, context tests Click's own test suite and CI workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing auto-generated environment variables in help screen & case-sensitivity

3 participants