Skip to content

πŸ› ScheduleEntry is not exported from the package root β€” every documented example is unimportableΒ #534

Description

@sodre

Release blocker for v0.14.0.

Description

ScheduleEntry is defined in src/zae_limiter/schedule.py and listed in that module's own __all__, but it is not re-exported from src/zae_limiter/__init__.py. Neither the #222 core plan (14 tasks, all merged) nor the surface plan (12 tasks) adds it anywhere.

Every documented usage of the scheduled-limits feature begins with:

from zae_limiter import Limit, ScheduleEntry

which raises ImportError. The public surface the feature is documented against does not exist.

This is already failing CI and has been for some time. tests/doctest/test_docs_run.py executes every Python block under docs/. All six blocks in docs/guide/scheduled-limits.md failed β€” the first on the import itself, the rest on the resulting undefined name. That guide lives on PR #483, which is deliberately held until v0.14.0 ships, so the red was on a held branch and nobody chased it.

Found while fixing #524. The immediate red was worked around there by tagging the blocks .lint-only per the file's existing convention (7 passed / 14 skipped), which stops the guide failing CI but does not fix the export β€” a user copying those examples still gets ImportError.

Steps to Reproduce

  1. uv run python -c "from zae_limiter import ScheduleEntry" β†’ ImportError: cannot import name 'ScheduleEntry' from 'zae_limiter'
  2. Check out PR πŸ“ docs(guide): add the scheduled (cron) limits user guideΒ #483 (docs/222-scheduled-limits-guide), revert the .lint-only tags in docs/guide/scheduled-limits.md
  3. uv run pytest tests/doctest/test_docs_run.py -k scheduled-limits β†’ every block in the guide fails

What to decide (not purely mechanical)

Which names belong at the package root. ScheduleEntry is required by every example and is the obvious yes. Also consider parse_cron, matches, effective_params, next_boundary, encode, decode, to_cron (and ParsedCron, also in schedule.__all__) β€” these are currently reachable only as zae_limiter.schedule.*. Most are internals that should stay that way, but the boundary should be chosen deliberately rather than by whichever name a doc example happened to need.

Constraint: schedule.py imports nothing from models.py, and that one-way dependency is load-bearing β€” it is what lets schedule.py be vendored into both Lambda packages. Re-exporting from __init__.py does not disturb it, but do not "tidy" the direction while in there.

Acceptance Criteria

  • from zae_limiter import ScheduleEntry succeeds; ScheduleEntry appears in src/zae_limiter/__init__.py's __all__
  • Any additional zae_limiter.schedule names the decision admits are likewise importable from the package root and listed in __init__.py's __all__
  • The .lint-only tags added by πŸ“ Calendar-reset guide predates ADR-137/138 and its main example is now invalidΒ #524's fix are removed from docs/guide/scheduled-limits.md (rg -c 'lint-only' docs/guide/scheduled-limits.md returns 0)
  • uv run pytest tests/doctest/test_docs_run.py -k scheduled-limits executes those blocks for real and passes (no skips for that file)
  • The public-surface decision β€” which schedule.py names are exported and which stay module-private β€” is recorded in CLAUDE.md (a Public API section; none exists today, so add one), naming the exported set explicitly

Dependencies

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/limiterCore rate limiting logicdocumentationImprovements or additions to documentation

    Type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions