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): ...@@ -18,91 +18,72 @@ class AutoTestDictTransform(ScopeTrackingTransform):
def visit_ModuleNode(self, node): def visit_ModuleNode(self, node):
if node.is_pxd: if node.is_pxd:
return node return node
self.scope_type = 'module' self.scope_type = 'module'
self.scope_node = node self.scope_node = node
if self.current_directives['autotestdict']:
assert isinstance(node.body, StatListNode)
# First see if __test__ is already created if not self.current_directives['autotestdict']:
if u'__test__' in node.scope.entries: return node
# Do nothing
return node assert isinstance(node.body, StatListNode)
pos = node.pos
self.tests = [] # First see if __test__ is already created
self.testspos = node.pos if u'__test__' in node.scope.entries:
# Do nothing
return node
pos = node.pos
test_dict_entry = node.scope.declare_var(EncodedString(u'__test__'), self.tests = []
py_object_type, self.testspos = node.pos
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)
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 return node
def add_test(self, testpos, name, func_ref_node): def add_test(self, testpos, path, doctest):
# 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)
pos = self.testspos 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)) key = UnicodeNode(pos, value=EncodedString(keystr))
value = UnicodeNode(pos, value=EncodedString(doctest))
value = DocstringRefNode(pos, func_ref_node)
self.tests.append(DictItemNode(pos, key=key, value=value)) self.tests.append(DictItemNode(pos, key=key, value=value))
def visit_FuncDefNode(self, node): def visit_FuncDefNode(self, node):
if node.doc: if not node.doc:
if isinstance(node, CFuncDefNode) and not node.py_func: return node
# skip non-cpdef cdef functions 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 return node
if self.scope_type == 'pyclass':
pos = self.testspos class_name = self.scope_node.name
if self.scope_type == 'module': else:
parent = ModuleRefNode(pos) class_name = self.scope_node.class_name
name = node.entry.name if isinstance(node.entry.scope, Symtab.PropertyScope):
elif self.scope_type in ('pyclass', 'cclass'): property_method_name = node.entry.scope.name
if isinstance(node, CFuncDefNode): path = "%s.%s.%s" % (class_name, node.entry.scope.name,
name = node.py_func.name node.entry.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)
else: else:
assert False path = "%s.%s" % (class_name, node.entry.name)
getfunc = AttributeNode(pos, obj=parent, else:
attribute=node.entry.name, assert False
type=py_object_type, self.add_test(node.pos, path, node.doc)
is_py_attr=True,
is_temp=True)
self.add_test(node.pos, name, getfunc)
return node return node
...@@ -12,8 +12,8 @@ all_tests_run() is executed which does final validation. ...@@ -12,8 +12,8 @@ all_tests_run() is executed which does final validation.
>>> items.sort() >>> items.sort()
>>> for key, value in items: >>> for key, value in items:
... print('%s ; %s' % (key, value)) ... print('%s ; %s' % (key, value))
MyCdefClass.cpdef_method (line 78) ; >>> add_log("cpdef class method") MyCdefClass.cpdef_method (line 79) ; >>> add_log("cpdef class method")
MyCdefClass.method (line 75) ; >>> add_log("cdef class method") MyCdefClass.method (line 76) ; >>> add_log("cdef class method")
MyClass.method (line 65) ; >>> add_log("class method") MyClass.method (line 65) ; >>> add_log("class method")
doc_without_test (line 47) ; Some docs doc_without_test (line 47) ; Some docs
mycpdeffunc (line 53) ; >>> add_log("cpdef") mycpdeffunc (line 53) ; >>> add_log("cpdef")
...@@ -33,7 +33,7 @@ cdef cdeffunc(): ...@@ -33,7 +33,7 @@ cdef cdeffunc():
def all_tests_run(): def all_tests_run():
log.sort() 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): def add_log(s):
log.append(unicode(s)) log.append(unicode(s))
...@@ -68,7 +68,8 @@ class MyClass: ...@@ -68,7 +68,8 @@ class MyClass:
cdef class MyCdefClass: cdef class MyCdefClass:
""" """
Needs no hack Needs no hack
>>> add_log("cdef class")
>>> True >>> True
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