DebugClients/Python/coverage/misc.py

changeset 4491
0d8612e24fef
parent 4489
d0d6e4ad31bd
child 5051
3586ebd9fac8
diff -r 4ba7a8ab24f2 -r 0d8612e24fef DebugClients/Python/coverage/misc.py
--- a/DebugClients/Python/coverage/misc.py	Sat Oct 10 12:06:10 2015 +0200
+++ b/DebugClients/Python/coverage/misc.py	Sat Oct 10 12:44:52 2015 +0200
@@ -1,12 +1,38 @@
-"""Miscellaneous stuff for Coverage."""
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Miscellaneous stuff for coverage.py."""
 
 import errno
+import hashlib
 import inspect
 import os
-import sys
+
+from coverage import env
+from coverage.backward import string_class, to_bytes, unicode_class
+
+
+# Use PyContracts for assertion testing on parameters and returns, but only if
+# we are running our own test suite.
+if env.TESTING:
+    from contracts import contract              # pylint: disable=unused-import
+    from contracts import new_contract
 
-from .backward import md5, sorted       # pylint: disable=W0622
-from .backward import string_class, to_bytes
+    try:
+        # Define contract words that PyContract doesn't have.
+        new_contract('bytes', lambda v: isinstance(v, bytes))
+        if env.PY3:
+            new_contract('unicode', lambda v: isinstance(v, unicode_class))
+    except ValueError:
+        # During meta-coverage, this module is imported twice, and PyContracts
+        # doesn't like redefining contracts. It's OK.
+        pass
+else:                                           # pragma: not covered
+    # We aren't using real PyContracts, so just define a no-op decorator as a
+    # stunt double.
+    def contract(**unused):
+        """Dummy no-op implementation of `contract`."""
+        return lambda func: func
 
 
 def nice_pair(pair):
@@ -42,7 +68,7 @@
     lines = sorted(lines)
     while i < len(statements) and j < len(lines):
         if statements[i] == lines[j]:
-            if start == None:
+            if start is None:
                 start = lines[j]
             end = lines[j]
             j += 1
@@ -56,25 +82,25 @@
     return ret
 
 
-def short_stack():
-    """Return a string summarizing the call stack."""
-    stack = inspect.stack()[:0:-1]
-    return "\n".join(["%30s : %s @%d" % (t[3],t[1],t[2]) for t in stack])
+def expensive(fn):
+    """A decorator to indicate that a method shouldn't be called more than once.
 
-
-def expensive(fn):
-    """A decorator to cache the result of an expensive operation.
-
-    Only applies to methods with no arguments.
+    Normally, this does nothing.  During testing, this raises an exception if
+    called more than once.
 
     """
-    attr = "_cache_" + fn.__name__
-    def _wrapped(self):
-        """Inner fn that checks the cache."""
-        if not hasattr(self, attr):
-            setattr(self, attr, fn(self))
-        return getattr(self, attr)
-    return _wrapped
+    if env.TESTING:
+        attr = "_once_" + fn.__name__
+
+        def _wrapped(self):
+            """Inner function that checks the cache."""
+            if hasattr(self, attr):
+                raise Exception("Shouldn't have called %s more than once" % fn.__name__)
+            setattr(self, attr, True)
+            return fn(self)
+        return _wrapped
+    else:
+        return fn
 
 
 def bool_or_none(b):
@@ -87,20 +113,14 @@
 
 def join_regex(regexes):
     """Combine a list of regexes into one that matches any of them."""
-    if len(regexes) > 1:
-        return "|".join(["(%s)" % r for r in regexes])
-    elif regexes:
-        return regexes[0]
-    else:
-        return ""
+    return "|".join("(?:%s)" % r for r in regexes)
 
 
 def file_be_gone(path):
     """Remove a file, and don't get annoyed if it doesn't exist."""
     try:
         os.remove(path)
-    except OSError:
-        _, e, _ = sys.exc_info()
+    except OSError as e:
         if e.errno != errno.ENOENT:
             raise
 
@@ -108,13 +128,15 @@
 class Hasher(object):
     """Hashes Python data into md5."""
     def __init__(self):
-        self.md5 = md5()
+        self.md5 = hashlib.md5()
 
     def update(self, v):
         """Add `v` to the hash, recursively if needed."""
         self.md5.update(to_bytes(str(type(v))))
         if isinstance(v, string_class):
             self.md5.update(to_bytes(v))
+        elif isinstance(v, bytes):
+            self.md5.update(v)
         elif v is None:
             pass
         elif isinstance(v, (int, float)):
@@ -137,27 +159,48 @@
                 self.update(k)
                 self.update(a)
 
-    def digest(self):
-        """Retrieve the digest of the hash."""
-        return self.md5.digest()
+    def hexdigest(self):
+        """Retrieve the hex digest of the hash."""
+        return self.md5.hexdigest()
+
+
+def _needs_to_implement(that, func_name):
+    """Helper to raise NotImplementedError in interface stubs."""
+    if hasattr(that, "_coverage_plugin_name"):
+        thing = "Plugin"
+        name = that._coverage_plugin_name
+    else:
+        thing = "Class"
+        klass = that.__class__
+        name = "{klass.__module__}.{klass.__name__}".format(klass=klass)
+
+    raise NotImplementedError(
+        "{thing} {name!r} needs to implement {func_name}()".format(
+            thing=thing, name=name, func_name=func_name
+            )
+        )
 
 
 class CoverageException(Exception):
-    """An exception specific to Coverage."""
+    """An exception specific to coverage.py."""
     pass
 
+
 class NoSource(CoverageException):
     """We couldn't find the source for a module."""
     pass
 
+
 class NoCode(NoSource):
     """We couldn't find any code at all."""
     pass
 
+
 class NotPython(CoverageException):
     """A source file turned out not to be parsable Python."""
     pass
 
+
 class ExceptionDuringRun(CoverageException):
     """An exception happened while running customer code.
 

eric ide

mercurial