Commit 833e3b2c authored by Dag Sverre Seljebotn's avatar Dag Sverre Seljebotn

doctesthack directive

parent 5117b05e
from Cython.Compiler.Visitor import VisitorTransform, CythonTransform, TreeVisitor from Cython.Compiler.Visitor import VisitorTransform, ScopeTrackingTransform, TreeVisitor
from Nodes import StatListNode, SingleAssignmentNode from Nodes import StatListNode, SingleAssignmentNode
from ExprNodes import (DictNode, DictItemNode, NameNode, UnicodeNode, NoneNode, from ExprNodes import (DictNode, DictItemNode, NameNode, UnicodeNode, NoneNode,
ExprNode, AttributeNode) ExprNode, AttributeNode)
...@@ -7,12 +7,20 @@ from Builtin import dict_type ...@@ -7,12 +7,20 @@ from Builtin import dict_type
from StringEncoding import EncodedString from StringEncoding import EncodedString
import Naming import Naming
class DoctestHackTransform(CythonTransform): class DoctestHackTransform(ScopeTrackingTransform):
# Handles doctesthack directive # Handles doctesthack directive
def visit_ModuleNode(self, node): def visit_ModuleNode(self, node):
self.scope_type = 'module'
self.scope_node = node
if self.current_directives['doctesthack']: if self.current_directives['doctesthack']:
assert isinstance(node.body, StatListNode) 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 pos = node.pos
self.tests = [] self.tests = []
...@@ -32,26 +40,41 @@ class DoctestHackTransform(CythonTransform): ...@@ -32,26 +40,41 @@ class DoctestHackTransform(CythonTransform):
return node return node
def add_test(self, testpos, name, funcname): 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)
pos = self.testspos pos = self.testspos
keystr = u'%s (line %d)' % (name, testpos[1]) keystr = u'%s (line %d)' % (name, testpos[1])
key = UnicodeNode(pos, value=EncodedString(keystr)) key = UnicodeNode(pos, value=EncodedString(keystr))
getfunc = AttributeNode(pos, obj=ModuleRefNode(pos), value = DocstringRefNode(pos, func_ref_node)
attribute=funcname,
type=py_object_type,
is_py_attr=True,
is_temp=True)
value = DocstringRefNode(pos, getfunc)
self.tests.append(DictItemNode(pos, key=key, value=value)) self.tests.append(DictItemNode(pos, key=key, value=value))
def visit_ClassDefNode(self, node):
return node
def visit_FuncDefNode(self, node): def visit_FuncDefNode(self, node):
if node.doc: if node.doc:
self.add_test(node.pos, node.entry.name, node.entry.name) pos = self.testspos
if self.scope_type == 'module':
parent = ModuleRefNode(pos)
name = node.entry.name
elif self.scope_type in ('pyclass', 'cclass'):
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)
name = "%s.%s" % (clsname, node.entry.name)
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)
return node return node
......
...@@ -275,6 +275,37 @@ class CythonTransform(VisitorTransform): ...@@ -275,6 +275,37 @@ class CythonTransform(VisitorTransform):
self.visitchildren(node) self.visitchildren(node)
return node return node
class ScopeTrackingTransform(CythonTransform):
# Keeps track of type of scopes
scope_type = None # can be either of 'module', 'function', 'cclass', 'pyclass'
scope_node = None
def visit_ModuleNode(self, node):
self.scope_type = 'module'
self.scope_node = node
self.visitchildren(node)
return node
def visit_scope(self, node, scope_type):
prev = self.scope_type, self.scope_node
self.scope_type = scope_type
self.scope_node = node
self.visitchildren(node)
self.scope_type, self.scope_node = prev
return node
def visit_CClassDefNode(self, node):
return self.visit_scope(node, 'cclass')
def visit_PyClassDefNode(self, node):
return self.visit_scope(node, 'pyclass')
def visit_FuncDefNode(self, node):
return self.visit_scope(node, 'function')
def visit_CStructOrUnionDefNode(self, node):
return self.visit_scope(node, 'struct')
......
...@@ -12,18 +12,20 @@ all_tests_run() is executed which does final validation. ...@@ -12,18 +12,20 @@ all_tests_run() is executed which does final validation.
>>> items.sort() >>> items.sort()
>>> for key, value in items: >>> for key, value in items:
... print key, ';', value ... print key, ';', value
mycpdeffunc (line 40) ; >>> add_log("cpdef") MyCdefClass.method (line 67) ; >>> add_log("cdef class method")
myfunc (line 34) ; >>> add_log("def") MyClass.method (line 57) ; >>> add_log("class method")
doc_without_test (line 39) ; Some docs
mycpdeffunc (line 45) ; >>> add_log("cpdef")
myfunc (line 36) ; >>> add_log("def")
""" """
log = [] log = []
#__test__ = {'a':'445', 'b':'3'}
def all_tests_run(): def all_tests_run():
log.sort() log.sort()
assert log == [u'cpdef', u'def'], log assert log == [u'cdef class method', u'class method', u'cpdef', u'def'], log
def add_log(s): def add_log(s):
log.append(unicode(s)) log.append(unicode(s))
...@@ -34,6 +36,9 @@ def add_log(s): ...@@ -34,6 +36,9 @@ def add_log(s):
def myfunc(): def myfunc():
""">>> add_log("def")""" """>>> add_log("def")"""
def doc_without_test():
"""Some docs"""
def nodocstring(): def nodocstring():
pass pass
...@@ -50,17 +55,15 @@ class MyClass: ...@@ -50,17 +55,15 @@ class MyClass:
""" """
def method(self): def method(self):
""">>> add_log("class method")"""
cdef class MyCdefClass:
""" """
Needs no hack
>>> True >>> True
False True
""" """
def method(self):
## cdef class MyCdefClass: """>>> add_log("cdef class method")"""
## """
## >>> add_log("cdef class")
## """
## def method(self):
## """
## >>> add_log("cdef class method")
## """
#cython: doctesthack=True
"""
Tests that doctesthack doesn't come into effect when
a __test__ is defined manually.
If this doesn't work, then the function doctest should fail.
>>> True
True
"""
def func():
"""
>>> True
False
"""
__test__ = {
u"one" : """
>>> 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