Initial commit
This commit is contained in:
0
venv/Lib/site-packages/mako/testing/__init__.py
Normal file
0
venv/Lib/site-packages/mako/testing/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
128
venv/Lib/site-packages/mako/testing/_config.py
Normal file
128
venv/Lib/site-packages/mako/testing/_config.py
Normal file
@@ -0,0 +1,128 @@
|
||||
import configparser
|
||||
import dataclasses
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
from typing import ClassVar
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
from .helpers import make_path
|
||||
|
||||
|
||||
class ConfigError(BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class MissingConfig(ConfigError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingConfigSection(ConfigError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingConfigItem(ConfigError):
|
||||
pass
|
||||
|
||||
|
||||
class ConfigValueTypeError(ConfigError):
|
||||
pass
|
||||
|
||||
|
||||
class _GetterDispatch:
|
||||
def __init__(self, initialdata, default_getter: Callable):
|
||||
self.default_getter = default_getter
|
||||
self.data = initialdata
|
||||
|
||||
def get_fn_for_type(self, type_):
|
||||
return self.data.get(type_, self.default_getter)
|
||||
|
||||
def get_typed_value(self, type_, name):
|
||||
get_fn = self.get_fn_for_type(type_)
|
||||
return get_fn(name)
|
||||
|
||||
|
||||
def _parse_cfg_file(filespec: Union[Path, str]):
|
||||
cfg = configparser.ConfigParser()
|
||||
try:
|
||||
filepath = make_path(filespec, check_exists=True)
|
||||
except FileNotFoundError as e:
|
||||
raise MissingConfig(f"No config file found at {filespec}") from e
|
||||
else:
|
||||
with open(filepath, encoding="utf-8") as f:
|
||||
cfg.read_file(f)
|
||||
return cfg
|
||||
|
||||
|
||||
def _build_getter(cfg_obj, cfg_section, method, converter=None):
|
||||
def caller(option, **kwargs):
|
||||
try:
|
||||
rv = getattr(cfg_obj, method)(cfg_section, option, **kwargs)
|
||||
except configparser.NoSectionError as nse:
|
||||
raise MissingConfigSection(
|
||||
f"No config section named {cfg_section}"
|
||||
) from nse
|
||||
except configparser.NoOptionError as noe:
|
||||
raise MissingConfigItem(f"No config item for {option}") from noe
|
||||
except ValueError as ve:
|
||||
# ConfigParser.getboolean, .getint, .getfloat raise ValueError
|
||||
# on bad types
|
||||
raise ConfigValueTypeError(
|
||||
f"Wrong value type for {option}"
|
||||
) from ve
|
||||
else:
|
||||
if converter:
|
||||
try:
|
||||
rv = converter(rv)
|
||||
except Exception as e:
|
||||
raise ConfigValueTypeError(
|
||||
f"Wrong value type for {option}"
|
||||
) from e
|
||||
return rv
|
||||
|
||||
return caller
|
||||
|
||||
|
||||
def _build_getter_dispatch(cfg_obj, cfg_section, converters=None):
|
||||
converters = converters or {}
|
||||
|
||||
default_getter = _build_getter(cfg_obj, cfg_section, "get")
|
||||
|
||||
# support ConfigParser builtins
|
||||
getters = {
|
||||
int: _build_getter(cfg_obj, cfg_section, "getint"),
|
||||
bool: _build_getter(cfg_obj, cfg_section, "getboolean"),
|
||||
float: _build_getter(cfg_obj, cfg_section, "getfloat"),
|
||||
str: default_getter,
|
||||
}
|
||||
|
||||
# use ConfigParser.get and convert value
|
||||
getters.update(
|
||||
{
|
||||
type_: _build_getter(
|
||||
cfg_obj, cfg_section, "get", converter=converter_fn
|
||||
)
|
||||
for type_, converter_fn in converters.items()
|
||||
}
|
||||
)
|
||||
|
||||
return _GetterDispatch(getters, default_getter)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReadsCfg:
|
||||
section_header: ClassVar[str]
|
||||
converters: ClassVar[Optional[dict]] = None
|
||||
|
||||
@classmethod
|
||||
def from_cfg_file(cls, filespec: Union[Path, str]):
|
||||
cfg = _parse_cfg_file(filespec)
|
||||
dispatch = _build_getter_dispatch(
|
||||
cfg, cls.section_header, converters=cls.converters
|
||||
)
|
||||
kwargs = {
|
||||
field.name: dispatch.get_typed_value(field.type, field.name)
|
||||
for field in dataclasses.fields(cls)
|
||||
}
|
||||
return cls(**kwargs)
|
||||
166
venv/Lib/site-packages/mako/testing/assertions.py
Normal file
166
venv/Lib/site-packages/mako/testing/assertions.py
Normal file
@@ -0,0 +1,166 @@
|
||||
import contextlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def eq_(a, b, msg=None):
|
||||
"""Assert a == b, with repr messaging on failure."""
|
||||
assert a == b, msg or "%r != %r" % (a, b)
|
||||
|
||||
|
||||
def ne_(a, b, msg=None):
|
||||
"""Assert a != b, with repr messaging on failure."""
|
||||
assert a != b, msg or "%r == %r" % (a, b)
|
||||
|
||||
|
||||
def in_(a, b, msg=None):
|
||||
"""Assert a in b, with repr messaging on failure."""
|
||||
assert a in b, msg or "%r not in %r" % (a, b)
|
||||
|
||||
|
||||
def not_in(a, b, msg=None):
|
||||
"""Assert a in not b, with repr messaging on failure."""
|
||||
assert a not in b, msg or "%r is in %r" % (a, b)
|
||||
|
||||
|
||||
def _assert_proper_exception_context(exception):
|
||||
"""assert that any exception we're catching does not have a __context__
|
||||
without a __cause__, and that __suppress_context__ is never set.
|
||||
|
||||
Python 3 will report nested as exceptions as "during the handling of
|
||||
error X, error Y occurred". That's not what we want to do. We want
|
||||
these exceptions in a cause chain.
|
||||
|
||||
"""
|
||||
|
||||
if (
|
||||
exception.__context__ is not exception.__cause__
|
||||
and not exception.__suppress_context__
|
||||
):
|
||||
assert False, (
|
||||
"Exception %r was correctly raised but did not set a cause, "
|
||||
"within context %r as its cause."
|
||||
% (exception, exception.__context__)
|
||||
)
|
||||
|
||||
|
||||
def _assert_proper_cause_cls(exception, cause_cls):
|
||||
"""assert that any exception we're catching does not have a __context__
|
||||
without a __cause__, and that __suppress_context__ is never set.
|
||||
|
||||
Python 3 will report nested as exceptions as "during the handling of
|
||||
error X, error Y occurred". That's not what we want to do. We want
|
||||
these exceptions in a cause chain.
|
||||
|
||||
"""
|
||||
assert isinstance(exception.__cause__, cause_cls), (
|
||||
"Exception %r was correctly raised but has cause %r, which does not "
|
||||
"have the expected cause type %r."
|
||||
% (exception, exception.__cause__, cause_cls)
|
||||
)
|
||||
|
||||
|
||||
def assert_raises(except_cls, callable_, *args, **kw):
|
||||
return _assert_raises(except_cls, callable_, args, kw)
|
||||
|
||||
|
||||
def assert_raises_with_proper_context(except_cls, callable_, *args, **kw):
|
||||
return _assert_raises(except_cls, callable_, args, kw, check_context=True)
|
||||
|
||||
|
||||
def assert_raises_with_given_cause(
|
||||
except_cls, cause_cls, callable_, *args, **kw
|
||||
):
|
||||
return _assert_raises(except_cls, callable_, args, kw, cause_cls=cause_cls)
|
||||
|
||||
|
||||
def assert_raises_message(except_cls, msg, callable_, *args, **kwargs):
|
||||
return _assert_raises(except_cls, callable_, args, kwargs, msg=msg)
|
||||
|
||||
|
||||
def assert_raises_message_with_proper_context(
|
||||
except_cls, msg, callable_, *args, **kwargs
|
||||
):
|
||||
return _assert_raises(
|
||||
except_cls, callable_, args, kwargs, msg=msg, check_context=True
|
||||
)
|
||||
|
||||
|
||||
def assert_raises_message_with_given_cause(
|
||||
except_cls, msg, cause_cls, callable_, *args, **kwargs
|
||||
):
|
||||
return _assert_raises(
|
||||
except_cls, callable_, args, kwargs, msg=msg, cause_cls=cause_cls
|
||||
)
|
||||
|
||||
|
||||
def _assert_raises(
|
||||
except_cls,
|
||||
callable_,
|
||||
args,
|
||||
kwargs,
|
||||
msg=None,
|
||||
check_context=False,
|
||||
cause_cls=None,
|
||||
):
|
||||
with _expect_raises(except_cls, msg, check_context, cause_cls) as ec:
|
||||
callable_(*args, **kwargs)
|
||||
return ec.error
|
||||
|
||||
|
||||
class _ErrorContainer:
|
||||
error = None
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _expect_raises(except_cls, msg=None, check_context=False, cause_cls=None):
|
||||
ec = _ErrorContainer()
|
||||
if check_context:
|
||||
are_we_already_in_a_traceback = sys.exc_info()[0]
|
||||
try:
|
||||
yield ec
|
||||
success = False
|
||||
except except_cls as err:
|
||||
ec.error = err
|
||||
success = True
|
||||
if msg is not None:
|
||||
# I'm often pdbing here, and "err" above isn't
|
||||
# in scope, so assign the string explicitly
|
||||
error_as_string = str(err)
|
||||
assert re.search(msg, error_as_string, re.UNICODE), "%r !~ %s" % (
|
||||
msg,
|
||||
error_as_string,
|
||||
)
|
||||
if cause_cls is not None:
|
||||
_assert_proper_cause_cls(err, cause_cls)
|
||||
if check_context and not are_we_already_in_a_traceback:
|
||||
_assert_proper_exception_context(err)
|
||||
print(str(err).encode("utf-8"))
|
||||
|
||||
# it's generally a good idea to not carry traceback objects outside
|
||||
# of the except: block, but in this case especially we seem to have
|
||||
# hit some bug in either python 3.10.0b2 or greenlet or both which
|
||||
# this seems to fix:
|
||||
# https://github.com/python-greenlet/greenlet/issues/242
|
||||
del ec
|
||||
|
||||
# assert outside the block so it works for AssertionError too !
|
||||
assert success, "Callable did not raise an exception"
|
||||
|
||||
|
||||
def expect_raises(except_cls, check_context=False):
|
||||
return _expect_raises(except_cls, check_context=check_context)
|
||||
|
||||
|
||||
def expect_raises_message(except_cls, msg, check_context=False):
|
||||
return _expect_raises(except_cls, msg=msg, check_context=check_context)
|
||||
|
||||
|
||||
def expect_raises_with_proper_context(except_cls, check_context=True):
|
||||
return _expect_raises(except_cls, check_context=check_context)
|
||||
|
||||
|
||||
def expect_raises_message_with_proper_context(
|
||||
except_cls, msg, check_context=True
|
||||
):
|
||||
return _expect_raises(except_cls, msg=msg, check_context=check_context)
|
||||
17
venv/Lib/site-packages/mako/testing/config.py
Normal file
17
venv/Lib/site-packages/mako/testing/config.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from ._config import ReadsCfg
|
||||
from .helpers import make_path
|
||||
|
||||
|
||||
@dataclass
|
||||
class Config(ReadsCfg):
|
||||
module_base: Path
|
||||
template_base: Path
|
||||
|
||||
section_header = "mako_testing"
|
||||
converters = {Path: make_path}
|
||||
|
||||
|
||||
config = Config.from_cfg_file("./setup.cfg")
|
||||
80
venv/Lib/site-packages/mako/testing/exclusions.py
Normal file
80
venv/Lib/site-packages/mako/testing/exclusions.py
Normal file
@@ -0,0 +1,80 @@
|
||||
import pytest
|
||||
|
||||
from mako.ext.beaker_cache import has_beaker
|
||||
from mako.util import update_wrapper
|
||||
|
||||
|
||||
try:
|
||||
import babel.messages.extract as babel
|
||||
except ImportError:
|
||||
babel = None
|
||||
|
||||
|
||||
try:
|
||||
import lingua
|
||||
except ImportError:
|
||||
lingua = None
|
||||
|
||||
|
||||
try:
|
||||
import dogpile.cache # noqa
|
||||
except ImportError:
|
||||
has_dogpile_cache = False
|
||||
else:
|
||||
has_dogpile_cache = True
|
||||
|
||||
|
||||
requires_beaker = pytest.mark.skipif(
|
||||
not has_beaker, reason="Beaker is required for these tests."
|
||||
)
|
||||
|
||||
|
||||
requires_babel = pytest.mark.skipif(
|
||||
babel is None, reason="babel not installed: skipping babelplugin test"
|
||||
)
|
||||
|
||||
|
||||
requires_lingua = pytest.mark.skipif(
|
||||
lingua is None, reason="lingua not installed: skipping linguaplugin test"
|
||||
)
|
||||
|
||||
|
||||
requires_dogpile_cache = pytest.mark.skipif(
|
||||
not has_dogpile_cache,
|
||||
reason="dogpile.cache is required to run these tests",
|
||||
)
|
||||
|
||||
|
||||
def _pygments_version():
|
||||
try:
|
||||
import pygments
|
||||
|
||||
version = pygments.__version__
|
||||
except:
|
||||
version = "0"
|
||||
return version
|
||||
|
||||
|
||||
requires_pygments_14 = pytest.mark.skipif(
|
||||
_pygments_version() < "1.4", reason="Requires pygments 1.4 or greater"
|
||||
)
|
||||
|
||||
|
||||
# def requires_pygments_14(fn):
|
||||
|
||||
# return skip_if(
|
||||
# lambda: version < "1.4", "Requires pygments 1.4 or greater"
|
||||
# )(fn)
|
||||
|
||||
|
||||
def requires_no_pygments_exceptions(fn):
|
||||
def go(*arg, **kw):
|
||||
from mako import exceptions
|
||||
|
||||
exceptions._install_fallback()
|
||||
try:
|
||||
return fn(*arg, **kw)
|
||||
finally:
|
||||
exceptions._install_highlighting()
|
||||
|
||||
return update_wrapper(go, fn)
|
||||
119
venv/Lib/site-packages/mako/testing/fixtures.py
Normal file
119
venv/Lib/site-packages/mako/testing/fixtures.py
Normal file
@@ -0,0 +1,119 @@
|
||||
import os
|
||||
|
||||
from mako.cache import CacheImpl
|
||||
from mako.cache import register_plugin
|
||||
from mako.template import Template
|
||||
from .assertions import eq_
|
||||
from .config import config
|
||||
|
||||
|
||||
class TemplateTest:
|
||||
def _file_template(self, filename, **kw):
|
||||
filepath = self._file_path(filename)
|
||||
return Template(
|
||||
uri=filename,
|
||||
filename=filepath,
|
||||
module_directory=config.module_base,
|
||||
**kw,
|
||||
)
|
||||
|
||||
def _file_path(self, filename):
|
||||
name, ext = os.path.splitext(filename)
|
||||
py3k_path = os.path.join(config.template_base, name + "_py3k" + ext)
|
||||
if os.path.exists(py3k_path):
|
||||
return py3k_path
|
||||
|
||||
return os.path.join(config.template_base, filename)
|
||||
|
||||
def _do_file_test(
|
||||
self,
|
||||
filename,
|
||||
expected,
|
||||
filters=None,
|
||||
unicode_=True,
|
||||
template_args=None,
|
||||
**kw,
|
||||
):
|
||||
t1 = self._file_template(filename, **kw)
|
||||
self._do_test(
|
||||
t1,
|
||||
expected,
|
||||
filters=filters,
|
||||
unicode_=unicode_,
|
||||
template_args=template_args,
|
||||
)
|
||||
|
||||
def _do_memory_test(
|
||||
self,
|
||||
source,
|
||||
expected,
|
||||
filters=None,
|
||||
unicode_=True,
|
||||
template_args=None,
|
||||
**kw,
|
||||
):
|
||||
t1 = Template(text=source, **kw)
|
||||
self._do_test(
|
||||
t1,
|
||||
expected,
|
||||
filters=filters,
|
||||
unicode_=unicode_,
|
||||
template_args=template_args,
|
||||
)
|
||||
|
||||
def _do_test(
|
||||
self,
|
||||
template,
|
||||
expected,
|
||||
filters=None,
|
||||
template_args=None,
|
||||
unicode_=True,
|
||||
):
|
||||
if template_args is None:
|
||||
template_args = {}
|
||||
if unicode_:
|
||||
output = template.render_unicode(**template_args)
|
||||
else:
|
||||
output = template.render(**template_args)
|
||||
|
||||
if filters:
|
||||
output = filters(output)
|
||||
eq_(output, expected)
|
||||
|
||||
def indicates_unbound_local_error(self, rendered_output, unbound_var):
|
||||
var = f"'{unbound_var}'"
|
||||
error_msgs = (
|
||||
# < 3.11
|
||||
f"local variable {var} referenced before assignment",
|
||||
# >= 3.11
|
||||
f"cannot access local variable {var} where it is not associated",
|
||||
)
|
||||
return any((msg in rendered_output) for msg in error_msgs)
|
||||
|
||||
|
||||
class PlainCacheImpl(CacheImpl):
|
||||
"""Simple memory cache impl so that tests which
|
||||
use caching can run without beaker."""
|
||||
|
||||
def __init__(self, cache):
|
||||
self.cache = cache
|
||||
self.data = {}
|
||||
|
||||
def get_or_create(self, key, creation_function, **kw):
|
||||
if key in self.data:
|
||||
return self.data[key]
|
||||
else:
|
||||
self.data[key] = data = creation_function(**kw)
|
||||
return data
|
||||
|
||||
def put(self, key, value, **kw):
|
||||
self.data[key] = value
|
||||
|
||||
def get(self, key, **kw):
|
||||
return self.data[key]
|
||||
|
||||
def invalidate(self, key, **kw):
|
||||
del self.data[key]
|
||||
|
||||
|
||||
register_plugin("plain", __name__, "PlainCacheImpl")
|
||||
71
venv/Lib/site-packages/mako/testing/helpers.py
Normal file
71
venv/Lib/site-packages/mako/testing/helpers.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import contextlib
|
||||
import pathlib
|
||||
from pathlib import Path
|
||||
import re
|
||||
import time
|
||||
from typing import Union
|
||||
from unittest import mock
|
||||
|
||||
|
||||
def flatten_result(result):
|
||||
return re.sub(r"[\s\r\n]+", " ", result).strip()
|
||||
|
||||
|
||||
def result_lines(result):
|
||||
return [
|
||||
x.strip()
|
||||
for x in re.split(r"\r?\n", re.sub(r" +", " ", result))
|
||||
if x.strip() != ""
|
||||
]
|
||||
|
||||
|
||||
def result_raw_lines(result):
|
||||
return [x for x in re.split(r"\r?\n", result) if x.strip() != ""]
|
||||
|
||||
|
||||
def make_path(
|
||||
filespec: Union[Path, str],
|
||||
make_absolute: bool = True,
|
||||
check_exists: bool = False,
|
||||
) -> Path:
|
||||
path = Path(filespec)
|
||||
if make_absolute:
|
||||
path = path.resolve(strict=check_exists)
|
||||
if check_exists and (not path.exists()):
|
||||
raise FileNotFoundError(f"No file or directory at {filespec}")
|
||||
return path
|
||||
|
||||
|
||||
def _unlink_path(path, missing_ok=False):
|
||||
# Replicate 3.8+ functionality in 3.7
|
||||
cm = contextlib.nullcontext()
|
||||
if missing_ok:
|
||||
cm = contextlib.suppress(FileNotFoundError)
|
||||
|
||||
with cm:
|
||||
path.unlink()
|
||||
|
||||
|
||||
def replace_file_with_dir(pathspec):
|
||||
path = pathlib.Path(pathspec)
|
||||
_unlink_path(path, missing_ok=True)
|
||||
path.mkdir(exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
def file_with_template_code(filespec):
|
||||
with open(filespec, "w") as f:
|
||||
f.write(
|
||||
"""
|
||||
i am an artificial template just for you
|
||||
"""
|
||||
)
|
||||
return filespec
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def rewind_compile_time(hours=1):
|
||||
rewound = time.time() - (hours * 3_600)
|
||||
with mock.patch("mako.codegen.time") as codegen_time:
|
||||
codegen_time.time.return_value = rewound
|
||||
yield
|
||||
Reference in New Issue
Block a user