Commit 4978fea0 authored by Stefan Behnel's avatar Stefan Behnel

simplify auto __test__ dict generation:

store docstrings directly in the dict instead of looking them up at module init time
=> much faster, a lot less code, fewer redundant string constants (duplicate strings are unified anyway), and just as good, as docstrings of Cython functions/methods can't currently be changed anyway
parent ff27eac9
......@@ -18,91 +18,72 @@ class AutoTestDictTransform(ScopeTrackingTransform):
def visit_ModuleNode(self, node):
if node.is_pxd:
return node
self.scope_type = 'module'
self.scope_node = node
if self.current_directives['autotestdict']:
assert isinstance(node.body, StatListNode)
# First see if __test__ is already created
if u'__test__' in node.scope.entries:
# Do nothing
return node
pos = node.pos
if not self.current_directives['autotestdict']:
return node
assert isinstance(node.body, StatListNode)
self.tests = []
self.testspos = node.pos
# First see if __test__ is already created
if u'__test__' in node.scope.entries:
# Do nothing
return node
pos = node.pos
test_dict_entry = node.scope.declare_var(EncodedString(u'__test__'),
py_object_type,
pos,
visibility='public')
create_test_dict_assignment = SingleAssignmentNode(pos,
lhs=NameNode(pos, name=EncodedString(u'__test__'),
entry=test_dict_entry),
rhs=DictNode(pos, key_value_pairs=self.tests))
self.visitchildren(node)
node.body.stats.append(create_test_dict_assignment)
self.tests = []
self.testspos = node.pos
test_dict_entry = node.scope.declare_var(EncodedString(u'__test__'),
py_object_type,
pos,
visibility='public')
create_test_dict_assignment = SingleAssignmentNode(pos,
lhs=NameNode(pos, name=EncodedString(u'__test__'),
entry=test_dict_entry),
rhs=DictNode(pos, key_value_pairs=self.tests))
self.visitchildren(node)
node.body.stats.append(create_test_dict_assignment)
return node
def add_test(self, testpos, name, func_ref_node):
# func_ref_node must evaluate to the function object containing
# the docstring, BUT it should not be the function itself (which
# would lead to a new *definition* of the function)
def add_test(self, testpos, path, doctest):
pos = self.testspos
keystr = u'%s (line %d)' % (name, testpos[1])
keystr = u'%s (line %d)' % (path, testpos[1])
key = UnicodeNode(pos, value=EncodedString(keystr))
value = DocstringRefNode(pos, func_ref_node)
value = UnicodeNode(pos, value=EncodedString(doctest))
self.tests.append(DictItemNode(pos, key=key, value=value))
def visit_FuncDefNode(self, node):
if node.doc:
if isinstance(node, CFuncDefNode) and not node.py_func:
# skip non-cpdef cdef functions
if not node.doc:
return node
if isinstance(node, CFuncDefNode) and not node.py_func:
# skip non-cpdef cdef functions
return node
pos = self.testspos
if self.scope_type == 'module':
path = node.entry.name
elif self.scope_type in ('pyclass', 'cclass'):
if isinstance(node, CFuncDefNode):
name = node.py_func.name
else:
name = node.name
if self.scope_type == 'cclass' and name in self.blacklist:
return node
pos = self.testspos
if self.scope_type == 'module':
parent = ModuleRefNode(pos)
name = node.entry.name
elif self.scope_type in ('pyclass', 'cclass'):
if isinstance(node, CFuncDefNode):
name = node.py_func.name
else:
name = node.name
if self.scope_type == 'cclass' and name in self.blacklist:
return node
mod = ModuleRefNode(pos)
if self.scope_type == 'pyclass':
clsname = self.scope_node.name
else:
clsname = self.scope_node.class_name
parent = AttributeNode(pos, obj=mod,
attribute=clsname,
type=py_object_type,
is_py_attr=True,
is_temp=True)
if isinstance(node.entry.scope, Symtab.PropertyScope):
new_node = AttributeNode(pos, obj=parent,
attribute=node.entry.scope.name,
type=py_object_type,
is_py_attr=True,
is_temp=True)
parent = new_node
name = "%s.%s.%s" % (clsname, node.entry.scope.name,
node.entry.name)
else:
name = "%s.%s" % (clsname, node.entry.name)
if self.scope_type == 'pyclass':
class_name = self.scope_node.name
else:
class_name = self.scope_node.class_name
if isinstance(node.entry.scope, Symtab.PropertyScope):
property_method_name = node.entry.scope.name
path = "%s.%s.%s" % (class_name, node.entry.scope.name,
node.entry.name)
else:
assert False
getfunc = AttributeNode(pos, obj=parent,
attribute=node.entry.name,
type=py_object_type,
is_py_attr=True,
is_temp=True)
self.add_test(node.pos, name, getfunc)
path = "%s.%s" % (class_name, node.entry.name)
else:
assert False
self.add_test(node.pos, path, node.doc)
return node
......@@ -12,8 +12,8 @@ all_tests_run() is executed which does final validation.
>>> items.sort()
>>> for key, value in items:
... print('%s ; %s' % (key, value))
MyCdefClass.cpdef_method (line 78) ; >>> add_log("cpdef class method")
MyCdefClass.method (line 75) ; >>> add_log("cdef class method")
MyCdefClass.cpdef_method (line 79) ; >>> add_log("cpdef class method")
MyCdefClass.method (line 76) ; >>> add_log("cdef class method")
MyClass.method (line 65) ; >>> add_log("class method")
doc_without_test (line 47) ; Some docs
mycpdeffunc (line 53) ; >>> add_log("cpdef")
......@@ -33,7 +33,7 @@ cdef cdeffunc():
def all_tests_run():
log.sort()
assert log == [u'cdef class method', u'class method', u'cpdef', u'def'], log
assert log == [u'cdef class', u'cdef class method', u'class method', u'cpdef', u'cpdef class method', u'def'], log
def add_log(s):
log.append(unicode(s))
......@@ -68,7 +68,8 @@ class MyClass:
cdef class MyCdefClass:
"""
Needs no hack
>>> add_log("cdef class")
>>> True
True
"""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment