[Checkins] SVN: z3c.testsetup/trunk/ - Test files that we attempt to read but that do not exist raise an error

Reinout van Rees reinout at vanrees.org
Thu Nov 19 06:36:24 EST 2009


Log message for revision 105870:
  - Test files that we attempt to read but that do not exist raise an error
    instead of passing silently.
  
  - Also fixed the tests as several non-existing files were tested (with false
    results).  That try/except IOError was horrible.
  
  - Internal refactoring: regex caching.
  
  

Changed:
  U   z3c.testsetup/trunk/CHANGES.txt
  U   z3c.testsetup/trunk/src/z3c/testsetup/base.py
  U   z3c.testsetup/trunk/src/z3c/testsetup/basicsetup.txt
  U   z3c.testsetup/trunk/src/z3c/testsetup/functional/functionaldoctestsetup.txt
  U   z3c.testsetup/trunk/src/z3c/testsetup/pythontestsetup.txt
  U   z3c.testsetup/trunk/src/z3c/testsetup/testing.py
  U   z3c.testsetup/trunk/src/z3c/testsetup/unitdoctestsetup.txt
  U   z3c.testsetup/trunk/src/z3c/testsetup/util.py

-=-
Modified: z3c.testsetup/trunk/CHANGES.txt
===================================================================
--- z3c.testsetup/trunk/CHANGES.txt	2009-11-19 10:02:48 UTC (rev 105869)
+++ z3c.testsetup/trunk/CHANGES.txt	2009-11-19 11:36:24 UTC (rev 105870)
@@ -4,9 +4,12 @@
 0.7 (unreleased)
 ================
 
-- Nothing changed yet.
+- Test files that we attempt to read but that do not exist raise an error
+  instead of passing silently.
 
+- Internal refactoring: regex caching.
 
+
 0.6 (2009-11-19)
 ================
 

Modified: z3c.testsetup/trunk/src/z3c/testsetup/base.py
===================================================================
--- z3c.testsetup/trunk/src/z3c/testsetup/base.py	2009-11-19 10:02:48 UTC (rev 105869)
+++ z3c.testsetup/trunk/src/z3c/testsetup/base.py	2009-11-19 11:36:24 UTC (rev 105870)
@@ -29,9 +29,7 @@
     """
 
     extensions = ['.rst', '.txt']
-
     regexp_list = []
-
     additional_options = {}
 
     param_list = ['filter_func', 'extensions']
@@ -41,7 +39,8 @@
         self.package = get_package(package)
         self.filter_func = filter_func or self.isTestFile
         self.extensions = extensions or self.extensions
-        self.regexp_list = regexp_list or self.regexp_list
+        if regexp_list is not None:
+            self.regexp_list = regexp_list
         self.additional_options = kw
         self._init(package, filter_func, extensions, **kw)
         return
@@ -52,30 +51,40 @@
         """
         pass
 
+    @property
+    def regexs(self):
+        """Return compiled regexs (cached version, if possible)"""
+        cached = getattr(self, '_regexs', None)
+        if cached is not None:
+            return cached
+        self._regexs = [re.compile(regex) for regex in self.regexp_list]
+        return self._regexs
+
     def setUp(self, test):
         pass
 
     def tearDown(self, test):
         pass
 
-    def fileContains(self, filename, regexp_list):
+    def fileContains(self, filename):
         """Does a file contain lines matching every of the regular
         expressions?
         """
         found_list = []
-        try:
-            for line in open(filename):
-                for regexp in regexp_list:
-                    if re.compile(regexp).match(line) and (
-                        regexp not in found_list):
-                        found_list.append(regexp)
-                if len(regexp_list) == len(found_list):
-                    break
-        except IOError:
-            # be gentle
-            pass
-        return len(regexp_list) == len(found_list)
+        content = open(filename).read()
+        return self.textContains(content)
 
