mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-07 22:44:27 +08:00
assert isinstance checks
This commit is contained in:
@@ -60,14 +60,13 @@ Jedi supports many of the widely used Python features:
|
||||
- class decorators (py3k feature, are being ignored too, until I find a use
|
||||
case, that doesn't work with Jedi)
|
||||
- simple/usual ``sys.path`` modifications
|
||||
- ``isinstance`` checks for if/while
|
||||
- ``isinstance`` checks for if/while/assert
|
||||
|
||||
However, it does not yet support (and probably will in future versions, because
|
||||
they are on my todo list):
|
||||
|
||||
- manipulations of instances outside the instance variables, without using
|
||||
functions
|
||||
- assert
|
||||
|
||||
It does not support (and most probably will not in future versions):
|
||||
|
||||
|
||||
@@ -494,18 +494,32 @@ class RelatedName(BaseOutput):
|
||||
return hash((self.start_pos, self.module_path))
|
||||
|
||||
|
||||
def check_flow_information(flow, search_name):
|
||||
def check_flow_information(flow, search_name, pos):
|
||||
""" Try to find out the type of a variable just with the information that
|
||||
is given by the flows: e.g.
|
||||
is given by the flows: e.g. It is also responsible for assert checks.
|
||||
>>> if isinstance(k, str):
|
||||
>>> k. # <- completion here
|
||||
|
||||
ensures that `k` is a string.
|
||||
"""
|
||||
result = []
|
||||
if isinstance(flow, parsing.Scope) and not result:
|
||||
for ass in reversed(flow.asserts):
|
||||
if ass.start_pos > pos:
|
||||
continue
|
||||
result = check_statement_information(ass, search_name)
|
||||
if result:
|
||||
break
|
||||
|
||||
if isinstance(flow, parsing.Flow) and not result:
|
||||
if flow.command in ['if', 'while'] and len(flow.inits) == 1:
|
||||
result = check_statement_information(flow.inits[0], search_name)
|
||||
return result
|
||||
|
||||
|
||||
def check_statement_information(stmt, search_name):
|
||||
try:
|
||||
assert flow.command in ['if', 'while']
|
||||
assert len(flow.inits) == 1
|
||||
ass = flow.inits[0].get_assignment_calls()
|
||||
ass = stmt.get_assignment_calls()
|
||||
assert len(ass.values) == 1 and len(ass.values[0]) == 1
|
||||
call = ass.values[0][0]
|
||||
assert type(call) == parsing.Call and str(call.name) == 'isinstance'
|
||||
|
||||
@@ -264,7 +264,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name not in ['start_pos', 'end_pos', 'name', 'get_imports',
|
||||
'docstr']:
|
||||
'docstr', 'asserts']:
|
||||
raise AttributeError("Instance %s: Don't touch this (%s)!"
|
||||
% (self, name))
|
||||
return getattr(self.base, name)
|
||||
@@ -386,7 +386,7 @@ class Class(use_metaclass(CachedMetaClass, parsing.Base)):
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name not in ['start_pos', 'end_pos', 'parent', 'subscopes',
|
||||
'get_imports', 'get_parent_until', 'docstr']:
|
||||
'get_imports', 'get_parent_until', 'docstr', 'asserts']:
|
||||
raise AttributeError("Don't touch this (%s)!" % name)
|
||||
return getattr(self.base, name)
|
||||
|
||||
@@ -726,6 +726,11 @@ class Execution(Executable):
|
||||
def returns(self):
|
||||
return self.copy_properties('returns')
|
||||
|
||||
@property
|
||||
@memoize_default()
|
||||
def asserts(self):
|
||||
return self.copy_properties('asserts')
|
||||
|
||||
@property
|
||||
@memoize_default()
|
||||
def statements(self):
|
||||
@@ -1148,10 +1153,13 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False,
|
||||
if result:
|
||||
break
|
||||
|
||||
while flow_scope and flow_scope.isinstance(parsing.Flow):
|
||||
result = dynamic.check_flow_information(flow_scope, name_str)
|
||||
while flow_scope:
|
||||
result = dynamic.check_flow_information(flow_scope, name_str,
|
||||
position)
|
||||
if result:
|
||||
break
|
||||
if flow_scope == nscope:
|
||||
break
|
||||
flow_scope = flow_scope.parent()
|
||||
flow_scope = nscope
|
||||
if result:
|
||||
|
||||
@@ -92,7 +92,7 @@ class ExecutionRecursionDecorator(object):
|
||||
self.reset()
|
||||
|
||||
def __call__(self, execution, evaluate_generator=False):
|
||||
debug.dbg('Execution recursions: ', execution, self.recursion_level,
|
||||
debug.dbg('Execution recursions: %s' % execution, self.recursion_level,
|
||||
self.execution_count, len(self.execution_funcs))
|
||||
if self.check_recursion(execution, evaluate_generator):
|
||||
result = []
|
||||
|
||||
@@ -139,6 +139,7 @@ class Scope(Simple):
|
||||
self.imports = []
|
||||
self.statements = []
|
||||
self.docstr = docstr
|
||||
self.asserts = []
|
||||
|
||||
def add_scope(self, sub, decorators):
|
||||
sub.parent = weakref.ref(self)
|
||||
@@ -1416,7 +1417,7 @@ class PyFuzzyParser(object):
|
||||
string += ".".join(n.names)
|
||||
continue
|
||||
elif token_type == tokenize.NAME:
|
||||
if tok in ['return', 'yield', 'del', 'raise', 'assert']:
|
||||
if tok in ['return', 'yield', 'del', 'raise']:
|
||||
if len(tok_list) > 1:
|
||||
# this happens, when a statement has opening
|
||||
# brackets, which are not closed again, here I just
|
||||
@@ -1751,6 +1752,10 @@ class PyFuzzyParser(object):
|
||||
decorators.append(stmt)
|
||||
elif tok == 'pass':
|
||||
continue
|
||||
elif tok == 'assert':
|
||||
stmt, tok = self._parse_statement()
|
||||
stmt.parent = weakref.ref(self.scope)
|
||||
self.scope.asserts.append(stmt)
|
||||
# default
|
||||
elif token_type in [tokenize.NAME, tokenize.STRING,
|
||||
tokenize.NUMBER] \
|
||||
|
||||
@@ -17,3 +17,14 @@ if not isinstance(k, (str, int)):
|
||||
while not isinstance(k, (str, int)):
|
||||
#?
|
||||
k
|
||||
|
||||
assert isinstance(ass, int):
|
||||
#? int()
|
||||
ass
|
||||
|
||||
assert isinstance(ass, str):
|
||||
assert not isinstance(ass, int):
|
||||
|
||||
if 2:
|
||||
#? str()
|
||||
ass
|
||||
|
||||
Reference in New Issue
Block a user