From ee3e8d260f9fb7f31d4beb48548ccbd55f80e274 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Wed, 3 Nov 2010 10:20:29 +0100 Subject: [PATCH] new autotestdict.{cdef,all} directives that put cdef and non-doctest docstrings into __test__, skip non-doctest docstrings by default --HG-- rename : tests/run/autotestdict.pyx => tests/run/autotestdict_all.pyx rename : tests/run/autotestdict.pyx => tests/run/autotestdict_cdef.pyx --- Cython/Compiler/AnalysedTreeTransforms.py | 14 ++- Cython/Compiler/Options.py | 4 + tests/run/autotestdict.pyx | 30 +++-- tests/run/autotestdict_all.pyx | 144 ++++++++++++++++++++++ tests/run/autotestdict_cdef.pyx | 143 +++++++++++++++++++++ tests/run/autotestdict_skip.pyx | 4 +- 6 files changed, 317 insertions(+), 22 deletions(-) create mode 100644 tests/run/autotestdict_all.pyx create mode 100644 tests/run/autotestdict_cdef.pyx diff --git a/Cython/Compiler/AnalysedTreeTransforms.py b/Cython/Compiler/AnalysedTreeTransforms.py index 69667abf..38712049 100644 --- a/Cython/Compiler/AnalysedTreeTransforms.py +++ b/Cython/Compiler/AnalysedTreeTransforms.py @@ -18,12 +18,13 @@ class AutoTestDictTransform(ScopeTrackingTransform): def visit_ModuleNode(self, node): if node.is_pxd: return node - self.scope_type = 'module' self.scope_node = node if not self.current_directives['autotestdict']: return node + self.all_docstrings = self.current_directives['autotestdict.all'] + self.cdef_docstrings = self.all_docstrings or self.current_directives['autotestdict.cdef'] assert isinstance(node.body, StatListNode) @@ -59,8 +60,10 @@ class AutoTestDictTransform(ScopeTrackingTransform): def visit_FuncDefNode(self, node): if not node.doc: return node - if isinstance(node, CFuncDefNode) and not node.py_func: - # skip non-cpdef cdef functions + if not self.cdef_docstrings: + if isinstance(node, CFuncDefNode) and not node.py_func: + return node + if not self.all_docstrings and '>>>' not in node.doc: return node pos = self.testspos @@ -68,7 +71,10 @@ class AutoTestDictTransform(ScopeTrackingTransform): path = node.entry.name elif self.scope_type in ('pyclass', 'cclass'): if isinstance(node, CFuncDefNode): - name = node.py_func.name + if node.py_func is not None: + name = node.py_func.name + else: + name = node.entry.name else: name = node.name if self.scope_type == 'cclass' and name in self.blacklist: diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py index f36a33a0..a21fd8df 100644 --- a/Cython/Compiler/Options.py +++ b/Cython/Compiler/Options.py @@ -68,6 +68,8 @@ directive_defaults = { 'infer_types': None, 'infer_types.verbose': False, 'autotestdict': True, + 'autotestdict.cdef': False, + 'autotestdict.all': False, 'language_level': 2, 'warn': None, @@ -97,6 +99,8 @@ directive_scopes = { # defaults to available everywhere 'final' : ('cclass',), # add 'method' in the future 'internal' : ('cclass',), 'autotestdict' : ('module',), + 'autotestdict.all' : ('module',), + 'autotestdict.cdef' : ('module',), 'test_assert_path_exists' : ('function',), 'test_fail_if_path_exists' : ('function',), } diff --git a/tests/run/autotestdict.pyx b/tests/run/autotestdict.pyx index 0e3bc09e..1e740c08 100644 --- a/tests/run/autotestdict.pyx +++ b/tests/run/autotestdict.pyx @@ -1,23 +1,20 @@ # Directive defaults to True """ -Tests doctesthack compiler directive. +Tests autotestdict compiler directive. -The doctests are actually run as part of this test; -which makes the test flow a bit untraditional. Both -module test and individual tests are run; finally, +Both module test and individual tests are run; finally, all_tests_run() is executed which does final validation. >>> items = list(__test__.items()) >>> items.sort() >>> for key, value in items: ... print('%s ; %s' % (key, value)) -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") -myfunc (line 44) ; >>> add_log("def") +MyCdefClass.cpdef_method (line 76) ; >>> add_log("cpdef class method") +MyCdefClass.method (line 73) ; >>> add_log("cdef class method") +MyClass.method (line 62) ; >>> add_log("class method") +mycpdeffunc (line 49) ; >>> add_log("cpdef") +myfunc (line 40) ; >>> add_log("def") """ @@ -25,19 +22,18 @@ log = [] cdef cdeffunc(): """ - Please don't include me! - >>> True False """ +cdeffunc() # make sure it's being used def all_tests_run(): log.sort() - assert log == [u'cdef class', u'cdef class method', u'class method', u'cpdef', u'cpdef class method', u'def'], log + assert log == [u'cdef class', u'cdef class method', u'class', u'class method', u'cpdef', u'cpdef class method', u'def'], log def add_log(s): log.append(unicode(s)) - if len(log) == len(__test__): + if len(log) == len(__test__) + 2: # Final per-function doctest executed all_tests_run() @@ -58,6 +54,7 @@ class MyClass: """ Needs no hack + >>> add_log("class") >>> True True """ @@ -79,6 +76,9 @@ cdef class MyCdefClass: cpdef cpdef_method(self): """>>> add_log("cpdef class method")""" + cdef cdef_method(self): + """>>> add_log("cdef class method")""" + def __cinit__(self): """ Should not be included, as it can't be looked up with getattr @@ -142,5 +142,3 @@ cdef class MyOtherCdefClass: >>> True False """ - -cdeffunc() diff --git a/tests/run/autotestdict_all.pyx b/tests/run/autotestdict_all.pyx new file mode 100644 index 00000000..71deb439 --- /dev/null +++ b/tests/run/autotestdict_all.pyx @@ -0,0 +1,144 @@ +# cython: autotestdict.all=True + +""" +Tests autotestdict compiler directive. + +Both module test and individual tests are run; finally, +all_tests_run() is executed which does final validation. + +>>> items = list(__test__.items()) +>>> items.sort() +>>> for key, value in items: +... print('%s ; %s' % (key, value)) +MyCdefClass.cdef_method (line 79) ; >>> add_log("cdef class method") +MyCdefClass.cpdef_method (line 76) ; >>> add_log("cpdef class method") +MyCdefClass.method (line 73) ; >>> add_log("cdef class method") +MyClass.method (line 62) ; >>> add_log("class method") +cdeffunc (line 26) ; >>> add_log("cdef") +doc_without_test (line 43) ; Some docs +mycpdeffunc (line 49) ; >>> add_log("cpdef") +myfunc (line 40) ; >>> add_log("def") + +""" + +log = [] + +cdef cdeffunc(): + """>>> add_log("cdef")""" +cdeffunc() # make sure it's being used + +def all_tests_run(): + log.sort() + assert log == [u'cdef', u'cdef class', u'cdef class method', u'class', u'class method', u'cpdef', u'cpdef class method', u'def'], log + +def add_log(s): + log.append(unicode(s)) + if len(log) == len(__test__): + # Final per-function doctest executed + all_tests_run() + +def myfunc(): + """>>> add_log("def")""" + +def doc_without_test(): + """Some docs""" + +def nodocstring(): + pass + +cpdef mycpdeffunc(): + """>>> add_log("cpdef")""" + + +class MyClass: + """ + Needs no hack + + >>> add_log("class") + >>> True + True + """ + + def method(self): + """>>> add_log("class method")""" + +cdef class MyCdefClass: + """ + Needs no hack + + >>> add_log("cdef class") + >>> True + True + """ + def method(self): + """>>> add_log("cdef class method")""" + + cpdef cpdef_method(self): + """>>> add_log("cpdef class method")""" + + cdef cdef_method(self): + """>>> add_log("cdef class method")""" + + def __cinit__(self): + """ + Should not be included, as it can't be looked up with getattr + + >>> True + False + """ + + def __dealloc__(self): + """ + Should not be included, as it can't be looked up with getattr + + >>> True + False + """ + + def __richcmp__(self, other, int op): + """ + Should not be included, as it can't be looked up with getattr in Py 2 + + >>> True + False + """ + + def __nonzero__(self): + """ + Should not be included, as it can't be looked up with getattr in Py 3.1 + + >>> True + False + """ + + def __len__(self): + """ + Should not be included, as it can't be looked up with getattr in Py 3.1 + + >>> True + False + """ + + def __contains__(self, value): + """ + Should not be included, as it can't be looked up with getattr in Py 3.1 + + >>> True + False + """ + +cdef class MyOtherCdefClass: + """ + Needs no hack + + >>> True + True + """ + + def __bool__(self): + """ + Should not be included, as it can't be looked up with getattr in Py 2 + + >>> True + False + """ diff --git a/tests/run/autotestdict_cdef.pyx b/tests/run/autotestdict_cdef.pyx new file mode 100644 index 00000000..9ebd1c53 --- /dev/null +++ b/tests/run/autotestdict_cdef.pyx @@ -0,0 +1,143 @@ +# cython: autotestdict.cdef=True + +""" +Tests autotestdict compiler directive. + +Both module test and individual tests are run; finally, +all_tests_run() is executed which does final validation. + +>>> items = list(__test__.items()) +>>> items.sort() +>>> for key, value in items: +... print('%s ; %s' % (key, value)) +MyCdefClass.cdef_method (line 78) ; >>> add_log("cdef class method") +MyCdefClass.cpdef_method (line 75) ; >>> add_log("cpdef class method") +MyCdefClass.method (line 72) ; >>> add_log("cdef class method") +MyClass.method (line 61) ; >>> add_log("class method") +cdeffunc (line 25) ; >>> add_log("cdef") +mycpdeffunc (line 48) ; >>> add_log("cpdef") +myfunc (line 39) ; >>> add_log("def") + +""" + +log = [] + +cdef cdeffunc(): + """>>> add_log("cdef")""" +cdeffunc() # make sure it's being used + +def all_tests_run(): + log.sort() + assert log == [u'cdef', u'cdef class', u'cdef class method', u'class', u'class method', u'cpdef', u'cpdef class method', u'def'], log + +def add_log(s): + log.append(unicode(s)) + if len(log) == len(__test__) + 1: + # Final per-function doctest executed + all_tests_run() + +def myfunc(): + """>>> add_log("def")""" + +def doc_without_test(): + """Some docs""" + +def nodocstring(): + pass + +cpdef mycpdeffunc(): + """>>> add_log("cpdef")""" + + +class MyClass: + """ + Needs no hack + + >>> add_log("class") + >>> True + True + """ + + def method(self): + """>>> add_log("class method")""" + +cdef class MyCdefClass: + """ + Needs no hack + + >>> add_log("cdef class") + >>> True + True + """ + def method(self): + """>>> add_log("cdef class method")""" + + cpdef cpdef_method(self): + """>>> add_log("cpdef class method")""" + + cdef cdef_method(self): + """>>> add_log("cdef class method")""" + + def __cinit__(self): + """ + Should not be included, as it can't be looked up with getattr + + >>> True + False + """ + + def __dealloc__(self): + """ + Should not be included, as it can't be looked up with getattr + + >>> True + False + """ + + def __richcmp__(self, other, int op): + """ + Should not be included, as it can't be looked up with getattr in Py 2 + + >>> True + False + """ + + def __nonzero__(self): + """ + Should not be included, as it can't be looked up with getattr in Py 3.1 + + >>> True + False + """ + + def __len__(self): + """ + Should not be included, as it can't be looked up with getattr in Py 3.1 + + >>> True + False + """ + + def __contains__(self, value): + """ + Should not be included, as it can't be looked up with getattr in Py 3.1 + + >>> True + False + """ + +cdef class MyOtherCdefClass: + """ + Needs no hack + + >>> True + True + """ + + def __bool__(self): + """ + Should not be included, as it can't be looked up with getattr in Py 2 + + >>> True + False + """ diff --git a/tests/run/autotestdict_skip.pyx b/tests/run/autotestdict_skip.pyx index 8770b666..9042606d 100644 --- a/tests/run/autotestdict_skip.pyx +++ b/tests/run/autotestdict_skip.pyx @@ -1,6 +1,6 @@ -#cython: doctesthack=True +#cython: autotestdict=True """ -Tests that doctesthack doesn't come into effect when +Tests that autotestdict doesn't come into effect when a __test__ is defined manually. If this doesn't work, then the function doctest should fail. -- 2.26.2