Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename cache-max-size-none and check functools.cache #6182

Merged
merged 7 commits into from Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog
Expand Up @@ -40,6 +40,10 @@ Release date: TBA

Closes #5936

* * Added ``cache-max-size-none`` checker will now also check ``functools.cache``.
DanielNoord marked this conversation as resolved.
Show resolved Hide resolved

Closes #5670

* ``potential-index-error``: Emitted when the index of a list or tuple exceeds its length.
This checker is currently quite conservative to avoid false positives. We welcome
suggestions for improvements.
Expand Down
9 changes: 9 additions & 0 deletions doc/data/messages/c/cache-max-size-none/bad.py
@@ -0,0 +1,9 @@
import functools


class Fibonnaci:
@functools.cache # [cache-max-size-none]
def fibonacci(self, n):
if n in {0, 1}:
return n
return self.fibonacci(n - 1) + self.fibonacci(n - 2)
13 changes: 13 additions & 0 deletions doc/data/messages/c/cache-max-size-none/good.py
@@ -0,0 +1,13 @@
import functools


@functools.cache
jacobtylerwalls marked this conversation as resolved.
Show resolved Hide resolved
def cached_fibonacci(n):
if n in {0, 1}:
return n
return cached_fibonacci(n - 1) + cached_fibonacci(n - 2)


class Fibonnaci:
def fibonacci(self, n):
return cached_fibonacci(n)
4 changes: 4 additions & 0 deletions doc/whatsnew/2.14.rst
Expand Up @@ -71,6 +71,10 @@ Other Changes

* The concept of checker priority has been removed.

* Added ``cache-max-size-none`` checker will now also check ``functools.cache``.
DanielNoord marked this conversation as resolved.
Show resolved Hide resolved

Closes #5670

* The ``set_config_directly`` decorator has been removed.

* The ``ignore-mixin-members`` option has been deprecated. You should now use the new
Expand Down
22 changes: 13 additions & 9 deletions pylint/checkers/stdlib.py
Expand Up @@ -429,10 +429,10 @@ class StdlibChecker(DeprecatedMixin, BaseChecker):
"from code that is not actively being debugged.",
),
"W1517": (
"'lru_cache(maxsize=None)' will keep all method args alive indefinitely, including 'self'",
"'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self'",
"cache-max-size-none",
"By decorating a method with lru_cache the 'self' argument will be linked to "
"the lru_cache function and therefore never garbage collected. Unless your instance "
"By decorating a method with lru_cache or cache the 'self' argument will be linked to "
"the function and therefore never garbage collected. Unless your instance "
"will never need to be garbage collected (singleton) it is recommended to refactor "
"code to avoid this pattern or add a maxsize to the cache."
"The default value for maxsize is 128.",
Expand Down Expand Up @@ -575,23 +575,27 @@ def _check_lru_cache_decorators(self, decorators: nodes.Decorators) -> None:
try:
for infered_node in d_node.infer():
q_name = infered_node.qname()
if q_name in NON_INSTANCE_METHODS or q_name not in LRU_CACHE:
if q_name in NON_INSTANCE_METHODS:
return

# Check if there is a maxsize argument set to None in the call
if isinstance(d_node, nodes.Call):
if q_name in LRU_CACHE and isinstance(d_node, nodes.Call):
try:
arg = utils.get_argument_from_call(
d_node, position=0, keyword="maxsize"
)
except utils.NoSuchArgumentError:
return
break

if not isinstance(arg, nodes.Const) or arg.value is not None:
return
break

lru_cache_nodes.append(d_node)
break
lru_cache_nodes.append(d_node)
break

if q_name == "functools.cache":
lru_cache_nodes.append(d_node)
break
except astroid.InferenceError:
pass
for lru_cache_node in lru_cache_nodes:
Expand Down
14 changes: 7 additions & 7 deletions tests/functional/c/cache_max_size_none.txt
@@ -1,7 +1,7 @@
cache-max-size-none:25:5:25:20:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:29:5:29:30:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:33:5:33:38:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:37:5:37:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:42:5:42:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:43:5:43:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:73:5:73:40:MyClassWithMethodsAndMaxSize.my_func:'lru_cache(maxsize=None)' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:25:5:25:20:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:29:5:29:30:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:33:5:33:38:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:37:5:37:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:42:5:42:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:43:5:43:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:73:5:73:40:MyClassWithMethodsAndMaxSize.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
47 changes: 47 additions & 0 deletions tests/functional/c/cache_max_size_none_py39.py
@@ -0,0 +1,47 @@
"""Tests for cache-max-size-none"""
# pylint: disable=no-self-use, missing-function-docstring, reimported, too-few-public-methods
# pylint: disable=missing-class-docstring, function-redefined

import functools
import functools as aliased_functools
from functools import cache
from functools import cache as aliased_cache


@cache
def my_func(param):
return param + 1


class MyClassWithMethods:
@cache
@staticmethod
def my_func(param):
return param + 1

@cache
@classmethod
def my_func(cls, param):
return param + 1

@cache # [cache-max-size-none]
def my_func(self, param):
return param + 1

@functools.cache # [cache-max-size-none]
def my_func(self, param):
return param + 1

@aliased_functools.cache # [cache-max-size-none]
def my_func(self, param):
return param + 1

@aliased_cache # [cache-max-size-none]
def my_func(self, param):
return param + 1

# Check double decorating to check robustness of checker itself
@functools.lru_cache(maxsize=1)
@aliased_cache # [cache-max-size-none]
def my_func(self, param):
return param + 1
2 changes: 2 additions & 0 deletions tests/functional/c/cache_max_size_none_py39.rc
@@ -0,0 +1,2 @@
[testoptions]
min_pyver = 3.9
5 changes: 5 additions & 0 deletions tests/functional/c/cache_max_size_none_py39.txt
@@ -0,0 +1,5 @@
cache-max-size-none:27:5:27:10:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:31:5:31:20:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:35:5:35:28:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:39:5:39:18:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
cache-max-size-none:45:5:45:18:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE