diff options
author | ttt | 2017-05-13 00:29:47 +0530 |
---|---|---|
committer | ttt | 2017-05-13 00:29:47 +0530 |
commit | abf599be33b383a6a5baf9493093b2126a622ac8 (patch) | |
tree | 4c5ab6e0d935d5e65fabcf0258e4a00dd20a5afa /lib/python2.7/site-packages/django/test | |
download | SBHS-2018-Rpi-abf599be33b383a6a5baf9493093b2126a622ac8.tar.gz SBHS-2018-Rpi-abf599be33b383a6a5baf9493093b2126a622ac8.tar.bz2 SBHS-2018-Rpi-abf599be33b383a6a5baf9493093b2126a622ac8.zip |
added all server files
Diffstat (limited to 'lib/python2.7/site-packages/django/test')
-rw-r--r-- | lib/python2.7/site-packages/django/test/__init__.py | 10 | ||||
-rw-r--r-- | lib/python2.7/site-packages/django/test/_doctest.py | 2755 | ||||
-rw-r--r-- | lib/python2.7/site-packages/django/test/client.py | 623 | ||||
-rw-r--r-- | lib/python2.7/site-packages/django/test/html.py | 238 | ||||
-rw-r--r-- | lib/python2.7/site-packages/django/test/runner.py | 291 | ||||
-rw-r--r-- | lib/python2.7/site-packages/django/test/signals.py | 81 | ||||
-rw-r--r-- | lib/python2.7/site-packages/django/test/simple.py | 253 | ||||
-rw-r--r-- | lib/python2.7/site-packages/django/test/testcases.py | 1208 | ||||
-rw-r--r-- | lib/python2.7/site-packages/django/test/utils.py | 469 |
9 files changed, 5928 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/django/test/__init__.py b/lib/python2.7/site-packages/django/test/__init__.py new file mode 100644 index 0000000..7a49875 --- /dev/null +++ b/lib/python2.7/site-packages/django/test/__init__.py @@ -0,0 +1,10 @@ +""" +Django Unit Test and Doctest framework. +""" + +from django.test.client import Client, RequestFactory +from django.test.testcases import (TestCase, TransactionTestCase, + SimpleTestCase, LiveServerTestCase, skipIfDBFeature, + skipUnlessDBFeature +) +from django.test.utils import Approximate diff --git a/lib/python2.7/site-packages/django/test/_doctest.py b/lib/python2.7/site-packages/django/test/_doctest.py new file mode 100644 index 0000000..5381cff --- /dev/null +++ b/lib/python2.7/site-packages/django/test/_doctest.py @@ -0,0 +1,2755 @@ +# This is a slightly modified version of the doctest.py that shipped with Python 2.5 +# It incorporates changes that have been submitted to the Python ticket tracker +# as ticket #1521051. These changes allow for a DoctestRunner and Doctest base +# class to be specified when constructing a DoctestSuite. + +# Module doctest. +# Released to the public domain 16-Jan-2001, by Tim Peters (tim@python.org). +# Major enhancements and refactoring by: +# Jim Fulton +# Edward Loper + +# Provided as-is; use at your own risk; no warranty; no promises; enjoy! + +r"""Module doctest -- a framework for running examples in docstrings. + +In simplest use, end each module M to be tested with: + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() + +Then running the module as a script will cause the examples in the +docstrings to get executed and verified: + +python M.py + +This won't display anything unless an example fails, in which case the +failing example(s) and the cause(s) of the failure(s) are printed to stdout +(why not stderr? because stderr is a lame hack <0.2 wink>), and the final +line of output is "Test failed.". + +Run it with the -v switch instead: + +python M.py -v + +and a detailed report of all examples tried is printed to stdout, along +with assorted summaries at the end. + +You can force verbose mode by passing "verbose=True" to testmod, or prohibit +it by passing "verbose=False". In either of those cases, sys.argv is not +examined by testmod. + +There are a variety of other ways to run doctests, including integration +with the unittest framework, and support for running non-Python text +files containing doctests. There are also many ways to override parts +of doctest's default behaviors. See the Library Reference Manual for +details. +""" +import warnings + +warnings.warn( + "The django.test._doctest module is deprecated; " + "use the doctest module from the Python standard library instead.", + PendingDeprecationWarning) + + +__docformat__ = 'reStructuredText en' + +__all__ = [ + # 0, Option Flags + 'register_optionflag', + 'DONT_ACCEPT_TRUE_FOR_1', + 'DONT_ACCEPT_BLANKLINE', + 'NORMALIZE_WHITESPACE', + 'ELLIPSIS', + 'SKIP', + 'IGNORE_EXCEPTION_DETAIL', + 'COMPARISON_FLAGS', + 'REPORT_UDIFF', + 'REPORT_CDIFF', + 'REPORT_NDIFF', + 'REPORT_ONLY_FIRST_FAILURE', + 'REPORTING_FLAGS', + # 1. Utility Functions + # 2. Example & DocTest + 'Example', + 'DocTest', + # 3. Doctest Parser + 'DocTestParser', + # 4. Doctest Finder + 'DocTestFinder', + # 5. Doctest Runner + 'DocTestRunner', + 'OutputChecker', + 'DocTestFailure', + 'UnexpectedException', + 'DebugRunner', + # 6. Test Functions + 'testmod', + 'testfile', + 'run_docstring_examples', + # 7. Tester + 'Tester', + # 8. Unittest Support + 'DocTestSuite', + 'DocFileSuite', + 'set_unittest_reportflags', + # 9. Debugging Support + 'script_from_examples', + 'testsource', + 'debug_src', + 'debug', +] + +import __future__ + +import sys, traceback, inspect, linecache, os, re +import unittest, difflib, pdb, tempfile +import warnings + +from django.utils import six +from django.utils.six.moves import StringIO, xrange + +if sys.platform.startswith('java'): + # On Jython, isclass() reports some modules as classes. Patch it. + def patch_isclass(isclass): + def patched_isclass(obj): + return isclass(obj) and hasattr(obj, '__module__') + return patched_isclass + inspect.isclass = patch_isclass(inspect.isclass) + +# There are 4 basic classes: +# - Example: a <source, want> pair, plus an intra-docstring line number. +# - DocTest: a collection of examples, parsed from a docstring, plus +# info about where the docstring came from (name, filename, lineno). +# - DocTestFinder: extracts DocTests from a given object's docstring and +# its contained objects' docstrings. +# - DocTestRunner: runs DocTest cases, and accumulates statistics. +# +# So the basic picture is: +# +# list of: +# +------+ +---------+ +-------+ +# |object| --DocTestFinder-> | DocTest | --DocTestRunner-> |results| +# +------+ +---------+ +-------+ +# | Example | +# | ... | +# | Example | +# +---------+ + +# Option constants. + +OPTIONFLAGS_BY_NAME = {} +def register_optionflag(name): + # Create a new flag unless `name` is already known. + return OPTIONFLAGS_BY_NAME.setdefault(name, 1 << len(OPTIONFLAGS_BY_NAME)) + +DONT_ACCEPT_TRUE_FOR_1 = register_optionflag('DONT_ACCEPT_TRUE_FOR_1') +DONT_ACCEPT_BLANKLINE = register_optionflag('DONT_ACCEPT_BLANKLINE') +NORMALIZE_WHITESPACE = register_optionflag('NORMALIZE_WHITESPACE') +ELLIPSIS = register_optionflag('ELLIPSIS') +SKIP = register_optionflag('SKIP') +IGNORE_EXCEPTION_DETAIL = register_optionflag('IGNORE_EXCEPTION_DETAIL') + +COMPARISON_FLAGS = (DONT_ACCEPT_TRUE_FOR_1 | + DONT_ACCEPT_BLANKLINE | + NORMALIZE_WHITESPACE | + ELLIPSIS | + SKIP | + IGNORE_EXCEPTION_DETAIL) + +REPORT_UDIFF = register_optionflag('REPORT_UDIFF') +REPORT_CDIFF = register_optionflag('REPORT_CDIFF') +REPORT_NDIFF = register_optionflag('REPORT_NDIFF') +REPORT_ONLY_FIRST_FAILURE = register_optionflag('REPORT_ONLY_FIRST_FAILURE') + +REPORTING_FLAGS = (REPORT_UDIFF | + REPORT_CDIFF | + REPORT_NDIFF | + REPORT_ONLY_FIRST_FAILURE) + +# Special string markers for use in `want` strings: +BLANKLINE_MARKER = '<BLANKLINE>' +ELLIPSIS_MARKER = '...' + +###################################################################### +## Table of Contents +###################################################################### +# 1. Utility Functions +# 2. Example & DocTest -- store test cases +# 3. DocTest Parser -- extracts examples from strings +# 4. DocTest Finder -- extracts test cases from objects +# 5. DocTest Runner -- runs test cases +# 6. Test Functions -- convenient wrappers for testing +# 7. Tester Class -- for backwards compatibility +# 8. Unittest Support +# 9. Debugging Support +# 10. Example Usage + +###################################################################### +## 1. Utility Functions +###################################################################### + +def _extract_future_flags(globs): + """ + Return the compiler-flags associated with the future features that + have been imported into the given namespace (globs). + """ + flags = 0 + for fname in __future__.all_feature_names: + feature = globs.get(fname, None) + if feature is getattr(__future__, fname): + flags |= feature.compiler_flag + return flags + +def _normalize_module(module, depth=2): + """ + Return the module specified by `module`. In particular: + - If `module` is a module, then return module. + - If `module` is a string, then import and return the + module with that name. + - If `module` is None, then return the calling module. + The calling module is assumed to be the module of + the stack frame at the given depth in the call stack. + """ + if inspect.ismodule(module): + return module + elif isinstance(module, six.string_types): + return __import__(module, globals(), locals(), ["*"]) + elif module is None: + return sys.modules[sys._getframe(depth).f_globals['__name__']] + else: + raise TypeError("Expected a module, string, or None") + +def _load_testfile(filename, package, module_relative): + if module_relative: + package = _normalize_module(package, 3) + filename = _module_relative_path(package, filename) + if hasattr(package, '__loader__'): + if hasattr(package.__loader__, 'get_data'): + file_contents = package.__loader__.get_data(filename) + # get_data() opens files as 'rb', so one must do the equivalent + # conversion as universal newlines would do. + return file_contents.replace(os.linesep, '\n'), filename + with open(filename) as fp: + return fp.read(), filename + +def _indent(s, indent=4): + """ + Add the given number of space characters to the beginning every + non-blank line in `s`, and return the result. + """ + # This regexp matches the start of non-blank lines: + return re.sub('(?m)^(?!$)', indent*' ', s) + +def _exception_traceback(exc_info): + """ + Return a string containing a traceback message for the given + exc_info tuple (as returned by sys.exc_info()). + """ + # Get a traceback message. + excout = StringIO() + exc_type, exc_val, exc_tb = exc_info + traceback.print_exception(exc_type, exc_val, exc_tb, file=excout) + return excout.getvalue() + +# Override some StringIO methods. +class _SpoofOut(StringIO): + def getvalue(self): + result = StringIO.getvalue(self) + # If anything at all was written, make sure there's a trailing + # newline. There's no way for the expected output to indicate + # that a trailing newline is missing. + if result and not result.endswith("\n"): + result += "\n" + # Prevent softspace from screwing up the next test case, in + # case they used print with a trailing comma in an example. + if hasattr(self, "softspace"): + del self.softspace + return result + + def truncate(self, size=None): + StringIO.truncate(self, size) + if hasattr(self, "softspace"): + del self.softspace + +# Worst-case linear-time ellipsis matching. +def _ellipsis_match(want, got): + """ + Essentially the only subtle case: + >>> _ellipsis_match('aa...aa', 'aaa') + False + """ + if ELLIPSIS_MARKER not in want: + return want == got + + # Find "the real" strings. + ws = want.split(ELLIPSIS_MARKER) + assert len(ws) >= 2 + + # Deal with exact matches possibly needed at one or both ends. + startpos, endpos = 0, len(got) + w = ws[0] + if w: # starts with exact match + if got.startswith(w): + startpos = len(w) + del ws[0] + else: + return False + w = ws[-1] + if w: # ends with exact match + if got.endswith(w): + endpos -= len(w) + del ws[-1] + else: + return False + + if startpos > endpos: + # Exact end matches required more characters than we have, as in + # _ellipsis_match('aa...aa', 'aaa') + return False + + # For the rest, we only need to find the leftmost non-overlapping + # match for each piece. If there's no overall match that way alone, + # there's no overall match period. + for w in ws: + # w may be '' at times, if there are consecutive ellipses, or + # due to an ellipsis at the start or end of `want`. That's OK. + # Search for an empty string succeeds, and doesn't change startpos. + startpos = got.find(w, startpos, endpos) + if startpos < 0: + return False + startpos += len(w) + + return True + +def _comment_line(line): + "Return a commented form of the given line" + line = line.rstrip() + if line: + return '# '+line + else: + return '#' + +class _OutputRedirectingPdb(pdb.Pdb): + """ + A specialized version of the python debugger that redirects stdout + to a given stream when interacting with the user. Stdout is *not* + redirected when traced code is executed. + """ + def __init__(self, out): + self.__out = out + self.__debugger_used = False + pdb.Pdb.__init__(self, stdout=out) + + def set_trace(self, frame=None): + self.__debugger_used = True + if frame is None: + frame = sys._getframe().f_back + pdb.Pdb.set_trace(self, frame) + + def set_continue(self): + # Calling set_continue unconditionally would break unit test + # coverage reporting, as Bdb.set_continue calls sys.settrace(None). + if self.__debugger_used: + pdb.Pdb.set_continue(self) + + def trace_dispatch(self, *args): + # Redirect stdout to the given stream. + save_stdout = sys.stdout + sys.stdout = self.__out + # Call Pdb's trace dispatch method. + try: + return pdb.Pdb.trace_dispatch(self, *args) + finally: + sys.stdout = save_stdout + +# [XX] Normalize with respect to os.path.pardir? +def _module_relative_path(module, path): + if not inspect.ismodule(module): + raise TypeError('Expected a module: %r' % module) + if path.startswith('/'): + raise ValueError('Module-relative files may not have absolute paths') + + # Find the base directory for the path. + if hasattr(module, '__file__'): + # A normal module/package + basedir = os.path.split(module.__file__)[0] + elif module.__name__ == '__main__': + # An interactive session. + if len(sys.argv)>0 and sys.argv[0] != '': + basedir = os.path.split(sys.argv[0])[0] + else: + basedir = os.curdir + else: + # A module w/o __file__ (this includes builtins) + raise ValueError("Can't resolve paths relative to the module " + + module + " (it has no __file__)") + + # Combine the base directory and the path. + return os.path.join(basedir, *(path.split('/'))) + +###################################################################### +## 2. Example & DocTest +###################################################################### +## - An "example" is a <source, want> pair, where "source" is a +## fragment of source code, and "want" is the expected output for +## "source." The Example class also includes information about +## where the example was extracted from. +## +## - A "doctest" is a collection of examples, typically extracted from +## a string (such as an object's docstring). The DocTest class also +## includes information about where the string was extracted from. + +class Example: + """ + A single doctest example, consisting of source code and expected + output. `Example` defines the following attributes: + + - source: A single Python statement, always ending with a newline. + The constructor adds a newline if needed. + + - want: The expected output from running the source code (either + from stdout, or a traceback in case of exception). `want` ends + with a newline unless it's empty, in which case it's an empty + string. The constructor adds a newline if needed. + + - exc_msg: The exception message generated by the example, if + the example is expected to generate an exception; or `None` if + it is not expected to generate an exception. This exception + message is compared against the return value of + `traceback.format_exception_only()`. `exc_msg` ends with a + newline unless it's `None`. The constructor adds a newline + if needed. + + - lineno: The line number within the DocTest string containing + this Example where the Example begins. This line number is + zero-based, with respect to the beginning of the DocTest. + + - indent: The example's indentation in the DocTest string. + I.e., the number of space characters that preceed the + example's first prompt. + + - options: A dictionary mapping from option flags to True or + False, which is used to override default options for this + example. Any option flags not contained in this dictionary + are left at their default value (as specified by the + DocTestRunner's optionflags). By default, no options are set. + """ + def __init__(self, source, want, exc_msg=None, lineno=0, indent=0, + options=None): + # Normalize inputs. + if not source.endswith('\n'): + source += '\n' + if want and not want.endswith('\n'): + want += '\n' + if exc_msg is not None and not exc_msg.endswith('\n'): + exc_msg += '\n' + # Store properties. + self.source = source + self.want = want + self.lineno = lineno + self.indent = indent + if options is None: options = {} + self.options = options + self.exc_msg = exc_msg + +class DocTest: + """ + A collection of doctest examples that should be run in a single + namespace. Each `DocTest` defines the following attributes: + + - examples: the list of examples. + + - globs: The namespace (aka globals) that the examples should + be run in. + + - name: A name identifying the DocTest (typically, the name of + the object whose docstring this DocTest was extracted from). + + - filename: The name of the file that this DocTest was extracted + from, or `None` if the filename is unknown. + + - lineno: The line number within filename where this DocTest + begins, or `None` if the line number is unavailable. This + line number is zero-based, with respect to the beginning of + the file. + + - docstring: The string that the examples were extracted from, + or `None` if the string is unavailable. + """ + def __init__(self, examples, globs, name, filename, lineno, docstring): + """ + Create a new DocTest containing the given examples. The + DocTest's globals are initialized with a copy of `globs`. + """ + assert not isinstance(examples, six.string_types), \ + "DocTest no longer accepts str; use DocTestParser instead" + self.examples = examples + self.docstring = docstring + self.globs = globs.copy() + self.name = name + self.filename = filename + self.lineno = lineno + + def __repr__(self): + if len(self.examples) == 0: + examples = 'no examples' + elif len(self.examples) == 1: + examples = '1 example' + else: + examples = '%d examples' % len(self.examples) + return ('<DocTest %s from %s:%s (%s)>' % + (self.name, self.filename, self.lineno, examples)) + + + # This lets us sort tests by name: + def _cmpkey(self): + return (self.name, self.filename, self.lineno, id(self)) + def __cmp__(self, other): + if not isinstance(other, DocTest): + return -1 + return cmp(self._cmpkey(), other._cmpkey()) + + def __lt__(self, other): + return self._cmpkey() < other._cmpkey() + + def __le__(self, other): + return self._cmpkey() <= other._cmpkey() + + def __gt__(self, other): + return self._cmpkey() > other._cmpkey() + + def __ge__(self, other): + return self._cmpkey() >= other._cmpkey() + + def __eq__(self, other): + return self._cmpkey() == other._cmpkey() + + def __ne__(self, other): + return self._cmpkey() != other._cmpkey() + + +###################################################################### +## 3. DocTestParser +###################################################################### + +class DocTestParser: + """ + A class used to parse strings containing doctest examples. + """ + # This regular expression is used to find doctest examples in a + # string. It defines three groups: `source` is the source code + # (including leading indentation and prompts); `indent` is the + # indentation of the first (PS1) line of the source code; and + # `want` is the expected output (including leading indentation). + _EXAMPLE_RE = re.compile(r''' + # Source consists of a PS1 line followed by zero or more PS2 lines. + (?P<source> + (?:^(?P<indent> [ ]*) >>> .*) # PS1 line + (?:\n [ ]* \.\.\. .*)*) # PS2 lines + \n? + # Want consists of any non-blank lines that do not start with PS1. + (?P<want> (?:(?![ ]*$) # Not a blank line + (?![ ]*>>>) # Not a line starting with PS1 + .*$\n? # But any other line + )*) + ''', re.MULTILINE | re.VERBOSE) + + # A regular expression for handling `want` strings that contain + # expected exceptions. It divides `want` into three pieces: + # - the traceback header line (`hdr`) + # - the traceback stack (`stack`) + # - the exception message (`msg`), as generated by + # traceback.format_exception_only() + # `msg` may have multiple lines. We assume/require that the + # exception message is the first non-indented line starting with a word + # character following the traceback header line. + _EXCEPTION_RE = re.compile(r""" + # Grab the traceback header. Different versions of Python have + # said different things on the first traceback line. + ^(?P<hdr> Traceback\ \( + (?: most\ recent\ call\ last + | innermost\ last + ) \) : + ) + \s* $ # toss trailing whitespace on the header. + (?P<stack> .*?) # don't blink: absorb stuff until... + ^ (?P<msg> \w+ .*) # a line *starts* with alphanum. + """, re.VERBOSE | re.MULTILINE | re.DOTALL) + + # A callable returning a true value if its argument is a blank line + # or contains a single comment. + _IS_BLANK_OR_COMMENT = re.compile(r'^[ ]*(#.*)?$').match + + def parse(self, string, name='<string>'): + """ + Divide the given string into examples and intervening text, + and return them as a list of alternating Examples and strings. + Line numbers for the Examples are 0-based. The optional + argument `name` is a name identifying this string, and is only + used for error messages. + """ + string = string.expandtabs() + # If all lines begin with the same indentation, then strip it. + min_indent = self._min_indent(string) + if min_indent > 0: + string = '\n'.join([l[min_indent:] for l in string.split('\n')]) + + output = [] + charno, lineno = 0, 0 + # Find all doctest examples in the string: + for m in self._EXAMPLE_RE.finditer(string): + # Add the pre-example text to `output`. + output.append(string[charno:m.start()]) + # Update lineno (lines before this example) + lineno += string.count('\n', charno, m.start()) + # Extract info from the regexp match. + (source, options, want, exc_msg) = \ + self._parse_example(m, name, lineno) + # Create an Example, and add it to the list. + if not self._IS_BLANK_OR_COMMENT(source): + output.append( Example(source, want, exc_msg, + lineno=lineno, + indent=min_indent+len(m.group('indent')), + options=options) ) + # Update lineno (lines inside this example) + lineno += string.count('\n', m.start(), m.end()) + # Update charno. + charno = m.end() + # Add any remaining post-example text to `output`. + output.append(string[charno:]) + return output + + def get_doctest(self, string, globs, name, filename, lineno): + """ + Extract all doctest examples from the given string, and + collect them into a `DocTest` object. + + `globs`, `name`, `filename`, and `lineno` are attributes for + the new `DocTest` object. See the documentation for `DocTest` + for more information. + """ + return DocTest(self.get_examples(string, name), globs, + name, filename, lineno, string) + + def get_examples(self, string, name='<string>'): + """ + Extract all doctest examples from the given string, and return + them as a list of `Example` objects. Line numbers are + 0-based, because it's most common in doctests that nothing + interesting appears on the same line as opening triple-quote, + and so the first interesting line is called \"line 1\" then. + + The optional argument `name` is a name identifying this + string, and is only used for error messages. + """ + return [x for x in self.parse(string, name) + if isinstance(x, Example)] + + def _parse_example(self, m, name, lineno): + """ + Given a regular expression match from `_EXAMPLE_RE` (`m`), + return a pair `(source, want)`, where `source` is the matched + example's source code (with prompts and indentation stripped); + and `want` is the example's expected output (with indentation + stripped). + + `name` is the string's name, and `lineno` is the line number + where the example starts; both are used for error messages. + """ + # Get the example's indentation level. + indent = len(m.group('indent')) + + # Divide source into lines; check that they're properly + # indented; and then strip their indentation & prompts. + source_lines = m.group('source').split('\n') + self._check_prompt_blank(source_lines, indent, name, lineno) + self._check_prefix(source_lines[1:], ' '*indent + '.', name, lineno) + source = '\n'.join([sl[indent+4:] for sl in source_lines]) + + # Divide want into lines; check that it's properly indented; and + # then strip the indentation. Spaces before the last newline should + # be preserved, so plain rstrip() isn't good enough. + want = m.group('want') + want_lines = want.split('\n') + if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): + del want_lines[-1] # forget final newline & spaces after it + self._check_prefix(want_lines, ' '*indent, name, + lineno + len(source_lines)) + want = '\n'.join([wl[indent:] for wl in want_lines]) + + # If `want` contains a traceback message, then extract it. + m = self._EXCEPTION_RE.match(want) + if m: + exc_msg = m.group('msg') + else: + exc_msg = None + + # Extract options from the source. + options = self._find_options(source, name, lineno) + + return source, options, want, exc_msg + + # This regular expression looks for option directives in the + # source code of an example. Option directives are comments + # starting with "doctest:". Warning: this may give false + # positives for string-literals that contain the string + # "#doctest:". Eliminating these false positives would require + # actually parsing the string; but we limit them by ignoring any + # line containing "#doctest:" that is *followed* by a quote mark. + _OPTION_DIRECTIVE_RE = re.compile(r'#\s*doctest:\s*([^\n\'"]*)$', + re.MULTILINE) + + def _find_options(self, source, name, lineno): + """ + Return a dictionary containing option overrides extracted from + option directives in the given source string. + + `name` is the string's name, and `lineno` is the line number + where the example starts; both are used for error messages. + """ + options = {} + # (note: with the current regexp, this will match at most once:) + for m in self._OPTION_DIRECTIVE_RE.finditer(source): + option_strings = m.group(1).replace(',', ' ').split() + for option in option_strings: + if (option[0] not in '+-' or + option[1:] not in OPTIONFLAGS_BY_NAME): + raise ValueError('line %r of the doctest for %s ' + 'has an invalid option: %r' % + (lineno+1, name, option)) + flag = OPTIONFLAGS_BY_NAME[option[1:]] + options[flag] = (option[0] == '+') + if options and self._IS_BLANK_OR_COMMENT(source): + raise ValueError('line %r of the doctest for %s has an option ' + 'directive on a line with no example: %r' % + (lineno, name, source)) + return options + + # This regular expression finds the indentation of every non-blank + # line in a string. + _INDENT_RE = re.compile('^([ ]*)(?=\S)', re.MULTILINE) + + def _min_indent(self, s): + "Return the minimum indentation of any non-blank line in `s`" + indents = [len(indent) for indent in self._INDENT_RE.findall(s)] + if len(indents) > 0: + return min(indents) + else: + return 0 + + def _check_prompt_blank(self, lines, indent, name, lineno): + """ + Given the lines of a source string (including prompts and + leading indentation), check to make sure that every prompt is + followed by a space character. If any line is not followed by + a space character, then raise ValueError. + """ + for i, line in enumerate(lines): + if len(line) >= indent+4 and line[indent+3] != ' ': + raise ValueError('line %r of the docstring for %s ' + 'lacks blank after %s: %r' % + (lineno+i+1, name, + line[indent:indent+3], line)) + + def _check_prefix(self, lines, prefix, name, lineno): + """ + Check that every line in the given list starts with the given + prefix; if any line does not, then raise a ValueError. + """ + for i, line in enumerate(lines): + if line and not line.startswith(prefix): + raise ValueError('line %r of the docstring for %s has ' + 'inconsistent leading whitespace: %r' % + (lineno+i+1, name, line)) + + +###################################################################### +## 4. DocTest Finder +###################################################################### + +class DocTestFinder: + """ + A class used to extract the DocTests that are relevant to a given + object, from its docstring and the docstrings of its contained + objects. Doctests can currently be extracted from the following + object types: modules, functions, classes, methods, staticmethods, + classmethods, and properties. + """ + + def __init__(self, verbose=False, parser=DocTestParser(), + recurse=True, exclude_empty=True): + """ + Create a new doctest finder. + + The optional argument `parser` specifies a class or + function that should be used to create new DocTest objects (or + objects that implement the same interface as DocTest). The + signature for this factory function should match the signature + of the DocTest constructor. + + If the optional argument `recurse` is false, then `find` will + only examine the given object, and not any contained objects. + + If the optional argument `exclude_empty` is false, then `find` + will include tests for objects with empty docstrings. + """ + self._parser = parser + self._verbose = verbose + self._recurse = recurse + self._exclude_empty = exclude_empty + + def find(self, obj, name=None, module=None, globs=None, extraglobs=None): + """ + Return a list of the DocTests that are defined by the given + object's docstring, or by any of its contained objects' + docstrings. + + The optional parameter `module` is the module that contains + the given object. If the module is not specified or is None, then + the test finder will attempt to automatically determine the + correct module. The object's module is used: + + - As a default namespace, if `globs` is not specified. + - To prevent the DocTestFinder from extracting DocTests + from objects that are imported from other modules. + - To find the name of the file containing the object. + - To help find the line number of the object within its + file. + + Contained objects whose module does not match `module` are ignored. + + If `module` is False, no attempt to find the module will be made. + This is obscure, of use mostly in tests: if `module` is False, or + is None but cannot be found automatically, then all objects are + considered to belong to the (non-existent) module, so all contained + objects will (recursively) be searched for doctests. + + The globals for each DocTest is formed by combining `globs` + and `extraglobs` (bindings in `extraglobs` override bindings + in `globs`). A new copy of the globals dictionary is created + for each DocTest. If `globs` is not specified, then it + defaults to the module's `__dict__`, if specified, or {} + otherwise. If `extraglobs` is not specified, then it defaults + to {}. + + """ + # If name was not specified, then extract it from the object. + if name is None: + name = getattr(obj, '__name__', None) + if name is None: + raise ValueError("DocTestFinder.find: name must be given " + "when obj.__name__ doesn't exist: %r" % + (type(obj),)) + + # Find the module that contains the given object (if obj is + # a module, then module=obj.). Note: this may fail, in which + # case module will be None. + if module is False: + module = None + elif module is None: + module = inspect.getmodule(obj) + + # Read the module's source code. This is used by + # DocTestFinder._find_lineno to find the line number for a + # given object's docstring. + try: + file = inspect.getsourcefile(obj) or inspect.getfile(obj) + source_lines = linecache.getlines(file) + if not source_lines: + source_lines = None + except TypeError: + source_lines = None + + # Initialize globals, and merge in extraglobs. + if globs is None: + if module is None: + globs = {} + else: + globs = module.__dict__.copy() + else: + globs = globs.copy() + if extraglobs is not None: + globs.update(extraglobs) + + # Recursively explore `obj`, extracting DocTests. + tests = [] + self._find(tests, obj, name, module, source_lines, globs, {}) + return tests + + def _from_module(self, module, object): + """ + Return true if the given object is defined in the given + module. + """ + if module is None: + return True + elif inspect.isfunction(object): + return module.__dict__ is six.get_function_globals(object) + elif inspect.isclass(object): + return module.__name__ == object.__module__ + elif inspect.getmodule(object) is not None: + return module is inspect.getmodule(object) + elif hasattr(object, '__module__'): + return module.__name__ == object.__module__ + elif isinstance(object, property): + return True # [XX] no way not be sure. + else: + raise ValueError("object must be a class or function") + + def _find(self, tests, obj, name, module, source_lines, globs, seen): + """ + Find tests for the given object and any contained objects, and + add them to `tests`. + """ + if self._verbose: + print('Finding tests in %s' % name) + + # If we've already processed this object, then ignore it. + if id(obj) in seen: + return + seen[id(obj)] = 1 + + # Find a test for this object, and add it to the list of tests. + test = self._get_test(obj, name, module, globs, source_lines) + if test is not None: + tests.append(test) + + # Look for tests in a module's contained objects. + if inspect.ismodule(obj) and self._recurse: + for valname, val in obj.__dict__.items(): + valname = '%s.%s' % (name, valname) + # Recurse to functions & classes. + if ((inspect.isfunction(val) or inspect.isclass(val)) and + self._from_module(module, val)): + self._find(tests, val, valname, module, source_lines, + globs, seen) + + # Look for tests in a module's __test__ dictionary. + if inspect.ismodule(obj) and self._recurse: + for valname, val in getattr(obj, '__test__', {}).items(): + if not isinstance(valname, six.string_types): + raise ValueError("DocTestFinder.find: __test__ keys " + "must be strings: %r" % + (type(valname),)) + if not (inspect.isfunction(val) or inspect.isclass(val) or + inspect.ismethod(val) or inspect.ismodule(val) or + isinstance(val, six.string_types)): + raise ValueError("DocTestFinder.find: __test__ values " + "must be strings, functions, methods, " + "classes, or modules: %r" % + (type(val),)) + valname = '%s.__test__.%s' % (name, valname) + self._find(tests, val, valname, module, source_lines, + globs, seen) + + # Look for tests in a class's contained objects. + if inspect.isclass(obj) and self._recurse: + for valname, val in obj.__dict__.items(): + # Special handling for staticmethod/classmethod. + if isinstance(val, staticmethod): + val = getattr(obj, valname) + if isinstance(val, classmethod): + val = getattr(obj, valname).__func__ + + # Recurse to methods, properties, and nested classes. + if ((inspect.isfunction(val) or inspect.isclass(val) or + isinstance(val, property)) and + self._from_module(module, val)): + valname = '%s.%s' % (name, valname) + self._find(tests, val, valname, module, source_lines, + globs, seen) + + def _get_test(self, obj, name, module, globs, source_lines): + """ + Return a DocTest for the given object, if it defines a docstring; + otherwise, return None. + """ + # Extract the object's docstring. If it doesn't have one, + # then return None (no test for this object). + if isinstance(obj, six.string_types): + docstring = obj + else: + try: + if obj.__doc__ is None: + docstring = '' + else: + docstring = obj.__doc__ + if not isinstance(docstring, six.string_types): + docstring = str(docstring) + except (TypeError, AttributeError): + docstring = '' + + # Find the docstring's location in the file. + lineno = self._find_lineno(obj, source_lines) + + # Don't bother if the docstring is empty. + if self._exclude_empty and not docstring: + return None + + # Return a DocTest for this object. + if module is None: + filename = None + else: + filename = getattr(module, '__file__', module.__name__) + if filename[-4:] in (".pyc", ".pyo"): + filename = filename[:-1] + return self._parser.get_doctest(docstring, globs, name, + filename, lineno) + + def _find_lineno(self, obj, source_lines): + """ + Return a line number of the given object's docstring. Note: + this method assumes that the object has a docstring. + """ + lineno = None + + # Find the line number for modules. + if inspect.ismodule(obj): + lineno = 0 + + # Find the line number for classes. + # Note: this could be fooled if a class is defined multiple + # times in a single file. + if inspect.isclass(obj): + if source_lines is None: + return None + pat = re.compile(r'^\s*class\s*%s\b' % + getattr(obj, '__name__', '-')) + for i, line in enumerate(source_lines): + if pat.match(line): + lineno = i + break + + # Find the line number for functions & methods. + if inspect.ismethod(obj): obj = obj.__func__ + if inspect.isfunction(obj): obj = six.get_function_code(obj) + if inspect.istraceback(obj): obj = obj.tb_frame + if inspect.isframe(obj): obj = obj.f_code + if inspect.iscode(obj): + lineno = getattr(obj, 'co_firstlineno', None)-1 + + # Find the line number where the docstring starts. Assume + # that it's the first line that begins with a quote mark. + # Note: this could be fooled by a multiline function + # signature, where a continuation line begins with a quote + # mark. + if lineno is not None: + if source_lines is None: + return lineno+1 + pat = re.compile('(^|.*:)\s*\w*("|\')') + for lineno in range(lineno, len(source_lines)): + if pat.match(source_lines[lineno]): + return lineno + + # We couldn't find the line number. + return None + +###################################################################### +## 5. DocTest Runner +###################################################################### + +class DocTestRunner: + """ + A class used to run DocTest test cases, and accumulate statistics. + The `run` method is used to process a single DocTest case. It + returns a tuple `(f, t)`, where `t` is the number of test cases + tried, and `f` is the number of test cases that failed. + + >>> tests = DocTestFinder().find(_TestClass) + >>> runner = DocTestRunner(verbose=False) + >>> for test in tests: + ... print(runner.run(test)) + (0, 2) + (0, 1) + (0, 2) + (0, 2) + + The `summarize` method prints a summary of all the test cases that + have been run by the runner, and returns an aggregated `(f, t)` + tuple: + + >>> runner.summarize(verbose=1) + 4 items passed all tests: + 2 tests in _TestClass + 2 tests in _TestClass.__init__ + 2 tests in _TestClass.get + 1 tests in _TestClass.square + 7 tests in 4 items. + 7 passed and 0 failed. + Test passed. + (0, 7) + + The aggregated number of tried examples and failed examples is + also available via the `tries` and `failures` attributes: + + >>> runner.tries + 7 + >>> runner.failures + 0 + + The comparison between expected outputs and actual outputs is done + by an `OutputChecker`. This comparison may be customized with a + number of option flags; see the documentation for `testmod` for + more information. If the option flags are insufficient, then the + comparison may also be customized by passing a subclass of + `OutputChecker` to the constructor. + + The test runner's display output can be controlled in two ways. + First, an output function (`out) can be passed to + `TestRunner.run`; this function will be called with strings that + should be displayed. It defaults to `sys.stdout.write`. If + capturing the output is not sufficient, then the display output + can be also customized by subclassing DocTestRunner, and + overriding the methods `report_start`, `report_success`, + `report_unexpected_exception`, and `report_failure`. + """ + # This divider string is used to separate failure messages, and to + # separate sections of the summary. + DIVIDER = "*" * 70 + + def __init__(self, checker=None, verbose=None, optionflags=0): + """ + Create a new test runner. + + Optional keyword arg `checker` is the `OutputChecker` that + should be used to compare the expected outputs and actual + outputs of doctest examples. + + Optional keyword arg 'verbose' prints lots of stuff if true, + only failures if false; by default, it's true iff '-v' is in + sys.argv. + + Optional argument `optionflags` can be used to control how the + test runner compares expected output to actual output, and how + it displays failures. See the documentation for `testmod` for + more information. + """ + self._checker = checker or OutputChecker() + if verbose is None: + verbose = '-v' in sys.argv + self._verbose = verbose + self.optionflags = optionflags + self.original_optionflags = optionflags + + # Keep track of the examples we've run. + self.tries = 0 + self.failures = 0 + self._name2ft = {} + + # Create a fake output target for capturing doctest output. + self._fakeout = _SpoofOut() + + #///////////////////////////////////////////////////////////////// + # Reporting methods + #///////////////////////////////////////////////////////////////// + + def report_start(self, out, test, example): + """ + Report that the test runner is about to process the given + example. (Only displays a message if verbose=True) + """ + if self._verbose: + if example.want: + out('Trying:\n' + _indent(example.source) + + 'Expecting:\n' + _indent(example.want)) + else: + out('Trying:\n' + _indent(example.source) + + 'Expecting nothing\n') + + def report_success(self, out, test, example, got): + """ + Report that the given example ran successfully. (Only + displays a message if verbose=True) + """ + if self._verbose: + out("ok\n") + + def report_failure(self, out, test, example, got): + """ + Report that the given example failed. + """ + out(self._failure_header(test, example) + + self._checker.output_difference(example, got, self.optionflags)) + + def report_unexpected_exception(self, out, test, example, exc_info): + """ + Report that the given example raised an unexpected exception. + """ + out(self._failure_header(test, example) + + 'Exception raised:\n' + _indent(_exception_traceback(exc_info))) + + def _failure_header(self, test, example): + out = [self.DIVIDER] + if test.filename: + if test.lineno is not None and example.lineno is not None: + lineno = test.lineno + example.lineno + 1 + else: + lineno = '?' + out.append('File "%s", line %s, in %s' % + (test.filename, lineno, test.name)) + else: + out.append('Line %s, in %s' % (example.lineno+1, test.name)) + out.append('Failed example:') + source = example.source + out.append(_indent(source)) + return '\n'.join(out) + + #///////////////////////////////////////////////////////////////// + # DocTest Running + #///////////////////////////////////////////////////////////////// + + def __run(self, test, compileflags, out): + """ + Run the examples in `test`. Write the outcome of each example + with one of the `DocTestRunner.report_*` methods, using the + writer function `out`. `compileflags` is the set of compiler + flags that should be used to execute examples. Return a tuple + `(f, t)`, where `t` is the number of examples tried, and `f` + is the number of examples that failed. The examples are run + in the namespace `test.globs`. + """ + # Keep track of the number of failures and tries. + failures = tries = 0 + + # Save the option flags (since option directives can be used + # to modify them). + original_optionflags = self.optionflags + + SUCCESS, FAILURE, BOOM = range(3) # `outcome` state + + check = self._checker.check_output + + # Process each example. + for examplenum, example in enumerate(test.examples): + + # If REPORT_ONLY_FIRST_FAILURE is set, then suppress + # reporting after the first failure. + quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and + failures > 0) + + # Merge in the example's options. + self.optionflags = original_optionflags + if example.options: + for (optionflag, val) in example.options.items(): + if val: + self.optionflags |= optionflag + else: + self.optionflags &= ~optionflag + + # If 'SKIP' is set, then skip this example. + if self.optionflags & SKIP: + continue + + # Record that we started this example. + tries += 1 + if not quiet: + self.report_start(out, test, example) + + # Use a special filename for compile(), so we can retrieve + # the source code during interactive debugging (see + # __patched_linecache_getlines). + filename = '<doctest %s[%d]>' % (test.name, examplenum) + + # Doctest and Py3 issue: + # If the current example that we wish to run is going to fail + # because it expects a leading u"", then use an alternate displayhook + original_displayhook = sys.displayhook + + if six.PY3: + # only set alternate displayhook if Python 3.x or after + lines = [] + def py3_displayhook(value): + if value is None: + # None should not be considered at all + return original_displayhook(value) + + # Collect the repr output in one variable + s = repr(value) + # Strip b"" and u"" prefixes from the repr and expected output + # TODO: better way of stripping the prefixes? + expected = example.want + expected = expected.strip() # be wary of newlines + s = s.replace("u", "") + s = s.replace("b", "") + expected = expected.replace("u", "") + expected = expected.replace("b", "") + # single quote vs. double quote should not matter + # default all quote marks to double quote + s = s.replace("'", '"') + expected = expected.replace("'", '"') + + # In case of multi-line expected result + lines.append(s) + + # let them match + if s == expected: # be wary of false positives here + # they should be the same, print expected value + sys.stdout.write("%s\n" % example.want.strip()) + + # multi-line expected output, doctest uses loop + elif len(expected.split("\n")) == len(lines): + if "\n".join(lines) == expected: + sys.stdout.write("%s\n" % example.want.strip()) + else: + sys.stdout.write("%s\n" % repr(value)) + elif len(expected.split("\n")) != len(lines): + # we are not done looping yet, do not print anything! + pass + + else: + sys.stdout.write("%s\n" % repr(value)) + + sys.displayhook = py3_displayhook + + # Run the example in the given context (globs), and record + # any exception that gets raised. (But don't intercept + # keyboard interrupts.) + try: + # Don't blink! This is where the user's code gets run. + six.exec_(compile(example.source, filename, "single", + compileflags, 1), test.globs) + self.debugger.set_continue() # ==== Example Finished ==== + exception = None + except KeyboardInterrupt: + raise + except: + exception = sys.exc_info() + self.debugger.set_continue() # ==== Example Finished ==== + finally: + # restore the original displayhook + sys.displayhook = original_displayhook + + got = self._fakeout.getvalue() # the actual output + self._fakeout.truncate(0) + # Python 3.1 requires seek after truncate + self._fakeout.seek(0) + outcome = FAILURE # guilty until proved innocent or insane + + # If the example executed without raising any exceptions, + # verify its output. + if exception is None: + if check(example.want, got, self.optionflags): + outcome = SUCCESS + + # The example raised an exception: check if it was expected. + else: + exc_msg = traceback.format_exception_only(*exception[:2])[-1] + if six.PY3: + # module name will be in group(1) and the expected + # exception message will be in group(2) + m = re.match(r'(.*)\.(\w+:.+\s)', exc_msg) + # make sure there's a match + if m != None: + f_name = m.group(1) + # check to see if m.group(1) contains the module name + if f_name == exception[0].__module__: + # strip the module name from exc_msg + exc_msg = m.group(2) + + if not quiet: + got += _exception_traceback(exception) + + # If `example.exc_msg` is None, then we weren't expecting + # an exception. + if example.exc_msg is None: + outcome = BOOM + + # We expected an exception: see whether it matches. + elif check(example.exc_msg, exc_msg, self.optionflags): + outcome = SUCCESS + + # Another chance if they didn't care about the detail. + elif self.optionflags & IGNORE_EXCEPTION_DETAIL: + m1 = re.match(r'[^:]*:', example.exc_msg) + m2 = re.match(r'[^:]*:', exc_msg) + if m1 and m2 and check(m1.group(0), m2.group(0), + self.optionflags): + outcome = SUCCESS + + # Report the outcome. + if outcome is SUCCESS: + if not quiet: + self.report_success(out, test, example, got) + elif outcome is FAILURE: + if not quiet: + self.report_failure(out, test, example, got) + failures += 1 + elif outcome is BOOM: + if not quiet: + self.report_unexpected_exception(out, test, example, + exception) + failures += 1 + else: + assert False, ("unknown outcome", outcome) + + # Restore the option flags (in case they were modified) + self.optionflags = original_optionflags + + # Record and return the number of failures and tries. + self.__record_outcome(test, failures, tries) + return failures, tries + + def __record_outcome(self, test, f, t): + """ + Record the fact that the given DocTest (`test`) generated `f` + failures out of `t` tried examples. + """ + f2, t2 = self._name2ft.get(test.name, (0,0)) + self._name2ft[test.name] = (f+f2, t+t2) + self.failures += f + self.tries += t + + __LINECACHE_FILENAME_RE = re.compile(r'<doctest ' + r'(?P<name>[\w\.]+)' + r'\[(?P<examplenum>\d+)\]>$') + def __patched_linecache_getlines(self, filename, module_globals=None): + m = self.__LINECACHE_FILENAME_RE.match(filename) + if m and m.group('name') == self.test.name: + example = self.test.examples[int(m.group('examplenum'))] + return example.source.splitlines(True) + else: + return self.save_linecache_getlines(filename, module_globals) + + def run(self, test, compileflags=None, out=None, clear_globs=True): + """ + Run the examples in `test`, and display the results using the + writer function `out`. + + The examples are run in the namespace `test.globs`. If + `clear_globs` is true (the default), then this namespace will + be cleared after the test runs, to help with garbage + collection. If you would like to examine the namespace after + the test completes, then use `clear_globs=False`. + + `compileflags` gives the set of flags that should be used by + the Python compiler when running the examples. If not + specified, then it will default to the set of future-import + flags that apply to `globs`. + + The output of each example is checked using + `DocTestRunner.check_output`, and the results are formatted by + the `DocTestRunner.report_*` methods. + """ + self.test = test + + if compileflags is None: + compileflags = _extract_future_flags(test.globs) + + save_stdout = sys.stdout + if out is None: + out = save_stdout.write + sys.stdout = self._fakeout + + # Patch pdb.set_trace to restore sys.stdout during interactive + # debugging (so it's not still redirected to self._fakeout). + # Note that the interactive output will go to *our* + # save_stdout, even if that's not the real sys.stdout; this + # allows us to write test cases for the set_trace behavior. + save_set_trace = pdb.set_trace + self.debugger = _OutputRedirectingPdb(save_stdout) + self.debugger.reset() + pdb.set_trace = self.debugger.set_trace + + # Patch linecache.getlines, so we can see the example's source + # when we're inside the debugger. + self.save_linecache_getlines = linecache.getlines + linecache.getlines = self.__patched_linecache_getlines + + try: + return self.__run(test, compileflags, out) + finally: + sys.stdout = save_stdout + pdb.set_trace = save_set_trace + linecache.getlines = self.save_linecache_getlines + if clear_globs: + test.globs.clear() + + #///////////////////////////////////////////////////////////////// + # Summarization + #///////////////////////////////////////////////////////////////// + def summarize(self, verbose=None): + """ + Print a summary of all the test cases that have been run by + this DocTestRunner, and return a tuple `(f, t)`, where `f` is + the total number of failed examples, and `t` is the total + number of tried examples. + + The optional `verbose` argument controls how detailed the + summary is. If the verbosity is not specified, then the + DocTestRunner's verbosity is used. + """ + if verbose is None: + verbose = self._verbose + notests = [] + passed = [] + failed = [] + totalt = totalf = 0 + for x in self._name2ft.items(): + name, (f, t) = x + assert f <= t + totalt += t + totalf += f + if t == 0: + notests.append(name) + elif f == 0: + passed.append( (name, t) ) + else: + failed.append(x) + if verbose: + if notests: + print("%d items had no tests:" % len(notests)) + notests.sort() + for thing in notests: + print(" %s" % thing) + if passed: + print("%d items passed all tests:" % len(passed)) + passed.sort() + for thing, count in passed: + print(" %3d tests in %s" % (count, thing)) + if failed: + print(self.DIVIDER) + print("%d items had failures:" % len(failed)) + failed.sort() + for thing, (f, t) in failed: + print(" %3d of %3d in %s" % (f, t, thing)) + if verbose: + print("%d tests in % d items" % (len(self._name2ft), totalt)) + print("%d passed and %d failed." % (totalt - totalf, totalf)) + if totalf: + print("***Test Failed*** %d failures." % totalf) + elif verbose: + print("Test passed.") + return totalf, totalt + + #///////////////////////////////////////////////////////////////// + # Backward compatibility cruft to maintain doctest.master. + #///////////////////////////////////////////////////////////////// + def merge(self, other): + d = self._name2ft + for name, (f, t) in other._name2ft.items(): + if name in d: + print("*** DocTestRunner.merge: '" + name + "' in both" \ + " testers; summing outcomes.") + f2, t2 = d[name] + f = f + f2 + t = t + t2 + d[name] = f, t + +class OutputChecker: + """ + A class used to check the whether the actual output from a doctest + example matches the expected output. `OutputChecker` defines two + methods: `check_output`, which compares a given pair of outputs, + and returns true if they match; and `output_difference`, which + returns a string describing the differences between two outputs. + """ + def check_output(self, want, got, optionflags): + """ + Return True iff the actual output from an example (`got`) + matches the expected output (`want`). These strings are + always considered to match if they are identical; but + depending on what option flags the test runner is using, + several non-exact match types are also possible. See the + documentation for `TestRunner` for more information about + option flags. + """ + # Handle the common case first, for efficiency: + # if they're string-identical, always return true. + if got == want: + return True + + # The values True and False replaced 1 and 0 as the return + # value for boolean comparisons in Python 2.3. + if not (optionflags & DONT_ACCEPT_TRUE_FOR_1): + if (got,want) == ("True\n", "1\n"): + return True + if (got,want) == ("False\n", "0\n"): + return True + + # <BLANKLINE> can be used as a special sequence to signify a + # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used. + if not (optionflags & DONT_ACCEPT_BLANKLINE): + # Replace <BLANKLINE> in want with a blank line. + want = re.sub('(?m)^%s\s*?$' % re.escape(BLANKLINE_MARKER), + '', want) + # If a line in got contains only spaces, then remove the + # spaces. + got = re.sub('(?m)^\s*?$', '', got) + if got == want: + return True + + # This flag causes doctest to ignore any differences in the + # contents of whitespace strings. Note that this can be used + # in conjunction with the ELLIPSIS flag. + if optionflags & NORMALIZE_WHITESPACE: + got = ' '.join(got.split()) + want = ' '.join(want.split()) + if got == want: + return True + + # The ELLIPSIS flag says to let the sequence "..." in `want` + # match any substring in `got`. + if optionflags & ELLIPSIS: + if _ellipsis_match(want, got): + return True + + # We didn't find any match; return false. + return False + + # Should we do a fancy diff? + def _do_a_fancy_diff(self, want, got, optionflags): + # Not unless they asked for a fancy diff. + if not optionflags & (REPORT_UDIFF | + REPORT_CDIFF | + REPORT_NDIFF): + return False + + # If expected output uses ellipsis, a meaningful fancy diff is + # too hard ... or maybe not. In two real-life failures Tim saw, + # a diff was a major help anyway, so this is commented out. + # [todo] _ellipsis_match() knows which pieces do and don't match, + # and could be the basis for a kick-ass diff in this case. + ##if optionflags & ELLIPSIS and ELLIPSIS_MARKER in want: + ## return False + + # ndiff does intraline difference marking, so can be useful even + # for 1-line differences. + if optionflags & REPORT_NDIFF: + return True + + # The other diff types need at least a few lines to be helpful. + return want.count('\n') > 2 and got.count('\n') > 2 + + def output_difference(self, example, got, optionflags): + """ + Return a string describing the differences between the + expected output for a given example (`example`) and the actual + output (`got`). `optionflags` is the set of option flags used + to compare `want` and `got`. + """ + want = example.want + # If <BLANKLINE>s are being used, then replace blank lines + # with <BLANKLINE> in the actual output string. + if not (optionflags & DONT_ACCEPT_BLANKLINE): + got = re.sub('(?m)^[ ]*(?=\n)', BLANKLINE_MARKER, got) + + # Check if we should use diff. + if self._do_a_fancy_diff(want, got, optionflags): + # Split want & got into lines. + want_lines = want.splitlines(True) # True == keep line ends + got_lines = got.splitlines(True) + # Use difflib to find their differences. + if optionflags & REPORT_UDIFF: + diff = difflib.unified_diff(want_lines, got_lines, n=2) + diff = list(diff)[2:] # strip the diff header + kind = 'unified diff with -expected +actual' + elif optionflags & REPORT_CDIFF: + diff = difflib.context_diff(want_lines, got_lines, n=2) + diff = list(diff)[2:] # strip the diff header + kind = 'context diff with expected followed by actual' + elif optionflags & REPORT_NDIFF: + engine = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) + diff = list(engine.compare(want_lines, got_lines)) + kind = 'ndiff with -expected +actual' + else: + assert 0, 'Bad diff option' + # Remove trailing whitespace on diff output. + diff = [line.rstrip() + '\n' for line in diff] + return 'Differences (%s):\n' % kind + _indent(''.join(diff)) + + # If we're not using diff, then simply list the expected + # output followed by the actual output. + if want and got: + return 'Expected:\n%sGot:\n%s' % (_indent(want), _indent(got)) + elif want: + return 'Expected:\n%sGot nothing\n' % _indent(want) + elif got: + return 'Expected nothing\nGot:\n%s' % _indent(got) + else: + return 'Expected nothing\nGot nothing\n' + +class DocTestFailure(Exception): + """A DocTest example has failed in debugging mode. + + The exception instance has variables: + + - test: the DocTest object being run + + - example: the Example object that failed + + - got: the actual output + """ + def __init__(self, test, example, got): + self.test = test + self.example = example + self.got = got + + def __str__(self): + return str(self.test) + +class UnexpectedException(Exception): + """A DocTest example has encountered an unexpected exception + + The exception instance has variables: + + - test: the DocTest object being run + + - example: the Example object that failed + + - exc_info: the exception info + """ + def __init__(self, test, example, exc_info): + self.test = test + self.example = example + self.exc_info = exc_info + + def __str__(self): + return str(self.test) + +class DebugRunner(DocTestRunner): + r"""Run doc tests but raise an exception as soon as there is a failure. + + If an unexpected exception occurs, an UnexpectedException is raised. + It contains the test, the example, and the original exception: + + >>> runner = DebugRunner(verbose=False) + >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', + ... {}, 'foo', 'foo.py', 0) + >>> try: + ... runner.run(test) + ... except UnexpectedException as e: + ... failure = e + + >>> failure.test is test + True + + >>> failure.example.want + '42\n' + + >>> exc_info = failure.exc_info + >>> raise exc_info[0], exc_info[1], exc_info[2] + Traceback (most recent call last): + ... + KeyError + + We wrap the original exception to give the calling application + access to the test and example information. + + If the output doesn't match, then a DocTestFailure is raised: + + >>> test = DocTestParser().get_doctest(''' + ... >>> x = 1 + ... >>> x + ... 2 + ... ''', {}, 'foo', 'foo.py', 0) + + >>> try: + ... runner.run(test) + ... except DocTestFailure as e: + ... failure = e + + DocTestFailure objects provide access to the test: + + >>> failure.test is test + True + + As well as to the example: + + >>> failure.example.want + '2\n' + + and the actual output: + + >>> failure.got + '1\n' + + If a failure or error occurs, the globals are left intact: + + >>> del test.globs['__builtins__'] + >>> test.globs + {'x': 1} + + >>> test = DocTestParser().get_doctest(''' + ... >>> x = 2 + ... >>> raise KeyError + ... ''', {}, 'foo', 'foo.py', 0) + + >>> runner.run(test) + Traceback (most recent call last): + ... + UnexpectedException: <DocTest foo from foo.py:0 (2 examples)> + + >>> del test.globs['__builtins__'] + >>> test.globs + {'x': 2} + + But the globals are cleared if there is no error: + + >>> test = DocTestParser().get_doctest(''' + ... >>> x = 2 + ... ''', {}, 'foo', 'foo.py', 0) + + >>> runner.run(test) + (0, 1) + + >>> test.globs + {} + + """ + + def run(self, test, compileflags=None, out=None, clear_globs=True): + r = DocTestRunner.run(self, test, compileflags, out, False) + if clear_globs: + test.globs.clear() + return r + + def report_unexpected_exception(self, out, test, example, exc_info): + raise UnexpectedException(test, example, exc_info) + + def report_failure(self, out, test, example, got): + raise DocTestFailure(test, example, got) + +###################################################################### +## 6. Test Functions +###################################################################### +# These should be backwards compatible. + +# For backward compatibility, a global instance of a DocTestRunner +# class, updated by testmod. +master = None + +def testmod(m=None, name=None, globs=None, verbose=None, + report=True, optionflags=0, extraglobs=None, + raise_on_error=False, exclude_empty=False): + """m=None, name=None, globs=None, verbose=None, report=True, + optionflags=0, extraglobs=None, raise_on_error=False, + exclude_empty=False + + Test examples in docstrings in functions and classes reachable + from module m (or the current module if m is not supplied), starting + with m.__doc__. + + Also test examples reachable from dict m.__test__ if it exists and is + not None. m.__test__ maps names to functions, classes and strings; + function and class docstrings are tested even if the name is private; + strings are tested directly, as if they were docstrings. + + Return (#failures, #tests). + + See doctest.__doc__ for an overview. + + Optional keyword arg "name" gives the name of the module; by default + use m.__name__. + + Optional keyword arg "globs" gives a dict to be used as the globals + when executing examples; by default, use m.__dict__. A copy of this + dict is actually used for each docstring, so that each docstring's + examples start with a clean slate. + + Optional keyword arg "extraglobs" gives a dictionary that should be + merged into the globals that are used to execute examples. By + default, no extra globals are used. This is new in 2.4. + + Optional keyword arg "verbose" prints lots of stuff if true, prints + only failures if false; by default, it's true iff "-v" is in sys.argv. + + Optional keyword arg "report" prints a summary at the end when true, + else prints nothing at the end. In verbose mode, the summary is + detailed, else very brief (in fact, empty if all tests passed). + + Optional keyword arg "optionflags" or's together module constants, + and defaults to 0. This is new in 2.3. Possible values (see the + docs for details): + + DONT_ACCEPT_TRUE_FOR_1 + DONT_ACCEPT_BLANKLINE + NORMALIZE_WHITESPACE + ELLIPSIS + SKIP + IGNORE_EXCEPTION_DETAIL + REPORT_UDIFF + REPORT_CDIFF + REPORT_NDIFF + REPORT_ONLY_FIRST_FAILURE + + Optional keyword arg "raise_on_error" raises an exception on the + first unexpected exception or failure. This allows failures to be + post-mortem debugged. + + Advanced tomfoolery: testmod runs methods of a local instance of + class doctest.Tester, then merges the results into (or creates) + global Tester instance doctest.master. Methods of doctest.master + can be called directly too, if you want to do something unusual. + Passing report=0 to testmod is especially useful then, to delay + displaying a summary. Invoke doctest.master.summarize(verbose) + when you're done fiddling. + """ + global master + + # If no module was given, then use __main__. + if m is None: + # DWA - m will still be None if this wasn't invoked from the command + # line, in which case the following TypeError is about as good an error + # as we should expect + m = sys.modules.get('__main__') + + # Check that we were actually given a module. + if not inspect.ismodule(m): + raise TypeError("testmod: module required; %r" % (m,)) + + # If no name was given, then use the module's name. + if name is None: + name = m.__name__ + + # Find, parse, and run all tests in the given module. + finder = DocTestFinder(exclude_empty=exclude_empty) + + if raise_on_error: + runner = DebugRunner(verbose=verbose, optionflags=optionflags) + else: + runner = DocTestRunner(verbose=verbose, optionflags=optionflags) + + for test in finder.find(m, name, globs=globs, extraglobs=extraglobs): + runner.run(test) + + if report: + runner.summarize() + + if master is None: + master = runner + else: + master.merge(runner) + + return runner.failures, runner.tries + +def testfile(filename, module_relative=True, name=None, package=None, + globs=None, verbose=None, report=True, optionflags=0, + extraglobs=None, raise_on_error=False, parser=DocTestParser(), + encoding=None): + """ + Test examples in the given file. Return (#failures, #tests). + + Optional keyword arg "module_relative" specifies how filenames + should be interpreted: + + - If "module_relative" is True (the default), then "filename" + specifies a module-relative path. By default, this path is + relative to the calling module's directory; but if the + "package" argument is specified, then it is relative to that + package. To ensure os-independence, "filename" should use + "/" characters to separate path segments, and should not + be an absolute path (i.e., it may not begin with "/"). + + - If "module_relative" is False, then "filename" specifies an + os-specific path. The path may be absolute or relative (to + the current working directory). + + Optional keyword arg "name" gives the name of the test; by default + use the file's basename. + + Optional keyword argument "package" is a Python package or the + name of a Python package whose directory should be used as the + base directory for a module relative filename. If no package is + specified, then the calling module's directory is used as the base + directory for module relative filenames. It is an error to + specify "package" if "module_relative" is False. + + Optional keyword arg "globs" gives a dict to be used as the globals + when executing examples; by default, use {}. A copy of this dict + is actually used for each docstring, so that each docstring's + examples start with a clean slate. + + Optional keyword arg "extraglobs" gives a dictionary that should be + merged into the globals that are used to execute examples. By + default, no extra globals are used. + + Optional keyword arg "verbose" prints lots of stuff if true, prints + only failures if false; by default, it's true iff "-v" is in sys.argv. + + Optional keyword arg "report" prints a summary at the end when true, + else prints nothing at the end. In verbose mode, the summary is + detailed, else very brief (in fact, empty if all tests passed). + + Optional keyword arg "optionflags" or's together module constants, + and defaults to 0. Possible values (see the docs for details): + + DONT_ACCEPT_TRUE_FOR_1 + DONT_ACCEPT_BLANKLINE + NORMALIZE_WHITESPACE + ELLIPSIS + SKIP + IGNORE_EXCEPTION_DETAIL + REPORT_UDIFF + REPORT_CDIFF + REPORT_NDIFF + REPORT_ONLY_FIRST_FAILURE + + Optional keyword arg "raise_on_error" raises an exception on the + first unexpected exception or failure. This allows failures to be + post-mortem debugged. + + Optional keyword arg "parser" specifies a DocTestParser (or + subclass) that should be used to extract tests from the files. + + Optional keyword arg "encoding" specifies an encoding that should + be used to convert the file to unicode. + + Advanced tomfoolery: testmod runs methods of a local instance of + class doctest.Tester, then merges the results into (or creates) + global Tester instance doctest.master. Methods of doctest.master + can be called directly too, if you want to do something unusual. + Passing report=0 to testmod is especially useful then, to delay + displaying a summary. Invoke doctest.master.summarize(verbose) + when you're done fiddling. + """ + global master + + if package and not module_relative: + raise ValueError("Package may only be specified for module-" + "relative paths.") + + # Relativize the path + text, filename = _load_testfile(filename, package, module_relative) + + # If no name was given, then use the file's name. + if name is None: + name = os.path.basename(filename) + + # Assemble the globals. + if globs is None: + globs = {} + else: + globs = globs.copy() + if extraglobs is not None: + globs.update(extraglobs) + + if raise_on_error: + runner = DebugRunner(verbose=verbose, optionflags=optionflags) + else: + runner = DocTestRunner(verbose=verbose, optionflags=optionflags) + + if encoding is not None: + text = text.decode(encoding) + + # Read the file, convert it to a test, and run it. + test = parser.get_doctest(text, globs, name, filename, 0) + runner.run(test) + + if report: + runner.summarize() + + if master is None: + master = runner + else: + master.merge(runner) + + return runner.failures, runner.tries + +def run_docstring_examples(f, globs, verbose=False, name="NoName", + compileflags=None, optionflags=0): + """ + Test examples in the given object's docstring (`f`), using `globs` + as globals. Optional argument `name` is used in failure messages. + If the optional argument `verbose` is true, then generate output + even if there are no failures. + + `compileflags` gives the set of flags that should be used by the + Python compiler when running the examples. If not specified, then + it will default to the set of future-import flags that apply to + `globs`. + + Optional keyword arg `optionflags` specifies options for the + testing and output. See the documentation for `testmod` for more + information. + """ + # Find, parse, and run all tests in the given module. + finder = DocTestFinder(verbose=verbose, recurse=False) + runner = DocTestRunner(verbose=verbose, optionflags=optionflags) + for test in finder.find(f, name, globs=globs): + runner.run(test, compileflags=compileflags) + +###################################################################### +## 7. Tester +###################################################################### +# This is provided only for backwards compatibility. It's not +# actually used in any way. + +class Tester: + def __init__(self, mod=None, globs=None, verbose=None, optionflags=0): + + warnings.warn("class Tester is deprecated; " + "use class doctest.DocTestRunner instead", + DeprecationWarning, stacklevel=2) + if mod is None and globs is None: + raise TypeError("Tester.__init__: must specify mod or globs") + if mod is not None and not inspect.ismodule(mod): + raise TypeError("Tester.__init__: mod must be a module; %r" % + (mod,)) + if globs is None: + globs = mod.__dict__ + self.globs = globs + + self.verbose = verbose + self.optionflags = optionflags + self.testfinder = DocTestFinder() + self.testrunner = DocTestRunner(verbose=verbose, + optionflags=optionflags) + + def runstring(self, s, name): + test = DocTestParser().get_doctest(s, self.globs, name, None, None) + if self.verbose: + print("Running string %s" % name) + (f,t) = self.testrunner.run(test) + if self.verbose: + print("%s of %s examples failed in string %s" % (f, t, name)) + return (f,t) + + def rundoc(self, object, name=None, module=None): + f = t = 0 + tests = self.testfinder.find(object, name, module=module, + globs=self.globs) + for test in tests: + (f2, t2) = self.testrunner.run(test) + (f,t) = (f+f2, t+t2) + return (f,t) + + def rundict(self, d, name, module=None): + import new + m = new.module(name) + m.__dict__.update(d) + if module is None: + module = False + return self.rundoc(m, name, module) + + def run__test__(self, d, name): + import new + m = new.module(name) + m.__test__ = d + return self.rundoc(m, name) + + def summarize(self, verbose=None): + return self.testrunner.summarize(verbose) + + def merge(self, other): + self.testrunner.merge(other.testrunner) + +###################################################################### +## 8. Unittest Support +###################################################################### + +_unittest_reportflags = 0 + +def set_unittest_reportflags(flags): + """Sets the unittest option flags. + + The old flag is returned so that a runner could restore the old + value if it wished to: + + >>> old = _unittest_reportflags + >>> set_unittest_reportflags(REPORT_NDIFF | + ... REPORT_ONLY_FIRST_FAILURE) == old + True + + >>> import doctest + >>> doctest._unittest_reportflags == (REPORT_NDIFF | + ... REPORT_ONLY_FIRST_FAILURE) + True + + Only reporting flags can be set: + + >>> set_unittest_reportflags(ELLIPSIS) + Traceback (most recent call last): + ... + ValueError: ('Only reporting flags allowed', 8) + + >>> set_unittest_reportflags(old) == (REPORT_NDIFF | + ... REPORT_ONLY_FIRST_FAILURE) + True + """ + global _unittest_reportflags + + if (flags & REPORTING_FLAGS) != flags: + raise ValueError("Only reporting flags allowed", flags) + old = _unittest_reportflags + _unittest_reportflags = flags + return old + + +class DocTestCase(unittest.TestCase): + + def __init__(self, test, optionflags=0, setUp=None, tearDown=None, + checker=None, runner=DocTestRunner): + + unittest.TestCase.__init__(self) + self._dt_optionflags = optionflags + self._dt_checker = checker + self._dt_test = test + self._dt_setUp = setUp + self._dt_tearDown = tearDown + self._dt_runner = runner + + def setUp(self): + test = self._dt_test + + if self._dt_setUp is not None: + self._dt_setUp(test) + + def tearDown(self): + test = self._dt_test + + if self._dt_tearDown is not None: + self._dt_tearDown(test) + + test.globs.clear() + + def runTest(self): + test = self._dt_test + old = sys.stdout + new = StringIO() + optionflags = self._dt_optionflags + + if not (optionflags & REPORTING_FLAGS): + # The option flags don't include any reporting flags, + # so add the default reporting flags + optionflags |= _unittest_reportflags + + runner = self._dt_runner(optionflags=optionflags, + checker=self._dt_checker, verbose=False) + + try: + runner.DIVIDER = "-"*70 + failures, tries = runner.run( + test, out=new.write, clear_globs=False) + finally: + sys.stdout = old + + if failures: + raise self.failureException(self.format_failure(new.getvalue())) + + def format_failure(self, err): + test = self._dt_test + if test.lineno is None: + lineno = 'unknown line number' + else: + lineno = '%s' % test.lineno + lname = '.'.join(test.name.split('.')[-1:]) + return ('Failed doctest test for %s\n' + ' File "%s", line %s, in %s\n\n%s' + % (test.name, test.filename, lineno, lname, err) + ) + + def debug(self): + r"""Run the test case without results and without catching exceptions + + The unit test framework includes a debug method on test cases + and test suites to support post-mortem debugging. The test code + is run in such a way that errors are not caught. This way a + caller can catch the errors and initiate post-mortem debugging. + + The DocTestCase provides a debug method that raises + UnexpectedException errors if there is an unexepcted + exception: + + >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', + ... {}, 'foo', 'foo.py', 0) + >>> case = DocTestCase(test) + >>> try: + ... case.debug() + ... except UnexpectedException as e: + ... failure = e + + The UnexpectedException contains the test, the example, and + the original exception: + + >>> failure.test is test + True + + >>> failure.example.want + '42\n' + + >>> exc_info = failure.exc_info + >>> raise exc_info[0], exc_info[1], exc_info[2] + Traceback (most recent call last): + ... + KeyError + + If the output doesn't match, then a DocTestFailure is raised: + + >>> test = DocTestParser().get_doctest(''' + ... >>> x = 1 + ... >>> x + ... 2 + ... ''', {}, 'foo', 'foo.py', 0) + >>> case = DocTestCase(test) + + >>> try: + ... case.debug() + ... except DocTestFailure as e: + ... failure = e + + DocTestFailure objects provide access to the test: + + >>> failure.test is test + True + + As well as to the example: + + >>> failure.example.want + '2\n' + + and the actual output: + + >>> failure.got + '1\n' + + """ + + self.setUp() + runner = DebugRunner(optionflags=self._dt_optionflags, + checker=self._dt_checker, verbose=False) + runner.run(self._dt_test) + self.tearDown() + + def id(self): + return self._dt_test.name + + def __repr__(self): + name = self._dt_test.name.split('.') + return "%s (%s)" % (name[-1], '.'.join(name[:-1])) + + __str__ = __repr__ + + def shortDescription(self): + return "Doctest: " + self._dt_test.name + +def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, + test_class=DocTestCase, **options): + """ + Convert doctest tests for a module to a unittest test suite. + + This converts each documentation string in a module that + contains doctest tests to a unittest test case. If any of the + tests in a doc string fail, then the test case fails. An exception + is raised showing the name of the file containing the test and a + (sometimes approximate) line number. + + The `module` argument provides the module to be tested. The argument + can be either a module or a module name. + + If no argument is given, the calling module is used. + + A number of options may be provided as keyword arguments: + + setUp + A set-up function. This is called before running the + tests in each file. The setUp function will be passed a DocTest + object. The setUp function can access the test globals as the + globs attribute of the test passed. + + tearDown + A tear-down function. This is called after running the + tests in each file. The tearDown function will be passed a DocTest + object. The tearDown function can access the test globals as the + globs attribute of the test passed. + + globs + A dictionary containing initial global variables for the tests. + + optionflags + A set of doctest option flags expressed as an integer. + """ + + if test_finder is None: + test_finder = DocTestFinder() + + module = _normalize_module(module) + tests = test_finder.find(module, globs=globs, extraglobs=extraglobs) + if globs is None: + globs = module.__dict__ + if not tests: + # Why do we want to do this? Because it reveals a bug that might + # otherwise be hidden. + raise ValueError(module, "has no tests") + + tests.sort() + suite = unittest.TestSuite() + for test in tests: + if len(test.examples) == 0: + continue + if not test.filename: + filename = module.__file__ + if filename[-4:] in (".pyc", ".pyo"): + filename = filename[:-1] + test.filename = filename + suite.addTest(test_class(test, **options)) + + return suite + +class DocFileCase(DocTestCase): + + def id(self): + return '_'.join(self._dt_test.name.split('.')) + + def __repr__(self): + return self._dt_test.filename + __str__ = __repr__ + + def format_failure(self, err): + return ('Failed doctest test for %s\n File "%s", line 0\n\n%s' + % (self._dt_test.name, self._dt_test.filename, err) + ) + +def DocFileTest(path, module_relative=True, package=None, + globs=None, parser=DocTestParser(), + encoding=None, **options): + if globs is None: + globs = {} + else: + globs = globs.copy() + + if package and not module_relative: + raise ValueError("Package may only be specified for module-" + "relative paths.") + + # Relativize the path. + doc, path = _load_testfile(path, package, module_relative) + + if "__file__" not in globs: + globs["__file__"] = path + + # Find the file and read it. + name = os.path.basename(path) + + # If an encoding is specified, use it to convert the file to unicode + if encoding is not None: + doc = doc.decode(encoding) + + # Convert it to a test, and wrap it in a DocFileCase. + test = parser.get_doctest(doc, globs, name, path, 0) + return DocFileCase(test, **options) + +def DocFileSuite(*paths, **kw): + """A unittest suite for one or more doctest files. + + The path to each doctest file is given as a string; the + interpretation of that string depends on the keyword argument + "module_relative". + + A number of options may be provided as keyword arguments: + + module_relative + If "module_relative" is True, then the given file paths are + interpreted as os-independent module-relative paths. By + default, these paths are relative to the calling module's + directory; but if the "package" argument is specified, then + they are relative to that package. To ensure os-independence, + "filename" should use "/" characters to separate path + segments, and may not be an absolute path (i.e., it may not + begin with "/"). + + If "module_relative" is False, then the given file paths are + interpreted as os-specific paths. These paths may be absolute + or relative (to the current working directory). + + package + A Python package or the name of a Python package whose directory + should be used as the base directory for module relative paths. + If "package" is not specified, then the calling module's + directory is used as the base directory for module relative + filenames. It is an error to specify "package" if + "module_relative" is False. + + setUp + A set-up function. This is called before running the + tests in each file. The setUp function will be passed a DocTest + object. The setUp function can access the test globals as the + globs attribute of the test passed. + + tearDown + A tear-down function. This is called after running the + tests in each file. The tearDown function will be passed a DocTest + object. The tearDown function can access the test globals as the + globs attribute of the test passed. + + globs + A dictionary containing initial global variables for the tests. + + optionflags + A set of doctest option flags expressed as an integer. + + parser + A DocTestParser (or subclass) that should be used to extract + tests from the files. + + encoding + An encoding that will be used to convert the files to unicode. + """ + suite = unittest.TestSuite() + + # We do this here so that _normalize_module is called at the right + # level. If it were called in DocFileTest, then this function + # would be the caller and we might guess the package incorrectly. + if kw.get('module_relative', True): + kw['package'] = _normalize_module(kw.get('package')) + + for path in paths: + suite.addTest(DocFileTest(path, **kw)) + + return suite + +###################################################################### +## 9. Debugging Support +###################################################################### + +def script_from_examples(s): + r"""Extract script from text with examples. + + Converts text with examples to a Python script. Example input is + converted to regular code. Example output and all other words + are converted to comments: + + >>> text = ''' + ... Here are examples of simple math. + ... + ... Python has super accurate integer addition + ... + ... >>> 2 + 2 + ... 5 + ... + ... And very friendly error messages: + ... + ... >>> 1/0 + ... To Infinity + ... And + ... Beyond + ... + ... You can use logic if you want: + ... + ... >>> if 0: + ... ... blah + ... ... blah + ... ... + ... + ... Ho hum + ... ''' + + >>> print(script_from_examples(text)) + # Here are examples of simple math. + # + # Python has super accurate integer addition + # + 2 + 2 + # Expected: + ## 5 + # + # And very friendly error messages: + # + 1/0 + # Expected: + ## To Infinity + ## And + ## Beyond + # + # You can use logic if you want: + # + if 0: + blah + blah + # + # Ho hum + """ + output = [] + for piece in DocTestParser().parse(s): + if isinstance(piece, Example): + # Add the example's source code (strip trailing NL) + output.append(piece.source[:-1]) + # Add the expected output: + want = piece.want + if want: + output.append('# Expected:') + output += ['## '+l for l in want.split('\n')[:-1]] + else: + # Add non-example text. + output += [_comment_line(l) + for l in piece.split('\n')[:-1]] + + # Trim junk on both ends. + while output and output[-1] == '#': + output.pop() + while output and output[0] == '#': + output.pop(0) + # Combine the output, and return it. + return '\n'.join(output) + +def testsource(module, name): + """Extract the test sources from a doctest docstring as a script. + + Provide the module (or dotted name of the module) containing the + test to be debugged and the name (within the module) of the object + with the doc string with tests to be debugged. + """ + module = _normalize_module(module) + tests = DocTestFinder().find(module) + test = [t for t in tests if t.name == name] + if not test: + raise ValueError(name, "not found in tests") + test = test[0] + testsrc = script_from_examples(test.docstring) + return testsrc + +def debug_src(src, pm=False, globs=None): + """Debug a single doctest docstring, in argument `src`'""" + testsrc = script_from_examples(src) + debug_script(testsrc, pm, globs) + +def debug_script(src, pm=False, globs=None): + "Debug a test script. `src` is the script, as a string." + import pdb + + # Note that tempfile.NameTemporaryFile() cannot be used. As the + # docs say, a file so created cannot be opened by name a second time + # on modern Windows boxes, and execfile() needs to open it. + srcfilename = tempfile.mktemp(".py", "doctestdebug") + with open(srcfilename, 'w') as fp: + fp.write(src) + + try: + if globs: + globs = globs.copy() + else: + globs = {} + + if pm: + try: + execfile(srcfilename, globs, globs) + except: + print(sys.exc_info()[1]) + pdb.post_mortem(sys.exc_info()[2]) + else: + # Note that %r is vital here. '%s' instead can, e.g., cause + # backslashes to get treated as metacharacters on Windows. + pdb.run("execfile(%r)" % srcfilename, globs, globs) + + finally: + os.remove(srcfilename) + +def debug(module, name, pm=False): + """Debug a single doctest docstring. + + Provide the module (or dotted name of the module) containing the + test to be debugged and the name (within the module) of the object + with the docstring with tests to be debugged. + """ + module = _normalize_module(module) + testsrc = testsource(module, name) + debug_script(testsrc, pm, module.__dict__) + +###################################################################### +## 10. Example Usage +###################################################################### +class _TestClass: + """ + A pointless class, for sanity-checking of docstring testing. + + Methods: + square() + get() + + >>> _TestClass(13).get() + _TestClass(-12).get() + 1 + >>> hex(_TestClass(13).square().get()) + '0xa9' + """ + + def __init__(self, val): + """val -> _TestClass object with associated value val. + + >>> t = _TestClass(123) + >>> print(t.get()) + 123 + """ + + self.val = val + + def square(self): + """square() -> square TestClass's associated value + + >>> _TestClass(13).square().get() + 169 + """ + + self.val = self.val ** 2 + return self + + def get(self): + """get() -> return TestClass's associated value. + + >>> x = _TestClass(-42) + >>> print(x.get()) + -42 + """ + + return self.val + +__test__ = {"_TestClass": _TestClass, + "string": r""" + Example of a string object, searched as-is. + >>> x = 1; y = 2 + >>> x + y, x * y + (3, 2) + """, + + "bool-int equivalence": r""" + In 2.2, boolean expressions displayed + 0 or 1. By default, we still accept + them. This can be disabled by passing + DONT_ACCEPT_TRUE_FOR_1 to the new + optionflags argument. + >>> 4 == 4 + 1 + >>> 4 == 4 + True + >>> 4 > 4 + 0 + >>> 4 > 4 + False + """, + + "blank lines": r""" + Blank lines can be marked with <BLANKLINE>: + >>> print('foo\n\nbar\n') + foo + <BLANKLINE> + bar + <BLANKLINE> + """, + + "ellipsis": r""" + If the ellipsis flag is used, then '...' can be used to + elide substrings in the desired output: + >>> print(range(1000)) #doctest: +ELLIPSIS + [0, 1, 2, ..., 999] + """, + + "whitespace normalization": r""" + If the whitespace normalization flag is used, then + differences in whitespace are ignored. + >>> print(list(xrange(30))) #doctest: +NORMALIZE_WHITESPACE + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29] + """, + } + +def _test(): + r = unittest.TextTestRunner() + r.run(DocTestSuite()) + +if __name__ == "__main__": + _test() diff --git a/lib/python2.7/site-packages/django/test/client.py b/lib/python2.7/site-packages/django/test/client.py new file mode 100644 index 0000000..6ea8119 --- /dev/null +++ b/lib/python2.7/site-packages/django/test/client.py @@ -0,0 +1,623 @@ +from __future__ import unicode_literals + +import sys +import os +import re +import mimetypes +from copy import copy +from io import BytesIO + +from django.conf import settings +from django.contrib.auth import authenticate, login, logout, get_user_model +from django.core.handlers.base import BaseHandler +from django.core.handlers.wsgi import WSGIRequest +from django.core.signals import (request_started, request_finished, + got_request_exception) +from django.db import close_old_connections +from django.http import SimpleCookie, HttpRequest, QueryDict +from django.template import TemplateDoesNotExist +from django.test import signals +from django.utils.functional import curry +from django.utils.encoding import force_bytes, force_str +from django.utils.http import urlencode +from django.utils.importlib import import_module +from django.utils.itercompat import is_iterable +from django.utils import six +from django.utils.six.moves.urllib.parse import unquote, urlparse, urlsplit +from django.test.utils import ContextList + +__all__ = ('Client', 'RequestFactory', 'encode_file', 'encode_multipart') + + +BOUNDARY = 'BoUnDaRyStRiNg' +MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY +CONTENT_TYPE_RE = re.compile('.*; charset=([\w\d-]+);?') + +class FakePayload(object): + """ + A wrapper around BytesIO that restricts what can be read since data from + the network can't be seeked and cannot be read outside of its content + length. This makes sure that views can't do anything under the test client + that wouldn't work in Real Life. + """ + def __init__(self, content=None): + self.__content = BytesIO() + self.__len = 0 + self.read_started = False + if content is not None: + self.write(content) + + def __len__(self): + return self.__len + + def read(self, num_bytes=None): + if not self.read_started: + self.__content.seek(0) + self.read_started = True + if num_bytes is None: + num_bytes = self.__len or 0 + assert self.__len >= num_bytes, "Cannot read more than the available bytes from the HTTP incoming data." + content = self.__content.read(num_bytes) + self.__len -= num_bytes + return content + + def write(self, content): + if self.read_started: + raise ValueError("Unable to write a payload after he's been read") + content = force_bytes(content) + self.__content.write(content) + self.__len += len(content) + + +def closing_iterator_wrapper(iterable, close): + try: + for item in iterable: + yield item + finally: + request_finished.disconnect(close_old_connections) + close() # will fire request_finished + request_finished.connect(close_old_connections) + + +class ClientHandler(BaseHandler): + """ + A HTTP Handler that can be used for testing purposes. + Uses the WSGI interface to compose requests, but returns + the raw HttpResponse object + """ + def __init__(self, enforce_csrf_checks=True, *args, **kwargs): + self.enforce_csrf_checks = enforce_csrf_checks + super(ClientHandler, self).__init__(*args, **kwargs) + + def __call__(self, environ): + from django.conf import settings + + # Set up middleware if needed. We couldn't do this earlier, because + # settings weren't available. + if self._request_middleware is None: + self.load_middleware() + + request_started.disconnect(close_old_connections) + request_started.send(sender=self.__class__) + request_started.connect(close_old_connections) + request = WSGIRequest(environ) + # sneaky little hack so that we can easily get round + # CsrfViewMiddleware. This makes life easier, and is probably + # required for backwards compatibility with external tests against + # admin views. + request._dont_enforce_csrf_checks = not self.enforce_csrf_checks + response = self.get_response(request) + # We're emulating a WSGI server; we must call the close method + # on completion. + if response.streaming: + response.streaming_content = closing_iterator_wrapper( + response.streaming_content, response.close) + else: + request_finished.disconnect(close_old_connections) + response.close() # will fire request_finished + request_finished.connect(close_old_connections) + + return response + +def store_rendered_templates(store, signal, sender, template, context, **kwargs): + """ + Stores templates and contexts that are rendered. + + The context is copied so that it is an accurate representation at the time + of rendering. + """ + store.setdefault('templates', []).append(template) + store.setdefault('context', ContextList()).append(copy(context)) + +def encode_multipart(boundary, data): + """ + Encodes multipart POST data from a dictionary of form values. + + The key will be used as the form data name; the value will be transmitted + as content. If the value is a file, the contents of the file will be sent + as an application/octet-stream; otherwise, str(value) will be sent. + """ + lines = [] + to_bytes = lambda s: force_bytes(s, settings.DEFAULT_CHARSET) + + # Not by any means perfect, but good enough for our purposes. + is_file = lambda thing: hasattr(thing, "read") and callable(thing.read) + + # Each bit of the multipart form data could be either a form value or a + # file, or a *list* of form values and/or files. Remember that HTTP field + # names can be duplicated! + for (key, value) in data.items(): + if is_file(value): + lines.extend(encode_file(boundary, key, value)) + elif not isinstance(value, six.string_types) and is_iterable(value): + for item in value: + if is_file(item): + lines.extend(encode_file(boundary, key, item)) + else: + lines.extend([to_bytes(val) for val in [ + '--%s' % boundary, + 'Content-Disposition: form-data; name="%s"' % key, + '', + item + ]]) + else: + lines.extend([to_bytes(val) for val in [ + '--%s' % boundary, + 'Content-Disposition: form-data; name="%s"' % key, + '', + value + ]]) + + lines.extend([ + to_bytes('--%s--' % boundary), + b'', + ]) + return b'\r\n'.join(lines) + +def encode_file(boundary, key, file): + to_bytes = lambda s: force_bytes(s, settings.DEFAULT_CHARSET) + content_type = mimetypes.guess_type(file.name)[0] + if content_type is None: + content_type = 'application/octet-stream' + return [ + to_bytes('--%s' % boundary), + to_bytes('Content-Disposition: form-data; name="%s"; filename="%s"' \ + % (key, os.path.basename(file.name))), + to_bytes('Content-Type: %s' % content_type), + b'', + file.read() + ] + + +class RequestFactory(object): + """ + Class that lets you create mock Request objects for use in testing. + + Usage: + + rf = RequestFactory() + get_request = rf.get('/hello/') + post_request = rf.post('/submit/', {'foo': 'bar'}) + + Once you have a request object you can pass it to any view function, + just as if that view had been hooked up using a URLconf. + """ + def __init__(self, **defaults): + self.defaults = defaults + self.cookies = SimpleCookie() + self.errors = BytesIO() + + def _base_environ(self, **request): + """ + The base environment for a request. + """ + # This is a minimal valid WSGI environ dictionary, plus: + # - HTTP_COOKIE: for cookie support, + # - REMOTE_ADDR: often useful, see #8551. + # See http://www.python.org/dev/peps/pep-3333/#environ-variables + environ = { + 'HTTP_COOKIE': self.cookies.output(header='', sep='; '), + 'PATH_INFO': str('/'), + 'REMOTE_ADDR': str('127.0.0.1'), + 'REQUEST_METHOD': str('GET'), + 'SCRIPT_NAME': str(''), + 'SERVER_NAME': str('testserver'), + 'SERVER_PORT': str('80'), + 'SERVER_PROTOCOL': str('HTTP/1.1'), + 'wsgi.version': (1, 0), + 'wsgi.url_scheme': str('http'), + 'wsgi.input': FakePayload(b''), + 'wsgi.errors': self.errors, + 'wsgi.multiprocess': True, + 'wsgi.multithread': False, + 'wsgi.run_once': False, + } + environ.update(self.defaults) + environ.update(request) + return environ + + def request(self, **request): + "Construct a generic request object." + return WSGIRequest(self._base_environ(**request)) + + def _encode_data(self, data, content_type, ): + if content_type is MULTIPART_CONTENT: + return encode_multipart(BOUNDARY, data) + else: + # Encode the content so that the byte representation is correct. + match = CONTENT_TYPE_RE.match(content_type) + if match: + charset = match.group(1) + else: + charset = settings.DEFAULT_CHARSET + return force_bytes(data, encoding=charset) + + def _get_path(self, parsed): + path = force_str(parsed[2]) + # If there are parameters, add them + if parsed[3]: + path += str(";") + force_str(parsed[3]) + path = unquote(path) + # WSGI requires latin-1 encoded strings. See get_path_info(). + if six.PY3: + path = path.encode('utf-8').decode('iso-8859-1') + return path + + def get(self, path, data={}, **extra): + "Construct a GET request." + + parsed = urlparse(path) + query_string = urlencode(data, doseq=True) or force_str(parsed[4]) + if six.PY3: + query_string = query_string.encode('utf-8').decode('iso-8859-1') + + r = { + 'PATH_INFO': self._get_path(parsed), + 'QUERY_STRING': query_string, + 'REQUEST_METHOD': str('GET'), + } + r.update(extra) + return self.request(**r) + + def post(self, path, data={}, content_type=MULTIPART_CONTENT, + **extra): + "Construct a POST request." + + post_data = self._encode_data(data, content_type) + + parsed = urlparse(path) + query_string = force_str(parsed[4]) + if six.PY3: + query_string = query_string.encode('utf-8').decode('iso-8859-1') + + r = { + 'CONTENT_LENGTH': len(post_data), + 'CONTENT_TYPE': content_type, + 'PATH_INFO': self._get_path(parsed), + 'QUERY_STRING': query_string, + 'REQUEST_METHOD': str('POST'), + 'wsgi.input': FakePayload(post_data), + } + r.update(extra) + return self.request(**r) + + def head(self, path, data={}, **extra): + "Construct a HEAD request." + + parsed = urlparse(path) + query_string = urlencode(data, doseq=True) or force_str(parsed[4]) + if six.PY3: + query_string = query_string.encode('utf-8').decode('iso-8859-1') + + r = { + 'PATH_INFO': self._get_path(parsed), + 'QUERY_STRING': query_string, + 'REQUEST_METHOD': str('HEAD'), + } + r.update(extra) + return self.request(**r) + + def options(self, path, data='', content_type='application/octet-stream', + **extra): + "Construct an OPTIONS request." + return self.generic('OPTIONS', path, data, content_type, **extra) + + def put(self, path, data='', content_type='application/octet-stream', + **extra): + "Construct a PUT request." + return self.generic('PUT', path, data, content_type, **extra) + + def patch(self, path, data='', content_type='application/octet-stream', + **extra): + "Construct a PATCH request." + return self.generic('PATCH', path, data, content_type, **extra) + + def delete(self, path, data='', content_type='application/octet-stream', + **extra): + "Construct a DELETE request." + return self.generic('DELETE', path, data, content_type, **extra) + + def generic(self, method, path, + data='', content_type='application/octet-stream', **extra): + parsed = urlparse(path) + data = force_bytes(data, settings.DEFAULT_CHARSET) + r = { + 'PATH_INFO': self._get_path(parsed), + 'REQUEST_METHOD': str(method), + } + if data: + r.update({ + 'CONTENT_LENGTH': len(data), + 'CONTENT_TYPE': str(content_type), + 'wsgi.input': FakePayload(data), + }) + r.update(extra) + # If QUERY_STRING is absent or empty, we want to extract it from the URL. + if not r.get('QUERY_STRING'): + query_string = force_bytes(parsed[4]) + # WSGI requires latin-1 encoded strings. See get_path_info(). + if six.PY3: + query_string = query_string.decode('iso-8859-1') + r['QUERY_STRING'] = query_string + return self.request(**r) + + +class Client(RequestFactory): + """ + A class that can act as a client for testing purposes. + + It allows the user to compose GET and POST requests, and + obtain the response that the server gave to those requests. + The server Response objects are annotated with the details + of the contexts and templates that were rendered during the + process of serving the request. + + Client objects are stateful - they will retain cookie (and + thus session) details for the lifetime of the Client instance. + + This is not intended as a replacement for Twill/Selenium or + the like - it is here to allow testing against the + contexts and templates produced by a view, rather than the + HTML rendered to the end-user. + """ + def __init__(self, enforce_csrf_checks=False, **defaults): + super(Client, self).__init__(**defaults) + self.handler = ClientHandler(enforce_csrf_checks) + self.exc_info = None + + def store_exc_info(self, **kwargs): + """ + Stores exceptions when they are generated by a view. + """ + self.exc_info = sys.exc_info() + + def _session(self): + """ + Obtains the current session variables. + """ + if 'django.contrib.sessions' in settings.INSTALLED_APPS: + engine = import_module(settings.SESSION_ENGINE) + cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None) + if cookie: + return engine.SessionStore(cookie.value) + return {} + session = property(_session) + + + def request(self, **request): + """ + The master request method. Composes the environment dictionary + and passes to the handler, returning the result of the handler. + Assumes defaults for the query environment, which can be overridden + using the arguments to the request. + """ + environ = self._base_environ(**request) + + # Curry a data dictionary into an instance of the template renderer + # callback function. + data = {} + on_template_render = curry(store_rendered_templates, data) + signals.template_rendered.connect(on_template_render, dispatch_uid="template-render") + # Capture exceptions created by the handler. + got_request_exception.connect(self.store_exc_info, dispatch_uid="request-exception") + try: + + try: + response = self.handler(environ) + except TemplateDoesNotExist as e: + # If the view raises an exception, Django will attempt to show + # the 500.html template. If that template is not available, + # we should ignore the error in favor of re-raising the + # underlying exception that caused the 500 error. Any other + # template found to be missing during view error handling + # should be reported as-is. + if e.args != ('500.html',): + raise + + # Look for a signalled exception, clear the current context + # exception data, then re-raise the signalled exception. + # Also make sure that the signalled exception is cleared from + # the local cache! + if self.exc_info: + exc_info = self.exc_info + self.exc_info = None + six.reraise(*exc_info) + + # Save the client and request that stimulated the response. + response.client = self + response.request = request + + # Add any rendered template detail to the response. + response.templates = data.get("templates", []) + response.context = data.get("context") + + # Flatten a single context. Not really necessary anymore thanks to + # the __getattr__ flattening in ContextList, but has some edge-case + # backwards-compatibility implications. + if response.context and len(response.context) == 1: + response.context = response.context[0] + + # Update persistent cookie data. + if response.cookies: + self.cookies.update(response.cookies) + + return response + finally: + signals.template_rendered.disconnect(dispatch_uid="template-render") + got_request_exception.disconnect(dispatch_uid="request-exception") + + def get(self, path, data={}, follow=False, **extra): + """ + Requests a response from the server using GET. + """ + response = super(Client, self).get(path, data=data, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def post(self, path, data={}, content_type=MULTIPART_CONTENT, + follow=False, **extra): + """ + Requests a response from the server using POST. + """ + response = super(Client, self).post(path, data=data, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def head(self, path, data={}, follow=False, **extra): + """ + Request a response from the server using HEAD. + """ + response = super(Client, self).head(path, data=data, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def options(self, path, data='', content_type='application/octet-stream', + follow=False, **extra): + """ + Request a response from the server using OPTIONS. + """ + response = super(Client, self).options(path, + data=data, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def put(self, path, data='', content_type='application/octet-stream', + follow=False, **extra): + """ + Send a resource to the server using PUT. + """ + response = super(Client, self).put(path, + data=data, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def patch(self, path, data='', content_type='application/octet-stream', + follow=False, **extra): + """ + Send a resource to the server using PATCH. + """ + response = super(Client, self).patch( + path, data=data, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def delete(self, path, data='', content_type='application/octet-stream', + follow=False, **extra): + """ + Send a DELETE request to the server. + """ + response = super(Client, self).delete(path, + data=data, content_type=content_type, **extra) + if follow: + response = self._handle_redirects(response, **extra) + return response + + def login(self, **credentials): + """ + Sets the Factory to appear as if it has successfully logged into a site. + + Returns True if login is possible; False if the provided credentials + are incorrect, or the user is inactive, or if the sessions framework is + not available. + """ + user = authenticate(**credentials) + if user and user.is_active \ + and 'django.contrib.sessions' in settings.INSTALLED_APPS: + engine = import_module(settings.SESSION_ENGINE) + + # Create a fake request to store login details. + request = HttpRequest() + if self.session: + request.session = self.session + else: + request.session = engine.SessionStore() + login(request, user) + + # Save the session values. + request.session.save() + + # Set the cookie to represent the session. + session_cookie = settings.SESSION_COOKIE_NAME + self.cookies[session_cookie] = request.session.session_key + cookie_data = { + 'max-age': None, + 'path': '/', + 'domain': settings.SESSION_COOKIE_DOMAIN, + 'secure': settings.SESSION_COOKIE_SECURE or None, + 'expires': None, + } + self.cookies[session_cookie].update(cookie_data) + + return True + else: + return False + + def logout(self): + """ + Removes the authenticated user's cookies and session object. + + Causes the authenticated user to be logged out. + """ + request = HttpRequest() + engine = import_module(settings.SESSION_ENGINE) + UserModel = get_user_model() + if self.session: + request.session = self.session + uid = self.session.get("_auth_user_id") + if uid: + request.user = UserModel._default_manager.get(pk=uid) + else: + request.session = engine.SessionStore() + logout(request) + self.cookies = SimpleCookie() + + def _handle_redirects(self, response, **extra): + "Follows any redirects by requesting responses from the server using GET." + + response.redirect_chain = [] + while response.status_code in (301, 302, 303, 307): + url = response.url + redirect_chain = response.redirect_chain + redirect_chain.append((url, response.status_code)) + + url = urlsplit(url) + if url.scheme: + extra['wsgi.url_scheme'] = url.scheme + if url.hostname: + extra['SERVER_NAME'] = url.hostname + if url.port: + extra['SERVER_PORT'] = str(url.port) + + response = self.get(url.path, QueryDict(url.query), follow=False, **extra) + response.redirect_chain = redirect_chain + + # Prevent loops + if response.redirect_chain[-1] in response.redirect_chain[0:-1]: + break + return response diff --git a/lib/python2.7/site-packages/django/test/html.py b/lib/python2.7/site-packages/django/test/html.py new file mode 100644 index 0000000..0d30bd2 --- /dev/null +++ b/lib/python2.7/site-packages/django/test/html.py @@ -0,0 +1,238 @@ +""" +Comparing two html documents. +""" + +from __future__ import unicode_literals + +import re +from django.utils.encoding import force_text +from django.utils.html_parser import HTMLParser, HTMLParseError +from django.utils import six +from django.utils.encoding import python_2_unicode_compatible + + +WHITESPACE = re.compile('\s+') + + +def normalize_whitespace(string): + return WHITESPACE.sub(' ', string) + + +@python_2_unicode_compatible +class Element(object): + def __init__(self, name, attributes): + self.name = name + self.attributes = sorted(attributes) + self.children = [] + + def append(self, element): + if isinstance(element, six.string_types): + element = force_text(element) + element = normalize_whitespace(element) + if self.children: + if isinstance(self.children[-1], six.string_types): + self.children[-1] += element + self.children[-1] = normalize_whitespace(self.children[-1]) + return + elif self.children: + # removing last children if it is only whitespace + # this can result in incorrect dom representations since + # whitespace between inline tags like <span> is significant + if isinstance(self.children[-1], six.string_types): + if self.children[-1].isspace(): + self.children.pop() + if element: + self.children.append(element) + + def finalize(self): + def rstrip_last_element(children): + if children: + if isinstance(children[-1], six.string_types): + children[-1] = children[-1].rstrip() + if not children[-1]: + children.pop() + children = rstrip_last_element(children) + return children + + rstrip_last_element(self.children) + for i, child in enumerate(self.children): + if isinstance(child, six.string_types): + self.children[i] = child.strip() + elif hasattr(child, 'finalize'): + child.finalize() + + def __eq__(self, element): + if not hasattr(element, 'name'): + return False + if hasattr(element, 'name') and self.name != element.name: + return False + if len(self.attributes) != len(element.attributes): + return False + if self.attributes != element.attributes: + # attributes without a value is same as attribute with value that + # equals the attributes name: + # <input checked> == <input checked="checked"> + for i in range(len(self.attributes)): + attr, value = self.attributes[i] + other_attr, other_value = element.attributes[i] + if value is None: + value = attr + if other_value is None: + other_value = other_attr + if attr != other_attr or value != other_value: + return False + if self.children != element.children: + return False + return True + + def __hash__(self): + return hash((self.name,) + tuple(a for a in self.attributes)) + + def __ne__(self, element): + return not self.__eq__(element) + + def _count(self, element, count=True): + if not isinstance(element, six.string_types): + if self == element: + return 1 + i = 0 + for child in self.children: + # child is text content and element is also text content, then + # make a simple "text" in "text" + if isinstance(child, six.string_types): + if isinstance(element, six.string_types): + if count: + i += child.count(element) + elif element in child: + return 1 + else: + i += child._count(element, count=count) + if not count and i: + return i + return i + + def __contains__(self, element): + return self._count(element, count=False) > 0 + + def count(self, element): + return self._count(element, count=True) + + def __getitem__(self, key): + return self.children[key] + + def __str__(self): + output = '<%s' % self.name + for key, value in self.attributes: + if value: + output += ' %s="%s"' % (key, value) + else: + output += ' %s' % key + if self.children: + output += '>\n' + output += ''.join(six.text_type(c) for c in self.children) + output += '\n</%s>' % self.name + else: + output += ' />' + return output + + def __repr__(self): + return six.text_type(self) + + +@python_2_unicode_compatible +class RootElement(Element): + def __init__(self): + super(RootElement, self).__init__(None, ()) + + def __str__(self): + return ''.join(six.text_type(c) for c in self.children) + + +class Parser(HTMLParser): + SELF_CLOSING_TAGS = ('br' , 'hr', 'input', 'img', 'meta', 'spacer', + 'link', 'frame', 'base', 'col') + + def __init__(self): + HTMLParser.__init__(self) + self.root = RootElement() + self.open_tags = [] + self.element_positions = {} + + def error(self, msg): + raise HTMLParseError(msg, self.getpos()) + + def format_position(self, position=None, element=None): + if not position and element: + position = self.element_positions[element] + if position is None: + position = self.getpos() + if hasattr(position, 'lineno'): + position = position.lineno, position.offset + return 'Line %d, Column %d' % position + + @property + def current(self): + if self.open_tags: + return self.open_tags[-1] + else: + return self.root + + def handle_startendtag(self, tag, attrs): + self.handle_starttag(tag, attrs) + if tag not in self.SELF_CLOSING_TAGS: + self.handle_endtag(tag) + + def handle_starttag(self, tag, attrs): + # Special case handling of 'class' attribute, so that comparisons of DOM + # instances are not sensitive to ordering of classes. + attrs = [ + (name, " ".join(sorted(value.split(" ")))) + if name == "class" + else (name, value) + for name, value in attrs + ] + element = Element(tag, attrs) + self.current.append(element) + if tag not in self.SELF_CLOSING_TAGS: + self.open_tags.append(element) + self.element_positions[element] = self.getpos() + + def handle_endtag(self, tag): + if not self.open_tags: + self.error("Unexpected end tag `%s` (%s)" % ( + tag, self.format_position())) + element = self.open_tags.pop() + while element.name != tag: + if not self.open_tags: + self.error("Unexpected end tag `%s` (%s)" % ( + tag, self.format_position())) + element = self.open_tags.pop() + + def handle_data(self, data): + self.current.append(data) + + def handle_charref(self, name): + self.current.append('&%s;' % name) + + def handle_entityref(self, name): + self.current.append('&%s;' % name) + + +def parse_html(html): + """ + Takes a string that contains *valid* HTML and turns it into a Python object + structure that can be easily compared against other HTML on semantic + equivalence. Syntactical differences like which quotation is used on + arguments will be ignored. + + """ + parser = Parser() + parser.feed(html) + parser.close() + document = parser.root + document.finalize() + # Removing ROOT element if it's not necessary + if len(document.children) == 1: + if not isinstance(document.children[0], six.string_types): + document = document.children[0] + return document diff --git a/lib/python2.7/site-packages/django/test/runner.py b/lib/python2.7/site-packages/django/test/runner.py new file mode 100644 index 0000000..d8db8da --- /dev/null +++ b/lib/python2.7/site-packages/django/test/runner.py @@ -0,0 +1,291 @@ +import os +from optparse import make_option + +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase +from django.test.utils import setup_test_environment, teardown_test_environment +from django.utils import unittest +from django.utils.unittest import TestSuite, defaultTestLoader + + +class DiscoverRunner(object): + """ + A Django test runner that uses unittest2 test discovery. + """ + + test_loader = defaultTestLoader + reorder_by = (TestCase, ) + option_list = ( + make_option('-t', '--top-level-directory', + action='store', dest='top_level', default=None, + help='Top level of project for unittest discovery.'), + make_option('-p', '--pattern', action='store', dest='pattern', + default="test*.py", + help='The test matching pattern. Defaults to test*.py.'), + ) + + def __init__(self, pattern=None, top_level=None, + verbosity=1, interactive=True, failfast=False, + **kwargs): + + self.pattern = pattern + self.top_level = top_level + + self.verbosity = verbosity + self.interactive = interactive + self.failfast = failfast + + def setup_test_environment(self, **kwargs): + setup_test_environment() + settings.DEBUG = False + unittest.installHandler() + + def build_suite(self, test_labels=None, extra_tests=None, **kwargs): + suite = TestSuite() + test_labels = test_labels or ['.'] + extra_tests = extra_tests or [] + + discover_kwargs = {} + if self.pattern is not None: + discover_kwargs['pattern'] = self.pattern + if self.top_level is not None: + discover_kwargs['top_level_dir'] = self.top_level + + for label in test_labels: + kwargs = discover_kwargs.copy() + tests = None + + label_as_path = os.path.abspath(label) + + # if a module, or "module.ClassName[.method_name]", just run those + if not os.path.exists(label_as_path): + tests = self.test_loader.loadTestsFromName(label) + elif os.path.isdir(label_as_path) and not self.top_level: + # Try to be a bit smarter than unittest about finding the + # default top-level for a given directory path, to avoid + # breaking relative imports. (Unittest's default is to set + # top-level equal to the path, which means relative imports + # will result in "Attempted relative import in non-package."). + + # We'd be happy to skip this and require dotted module paths + # (which don't cause this problem) instead of file paths (which + # do), but in the case of a directory in the cwd, which would + # be equally valid if considered as a top-level module or as a + # directory path, unittest unfortunately prefers the latter. + + top_level = label_as_path + while True: + init_py = os.path.join(top_level, '__init__.py') + if os.path.exists(init_py): + try_next = os.path.dirname(top_level) + if try_next == top_level: + # __init__.py all the way down? give up. + break + top_level = try_next + continue + break + kwargs['top_level_dir'] = top_level + + + if not (tests and tests.countTestCases()): + # if no tests found, it's probably a package; try discovery + tests = self.test_loader.discover(start_dir=label, **kwargs) + + # make unittest forget the top-level dir it calculated from this + # run, to support running tests from two different top-levels. + self.test_loader._top_level_dir = None + + suite.addTests(tests) + + for test in extra_tests: + suite.addTest(test) + + return reorder_suite(suite, self.reorder_by) + + def setup_databases(self, **kwargs): + return setup_databases(self.verbosity, self.interactive, **kwargs) + + def run_suite(self, suite, **kwargs): + return unittest.TextTestRunner( + verbosity=self.verbosity, + failfast=self.failfast, + ).run(suite) + + def teardown_databases(self, old_config, **kwargs): + """ + Destroys all the non-mirror databases. + """ + old_names, mirrors = old_config + for connection, old_name, destroy in old_names: + if destroy: + connection.creation.destroy_test_db(old_name, self.verbosity) + + def teardown_test_environment(self, **kwargs): + unittest.removeHandler() + teardown_test_environment() + + def suite_result(self, suite, result, **kwargs): + return len(result.failures) + len(result.errors) + + def run_tests(self, test_labels, extra_tests=None, **kwargs): + """ + Run the unit tests for all the test labels in the provided list. + + Test labels should be dotted Python paths to test modules, test + classes, or test methods. + + A list of 'extra' tests may also be provided; these tests + will be added to the test suite. + + Returns the number of tests that failed. + """ + self.setup_test_environment() + suite = self.build_suite(test_labels, extra_tests) + old_config = self.setup_databases() + result = self.run_suite(suite) + self.teardown_databases(old_config) + self.teardown_test_environment() + return self.suite_result(suite, result) + + +def dependency_ordered(test_databases, dependencies): + """ + Reorder test_databases into an order that honors the dependencies + described in TEST_DEPENDENCIES. + """ + ordered_test_databases = [] + resolved_databases = set() + + # Maps db signature to dependencies of all it's aliases + dependencies_map = {} + + # sanity check - no DB can depend on it's own alias + for sig, (_, aliases) in test_databases: + all_deps = set() + for alias in aliases: + all_deps.update(dependencies.get(alias, [])) + if not all_deps.isdisjoint(aliases): + raise ImproperlyConfigured( + "Circular dependency: databases %r depend on each other, " + "but are aliases." % aliases) + dependencies_map[sig] = all_deps + + while test_databases: + changed = False + deferred = [] + + # Try to find a DB that has all it's dependencies met + for signature, (db_name, aliases) in test_databases: + if dependencies_map[signature].issubset(resolved_databases): + resolved_databases.update(aliases) + ordered_test_databases.append((signature, (db_name, aliases))) + changed = True + else: + deferred.append((signature, (db_name, aliases))) + + if not changed: + raise ImproperlyConfigured( + "Circular dependency in TEST_DEPENDENCIES") + test_databases = deferred + return ordered_test_databases + + +def reorder_suite(suite, classes): + """ + Reorders a test suite by test type. + + `classes` is a sequence of types + + All tests of type classes[0] are placed first, then tests of type + classes[1], etc. Tests with no match in classes are placed last. + """ + class_count = len(classes) + bins = [unittest.TestSuite() for i in range(class_count+1)] + partition_suite(suite, classes, bins) + for i in range(class_count): + bins[0].addTests(bins[i+1]) + return bins[0] + + +def partition_suite(suite, classes, bins): + """ + Partitions a test suite by test type. + + classes is a sequence of types + bins is a sequence of TestSuites, one more than classes + + Tests of type classes[i] are added to bins[i], + tests with no match found in classes are place in bins[-1] + """ + for test in suite: + if isinstance(test, unittest.TestSuite): + partition_suite(test, classes, bins) + else: + for i in range(len(classes)): + if isinstance(test, classes[i]): + bins[i].addTest(test) + break + else: + bins[-1].addTest(test) + + +def setup_databases(verbosity, interactive, **kwargs): + from django.db import connections, DEFAULT_DB_ALIAS + + # First pass -- work out which databases actually need to be created, + # and which ones are test mirrors or duplicate entries in DATABASES + mirrored_aliases = {} + test_databases = {} + dependencies = {} + default_sig = connections[DEFAULT_DB_ALIAS].creation.test_db_signature() + for alias in connections: + connection = connections[alias] + if connection.settings_dict['TEST_MIRROR']: + # If the database is marked as a test mirror, save + # the alias. + mirrored_aliases[alias] = ( + connection.settings_dict['TEST_MIRROR']) + else: + # Store a tuple with DB parameters that uniquely identify it. + # If we have two aliases with the same values for that tuple, + # we only need to create the test database once. + item = test_databases.setdefault( + connection.creation.test_db_signature(), + (connection.settings_dict['NAME'], set()) + ) + item[1].add(alias) + + if 'TEST_DEPENDENCIES' in connection.settings_dict: + dependencies[alias] = ( + connection.settings_dict['TEST_DEPENDENCIES']) + else: + if alias != DEFAULT_DB_ALIAS and connection.creation.test_db_signature() != default_sig: + dependencies[alias] = connection.settings_dict.get( + 'TEST_DEPENDENCIES', [DEFAULT_DB_ALIAS]) + + # Second pass -- actually create the databases. + old_names = [] + mirrors = [] + + for signature, (db_name, aliases) in dependency_ordered( + test_databases.items(), dependencies): + test_db_name = None + # Actually create the database for the first connection + for alias in aliases: + connection = connections[alias] + if test_db_name is None: + test_db_name = connection.creation.create_test_db( + verbosity, autoclobber=not interactive) + destroy = True + else: + connection.settings_dict['NAME'] = test_db_name + destroy = False + old_names.append((connection, db_name, destroy)) + + for alias, mirror_alias in mirrored_aliases.items(): + mirrors.append((alias, connections[alias].settings_dict['NAME'])) + connections[alias].settings_dict['NAME'] = ( + connections[mirror_alias].settings_dict['NAME']) + + return old_names, mirrors diff --git a/lib/python2.7/site-packages/django/test/signals.py b/lib/python2.7/site-packages/django/test/signals.py new file mode 100644 index 0000000..a96bdff --- /dev/null +++ b/lib/python2.7/site-packages/django/test/signals.py @@ -0,0 +1,81 @@ +import os +import time + +from django.conf import settings +from django.db import connections +from django.dispatch import receiver, Signal +from django.utils import timezone +from django.utils.functional import empty + +template_rendered = Signal(providing_args=["template", "context"]) + +setting_changed = Signal(providing_args=["setting", "value"]) + +# Most setting_changed receivers are supposed to be added below, +# except for cases where the receiver is related to a contrib app. + + +@receiver(setting_changed) +def update_connections_time_zone(**kwargs): + if kwargs['setting'] == 'TIME_ZONE': + # Reset process time zone + if hasattr(time, 'tzset'): + if kwargs['value']: + os.environ['TZ'] = kwargs['value'] + else: + os.environ.pop('TZ', None) + time.tzset() + + # Reset local time zone cache + timezone._localtime = None + + # Reset the database connections' time zone + if kwargs['setting'] == 'USE_TZ' and settings.TIME_ZONE != 'UTC': + USE_TZ, TIME_ZONE = kwargs['value'], settings.TIME_ZONE + elif kwargs['setting'] == 'TIME_ZONE' and not settings.USE_TZ: + USE_TZ, TIME_ZONE = settings.USE_TZ, kwargs['value'] + else: + # no need to change the database connnections' time zones + return + tz = 'UTC' if USE_TZ else TIME_ZONE + for conn in connections.all(): + conn.settings_dict['TIME_ZONE'] = tz + tz_sql = conn.ops.set_time_zone_sql() + if tz_sql: + conn.cursor().execute(tz_sql, [tz]) + + +@receiver(setting_changed) +def clear_context_processors_cache(**kwargs): + if kwargs['setting'] == 'TEMPLATE_CONTEXT_PROCESSORS': + from django.template import context + context._standard_context_processors = None + + +@receiver(setting_changed) +def clear_template_loaders_cache(**kwargs): + if kwargs['setting'] == 'TEMPLATE_LOADERS': + from django.template import loader + loader.template_source_loaders = None + + +@receiver(setting_changed) +def clear_serializers_cache(**kwargs): + if kwargs['setting'] == 'SERIALIZATION_MODULES': + from django.core import serializers + serializers._serializers = {} + + +@receiver(setting_changed) +def language_changed(**kwargs): + if kwargs['setting'] in ('LOCALE_PATHS', 'LANGUAGE_CODE'): + from django.utils.translation import trans_real + trans_real._default = None + if kwargs['setting'] == 'LOCALE_PATHS': + trans_real._translations = {} + +@receiver(setting_changed) +def file_storage_changed(**kwargs): + if kwargs['setting'] in ('MEDIA_ROOT', 'DEFAULT_FILE_STORAGE'): + from django.core.files.storage import default_storage + default_storage._wrapped = empty diff --git a/lib/python2.7/site-packages/django/test/simple.py b/lib/python2.7/site-packages/django/test/simple.py new file mode 100644 index 0000000..f28b8a2 --- /dev/null +++ b/lib/python2.7/site-packages/django/test/simple.py @@ -0,0 +1,253 @@ +""" +This module is pending deprecation as of Django 1.6 and will be removed in +version 1.8. + +""" +import json +import re +import unittest as real_unittest +import warnings + +from django.db.models import get_app, get_apps +from django.test import _doctest as doctest +from django.test import runner +from django.test.utils import compare_xml, strip_quotes +from django.utils import unittest +from django.utils.importlib import import_module +from django.utils.module_loading import module_has_submodule + +__all__ = ('DjangoTestSuiteRunner',) + +warnings.warn( + "The django.test.simple module and DjangoTestSuiteRunner are deprecated; " + "use django.test.runner.DiscoverRunner instead.", + PendingDeprecationWarning) + +# The module name for tests outside models.py +TEST_MODULE = 'tests' + + +normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s) +normalize_decimals = lambda s: re.sub(r"Decimal\('(\d+(\.\d*)?)'\)", + lambda m: "Decimal(\"%s\")" % m.groups()[0], s) + + +class OutputChecker(doctest.OutputChecker): + def check_output(self, want, got, optionflags): + """ + The entry method for doctest output checking. Defers to a sequence of + child checkers + """ + checks = (self.check_output_default, + self.check_output_numeric, + self.check_output_xml, + self.check_output_json) + for check in checks: + if check(want, got, optionflags): + return True + return False + + def check_output_default(self, want, got, optionflags): + """ + The default comparator provided by doctest - not perfect, but good for + most purposes + """ + return doctest.OutputChecker.check_output(self, want, got, optionflags) + + def check_output_numeric(self, want, got, optionflags): + """Doctest does an exact string comparison of output, which means that + some numerically equivalent values aren't equal. This check normalizes + * long integers (22L) so that they equal normal integers. (22) + * Decimals so that they are comparable, regardless of the change + made to __repr__ in Python 2.6. + """ + return doctest.OutputChecker.check_output(self, + normalize_decimals(normalize_long_ints(want)), + normalize_decimals(normalize_long_ints(got)), + optionflags) + + def check_output_xml(self, want, got, optionsflags): + try: + return compare_xml(want, got) + except Exception: + return False + + def check_output_json(self, want, got, optionsflags): + """ + Tries to compare want and got as if they were JSON-encoded data + """ + want, got = strip_quotes(want, got) + try: + want_json = json.loads(want) + got_json = json.loads(got) + except Exception: + return False + return want_json == got_json + + +class DocTestRunner(doctest.DocTestRunner): + def __init__(self, *args, **kwargs): + doctest.DocTestRunner.__init__(self, *args, **kwargs) + self.optionflags = doctest.ELLIPSIS + + +doctestOutputChecker = OutputChecker() + + +def get_tests(app_module): + parts = app_module.__name__.split('.') + prefix, last = parts[:-1], parts[-1] + try: + test_module = import_module('.'.join(prefix + [TEST_MODULE])) + except ImportError: + # Couldn't import tests.py. Was it due to a missing file, or + # due to an import error in a tests.py that actually exists? + # app_module either points to a models.py file, or models/__init__.py + # Tests are therefore either in same directory, or one level up + if last == 'models': + app_root = import_module('.'.join(prefix)) + else: + app_root = app_module + + if not module_has_submodule(app_root, TEST_MODULE): + test_module = None + else: + # The module exists, so there must be an import error in the test + # module itself. + raise + return test_module + + +def make_doctest(module): + return doctest.DocTestSuite(module, + checker=doctestOutputChecker, + runner=DocTestRunner, + ) + + +def build_suite(app_module): + """ + Create a complete Django test suite for the provided application module. + """ + suite = unittest.TestSuite() + + # Load unit and doctests in the models.py module. If module has + # a suite() method, use it. Otherwise build the test suite ourselves. + if hasattr(app_module, 'suite'): + suite.addTest(app_module.suite()) + else: + suite.addTest(unittest.defaultTestLoader.loadTestsFromModule( + app_module)) + try: + suite.addTest(make_doctest(app_module)) + except ValueError: + # No doc tests in models.py + pass + + # Check to see if a separate 'tests' module exists parallel to the + # models module + test_module = get_tests(app_module) + if test_module: + # Load unit and doctests in the tests.py module. If module has + # a suite() method, use it. Otherwise build the test suite ourselves. + if hasattr(test_module, 'suite'): + suite.addTest(test_module.suite()) + else: + suite.addTest(unittest.defaultTestLoader.loadTestsFromModule( + test_module)) + try: + suite.addTest(make_doctest(test_module)) + except ValueError: + # No doc tests in tests.py + pass + return suite + + +def build_test(label): + """ + Construct a test case with the specified label. Label should be of the + form model.TestClass or model.TestClass.test_method. Returns an + instantiated test or test suite corresponding to the label provided. + + """ + parts = label.split('.') + if len(parts) < 2 or len(parts) > 3: + raise ValueError("Test label '%s' should be of the form app.TestCase " + "or app.TestCase.test_method" % label) + + # + # First, look for TestCase instances with a name that matches + # + app_module = get_app(parts[0]) + test_module = get_tests(app_module) + TestClass = getattr(app_module, parts[1], None) + + # Couldn't find the test class in models.py; look in tests.py + if TestClass is None: + if test_module: + TestClass = getattr(test_module, parts[1], None) + + try: + if issubclass(TestClass, (unittest.TestCase, real_unittest.TestCase)): + if len(parts) == 2: # label is app.TestClass + try: + return unittest.TestLoader().loadTestsFromTestCase( + TestClass) + except TypeError: + raise ValueError( + "Test label '%s' does not refer to a test class" + % label) + else: # label is app.TestClass.test_method + return TestClass(parts[2]) + except TypeError: + # TestClass isn't a TestClass - it must be a method or normal class + pass + + # + # If there isn't a TestCase, look for a doctest that matches + # + tests = [] + for module in app_module, test_module: + try: + doctests = make_doctest(module) + # Now iterate over the suite, looking for doctests whose name + # matches the pattern that was given + for test in doctests: + if test._dt_test.name in ( + '%s.%s' % (module.__name__, '.'.join(parts[1:])), + '%s.__test__.%s' % ( + module.__name__, '.'.join(parts[1:]))): + tests.append(test) + except ValueError: + # No doctests found. + pass + + # If no tests were found, then we were given a bad test label. + if not tests: + raise ValueError("Test label '%s' does not refer to a test" % label) + + # Construct a suite out of the tests that matched. + return unittest.TestSuite(tests) + + +class DjangoTestSuiteRunner(runner.DiscoverRunner): + + def build_suite(self, test_labels, extra_tests=None, **kwargs): + suite = unittest.TestSuite() + + if test_labels: + for label in test_labels: + if '.' in label: + suite.addTest(build_test(label)) + else: + app = get_app(label) + suite.addTest(build_suite(app)) + else: + for app in get_apps(): + suite.addTest(build_suite(app)) + + if extra_tests: + for test in extra_tests: + suite.addTest(test) + + return runner.reorder_suite(suite, (unittest.TestCase,)) diff --git a/lib/python2.7/site-packages/django/test/testcases.py b/lib/python2.7/site-packages/django/test/testcases.py new file mode 100644 index 0000000..85ff66a --- /dev/null +++ b/lib/python2.7/site-packages/django/test/testcases.py @@ -0,0 +1,1208 @@ +from __future__ import unicode_literals + +from copy import copy +import difflib +import errno +from functools import wraps +import json +import os +import re +import socket +import sys +import select +import socket +import threading +import warnings + +from django.conf import settings +from django.contrib.staticfiles.handlers import StaticFilesHandler +from django.core import mail +from django.core.exceptions import ValidationError, ImproperlyConfigured +from django.core.handlers.wsgi import WSGIHandler +from django.core.management import call_command +from django.core.management.color import no_style +from django.core.management.commands import flush +from django.core.servers.basehttp import WSGIRequestHandler, WSGIServer +from django.core.urlresolvers import clear_url_caches, set_urlconf +from django.db import connection, connections, DEFAULT_DB_ALIAS, transaction +from django.db.models.loading import cache +from django.forms.fields import CharField +from django.http import QueryDict +from django.test.client import Client +from django.test.html import HTMLParseError, parse_html +from django.test.signals import template_rendered +from django.test.utils import (CaptureQueriesContext, ContextList, + override_settings, compare_xml) +from django.utils import six, unittest as ut2 +from django.utils.encoding import force_text +from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit +from django.utils.unittest import skipIf # Imported here for backward compatibility +from django.utils.unittest.util import safe_repr +from django.views.static import serve + + +__all__ = ('TestCase', 'TransactionTestCase', + 'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature') + + +def to_list(value): + """ + Puts value into a list if it's not already one. + Returns an empty list if value is None. + """ + if value is None: + value = [] + elif not isinstance(value, list): + value = [value] + return value + +real_commit = transaction.commit +real_rollback = transaction.rollback +real_enter_transaction_management = transaction.enter_transaction_management +real_leave_transaction_management = transaction.leave_transaction_management +real_abort = transaction.abort + +def nop(*args, **kwargs): + return + +def disable_transaction_methods(): + transaction.commit = nop + transaction.rollback = nop + transaction.enter_transaction_management = nop + transaction.leave_transaction_management = nop + transaction.abort = nop + +def restore_transaction_methods(): + transaction.commit = real_commit + transaction.rollback = real_rollback + transaction.enter_transaction_management = real_enter_transaction_management + transaction.leave_transaction_management = real_leave_transaction_management + transaction.abort = real_abort + + +def assert_and_parse_html(self, html, user_msg, msg): + try: + dom = parse_html(html) + except HTMLParseError as e: + standardMsg = '%s\n%s' % (msg, e.msg) + self.fail(self._formatMessage(user_msg, standardMsg)) + return dom + + +class _AssertNumQueriesContext(CaptureQueriesContext): + def __init__(self, test_case, num, connection): + self.test_case = test_case + self.num = num + super(_AssertNumQueriesContext, self).__init__(connection) + + def __exit__(self, exc_type, exc_value, traceback): + super(_AssertNumQueriesContext, self).__exit__(exc_type, exc_value, traceback) + if exc_type is not None: + return + executed = len(self) + self.test_case.assertEqual( + executed, self.num, "%d queries executed, %d expected" % ( + executed, self.num + ) + ) + + +class _AssertTemplateUsedContext(object): + def __init__(self, test_case, template_name): + self.test_case = test_case + self.template_name = template_name + self.rendered_templates = [] + self.rendered_template_names = [] + self.context = ContextList() + + def on_template_render(self, sender, signal, template, context, **kwargs): + self.rendered_templates.append(template) + self.rendered_template_names.append(template.name) + self.context.append(copy(context)) + + def test(self): + return self.template_name in self.rendered_template_names + + def message(self): + return '%s was not rendered.' % self.template_name + + def __enter__(self): + template_rendered.connect(self.on_template_render) + return self + + def __exit__(self, exc_type, exc_value, traceback): + template_rendered.disconnect(self.on_template_render) + if exc_type is not None: + return + + if not self.test(): + message = self.message() + if len(self.rendered_templates) == 0: + message += ' No template was rendered.' + else: + message += ' Following templates were rendered: %s' % ( + ', '.join(self.rendered_template_names)) + self.test_case.fail(message) + + +class _AssertTemplateNotUsedContext(_AssertTemplateUsedContext): + def test(self): + return self.template_name not in self.rendered_template_names + + def message(self): + return '%s was rendered.' % self.template_name + + +class SimpleTestCase(ut2.TestCase): + + # The class we'll use for the test client self.client. + # Can be overridden in derived classes. + client_class = Client + + _warn_txt = ("save_warnings_state/restore_warnings_state " + "django.test.*TestCase methods are deprecated. Use Python's " + "warnings.catch_warnings context manager instead.") + + def __call__(self, result=None): + """ + Wrapper around default __call__ method to perform common Django test + set up. This means that user-defined Test Cases aren't required to + include a call to super().setUp(). + """ + testMethod = getattr(self, self._testMethodName) + skipped = (getattr(self.__class__, "__unittest_skip__", False) or + getattr(testMethod, "__unittest_skip__", False)) + + if not skipped: + try: + self._pre_setup() + except (KeyboardInterrupt, SystemExit): + raise + except Exception: + result.addError(self, sys.exc_info()) + return + super(SimpleTestCase, self).__call__(result) + if not skipped: + try: + self._post_teardown() + except (KeyboardInterrupt, SystemExit): + raise + except Exception: + result.addError(self, sys.exc_info()) + return + + def _pre_setup(self): + """Performs any pre-test setup. This includes: + + * Creating a test client. + * If the class has a 'urls' attribute, replace ROOT_URLCONF with it. + * Clearing the mail test outbox. + """ + self.client = self.client_class() + self._urlconf_setup() + mail.outbox = [] + + def _urlconf_setup(self): + set_urlconf(None) + if hasattr(self, 'urls'): + self._old_root_urlconf = settings.ROOT_URLCONF + settings.ROOT_URLCONF = self.urls + clear_url_caches() + + def _post_teardown(self): + """Performs any post-test things. This includes: + + * Putting back the original ROOT_URLCONF if it was changed. + """ + self._urlconf_teardown() + + def _urlconf_teardown(self): + set_urlconf(None) + if hasattr(self, '_old_root_urlconf'): + settings.ROOT_URLCONF = self._old_root_urlconf + clear_url_caches() + + def save_warnings_state(self): + """ + Saves the state of the warnings module + """ + warnings.warn(self._warn_txt, DeprecationWarning, stacklevel=2) + self._warnings_state = warnings.filters[:] + + def restore_warnings_state(self): + """ + Restores the state of the warnings module to the state + saved by save_warnings_state() + """ + warnings.warn(self._warn_txt, DeprecationWarning, stacklevel=2) + warnings.filters = self._warnings_state[:] + + def settings(self, **kwargs): + """ + A context manager that temporarily sets a setting and reverts + back to the original value when exiting the context. + """ + return override_settings(**kwargs) + + def assertRedirects(self, response, expected_url, status_code=302, + target_status_code=200, host=None, msg_prefix=''): + """Asserts that a response redirected to a specific URL, and that the + redirect URL can be loaded. + + Note that assertRedirects won't work for external links since it uses + TestClient to do a request. + """ + if msg_prefix: + msg_prefix += ": " + + if hasattr(response, 'redirect_chain'): + # The request was a followed redirect + self.assertTrue(len(response.redirect_chain) > 0, + msg_prefix + "Response didn't redirect as expected: Response" + " code was %d (expected %d)" % + (response.status_code, status_code)) + + self.assertEqual(response.redirect_chain[0][1], status_code, + msg_prefix + "Initial response didn't redirect as expected:" + " Response code was %d (expected %d)" % + (response.redirect_chain[0][1], status_code)) + + url, status_code = response.redirect_chain[-1] + + self.assertEqual(response.status_code, target_status_code, + msg_prefix + "Response didn't redirect as expected: Final" + " Response code was %d (expected %d)" % + (response.status_code, target_status_code)) + + else: + # Not a followed redirect + self.assertEqual(response.status_code, status_code, + msg_prefix + "Response didn't redirect as expected: Response" + " code was %d (expected %d)" % + (response.status_code, status_code)) + + url = response.url + scheme, netloc, path, query, fragment = urlsplit(url) + + redirect_response = response.client.get(path, QueryDict(query)) + + # Get the redirection page, using the same client that was used + # to obtain the original response. + self.assertEqual(redirect_response.status_code, target_status_code, + msg_prefix + "Couldn't retrieve redirection page '%s':" + " response code was %d (expected %d)" % + (path, redirect_response.status_code, target_status_code)) + + e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit( + expected_url) + if not (e_scheme or e_netloc): + expected_url = urlunsplit(('http', host or 'testserver', e_path, + e_query, e_fragment)) + + self.assertEqual(url, expected_url, + msg_prefix + "Response redirected to '%s', expected '%s'" % + (url, expected_url)) + + def assertContains(self, response, text, count=None, status_code=200, + msg_prefix='', html=False): + """ + Asserts that a response indicates that some content was retrieved + successfully, (i.e., the HTTP status code was as expected), and that + ``text`` occurs ``count`` times in the content of the response. + If ``count`` is None, the count doesn't matter - the assertion is true + if the text occurs at least once in the response. + """ + + # If the response supports deferred rendering and hasn't been rendered + # yet, then ensure that it does get rendered before proceeding further. + if (hasattr(response, 'render') and callable(response.render) + and not response.is_rendered): + response.render() + + if msg_prefix: + msg_prefix += ": " + + self.assertEqual(response.status_code, status_code, + msg_prefix + "Couldn't retrieve content: Response code was %d" + " (expected %d)" % (response.status_code, status_code)) + + if response.streaming: + content = b''.join(response.streaming_content) + else: + content = response.content + if not isinstance(text, bytes) or html: + text = force_text(text, encoding=response._charset) + content = content.decode(response._charset) + text_repr = "'%s'" % text + else: + text_repr = repr(text) + if html: + content = assert_and_parse_html(self, content, None, + "Response's content is not valid HTML:") + text = assert_and_parse_html(self, text, None, + "Second argument is not valid HTML:") + real_count = content.count(text) + if count is not None: + self.assertEqual(real_count, count, + msg_prefix + "Found %d instances of %s in response" + " (expected %d)" % (real_count, text_repr, count)) + else: + self.assertTrue(real_count != 0, + msg_prefix + "Couldn't find %s in response" % text_repr) + + def assertNotContains(self, response, text, status_code=200, + msg_prefix='', html=False): + """ + Asserts that a response indicates that some content was retrieved + successfully, (i.e., the HTTP status code was as expected), and that + ``text`` doesn't occurs in the content of the response. + """ + + # If the response supports deferred rendering and hasn't been rendered + # yet, then ensure that it does get rendered before proceeding further. + if (hasattr(response, 'render') and callable(response.render) + and not response.is_rendered): + response.render() + + if msg_prefix: + msg_prefix += ": " + + self.assertEqual(response.status_code, status_code, + msg_prefix + "Couldn't retrieve content: Response code was %d" + " (expected %d)" % (response.status_code, status_code)) + + content = response.content + if not isinstance(text, bytes) or html: + text = force_text(text, encoding=response._charset) + content = content.decode(response._charset) + text_repr = "'%s'" % text + else: + text_repr = repr(text) + if html: + content = assert_and_parse_html(self, content, None, + 'Response\'s content is not valid HTML:') + text = assert_and_parse_html(self, text, None, + 'Second argument is not valid HTML:') + self.assertEqual(content.count(text), 0, + msg_prefix + "Response should not contain %s" % text_repr) + + def assertFormError(self, response, form, field, errors, msg_prefix=''): + """ + Asserts that a form used to render the response has a specific field + error. + """ + if msg_prefix: + msg_prefix += ": " + + # Put context(s) into a list to simplify processing. + contexts = to_list(response.context) + if not contexts: + self.fail(msg_prefix + "Response did not use any contexts to " + "render the response") + + # Put error(s) into a list to simplify processing. + errors = to_list(errors) + + # Search all contexts for the error. + found_form = False + for i,context in enumerate(contexts): + if form not in context: + continue + found_form = True + for err in errors: + if field: + if field in context[form].errors: + field_errors = context[form].errors[field] + self.assertTrue(err in field_errors, + msg_prefix + "The field '%s' on form '%s' in" + " context %d does not contain the error '%s'" + " (actual errors: %s)" % + (field, form, i, err, repr(field_errors))) + elif field in context[form].fields: + self.fail(msg_prefix + "The field '%s' on form '%s'" + " in context %d contains no errors" % + (field, form, i)) + else: + self.fail(msg_prefix + "The form '%s' in context %d" + " does not contain the field '%s'" % + (form, i, field)) + else: + non_field_errors = context[form].non_field_errors() + self.assertTrue(err in non_field_errors, + msg_prefix + "The form '%s' in context %d does not" + " contain the non-field error '%s'" + " (actual errors: %s)" % + (form, i, err, non_field_errors)) + if not found_form: + self.fail(msg_prefix + "The form '%s' was not used to render the" + " response" % form) + + def assertFormsetError(self, response, formset, form_index, field, errors, + msg_prefix=''): + """ + Asserts that a formset used to render the response has a specific error. + + For field errors, specify the ``form_index`` and the ``field``. + For non-field errors, specify the ``form_index`` and the ``field`` as + None. + For non-form errors, specify ``form_index`` as None and the ``field`` + as None. + """ + # Add punctuation to msg_prefix + if msg_prefix: + msg_prefix += ": " + + # Put context(s) into a list to simplify processing. + contexts = to_list(response.context) + if not contexts: + self.fail(msg_prefix + 'Response did not use any contexts to ' + 'render the response') + + # Put error(s) into a list to simplify processing. + errors = to_list(errors) + + # Search all contexts for the error. + found_formset = False + for i, context in enumerate(contexts): + if formset not in context: + continue + found_formset = True + for err in errors: + if field is not None: + if field in context[formset].forms[form_index].errors: + field_errors = context[formset].forms[form_index].errors[field] + self.assertTrue(err in field_errors, + msg_prefix + "The field '%s' on formset '%s', " + "form %d in context %d does not contain the " + "error '%s' (actual errors: %s)" % + (field, formset, form_index, i, err, + repr(field_errors))) + elif field in context[formset].forms[form_index].fields: + self.fail(msg_prefix + "The field '%s' " + "on formset '%s', form %d in " + "context %d contains no errors" % + (field, formset, form_index, i)) + else: + self.fail(msg_prefix + "The formset '%s', form %d in " + "context %d does not contain the field '%s'" % + (formset, form_index, i, field)) + elif form_index is not None: + non_field_errors = context[formset].forms[form_index].non_field_errors() + self.assertFalse(len(non_field_errors) == 0, + msg_prefix + "The formset '%s', form %d in " + "context %d does not contain any non-field " + "errors." % (formset, form_index, i)) + self.assertTrue(err in non_field_errors, + msg_prefix + "The formset '%s', form %d " + "in context %d does not contain the " + "non-field error '%s' " + "(actual errors: %s)" % + (formset, form_index, i, err, + repr(non_field_errors))) + else: + non_form_errors = context[formset].non_form_errors() + self.assertFalse(len(non_form_errors) == 0, + msg_prefix + "The formset '%s' in " + "context %d does not contain any " + "non-form errors." % (formset, i)) + self.assertTrue(err in non_form_errors, + msg_prefix + "The formset '%s' in context " + "%d does not contain the " + "non-form error '%s' (actual errors: %s)" % + (formset, i, err, repr(non_form_errors))) + if not found_formset: + self.fail(msg_prefix + "The formset '%s' was not used to render " + "the response" % formset) + + def assertTemplateUsed(self, response=None, template_name=None, msg_prefix=''): + """ + Asserts that the template with the provided name was used in rendering + the response. Also usable as context manager. + """ + if response is None and template_name is None: + raise TypeError('response and/or template_name argument must be provided') + + if msg_prefix: + msg_prefix += ": " + + # Use assertTemplateUsed as context manager. + if not hasattr(response, 'templates') or (response is None and template_name): + if response: + template_name = response + response = None + context = _AssertTemplateUsedContext(self, template_name) + return context + + template_names = [t.name for t in response.templates] + if not template_names: + self.fail(msg_prefix + "No templates used to render the response") + self.assertTrue(template_name in template_names, + msg_prefix + "Template '%s' was not a template used to render" + " the response. Actual template(s) used: %s" % + (template_name, ', '.join(template_names))) + + def assertTemplateNotUsed(self, response=None, template_name=None, msg_prefix=''): + """ + Asserts that the template with the provided name was NOT used in + rendering the response. Also usable as context manager. + """ + if response is None and template_name is None: + raise TypeError('response and/or template_name argument must be provided') + + if msg_prefix: + msg_prefix += ": " + + # Use assertTemplateUsed as context manager. + if not hasattr(response, 'templates') or (response is None and template_name): + if response: + template_name = response + response = None + context = _AssertTemplateNotUsedContext(self, template_name) + return context + + template_names = [t.name for t in response.templates] + self.assertFalse(template_name in template_names, + msg_prefix + "Template '%s' was used unexpectedly in rendering" + " the response" % template_name) + + def assertRaisesMessage(self, expected_exception, expected_message, + callable_obj=None, *args, **kwargs): + """ + Asserts that the message in a raised exception matches the passed + value. + + Args: + expected_exception: Exception class expected to be raised. + expected_message: expected error message string value. + callable_obj: Function to be called. + args: Extra args. + kwargs: Extra kwargs. + """ + return six.assertRaisesRegex(self, expected_exception, + re.escape(expected_message), callable_obj, *args, **kwargs) + + def assertFieldOutput(self, fieldclass, valid, invalid, field_args=None, + field_kwargs=None, empty_value=''): + """ + Asserts that a form field behaves correctly with various inputs. + + Args: + fieldclass: the class of the field to be tested. + valid: a dictionary mapping valid inputs to their expected + cleaned values. + invalid: a dictionary mapping invalid inputs to one or more + raised error messages. + field_args: the args passed to instantiate the field + field_kwargs: the kwargs passed to instantiate the field + empty_value: the expected clean output for inputs in empty_values + + """ + if field_args is None: + field_args = [] + if field_kwargs is None: + field_kwargs = {} + required = fieldclass(*field_args, **field_kwargs) + optional = fieldclass(*field_args, + **dict(field_kwargs, required=False)) + # test valid inputs + for input, output in valid.items(): + self.assertEqual(required.clean(input), output) + self.assertEqual(optional.clean(input), output) + # test invalid inputs + for input, errors in invalid.items(): + with self.assertRaises(ValidationError) as context_manager: + required.clean(input) + self.assertEqual(context_manager.exception.messages, errors) + + with self.assertRaises(ValidationError) as context_manager: + optional.clean(input) + self.assertEqual(context_manager.exception.messages, errors) + # test required inputs + error_required = [force_text(required.error_messages['required'])] + for e in required.empty_values: + with self.assertRaises(ValidationError) as context_manager: + required.clean(e) + self.assertEqual(context_manager.exception.messages, + error_required) + self.assertEqual(optional.clean(e), empty_value) + # test that max_length and min_length are always accepted + if issubclass(fieldclass, CharField): + field_kwargs.update({'min_length':2, 'max_length':20}) + self.assertTrue(isinstance(fieldclass(*field_args, **field_kwargs), + fieldclass)) + + def assertHTMLEqual(self, html1, html2, msg=None): + """ + Asserts that two HTML snippets are semantically the same. + Whitespace in most cases is ignored, and attribute ordering is not + significant. The passed-in arguments must be valid HTML. + """ + dom1 = assert_and_parse_html(self, html1, msg, + 'First argument is not valid HTML:') + dom2 = assert_and_parse_html(self, html2, msg, + 'Second argument is not valid HTML:') + + if dom1 != dom2: + standardMsg = '%s != %s' % ( + safe_repr(dom1, True), safe_repr(dom2, True)) + diff = ('\n' + '\n'.join(difflib.ndiff( + six.text_type(dom1).splitlines(), + six.text_type(dom2).splitlines()))) + standardMsg = self._truncateMessage(standardMsg, diff) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertHTMLNotEqual(self, html1, html2, msg=None): + """Asserts that two HTML snippets are not semantically equivalent.""" + dom1 = assert_and_parse_html(self, html1, msg, + 'First argument is not valid HTML:') + dom2 = assert_and_parse_html(self, html2, msg, + 'Second argument is not valid HTML:') + + if dom1 == dom2: + standardMsg = '%s == %s' % ( + safe_repr(dom1, True), safe_repr(dom2, True)) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertInHTML(self, needle, haystack, count=None, msg_prefix=''): + needle = assert_and_parse_html(self, needle, None, + 'First argument is not valid HTML:') + haystack = assert_and_parse_html(self, haystack, None, + 'Second argument is not valid HTML:') + real_count = haystack.count(needle) + if count is not None: + self.assertEqual(real_count, count, + msg_prefix + "Found %d instances of '%s' in response" + " (expected %d)" % (real_count, needle, count)) + else: + self.assertTrue(real_count != 0, + msg_prefix + "Couldn't find '%s' in response" % needle) + + def assertJSONEqual(self, raw, expected_data, msg=None): + try: + data = json.loads(raw) + except ValueError: + self.fail("First argument is not valid JSON: %r" % raw) + if isinstance(expected_data, six.string_types): + try: + expected_data = json.loads(expected_data) + except ValueError: + self.fail("Second argument is not valid JSON: %r" % expected_data) + self.assertEqual(data, expected_data, msg=msg) + + def assertXMLEqual(self, xml1, xml2, msg=None): + """ + Asserts that two XML snippets are semantically the same. + Whitespace in most cases is ignored, and attribute ordering is not + significant. The passed-in arguments must be valid XML. + """ + try: + result = compare_xml(xml1, xml2) + except Exception as e: + standardMsg = 'First or second argument is not valid XML\n%s' % e + self.fail(self._formatMessage(msg, standardMsg)) + else: + if not result: + standardMsg = '%s != %s' % (safe_repr(xml1, True), safe_repr(xml2, True)) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertXMLNotEqual(self, xml1, xml2, msg=None): + """ + Asserts that two XML snippets are not semantically equivalent. + Whitespace in most cases is ignored, and attribute ordering is not + significant. The passed-in arguments must be valid XML. + """ + try: + result = compare_xml(xml1, xml2) + except Exception as e: + standardMsg = 'First or second argument is not valid XML\n%s' % e + self.fail(self._formatMessage(msg, standardMsg)) + else: + if result: + standardMsg = '%s == %s' % (safe_repr(xml1, True), safe_repr(xml2, True)) + self.fail(self._formatMessage(msg, standardMsg)) + + +class TransactionTestCase(SimpleTestCase): + + # Subclasses can ask for resetting of auto increment sequence before each + # test case + reset_sequences = False + + # Subclasses can enable only a subset of apps for faster tests + available_apps = None + + def _pre_setup(self): + """Performs any pre-test setup. This includes: + + * If the class has an 'available_apps' attribute, restricting the app + cache to these applications, then firing post_syncdb -- it must run + with the correct set of applications for the test case. + * If the class has a 'fixtures' attribute, installing these fixtures. + """ + super(TransactionTestCase, self)._pre_setup() + if self.available_apps is not None: + cache.set_available_apps(self.available_apps) + for db_name in self._databases_names(include_mirrors=False): + flush.Command.emit_post_syncdb( + verbosity=0, interactive=False, database=db_name) + try: + self._fixture_setup() + except Exception: + if self.available_apps is not None: + cache.unset_available_apps() + raise + + def _databases_names(self, include_mirrors=True): + # If the test case has a multi_db=True flag, act on all databases, + # including mirrors or not. Otherwise, just on the default DB. + if getattr(self, 'multi_db', False): + return [alias for alias in connections + if include_mirrors or not connections[alias].settings_dict['TEST_MIRROR']] + else: + return [DEFAULT_DB_ALIAS] + + def _reset_sequences(self, db_name): + conn = connections[db_name] + if conn.features.supports_sequence_reset: + sql_list = \ + conn.ops.sequence_reset_by_name_sql(no_style(), + conn.introspection.sequence_list()) + if sql_list: + with transaction.commit_on_success_unless_managed(using=db_name): + cursor = conn.cursor() + for sql in sql_list: + cursor.execute(sql) + + def _fixture_setup(self): + for db_name in self._databases_names(include_mirrors=False): + # Reset sequences + if self.reset_sequences: + self._reset_sequences(db_name) + + if hasattr(self, 'fixtures'): + # We have to use this slightly awkward syntax due to the fact + # that we're using *args and **kwargs together. + call_command('loaddata', *self.fixtures, + **{'verbosity': 0, 'database': db_name, 'skip_validation': True}) + + def _post_teardown(self): + """Performs any post-test things. This includes: + + * Flushing the contents of the database, to leave a clean slate. If + the class has an 'available_apps' attribute, post_syncdb isn't fired. + * Force-closing the connection, so the next test gets a clean cursor. + """ + try: + self._fixture_teardown() + super(TransactionTestCase, self)._post_teardown() + # Some DB cursors include SQL statements as part of cursor + # creation. If you have a test that does rollback, the effect of + # these statements is lost, which can effect the operation of + # tests (e.g., losing a timezone setting causing objects to be + # created with the wrong time). To make sure this doesn't happen, + # get a clean connection at the start of every test. + for conn in connections.all(): + conn.close() + finally: + cache.unset_available_apps() + + def _fixture_teardown(self): + # Allow TRUNCATE ... CASCADE and don't emit the post_syncdb signal + # when flushing only a subset of the apps + for db_name in self._databases_names(include_mirrors=False): + call_command('flush', verbosity=0, interactive=False, + database=db_name, skip_validation=True, + reset_sequences=False, + allow_cascade=self.available_apps is not None, + inhibit_post_syncdb=self.available_apps is not None) + + def assertQuerysetEqual(self, qs, values, transform=repr, ordered=True): + items = six.moves.map(transform, qs) + if not ordered: + return self.assertEqual(set(items), set(values)) + values = list(values) + # For example qs.iterator() could be passed as qs, but it does not + # have 'ordered' attribute. + if len(values) > 1 and hasattr(qs, 'ordered') and not qs.ordered: + raise ValueError("Trying to compare non-ordered queryset " + "against more than one ordered values") + return self.assertEqual(list(items), values) + + def assertNumQueries(self, num, func=None, *args, **kwargs): + using = kwargs.pop("using", DEFAULT_DB_ALIAS) + conn = connections[using] + + context = _AssertNumQueriesContext(self, num, conn) + if func is None: + return context + + with context: + func(*args, **kwargs) + + +def connections_support_transactions(): + """ + Returns True if all connections support transactions. + """ + return all(conn.features.supports_transactions + for conn in connections.all()) + + +class TestCase(TransactionTestCase): + """ + Does basically the same as TransactionTestCase, but surrounds every test + with a transaction, monkey-patches the real transaction management routines + to do nothing, and rollsback the test transaction at the end of the test. + You have to use TransactionTestCase, if you need transaction management + inside a test. + """ + + def _fixture_setup(self): + if not connections_support_transactions(): + return super(TestCase, self)._fixture_setup() + + assert not self.reset_sequences, 'reset_sequences cannot be used on TestCase instances' + + self.atomics = {} + for db_name in self._databases_names(): + self.atomics[db_name] = transaction.atomic(using=db_name) + self.atomics[db_name].__enter__() + # Remove this when the legacy transaction management goes away. + disable_transaction_methods() + + for db_name in self._databases_names(include_mirrors=False): + if hasattr(self, 'fixtures'): + try: + call_command('loaddata', *self.fixtures, + **{ + 'verbosity': 0, + 'commit': False, + 'database': db_name, + 'skip_validation': True, + }) + except Exception: + self._fixture_teardown() + raise + + def _fixture_teardown(self): + if not connections_support_transactions(): + return super(TestCase, self)._fixture_teardown() + + # Remove this when the legacy transaction management goes away. + restore_transaction_methods() + for db_name in reversed(self._databases_names()): + # Hack to force a rollback + connections[db_name].needs_rollback = True + self.atomics[db_name].__exit__(None, None, None) + + +def _deferredSkip(condition, reason): + def decorator(test_func): + if not (isinstance(test_func, type) and + issubclass(test_func, TestCase)): + @wraps(test_func) + def skip_wrapper(*args, **kwargs): + if condition(): + raise ut2.SkipTest(reason) + return test_func(*args, **kwargs) + test_item = skip_wrapper + else: + test_item = test_func + test_item.__unittest_skip_why__ = reason + return test_item + return decorator + + +def skipIfDBFeature(feature): + """ + Skip a test if a database has the named feature + """ + return _deferredSkip(lambda: getattr(connection.features, feature), + "Database has feature %s" % feature) + + +def skipUnlessDBFeature(feature): + """ + Skip a test unless a database has the named feature + """ + return _deferredSkip(lambda: not getattr(connection.features, feature), + "Database doesn't support feature %s" % feature) + + +class QuietWSGIRequestHandler(WSGIRequestHandler): + """ + Just a regular WSGIRequestHandler except it doesn't log to the standard + output any of the requests received, so as to not clutter the output for + the tests' results. + """ + + def log_message(*args): + pass + + +if sys.version_info >= (3, 3, 0): + _ImprovedEvent = threading.Event +elif sys.version_info >= (2, 7, 0): + _ImprovedEvent = threading._Event +else: + class _ImprovedEvent(threading._Event): + """ + Does the same as `threading.Event` except it overrides the wait() method + with some code borrowed from Python 2.7 to return the set state of the + event (see: http://hg.python.org/cpython/rev/b5aa8aa78c0f/). This allows + to know whether the wait() method exited normally or because of the + timeout. This class can be removed when Django supports only Python >= 2.7. + """ + + def wait(self, timeout=None): + self._Event__cond.acquire() + try: + if not self._Event__flag: + self._Event__cond.wait(timeout) + return self._Event__flag + finally: + self._Event__cond.release() + + +class StoppableWSGIServer(WSGIServer): + """ + The code in this class is borrowed from the `SocketServer.BaseServer` class + in Python 2.6. The important functionality here is that the server is non- + blocking and that it can be shut down at any moment. This is made possible + by the server regularly polling the socket and checking if it has been + asked to stop. + Note for the future: Once Django stops supporting Python 2.6, this class + can be removed as `WSGIServer` will have this ability to shutdown on + demand and will not require the use of the _ImprovedEvent class whose code + is borrowed from Python 2.7. + """ + + def __init__(self, *args, **kwargs): + super(StoppableWSGIServer, self).__init__(*args, **kwargs) + self.__is_shut_down = _ImprovedEvent() + self.__serving = False + + def serve_forever(self, poll_interval=0.5): + """ + Handle one request at a time until shutdown. + + Polls for shutdown every poll_interval seconds. + """ + self.__serving = True + self.__is_shut_down.clear() + while self.__serving: + r, w, e = select.select([self], [], [], poll_interval) + if r: + self._handle_request_noblock() + self.__is_shut_down.set() + + def shutdown(self): + """ + Stops the serve_forever loop. + + Blocks until the loop has finished. This must be called while + serve_forever() is running in another thread, or it will + deadlock. + """ + self.__serving = False + if not self.__is_shut_down.wait(2): + raise RuntimeError( + "Failed to shutdown the live test server in 2 seconds. The " + "server might be stuck or generating a slow response.") + + def handle_request(self): + """Handle one request, possibly blocking. + """ + fd_sets = select.select([self], [], [], None) + if not fd_sets[0]: + return + self._handle_request_noblock() + + def _handle_request_noblock(self): + """ + Handle one request, without blocking. + + I assume that select.select has returned that the socket is + readable before this function was called, so there should be + no risk of blocking in get_request(). + """ + try: + request, client_address = self.get_request() + except socket.error: + return + if self.verify_request(request, client_address): + try: + self.process_request(request, client_address) + except Exception: + self.handle_error(request, client_address) + self.close_request(request) + + +class _MediaFilesHandler(StaticFilesHandler): + """ + Handler for serving the media files. This is a private class that is + meant to be used solely as a convenience by LiveServerThread. + """ + + def get_base_dir(self): + return settings.MEDIA_ROOT + + def get_base_url(self): + return settings.MEDIA_URL + + def serve(self, request): + relative_url = request.path[len(self.base_url[2]):] + return serve(request, relative_url, document_root=self.get_base_dir()) + + +class LiveServerThread(threading.Thread): + """ + Thread for running a live http server while the tests are running. + """ + + def __init__(self, host, possible_ports, connections_override=None): + self.host = host + self.port = None + self.possible_ports = possible_ports + self.is_ready = threading.Event() + self.error = None + self.connections_override = connections_override + super(LiveServerThread, self).__init__() + + def run(self): + """ + Sets up the live server and databases, and then loops over handling + http requests. + """ + if self.connections_override: + # Override this thread's database connections with the ones + # provided by the main thread. + for alias, conn in self.connections_override.items(): + connections[alias] = conn + try: + # Create the handler for serving static and media files + handler = StaticFilesHandler(_MediaFilesHandler(WSGIHandler())) + + # Go through the list of possible ports, hoping that we can find + # one that is free to use for the WSGI server. + for index, port in enumerate(self.possible_ports): + try: + self.httpd = StoppableWSGIServer( + (self.host, port), QuietWSGIRequestHandler) + except socket.error as e: + if (index + 1 < len(self.possible_ports) and + e.errno == errno.EADDRINUSE): + # This port is already in use, so we go on and try with + # the next one in the list. + continue + else: + # Either none of the given ports are free or the error + # is something else than "Address already in use". So + # we let that error bubble up to the main thread. + raise + else: + # A free port was found. + self.port = port + break + + self.httpd.set_app(handler) + self.is_ready.set() + self.httpd.serve_forever() + except Exception as e: + self.error = e + self.is_ready.set() + + def join(self, timeout=None): + if hasattr(self, 'httpd'): + # Stop the WSGI server + self.httpd.shutdown() + self.httpd.server_close() + super(LiveServerThread, self).join(timeout) + + +class LiveServerTestCase(TransactionTestCase): + """ + Does basically the same as TransactionTestCase but also launches a live + http server in a separate thread so that the tests may use another testing + framework, such as Selenium for example, instead of the built-in dummy + client. + Note that it inherits from TransactionTestCase instead of TestCase because + the threads do not share the same transactions (unless if using in-memory + sqlite) and each thread needs to commit all their transactions so that the + other thread can see the changes. + """ + + @property + def live_server_url(self): + return 'http://%s:%s' % ( + self.server_thread.host, self.server_thread.port) + + @classmethod + def setUpClass(cls): + connections_override = {} + for conn in connections.all(): + # If using in-memory sqlite databases, pass the connections to + # the server thread. + if (conn.settings_dict['ENGINE'].rsplit('.', 1)[-1] in ('sqlite3', 'spatialite') + and conn.settings_dict['NAME'] == ':memory:'): + # Explicitly enable thread-shareability for this connection + conn.allow_thread_sharing = True + connections_override[conn.alias] = conn + + # Launch the live server's thread + specified_address = os.environ.get( + 'DJANGO_LIVE_TEST_SERVER_ADDRESS', 'localhost:8081') + + # The specified ports may be of the form '8000-8010,8080,9200-9300' + # i.e. a comma-separated list of ports or ranges of ports, so we break + # it down into a detailed list of all possible ports. + possible_ports = [] + try: + host, port_ranges = specified_address.split(':') + for port_range in port_ranges.split(','): + # A port range can be of either form: '8000' or '8000-8010'. + extremes = list(map(int, port_range.split('-'))) + assert len(extremes) in [1, 2] + if len(extremes) == 1: + # Port range of the form '8000' + possible_ports.append(extremes[0]) + else: + # Port range of the form '8000-8010' + for port in range(extremes[0], extremes[1] + 1): + possible_ports.append(port) + except Exception: + msg = 'Invalid address ("%s") for live server.' % specified_address + six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg), sys.exc_info()[2]) + cls.server_thread = LiveServerThread( + host, possible_ports, connections_override) + cls.server_thread.daemon = True + cls.server_thread.start() + + # Wait for the live server to be ready + cls.server_thread.is_ready.wait() + if cls.server_thread.error: + # Clean up behind ourselves, since tearDownClass won't get called in + # case of errors. + cls._tearDownClassInternal() + raise cls.server_thread.error + + super(LiveServerTestCase, cls).setUpClass() + + @classmethod + def _tearDownClassInternal(cls): + # There may not be a 'server_thread' attribute if setUpClass() for some + # reasons has raised an exception. + if hasattr(cls, 'server_thread'): + # Terminate the live server's thread + cls.server_thread.join() + + # Restore sqlite connections' non-sharability + for conn in connections.all(): + if (conn.settings_dict['ENGINE'].rsplit('.', 1)[-1] in ('sqlite3', 'spatialite') + and conn.settings_dict['NAME'] == ':memory:'): + conn.allow_thread_sharing = False + + @classmethod + def tearDownClass(cls): + cls._tearDownClassInternal() + super(LiveServerTestCase, cls).tearDownClass() diff --git a/lib/python2.7/site-packages/django/test/utils.py b/lib/python2.7/site-packages/django/test/utils.py new file mode 100644 index 0000000..818ccaf --- /dev/null +++ b/lib/python2.7/site-packages/django/test/utils.py @@ -0,0 +1,469 @@ +from contextlib import contextmanager +import logging +import re +import sys +from threading import local +import time +import warnings +from functools import wraps +from xml.dom.minidom import parseString, Node + +from django.conf import settings, UserSettingsHolder +from django.core import mail +from django.core.signals import request_started +from django.db import reset_queries +from django.http import request +from django.template import Template, loader, TemplateDoesNotExist +from django.template.loaders import cached +from django.test.signals import template_rendered, setting_changed +from django.utils.encoding import force_str +from django.utils import six +from django.utils.translation import deactivate +from django.utils.unittest import skipUnless + + +__all__ = ( + 'Approximate', 'ContextList', 'get_runner', 'override_settings', + 'requires_tz_support', 'setup_test_environment', 'teardown_test_environment', +) + +RESTORE_LOADERS_ATTR = '_original_template_source_loaders' +TZ_SUPPORT = hasattr(time, 'tzset') + + +class Approximate(object): + def __init__(self, val, places=7): + self.val = val + self.places = places + + def __repr__(self): + return repr(self.val) + + def __eq__(self, other): + if self.val == other: + return True + return round(abs(self.val - other), self.places) == 0 + + +class ContextList(list): + """A wrapper that provides direct key access to context items contained + in a list of context objects. + """ + def __getitem__(self, key): + if isinstance(key, six.string_types): + for subcontext in self: + if key in subcontext: + return subcontext[key] + raise KeyError(key) + else: + return super(ContextList, self).__getitem__(key) + + def __contains__(self, key): + try: + self[key] + except KeyError: + return False + return True + + def keys(self): + """ + Flattened keys of subcontexts. + """ + keys = set() + for subcontext in self: + for dict in subcontext: + keys |= set(dict.keys()) + return keys + + +def instrumented_test_render(self, context): + """ + An instrumented Template render method, providing a signal + that can be intercepted by the test system Client + """ + template_rendered.send(sender=self, template=self, context=context) + return self.nodelist.render(context) + + +def setup_test_environment(): + """Perform any global pre-test setup. This involves: + + - Installing the instrumented test renderer + - Set the email backend to the locmem email backend. + - Setting the active locale to match the LANGUAGE_CODE setting. + """ + Template._original_render = Template._render + Template._render = instrumented_test_render + + # Storing previous values in the settings module itself is problematic. + # Store them in arbitrary (but related) modules instead. See #20636. + + mail._original_email_backend = settings.EMAIL_BACKEND + settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' + + request._original_allowed_hosts = settings.ALLOWED_HOSTS + settings.ALLOWED_HOSTS = ['*'] + + mail.outbox = [] + + deactivate() + + +def teardown_test_environment(): + """Perform any global post-test teardown. This involves: + + - Restoring the original test renderer + - Restoring the email sending functions + + """ + Template._render = Template._original_render + del Template._original_render + + settings.EMAIL_BACKEND = mail._original_email_backend + del mail._original_email_backend + + settings.ALLOWED_HOSTS = request._original_allowed_hosts + del request._original_allowed_hosts + + del mail.outbox + + +warn_txt = ("get_warnings_state/restore_warnings_state functions from " + "django.test.utils are deprecated. Use Python's warnings.catch_warnings() " + "context manager instead.") + + +def get_warnings_state(): + """ + Returns an object containing the state of the warnings module + """ + # There is no public interface for doing this, but this implementation of + # get_warnings_state and restore_warnings_state appears to work on Python + # 2.4 to 2.7. + warnings.warn(warn_txt, DeprecationWarning, stacklevel=2) + return warnings.filters[:] + + +def restore_warnings_state(state): + """ + Restores the state of the warnings module when passed an object that was + returned by get_warnings_state() + """ + warnings.warn(warn_txt, DeprecationWarning, stacklevel=2) + warnings.filters = state[:] + + +def get_runner(settings, test_runner_class=None): + if not test_runner_class: + test_runner_class = settings.TEST_RUNNER + + test_path = test_runner_class.split('.') + # Allow for Python 2.5 relative paths + if len(test_path) > 1: + test_module_name = '.'.join(test_path[:-1]) + else: + test_module_name = '.' + test_module = __import__(test_module_name, {}, {}, force_str(test_path[-1])) + test_runner = getattr(test_module, test_path[-1]) + return test_runner + + +def setup_test_template_loader(templates_dict, use_cached_loader=False): + """ + Changes Django to only find templates from within a dictionary (where each + key is the template name and each value is the corresponding template + content to return). + + Use meth:`restore_template_loaders` to restore the original loaders. + """ + if hasattr(loader, RESTORE_LOADERS_ATTR): + raise Exception("loader.%s already exists" % RESTORE_LOADERS_ATTR) + + def test_template_loader(template_name, template_dirs=None): + "A custom template loader that loads templates from a dictionary." + try: + return (templates_dict[template_name], "test:%s" % template_name) + except KeyError: + raise TemplateDoesNotExist(template_name) + + if use_cached_loader: + template_loader = cached.Loader(('test_template_loader',)) + template_loader._cached_loaders = (test_template_loader,) + else: + template_loader = test_template_loader + + setattr(loader, RESTORE_LOADERS_ATTR, loader.template_source_loaders) + loader.template_source_loaders = (template_loader,) + return template_loader + + +def restore_template_loaders(): + """ + Restores the original template loaders after + :meth:`setup_test_template_loader` has been run. + """ + loader.template_source_loaders = getattr(loader, RESTORE_LOADERS_ATTR) + delattr(loader, RESTORE_LOADERS_ATTR) + + +class override_settings(object): + """ + Acts as either a decorator, or a context manager. If it's a decorator it + takes a function and returns a wrapped function. If it's a contextmanager + it's used with the ``with`` statement. In either event entering/exiting + are called before and after, respectively, the function/block is executed. + """ + def __init__(self, **kwargs): + self.options = kwargs + + def __enter__(self): + self.enable() + + def __exit__(self, exc_type, exc_value, traceback): + self.disable() + + def __call__(self, test_func): + from django.test import SimpleTestCase + if isinstance(test_func, type): + if not issubclass(test_func, SimpleTestCase): + raise Exception( + "Only subclasses of Django SimpleTestCase can be decorated " + "with override_settings") + original_pre_setup = test_func._pre_setup + original_post_teardown = test_func._post_teardown + + def _pre_setup(innerself): + self.enable() + original_pre_setup(innerself) + + def _post_teardown(innerself): + original_post_teardown(innerself) + self.disable() + test_func._pre_setup = _pre_setup + test_func._post_teardown = _post_teardown + return test_func + else: + @wraps(test_func) + def inner(*args, **kwargs): + with self: + return test_func(*args, **kwargs) + return inner + + def enable(self): + override = UserSettingsHolder(settings._wrapped) + for key, new_value in self.options.items(): + setattr(override, key, new_value) + self.wrapped = settings._wrapped + settings._wrapped = override + for key, new_value in self.options.items(): + setting_changed.send(sender=settings._wrapped.__class__, + setting=key, value=new_value) + + def disable(self): + settings._wrapped = self.wrapped + del self.wrapped + for key in self.options: + new_value = getattr(settings, key, None) + setting_changed.send(sender=settings._wrapped.__class__, + setting=key, value=new_value) + + +def compare_xml(want, got): + """Tries to do a 'xml-comparison' of want and got. Plain string + comparison doesn't always work because, for example, attribute + ordering should not be important. Comment nodes are not considered in the + comparison. + + Based on http://codespeak.net/svn/lxml/trunk/src/lxml/doctestcompare.py + """ + _norm_whitespace_re = re.compile(r'[ \t\n][ \t\n]+') + def norm_whitespace(v): + return _norm_whitespace_re.sub(' ', v) + + def child_text(element): + return ''.join([c.data for c in element.childNodes + if c.nodeType == Node.TEXT_NODE]) + + def children(element): + return [c for c in element.childNodes + if c.nodeType == Node.ELEMENT_NODE] + + def norm_child_text(element): + return norm_whitespace(child_text(element)) + + def attrs_dict(element): + return dict(element.attributes.items()) + + def check_element(want_element, got_element): + if want_element.tagName != got_element.tagName: + return False + if norm_child_text(want_element) != norm_child_text(got_element): + return False + if attrs_dict(want_element) != attrs_dict(got_element): + return False + want_children = children(want_element) + got_children = children(got_element) + if len(want_children) != len(got_children): + return False + for want, got in zip(want_children, got_children): + if not check_element(want, got): + return False + return True + + def first_node(document): + for node in document.childNodes: + if node.nodeType != Node.COMMENT_NODE: + return node + + want, got = strip_quotes(want, got) + want = want.replace('\\n','\n') + got = got.replace('\\n','\n') + + # If the string is not a complete xml document, we may need to add a + # root element. This allow us to compare fragments, like "<foo/><bar/>" + if not want.startswith('<?xml'): + wrapper = '<root>%s</root>' + want = wrapper % want + got = wrapper % got + + # Parse the want and got strings, and compare the parsings. + want_root = first_node(parseString(want)) + got_root = first_node(parseString(got)) + + return check_element(want_root, got_root) + + +def strip_quotes(want, got): + """ + Strip quotes of doctests output values: + + >>> strip_quotes("'foo'") + "foo" + >>> strip_quotes('"foo"') + "foo" + """ + def is_quoted_string(s): + s = s.strip() + return (len(s) >= 2 + and s[0] == s[-1] + and s[0] in ('"', "'")) + + def is_quoted_unicode(s): + s = s.strip() + return (len(s) >= 3 + and s[0] == 'u' + and s[1] == s[-1] + and s[1] in ('"', "'")) + + if is_quoted_string(want) and is_quoted_string(got): + want = want.strip()[1:-1] + got = got.strip()[1:-1] + elif is_quoted_unicode(want) and is_quoted_unicode(got): + want = want.strip()[2:-1] + got = got.strip()[2:-1] + return want, got + + +def str_prefix(s): + return s % {'_': '' if six.PY3 else 'u'} + + +class CaptureQueriesContext(object): + """ + Context manager that captures queries executed by the specified connection. + """ + def __init__(self, connection): + self.connection = connection + + def __iter__(self): + return iter(self.captured_queries) + + def __getitem__(self, index): + return self.captured_queries[index] + + def __len__(self): + return len(self.captured_queries) + + @property + def captured_queries(self): + return self.connection.queries[self.initial_queries:self.final_queries] + + def __enter__(self): + self.use_debug_cursor = self.connection.use_debug_cursor + self.connection.use_debug_cursor = True + self.initial_queries = len(self.connection.queries) + self.final_queries = None + request_started.disconnect(reset_queries) + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.connection.use_debug_cursor = self.use_debug_cursor + request_started.connect(reset_queries) + if exc_type is not None: + return + self.final_queries = len(self.connection.queries) + + +class IgnoreDeprecationWarningsMixin(object): + + warning_class = DeprecationWarning + + def setUp(self): + super(IgnoreDeprecationWarningsMixin, self).setUp() + self.catch_warnings = warnings.catch_warnings() + self.catch_warnings.__enter__() + warnings.filterwarnings("ignore", category=self.warning_class) + + def tearDown(self): + self.catch_warnings.__exit__(*sys.exc_info()) + super(IgnoreDeprecationWarningsMixin, self).tearDown() + + +class IgnorePendingDeprecationWarningsMixin(IgnoreDeprecationWarningsMixin): + + warning_class = PendingDeprecationWarning + + +@contextmanager +def patch_logger(logger_name, log_level): + """ + Context manager that takes a named logger and the logging level + and provides a simple mock-like list of messages received + """ + calls = [] + def replacement(msg): + calls.append(msg) + logger = logging.getLogger(logger_name) + orig = getattr(logger, log_level) + setattr(logger, log_level, replacement) + try: + yield calls + finally: + setattr(logger, log_level, orig) + + +class TransRealMixin(object): + """This is the only way to reset the translation machinery. Otherwise + the test suite occasionally fails because of global state pollution + between tests.""" + def flush_caches(self): + from django.utils.translation import trans_real + trans_real._translations = {} + trans_real._active = local() + trans_real._default = None + trans_real._accepted = {} + trans_real._checked_languages = {} + + def tearDown(self): + self.flush_caches() + super(TransRealMixin, self).tearDown() + + +# On OSes that don't provide tzset (Windows), we can't set the timezone +# in which the program runs. As a consequence, we must skip tests that +# don't enforce a specific timezone (with timezone.override or equivalent), +# or attempt to interpret naive datetimes in the default timezone. + +requires_tz_support = skipUnless(TZ_SUPPORT, + "This test relies on the ability to run a program in an arbitrary " + "time zone, but your operating system isn't able to do that.") |