Skip to content

feat: add case-insensitive choice matching - #44

Merged
bhirsz merged 1 commit into
mainfrom
feat/case-insensitive-choices
Sep 1, 2026
Merged

feat: add case-insensitive choice matching#44
bhirsz merged 1 commit into
mainfrom
feat/case-insensitive-choices

Conversation

@bhirsz

@bhirsz bhirsz commented Sep 1, 2026

Copy link
Copy Markdown
Member

Summary

Adds an ignore_case flag to option() and argument(). When set and the value is constrained by Literal[...] choices, the value is matched case-insensitively and normalised to the spelling declared in the Literal.

class Args(ArgConfig):
    # --colors on / On / ON  all yield "ON"
    colors: Literal["AUTO", "ON", "OFF", "ANSI"] = option(
        name="colors", default="AUTO", ignore_case=True
    )
  • Unknown values are still rejected (invalid value 'purple'; choose from ...).
  • A case-insensitive match is only honoured when unambiguous.
  • Works for scalars, list[Literal[...]], method value parameters, positional argument()s, and values coming from TOML/env — the check lives in coerce_value.
  • Default is unchanged (case-sensitive), so existing options are unaffected.

Motivation

Tools often accept case-insensitive enum-style values whose canonical form is a fixed spelling (e.g. Robot Framework's --consolecolors auto|on|off|ansi, stored canonically as uppercase). Until now, using Literal for those would reject the lowercase spellings users type, so such options had to fall back to plain str and lose validation. With ignore_case=True they can use Literal and gain shell completion of the allowed choices and early validation, while still accepting any casing and handing downstream code the canonical spelling.

Tests

  • tests/test_choices.py: scalar normalisation, unknown-value rejection, list choices, arguments, TOML source, and a guard that the default stays case-sensitive.
  • Full suite: 371 passed; mypy --strict clean; ruff lint + format clean.

Docs

  • README "Restricting a value to a set of choices" gains an ignore_case example.
  • CHANGELOG [Unreleased] Features entry.

Add an ``ignore_case`` flag to ``option()`` and ``argument()``. When set and the
value is constrained by ``Literal[...]`` choices, the supplied value is matched
case-insensitively and normalised to the spelling declared in the ``Literal``
(e.g. ``--colors on`` yields ``"ON"`` for ``Literal["AUTO", "ON", "OFF"]``).

Unknown values are still rejected, and a case-insensitive match is only honoured
when unambiguous. The default behaviour remains case-sensitive, so existing
options are unaffected. This lets tools that accept case-insensitive enum-style
values (e.g. Robot Framework's console colour/marker settings) use ``Literal``
choices — gaining shell-completion of the allowed values and early validation —
without rejecting the lowercase spellings users commonly type.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bhirsz
bhirsz merged commit 76c22d1 into main Sep 1, 2026
8 checks passed
@bhirsz
bhirsz deleted the feat/case-insensitive-choices branch September 1, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant