def get_contracts_and_defs_and_globals(code):
_contracts = {}
_events = []
_globals = {}
_defs = []
_getters = []
for item in code:
# Contract references
if isinstance(item, ast.ClassDef):
if _events or _globals or _defs:
raise StructureException("External contract declarations must come before event declarations, global declarations, and function definitions", item)
_contracts[item.name] = add_contract(item.body)
# Statements of the form:
# variable_name: type
elif isinstance(item, ast.AnnAssign):
_contracts, _events, _globals, _getters = add_globals_and_events(_contracts, _defs, _events, _getters, _globals, item)
# Function definitions
elif isinstance(item, ast.FunctionDef):
_defs.append(item)
else:
raise StructureException("Invalid top-level statement", item)
return _contracts, _events, _defs + _getters, _globals
# Header code
python类ClassDef()的实例源码
def visit_Call(self, node):
_id = get_call_names_as_string(node.func)
self.function_return_stack.append(_id)
logging.debug(_id)
ast_node = None
local_definitions = self.module_definitions_stack[-1]
definition = local_definitions.get_definition(_id)
if definition:
if isinstance(definition.node, ast.ClassDef):
init = local_definitions.get_definition(_id + '.__init__')
self.add_builtin(node)
elif isinstance(definition.node, ast.FunctionDef):
self.undecided = False
return self.add_function(node, definition)
else:
raise Exception('Definition was neither FunctionDef or ClassDef, cannot add the function ')
return self.add_builtin(node)
def class_to_ast(class_: type, file: str = None) -> ast.ClassDef:
"""
"""
if class_ and not isinstance(class_, type):
raise TypeError('Unexpected type: {}'.format(str(type(class_))))
result = None
try:
src = inspect.getsource(class_)
file = file or inspect.getfile(class_)
result = source_to_ast(src, file)
except IOError:
pass
return result
def ast_to_class(node: ast.AST, old_class: type = None, file: str = None) -> type:
"""
:param node:
:param old_class:
:param file:
:return:
"""
if node and not isinstance(node, (ast.Module, ast.ClassDef)):
raise TypeError('Unexpected type for node: {}'.format(str(type(node))))
if old_class and not isinstance(old_class, type):
raise TypeError('Unexpected type for old_class: {}'.format(str(type(old_class))))
result = old_class
# @TODO:
raise NotImplementedError
return NotImplemented
def createNameMap(a, d=None):
if d == None:
d = { }
if not isinstance(a, ast.AST):
return d
if type(a) == ast.Module: # Need to go through the functions backwards to make this right
for i in range(len(a.body) - 1, -1, -1):
createNameMap(a.body[i], d)
return d
if type(a) in [ast.FunctionDef, ast.ClassDef]:
if hasattr(a, "originalId") and a.name not in d:
d[a.name] = a.originalId
elif type(a) == ast.arg:
if hasattr(a, "originalId") and a.arg not in d:
d[a.arg] = a.originalId
return d
elif type(a) == ast.Name:
if hasattr(a, "originalId") and a.id not in d:
d[a.id] = a.originalId
return d
for child in ast.iter_child_nodes(a):
createNameMap(child, d)
return d
def occursIn(sub, super):
"""Does the first AST occur as a subtree of the second?"""
superStatementTypes = [ ast.Module, ast.Interactive, ast.Suite,
ast.FunctionDef, ast.ClassDef, ast.For,
ast.While, ast.If, ast.With, ast.Try,
ast.ExceptHandler ]
if (not isinstance(super, ast.AST)):
return False
if type(sub) == type(super) and compareASTs(sub, super, checkEquality=True) == 0:
return True
# we know that a statement can never occur in an expression
# (or in a non-statement-holding statement), so cut the search off now to save time.
if isStatement(sub) and type(super) not in superStatementTypes:
return False
for child in ast.iter_child_nodes(super):
if occursIn(sub, child):
return True
return False
def varse(node):
vv = VarsVisitor()
if isinstance(node.ast_node, ast.FunctionDef) or\
isinstance(node.ast_node, ast.ClassDef):
return list()
elif isinstance(node.ast_node, ast.While)\
or isinstance(node.ast_node, ast.If):
vv.visit(node.ast_node.test)
else:
try:
vv.visit(node.ast_node)
except AttributeError:
return list()
if isinstance(node, AssignmentNode):
result = list()
for var in vv.result:
if var not in node.left_hand_side:
result.append(var)
return result
else:
return vv.result
def test_classes_with_specific_method(self):
matches1 = list(self.m.match('class:not(:has(def))',
self.filepath('classes.py')))
matches2 = list(self.m.match('class:has(def[name=bar])',
self.filepath('classes.py')))
matches3 = list(self.m.match('class:has(> def)',
self.filepath('classes.py')))
self.assertEqual(len(matches1), 1)
self.assertIsInstance(matches1[0][0], ast.ClassDef)
self.assertEqual(len(matches2), 3)
self.assertIsInstance(matches2[0][0], ast.ClassDef)
self.assertIsInstance(matches2[1][0], ast.ClassDef)
self.assertIsInstance(matches2[2][0], ast.ClassDef)
self.assertEqual(len(matches3), 2)
self.assertIsInstance(matches3[0][0], ast.ClassDef)
self.assertIsInstance(matches3[1][0], ast.ClassDef)
self.assertEqual(matches3[0][1], 1)
self.assertEqual(matches3[1][1], 14)
def match_type(self, typ, node):
if typ == 'class':
return isinstance(node, ast.ClassDef)
if typ == 'def':
return isinstance(node, ast.FunctionDef)
if typ == 'import':
return isinstance(node, (ast.Import, ast.ImportFrom))
if typ == 'assign':
return isinstance(node, ast.Assign)
if typ == 'attr':
return isinstance(node, ast.Attribute)
if typ == 'call':
if isinstance(node, ast.Call):
return True
# Python 2.x compatibility
return hasattr(ast, 'Print') and isinstance(node, ast.Print)
def _get_module_commands(module):
# type: (ast.Module) -> typing.Generator[_EntryPoint, None, None]
"""Yield all Command objects represented by the python module.
Module commands consist of a docopt-style module docstring and a callable
Command class.
Args:
module: An ast.Module object used to retrieve docopt-style commands.
Yields:
Command objects that represent entry points to append to setup.py.
"""
cls = next((n for n in module.body
if isinstance(n, ast.ClassDef) and n.name == 'Command'), None)
if not cls:
return
methods = (n.name for n in cls.body if isinstance(n, ast.FunctionDef))
if '__call__' not in methods:
return
docstring = ast.get_docstring(module)
for commands, _ in usage.parse_commands(docstring):
yield _EntryPoint(commands[0], next(iter(commands[1:]), None), None)
def _get_class_commands(module):
# type: (ast.Module) -> typing.Generator[_EntryPoint, None, None]
"""Yield all Command objects represented by python classes in the module.
Class commands are detected by inspecting all callable classes in the
module for docopt-style docstrings.
Args:
module: An ast.Module object used to retrieve docopt-style commands.
Yields:
Command objects that represent entry points to append to setup.py.
"""
nodes = (n for n in module.body if isinstance(n, ast.ClassDef))
for cls in nodes:
methods = (n.name for n in cls.body if isinstance(n, ast.FunctionDef))
if '__call__' in methods:
docstring = ast.get_docstring(cls)
for commands, _ in usage.parse_commands(docstring):
yield _EntryPoint(commands[0], next(iter(commands[1:]), None),
cls.name)
def check_assignment(self, statement):
is_class_def = False
if type(statement.__flake8_builtins_parent) is ast.ClassDef:
is_class_def = True
for element in statement.targets:
if isinstance(element, ast.Name) and \
element.id in BUILTINS:
line = element.lineno
offset = element.col_offset
if is_class_def:
msg = self.class_attribute_msg
else:
msg = self.assign_msg
yield (
line,
offset,
msg.format(element.id),
type(self),
)
def method_could_be_class(node, context, search_classes):
if isinstance(node, ast.Call):
call_node = node
parent = get_top_parent_node(call_node)
klass_found = next(
(klass for klass in iter_method_classes(parent, call_node, context) if klass in search_classes),
None
)
return klass_found is not None
elif isinstance(node, ast.FunctionDef):
klass_node = node.parent
if not isinstance(klass_node, ast.ClassDef):
# not sure what happened here
return False
for base_klass in klass_node.bases:
base_klass = base_klass.id
if base_klass in search_classes:
return True
if name_is_imported(base_klass, context, search_classes):
return True
else:
raise ValueError('node must be either an ast.Call or ast.FunctionDef instance')
def test_classdef(self):
def cls(bases=None, keywords=None, starargs=None, kwargs=None,
body=None, decorator_list=None):
if bases is None:
bases = []
if keywords is None:
keywords = []
if body is None:
body = [ast.Pass()]
if decorator_list is None:
decorator_list = []
return ast.ClassDef("myclass", bases, keywords, starargs,
kwargs, body, decorator_list)
self.stmt(cls(bases=[ast.Name("x", ast.Store())]),
"must have Load context")
self.stmt(cls(keywords=[ast.keyword("x", ast.Name("x", ast.Store()))]),
"must have Load context")
self.stmt(cls(starargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(kwargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(body=[]), "empty body on ClassDef")
self.stmt(cls(body=[None]), "None disallowed")
self.stmt(cls(decorator_list=[ast.Name("x", ast.Store())]),
"must have Load context")
def CONTINUE(self, node):
# Walk the tree up until we see a loop (OK), a function or class
# definition (not OK), for 'continue', a finally block (not OK), or
# the top module scope (not OK)
n = node
while hasattr(n, 'parent'):
n, n_child = n.parent, n
if isinstance(n, LOOP_TYPES):
# Doesn't apply unless it's in the loop itself
if n_child not in n.orelse:
return
if isinstance(n, (ast.FunctionDef, ast.ClassDef)):
break
# Handle Try/TryFinally difference in Python < and >= 3.3
if hasattr(n, 'finalbody') and isinstance(node, ast.Continue):
if n_child in n.finalbody:
self.report(messages.ContinueInFinally, node)
return
if isinstance(node, ast.Continue):
self.report(messages.ContinueOutsideLoop, node)
else: # ast.Break
self.report(messages.BreakOutsideLoop, node)
def get_compound_bodies(node):
"""Returns a list of bodies of a compound statement node.
Args:
node: AST node.
Returns:
A list of bodies of the node. If the given node does not represent
a compound statement, an empty list is returned.
"""
if isinstance(node, (ast.Module, ast.FunctionDef, ast.ClassDef, ast.With)):
return [node.body]
elif isinstance(node, (ast.If, ast.While, ast.For)):
return [node.body, node.orelse]
elif PY2 and isinstance(node, ast.TryFinally):
return [node.body, node.finalbody]
elif PY2 and isinstance(node, ast.TryExcept):
return [node.body, node.orelse] + [h.body for h in node.handlers]
elif PY3 and isinstance(node, ast.Try):
return ([node.body, node.orelse, node.finalbody]
+ [h.body for h in node.handlers])
end
return []
def check_methods_per_class(self, **kwargs):
"""
Inspect the code for too many methods per
class.
"""
try:
methods_per_class = kwargs['methods_per_class']
except KeyError:
return
klass = self.parsed_code.body[0]
if not isinstance(klass, ast.ClassDef):
return
methods = [(node, node.lineno) for node in ast.walk(klass)
if isinstance(node, ast.FunctionDef)]
try:
# Get the last method of the class
# and its line number:
line_number = methods[-1][1]
self.issues[line_number].add(
self.code_errors.too_many_methods_per_class(
len(methods), methods_per_class
)
)
except IndexError:
return
def test_classdef(self):
def cls(bases=None, keywords=None, starargs=None, kwargs=None,
body=None, decorator_list=None):
if bases is None:
bases = []
if keywords is None:
keywords = []
if body is None:
body = [ast.Pass()]
if decorator_list is None:
decorator_list = []
return ast.ClassDef("myclass", bases, keywords, starargs,
kwargs, body, decorator_list)
self.stmt(cls(bases=[ast.Name("x", ast.Store())]),
"must have Load context")
self.stmt(cls(keywords=[ast.keyword("x", ast.Name("x", ast.Store()))]),
"must have Load context")
self.stmt(cls(starargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(kwargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(body=[]), "empty body on ClassDef")
self.stmt(cls(body=[None]), "None disallowed")
self.stmt(cls(decorator_list=[ast.Name("x", ast.Store())]),
"must have Load context")
def infer_file(tree, solver, used_names, infer_func, method_type=None):
# Infer only structs that are used in the program to be inferred
# Function definitions
relevant_nodes = StubsHandler.get_relevant_nodes(tree, used_names)
# Only give class definitions to the context to prevent the creation of Z3 constants for stub functions
context = Context([stmt for stmt in tree.body if isinstance(stmt, ast.ClassDef)], solver)
if method_type:
# Add the flag in the statements to recognize the method statements during the inference
for node in relevant_nodes:
node.method_type = method_type
for stmt in relevant_nodes:
infer_func(stmt, context, solver)
return context
def file_contains_pluggable(file_path, pluggable):
plugin_class = None
try:
with open(file_path, "r") as f:
syntax_tree = ast.parse(f.read())
except FileNotFoundError:
return [False, None]
for statement in ast.walk(syntax_tree):
if isinstance(statement, ast.ClassDef):
class_name = statement.name
bases = list(map(lambda b: b.id if isinstance(b, ast.Name) else b.attr, statement.bases))
if pluggable in bases:
plugin_class = class_name
return [plugin_class is not None, plugin_class]
def get_all_filters():
"""Return all the available filters"""
return (
# TODO: Add ast.Module to the docstring search.
DocString('d', 'doc', (ast.FunctionDef, ast.ClassDef, ast.Module),
help="Match class and function docstrings."),
NameFilter('c', 'class', (ast.ClassDef, ),
help="Match class names."),
DefFilter('f', 'def', (ast.FunctionDef, ), (ast.AST, ),
help="Match all defs."),
DefFilter('F', 'function', (ast.FunctionDef, ), (ast.Module, ),
help="Match function names at the module level."),
DefFilter('m', 'method', (ast.FunctionDef, ), (ast.ClassDef, ),
help="Match class method names."),
DefFilter('j', 'closure', (ast.FunctionDef, ), (ast.FunctionDef, ),
help="Match closure def names."),
ImportFilter('i', 'import', (ast.Import, ast.ImportFrom, ),
help="Match imported package names."),
CallFilter('C', 'call', (ast.Call, ),
help="Match call statements."),
AttrFilter('a', 'attr', (ast.Attribute, ),
help="Match attributes on objects"),
)
def _generate_nodes(parent, level=0, breadcrumb=''):
nodes = []
for child in parent.body:
if isinstance(child, ast.ClassDef):
style = OutlineNodeItem.Style.cls
elif isinstance(child, ast.FunctionDef):
style = OutlineNodeItem.Style.fn
else:
style = None
if style:
node = OutlineNodeItem(style, child.name, child.lineno, child.col_offset, level, breadcrumb)
nodes.append(node)
if breadcrumb:
bc = '{} • {}'.format(breadcrumb, child.name)
else:
bc = child.name
child_nodes = OutlineDataSource._generate_nodes(child, level + 1, bc)
if child_nodes:
nodes.extend(child_nodes)
return nodes
def CONTINUE(self, node):
# Walk the tree up until we see a loop (OK), a function or class
# definition (not OK), for 'continue', a finally block (not OK), or
# the top module scope (not OK)
n = node
while hasattr(n, 'parent'):
n, n_child = n.parent, n
if isinstance(n, LOOP_TYPES):
# Doesn't apply unless it's in the loop itself
if n_child not in n.orelse:
return
if isinstance(n, (ast.FunctionDef, ast.ClassDef)):
break
# Handle Try/TryFinally difference in Python < and >= 3.3
if hasattr(n, 'finalbody') and isinstance(node, ast.Continue):
if n_child in n.finalbody:
self.report(messages.ContinueInFinally, node)
return
if isinstance(node, ast.Continue):
self.report(messages.ContinueOutsideLoop, node)
else: # ast.Break
self.report(messages.BreakOutsideLoop, node)
def is_overridden(self, node, name=None):
if not isinstance(node.parent, ast.ClassDef):
raise MutationResign()
if not name:
name = node.name
parent = node.parent
parent_names = []
while parent:
if not isinstance(parent, ast.Module):
parent_names.append(parent.name)
if not isinstance(parent, ast.ClassDef) and not isinstance(parent, ast.Module):
raise MutationResign()
parent = parent.parent
getattr_rec = lambda obj, attr: functools.reduce(getattr, attr, obj)
try:
klass = getattr_rec(self.module, reversed(parent_names))
except AttributeError:
raise MutationResign()
for base_klass in type.mro(klass)[1:-1]:
if hasattr(base_klass, name):
return True
return False
def mutate_FunctionDef(self, node):
if not isinstance(node.parent, ast.ClassDef):
raise MutationResign()
for decorator in node.decorator_list:
if isinstance(decorator, ast.Call):
decorator_name = decorator.func.id
elif isinstance(decorator, ast.Attribute):
decorator_name = decorator.value.id
else:
decorator_name = decorator.id
if decorator_name == self.get_decorator_name():
raise MutationResign()
decorator = ast.Name(id=self.get_decorator_name(), ctx=ast.Load())
node.decorator_list.append(decorator)
return node
def get_coverable_nodes(cls):
return {
ast.Assert,
ast.Assign,
ast.AugAssign,
ast.Break,
ast.Continue,
ast.Delete,
ast.Expr,
ast.Global,
ast.Import,
ast.ImportFrom,
ast.Nonlocal,
ast.Pass,
ast.Raise,
ast.Return,
ast.FunctionDef,
ast.ClassDef,
ast.TryExcept,
ast.TryFinally,
ast.ExceptHandler,
ast.If,
ast.For,
ast.While,
}
def test_classdef(self):
def cls(bases=None, keywords=None, starargs=None, kwargs=None,
body=None, decorator_list=None):
if bases is None:
bases = []
if keywords is None:
keywords = []
if body is None:
body = [ast.Pass()]
if decorator_list is None:
decorator_list = []
return ast.ClassDef("myclass", bases, keywords, starargs,
kwargs, body, decorator_list)
self.stmt(cls(bases=[ast.Name("x", ast.Store())]),
"must have Load context")
self.stmt(cls(keywords=[ast.keyword("x", ast.Name("x", ast.Store()))]),
"must have Load context")
self.stmt(cls(starargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(kwargs=ast.Name("x", ast.Store())),
"must have Load context")
self.stmt(cls(body=[]), "empty body on ClassDef")
self.stmt(cls(body=[None]), "None disallowed")
self.stmt(cls(decorator_list=[ast.Name("x", ast.Store())]),
"must have Load context")
def _pos_in_src(self):
"""Return the position in source of the generated node."""
py_node = self.node.py_node
if py_node:
offset = getattr(py_node, 'col_offset', 0)
# multi-line comments have an offset of -1
if offset < 0:
offset = 0
# special handling of nodes that are decorable. Those nodes expose
# a 'lineno' that starts with the first decorator. for now, take
# the last decorator lineno and add one
if isinstance(py_node, (ast.FunctionDef, ast.AsyncFunctionDef,
ast.ClassDef)) and py_node.decorator_list:
result = (py_node.decorator_list[-1].lineno + 1, offset)
else:
result = (getattr(py_node, 'lineno', None),
offset)
else:
result = (None, None)
return result
def run_in_context(code, context, defs={}):
ast_ = ast.parse(code, '<code>', 'exec')
last_expr = None
last_def_name = None
for field_ in ast.iter_fields(ast_):
if 'body' != field_[0]:
continue
if len(field_[1]) > 0:
le = field_[1][-1]
if isinstance(le, ast.Expr):
last_expr = ast.Expression()
last_expr.body = field_[1].pop().value
elif isinstance(le, (ast.FunctionDef, ast.ClassDef)):
last_def_name = le.name
exec(compile(ast_, '<hbi-code>', 'exec'), context, defs)
if last_expr is not None:
return eval(compile(last_expr, '<hbi-code>', 'eval'), context, defs)
elif last_def_name is not None:
return defs[last_def_name]
return None
def visit_Assign(self, node):
if isinstance(self.node_stack[-2], ast.ClassDef):
# note: by hasattr below we're ignoring starred arguments, slices
# and tuples for simplicity.
assign_targets = {t.id for t in node.targets if hasattr(t, 'id')}
if '__metaclass__' in assign_targets:
self.errors.append(
B303(node.lineno, node.col_offset)
)
elif len(node.targets) == 1:
t = node.targets[0]
if isinstance(t, ast.Attribute) and isinstance(t.value, ast.Name):
if (t.value.id, t.attr) == ('os', 'environ'):
self.errors.append(
B003(node.lineno, node.col_offset)
)
self.generic_visit(node)