Drop python 3.10 and simplify for click 8.2 enum choice #1890
Merged
Conversation
click 8.2 exposes `Choice.normalize_choice` as the extension point for mapping choices to CLI tokens; overriding it to return `.value` for Enum members (instead of the default `.name`) is all that's needed to preserve the lowercase value tokens on the command line. `Choice.convert` then matches by normalized string and returns the enum member automatically, so the custom `__init__` and `convert` become unnecessary. Bump the `click` dependency floor to `>= 8.2` accordingly; the earlier lower bound (`>= 7.1`) predates the `normalize_choice` API. Co-Authored-By: Claude Code 2.1.197 / Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1890 +/- ##
==========================================
+ Coverage 76.88% 76.93% +0.04%
==========================================
Files 88 88
Lines 12901 12872 -29
==========================================
- Hits 9919 9903 -16
+ Misses 2982 2969 -13
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
|
hint: if you tell your agent to 'watch the pr' it will automatically fix any CI failures, such as the docs build error |
Member
Author
|
thanks! I typically do not permit the beasts to push without my supervision, so would need another manual step in the loop |
Python 3.11 introduced `enum.StrEnum` — a string-mixed `Enum` with a `__str__` that returns the value. That is exactly the semantics the 11 `(str, Enum)` classes used for click `--` choice options implemented by hand via a `__str__ -> self.value` override. Migrate them to `StrEnum` and drop the overrides. Bump the minimum Python to 3.11 (from 3.10, EOL 2026-10-04): update the `requires-python`, classifiers, hatch default env, GitHub Actions matrix (removing the 3.10 leg and repointing the pinned single-Python `include:` entries to 3.11), the lone lint workflow, and remove the `tomli; python_version < '3.11'` conditional from the lint tox env (unreachable under the new floor). `EnumChoice` is unchanged — `StrEnum` members still expose distinct `.name` and `.value`, and click 8.2 still defaults to matching on `.name`, so the `normalize_choice` override that selects `.value` remains necessary. `dandi/utils.py:StrEnum` (a `(str, Enum)` shim used by `dandi/validate/_types.py` and `dandi/bids_validator_deno/_models.py`) is intentionally left untouched: its `_generate_next_value_` returns `name` (case-preserving), while `enum.StrEnum` returns `name.lower()`. Migrating those enums would change every `auto()`-derived value that appears in serialized validation output. Co-Authored-By: Claude Code 2.1.197 / Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5d2cec5 to
b53ceba
Compare
CodyCBakerPhD
approved these changes
Jul 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #1883 that collapses the click-8.2 compatibility shim
and rebases the CLI enums on the standard-library
enum.StrEnum(Python 3.11+). No behavior change — same command-line surface,
same
--helpoutput, same accept/reject semantics.Two independent commits:
click 8.2 exposes
Choice.normalize_choiceas the explicit hookfor choice-string normalization. Overriding it to return
.valuefor
Enummembers (instead of the default.name) is all thatis needed to preserve the pre-8.2 CLI surface, because
Choice.convertthen matches by normalized string and returns thematched enum member automatically. The custom
__init__+convert+self.enum_clsbookkeeping in Support click >= 8.2.0 #1883'sEnumChoicebecome unnecessary. Bumps the
clickfloor from>= 7.1to>= 8.2— the earlier bound predatesnormalize_choice.Migrate the 11 CLI-facing
(str, Enum)classes toenum.StrEnumand drop their hand-written__str__ -> self.valueoverrides.StrEnumwas introduced inPython 3.11 and provides exactly these semantics natively. This
requires bumping the minimum Python from 3.10 (EOL 2026-10-04)
to 3.11:
pyproject.toml,tox.ini, and 5 GitHub Actionsworkflows are updated accordingly (matrix drops the 3.10 leg;
pinned single-Python
include:entries move to 3.11).Net change vs merged #1883: +39 / −83 across 14 files.
Commits
Simplify EnumChoice via click 8.2 normalize_choice hookAdopt enum.StrEnum for CLI choice enums; drop Python 3.10Per-commit details
b2d3c64
Simplify EnumChoice via click 8.2 normalize_choice hookEnumChoiceshrinks from ~15 lines (custom__init__,convert,self.enum_cls, defensiveNone/isinstanceguards) to a single3-line
normalize_choiceoverride. Callsites are unchanged —iterating an Enum class yields members, and
click.Choice.__init__already does
tuple(choices)on any iterable, soEnumChoice(DownloadExisting)still works verbatim.pyproject.toml:click >= 7.1→click >= 8.2. Thenormalize_choicehook is documented "added in 8.2.0" upstream;retaining a lower bound would just be dead compatibility.
5d2cec5
Adopt enum.StrEnum for CLI choice enums; drop Python 3.10Migrated:
SyncMode(consts.py),DownloadExisting/DownloadFormat/PathType(download.py),UploadExisting/UploadValidation(upload.py),MoveExisting/MoveWorkOn(
move.py),FileOperationMode/CopyMode/OrganizeInvalid(
organize.py). Each gets its(str, Enum)base changed toStrEnumand its__str__ -> self.valueoverride deleted._Existingindandi/cli/tests/test_base.pyalso migrated forconsistency.
EnumChoiceitself is unchanged —StrEnumstill exposes distinct.nameand.value, and click 8.2 still defaults to matching on.name, so thenormalize_choiceoverride remains necessary.Python floor moves from 3.10 to 3.11 to unlock
enum.StrEnum:pyproject.toml:requires-python = ">=3.11", drop the 3.10classifier,
[tool.hatch.envs.default] python = "3.11".tox.ini: drop thetomli; python_version < '3.11'conditionalfrom the lint env (unreachable under the new floor).
.github/workflows/run-tests.yml: matrix drops3.10; the fiveinclude:entries pinned to'3.10'(dandi-api,dandi-api+ember-dandi,dev-deps,nfs, and theember-dandisandboxvendored config) move to
'3.11'..github/workflows/{lint,docs,release,typing}.yml: singlepython-versionbumps to'3.11'.Intentionally not migrated:
dandi/utils.py:StrEnum(a(str, Enum)shim used bydandi/validate/_types.pyanddandi/bids_validator_deno/_models.py). Its_generate_next_value_returnsname(case-preserving), whileenum.StrEnum._generate_next_value_returnsname.lower().Migrating it would change every
auto()-derived value that appearsin serialized
ValidationResultoutput (Standard.BIDS→"BIDS"vs
"bids", etc.) — a separate audit worth its own PR.Test plan
pytest dandi/cli/tests/{test_base,test_command,test_move,test_download}.py— all pass on click 8.4.2 + Python 3.13dandi/cli/tests/ + dandi/tests/test_organize.py— 154 pass (only pre-existingbids-validator-deno-binary-dependent tests skipped in local env)dandi organize --files-mode SIMULATEstill rejected; lowercasesimulatestill accepteddandi {download,upload,organize,move} --helprender lowercase choice lists unchanged