Files
sqlalchemy/test/base/test_tutorials.py
T
Mike Bayer af0e59ff16 Disable "check unicode returns" under Python 3
Disabled the "unicode returns" check that runs on dialect startup when
running under Python 3, which for many years has occurred in order to test
the current DBAPI's behavior for whether or not it returns Python Unicode
or Py2K strings for the VARCHAR and NVARCHAR datatypes.  The check still
occurs by default under Python 2, however the mechanism to test the
behavior will be removed in SQLAlchemy 2.0 when Python 2 support is also
removed.

This logic was very effective when it was needed, however now that Python 3
is standard, all DBAPIs are expected to return Python 3 strings for
character datatypes.  In the unlikely case that a third party DBAPI does
not support this, the conversion logic within :class:`.String` is still
available and the third party dialect may specify this in its upfront
dialect flags by setting the dialect level flag ``returns_unicode_strings``
to one of :attr:`.String.RETURNS_CONDITIONAL` or
:attr:`.String.RETURNS_BYTES`, both of which will enable Unicode conversion
even under Python 3.

As part of this change, disabling testing of the doctest tutorials under
Python 2.

Fixes: #5315
Change-Id: I1260e894611409d3b7fe1a92bd90c52043bbcf19
2020-05-19 13:53:39 -04:00

160 lines
4.8 KiB
Python

from __future__ import print_function
import doctest
import logging
import os
import re
import sys
from sqlalchemy import testing
from sqlalchemy.testing import config
from sqlalchemy.testing import fixtures
class DocTest(fixtures.TestBase):
__requires__ = ("python3",)
def _setup_logger(self):
rootlogger = logging.getLogger("sqlalchemy.engine.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.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
| doctest.IGNORE_EXCEPTION_DETAIL
| _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")
@testing.emits_warning("SELECT statement has a cartesian")
def test_core(self):
self._run_doctest("core/tutorial.rst")
# unicode checker courtesy pytest
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")
# increase number to force pipeline run. 1