+    def textContains(self, text):
+        lines = text.split('\n')
+        for regexp in self.regexs:
+            found = [True for line in lines if regexp.match(line)]
+            if len(found):
+                # Yeah, found a match, continue to the next regex.
+                continue
+            else:
+                return False
+        return True
+
     def isTestFile(self, filepath):
         """Return ``True`` if a file matches our expectations for a
         doctest file.
@@ -85,7 +94,7 @@
         if os.path.basename(filepath).startswith('.'):
             # Ignore *nix hidden files
             return False
-        if not self.fileContains(filepath, self.regexp_list):
+        if not self.fileContains(filepath):
             return False
         return True
 

Modified: z3c.testsetup/trunk/src/z3c/testsetup/basicsetup.txt
===================================================================
--- z3c.testsetup/trunk/src/z3c/testsetup/basicsetup.txt	2009-11-19 10:02:48 UTC (rev 105869)
+++ z3c.testsetup/trunk/src/z3c/testsetup/basicsetup.txt	2009-11-19 11:36:24 UTC (rev 105870)
@@ -124,15 +124,6 @@
    >>> basic_setup.isTestFile('')
    False
 
-   >>> basic_setup.isTestFile('cave.txt')
-   True
-
-   >>> basic_setup.isTestFile('cave.rst')
-   True
-
-   >>> basic_setup.isTestFile('cave.RsT')
-   True
-
    >>> basic_setup.isTestFile('cave.foo')
    False
 
@@ -215,11 +206,14 @@
 complex implementation that also accesses other instance attributes
 like the package or similar.
 
-File paths, that cannot be found, are silently ignored::
+File paths, that cannot be found, are treated as real errors as they signal an
+error in the way we set everything up:
 
    >>> basic_setup4 = BasicTestSetup(cave, extensions=['.foo'])
-   >>> basic_setup4.fileContains('blah', ['FOO'])
-   False
+   >>> basic_setup4.fileContains('blah')
+   Traceback (most recent call last):
+   ...
+   IOError: [Errno 2] No such file or directory: 'blah'
 
 We pick up an existing file path::
 
@@ -234,40 +228,54 @@
 This file contains a string 'ME GROK SMASH ZCML!!', which we can
 search for::
 
-   >>> basic_setup4.fileContains(filepath, ['ME GROK'])
+   >>> basic_setup4.regexp_list = ['ME GROK']
+   >>> basic_setup4._regexs = None # Zap regex cache
+   >>> basic_setup4.fileContains(filepath)
    True
 
-   >>> basic_setup4.fileContains(filepath, ['ME GROK IS DUMB'])
+   >>> basic_setup4.regexp_list = ['ME GROK IS DUMB']
+   >>> basic_setup4._regexs = None # Zap regex cache
+   >>> basic_setup4.fileContains(filepath)
    False
 
 The terms to search are handled as real regular expressions as
 provided by the ``re`` package::
 
-   >>> basic_setup4.fileContains(filepath, ['^ME (G|g)ROK.*'])
+   >>> basic_setup4.regexp_list = ['^ME (G|g)ROK.*']
+   >>> basic_setup4._regexs = None # Zap regex cache
+   >>> basic_setup4.fileContains(filepath)
    True
 
-We can also search for several matches::
+We can also search for several matches (which ALL have to be present):
 
-   >>> basic_setup4.fileContains(filepath, ['.*SMASH.*', '.*GROK.*'])
+   >>> basic_setup4.regexp_list = ['.*SMASH.*', '.*GROK.*']
+   >>> basic_setup4._regexs = None # Zap regex cache
+   >>> basic_setup4.fileContains(filepath)
    True
 
 If one of the searched terms is not found, the whole thing fails::
 
-   >>> basic_setup4.fileContains(filepath, ['.*DUMB.*', '.*GROK.*'])
+   >>> basic_setup4.regexp_list = ['.*DUMB.*', '.*GROK.*']
+   >>> basic_setup4._regexs = None # Zap regex cache
+   >>> basic_setup4.fileContains(filepath)
    False
 
 It does not matter, whether matches occur in the same line or in
 different ones. In the example file there is also a heading stating
 'This is not a test'. Let's check this::
 
-   >>> basic_setup4.fileContains(filepath, ['ME GROK', 'This is not'])
+   >>> basic_setup4.regexp_list = ['ME GROK', 'This is not']
+   >>> basic_setup4._regexs = None # Zap regex cache
+   >>> basic_setup4.fileContains(filepath)
    True
 
 
 Note: The evaluation of regular expressions is done without any
 modifiers. Namely the matching is case sensitive::
 
-   >>> basic_setup4.fileContains(filepath, ['me grok'])
+   >>> basic_setup4.regexp_list = ['me grok']
+   >>> basic_setup4._regexs = None # Zap regex cache
+   >>> basic_setup4.fileContains(filepath)
    False
 
 Furthermore, matches are only done against one line at a time.

Modified: z3c.testsetup/trunk/src/z3c/testsetup/functional/functionaldoctestsetup.txt
===================================================================
--- z3c.testsetup/trunk/src/z3c/testsetup/functional/functionaldoctestsetup.txt	2009-11-19 10:02:48 UTC (rev 105869)
+++ z3c.testsetup/trunk/src/z3c/testsetup/functional/functionaldoctestsetup.txt	2009-11-19 11:36:24 UTC (rev 105870)
@@ -262,14 +262,6 @@
     >>> setup.isTestFile(testfile_list[0])
     True
 
-The file file1.rst does not contain a functional test marker::
-
-    >>> import os.path
-    >>> path = os.path.join(os.path.dirname(cave.__file__),
-    ...                     'test1.rst')
-    >>> setup.isTestFile(path)
-    False
-
 The `regexp_list` attribute of a ``FunctionalTestSetup`` contains a
 list of regular expressions, of which each one must at least match one
 line of a searched file to be accepted. If you want to include files
@@ -277,6 +269,12 @@
 will influence behaviour of the `isTestFile()``, ``getDocTestFiles()``
 and ``getTestSuite()`` methods.
 
+Note that the code uses self.regexs, which returns a cached compiled version
+of the regexs.
+
+    >>> setup.regexs
+    [<_sre.SRE_Pattern object at ...>]
+    
 If you need more complex checks here, you can derive your customized
 test setup class and overwrite ``isTestFile()``.
 
@@ -464,6 +462,7 @@
 To show this, we first create a custom layer::
 
    >>> from zope.app.testing.functional import ZCMLLayer
+   >>> import os.path
    >>> mylayer = ZCMLLayer(
    ...     os.path.join(os.path.dirname(__file__), 'ftesting.zcml'),
    ...     __name__,

Modified: z3c.testsetup/trunk/src/z3c/testsetup/pythontestsetup.txt
===================================================================
--- z3c.testsetup/trunk/src/z3c/testsetup/pythontestsetup.txt	2009-11-19 10:02:48 UTC (rev 105869)
+++ z3c.testsetup/trunk/src/z3c/testsetup/pythontestsetup.txt	2009-11-19 11:36:24 UTC (rev 105870)
@@ -218,6 +218,7 @@
 and we want its tests to be registered::
 
     >>> setup.regexp_list = ['^\\s*:(T|t)est-(L|l)ayer:\\s*(F|false)\\s*']
+    >>> setup._regexs = None # Zap internal regex cache
 
 Now we fetch the module list again::
 

Modified: z3c.testsetup/trunk/src/z3c/testsetup/testing.py
===================================================================
--- z3c.testsetup/trunk/src/z3c/testsetup/testing.py	2009-11-19 10:02:48 UTC (rev 105869)
+++ z3c.testsetup/trunk/src/z3c/testsetup/testing.py	2009-11-19 11:36:24 UTC (rev 105870)
@@ -45,7 +45,7 @@
         self.pfilter_func = pfilter_func or self.isTestModule
         self.filter_func = self.pfilter_func
 
-    def docstrContains(self, docstr, regexp_list):
+    def docstrContains(self, docstr):
         """Does a docstring contain lines matching every of the regular
         expressions?
         """
@@ -54,14 +54,7 @@
             return False
         if get_marker_from_string('unittest', docstr) is not None:
             return True
-        for line in docstr.split('\n'):
-            for regexp in regexp_list:
-                if re.compile(regexp).match(line) and (
-                    regexp not in found_list):
-                    found_list.append(regexp)
-            if len(regexp_list) == len(found_list):
-                break
-        return len(regexp_list) == len(found_list)
+        return self.textContains(docstr)
 
     def isTestModule(self, module_info):
         """Return ``True`` if a module matches our expectations for a
