mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-08 06:44:46 +08:00
Fix issues with interpreter completions on unittest.mock.
For 3.6+ an error was ignored that lead to crashes. In 3.5 the OOM killer eventually arrived... Fixes #1415
This commit is contained in:
@@ -684,3 +684,45 @@ if not is_py3:
|
|||||||
|
|
||||||
atexit.register(finalize._exitfunc)
|
atexit.register(finalize._exitfunc)
|
||||||
weakref.finalize = finalize
|
weakref.finalize = finalize
|
||||||
|
|
||||||
|
|
||||||
|
if is_py3 and sys.version_info[1] > 5:
|
||||||
|
from inspect import unwrap
|
||||||
|
else:
|
||||||
|
# Only Python >=3.6 does properly limit the amount of unwraps. This is very
|
||||||
|
# relevant in the case of unittest.mock.patch.
|
||||||
|
# Below is the implementation of Python 3.7.
|
||||||
|
def unwrap(func, stop=None):
|
||||||
|
"""Get the object wrapped by *func*.
|
||||||
|
|
||||||
|
Follows the chain of :attr:`__wrapped__` attributes returning the last
|
||||||
|
object in the chain.
|
||||||
|
|
||||||
|
*stop* is an optional callback accepting an object in the wrapper chain
|
||||||
|
as its sole argument that allows the unwrapping to be terminated early if
|
||||||
|
the callback returns a true value. If the callback never returns a true
|
||||||
|
value, the last object in the chain is returned as usual. For example,
|
||||||
|
:func:`signature` uses this to stop unwrapping if any object in the
|
||||||
|
chain has a ``__signature__`` attribute defined.
|
||||||
|
|
||||||
|
:exc:`ValueError` is raised if a cycle is encountered.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if stop is None:
|
||||||
|
def _is_wrapper(f):
|
||||||
|
return hasattr(f, '__wrapped__')
|
||||||
|
else:
|
||||||
|
def _is_wrapper(f):
|
||||||
|
return hasattr(f, '__wrapped__') and not stop(f)
|
||||||
|
f = func # remember the original func for error reporting
|
||||||
|
# Memoise by id to tolerate non-hashable objects, but store objects to
|
||||||
|
# ensure they aren't destroyed, which would allow their IDs to be reused.
|
||||||
|
memo = {id(f): f}
|
||||||
|
recursion_limit = sys.getrecursionlimit()
|
||||||
|
while _is_wrapper(func):
|
||||||
|
func = func.__wrapped__
|
||||||
|
id_func = id(func)
|
||||||
|
if (id_func in memo) or (len(memo) >= recursion_limit):
|
||||||
|
raise ValueError('wrapper loop when unwrapping {!r}'.format(f))
|
||||||
|
memo[id_func] = func
|
||||||
|
return func
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import sys
|
|||||||
|
|
||||||
from jedi.parser_utils import get_cached_code_lines
|
from jedi.parser_utils import get_cached_code_lines
|
||||||
|
|
||||||
|
from jedi._compatibility import unwrap
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi.inference import compiled
|
from jedi.inference import compiled
|
||||||
from jedi.cache import underscore_memoization
|
from jedi.cache import underscore_memoization
|
||||||
@@ -160,7 +161,11 @@ def _load_module(inference_state, path):
|
|||||||
def _get_object_to_check(python_object):
|
def _get_object_to_check(python_object):
|
||||||
"""Check if inspect.getfile has a chance to find the source."""
|
"""Check if inspect.getfile has a chance to find the source."""
|
||||||
if sys.version_info[0] > 2:
|
if sys.version_info[0] > 2:
|
||||||
python_object = inspect.unwrap(python_object)
|
try:
|
||||||
|
python_object = unwrap(python_object)
|
||||||
|
except ValueError:
|
||||||
|
# Can return a ValueError when it wraps around
|
||||||
|
pass
|
||||||
|
|
||||||
if (inspect.ismodule(python_object) or
|
if (inspect.ismodule(python_object) or
|
||||||
inspect.isclass(python_object) or
|
inspect.isclass(python_object) or
|
||||||
|
|||||||
@@ -466,10 +466,10 @@ def test__wrapped__():
|
|||||||
assert c.line == syslogs_to_df.__wrapped__.__code__.co_firstlineno + 1
|
assert c.line == syslogs_to_df.__wrapped__.__code__.co_firstlineno + 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('module_name', ['sys', 'time'])
|
@pytest.mark.parametrize('module_name', ['sys', 'time', 'unittest.mock'])
|
||||||
def test_core_module_completes(module_name):
|
def test_core_module_completes(module_name):
|
||||||
module = import_module(module_name)
|
module = import_module(module_name)
|
||||||
assert jedi.Interpreter(module_name + '.\n', [locals()]).completions()
|
assert jedi.Interpreter('module.', [locals()]).completions()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.version_info[0] == 2, reason="Ignore Python 2, because EOL")
|
@pytest.mark.skipif(sys.version_info[0] == 2, reason="Ignore Python 2, because EOL")
|
||||||
|
|||||||
Reference in New Issue
Block a user