summaryrefslogtreecommitdiff
path: root/coverage/misc.py
diff options
context:
space:
mode:
Diffstat (limited to 'coverage/misc.py')
-rw-r--r--coverage/misc.py125
1 files changed, 82 insertions, 43 deletions
diff --git a/coverage/misc.py b/coverage/misc.py
index d5197ea3..db6298b6 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -1,12 +1,64 @@
-"""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 locale
import os
+import sys
+import types
from coverage import env
-from coverage.backward import string_class, to_bytes
+from coverage.backward import string_class, to_bytes, unicode_class
+
+ISOLATED_MODULES = {}
+
+
+def isolate_module(mod):
+ """Copy a module so that we are isolated from aggressive mocking.
+
+ If a test suite mocks os.path.exists (for example), and then we need to use
+ it during the test, everything will get tangled up if we use their mock.
+ Making a copy of the module when we import it will isolate coverage.py from
+ those complications.
+ """
+ if mod not in ISOLATED_MODULES:
+ new_mod = types.ModuleType(mod.__name__)
+ ISOLATED_MODULES[mod] = new_mod
+ for name in dir(mod):
+ value = getattr(mod, name)
+ if isinstance(value, types.ModuleType):
+ value = isolate_module(value)
+ setattr(new_mod, name, value)
+ return ISOLATED_MODULES[mod]
+
+os = isolate_module(os)
+
+
+# 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
+
+ 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):
@@ -56,26 +108,25 @@ def format_lines(statements, lines):
return ret
-def short_stack(): # pragma: debugging
- """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 cache the result of an expensive operation.
+ """A decorator to indicate that a method shouldn't be called more than once.
- 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):
@@ -100,6 +151,18 @@ def file_be_gone(path):
raise
+def output_encoding(outfile=None):
+ """Determine the encoding to use for output written to `outfile` or stdout."""
+ if outfile is None:
+ outfile = sys.stdout
+ encoding = (
+ getattr(outfile, "encoding", None) or
+ getattr(sys.__stdout__, "encoding", None) or
+ locale.getpreferredencoding()
+ )
+ return encoding
+
+
class Hasher(object):
"""Hashes Python data into md5."""
def __init__(self):
@@ -139,30 +202,6 @@ class Hasher(object):
return self.md5.hexdigest()
-def overrides(obj, method_name, base_class):
- """Does `obj` override the `method_name` it got from `base_class`?
-
- Determine if `obj` implements the method called `method_name`, which it
- inherited from `base_class`.
-
- Returns a boolean.
-
- """
- klass = obj.__class__
- klass_func = getattr(klass, method_name)
- base_func = getattr(base_class, method_name)
-
- # Python 2/3 compatibility: Python 2 returns an instancemethod object, the
- # function is the .im_func attribute. Python 3 returns a plain function
- # object already.
- if env.PY2:
- klass_func = klass_func.im_func
- base_func = base_func.im_func
-
- return klass_func is not base_func
-
-
-# TODO: abc?
def _needs_to_implement(that, func_name):
"""Helper to raise NotImplementedError in interface stubs."""
if hasattr(that, "_coverage_plugin_name"):
@@ -181,7 +220,7 @@ def _needs_to_implement(that, func_name):
class CoverageException(Exception):
- """An exception specific to Coverage."""
+ """An exception specific to coverage.py."""
pass