@@ -71,8 +64,7 @@
         each of our regular expressions.
         """
         # Do not even try to load modules, that have no marker string.
-        if not self.fileContains(
-            module_info.path, self.regexp_list):
+        if not self.fileContains(module_info.path):
             # No ":test-layer: python" marker, so check for :unittest:.
             if get_marker_from_file('unittest', module_info.path) is None:
                 # Neither the old nor the new marker: this is no test module.
@@ -85,7 +77,7 @@
             # warn about this!
             print "Import error in", module_info.path
         docstr = getattr(module, '__doc__', '')
-        if not self.docstrContains(docstr, self.regexp_list):
+        if not self.docstrContains(docstr):
             return False
         return True
 

Modified: z3c.testsetup/trunk/src/z3c/testsetup/unitdoctestsetup.txt
===================================================================
--- z3c.testsetup/trunk/src/z3c/testsetup/unitdoctestsetup.txt	2009-11-19 10:02:48 UTC (rev 105869)
+++ z3c.testsetup/trunk/src/z3c/testsetup/unitdoctestsetup.txt	2009-11-19 11:36:24 UTC (rev 105870)
@@ -216,14 +216,6 @@
     >>> setup.isTestFile(testfile_list[0])
     True
 
-The file file1.txt does not contain a unit test marker::
-
-    >>> import os.path
-    >>> path = os.path.join(os.path.dirname(cave.__file__),
-    ...                     'test1.txt')
-    >>> setup.isTestFile(path)
-    False
-
 The `regexp_list` attribute of a ``UnitDocTestSetup`` contains a
 list of regular expressions, of which each one must at least match one
 line of a searched file to be accepted. If you want to include files
@@ -231,6 +223,8 @@
 will influence behaviour of the `isTestFile()``, ``getDocTestFiles()``
 and ``getTestSuite()`` methods.
 
+Note: in practice in the current code, it is always only one regex...
+
 If you need more complex checks here, you can derive your customized
 test setup class and overwrite ``isTestFile()``.
 

Modified: z3c.testsetup/trunk/src/z3c/testsetup/util.py
===================================================================
--- z3c.testsetup/trunk/src/z3c/testsetup/util.py	2009-11-19 10:02:48 UTC (rev 105869)
+++ z3c.testsetup/trunk/src/z3c/testsetup/util.py	2009-11-19 11:36:24 UTC (rev 105870)
@@ -15,9 +15,11 @@
 """
 import sys
 import re
