Skip to content

Add python 3.15, small fixes, update vendored packages - #2

Merged
eendebakpt merged 13 commits into
mainfrom
py315-fixes-revendor
Oct 20, 2026
Merged

eendebakpt merged 13 commits into
mainfrom
py315-fixes-revendor

Conversation

@eendebakpt

Copy link
Copy Markdown
Owner

No description provided.

eendebakpt and others added 13 commits May 21, 2026 09:36
Bump the floor to Python 3.13. CI matrix, wheel build matrix, ruff
target-version, classifiers, and the requires-python pin all drop the
3.11 / 3.12 cells. Also strip the now-dead `PY_VERSION_HEX < 0x030C0000`
branch in src/slots.cc (the function had a fallback for borrowing
tp_dict; everything we support has PyType_GetDict).

The PEP 562 module-level `__getattr__` experiment from this branch is
reverted — eager definitions remain in floatium/__init__.py, and the
autopatch hook goes through floatium.install (not floatium._ext.install
directly). `from __future__ import annotations` is dropped from
__init__.py and _autopatch.py since 3.13+ supports PEP 604 unions
natively at runtime; that saves an `import __future__` (~90 µs) on the
autopatch hot path without needing PEP 562.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Python 3.15.0 final shipped 2026-10-09. cibuildwheel 4.3.1 carries
cp315 / cp315t in its default catalog and needs no `enable` token for
free-threaded 3.14+, so bump to it and add cp315-* cp315t-* to the build
list. cibuildwheel 4.0 dropped the experimental cp313t builds together
with the cpython-freethreading enable, so wheels.yml keeps one pinned
cibuildwheel 3.4.1 step that builds only cp313t-*.

ci.yml / cpython-suite.yml: 3.15 and 3.15t resolve to final releases on
all three OSes now, so drop allow-prereleases and the 3.15t × windows
exclusion. New test-all-backends job compiles every backend
(FLOATIUM_*_BACKEND=all) so the non-default ones are exercised in CI.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- format(x, "r") returned repr(); stock float.__format__ raises
  ValueError. Drop the 'r' branch from parse_simple_spec so the spec
  falls through to the original and errors exactly like stock. The
  parity test used to skip "r" instead of checking it; it now asserts
  the same exception type and message.
- 3.15+ debug builds: install()/uninstall() left PyFloat_Type with
  tp_version_tag == 0 until the next lookup, and init_static_type()
  asserts a non-zero tag on static builtin types whenever a
  subinterpreter is created. Re-assign a tag immediately via
  PyUnstable_Type_AssignVersionTag() after invalidating.
- floatium.enabled(False) re-installed the build-default backends on
  exit instead of the ones that were active.
- The parse hook now only forwards strings made of [0-9.eE+-] to the
  backend, so parity no longer depends on how permissive a backend's
  strtod is: libc strtod (the "stock" backend) accepted hex floats and
  nan(...), and honoured LC_NUMERIC (float("1,5") == 1.5 under de_DE).
- Wuffs: integer-part digits beyond the 800-digit HPD cap did not
  advance the decimal point, so "1" + 800 zeros + "e-800" parsed as 0.1.
  Local one-line fix in the vendored header, re-applied by
  tools/sync_wuffs.sh and documented in README.vendor; also present in
  upstream Wuffs main.
- New tests/test_backends.py checks every compiled-in format and parse
  backend against stock in-process (random bit patterns, subnormals,
  long mantissas, 30+ specs).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
fmt 12.2.0 (1be298e): Dragonbox full cache is now on by default
(FMT_USE_FULL_CACHE_DRAGONBOX=1 unless __OPTIMIZE_SIZE__), which is the
shortest-repr path floatium uses for repr()/str(). No changes to the
format_float / Dragon4 code reached by modes 2 and 3.

fast_float v8.3.2 (42f2548): unrolled integer-digit scan, 4-digit SWAR
on GCC as well as clang, skipped rounding-mode probe for long
mantissas, compile-time format folding — all on the from_chars<double>
hot path — plus compiler compatibility fixes. No correctness changes
to the double path.

Synced with tools/sync_fmt.sh and tools/sync_fast_float.sh; vendored
files remain byte-identical to upstream. Ryu is already at upstream
HEAD; the Wuffs float-parsing code is unchanged upstream since 2023.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… script

Re-measured on the same release CPython 3.14.3 after the fmt 12.2.0 /
fast_float v8.3.2 bump (bench_ns_per_op for the README table,
bench_backend_matrix for BENCHMARKS.md). repr() is ~7% faster across
corpora from fmt's Dragonbox full cache; f"{x:.4f}" and float(s) are
unchanged within noise (an A/B against the v8.2.5 headers on this
machine gave identical parse timings).

bench/bench_backend_matrix.py is the script that produces the
BENCHMARKS.md tables, so they are reproducible with
`FLOATIUM_AUTOPATCH=0 python -m bench.bench_backend_matrix` on an
all-backends build.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…st_float v8.3.2

