assert isinstance checks

This commit is contained in:
David Halter
2012-10-07 15:13:40 +02:00
parent 8795b4fbac
commit 39f89462b4
6 changed files with 50 additions and 13 deletions

View File

@@ -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 - class decorators (py3k feature, are being ignored too, until I find a use
case, that doesn't work with Jedi) case, that doesn't work with Jedi)
- simple/usual ``sys.path`` modifications - 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 However, it does not yet support (and probably will in future versions, because
they are on my todo list): they are on my todo list):
- manipulations of instances outside the instance variables, without using - manipulations of instances outside the instance variables, without using
functions functions
- assert
It does not support (and most probably will not in future versions): It does not support (and most probably will not in future versions):

View File

@@ -494,18 +494,32 @@ class RelatedName(BaseOutput):
return hash((self.start_pos, self.module_path)) 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 """ 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): >>> if isinstance(k, str):
>>> k. # <- completion here >>> k. # <- completion here
ensures that `k` is a string. 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: try:
assert flow.command in ['if', 'while'] ass = stmt.get_assignment_calls()
assert len(flow.inits) == 1
ass = flow.inits[0].get_assignment_calls()
assert len(ass.values) == 1 and len(ass.values[0]) == 1 assert len(ass.values) == 1 and len(ass.values[0]) == 1
call = ass.values[0][0] call = ass.values[0][0]
assert type(call) == parsing.Call and str(call.name) == 'isinstance' assert type(call) == parsing.Call and str(call.name) == 'isinstance'

View File

@@ -264,7 +264,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
def __getattr__(self, name): def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'name', 'get_imports', if name not in ['start_pos', 'end_pos', 'name', 'get_imports',
'docstr']: 'docstr', 'asserts']:
raise AttributeError("Instance %s: Don't touch this (%s)!" raise AttributeError("Instance %s: Don't touch this (%s)!"
% (self, name)) % (self, name))
return getattr(self.base, name) return getattr(self.base, name)
@@ -386,7 +386,7 @@ class Class(use_metaclass(CachedMetaClass, parsing.Base)):
def __getattr__(self, name): def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'parent', 'subscopes', 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) raise AttributeError("Don't touch this (%s)!" % name)
return getattr(self.base, name) return getattr(self.base, name)
@@ -726,6 +726,11 @@ class Execution(Executable):
def returns(self): def returns(self):
return self.copy_properties('returns') return self.copy_properties('returns')
@property
@memoize_default()
def asserts(self):
return self.copy_properties('asserts')
@property @property
@memoize_default() @memoize_default()
def statements(self): def statements(self):
@@ -1148,10 +1153,13 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False,
if result: if result:
break break
while flow_scope and flow_scope.isinstance(parsing.Flow): while flow_scope:
result = dynamic.check_flow_information(flow_scope, name_str) result = dynamic.check_flow_information(flow_scope, name_str,
position)
if result: if result:
break break
if flow_scope == nscope:
break
flow_scope = flow_scope.parent() flow_scope = flow_scope.parent()
flow_scope = nscope flow_scope = nscope
if result: if result:

View File

@@ -92,7 +92,7 @@ class ExecutionRecursionDecorator(object):
self.reset() self.reset()
def __call__(self, execution, evaluate_generator=False): 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)) self.execution_count, len(self.execution_funcs))
if self.check_recursion(execution, evaluate_generator): if self.check_recursion(execution, evaluate_generator):
result = [] result = []

View File

@@ -139,6 +139,7 @@ class Scope(Simple):
self.imports = [] self.imports = []
self.statements = [] self.statements = []
self.docstr = docstr self.docstr = docstr
self.asserts = []
def add_scope(self, sub, decorators): def add_scope(self, sub, decorators):
sub.parent = weakref.ref(self) sub.parent = weakref.ref(self)
@@ -1416,7 +1417,7 @@ class PyFuzzyParser(object):
string += ".".join(n.names) string += ".".join(n.names)
continue continue
elif token_type == tokenize.NAME: 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: if len(tok_list) > 1:
# this happens, when a statement has opening # this happens, when a statement has opening
# brackets, which are not closed again, here I just # brackets, which are not closed again, here I just
@@ -1751,6 +1752,10 @@ class PyFuzzyParser(object):
decorators.append(stmt) decorators.append(stmt)
elif tok == 'pass': elif tok == 'pass':
continue continue
elif tok == 'assert':
stmt, tok = self._parse_statement()
stmt.parent = weakref.ref(self.scope)
self.scope.asserts.append(stmt)
# default # default
elif token_type in [tokenize.NAME, tokenize.STRING, elif token_type in [tokenize.NAME, tokenize.STRING,
tokenize.NUMBER] \ tokenize.NUMBER] \

View File

@@ -17,3 +17,14 @@ if not isinstance(k, (str, int)):
while not isinstance(k, (str, int)): while not isinstance(k, (str, int)):
#? #?
k k
assert isinstance(ass, int):
#? int()
ass
assert isinstance(ass, str):
assert not isinstance(ass, int):
if 2:
#? str()
ass