+
 from inspect import getmro, ismethod, getargspec
 from martian.scan import resolve
 
+
 def get_package(pkg_or_dotted_name):
     """Get a package denoted by the given argument.
 
@@ -32,6 +34,7 @@
         pkg = resolve(pkg)
     return pkg
 
+
 def get_keyword_params(cls, method_name):
     """Get a list of args of a method of a class.
 
@@ -54,6 +57,9 @@
             break
     return list(result)
 
+marker_regexs = {}
+
+
 def get_marker_from_string(marker, text):
     """Looks for a markerstring  in a string.
 
@@ -64,19 +70,22 @@
     or
 
      .. :<Tag>: <Value>
-    
+
     """
     marker = ":%s:" % marker.lower()
-    rexp = re.compile('^(\.\.\s+)?%s(.*)$' % (marker,), re.IGNORECASE)
+    if marker not in marker_regexs:
+        marker_regexs[marker] = re.compile('^(\.\.\s+)?%s(.*)$' % (marker,),
+                                           re.IGNORECASE)
     for line in text.split('\n'):
         line = line.strip()
-        result = rexp.match(line)
+        result = marker_regexs[marker].match(line)
         if result is None:
             continue
         result = result.groups()[1].strip()
         return unicode(result)
     return None
 
+
 def get_marker_from_file(marker, filepath):
     """Looks for a markerstring  in a file.
 
@@ -87,15 +96,17 @@
     """
     return get_marker_from_string(marker, open(filepath, 'rb').read())
 
+
 def warn(text):
     print "Warning: ", text
 
+
 def import_name(name):
     __import__(name)
     return sys.modules[name]
 
+
 def get_attribute(name):
     name, attr = name.rsplit('.', 1)
     obj = import_name(name)
     return getattr(obj, attr)
-



More information about the checkins mailing list