Skip to content

Commit

Permalink
Fix Use of cache on methods can lead to memory leaks
Browse files Browse the repository at this point in the history
  • Loading branch information
Pierre-Sassoulas committed Apr 19, 2022
1 parent 65a4360 commit 87a9653
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 17 deletions.
12 changes: 4 additions & 8 deletions astroid/interpreter/objectmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
import os
import pprint
import types
from functools import lru_cache
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, List, Optional

import astroid
from astroid import util
Expand All @@ -41,6 +40,7 @@
from astroid.objects import Property

IMPL_PREFIX = "attr_"
LEN_OF_IMPL_PREFIX = len(IMPL_PREFIX)


def _dunder_dict(instance, attributes):
Expand Down Expand Up @@ -100,20 +100,16 @@ def __get__(self, instance, cls=None):
def __contains__(self, name):
return name in self.attributes()

@lru_cache(maxsize=None)
def attributes(self):
def attributes(self) -> List[str]:
"""Get the attributes which are exported by this object model."""
return [
obj[len(IMPL_PREFIX) :] for obj in dir(self) if obj.startswith(IMPL_PREFIX)
]
return [o[LEN_OF_IMPL_PREFIX:] for o in dir(self) if o.startswith(IMPL_PREFIX)]

def lookup(self, name):
"""Look up the given *name* in the current model
It should return an AST or an interpreter object,
but if the name is not found, then an AttributeInferenceError will be raised.
"""

if name in self.attributes():
return getattr(self, IMPL_PREFIX + name)
raise AttributeInferenceError(target=self._instance, attribute=name)
Expand Down
6 changes: 2 additions & 4 deletions astroid/nodes/node_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,21 +367,19 @@ def get_children(self):
class LookupMixIn:
"""Mixin to look up a name in the right scope."""

@lru_cache(maxsize=None)
def lookup(self, name):
@lru_cache(maxsize=None) # pylint: disable=cache-max-size-none # noqa
def lookup(self, name: str) -> typing.Tuple[str, typing.List[NodeNG]]:
"""Lookup where the given variable is assigned.
The lookup starts from self's scope. If self is not a frame itself
and the name is found in the inner frame locals, statements will be
filtered to remove ignorable statements according to self's location.
:param name: The name of the variable to find assignments for.
:type name: str
:returns: The scope node and the list of assignments associated to the
given name according to the scope where it has been found (locals,
globals or builtin).
:rtype: tuple(str, list(NodeNG))
"""
return self.scope().scope_lookup(self, name)

Expand Down
10 changes: 5 additions & 5 deletions astroid/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt

import collections
from functools import lru_cache
from typing import TYPE_CHECKING

from astroid.context import _invalidate_cache

if TYPE_CHECKING:
from astroid import NodeNG


class TransformVisitor:
"""A visitor for handling transforms.
Expand All @@ -17,13 +20,10 @@ class TransformVisitor:
transforms for each encountered node.
"""

TRANSFORM_MAX_CACHE_SIZE = 10000

def __init__(self):
self.transforms = collections.defaultdict(list)

@lru_cache(maxsize=TRANSFORM_MAX_CACHE_SIZE)
def _transform(self, node):
def _transform(self, node: "NodeNG") -> "NodeNG":
"""Call matching transforms for the given node if any and return the
transformed node.
"""
Expand Down

0 comments on commit 87a9653

Please sign in to comment.