Skip to content

fix: lazy Redis client; deprecate LightApi.from_dict - #42

Closed
iklobato wants to merge 3 commits into
refactor/single-responsibilityfrom
refactor/no-global-state
Closed

iklobato wants to merge 3 commits into
refactor/single-responsibilityfrom
refactor/no-global-state

Conversation

@iklobato

@iklobato iklobato commented Sep 18, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #41 (then #40, #39, #38). Merge in order. This is the last one of the series.

What

Two small commits.

  1. The Redis client is built on first use. cache.py created its process-wide RedisCacheBackend as a module global. The constructor reads LIGHTAPI_REDIS_URL, so the URL was fixed to whatever the environment held before import lightapi; setting it later, in code or in a test, was ignored. _default_backend() now builds the client on the first cache call and keeps it. get_cached, set_cached, invalidate_cache_prefix and _ping_redis keep their names and behaviour.
  2. LightApi.from_dict is deprecated, to be removed in 0.2.0. It still works and now emits a DeprecationWarning that names the two replacements. It is a second way to build endpoint classes from data, with its own 137-line builder and its own class-naming rule, and it had no test. The docs page and the changelog say the same.

What I decided not to do

This phase was planned as "remove global state". After checking each step against what the README and docs promise, most of it would break users for little gain:

  • An injected ResponseCache instead of the module functions. invalidate_cache_prefix is documented for users, so a module-level client has to exist anyway. The tests patch these functions in 15 places, and injecting a fake would need an 11th constructor argument on LightApi. The lazy client fixes the one real defect.
  • Removing the config object that JWTAuthentication reads. lightapi.config.config lets an app set the JWT secret, algorithm and lifetime in code. Removing it breaks that path, and nothing is wrong with how it behaves today: the environment is read on each call.
  • Removing cls._session_manager from the endpoint class. _get_engine() and _get_async_engine() read it, and the pagination reference shows users calling _get_async_engine(). Moving it to the instance would break any use of an endpoint outside a request.
  • Deprecation warnings for RedisCache, BaseCache, AuthenticationMiddleware, Response and the lightapi.auth module. The docs teach all of them: RedisCache 18 times, Response in 17 places plus the README and 3 examples, from lightapi.auth import 16 times plus 4 in the README. Warning on what the tutorial tells people to type needs a decision about what the docs should recommend instead.

Checks

All run on my machine. The test workflow does not run for PRs into dev.

The whole series, measured on dev and here

dev this branch
Lines in lightapi/ 7,516 4,588
Python files 45 35
Tests passing 327 379
Coverage 59% 88%
Largest function register, 270 lines load_from_dict, 137 lines (deprecated here)

Not checked anywhere in the series: Postgres. Everything ran on SQLite and aiosqlite. #43 compares every response of this branch with the published 0.1.29; it found one regression, in #40, which is fixed there.

iklo added 3 commits September 18, 2026 11:33
cache.py created its process-wide RedisCacheBackend as a module global.
The constructor reads LIGHTAPI_REDIS_URL, so the URL was fixed to
whatever the environment held before `import lightapi`. Setting it later
in code, or in a test, had no effect.

_default_backend() builds the client on the first cache call and keeps
it. get_cached, set_cached, invalidate_cache_prefix and _ping_redis keep
their names and behaviour; invalidate_cache_prefix is documented for
users and the tests patch the others.

tests/test_cache_backend_lazy.py mocks redis.from_url and checks the late
URL is used and that three cache calls share one client. Both tests fail
with the eager global.

Tests: 352 passed, 1 xfailed, 2 xpassed.
from_dict is a second way to build endpoint classes from data, next to
the YAML loader. It has its own 137-line builder, its own rule for class
names (/api/v1/items becomes ApiV1ItemsEndpoint here and ItemsEndpoint in
YAML) and, until now, no test at all. One page of the docs mentions it;
no example uses it.

It keeps working and now emits a DeprecationWarning that names the two
replacements. The docs page and the changelog say the same.

tests/test_from_dict_deprecation.py checks the warning and that the app
it builds still serves a POST.

Tests: 354 passed, 1 xfailed, 2 xpassed.
@iklobato

Copy link
Copy Markdown
Owner Author

Consolidated into #44 for a single review pass (one PR covering all five refactors plus the validation work, instead of six stacked ones). Closing this one, no commits were rewritten, they're all still in #44's history.

@iklobato iklobato closed this Sep 18, 2026
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