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 | bytes and original: str | bytes become str
- The
encoding constructor kwarg and self.encoding attribute
- The bytes branch in
_set_list and the bytes tolerance in parse_pieces (accepting what later crashes)
add_with_encoding() — add() becomes str-only, raising the same decode-hint TypeError the 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
Summary
2.0 should stop accepting
bytesanywhere and requirestr, 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)
HumanName(b'John Smith')encodingkwarg; emitsDeprecationWarningsince 1.3.0hn.first = b'Jane'/HumanName(first=b'Jane')_set_listexplicitly accepts bytes, thenparse_piecesfails deep inside with the confusing inverse errorTypeError: a bytes-like object is required, not 'str'(bytes.split(' '))SetManager.add(b'esq')DeprecationWarningsince 1.3.0SetManager(b'esq')/SetManager([b'esq'])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_encodingresolves the encoding asencoding or sys.stdin.encoding or DEFAULT_ENCODING. The encoding of the process's stdin has no relationship to an arbitrarybytesargument, so the same call can decode differently depending on how the process was launched (terminal vs. pipe vs. embedded withsys.stdin = None). Library behavior should not vary with the runtime environment.What's removed in 2.0
full_name: str | bytesandoriginal: str | bytesbecomestrencodingconstructor kwarg andself.encodingattribute_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 itMigration
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 withstr.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)