Skip to content

Commit

Permalink
Limit inference to a maximum of 100 results at a time to prevent
Browse files Browse the repository at this point in the history
spot performance issues.

Add new envrionment variable call ASTROID_MAX_INFERABLE to tune
the max inferable amount of values at a time.

Close pylint-dev#579
Close pylint-dev/pylint#2251
  • Loading branch information
brycepg committed Jul 6, 2018
1 parent 0b7638a commit ea67cd8
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 1 deletion.
6 changes: 6 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ Release Date: Unknown

Close PyCQA/pylint#2159

* Limit the maximum amount of interable result in an NodeNG.infer() call to
100 by default for performance issues with variables with large amounts of
possible values.
The max inferable value can be tuned by setting the ASTROID_MAX_INFERABLE environment
variable at start up.


What's New in astroid 1.6.0?
============================
Expand Down
4 changes: 3 additions & 1 deletion astroid/node_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,9 @@ def infer(self, context=None, **kwargs):
if key in context.inferred:
return iter(context.inferred[key])

return context.cache_generator(key, self._infer(context, **kwargs))
gen = context.cache_generator(
key, self._infer(context, **kwargs))
return util.limit_inference(gen)

def _repr_name(self):
"""Get a name for nice representation.
Expand Down
30 changes: 30 additions & 0 deletions astroid/tests/unittest_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import sys
from functools import partial
import unittest
from unittest.mock import patch

import pytest

Expand Down Expand Up @@ -4656,5 +4657,34 @@ def f(**kwargs):
assert next(extract_node(code).infer()).as_string() == "{'f': 1}"


def test_limit_inference_result_amount():
"""Test setting limit inference result amount"""
code = """
args = []
if True:
args += ['a']
if True:
args += ['b']
if True:
args += ['c']
if True:
args += ['d']
args #@
"""
result = extract_node(code).inferred()
assert len(result) == 16
with patch('astroid.util.MAX_INFERABLE', 4):
result_limited = extract_node(code).inferred()
# Can't guarentee exact size
assert len(result_limited) < 16
# Will not always be at the end
assert util.Uninferable in result_limited


if __name__ == '__main__':
unittest.main()
17 changes: 17 additions & 0 deletions astroid/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER

import warnings
import os

import importlib
import lazy_object_proxy

MAX_INFERABLE = int(os.environ.get("ASTROID_MAX_INFERABLE", 100))

def lazy_descriptor(obj):
class DescriptorProxy(lazy_object_proxy.Proxy):
Expand Down Expand Up @@ -126,5 +128,20 @@ def proxy_alias(alias_name, node_type):
return proxy(lambda: node_type)


def limit_inference(iterable):
"""Limit inference to MAX_INFERABLE
Limit inference amount to help with performance issues with
exponentially exploding possible results.
"""
count = 0
for result in iterable:
count += 1
if count > MAX_INFERABLE:
yield Uninferable
return
yield result


# Backwards-compatibility aliases
YES = Uninferable

0 comments on commit ea67cd8

Please sign in to comment.