Skip to content

Reject negative n in generate_n_keys_between - #2

Merged
lukemerrett merged 2 commits into
mainfrom
fix/negative-n-guard
Jul 31, 2026
Merged

Reject negative n in generate_n_keys_between#2
lukemerrett merged 2 commits into
mainfrom
fix/negative-n-guard

Conversation

@lukemerrett

Copy link
Copy Markdown
Collaborator

What

generate_n_keys_between() documented n >= 0 as a precondition but never enforced it, and a negative n slipped past the n == 0 / n == 1 checks with two bad outcomes:

  • One bound None: the for _ in range(n - 1) loop didn't run, so the call silently returned a single key — looks like success.
  • Both bounds set: mid = floor(n / 2) went negative and the function recursed unboundedly until RecursionError.

Now n < 0 raises FIError('n must be >= 0: <n>') at the top of the function.

This is a faithful-port bug: the JS reference behaves identically and only documents the precondition. Guarding is a deliberate deviation, consistent with the one we already made for a defaulted odd-length int_digits — a clear error beats silent garbage or a 1000-frame traceback. The same change has been added to our upstream PR (httpie/fractional-indexing-python#3).

Testing

  • New test_negative_n_rejected covering all four bound combinations (None/None, one-sided ×2, both set) — 87 tests total, all passing.

Release

After merge, run Actions → Release with bump: patch to publish 1.0.1 (changelog entry included).

🤖 Generated with Claude Code

lukemerrett and others added 2 commits July 31, 2026 07:51
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>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lukemerrett lukemerrett self-assigned this Jul 31, 2026

@doistbot doistbot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds a clean guard to generate_n_keys_between() that raises FIError when n < 0, preventing both a silent single-key return (one bound None) and unbounded recursion (both bounds set). The error message is descriptive, the new parametrized test covers all four bound combinations, and the guard follows the existing inline-FIError pattern used throughout the module. No inline issues were flagged.

I also included a few optional follow-up notes in the details below.

Optional follow-up notes (2)
  • P3 tests.py:259: This repeats check_n_keys()’s existing FIError assertion branch. Replace the with pytest.raises block with check_n_keys(FIError('n must be >= 0: -1'), a, b, -1) so error checks for this API use the shared helper.
  • P3 tests.py:252: All four bound combinations hit the n < 0 guard before a/b are inspected, so they exercise the same branch. The two historically distinct failure modes are "one bound None" (silent single key) and "both bounds set" (unbounded recursion); one case of each already documents the intent and protects the regression. Consider trimming to two cases (e.g. (None, None) and ('a0', 'a5')) unless you want explicit per-combination coverage.

Share FeedbackReview Logs

@lukemerrett
lukemerrett merged commit 476a27c into main Jul 31, 2026
13 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.

2 participants