Bring library to parity with the JS reference v4.0.0 - #3
Open
lukemerrett wants to merge 2 commits into
Open
Conversation
Port of rocicorp/fractional-indexing v4.0.0 (this library previously matched ~v3.2.0): - Breaking: the head alphabet (int_digits) now defaults to digits itself, so custom alphabets produce self-contained keys (base-10 yields '50', not 'a0'). Keys from the default Base62 alphabet are unchanged. Pass the new BASE_52_DIGITS export as int_digits to keep the previous A-Z/a-z heads. - New int_digits argument on generate_key_between(), generate_n_keys_between() and validate_order_key() to customise the head alphabet (JS v3.3.0 feature). - Bounds may now be passed in either order and are swapped, instead of raising FIError (JS PR rocicorp/fractional-indexing#26). - Alphabets are validated: >= 2 chars, strictly ascending char codes, single-byte; int_digits must be even-length. Previously invalid alphabets were silently accepted, and digits outside the alphabet leaked ValueError from str.index (the `except IndexError` guards were dead code). Everything now raises FIError. - Unlike the JS reference, a defaulted int_digits is validated too: an odd-length digits alphabet with no explicit int_digits raises FIError instead of silently producing broken keys. - Perf: per-alphabet digit-index dicts (cached) replace O(n) str.index scans; the midpoint uses integer arithmetic, dropping the decimal module (JS v3.4.0 spirit). - Tests: full port of the JS v4.0.0 test suite, including ordering property tests; verified byte-for-byte identical to the JS reference across 2,500 generated keys in 5 alphabet configurations. - CI: test matrix across Python 3.8-3.13. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lukemerrett
added a commit
to Doist/fractional-indexing-python
that referenced
this pull request
Jul 30, 2026
Doist-fork-only changes on top of the js-v4-parity branch (which is offered upstream as httpie#3): - Replace poetry/setup.py with a hatchling pyproject; the distribution is renamed to doist-fractional-indexing (import name unchanged: fractional_indexing) so it can never shadow the public fractional-indexing name on the private index. - Release workflow (workflow_dispatch) publishing to Doist's AWS CodeArtifact via uv publish, modeled on Doist/billist, plus the companion AWS_CODEARTIFACT_TOKEN refresh workflow (CI role ARN needs Platform provisioning - see TODO). - README note explaining the fork and its relationship to upstream. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A negative n slipped past the n == 0 / n == 1 checks: with one bound None the range() loop simply didn't run and the function silently returned a single key; with both bounds set, mid = floor(n / 2) went negative and the function recursed until RecursionError. Both the silent wrong result and the 1000-frame traceback are worse than enforcing the documented 'n >= 0' precondition with a clear FIError. The JS reference (rocicorp/fractional-indexing) has the same behaviour and only documents the precondition; this is a deliberate deviation, like validating a defaulted int_digits. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Updates this library from its current state (equivalent to rocicorp/fractional-indexing ~v3.2.0) to parity with the JS reference v4.0.0, released June 2026.
digitsitself, so custom alphabets produce self-contained keys —generate_key_between(None, None, digits='0123456789')returns'50', not'a0'. Keys generated with the default Base62 alphabet are unchanged. The newBASE_52_DIGITSexport can be passed asint_digitsto restore the previousA-Z/a-zheads.int_digitsargument (JS v3.3.0) ongenerate_key_between(),generate_n_keys_between(), andvalidate_order_key()to customise the head alphabet; the hard-codeda-z/A-Zhead logic is generalised accordingly.int_digitsmust be even-length. Previously invalid alphabets were silently accepted and produced non-sorting keys; also, digits outside the alphabet leakedValueErrorfromstr.indexbecause theexcept IndexErrorguards were dead code. Everything now raisesFIError.int_digitsis validated too, so an odd-lengthdigitswith no explicitint_digitsraisesFIError(the JS version silently generates broken keys in that case);generate_n_keys_between()rejects a negativenwithFIError— previously (in both this library and the JS reference) a negativensilently returned a single key when one bound wasNone, and recursed untilRecursionErrorwhen both bounds were set.O(alphabet)str.indexscans, and the midpoint uses integer arithmetic, dropping thedecimalimport (in the spirit of JS v3.4.0).Testing
tests.pyis now a full port of the JS v4.0.0 test suite (default, base-10 self-headed, base-95, base-2, Latin-1, symbol alphabets,int_digitsoverrides, validation errors) plus 1000-insert ordering property tests and negative-ncases — 87 tests.Suggested release: 0.2.0 (version already bumped in
fractional_indexing.py).🤖 Generated with Claude Code