Skip to content

Add functools.lru_cache plugin support Fixes #16261#19432

Open
machin0r wants to merge 1 commit intopython:masterfrom
machin0r:implement-lru-cache-support
Open

Add functools.lru_cache plugin support Fixes #16261#19432
machin0r wants to merge 1 commit intopython:masterfrom
machin0r:implement-lru-cache-support

Conversation

@machin0r
Copy link

Add support for functools.lru_cache decorator, Fixes #16261

Description

Problem: Functions decorated with @lru_cache lose their original type signatures, preventing mypy from validating function calls properly.

Solution:

  • Added functools_lru_cache_callback to preserve function signatures for @lru_cache
  • Added lru_cache_wrapper_call_callback to validate calls to cached functions
  • Registered callbacks in DefaultPlugin for both decorator creation and wrapper calls
  • Added tests for different lru_cache patterns

Supported patterns:

  • @lru_cache - bare decorator
  • @lru_cache() - empty parentheses
  • @lru_cache(maxsize=128) - parameters

Example

Before:

from functools import lru_cache
@lru_cache()
def f(x: int) -> str:
    return str(x)

f()  # No error, mypy can't validate arguments
f("str")  # No error, mypy can't check types

After:
from functools import lru_cache
@lru_cache()
def f(x: int) -> str:
    return str(x)

f()  # error: Missing positional argument "x" in call to "f"  [call-arg]
f("str")  # error: Argument 1 to "f" has incompatible type "str"; expected "int"  [arg-type]

Files changed:

  • mypy/plugins/functools.py - Implementation
  • mypy/plugins/default.py - Plugin registration
  • test-data/unit/check-functools.test - Test cases
  • test-data/unit/lib-stub/functools.pyi - Type stub updates

@machin0r machin0r marked this pull request as draft July 13, 2025 17:14
@machin0r machin0r marked this pull request as ready for review July 13, 2025 17:47
@github-actions

This comment has been minimized.

@machin0r machin0r force-pushed the implement-lru-cache-support branch from 9aa3d9f to a4372e8 Compare July 13, 2025 18:35
@machin0r machin0r marked this pull request as draft July 13, 2025 18:46
@machin0r machin0r force-pushed the implement-lru-cache-support branch from a4372e8 to d9d5d00 Compare July 13, 2025 18:49
@github-actions

This comment has been minimized.

@machin0r machin0r marked this pull request as ready for review July 13, 2025 19:46
@cdce8p cdce8p added the topic-plugins The plugin API and ideas for new plugins label Jul 15, 2025
@machin0r
Copy link
Author

Hi! This PR has been open for a while now, I just wanted to check if there's anything I can clarify or improve to help with review.

Happy to make any changes needed!

@machin0r
Copy link
Author

Hey just following up on this again, is there anything I can do to help move this along?

@github-actions

This comment has been minimized.

@machin0r machin0r force-pushed the implement-lru-cache-support branch from 671ce92 to edfda9b Compare December 23, 2025 17:18
@github-actions

This comment has been minimized.

@machin0r machin0r force-pushed the implement-lru-cache-support branch from 2c022ea to 3e81c9d Compare December 23, 2025 23:19
- Add lru_cache callback to functools plugin for type validation
 - Register callbacks in default plugin for decorator and wrapper calls
 - Support different lru_cache patterns: @lru_cache, @lru_cache(), @lru_cache(maxsize=N)

Fixes issue python#16261
@machin0r machin0r force-pushed the implement-lru-cache-support branch from 98f0193 to d9466f0 Compare December 23, 2025 23:38
@github-actions

This comment has been minimized.

@machin0r
Copy link
Author

Hi @JukkaL @hauntsaninja ,

This PR has been open for a while, I'd love your review or guidance on what’s needed to get it merged.
Thanks!

@machin0r machin0r force-pushed the implement-lru-cache-support branch from d92d24a to d9466f0 Compare March 7, 2026 12:26
@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2026

Diff from mypy_primer, showing the effect of this PR on open source code:

psycopg (https://github.com/psycopg/psycopg)
+ psycopg/psycopg/rows.py:136: error: Argument 2 to "_make_nt" has incompatible type "*Generator[bytes | None, None, None]"; expected "bytes"  [arg-type]

meson (https://github.com/mesonbuild/meson)
+ mesonbuild/arglist.py:133:21: error: Missing positional argument "arg" in call to "_can_dedup" of "CompilerArgs"  [call-arg]
+ mesonbuild/arglist.py:133:37: error: Argument 1 to "_can_dedup" of "CompilerArgs" has incompatible type "str"; expected "type[CompilerArgs]"  [arg-type]
+ mesonbuild/arglist.py:139:21: error: Missing positional argument "arg" in call to "_can_dedup" of "CompilerArgs"  [call-arg]
+ mesonbuild/arglist.py:139:37: error: Argument 1 to "_can_dedup" of "CompilerArgs" has incompatible type "str"; expected "type[CompilerArgs]"  [arg-type]
+ mesonbuild/arglist.py:302:21: error: Missing positional argument "arg" in call to "_can_dedup" of "CompilerArgs"  [call-arg]
+ mesonbuild/arglist.py:302:37: error: Argument 1 to "_can_dedup" of "CompilerArgs" has incompatible type "str"; expected "type[CompilerArgs]"  [arg-type]
+ mesonbuild/arglist.py:309:16: error: Missing positional argument "arg" in call to "_should_prepend" of "CompilerArgs"  [call-arg]
+ mesonbuild/arglist.py:309:37: error: Argument 1 to "_should_prepend" of "CompilerArgs" has incompatible type "str"; expected "type[CompilerArgs]"  [arg-type]
+ mesonbuild/backend/backends.py:1164:34: error: Missing positional argument "target" in call to "extract_dll_paths" of "Backend"  [call-arg]
+ mesonbuild/backend/backends.py:1164:57: error: Argument 1 to "extract_dll_paths" of "Backend" has incompatible type "BuildTarget"; expected "type[Backend]"  [arg-type]

paasta (https://github.com/yelp/paasta)
+ paasta_tools/cli/cmds/validate.py:1205: error: Argument 1 to "get_config_file_dict" has incompatible type "Path"; expected "str"  [arg-type]

core (https://github.com/home-assistant/core)
+ homeassistant/components/camera/webrtc.py:54: error: Missing positional argument "cls" in call to "_get_type" of "WebRTCMessage"  [call-arg]
+ homeassistant/components/xiaomi_aqara/config_flow.py:229: error: Argument 1 to "format_mac" has incompatible type "str | None"; expected "str"  [arg-type]
+ homeassistant/components/bsblan/config_flow.py:310: error: Argument 1 to "format_mac" has incompatible type "str | None"; expected "str"  [arg-type]
+ homeassistant/components/wled/config_flow.py:75: error: Argument 1 to "format_mac" has incompatible type "str | None"; expected "str"  [arg-type]
+ homeassistant/components/tessie/number.py:202: error: Argument 1 to "icon_for_battery_level" has incompatible type "float | None"; expected "int | None"  [arg-type]
+ homeassistant/components/tesla_fleet/number.py:198: error: Argument 1 to "icon_for_battery_level" has incompatible type "float | None"; expected "int | None"  [arg-type]
+ homeassistant/components/mqtt_room/sensor.py:199: error: Argument 1 to "_slugify_upper" has incompatible type "Any | None"; expected "str"  [arg-type]
+ homeassistant/components/ecovacs/sensor.py:389: error: Argument "battery_level" to "icon_for_battery_level" has incompatible type "str | int | float | None"; expected "int | None"  [arg-type]
+ homeassistant/components/esphome/light.py:182: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:191: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:202: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:211: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:212: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:217: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:229: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:246: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:251: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:265: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/light.py:345: error: Argument 1 to "_filter_color_modes" has incompatible type "tuple[Any, ...]"; expected "list[Any]"  [arg-type]
+ homeassistant/components/esphome/manager.py:593: error: Argument 1 to "format_mac" has incompatible type "str | None"; expected "str"  [arg-type]
+ homeassistant/components/esphome/config_flow.py:664: error: Argument 1 to "format_mac" has incompatible type "str | None"; expected "str"  [arg-type]
+ homeassistant/components/esphome/config_flow.py:707: error: Argument 1 to "format_mac" has incompatible type "str | None"; expected "str"  [arg-type]

mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/coretypes/serializable.py:83: error: Missing positional argument "cls" in call to "__fields" of "SerializableDataclass"  [call-arg]
+ mitmproxy/coretypes/serializable.py:99: error: Missing positional argument "cls" in call to "__fields" of "SerializableDataclass"  [call-arg]
+ mitmproxy/tools/console/common.py:808: error: Argument "request_timestamp" to "format_dns_flow" has incompatible type "float | None"; expected "float"  [arg-type]
+ mitmproxy/tools/console/common.py:816: error: Argument "error_message" to "format_dns_flow" has incompatible type "str | None"; expected "str"  [arg-type]

rich (https://github.com/Textualize/rich)
+ rich/progress_bar.py:145: error: Argument 3 to "_get_pulse_segments" of "ProgressBar" has incompatible type "str | None"; expected "str"  [arg-type]

pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/arrays/base.py:2859: error: Missing positional argument "is_numeric" in call to "_get_cython_function" of "WrappedCythonOp"  [call-arg]
+ pandas/core/arrays/base.py:2859: error: Argument 1 to "_get_cython_function" of "WrappedCythonOp" has incompatible type "str"; expected "type[WrappedCythonOp]"  [arg-type]
+ pandas/core/arrays/base.py:2859: error: Argument 3 to "_get_cython_function" of "WrappedCythonOp" has incompatible type "dtype[object_ | Any]"; expected "str"  [arg-type]
+ pandas/core/arrays/base.py:2859: error: Argument 4 to "_get_cython_function" of "WrappedCythonOp" has incompatible type "bool"; expected "dtype[Any]"  [arg-type]
+ pandas/core/groupby/ops.py:420: error: Missing positional argument "is_numeric" in call to "_get_cython_function" of "WrappedCythonOp"  [call-arg]
+ pandas/core/groupby/ops.py:420: error: Argument 1 to "_get_cython_function" of "WrappedCythonOp" has incompatible type "str"; expected "type[WrappedCythonOp]"  [arg-type]
+ pandas/core/groupby/ops.py:420: error: Argument 3 to "_get_cython_function" of "WrappedCythonOp" has incompatible type "dtype[Any]"; expected "str"  [arg-type]
+ pandas/core/groupby/ops.py:420: error: Argument 4 to "_get_cython_function" of "WrappedCythonOp" has incompatible type "bool"; expected "dtype[Any]"  [arg-type]

ibis (https://github.com/ibis-project/ibis)
+ ibis/backends/tests/test_temporal.py:2429: error: Argument "exclude" to "_get_backend_names" has incompatible type "tuple[str, str]"; expected "tuple[str]"  [arg-type]
+ ibis/backends/tests/test_temporal.py:2447: error: Argument "exclude" to "_get_backend_names" has incompatible type "tuple[str, str, str, str, str, str]"; expected "tuple[str]"  [arg-type]

poetry (https://github.com/python-poetry/poetry)
+ tests/utils/test_password_manager.py:261: error: Missing positional argument "cls" in call to "is_available" of "PoetryKeyring"  [call-arg]
+ tests/utils/test_password_manager.py:269: error: Missing positional argument "cls" in call to "is_available" of "PoetryKeyring"  [call-arg]
+ tests/utils/test_password_manager.py:277: error: Missing positional argument "cls" in call to "is_available" of "PoetryKeyring"  [call-arg]
+ tests/utils/test_password_manager.py:285: error: Missing positional argument "cls" in call to "is_available" of "PoetryKeyring"  [call-arg]
+ tests/utils/test_password_manager.py:291: error: Missing positional argument "cls" in call to "is_available" of "PoetryKeyring"  [call-arg]
+ tests/utils/test_password_manager.py:299: error: Missing positional argument "cls" in call to "is_available" of "PoetryKeyring"  [call-arg]

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

Labels

topic-plugins The plugin API and ideas for new plugins

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parameters for a call to a function wrapped in a lru_cache are not checked

2 participants