Drop cp313t wheels: cibuildwheel 4.x no longer builds 3.13 free-threaded
CPython, and carrying a pinned 3.x step for one identifier is not worth
it. 3.13 (GIL) wheels, 3.14t and 3.15t stay; 3.13t users build from the
sdist.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ndidate

Vendors the C port of Żmij (vitaut/zmij a9d6363, MIT or BSL-1.0; zmij.c
+ zmij-c.h only) and adds a "zmij" format backend that serves every
_Py_dg_dtoa mode from it: shortest via Żmij's verified algorithm,
%e/%f precision via its correctly rounded scientific/fixed writers
(bigint-backed above 18 digits). Unlike fmt_opt and ryu_opt this is one
unmodified library plus one adapter, with no C++ anywhere — the shape a
CPython proposal needs.

The adapter (src/cpython_adapter/zmij_dtoa.c) is plain C11 against the
exact _Py_dg_dtoa contract, using nothing from CPython but PyMem_*: it
re-reads Żmij's printf-style text into digits + decpt and implements
mode 3 with negative ndigits (round(x, -k)) with ties-to-even digit
arithmetic on the exact integer part. Verified with 182k direct calls
through ctypes against repr/format/round, plus tests/test_backends.py
across 2800 values x 32 specs; suite passes on 3.14 and 3.15t debug.

Also: CMAKE_C_STANDARD 11 (zmij.c uses static_assert), the all-backends
CI job now runs on ubuntu/macos/windows so zmij.c is built with MSVC
and Apple clang too, INTERNALS.md documents the zmij + wuffs pair as
the CPython candidate, and README.vendor lists the three upstream
requests for Żmij (asm -> __asm__ for -std=c11, a to-decimal C entry
point, extern "C" guards).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…loatium backend)

Oracles are int/int true division (correctly rounded by contract),
Decimal quantize/round and brute-force shortest search, so they share no
code with dtoa.c or the vendored libraries. ~1M checks per 40k-value
run; stock dtoa.c and every floatium backend pass with zero mismatches,
so this is the parity evidence for the CPython proposal rather than a
bug finder.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rse candidate

Vendors kolemannix/ffc.h v26.09.01+1 (7e85c6d; Apache-2.0 / MIT /
BSL-1.0 like fast_float) and adds an "ffc" parse backend: the same
Clinger + Eisel-Lemire + bigint digit-comparison pipeline as the
fast_float backend, but a single C99 header with no C++. Used by valkey
and hiredis. Compiles under CPython's -std=c11 without warnings.

The adapter (src/cpython_adapter/ffc_strtod.c) is plain C with the
_Py_dg_strtod contract: no whitespace skipping, inf/nan left to
_Py_parse_inf_or_nan, endptr and errno = ERANGE semantics.

Chosen over Wuffs for the CPython proposal: Wuffs' parser is an extract
of a transpiler-generated multi-codec file, rejects numbers with leading
zeros (10,839 of 268k hard-case strings, 12 of 5.3M corpus lines, all of
which float() must accept), and its HPD fallback is ~12x slower on
17-digit inputs. ffc.h matched fast_float and glibc bit for bit on all
5.3M published corpus lines and 268k exact-midpoint / long-mantissa
cases, tracks fast_float within noise in BENCHMARKS.md, and passes
tools/fuzz_float_oracle.py (1M checks) installed as zmij + ffc.

INTERNALS.md now names zmij + ffc as the CPython candidate pair and
explains the choice; BENCHMARKS.md gains the ffc column.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…andidate)

The shipped wheel is now the exact pure-C configuration proposed for
CPython: Żmij for every dtoa mode and ffc.h (C99 fast_float) for
parsing, so every user exercises the candidate code. fmt_opt /
fast_float / ryu_opt / wuffs stay available at build time and, in an
"all" build, at install time.

README benchmark table re-measured with the new defaults on the same
release CPython 3.14.3 (repr 1.5–5.4x, fixed 1.1–4.3x, parse 1–4.1x).
fmt_opt remains faster on f"{x:.4f}" for huge magnitudes (Ryu d2fixed);
the default favours what CPython can take over the last nanoseconds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
PowerShell does not treat a trailing backslash as a line continuation,
so the multi-line pip install ran '-C ...' as a separate command and the
windows-latest all-backends job failed at install time.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ith MSVC

The extract included <intrin.h> inside multiply_u64 and
count_leading_zeroes_u64 for the MSVC branches; MSVC rejects a system
header at block scope (C2598 and a cascade of errors in xmmintrin.h),
which broke the new windows-latest all-backends CI job. Documented as
local modification 8 and re-applied by tools/sync_wuffs.sh.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@eendebakpt
eendebakpt merged commit 7a93aa7 into main Oct 20, 2026
18 checks passed
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