sorted() over bytes raises where sorted() over str works
Repro
print(sorted([b"cd", b"ab"])) # CPython: [b'ab', b'cd']
# before: TypeError: expected a number, got object
print(sorted(["cd", "ab"])) # worked
print(sorted([2, 1])) # worked
Cause
pyvar_gt (compiler/builtin/pylib.pas) already carried a lexicographic arm for
two TPyLists, added for sorted([("b",2),("a",1)]). That arm covers list,
tuple and set together because NilPy backs all three with one TPyList.
TPyBytes is a different class, so two bytes objects matched neither the
sequence arm nor the user-__gt__ arm and fell through to pyvar_to_int.
One concept — "two sequences compare lexicographically" — and the sibling nobody
extended is the one that stayed broken
(devdocs/dev/normalise-dont-special-case.md).
The part that could have been silently wrong
CPython compares bytes as unsigned values 0..255, so b"\xff" > b"\x01".
Reading the bytes as signed inverts every pair with the top bit set — and still
returns a list that is sorted, just in the wrong order. A "does it run" assertion
cannot see that. test/test_nilpy_bytes_join_and_bytes_n.npy asserts
sorted([b"\xff", b"\x01"]) and sorted([b"\x80", b"\x7f", b"\x00"]) against
CPython's own output for exactly that reason.
Found by grepping for siblings after fixing
bug-n-str-join-rejects-an-argument-shape-cpython-accepts; not reached by
lekkerzeilen, so it blocked nothing and was fixed because it was one line from
a defect already in hand.
Log
- 2026-09-09 — fixed and closed in the same change; the fix and its test row are
in commit d1efd1dee (the
TPyByteslexicographic arm ofpyvar_gtincompiler/builtin/pylib.pas, asserted bytest/test_nilpy_bytes_join_and_bytes_n.npy).