Skip to content

fix(typing): preserve decorated function type signatures with ParamSpec#376

Open
gencurrent wants to merge 4 commits intopython-cachier:masterfrom
gencurrent:fix/preserve-decorator-type-signature
Open

fix(typing): preserve decorated function type signatures with ParamSpec#376
gencurrent wants to merge 4 commits intopython-cachier:masterfrom
gencurrent:fix/preserve-decorator-type-signature

Conversation

@gencurrent
Copy link
Copy Markdown
Contributor

I encounter this issue with Pyrefly constantly: the declared return type of the decorated function / method does not match the expected return type, so I have to add # ... ignore ... comments.

The @cachier decorator erases all type information from decorated functions. Because cachier() has no return type annotation and no generic type variables, type checkers see every decorated function -- both its signature and return value -- as Any:

@cachier()
def my_func(x: int) -> str:
    return str(x)

reveal_type(my_func)      # Any
reveal_type(my_func(5))   # Any
This breaks IDE autocomplete, disables type error detection on wrong arguments, and forces downstream typed codebases to use stubs or type: ignore.

Changes
src/cachier/core.py: Add ParamSpec/TypeVar generics to cachier(), _cachier_decorator(), and func_wrapper so the original function signature is preserved through the decorator
tests/test_typing.py: 12 mypy-based tests covering sync, async, complex signatures, wrong-arg rejection, and decorator argument variants
tests/requirements.txt: Add mypy as a test dependency
After:


reveal_type(my_func)      # (x: int) -> str
reveal_type(my_func(5))   # str

@gencurrent gencurrent requested a review from shaypal5 as a code owner April 13, 2026 22:19
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 13, 2026

Codecov Report

❌ Patch coverage is 96.42857% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 99.90%. Comparing base (ece168c) to head (03dfc2a).

Files with missing lines Patch % Lines
src/cachier/core.py 96.42% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #376      +/-   ##
==========================================
- Coverage   99.95%   99.90%   -0.05%     
==========================================
  Files          16       16              
  Lines        2098     2110      +12     
  Branches      251      252       +1     
==========================================
+ Hits         2097     2108      +11     
- Partials        1        2       +1     
Flag Coverage Δ
local 66.54% <96.42%> (+0.14%) ⬆️
mongodb 40.00% <92.85%> (+0.29%) ⬆️
postgres 42.08% <92.85%> (+0.28%) ⬆️
redis 44.54% <92.85%> (+0.26%) ⬆️
s3 41.27% <92.85%> (+0.28%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/cachier/core.py 99.74% <96.42%> (-0.26%) ⬇️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ece168c...03dfc2a. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Member

@shaypal5 shaypal5 left a comment

Choose a reason for hiding this comment

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

Looks good. Let's wait and see what CoPilot says. :)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to improve Cachier’s static typing experience by preserving decorated function signatures/return types (especially for tools like mypy/Pyrefly) using ParamSpec + TypeVar, and adds a mypy-driven test suite to validate the behavior.

Changes:

  • Add ParamSpec/TypeVar generics to cachier() and its internal decorator/wrapper to preserve function signatures through decoration.
  • Add tests/test_typing.py which runs mypy programmatically to assert signature/return-type preservation for sync/async functions and some decorator argument variants.
  • Add mypy to tests/requirements.txt so the typing tests can run in CI.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
src/cachier/core.py Introduces generic typing for the decorator/wrapper to preserve callable signatures and return types.
tests/test_typing.py Adds mypy-based tests that validate the preserved typing behavior via reveal_type and error assertions.
tests/requirements.txt Adds mypy as a test dependency to enable running the typing tests.

Comment thread tests/test_typing.py
Comment thread tests/test_typing.py Outdated
Comment thread tests/test_typing.py
Comment thread tests/test_typing.py
Comment thread src/cachier/core.py Outdated
Comment thread src/cachier/core.py
@gencurrent
Copy link
Copy Markdown
Contributor Author

Now there's a test fail with
failed to connect to the docker API at npipe:////./pipe/docker_engine; check if the path is correct and if the daemon is running: open //./pipe/docker_engine: The system cannot find the file specified.

@shaypal5
Copy link
Copy Markdown
Member

Now there's a test fail with failed to connect to the docker API at npipe:////./pipe/docker_engine; check if the path is correct and if the daemon is running: open //./pipe/docker_engine: The system cannot find the file specified.

I don't know. I'm looking at it now, and the only red flow is codecov's. Please take a look at CoPilot's comments, treat anything valid, resolve as rejected anything that's not, raise patch test coverage to 100% (it's currently at 96.42%), and i'll merge this sucker.

@gencurrent
Copy link
Copy Markdown
Contributor Author

Now there's a test fail with failed to connect to the docker API at npipe:////./pipe/docker_engine; check if the path is correct and if the daemon is running: open //./pipe/docker_engine: The system cannot find the file specified.

I don't know. I'm looking at it now, and the only red flow is codecov's. Please take a look at CoPilot's comments, treat anything valid, resolve as rejected anything that's not, raise patch test coverage to 100% (it's currently at 96.42%), and i'll merge this sucker.

Perhaps it failed just once yesterday between some of my commits.
Thank you : ) I replied to all the comments and fixed the relevant ones and replied to each.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants