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,9 +18,13 @@ class AutoTestDictTransform(ScopeTrackingTransform): ...@@ -18,9 +18,13 @@ 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']:
if not self.current_directives['autotestdict']:
return node
assert isinstance(node.body, StatListNode) assert isinstance(node.body, StatListNode)
# First see if __test__ is already created # First see if __test__ is already created
...@@ -43,31 +47,25 @@ class AutoTestDictTransform(ScopeTrackingTransform): ...@@ -43,31 +47,25 @@ class AutoTestDictTransform(ScopeTrackingTransform):
rhs=DictNode(pos, key_value_pairs=self.tests)) rhs=DictNode(pos, key_value_pairs=self.tests))
self.visitchildren(node) self.visitchildren(node)
node.body.stats.append(create_test_dict_assignment) 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:
return node
if isinstance(node, CFuncDefNode) and not node.py_func: if isinstance(node, CFuncDefNode) and not node.py_func:
# skip non-cpdef cdef functions # skip non-cpdef cdef functions
return node return node
pos = self.testspos pos = self.testspos
if self.scope_type == 'module': if self.scope_type == 'module':
parent = ModuleRefNode(pos) path = node.entry.name
name = node.entry.name
elif self.scope_type in ('pyclass', 'cclass'): elif self.scope_type in ('pyclass', 'cclass'):
if isinstance(node, CFuncDefNode): if isinstance(node, CFuncDefNode):
name = node.py_func.name name = node.py_func.name
...@@ -75,34 +73,17 @@ class AutoTestDictTransform(ScopeTrackingTransform): ...@@ -75,34 +73,17 @@ class AutoTestDictTransform(ScopeTrackingTransform):
name = node.name name = node.name
if self.scope_type == 'cclass' and name in self.blacklist: if self.scope_type == 'cclass' and name in self.blacklist:
return node return node
mod = ModuleRefNode(pos)
if self.scope_type == 'pyclass': if self.scope_type == 'pyclass':
clsname = self.scope_node.name class_name = self.scope_node.name
else: else:
clsname = self.scope_node.class_name class_name = 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): if isinstance(node.entry.scope, Symtab.PropertyScope):
new_node = AttributeNode(pos, obj=parent, property_method_name = node.entry.scope.name
attribute=node.entry.scope.name, path = "%s.%s.%s" % (class_name, 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) node.entry.name)
else: else:
name = "%s.%s" % (clsname, node.entry.name) path = "%s.%s" % (class_name, node.entry.name)
else: else:
assert False assert False
getfunc = AttributeNode(pos, obj=parent, self.add_test(node.pos, path, node.doc)
attribute=node.entry.name,
type=py_object_type,
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))
...@@ -69,6 +69,7 @@ cdef class MyCdefClass: ...@@ -69,6 +69,7 @@ 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