Repository navigation
Drop bytes input in 2.0; require str #245
Description
Summary
2.0 should stop accepting bytes anywhere and require str, making decoding the caller's job. The bytes tolerance is a Python 2 legacy, and — verified below — it is already inconsistent to the point of being half-broken: bytes input currently gets a different behavior depending on which door it walks through.
Current state (verified)
| Entry point | Behavior with bytes |
|---|---|
HumanName(b'John Smith') |
works — decoded with the encoding kwarg; emits DeprecationWarning since 1.3.0 |
hn.first = b'Jane' / HumanName(first=b'Jane') |
crashes — _set_list explicitly accepts bytes, then parse_pieces fails deep inside with the confusing inverse error TypeError: a bytes-like object is required, not 'str' (bytes.split(' ')) |
SetManager.add(b'esq') |
works — decoded, but see the stdin sniffing below; emits DeprecationWarning since 1.3.0 |
SetManager(b'esq') / SetManager([b'esq']) |
already rejected loudly with a decode hint (1.3.0: #238, PR #240) |
So the "feature" is: two paths that decode, one that crashes confusingly despite an isinstance check that promises support, and one that's already banned. Nobody can be relying on the full surface, because part of it doesn't function.
Additional defect: environment-dependent decoding
SetManager.add_with_encoding resolves the encoding as encoding or sys.stdin.encoding or DEFAULT_ENCODING. The encoding of the process's stdin has no relationship to an arbitrary bytes argument, so the same call can decode differently depending on how the process was launched (terminal vs. pipe vs. embedded with sys.stdin = None). Library behavior should not vary with the runtime environment.
What's removed in 2.0
full_name: str | bytesandoriginal: str | bytesbecomestr- The
encodingconstructor kwarg andself.encodingattribute - The bytes branch in
_set_listand the bytes tolerance inparse_pieces(accepting what later crashes) add_with_encoding()—add()becomes str-only, raising the same decode-hintTypeErrorthe constructor already raises; the stdin sniffing goes with it
Migration
One line at the boundary: HumanName(raw.decode('utf-8')) — the caller knows the real encoding; the library never did. Everything else is the same call with str.
Bridge
-
DeprecationWarningwhen bytes reachesfull_nameorSetManager.add(), naming the decode-first replacement — shipped in 1.3.0 (Add 2.0 deprecation warnings for bytes input and SetManager legacy surface #253) -
1.4:
DeprecationWarningon everyadd_with_encoding()call regardless of argument type, namingadd()as the replacement — the str path is silent today, so the method's removal is otherwise unwarned (bytes callers additionally get the existing decode-first warning; two distinct migrations). Tracked in Add 2.0 deprecation warnings for second-wave removals (1.4 sweep) #263.
Activity
Add 2.0 deprecation warnings for 1.4 second-wave removals (#263) (#286)
Reconcile test_python_api against the facade (buckets A, B, E)
Reconcile test_constants against the shim (buckets A, B, C)
Harden shim notification/validation paths and facade failure ordering
docs: rewrite the 2.0 release log new-API-first and complete the audit
derek73 commentedon Jul 28, 2026
Shipped in 2.0.0. Delivery and release-log coverage were verified issue-by-issue in the pre-release milestone audit.