Skip to content

fix: withdraw the plain-string deprecation - #16

Merged
andremsouza merged 1 commit into
masterfrom
fix/withdraw-string-deprecation
Jul 30, 2026
Merged

fix: withdraw the plain-string deprecation#16
andremsouza merged 1 commit into
masterfrom
fix/withdraw-string-deprecation

Conversation

@andremsouza

Copy link
Copy Markdown
Owner

0.5.0 deprecated the plain-string options and announced removal in 1.0.0. That was wrong, and this
withdraws it.
Strings are permanently supported.

Why it was wrong

Every comparable library passes options as plain strings, and none export enums at all. Measured
against the installed versions, not recalled:

library how options are passed enums exported
scikit-learn 1.9 KMeans(init="k-means++", algorithm="lloyd") none
numpy 2.5 np.pad(mode="constant"), norm(ord="fro") none public
scipy 1.18 linkage(method="single"), minimize(method="BFGS") none
minisom neighborhood_function="gaussian", topology="rectangular" none
sompy normalization="var", neighborhood="gaussian" none

scikit-learn validates them at runtime with StrOptions — a constraint, not a type. minisom is the
closest peer, and python-som's method names already mirror it (activate, winner, quantization,
quantization_error, get_weights).

Removing the string form would have made this the only library in its ecosystem to reject
mode="batch"
.

The book said less than I claimed

The original design cited Viafore, Robust Python, ch. 8. Re-read, his case is about preventing typos
inside a codebase, and he includes a "When Not to Use" section. The benefit he actually claims is
that a type checker rejects a wrong value — and the Literal unions added in 0.4.0 already deliver
exactly that
, with strings still working:

som.train(data, mode="batch")             # fine
som.train(data, mode=TrainingMode.BATCH)  # fine
som.train(data, mode="bacth")             # error: incompatible type Literal['bacth']

So the deprecation bought nothing that was not already had, and cost ecosystem divergence.

What changes

  • warn_if_string and its four call sites are removed.
  • The tests that wrapped string calls in pytest.warns are unwrapped.
  • A new test asserts the absence of any warning at every call site, under
    warnings.simplefilter("error") rather than the removed pytest.warns(None), so any warning at all
    fails. The withdrawal cannot be quietly undone.
  • The docs section becomes "Both spellings are permanent", with the survey table above.

The enums stay. They cost nothing, some callers prefer them, and removing them would be a second
reversal in two releases. Anyone who migrated while 0.5.0 was current keeps working code.

Where this leaves the roadmap

refactor/enums-only is dropped from 1.0.0. With strings staying, what remains breaking is thin —
keyword-only arguments, removing two setters, removing pre-0.3.0 private aliases, dropping Python 3.10
after its October 2026 EOL — and the plan already warned against freezing an interface before it has
been used. So 0.6.0 adds the estimator API additively and 1.0.0 waits for real use of it.

On process

The deprecation was decided without checking what comparable libraries do. Reversing one release later
is cheap; discovering it at 1.0.0, after breaking every user, would not have been.

Verification

383 tests, 100% coverage, ruff / ruff format / mypy --strict clean, mkdocs build --strict exits
0, architecture profile --strict passes. Every call site exercised with plain strings under
-W error::DeprecationWarning.

0.5.0 made plain strings emit DeprecationWarning and announced removal in 1.0.0. That was wrong.
Strings are now permanently supported, and 1.0.0 will not remove them.

Every comparable library passes options as plain strings, and none export enums: scikit-learn
(KMeans(init="k-means++", algorithm="lloyd")), numpy (np.pad(mode="constant")), scipy
(linkage(method="single")), and both SOM libraries, minisom (neighborhood_function="gaussian") and
sompy (normalization="var"). scikit-learn validates them at runtime through StrOptions, a constraint
rather than a type. Removing the string form would have made this the only library in its ecosystem
to reject mode="batch".

The argument originally made for enums, from Viafore's Robust Python ch. 8, is about preventing typos
inside a codebase, and he adds a "When Not to Use" section. The benefit he claims is that a type
checker rejects a wrong value, and the Literal unions added in 0.4.0 already deliver exactly that
with strings still working: mode="bacth" is a type error while mode="batch" is not. The deprecation
bought nothing that was not already had.

The enums stay. They cost nothing, some callers prefer them, and removing them would be a second
reversal in two releases.

`warn_if_string` and its four call sites are gone. The tests that wrapped string calls in
`pytest.warns` are unwrapped, and a new test asserts the *absence* of any warning at every call site
under `simplefilter("error")`, so the withdrawal cannot be quietly undone.

The decision was originally made without checking what comparable libraries do.
@andremsouza
andremsouza merged commit 66b561e into master Jul 30, 2026
8 checks passed
@andremsouza
andremsouza deleted the fix/withdraw-string-deprecation branch July 31, 2026 12:30
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