Skip to content

feat: make orjson an optional dependency (cachekit[json])#196

Merged
27Bslash6 merged 2 commits into
mainfrom
feat/orjson-optional
Jun 20, 2026
Merged

feat: make orjson an optional dependency (cachekit[json])#196
27Bslash6 merged 2 commits into
mainfrom
feat/orjson-optional

Conversation

@27Bslash6

@27Bslash6 27Bslash6 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Why

orjson backed only the non-default OrjsonSerializer, yet was a hard, eagerly-imported dependency — so import cachekit pulled orjson even when only the default (MessagePack) serializer was ever used. Its structural twin, ArrowSerializer, is already optional + lazy (cachekit[data]). This brings orjson into line.

What changed

orjson now mirrors Arrow exactly:

  • Dependency (pyproject.toml): moved orjson out of core [project.dependencies] into a new json optional extra (cachekit[json]). Kept in the dev dependency-group so tests / doctests / markdown-docs still resolve it in CI.
  • Lazy load (serializers/__init__.py): SERIALIZER_REGISTRY["orjson"] is now None; get_serializer("orjson") and from cachekit.serializers import OrjsonSerializer resolve it on demand via _get_orjson_serializer() + module __getattr__. Removed the eager top-level import.
  • Fail-fast (orjson_serializer.py): guards import orjson with a helpful ImportError: ... requires the [json] extra: pip install 'cachekit[json]' (mirrors arrow_serializer.py's pyarrow guard).
  • get_serializer_info(): generalized its optional-dependency ImportError branch (was hardcoded to ArrowSerializer, would have mislabeled a missing orjson).
  • Docs: noted the [json] extra wherever orjson is presented as a serializer choice, mirroring the existing [data] treatment.

Behavior

  • import cachekit no longer imports orjson (the default/std/auto path never needed it) — verified by a subprocess regression guard.
  • With orjson installed, everything works unchanged.
  • Without orjson, cachekit + the default serializer still work; only get_serializer("orjson") raises the actionable install error.

Tests

tests/unit/test_serializer_lazy_loading.py: added orjson mechanism tests mirroring arrow (registry-None, _get_orjson_serializer, get_serializer, __getattr__, get_serializer_info) plus two subprocess guards — (1) import cachekit doesn't pull orjson, (2) absent orjson yields the helpful [json]-extra error while the default serializer keeps working. Updated test_orjson_serializer.py's registry assertion to the new lazy (None) contract.

Verification: full unit suite 1607 passed, 8 skipped; src doctests pass; markdown-docs pass (orjson present); ruff + basedpyright clean. Branched off current main (f35d0cd, 0.10.1).

⚠️ Breaking

orjson is no longer installed by pip install cachekit. To use the orjson serializer (serializer="orjson" or OrjsonSerializer), install cachekit[json]. (Greenfield, pre-1.0 — flagged for the changelog so users know to add the extra.)

Summary by CodeRabbit

  • Documentation
    • Updated the README and documentation to clarify that OrjsonSerializer requires the optional cachekit[json] extra to be installed.
  • Chores / Refactor
    • OrjsonSerializer now resolves lazily, avoiding eager optional-dependency imports.
    • Improved the error message when OrjsonSerializer is requested without orjson, including an actionable [json] install hint.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@27Bslash6, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 26 minutes and 10 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 96c23098-11db-4f2f-be8c-56d3eed73b48

📥 Commits

Reviewing files that changed from the base of the PR and between b996e4b and dee8428.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • DEVELOPMENT.md
  • README.md
  • docs/api-reference.md
  • docs/getting-started.md
  • docs/serializers/README.md
  • docs/serializers/orjson.md
  • pyproject.toml
  • src/cachekit/serializers/__init__.py
  • src/cachekit/serializers/orjson_serializer.py
  • tests/unit/test_orjson_serializer.py
  • tests/unit/test_serializer_lazy_loading.py

Walkthrough

orjson is moved from a required dependency to an optional cachekit[json] extra in pyproject.toml. OrjsonSerializer gains an import guard that raises a descriptive ImportError. The serializer registry lazy-loads OrjsonSerializer on demand (matching the existing ArrowSerializer pattern). Tests and all documentation are updated accordingly.

Changes

Make orjson an optional dependency

Layer / File(s) Summary
Package config and import guard
pyproject.toml, src/cachekit/serializers/orjson_serializer.py
orjson>=3.9.0 is removed from base dependencies and placed under a new [project.optional-dependencies].json extra; it is also kept in [dependency-groups].dev for CI coverage. orjson_serializer.py wraps the orjson import in a try/except ImportError that raises a message pointing to pip install 'cachekit[json]'.
Lazy-loading in serializers/__init__.py
src/cachekit/serializers/__init__.py
OrjsonSerializer is scoped to TYPE_CHECKING. A _get_orjson_serializer() factory with a module-level cache is introduced. SERIALIZER_REGISTRY["orjson"] is set to None as a lazy placeholder. get_serializer(), get_serializer_info(), and __getattr__ are all extended to resolve OrjsonSerializer lazily, matching the existing ArrowSerializer pattern.
Test updates
tests/unit/test_orjson_serializer.py, tests/unit/test_serializer_lazy_loading.py
test_orjson_serializer_in_registry is updated to assert the registry entry is None. TestLazyOrjsonSerializerLoading covers placeholder registration, factory caching, get_serializer() instantiation, __getattr__ access, get_serializer_info() reporting, and monkeypatched ImportError paths. TestOrjsonIsOptional uses subprocess checks to assert no eager import occurs and that get_serializer("orjson") raises ImportError with the [json] extra hint when orjson is absent.
Documentation
README.md, DEVELOPMENT.md, docs/api-reference.md, docs/getting-started.md, docs/serializers/README.md, docs/serializers/orjson.md
All documentation surfaces add explicit notes that OrjsonSerializer requires the cachekit[json] extra, including pip/uv install commands and the expected ImportError message.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely describes the main change: making orjson an optional dependency under the cachekit[json] extra, which is the central focus of this PR.
Description check ✅ Passed The PR description is comprehensive and addresses most template sections including motivation, changes, behaviour, tests, and breaking changes. While the formal checklist from the template is not used, the substantive required information is thoroughly documented.
Docstring Coverage ✅ Passed Docstring coverage is 94.44% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/orjson-optional

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 19, 2026
@27Bslash6 27Bslash6 force-pushed the feat/orjson-optional branch from c9485ce to b996e4b Compare June 19, 2026 23:39
orjson backed only the non-default OrjsonSerializer, yet was a hard, eagerly-
imported dependency — unlike its structural twin ArrowSerializer, which is the
optional + lazy `cachekit[data]`. So `import cachekit` pulled orjson even when
only the default (MessagePack) serializer was ever used.

orjson now mirrors Arrow exactly:
- Moved from core dependencies to a new `json` optional extra (cachekit[json]);
  kept in the dev dependency-group so tests/doctests/markdown-docs still resolve it.
- Lazy-loaded: SERIALIZER_REGISTRY["orjson"] is None; get_serializer("orjson")
  and `from cachekit.serializers import OrjsonSerializer` resolve it on demand via
  _get_orjson_serializer() + module __getattr__.
- orjson_serializer.py fails fast with a helpful install hint when orjson is
  absent (mirrors arrow_serializer.py's pyarrow guard).
- get_serializer_info()'s optional-dep ImportError branch generalized (was
  hardcoded to ArrowSerializer).

`import cachekit` no longer imports orjson — verified by a subprocess regression
guard; the default/std/auto path never needed it. Docs updated to note the
[json] extra (mirroring the [data] treatment).

BREAKING CHANGE: orjson is no longer installed by `pip install cachekit`. To use
the orjson serializer (serializer="orjson" or OrjsonSerializer), install
`cachekit[json]`. Without it, get_serializer("orjson") raises an ImportError with
an actionable install hint.
Patch coverage flagged the two optional-dep-absent paths the in-process suite
cannot reach while orjson is installed:

- get_serializer_info()'s generalized ImportError branch — now covered by a
  monkeypatch test asserting a missing orjson is labeled OrjsonSerializer (the
  mislabeling the generalization fixed), not the old hardcoded ArrowSerializer.
- orjson_serializer.py's import guard — marked `# pragma: no cover`: it is only
  reachable without the [json] extra, and its behavior is already verified by the
  subprocess regression test in test_serializer_lazy_loading.py (matches how
  arrow_serializer.py's pyarrow guard is left uncovered).
@27Bslash6 27Bslash6 force-pushed the feat/orjson-optional branch from b996e4b to dee8428 Compare June 19, 2026 23:42
@27Bslash6 27Bslash6 merged commit 4512a2a into main Jun 20, 2026
32 checks passed
@27Bslash6 27Bslash6 deleted the feat/orjson-optional branch June 20, 2026 00:02
27Bslash6 added a commit that referenced this pull request Jun 20, 2026
…major) (#199)

cachekit is still alpha (Development Status :: 3 - Alpha, version 0.x). Without
bump-minor-pre-major, release-please escalates any BREAKING CHANGE straight to
1.0.0 — which is why the orjson optional-dependency change (#196) produced a
"release 1.0.0" PR (#198).

Set bump-minor-pre-major so that while the version is below 1.0.0, breaking
changes bump the minor (0.10.1 -> 0.11.0) instead of the major. Once this lands
on main, release-please recomputes the pending release PR to 0.11.0.
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