Skip to content

fix: type CachedResponse.__init__ for mypy --strict (#628)#643

Open
jbbqqf wants to merge 2 commits into
pallets-eco:mainfrom
jbbqqf:fix/628-cachedresponse-init-type
Open

fix: type CachedResponse.__init__ for mypy --strict (#628)#643
jbbqqf wants to merge 2 commits into
pallets-eco:mainfrom
jbbqqf:fix/628-cachedresponse-init-type

Conversation

@jbbqqf

@jbbqqf jbbqqf commented May 22, 2026

Copy link
Copy Markdown
Contributor

Summary

CachedResponse.__init__ is currently un-annotated, so mypy --strict
codebases that import and call CachedResponse(...) get a
[no-untyped-call] error even though the class is part of the public
API. Annotate the constructor (response: Response,
timeout: int | None, -> None) and document the in-place __dict__
swap so the intent is clear to reviewers.

Fixes #628.

Context

CachedResponse is exported from flask_caching and is intended to be
constructed by user view code (e.g. return CachedResponse(make_response(...), timeout=2),
as in tests/test_view.py::test_cache_timeout_dynamic). Without
annotations, mypy refuses to consider it a typed callable. The class
already inherits typing from flask.Response, and timeout is already
declared as a class attribute, so this change just exposes those types
on the constructor signature.

Changes

  • src/flask_caching/__init__.py: add parameter and return annotations
    on CachedResponse.__init__, type the timeout class attribute as
    int | None, and leave a short comment explaining the in-place
    __dict__ adoption.
  • tests/test_init.py: new test_cached_response_init_is_typed
    regression test that introspects inspect.signature to assert the
    constructor declares -> None and all non-self parameters carry an
    annotation; plus test_cached_response_construction_with_flask_response
    which exercises construction end-to-end.
  • CHANGES.rst: add Unreleased entry.

Reproduce BEFORE/AFTER yourself (copy-paste)

# --- one-time setup ---
git clone https://github.com/pallets-eco/flask-caching.git /tmp/fc-628 && cd /tmp/fc-628
python3.11 -m venv .venv && . .venv/bin/activate
pip install -e . pytest >/dev/null

# --- BEFORE: origin/main, expect FAIL ---
git checkout origin/main
git fetch https://github.com/jbbqqf/flask-caching.git fix/628-cachedresponse-init-type
git checkout FETCH_HEAD -- tests/test_init.py
pytest tests/test_init.py::test_cached_response_init_is_typed -v
# Expected: 1 failed - AssertionError "return annotation is <class 'inspect._empty'>; expected None"

# --- AFTER: this branch, expect PASS ---
git checkout FETCH_HEAD
pytest tests/test_init.py::test_cached_response_init_is_typed -v
# Expected: 1 passed

What I ran locally

$ pytest tests/test_init.py -v
...
tests/test_init.py::test_cached_response_init_is_typed PASSED
tests/test_init.py::test_cached_response_construction_with_flask_response PASSED
======================== 8 passed in 0.05s ========================

$ pytest tests/test_init.py tests/test_view.py tests/test_cache.py
======================== 47 passed, 1 skipped in 42.38s ========================

The skipped test is the redis-server-dependent test_cache_unlink.

Edge cases

# Scenario Input Expected Verified by
1 Standard construction CachedResponse(make_response("hi"), 10) .timeout == 10, .get_data(as_text=True) == "hi" test_cached_response_construction_with_flask_response
2 timeout=None passed CachedResponse(resp, None) accepted by int | None annotation annotation allows None
3 inspect.signature introspection inspect.signature(CachedResponse.__init__) return annotation is None, params have annotations test_cached_response_init_is_typed
4 Use in @cached flow view returns CachedResponse TTL still respected end-to-end existing test_cache_timeout_dynamic (unchanged)

Risk / blast radius

Annotation-only change to the public class constructor signature. No
runtime behavior changes: __init__ still copies __dict__ from the
wrapped Response and assigns timeout. The class attribute type
narrows from implicit None to int | None, which matches existing
usage at tests/test_view.py::test_cache_timeout_dynamic and the
isinstance(rv, CachedResponse); rv.timeout access path at
src/flask_caching/__init__.py:432.


PR drafted with assistance from Claude Code (Anthropic). The change
was reviewed manually against pallets-eco/flask-caching's source. The
reproducer block above is the one I used during development; reviewers
can paste it verbatim.

Jean-Baptiste Braun and others added 2 commits May 22, 2026 12:19
…s-eco#628)

Without ``-> None`` and parameter annotations, calling
``CachedResponse(response, timeout)`` in a mypy --strict codebase
raises ``[no-untyped-call]``. Annotate the constructor and document
the in-place ``__dict__`` swap that adopts an existing Response.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

CachedResponse __init__ has no return value, mypy complains

1 participant