diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-12-10 17:09:47 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-12-10 17:09:47 -0500 |
| commit | d533b8e9223b9c938655e5b666fc928e2d996cd3 (patch) | |
| tree | 965e200efa7590878b40d61399a16f32453655d0 /test/base/test_tutorials.py | |
| parent | fb443199c19296edbd3ea5e805f57777d450a4db (diff) | |
| download | sqlalchemy-d533b8e9223b9c938655e5b666fc928e2d996cd3.tar.gz | |
- The ORM and Core tutorials, which have always been in doctest format,
are now exercised within the normal unit test suite in both Python
2 and Python 3.
- remove the old testdocs.py runner and replace with test/base/test_tutorials.py
- use pytest's unicode fixer so that we can test for unicode strings
in both py2k/3k
- use py3k format overall for prints, exceptions
- add other fixers to guarantee deterministic results
- add skips and ellipses to outputs that aren't worth matching
Diffstat (limited to 'test/base/test_tutorials.py')
| -rw-r--r-- | test/base/test_tutorials.py | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/test/base/test_tutorials.py b/test/base/test_tutorials.py new file mode 100644 index 000000000..73dcbb524 --- /dev/null +++ b/test/base/test_tutorials.py @@ -0,0 +1,144 @@ +from __future__ import print_function +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import config +import doctest +import logging +import sys +import re +import os + + +class DocTest(fixtures.TestBase): + def _setup_logger(self): + rootlogger = logging.getLogger('sqlalchemy.engine.base.Engine') + + class MyStream(object): + def write(self, string): + sys.stdout.write(string) + sys.stdout.flush() + + def flush(self): + pass + + self._handler = handler = logging.StreamHandler(MyStream()) + handler.setFormatter(logging.Formatter('%(message)s')) + rootlogger.addHandler(handler) + + def _teardown_logger(self): + rootlogger = logging.getLogger('sqlalchemy.engine.base.Engine') + rootlogger.removeHandler(self._handler) + + def _setup_create_table_patcher(self): + from sqlalchemy.sql import ddl + self.orig_sort = ddl.sort_tables_and_constraints + + def our_sort(tables, **kw): + return self.orig_sort( + sorted(tables, key=lambda t: t.key), **kw + ) + ddl.sort_tables_and_constraints = our_sort + + def _teardown_create_table_patcher(self): + from sqlalchemy.sql import ddl + ddl.sort_tables_and_constraints = self.orig_sort + + def setup(self): + self._setup_logger() + self._setup_create_table_patcher() + + def teardown(self): + self._teardown_create_table_patcher() + self._teardown_logger() + + + def _run_doctest_for_content(self, name, content): + optionflags = ( + doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | + _get_allow_unicode_flag() + ) + runner = doctest.DocTestRunner( + verbose=None, optionflags=optionflags, + checker=_get_unicode_checker()) + globs = { + 'print_function': print_function} + parser = doctest.DocTestParser() + test = parser.get_doctest(content, globs, name, name, 0) + runner.run(test) + runner.summarize() + assert not runner.failures + + def _run_doctest(self, fname): + here = os.path.dirname(__file__) + sqla_base = os.path.normpath(os.path.join(here, "..", "..")) + path = os.path.join(sqla_base, "doc/build", fname) + if not os.path.exists(path): + config.skip_test("Can't find documentation file %r" % path) + with open(path) as file_: + content = file_.read() + content = re.sub(r'{(?:stop|sql|opensql)}', '', content) + self._run_doctest_for_content(fname, content) + + def test_orm(self): + self._run_doctest("orm/tutorial.rst") + + def test_core(self): + self._run_doctest("core/tutorial.rst") + + +# unicode checker courtesy py.test + + +def _get_unicode_checker(): + """ + Returns a doctest.OutputChecker subclass that takes in account the + ALLOW_UNICODE option to ignore u'' prefixes in strings. Useful + when the same doctest should run in Python 2 and Python 3. + + An inner class is used to avoid importing "doctest" at the module + level. + """ + if hasattr(_get_unicode_checker, 'UnicodeOutputChecker'): + return _get_unicode_checker.UnicodeOutputChecker() + + import doctest + import re + + class UnicodeOutputChecker(doctest.OutputChecker): + """ + Copied from doctest_nose_plugin.py from the nltk project: + https://github.com/nltk/nltk + """ + + _literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE) + + def check_output(self, want, got, optionflags): + res = doctest.OutputChecker.check_output(self, want, got, + optionflags) + if res: + return True + + if not (optionflags & _get_allow_unicode_flag()): + return False + + else: # pragma: no cover + # the code below will end up executed only in Python 2 in + # our tests, and our coverage check runs in Python 3 only + def remove_u_prefixes(txt): + return re.sub(self._literal_re, r'\1\2', txt) + + want = remove_u_prefixes(want) + got = remove_u_prefixes(got) + res = doctest.OutputChecker.check_output(self, want, got, + optionflags) + return res + + _get_unicode_checker.UnicodeOutputChecker = UnicodeOutputChecker + return _get_unicode_checker.UnicodeOutputChecker() + + +def _get_allow_unicode_flag(): + """ + Registers and returns the ALLOW_UNICODE flag. + """ + import doctest + return doctest.register_optionflag('ALLOW_UNICODE') |
