diff options
Diffstat (limited to 'coverage/misc.py')
-rw-r--r-- | coverage/misc.py | 125 |
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 |