mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-06 13:11:15 -07:00
Bump mako from 1.1.6 to 1.2.0 (#1684)
* Bump mako from 1.1.6 to 1.2.0 Bumps [mako](https://github.com/sqlalchemy/mako) from 1.1.6 to 1.2.0. - [Release notes](https://github.com/sqlalchemy/mako/releases) - [Changelog](https://github.com/sqlalchemy/mako/blob/main/CHANGES) - [Commits](https://github.com/sqlalchemy/mako/commits) --- updated-dependencies: - dependency-name: mako dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update mako==1.2.0 * Update MarkupSafe==2.1.1 * Add importlib-metadata==4.11.3 * Update requirements.txt Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci]
This commit is contained in:
parent
aa0c58ef0e
commit
238afb4794
45 changed files with 2948 additions and 848 deletions
1075
lib/importlib_metadata/__init__.py
Normal file
1075
lib/importlib_metadata/__init__.py
Normal file
File diff suppressed because it is too large
Load diff
68
lib/importlib_metadata/_adapters.py
Normal file
68
lib/importlib_metadata/_adapters.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
import re
|
||||
import textwrap
|
||||
import email.message
|
||||
|
||||
from ._text import FoldedCase
|
||||
|
||||
|
||||
class Message(email.message.Message):
|
||||
multiple_use_keys = set(
|
||||
map(
|
||||
FoldedCase,
|
||||
[
|
||||
'Classifier',
|
||||
'Obsoletes-Dist',
|
||||
'Platform',
|
||||
'Project-URL',
|
||||
'Provides-Dist',
|
||||
'Provides-Extra',
|
||||
'Requires-Dist',
|
||||
'Requires-External',
|
||||
'Supported-Platform',
|
||||
'Dynamic',
|
||||
],
|
||||
)
|
||||
)
|
||||
"""
|
||||
Keys that may be indicated multiple times per PEP 566.
|
||||
"""
|
||||
|
||||
def __new__(cls, orig: email.message.Message):
|
||||
res = super().__new__(cls)
|
||||
vars(res).update(vars(orig))
|
||||
return res
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._headers = self._repair_headers()
|
||||
|
||||
# suppress spurious error from mypy
|
||||
def __iter__(self):
|
||||
return super().__iter__()
|
||||
|
||||
def _repair_headers(self):
|
||||
def redent(value):
|
||||
"Correct for RFC822 indentation"
|
||||
if not value or '\n' not in value:
|
||||
return value
|
||||
return textwrap.dedent(' ' * 8 + value)
|
||||
|
||||
headers = [(key, redent(value)) for key, value in vars(self)['_headers']]
|
||||
if self._payload:
|
||||
headers.append(('Description', self.get_payload()))
|
||||
return headers
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
"""
|
||||
Convert PackageMetadata to a JSON-compatible format
|
||||
per PEP 0566.
|
||||
"""
|
||||
|
||||
def transform(key):
|
||||
value = self.get_all(key) if key in self.multiple_use_keys else self[key]
|
||||
if key == 'Keywords':
|
||||
value = re.split(r'\s+', value)
|
||||
tk = key.lower().replace('-', '_')
|
||||
return tk, value
|
||||
|
||||
return dict(map(transform, map(FoldedCase, self)))
|
30
lib/importlib_metadata/_collections.py
Normal file
30
lib/importlib_metadata/_collections.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
import collections
|
||||
|
||||
|
||||
# from jaraco.collections 3.3
|
||||
class FreezableDefaultDict(collections.defaultdict):
|
||||
"""
|
||||
Often it is desirable to prevent the mutation of
|
||||
a default dict after its initial construction, such
|
||||
as to prevent mutation during iteration.
|
||||
|
||||
>>> dd = FreezableDefaultDict(list)
|
||||
>>> dd[0].append('1')
|
||||
>>> dd.freeze()
|
||||
>>> dd[1]
|
||||
[]
|
||||
>>> len(dd)
|
||||
1
|
||||
"""
|
||||
|
||||
def __missing__(self, key):
|
||||
return getattr(self, '_frozen', super().__missing__)(key)
|
||||
|
||||
def freeze(self):
|
||||
self._frozen = lambda key: self.default_factory()
|
||||
|
||||
|
||||
class Pair(collections.namedtuple('Pair', 'name value')):
|
||||
@classmethod
|
||||
def parse(cls, text):
|
||||
return cls(*map(str.strip, text.split("=", 1)))
|
71
lib/importlib_metadata/_compat.py
Normal file
71
lib/importlib_metadata/_compat.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
import sys
|
||||
import platform
|
||||
|
||||
|
||||
__all__ = ['install', 'NullFinder', 'Protocol']
|
||||
|
||||
|
||||
try:
|
||||
from typing import Protocol
|
||||
except ImportError: # pragma: no cover
|
||||
from typing_extensions import Protocol # type: ignore
|
||||
|
||||
|
||||
def install(cls):
|
||||
"""
|
||||
Class decorator for installation on sys.meta_path.
|
||||
|
||||
Adds the backport DistributionFinder to sys.meta_path and
|
||||
attempts to disable the finder functionality of the stdlib
|
||||
DistributionFinder.
|
||||
"""
|
||||
sys.meta_path.append(cls())
|
||||
disable_stdlib_finder()
|
||||
return cls
|
||||
|
||||
|
||||
def disable_stdlib_finder():
|
||||
"""
|
||||
Give the backport primacy for discovering path-based distributions
|
||||
by monkey-patching the stdlib O_O.
|
||||
|
||||
See #91 for more background for rationale on this sketchy
|
||||
behavior.
|
||||
"""
|
||||
|
||||
def matches(finder):
|
||||
return getattr(
|
||||
finder, '__module__', None
|
||||
) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions')
|
||||
|
||||
for finder in filter(matches, sys.meta_path): # pragma: nocover
|
||||
del finder.find_distributions
|
||||
|
||||
|
||||
class NullFinder:
|
||||
"""
|
||||
A "Finder" (aka "MetaClassFinder") that never finds any modules,
|
||||
but may find distributions.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def find_spec(*args, **kwargs):
|
||||
return None
|
||||
|
||||
# In Python 2, the import system requires finders
|
||||
# to have a find_module() method, but this usage
|
||||
# is deprecated in Python 3 in favor of find_spec().
|
||||
# For the purposes of this finder (i.e. being present
|
||||
# on sys.meta_path but having no other import
|
||||
# system functionality), the two methods are identical.
|
||||
find_module = find_spec
|
||||
|
||||
|
||||
def pypy_partial(val):
|
||||
"""
|
||||
Adjust for variable stacklevel on partial under PyPy.
|
||||
|
||||
Workaround for #327.
|
||||
"""
|
||||
is_pypy = platform.python_implementation() == 'PyPy'
|
||||
return val + is_pypy
|
104
lib/importlib_metadata/_functools.py
Normal file
104
lib/importlib_metadata/_functools.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
import types
|
||||
import functools
|
||||
|
||||
|
||||
# from jaraco.functools 3.3
|
||||
def method_cache(method, cache_wrapper=None):
|
||||
"""
|
||||
Wrap lru_cache to support storing the cache data in the object instances.
|
||||
|
||||
Abstracts the common paradigm where the method explicitly saves an
|
||||
underscore-prefixed protected property on first call and returns that
|
||||
subsequently.
|
||||
|
||||
>>> class MyClass:
|
||||
... calls = 0
|
||||
...
|
||||
... @method_cache
|
||||
... def method(self, value):
|
||||
... self.calls += 1
|
||||
... return value
|
||||
|
||||
>>> a = MyClass()
|
||||
>>> a.method(3)
|
||||
3
|
||||
>>> for x in range(75):
|
||||
... res = a.method(x)
|
||||
>>> a.calls
|
||||
75
|
||||
|
||||
Note that the apparent behavior will be exactly like that of lru_cache
|
||||
except that the cache is stored on each instance, so values in one
|
||||
instance will not flush values from another, and when an instance is
|
||||
deleted, so are the cached values for that instance.
|
||||
|
||||
>>> b = MyClass()
|
||||
>>> for x in range(35):
|
||||
... res = b.method(x)
|
||||
>>> b.calls
|
||||
35
|
||||
>>> a.method(0)
|
||||
0
|
||||
>>> a.calls
|
||||
75
|
||||
|
||||
Note that if method had been decorated with ``functools.lru_cache()``,
|
||||
a.calls would have been 76 (due to the cached value of 0 having been
|
||||
flushed by the 'b' instance).
|
||||
|
||||
Clear the cache with ``.cache_clear()``
|
||||
|
||||
>>> a.method.cache_clear()
|
||||
|
||||
Same for a method that hasn't yet been called.
|
||||
|
||||
>>> c = MyClass()
|
||||
>>> c.method.cache_clear()
|
||||
|
||||
Another cache wrapper may be supplied:
|
||||
|
||||
>>> cache = functools.lru_cache(maxsize=2)
|
||||
>>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache)
|
||||
>>> a = MyClass()
|
||||
>>> a.method2()
|
||||
3
|
||||
|
||||
Caution - do not subsequently wrap the method with another decorator, such
|
||||
as ``@property``, which changes the semantics of the function.
|
||||
|
||||
See also
|
||||
http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
|
||||
for another implementation and additional justification.
|
||||
"""
|
||||
cache_wrapper = cache_wrapper or functools.lru_cache()
|
||||
|
||||
def wrapper(self, *args, **kwargs):
|
||||
# it's the first call, replace the method with a cached, bound method
|
||||
bound_method = types.MethodType(method, self)
|
||||
cached_method = cache_wrapper(bound_method)
|
||||
setattr(self, method.__name__, cached_method)
|
||||
return cached_method(*args, **kwargs)
|
||||
|
||||
# Support cache clear even before cache has been created.
|
||||
wrapper.cache_clear = lambda: None
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
# From jaraco.functools 3.3
|
||||
def pass_none(func):
|
||||
"""
|
||||
Wrap func so it's not called if its first param is None
|
||||
|
||||
>>> print_text = pass_none(print)
|
||||
>>> print_text('text')
|
||||
text
|
||||
>>> print_text(None)
|
||||
"""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(param, *args, **kwargs):
|
||||
if param is not None:
|
||||
return func(param, *args, **kwargs)
|
||||
|
||||
return wrapper
|
73
lib/importlib_metadata/_itertools.py
Normal file
73
lib/importlib_metadata/_itertools.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
from itertools import filterfalse
|
||||
|
||||
|
||||
def unique_everseen(iterable, key=None):
|
||||
"List unique elements, preserving order. Remember all elements ever seen."
|
||||
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
|
||||
# unique_everseen('ABBCcAD', str.lower) --> A B C D
|
||||
seen = set()
|
||||
seen_add = seen.add
|
||||
if key is None:
|
||||
for element in filterfalse(seen.__contains__, iterable):
|
||||
seen_add(element)
|
||||
yield element
|
||||
else:
|
||||
for element in iterable:
|
||||
k = key(element)
|
||||
if k not in seen:
|
||||
seen_add(k)
|
||||
yield element
|
||||
|
||||
|
||||
# copied from more_itertools 8.8
|
||||
def always_iterable(obj, base_type=(str, bytes)):
|
||||
"""If *obj* is iterable, return an iterator over its items::
|
||||
|
||||
>>> obj = (1, 2, 3)
|
||||
>>> list(always_iterable(obj))
|
||||
[1, 2, 3]
|
||||
|
||||
If *obj* is not iterable, return a one-item iterable containing *obj*::
|
||||
|
||||
>>> obj = 1
|
||||
>>> list(always_iterable(obj))
|
||||
[1]
|
||||
|
||||
If *obj* is ``None``, return an empty iterable:
|
||||
|
||||
>>> obj = None
|
||||
>>> list(always_iterable(None))
|
||||
[]
|
||||
|
||||
By default, binary and text strings are not considered iterable::
|
||||
|
||||
>>> obj = 'foo'
|
||||
>>> list(always_iterable(obj))
|
||||
['foo']
|
||||
|
||||
If *base_type* is set, objects for which ``isinstance(obj, base_type)``
|
||||
returns ``True`` won't be considered iterable.
|
||||
|
||||
>>> obj = {'a': 1}
|
||||
>>> list(always_iterable(obj)) # Iterate over the dict's keys
|
||||
['a']
|
||||
>>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit
|
||||
[{'a': 1}]
|
||||
|
||||
Set *base_type* to ``None`` to avoid any special handling and treat objects
|
||||
Python considers iterable as iterable:
|
||||
|
||||
>>> obj = 'foo'
|
||||
>>> list(always_iterable(obj, base_type=None))
|
||||
['f', 'o', 'o']
|
||||
"""
|
||||
if obj is None:
|
||||
return iter(())
|
||||
|
||||
if (base_type is not None) and isinstance(obj, base_type):
|
||||
return iter((obj,))
|
||||
|
||||
try:
|
||||
return iter(obj)
|
||||
except TypeError:
|
||||
return iter((obj,))
|
48
lib/importlib_metadata/_meta.py
Normal file
48
lib/importlib_metadata/_meta.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
from ._compat import Protocol
|
||||
from typing import Any, Dict, Iterator, List, TypeVar, Union
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
class PackageMetadata(Protocol):
|
||||
def __len__(self) -> int:
|
||||
... # pragma: no cover
|
||||
|
||||
def __contains__(self, item: str) -> bool:
|
||||
... # pragma: no cover
|
||||
|
||||
def __getitem__(self, key: str) -> str:
|
||||
... # pragma: no cover
|
||||
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
... # pragma: no cover
|
||||
|
||||
def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]:
|
||||
"""
|
||||
Return all values associated with a possibly multi-valued key.
|
||||
"""
|
||||
|
||||
@property
|
||||
def json(self) -> Dict[str, Union[str, List[str]]]:
|
||||
"""
|
||||
A JSON-compatible form of the metadata.
|
||||
"""
|
||||
|
||||
|
||||
class SimplePath(Protocol):
|
||||
"""
|
||||
A minimal subset of pathlib.Path required by PathDistribution.
|
||||
"""
|
||||
|
||||
def joinpath(self) -> 'SimplePath':
|
||||
... # pragma: no cover
|
||||
|
||||
def __truediv__(self) -> 'SimplePath':
|
||||
... # pragma: no cover
|
||||
|
||||
def parent(self) -> 'SimplePath':
|
||||
... # pragma: no cover
|
||||
|
||||
def read_text(self) -> str:
|
||||
... # pragma: no cover
|
99
lib/importlib_metadata/_text.py
Normal file
99
lib/importlib_metadata/_text.py
Normal file
|
@ -0,0 +1,99 @@
|
|||
import re
|
||||
|
||||
from ._functools import method_cache
|
||||
|
||||
|
||||
# from jaraco.text 3.5
|
||||
class FoldedCase(str):
|
||||
"""
|
||||
A case insensitive string class; behaves just like str
|
||||
except compares equal when the only variation is case.
|
||||
|
||||
>>> s = FoldedCase('hello world')
|
||||
|
||||
>>> s == 'Hello World'
|
||||
True
|
||||
|
||||
>>> 'Hello World' == s
|
||||
True
|
||||
|
||||
>>> s != 'Hello World'
|
||||
False
|
||||
|
||||
>>> s.index('O')
|
||||
4
|
||||
|
||||
>>> s.split('O')
|
||||
['hell', ' w', 'rld']
|
||||
|
||||
>>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
|
||||
['alpha', 'Beta', 'GAMMA']
|
||||
|
||||
Sequence membership is straightforward.
|
||||
|
||||
>>> "Hello World" in [s]
|
||||
True
|
||||
>>> s in ["Hello World"]
|
||||
True
|
||||
|
||||
You may test for set inclusion, but candidate and elements
|
||||
must both be folded.
|
||||
|
||||
>>> FoldedCase("Hello World") in {s}
|
||||
True
|
||||
>>> s in {FoldedCase("Hello World")}
|
||||
True
|
||||
|
||||
String inclusion works as long as the FoldedCase object
|
||||
is on the right.
|
||||
|
||||
>>> "hello" in FoldedCase("Hello World")
|
||||
True
|
||||
|
||||
But not if the FoldedCase object is on the left:
|
||||
|
||||
>>> FoldedCase('hello') in 'Hello World'
|
||||
False
|
||||
|
||||
In that case, use in_:
|
||||
|
||||
>>> FoldedCase('hello').in_('Hello World')
|
||||
True
|
||||
|
||||
>>> FoldedCase('hello') > FoldedCase('Hello')
|
||||
False
|
||||
"""
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.lower() < other.lower()
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.lower() > other.lower()
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.lower() == other.lower()
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.lower() != other.lower()
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.lower())
|
||||
|
||||
def __contains__(self, other):
|
||||
return super().lower().__contains__(other.lower())
|
||||
|
||||
def in_(self, other):
|
||||
"Does self appear in other?"
|
||||
return self in FoldedCase(other)
|
||||
|
||||
# cache lower since it's likely to be called frequently.
|
||||
@method_cache
|
||||
def lower(self):
|
||||
return super().lower()
|
||||
|
||||
def index(self, sub):
|
||||
return self.lower().index(sub.lower())
|
||||
|
||||
def split(self, splitter=' ', maxsplit=0):
|
||||
pattern = re.compile(re.escape(splitter), re.I)
|
||||
return pattern.split(self, maxsplit)
|
0
lib/importlib_metadata/py.typed
Normal file
0
lib/importlib_metadata/py.typed
Normal file
|
@ -1,8 +1,8 @@
|
|||
# mako/__init__.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
|
||||
__version__ = "1.1.6"
|
||||
__version__ = "1.2.0"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/_ast_util.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -47,7 +47,6 @@ from _ast import Sub
|
|||
from _ast import UAdd
|
||||
from _ast import USub
|
||||
|
||||
from mako.compat import arg_stringname
|
||||
|
||||
BOOLOP_SYMBOLS = {And: "and", Or: "or"}
|
||||
|
||||
|
@ -94,9 +93,7 @@ def parse(expr, filename="<unknown>", mode="exec"):
|
|||
|
||||
def iter_fields(node):
|
||||
"""Iterate over all fields of a node, only yielding existing fields."""
|
||||
# CPython 2.5 compat
|
||||
if not hasattr(node, "_fields") or not node._fields:
|
||||
return
|
||||
|
||||
for field in node._fields:
|
||||
try:
|
||||
yield field, getattr(node, field)
|
||||
|
@ -104,7 +101,7 @@ def iter_fields(node):
|
|||
pass
|
||||
|
||||
|
||||
class NodeVisitor(object):
|
||||
class NodeVisitor:
|
||||
|
||||
"""
|
||||
Walks the abstract syntax tree and call visitor functions for every node
|
||||
|
@ -266,10 +263,10 @@ class SourceGenerator(NodeVisitor):
|
|||
self.visit(default)
|
||||
if node.vararg is not None:
|
||||
write_comma()
|
||||
self.write("*" + arg_stringname(node.vararg))
|
||||
self.write("*" + node.vararg.arg)
|
||||
if node.kwarg is not None:
|
||||
write_comma()
|
||||
self.write("**" + arg_stringname(node.kwarg))
|
||||
self.write("**" + node.kwarg.arg)
|
||||
|
||||
def decorators(self, node):
|
||||
for decorator in node.decorator_list:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/ast.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -9,12 +9,11 @@ code, as well as generating Python from AST nodes"""
|
|||
|
||||
import re
|
||||
|
||||
from mako import compat
|
||||
from mako import exceptions
|
||||
from mako import pyparser
|
||||
|
||||
|
||||
class PythonCode(object):
|
||||
class PythonCode:
|
||||
|
||||
"""represents information about a string containing Python code"""
|
||||
|
||||
|
@ -39,7 +38,7 @@ class PythonCode(object):
|
|||
# - AST is less likely to break with version changes
|
||||
# (for example, the behavior of co_names changed a little bit
|
||||
# in python version 2.5)
|
||||
if isinstance(code, compat.string_types):
|
||||
if isinstance(code, str):
|
||||
expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
|
||||
else:
|
||||
expr = code
|
||||
|
@ -48,7 +47,7 @@ class PythonCode(object):
|
|||
f.visit(expr)
|
||||
|
||||
|
||||
class ArgumentList(object):
|
||||
class ArgumentList:
|
||||
|
||||
"""parses a fragment of code as a comma-separated list of expressions"""
|
||||
|
||||
|
@ -57,7 +56,7 @@ class ArgumentList(object):
|
|||
self.args = []
|
||||
self.declared_identifiers = set()
|
||||
self.undeclared_identifiers = set()
|
||||
if isinstance(code, compat.string_types):
|
||||
if isinstance(code, str):
|
||||
if re.match(r"\S", code) and not re.match(r",\s*$", code):
|
||||
# if theres text and no trailing comma, insure its parsed
|
||||
# as a tuple by adding a trailing comma
|
||||
|
@ -88,7 +87,7 @@ class PythonFragment(PythonCode):
|
|||
if not m:
|
||||
raise exceptions.CompileException(
|
||||
"Fragment '%s' is not a partial control statement" % code,
|
||||
**exception_kwargs
|
||||
**exception_kwargs,
|
||||
)
|
||||
if m.group(3):
|
||||
code = code[: m.start(3)]
|
||||
|
@ -97,7 +96,7 @@ class PythonFragment(PythonCode):
|
|||
code = code + "pass"
|
||||
elif keyword == "try":
|
||||
code = code + "pass\nexcept:pass"
|
||||
elif keyword == "elif" or keyword == "else":
|
||||
elif keyword in ["elif", "else"]:
|
||||
code = "if False:pass\n" + code + "pass"
|
||||
elif keyword == "except":
|
||||
code = "try:pass\n" + code + "pass"
|
||||
|
@ -106,12 +105,12 @@ class PythonFragment(PythonCode):
|
|||
else:
|
||||
raise exceptions.CompileException(
|
||||
"Unsupported control keyword: '%s'" % keyword,
|
||||
**exception_kwargs
|
||||
**exception_kwargs,
|
||||
)
|
||||
super(PythonFragment, self).__init__(code, **exception_kwargs)
|
||||
super().__init__(code, **exception_kwargs)
|
||||
|
||||
|
||||
class FunctionDecl(object):
|
||||
class FunctionDecl:
|
||||
|
||||
"""function declaration"""
|
||||
|
||||
|
@ -124,13 +123,13 @@ class FunctionDecl(object):
|
|||
if not hasattr(self, "funcname"):
|
||||
raise exceptions.CompileException(
|
||||
"Code '%s' is not a function declaration" % code,
|
||||
**exception_kwargs
|
||||
**exception_kwargs,
|
||||
)
|
||||
if not allow_kwargs and self.kwargs:
|
||||
raise exceptions.CompileException(
|
||||
"'**%s' keyword argument not allowed here"
|
||||
% self.kwargnames[-1],
|
||||
**exception_kwargs
|
||||
**exception_kwargs,
|
||||
)
|
||||
|
||||
def get_argument_expressions(self, as_call=False):
|
||||
|
@ -200,6 +199,4 @@ class FunctionArgs(FunctionDecl):
|
|||
"""the argument portion of a function declaration"""
|
||||
|
||||
def __init__(self, code, **kwargs):
|
||||
super(FunctionArgs, self).__init__(
|
||||
"def ANON(%s):pass" % code, **kwargs
|
||||
)
|
||||
super().__init__("def ANON(%s):pass" % code, **kwargs)
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# mako/cache.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from mako import compat
|
||||
from mako import util
|
||||
|
||||
_cache_plugins = util.PluginLoader("mako.cache")
|
||||
|
@ -13,7 +12,7 @@ register_plugin = _cache_plugins.register
|
|||
register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl")
|
||||
|
||||
|
||||
class Cache(object):
|
||||
class Cache:
|
||||
|
||||
"""Represents a data content cache made available to the module
|
||||
space of a specific :class:`.Template` object.
|
||||
|
@ -66,7 +65,7 @@ class Cache(object):
|
|||
def __init__(self, template, *args):
|
||||
# check for a stale template calling the
|
||||
# constructor
|
||||
if isinstance(template, compat.string_types) and args:
|
||||
if isinstance(template, str) and args:
|
||||
return
|
||||
self.template = template
|
||||
self.id = template.module.__name__
|
||||
|
@ -181,7 +180,7 @@ class Cache(object):
|
|||
return tmpl_kw
|
||||
|
||||
|
||||
class CacheImpl(object):
|
||||
class CacheImpl:
|
||||
|
||||
"""Provide a cache implementation for use by :class:`.Cache`."""
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# mako/cmd.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
from argparse import ArgumentParser
|
||||
import io
|
||||
from os.path import dirname
|
||||
from os.path import isfile
|
||||
import sys
|
||||
|
@ -85,16 +84,14 @@ def cmdline(argv=None):
|
|||
except:
|
||||
_exit()
|
||||
|
||||
kw = dict([varsplit(var) for var in options.var])
|
||||
kw = dict(varsplit(var) for var in options.var)
|
||||
try:
|
||||
rendered = template.render(**kw)
|
||||
except:
|
||||
_exit()
|
||||
else:
|
||||
if output_file:
|
||||
io.open(output_file, "wt", encoding=output_encoding).write(
|
||||
rendered
|
||||
)
|
||||
open(output_file, "wt", encoding=output_encoding).write(rendered)
|
||||
else:
|
||||
sys.stdout.write(rendered)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/codegen.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -12,7 +12,6 @@ import re
|
|||
import time
|
||||
|
||||
from mako import ast
|
||||
from mako import compat
|
||||
from mako import exceptions
|
||||
from mako import filters
|
||||
from mako import parsetree
|
||||
|
@ -25,8 +24,8 @@ MAGIC_NUMBER = 10
|
|||
# names which are hardwired into the
|
||||
# template and are not accessed via the
|
||||
# context itself
|
||||
TOPLEVEL_DECLARED = set(["UNDEFINED", "STOP_RENDERING"])
|
||||
RESERVED_NAMES = set(["context", "loop"]).union(TOPLEVEL_DECLARED)
|
||||
TOPLEVEL_DECLARED = {"UNDEFINED", "STOP_RENDERING"}
|
||||
RESERVED_NAMES = {"context", "loop"}.union(TOPLEVEL_DECLARED)
|
||||
|
||||
|
||||
def compile( # noqa
|
||||
|
@ -39,20 +38,12 @@ def compile( # noqa
|
|||
future_imports=None,
|
||||
source_encoding=None,
|
||||
generate_magic_comment=True,
|
||||
disable_unicode=False,
|
||||
strict_undefined=False,
|
||||
enable_loop=True,
|
||||
reserved_names=frozenset(),
|
||||
):
|
||||
"""Generate module source code given a parsetree node,
|
||||
uri, and optional source filename"""
|
||||
|
||||
# if on Py2K, push the "source_encoding" string to be
|
||||
# a bytestring itself, as we will be embedding it into
|
||||
# the generated source and we don't want to coerce the
|
||||
# result into a unicode object, in "disable_unicode" mode
|
||||
if not compat.py3k and isinstance(source_encoding, compat.text_type):
|
||||
source_encoding = source_encoding.encode(source_encoding)
|
||||
uri, and optional source filename"""
|
||||
|
||||
buf = util.FastEncodingBuffer()
|
||||
|
||||
|
@ -68,7 +59,6 @@ def compile( # noqa
|
|||
future_imports,
|
||||
source_encoding,
|
||||
generate_magic_comment,
|
||||
disable_unicode,
|
||||
strict_undefined,
|
||||
enable_loop,
|
||||
reserved_names,
|
||||
|
@ -78,7 +68,7 @@ def compile( # noqa
|
|||
return buf.getvalue()
|
||||
|
||||
|
||||
class _CompileContext(object):
|
||||
class _CompileContext:
|
||||
def __init__(
|
||||
self,
|
||||
uri,
|
||||
|
@ -89,7 +79,6 @@ class _CompileContext(object):
|
|||
future_imports,
|
||||
source_encoding,
|
||||
generate_magic_comment,
|
||||
disable_unicode,
|
||||
strict_undefined,
|
||||
enable_loop,
|
||||
reserved_names,
|
||||
|
@ -102,16 +91,15 @@ class _CompileContext(object):
|
|||
self.future_imports = future_imports
|
||||
self.source_encoding = source_encoding
|
||||
self.generate_magic_comment = generate_magic_comment
|
||||
self.disable_unicode = disable_unicode
|
||||
self.strict_undefined = strict_undefined
|
||||
self.enable_loop = enable_loop
|
||||
self.reserved_names = reserved_names
|
||||
|
||||
|
||||
class _GenerateRenderMethod(object):
|
||||
class _GenerateRenderMethod:
|
||||
|
||||
"""A template visitor object which generates the
|
||||
full module source for a template.
|
||||
full module source for a template.
|
||||
|
||||
"""
|
||||
|
||||
|
@ -196,7 +184,7 @@ class _GenerateRenderMethod(object):
|
|||
|
||||
self.compiler.pagetag = None
|
||||
|
||||
class FindTopLevel(object):
|
||||
class FindTopLevel:
|
||||
def visitInheritTag(s, node):
|
||||
inherit.append(node)
|
||||
|
||||
|
@ -392,7 +380,7 @@ class _GenerateRenderMethod(object):
|
|||
identifiers = self.compiler.identifiers.branch(node)
|
||||
self.in_def = True
|
||||
|
||||
class NSDefVisitor(object):
|
||||
class NSDefVisitor:
|
||||
def visitDefTag(s, node):
|
||||
s.visitDefOrBase(node)
|
||||
|
||||
|
@ -404,7 +392,7 @@ class _GenerateRenderMethod(object):
|
|||
raise exceptions.CompileException(
|
||||
"Can't put anonymous blocks inside "
|
||||
"<%namespace>",
|
||||
**node.exception_kwargs
|
||||
**node.exception_kwargs,
|
||||
)
|
||||
self.write_inline_def(node, identifiers, nested=False)
|
||||
export.append(node.funcname)
|
||||
|
@ -481,7 +469,7 @@ class _GenerateRenderMethod(object):
|
|||
"""
|
||||
|
||||
# collection of all defs available to us in this scope
|
||||
comp_idents = dict([(c.funcname, c) for c in identifiers.defs])
|
||||
comp_idents = {c.funcname: c for c in identifiers.defs}
|
||||
to_write = set()
|
||||
|
||||
# write "context.get()" for all variables we are going to
|
||||
|
@ -714,7 +702,7 @@ class _GenerateRenderMethod(object):
|
|||
toplevel=False,
|
||||
):
|
||||
"""write a post-function decorator to replace a rendering
|
||||
callable with a cached version of itself."""
|
||||
callable with a cached version of itself."""
|
||||
|
||||
self.printer.writeline("__M_%s = %s" % (name, name))
|
||||
cachekey = node_or_pagetag.parsed_attributes.get(
|
||||
|
@ -794,8 +782,6 @@ class _GenerateRenderMethod(object):
|
|||
def locate_encode(name):
|
||||
if re.match(r"decode\..+", name):
|
||||
return "filters." + name
|
||||
elif self.compiler.disable_unicode:
|
||||
return filters.NON_UNICODE_ESCAPES.get(name, name)
|
||||
else:
|
||||
return filters.DEFAULT_ESCAPES.get(name, name)
|
||||
|
||||
|
@ -859,11 +845,11 @@ class _GenerateRenderMethod(object):
|
|||
# and end control lines, and
|
||||
# 3) any control line with no content other than comments
|
||||
if not children or (
|
||||
compat.all(
|
||||
all(
|
||||
isinstance(c, (parsetree.Comment, parsetree.ControlLine))
|
||||
for c in children
|
||||
)
|
||||
and compat.all(
|
||||
and all(
|
||||
(node.is_ternary(c.keyword) or c.isend)
|
||||
for c in children
|
||||
if isinstance(c, parsetree.ControlLine)
|
||||
|
@ -969,7 +955,7 @@ class _GenerateRenderMethod(object):
|
|||
|
||||
self.identifier_stack.append(body_identifiers)
|
||||
|
||||
class DefVisitor(object):
|
||||
class DefVisitor:
|
||||
def visitDefTag(s, node):
|
||||
s.visitDefOrBase(node)
|
||||
|
||||
|
@ -1025,7 +1011,7 @@ class _GenerateRenderMethod(object):
|
|||
)
|
||||
|
||||
|
||||
class _Identifiers(object):
|
||||
class _Identifiers:
|
||||
|
||||
"""tracks the status of identifier names as template code is rendered."""
|
||||
|
||||
|
@ -1098,7 +1084,7 @@ class _Identifiers(object):
|
|||
|
||||
def branch(self, node, **kwargs):
|
||||
"""create a new Identifiers for a new Node, with
|
||||
this Identifiers as the parent."""
|
||||
this Identifiers as the parent."""
|
||||
|
||||
return _Identifiers(self.compiler, node, self, **kwargs)
|
||||
|
||||
|
@ -1123,7 +1109,7 @@ class _Identifiers(object):
|
|||
|
||||
def check_declared(self, node):
|
||||
"""update the state of this Identifiers with the undeclared
|
||||
and declared identifiers of the given node."""
|
||||
and declared identifiers of the given node."""
|
||||
|
||||
for ident in node.undeclared_identifiers():
|
||||
if ident != "context" and ident not in self.declared.union(
|
||||
|
@ -1170,7 +1156,7 @@ class _Identifiers(object):
|
|||
raise exceptions.CompileException(
|
||||
"%%def or %%block named '%s' already "
|
||||
"exists in this template." % node.funcname,
|
||||
**node.exception_kwargs
|
||||
**node.exception_kwargs,
|
||||
)
|
||||
|
||||
def visitDefTag(self, node):
|
||||
|
@ -1200,7 +1186,7 @@ class _Identifiers(object):
|
|||
raise exceptions.CompileException(
|
||||
"Named block '%s' not allowed inside of def '%s'"
|
||||
% (node.name, self.node.name),
|
||||
**node.exception_kwargs
|
||||
**node.exception_kwargs,
|
||||
)
|
||||
elif isinstance(
|
||||
self.node, (parsetree.CallTag, parsetree.CallNamespaceTag)
|
||||
|
@ -1208,7 +1194,7 @@ class _Identifiers(object):
|
|||
raise exceptions.CompileException(
|
||||
"Named block '%s' not allowed inside of <%%call> tag"
|
||||
% (node.name,),
|
||||
**node.exception_kwargs
|
||||
**node.exception_kwargs,
|
||||
)
|
||||
|
||||
for ident in node.undeclared_identifiers():
|
||||
|
@ -1293,7 +1279,7 @@ def mangle_mako_loop(node, printer):
|
|||
return text
|
||||
|
||||
|
||||
class LoopVariable(object):
|
||||
class LoopVariable:
|
||||
|
||||
"""A node visitor which looks for the name 'loop' within undeclared
|
||||
identifiers."""
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
# mako/compat.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
import collections
|
||||
from importlib import util
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
py3k = sys.version_info >= (3, 0)
|
||||
py2k = sys.version_info < (3,)
|
||||
py27 = sys.version_info >= (2, 7)
|
||||
jython = sys.platform.startswith("java")
|
||||
win32 = sys.platform.startswith("win")
|
||||
pypy = hasattr(sys, "pypy_version_info")
|
||||
py38 = sys.version_info >= (3, 8)
|
||||
|
||||
ArgSpec = collections.namedtuple(
|
||||
"ArgSpec", ["args", "varargs", "keywords", "defaults"]
|
||||
|
@ -26,15 +24,15 @@ def inspect_getargspec(func):
|
|||
if inspect.ismethod(func):
|
||||
func = func.__func__
|
||||
if not inspect.isfunction(func):
|
||||
raise TypeError("{!r} is not a Python function".format(func))
|
||||
raise TypeError(f"{func!r} is not a Python function")
|
||||
|
||||
co = func.__code__
|
||||
if not inspect.iscode(co):
|
||||
raise TypeError("{!r} is not a code object".format(co))
|
||||
raise TypeError(f"{co!r} is not a code object")
|
||||
|
||||
nargs = co.co_argcount
|
||||
names = co.co_varnames
|
||||
nkwargs = co.co_kwonlyargcount if py3k else 0
|
||||
nkwargs = co.co_kwonlyargcount
|
||||
args = list(names[:nargs])
|
||||
|
||||
nargs += nkwargs
|
||||
|
@ -49,129 +47,30 @@ def inspect_getargspec(func):
|
|||
return ArgSpec(args, varargs, varkw, func.__defaults__)
|
||||
|
||||
|
||||
if py3k:
|
||||
from io import StringIO
|
||||
import builtins as compat_builtins
|
||||
from urllib.parse import quote_plus, unquote_plus
|
||||
from html.entities import codepoint2name, name2codepoint
|
||||
|
||||
string_types = (str,)
|
||||
binary_type = bytes
|
||||
text_type = str
|
||||
|
||||
from io import BytesIO as byte_buffer
|
||||
|
||||
def u(s):
|
||||
return s
|
||||
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
|
||||
def octal(lit):
|
||||
return eval("0o" + lit)
|
||||
|
||||
|
||||
else:
|
||||
import __builtin__ as compat_builtins # noqa
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except:
|
||||
from StringIO import StringIO
|
||||
|
||||
byte_buffer = StringIO
|
||||
|
||||
from urllib import quote_plus, unquote_plus # noqa
|
||||
from htmlentitydefs import codepoint2name, name2codepoint # noqa
|
||||
|
||||
string_types = (basestring,) # noqa
|
||||
binary_type = str
|
||||
text_type = unicode # noqa
|
||||
|
||||
def u(s):
|
||||
return unicode(s, "utf-8") # noqa
|
||||
|
||||
def b(s):
|
||||
return s
|
||||
|
||||
def octal(lit):
|
||||
return eval("0" + lit)
|
||||
|
||||
|
||||
if py3k:
|
||||
from importlib import machinery, util
|
||||
|
||||
if hasattr(util, 'module_from_spec'):
|
||||
# Python 3.5+
|
||||
def load_module(module_id, path):
|
||||
spec = util.spec_from_file_location(module_id, path)
|
||||
module = util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
else:
|
||||
def load_module(module_id, path):
|
||||
module = machinery.SourceFileLoader(module_id, path).load_module()
|
||||
del sys.modules[module_id]
|
||||
return module
|
||||
|
||||
else:
|
||||
import imp
|
||||
|
||||
def load_module(module_id, path):
|
||||
fp = open(path, "rb")
|
||||
try:
|
||||
module = imp.load_source(module_id, path, fp)
|
||||
del sys.modules[module_id]
|
||||
return module
|
||||
finally:
|
||||
fp.close()
|
||||
|
||||
|
||||
if py3k:
|
||||
|
||||
def reraise(tp, value, tb=None, cause=None):
|
||||
if cause is not None:
|
||||
value.__cause__ = cause
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
|
||||
else:
|
||||
exec(
|
||||
"def reraise(tp, value, tb=None, cause=None):\n"
|
||||
" raise tp, value, tb\n"
|
||||
)
|
||||
def load_module(module_id, path):
|
||||
spec = util.spec_from_file_location(module_id, path)
|
||||
module = util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def exception_as():
|
||||
return sys.exc_info()[1]
|
||||
|
||||
|
||||
all = all # noqa
|
||||
|
||||
|
||||
def exception_name(exc):
|
||||
return exc.__class__.__name__
|
||||
|
||||
|
||||
################################################
|
||||
# cross-compatible metaclass implementation
|
||||
# Copyright (c) 2010-2012 Benjamin Peterson
|
||||
def with_metaclass(meta, base=object):
|
||||
"""Create a base class with a metaclass."""
|
||||
return meta("%sBase" % meta.__name__, (base,), {})
|
||||
if py38:
|
||||
from importlib import metadata as importlib_metadata
|
||||
else:
|
||||
import importlib_metadata # noqa
|
||||
|
||||
|
||||
################################################
|
||||
|
||||
|
||||
def arg_stringname(func_arg):
|
||||
"""Gets the string name of a kwarg or vararg
|
||||
In Python3.4 a function's args are
|
||||
of _ast.arg type not _ast.name
|
||||
"""
|
||||
if hasattr(func_arg, "arg"):
|
||||
return func_arg.arg
|
||||
def importlib_metadata_get(group):
|
||||
ep = importlib_metadata.entry_points()
|
||||
if hasattr(ep, "select"):
|
||||
return ep.select(group=group)
|
||||
else:
|
||||
return str(func_arg)
|
||||
return ep.get(group, ())
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/exceptions.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -68,7 +68,7 @@ class TopLevelLookupException(TemplateLookupException):
|
|||
pass
|
||||
|
||||
|
||||
class RichTraceback(object):
|
||||
class RichTraceback:
|
||||
|
||||
"""Pull the current exception from the ``sys`` traceback and extracts
|
||||
Mako-specific template information.
|
||||
|
@ -106,7 +106,7 @@ class RichTraceback(object):
|
|||
def _init_message(self):
|
||||
"""Find a unicode representation of self.error"""
|
||||
try:
|
||||
self.message = compat.text_type(self.error)
|
||||
self.message = str(self.error)
|
||||
except UnicodeError:
|
||||
try:
|
||||
self.message = str(self.error)
|
||||
|
@ -114,8 +114,8 @@ class RichTraceback(object):
|
|||
# Fallback to args as neither unicode nor
|
||||
# str(Exception(u'\xe6')) work in Python < 2.6
|
||||
self.message = self.error.args[0]
|
||||
if not isinstance(self.message, compat.text_type):
|
||||
self.message = compat.text_type(self.message, "ascii", "replace")
|
||||
if not isinstance(self.message, str):
|
||||
self.message = str(self.message, "ascii", "replace")
|
||||
|
||||
def _get_reformatted_records(self, records):
|
||||
for rec in records:
|
||||
|
@ -139,8 +139,7 @@ class RichTraceback(object):
|
|||
|
||||
@property
|
||||
def reverse_traceback(self):
|
||||
"""Return the same data as traceback, except in reverse order.
|
||||
"""
|
||||
"""Return the same data as traceback, except in reverse order."""
|
||||
|
||||
return list(self._get_reformatted_records(self.reverse_records))
|
||||
|
||||
|
@ -170,17 +169,6 @@ class RichTraceback(object):
|
|||
)
|
||||
except KeyError:
|
||||
# A normal .py file (not a Template)
|
||||
if not compat.py3k:
|
||||
try:
|
||||
fp = open(filename, "rb")
|
||||
encoding = util.parse_encoding(fp)
|
||||
fp.close()
|
||||
except IOError:
|
||||
encoding = None
|
||||
if encoding:
|
||||
line = line.decode(encoding)
|
||||
else:
|
||||
line = line.decode("ascii", "replace")
|
||||
new_trcback.append(
|
||||
(
|
||||
filename,
|
||||
|
@ -236,13 +224,12 @@ class RichTraceback(object):
|
|||
if new_trcback:
|
||||
try:
|
||||
# A normal .py file (not a Template)
|
||||
fp = open(new_trcback[-1][0], "rb")
|
||||
encoding = util.parse_encoding(fp)
|
||||
if compat.py3k and not encoding:
|
||||
encoding = "utf-8"
|
||||
fp.seek(0)
|
||||
self.source = fp.read()
|
||||
fp.close()
|
||||
with open(new_trcback[-1][0], "rb") as fp:
|
||||
encoding = util.parse_encoding(fp)
|
||||
if not encoding:
|
||||
encoding = "utf-8"
|
||||
fp.seek(0)
|
||||
self.source = fp.read()
|
||||
if encoding:
|
||||
self.source = self.source.decode(encoding)
|
||||
except IOError:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# ext/autohandler.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# ext/babelplugin.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""gettext message extraction via Babel: http://babel.edgewall.org/"""
|
||||
"""gettext message extraction via Babel: https://pypi.org/project/Babel/"""
|
||||
from babel.messages.extract import extract_python
|
||||
|
||||
from mako.ext.extract import MessageExtractor
|
||||
|
@ -15,12 +15,12 @@ class BabelMakoExtractor(MessageExtractor):
|
|||
self.keywords = keywords
|
||||
self.options = options
|
||||
self.config = {
|
||||
"comment-tags": u" ".join(comment_tags),
|
||||
"comment-tags": " ".join(comment_tags),
|
||||
"encoding": options.get(
|
||||
"input_encoding", options.get("encoding", None)
|
||||
),
|
||||
}
|
||||
super(BabelMakoExtractor, self).__init__()
|
||||
super().__init__()
|
||||
|
||||
def __call__(self, fileobj):
|
||||
return self.process_file(fileobj)
|
||||
|
@ -54,5 +54,4 @@ def extract(fileobj, keywords, comment_tags, options):
|
|||
:rtype: ``iterator``
|
||||
"""
|
||||
extractor = BabelMakoExtractor(keywords, comment_tags, options)
|
||||
for message in extractor(fileobj):
|
||||
yield message
|
||||
yield from extractor(fileobj)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# ext/beaker_cache.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -40,7 +40,7 @@ class BeakerCacheImpl(CacheImpl):
|
|||
_beaker_cache = cache.template.cache_args["manager"]
|
||||
else:
|
||||
_beaker_cache = beaker_cache.CacheManager()
|
||||
super(BeakerCacheImpl, self).__init__(cache)
|
||||
super().__init__(cache)
|
||||
|
||||
def _get_cache(self, **kw):
|
||||
expiretime = kw.pop("timeout", None)
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
# ext/extract.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from io import BytesIO
|
||||
from io import StringIO
|
||||
import re
|
||||
|
||||
from mako import compat
|
||||
from mako import lexer
|
||||
from mako import parsetree
|
||||
|
||||
|
||||
class MessageExtractor(object):
|
||||
class MessageExtractor:
|
||||
use_bytes = True
|
||||
|
||||
def process_file(self, fileobj):
|
||||
template_node = lexer.Lexer(
|
||||
fileobj.read(), input_encoding=self.config["encoding"]
|
||||
).parse()
|
||||
for extracted in self.extract_nodes(template_node.get_children()):
|
||||
yield extracted
|
||||
yield from self.extract_nodes(template_node.get_children())
|
||||
|
||||
def extract_nodes(self, nodes):
|
||||
translator_comments = []
|
||||
|
@ -90,7 +92,7 @@ class MessageExtractor(object):
|
|||
comment[1] for comment in translator_comments
|
||||
]
|
||||
|
||||
if isinstance(code, compat.text_type):
|
||||
if isinstance(code, str) and self.use_bytes:
|
||||
code = code.encode(input_encoding, "backslashreplace")
|
||||
|
||||
used_translator_comments = False
|
||||
|
@ -99,7 +101,10 @@ class MessageExtractor(object):
|
|||
# input string of the input is non-ascii)
|
||||
# Also, because we added it, we have to subtract one from
|
||||
# node.lineno
|
||||
code = compat.byte_buffer(compat.b("\n") + code)
|
||||
if self.use_bytes:
|
||||
code = BytesIO(b"\n" + code)
|
||||
else:
|
||||
code = StringIO("\n" + code)
|
||||
|
||||
for message in self.process_python(
|
||||
code, node.lineno - 1, translator_strings
|
||||
|
@ -112,8 +117,7 @@ class MessageExtractor(object):
|
|||
in_translator_comments = False
|
||||
|
||||
if child_nodes:
|
||||
for extracted in self.extract_nodes(child_nodes):
|
||||
yield extracted
|
||||
yield from self.extract_nodes(child_nodes)
|
||||
|
||||
@staticmethod
|
||||
def _split_comment(lineno, comment):
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
# ext/linguaplugin.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
import contextlib
|
||||
import io
|
||||
|
||||
from lingua.extractors import Extractor
|
||||
from lingua.extractors import get_extractor
|
||||
from lingua.extractors import Message
|
||||
|
||||
from mako import compat
|
||||
from mako.ext.extract import MessageExtractor
|
||||
|
||||
|
||||
class LinguaMakoExtractor(Extractor, MessageExtractor):
|
||||
|
||||
"""Mako templates"""
|
||||
|
||||
use_bytes = False
|
||||
extensions = [".mako"]
|
||||
default_config = {"encoding": "utf-8", "comment-tags": ""}
|
||||
|
||||
|
@ -26,29 +26,21 @@ class LinguaMakoExtractor(Extractor, MessageExtractor):
|
|||
self.filename = filename
|
||||
self.python_extractor = get_extractor("x.py")
|
||||
if fileobj is None:
|
||||
fileobj = open(filename, "rb")
|
||||
must_close = True
|
||||
ctx = open(filename, "r")
|
||||
else:
|
||||
must_close = False
|
||||
try:
|
||||
for message in self.process_file(fileobj):
|
||||
yield message
|
||||
finally:
|
||||
if must_close:
|
||||
fileobj.close()
|
||||
ctx = contextlib.nullcontext(fileobj)
|
||||
with ctx as file_:
|
||||
yield from self.process_file(file_)
|
||||
|
||||
def process_python(self, code, code_lineno, translator_strings):
|
||||
source = code.getvalue().strip()
|
||||
if source.endswith(compat.b(":")):
|
||||
if source in (
|
||||
compat.b("try:"),
|
||||
compat.b("else:"),
|
||||
) or source.startswith(compat.b("except")):
|
||||
source = compat.b("") # Ignore try/except and else
|
||||
elif source.startswith(compat.b("elif")):
|
||||
if source.endswith(":"):
|
||||
if source in ("try:", "else:") or source.startswith("except"):
|
||||
source = "" # Ignore try/except and else
|
||||
elif source.startswith("elif"):
|
||||
source = source[2:] # Replace "elif" with "if"
|
||||
source += compat.b("pass")
|
||||
code = io.BytesIO(source)
|
||||
source += "pass"
|
||||
code = io.StringIO(source)
|
||||
for msg in self.python_extractor(
|
||||
self.filename, self.options, code, code_lineno - 1
|
||||
):
|
||||
|
@ -58,7 +50,7 @@ class LinguaMakoExtractor(Extractor, MessageExtractor):
|
|||
msg.msgid,
|
||||
msg.msgid_plural,
|
||||
msg.flags,
|
||||
compat.u(" ").join(translator_strings + [msg.comment]),
|
||||
" ".join(translator_strings + [msg.comment]),
|
||||
msg.tcomment,
|
||||
msg.location,
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# ext/preprocessors.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# ext/pygmentplugin.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -25,8 +25,6 @@ from pygments.token import Other
|
|||
from pygments.token import String
|
||||
from pygments.token import Text
|
||||
|
||||
from mako import compat
|
||||
|
||||
|
||||
class MakoLexer(RegexLexer):
|
||||
name = "Mako"
|
||||
|
@ -108,7 +106,7 @@ class MakoHtmlLexer(DelegatingLexer):
|
|||
aliases = ["html+mako"]
|
||||
|
||||
def __init__(self, **options):
|
||||
super(MakoHtmlLexer, self).__init__(HtmlLexer, MakoLexer, **options)
|
||||
super().__init__(HtmlLexer, MakoLexer, **options)
|
||||
|
||||
|
||||
class MakoXmlLexer(DelegatingLexer):
|
||||
|
@ -116,7 +114,7 @@ class MakoXmlLexer(DelegatingLexer):
|
|||
aliases = ["xml+mako"]
|
||||
|
||||
def __init__(self, **options):
|
||||
super(MakoXmlLexer, self).__init__(XmlLexer, MakoLexer, **options)
|
||||
super().__init__(XmlLexer, MakoLexer, **options)
|
||||
|
||||
|
||||
class MakoJavascriptLexer(DelegatingLexer):
|
||||
|
@ -124,9 +122,7 @@ class MakoJavascriptLexer(DelegatingLexer):
|
|||
aliases = ["js+mako", "javascript+mako"]
|
||||
|
||||
def __init__(self, **options):
|
||||
super(MakoJavascriptLexer, self).__init__(
|
||||
JavascriptLexer, MakoLexer, **options
|
||||
)
|
||||
super().__init__(JavascriptLexer, MakoLexer, **options)
|
||||
|
||||
|
||||
class MakoCssLexer(DelegatingLexer):
|
||||
|
@ -134,7 +130,7 @@ class MakoCssLexer(DelegatingLexer):
|
|||
aliases = ["css+mako"]
|
||||
|
||||
def __init__(self, **options):
|
||||
super(MakoCssLexer, self).__init__(CssLexer, MakoLexer, **options)
|
||||
super().__init__(CssLexer, MakoLexer, **options)
|
||||
|
||||
|
||||
pygments_html_formatter = HtmlFormatter(
|
||||
|
@ -144,10 +140,7 @@ pygments_html_formatter = HtmlFormatter(
|
|||
|
||||
def syntax_highlight(filename="", language=None):
|
||||
mako_lexer = MakoLexer()
|
||||
if compat.py3k:
|
||||
python_lexer = Python3Lexer()
|
||||
else:
|
||||
python_lexer = PythonLexer()
|
||||
python_lexer = Python3Lexer()
|
||||
if filename.startswith("memory:") or language == "mako":
|
||||
return lambda string: highlight(
|
||||
string, mako_lexer, pygments_html_formatter
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# ext/turbogears.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -9,7 +9,7 @@ from mako.lookup import TemplateLookup
|
|||
from mako.template import Template
|
||||
|
||||
|
||||
class TGPlugin(object):
|
||||
class TGPlugin:
|
||||
|
||||
"""TurboGears compatible Template Plugin."""
|
||||
|
||||
|
@ -51,7 +51,7 @@ class TGPlugin(object):
|
|||
def render(
|
||||
self, info, format="html", fragment=False, template=None # noqa
|
||||
):
|
||||
if isinstance(template, compat.string_types):
|
||||
if isinstance(template, str):
|
||||
template = self.load_template(template)
|
||||
|
||||
# Load extra vars func if provided
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
# mako/filters.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
|
||||
import codecs
|
||||
from html.entities import codepoint2name
|
||||
from html.entities import name2codepoint
|
||||
import re
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from mako import compat
|
||||
from mako.compat import codepoint2name
|
||||
from mako.compat import name2codepoint
|
||||
from mako.compat import quote_plus
|
||||
from mako.compat import unquote_plus
|
||||
import markupsafe
|
||||
|
||||
html_escape = markupsafe.escape
|
||||
|
||||
xml_escapes = {
|
||||
"&": "&",
|
||||
|
@ -22,27 +23,6 @@ xml_escapes = {
|
|||
"'": "'", # also ' in html-only
|
||||
}
|
||||
|
||||
# XXX: " is valid in HTML and XML
|
||||
# ' is not valid HTML, but is valid XML
|
||||
|
||||
|
||||
def legacy_html_escape(s):
|
||||
"""legacy HTML escape for non-unicode mode."""
|
||||
s = s.replace("&", "&")
|
||||
s = s.replace(">", ">")
|
||||
s = s.replace("<", "<")
|
||||
s = s.replace('"', """)
|
||||
s = s.replace("'", "'")
|
||||
return s
|
||||
|
||||
|
||||
try:
|
||||
import markupsafe
|
||||
|
||||
html_escape = markupsafe.escape
|
||||
except ImportError:
|
||||
html_escape = legacy_html_escape
|
||||
|
||||
|
||||
def xml_escape(string):
|
||||
return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string)
|
||||
|
@ -54,31 +34,19 @@ def url_escape(string):
|
|||
return quote_plus(string)
|
||||
|
||||
|
||||
def legacy_url_escape(string):
|
||||
# convert into a list of octets
|
||||
return quote_plus(string)
|
||||
|
||||
|
||||
def url_unescape(string):
|
||||
text = unquote_plus(string)
|
||||
if not is_ascii_str(text):
|
||||
text = text.decode("utf8")
|
||||
return text
|
||||
|
||||
|
||||
def trim(string):
|
||||
return string.strip()
|
||||
|
||||
|
||||
class Decode(object):
|
||||
class Decode:
|
||||
def __getattr__(self, key):
|
||||
def decode(x):
|
||||
if isinstance(x, compat.text_type):
|
||||
if isinstance(x, str):
|
||||
return x
|
||||
elif not isinstance(x, compat.binary_type):
|
||||
elif not isinstance(x, bytes):
|
||||
return decode(str(x))
|
||||
else:
|
||||
return compat.text_type(x, encoding=key)
|
||||
return str(x, encoding=key)
|
||||
|
||||
return decode
|
||||
|
||||
|
@ -86,24 +54,11 @@ class Decode(object):
|
|||
decode = Decode()
|
||||
|
||||
|
||||
_ASCII_re = re.compile(r"\A[\x00-\x7f]*\Z")
|
||||
|
||||
|
||||
def is_ascii_str(text):
|
||||
return isinstance(text, str) and _ASCII_re.match(text)
|
||||
|
||||
|
||||
################################################################
|
||||
|
||||
|
||||
class XMLEntityEscaper(object):
|
||||
class XMLEntityEscaper:
|
||||
def __init__(self, codepoint2name, name2codepoint):
|
||||
self.codepoint2entity = dict(
|
||||
[
|
||||
(c, compat.text_type("&%s;" % n))
|
||||
for c, n in codepoint2name.items()
|
||||
]
|
||||
)
|
||||
self.codepoint2entity = {
|
||||
c: str("&%s;" % n) for c, n in codepoint2name.items()
|
||||
}
|
||||
self.name2codepoint = name2codepoint
|
||||
|
||||
def escape_entities(self, text):
|
||||
|
@ -111,7 +66,7 @@ class XMLEntityEscaper(object):
|
|||
|
||||
Only characters corresponding to a named entity are replaced.
|
||||
"""
|
||||
return compat.text_type(text).translate(self.codepoint2entity)
|
||||
return str(text).translate(self.codepoint2entity)
|
||||
|
||||
def __escape(self, m):
|
||||
codepoint = ord(m.group())
|
||||
|
@ -131,9 +86,7 @@ class XMLEntityEscaper(object):
|
|||
|
||||
The return value is guaranteed to be ASCII.
|
||||
"""
|
||||
return self.__escapable.sub(
|
||||
self.__escape, compat.text_type(text)
|
||||
).encode("ascii")
|
||||
return self.__escapable.sub(self.__escape, str(text)).encode("ascii")
|
||||
|
||||
# XXX: This regexp will not match all valid XML entity names__.
|
||||
# (It punts on details involving involving CombiningChars and Extenders.)
|
||||
|
@ -183,37 +136,28 @@ def htmlentityreplace_errors(ex):
|
|||
characters with HTML entities, or, if no HTML entity exists for
|
||||
the character, XML character references::
|
||||
|
||||
>>> u'The cost was \u20ac12.'.encode('latin1', 'htmlentityreplace')
|
||||
>>> 'The cost was \u20ac12.'.encode('latin1', 'htmlentityreplace')
|
||||
'The cost was €12.'
|
||||
"""
|
||||
if isinstance(ex, UnicodeEncodeError):
|
||||
# Handle encoding errors
|
||||
bad_text = ex.object[ex.start : ex.end]
|
||||
text = _html_entities_escaper.escape(bad_text)
|
||||
return (compat.text_type(text), ex.end)
|
||||
return (str(text), ex.end)
|
||||
raise ex
|
||||
|
||||
|
||||
codecs.register_error("htmlentityreplace", htmlentityreplace_errors)
|
||||
|
||||
|
||||
# TODO: options to make this dynamic per-compilation will be added in a later
|
||||
# release
|
||||
DEFAULT_ESCAPES = {
|
||||
"x": "filters.xml_escape",
|
||||
"h": "filters.html_escape",
|
||||
"u": "filters.url_escape",
|
||||
"trim": "filters.trim",
|
||||
"entity": "filters.html_entities_escape",
|
||||
"unicode": "unicode",
|
||||
"unicode": "str",
|
||||
"decode": "decode",
|
||||
"str": "str",
|
||||
"n": "n",
|
||||
}
|
||||
|
||||
if compat.py3k:
|
||||
DEFAULT_ESCAPES.update({"unicode": "str"})
|
||||
|
||||
NON_UNICODE_ESCAPES = DEFAULT_ESCAPES.copy()
|
||||
NON_UNICODE_ESCAPES["h"] = "filters.legacy_html_escape"
|
||||
NON_UNICODE_ESCAPES["u"] = "filters.legacy_url_escape"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/lexer.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -9,7 +9,6 @@
|
|||
import codecs
|
||||
import re
|
||||
|
||||
from mako import compat
|
||||
from mako import exceptions
|
||||
from mako import parsetree
|
||||
from mako.pygen import adjust_whitespace
|
||||
|
@ -17,14 +16,9 @@ from mako.pygen import adjust_whitespace
|
|||
_regexp_cache = {}
|
||||
|
||||
|
||||
class Lexer(object):
|
||||
class Lexer:
|
||||
def __init__(
|
||||
self,
|
||||
text,
|
||||
filename=None,
|
||||
disable_unicode=False,
|
||||
input_encoding=None,
|
||||
preprocessor=None,
|
||||
self, text, filename=None, input_encoding=None, preprocessor=None
|
||||
):
|
||||
self.text = text
|
||||
self.filename = filename
|
||||
|
@ -36,14 +30,8 @@ class Lexer(object):
|
|||
self.tag = []
|
||||
self.control_line = []
|
||||
self.ternary_stack = []
|
||||
self.disable_unicode = disable_unicode
|
||||
self.encoding = input_encoding
|
||||
|
||||
if compat.py3k and disable_unicode:
|
||||
raise exceptions.UnsupportedError(
|
||||
"Mako for Python 3 does not " "support disabling Unicode"
|
||||
)
|
||||
|
||||
if preprocessor is None:
|
||||
self.preprocessor = []
|
||||
elif not hasattr(preprocessor, "__iter__"):
|
||||
|
@ -66,10 +54,7 @@ class Lexer(object):
|
|||
try:
|
||||
reg = _regexp_cache[(regexp, flags)]
|
||||
except KeyError:
|
||||
if flags:
|
||||
reg = re.compile(regexp, flags)
|
||||
else:
|
||||
reg = re.compile(regexp)
|
||||
reg = re.compile(regexp, flags) if flags else re.compile(regexp)
|
||||
_regexp_cache[(regexp, flags)] = reg
|
||||
|
||||
return self.match_reg(reg)
|
||||
|
@ -87,10 +72,7 @@ class Lexer(object):
|
|||
match = reg.match(self.text, self.match_position)
|
||||
if match:
|
||||
(start, end) = match.span()
|
||||
if end == start:
|
||||
self.match_position = end + 1
|
||||
else:
|
||||
self.match_position = end
|
||||
self.match_position = end + 1 if end == start else end
|
||||
self.matched_lineno = self.lineno
|
||||
lines = re.findall(r"\n", self.text[mp : self.match_position])
|
||||
cp = mp - 1
|
||||
|
@ -98,10 +80,6 @@ class Lexer(object):
|
|||
cp -= 1
|
||||
self.matched_charpos = mp - cp
|
||||
self.lineno += len(lines)
|
||||
# print "MATCHED:", match.group(0), "LINE START:",
|
||||
# self.matched_lineno, "LINE END:", self.lineno
|
||||
# print "MATCH:", regexp, "\n", self.text[mp : mp + 15], \
|
||||
# (match and "TRUE" or "FALSE")
|
||||
return match
|
||||
|
||||
def parse_until_text(self, watch_nesting, *text):
|
||||
|
@ -161,12 +139,15 @@ class Lexer(object):
|
|||
if self.control_line:
|
||||
control_frame = self.control_line[-1]
|
||||
control_frame.nodes.append(node)
|
||||
if not (
|
||||
isinstance(node, parsetree.ControlLine)
|
||||
and control_frame.is_ternary(node.keyword)
|
||||
if (
|
||||
not (
|
||||
isinstance(node, parsetree.ControlLine)
|
||||
and control_frame.is_ternary(node.keyword)
|
||||
)
|
||||
and self.ternary_stack
|
||||
and self.ternary_stack[-1]
|
||||
):
|
||||
if self.ternary_stack and self.ternary_stack[-1]:
|
||||
self.ternary_stack[-1][-1].nodes.append(node)
|
||||
self.ternary_stack[-1][-1].nodes.append(node)
|
||||
if isinstance(node, parsetree.Tag):
|
||||
if len(self.tag):
|
||||
node.parent = self.tag[-1]
|
||||
|
@ -188,18 +169,18 @@ class Lexer(object):
|
|||
raise exceptions.SyntaxException(
|
||||
"Keyword '%s' not a legal ternary for keyword '%s'"
|
||||
% (node.keyword, self.control_line[-1].keyword),
|
||||
**self.exception_kwargs
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
|
||||
_coding_re = re.compile(r"#.*coding[:=]\s*([-\w.]+).*\r?\n")
|
||||
|
||||
def decode_raw_stream(self, text, decode_raw, known_encoding, filename):
|
||||
"""given string/unicode or bytes/string, determine encoding
|
||||
from magic encoding comment, return body as unicode
|
||||
or raw if decode_raw=False
|
||||
from magic encoding comment, return body as unicode
|
||||
or raw if decode_raw=False
|
||||
|
||||
"""
|
||||
if isinstance(text, compat.text_type):
|
||||
if isinstance(text, str):
|
||||
m = self._coding_re.match(text)
|
||||
encoding = m and m.group(1) or known_encoding or "utf-8"
|
||||
return encoding, text
|
||||
|
@ -219,11 +200,7 @@ class Lexer(object):
|
|||
)
|
||||
else:
|
||||
m = self._coding_re.match(text.decode("utf-8", "ignore"))
|
||||
if m:
|
||||
parsed_encoding = m.group(1)
|
||||
else:
|
||||
parsed_encoding = known_encoding or "utf-8"
|
||||
|
||||
parsed_encoding = m.group(1) if m else known_encoding or "utf-8"
|
||||
if decode_raw:
|
||||
try:
|
||||
text = text.decode(parsed_encoding)
|
||||
|
@ -241,7 +218,7 @@ class Lexer(object):
|
|||
|
||||
def parse(self):
|
||||
self.encoding, self.text = self.decode_raw_stream(
|
||||
self.text, not self.disable_unicode, self.encoding, self.filename
|
||||
self.text, True, self.encoding, self.filename
|
||||
)
|
||||
|
||||
for preproc in self.preprocessor:
|
||||
|
@ -276,12 +253,13 @@ class Lexer(object):
|
|||
|
||||
if self.match_position > self.textlength:
|
||||
break
|
||||
raise exceptions.CompileException("assertion failed")
|
||||
# TODO: no coverage here
|
||||
raise exceptions.MakoException("assertion failed")
|
||||
|
||||
if len(self.tag):
|
||||
raise exceptions.SyntaxException(
|
||||
"Unclosed tag: <%%%s>" % self.tag[-1].keyword,
|
||||
**self.exception_kwargs
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
if len(self.control_line):
|
||||
raise exceptions.SyntaxException(
|
||||
|
@ -312,35 +290,34 @@ class Lexer(object):
|
|||
re.I | re.S | re.X,
|
||||
)
|
||||
|
||||
if match:
|
||||
keyword, attr, isend = match.groups()
|
||||
self.keyword = keyword
|
||||
attributes = {}
|
||||
if attr:
|
||||
for att in re.findall(
|
||||
r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr
|
||||
):
|
||||
key, val1, val2 = att
|
||||
text = val1 or val2
|
||||
text = text.replace("\r\n", "\n")
|
||||
attributes[key] = text
|
||||
self.append_node(parsetree.Tag, keyword, attributes)
|
||||
if isend:
|
||||
self.tag.pop()
|
||||
else:
|
||||
if keyword == "text":
|
||||
match = self.match(r"(.*?)(?=\</%text>)", re.S)
|
||||
if not match:
|
||||
raise exceptions.SyntaxException(
|
||||
"Unclosed tag: <%%%s>" % self.tag[-1].keyword,
|
||||
**self.exception_kwargs
|
||||
)
|
||||
self.append_node(parsetree.Text, match.group(1))
|
||||
return self.match_tag_end()
|
||||
return True
|
||||
else:
|
||||
if not match:
|
||||
return False
|
||||
|
||||
keyword, attr, isend = match.groups()
|
||||
self.keyword = keyword
|
||||
attributes = {}
|
||||
if attr:
|
||||
for att in re.findall(
|
||||
r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr
|
||||
):
|
||||
key, val1, val2 = att
|
||||
text = val1 or val2
|
||||
text = text.replace("\r\n", "\n")
|
||||
attributes[key] = text
|
||||
self.append_node(parsetree.Tag, keyword, attributes)
|
||||
if isend:
|
||||
self.tag.pop()
|
||||
elif keyword == "text":
|
||||
match = self.match(r"(.*?)(?=\</%text>)", re.S)
|
||||
if not match:
|
||||
raise exceptions.SyntaxException(
|
||||
"Unclosed tag: <%%%s>" % self.tag[-1].keyword,
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
self.append_node(parsetree.Text, match.group(1))
|
||||
return self.match_tag_end()
|
||||
return True
|
||||
|
||||
def match_tag_end(self):
|
||||
match = self.match(r"\</%[\t ]*(.+?)[\t ]*>")
|
||||
if match:
|
||||
|
@ -348,13 +325,13 @@ class Lexer(object):
|
|||
raise exceptions.SyntaxException(
|
||||
"Closing tag without opening tag: </%%%s>"
|
||||
% match.group(1),
|
||||
**self.exception_kwargs
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
elif self.tag[-1].keyword != match.group(1):
|
||||
raise exceptions.SyntaxException(
|
||||
"Closing tag </%%%s> does not match tag: <%%%s>"
|
||||
% (match.group(1), self.tag[-1].keyword),
|
||||
**self.exception_kwargs
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
self.tag.pop()
|
||||
return True
|
||||
|
@ -363,15 +340,15 @@ class Lexer(object):
|
|||
|
||||
def match_end(self):
|
||||
match = self.match(r"\Z", re.S)
|
||||
if match:
|
||||
string = match.group()
|
||||
if string:
|
||||
return string
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
if not match:
|
||||
return False
|
||||
|
||||
string = match.group()
|
||||
if string:
|
||||
return string
|
||||
else:
|
||||
return True
|
||||
|
||||
def match_text(self):
|
||||
match = self.match(
|
||||
r"""
|
||||
|
@ -422,64 +399,63 @@ class Lexer(object):
|
|||
|
||||
def match_expression(self):
|
||||
match = self.match(r"\${")
|
||||
if match:
|
||||
line, pos = self.matched_lineno, self.matched_charpos
|
||||
text, end = self.parse_until_text(True, r"\|", r"}")
|
||||
if end == "|":
|
||||
escapes, end = self.parse_until_text(True, r"}")
|
||||
else:
|
||||
escapes = ""
|
||||
text = text.replace("\r\n", "\n")
|
||||
self.append_node(
|
||||
parsetree.Expression,
|
||||
text,
|
||||
escapes.strip(),
|
||||
lineno=line,
|
||||
pos=pos,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
if not match:
|
||||
return False
|
||||
|
||||
line, pos = self.matched_lineno, self.matched_charpos
|
||||
text, end = self.parse_until_text(True, r"\|", r"}")
|
||||
if end == "|":
|
||||
escapes, end = self.parse_until_text(True, r"}")
|
||||
else:
|
||||
escapes = ""
|
||||
text = text.replace("\r\n", "\n")
|
||||
self.append_node(
|
||||
parsetree.Expression,
|
||||
text,
|
||||
escapes.strip(),
|
||||
lineno=line,
|
||||
pos=pos,
|
||||
)
|
||||
return True
|
||||
|
||||
def match_control_line(self):
|
||||
match = self.match(
|
||||
r"(?<=^)[\t ]*(%(?!%)|##)[\t ]*((?:(?:\\\r?\n)|[^\r\n])*)"
|
||||
r"(?:\r?\n|\Z)",
|
||||
re.M,
|
||||
)
|
||||
if match:
|
||||
operator = match.group(1)
|
||||
text = match.group(2)
|
||||
if operator == "%":
|
||||
m2 = re.match(r"(end)?(\w+)\s*(.*)", text)
|
||||
if not m2:
|
||||
raise exceptions.SyntaxException(
|
||||
"Invalid control line: '%s'" % text,
|
||||
**self.exception_kwargs
|
||||
)
|
||||
isend, keyword = m2.group(1, 2)
|
||||
isend = isend is not None
|
||||
|
||||
if isend:
|
||||
if not len(self.control_line):
|
||||
raise exceptions.SyntaxException(
|
||||
"No starting keyword '%s' for '%s'"
|
||||
% (keyword, text),
|
||||
**self.exception_kwargs
|
||||
)
|
||||
elif self.control_line[-1].keyword != keyword:
|
||||
raise exceptions.SyntaxException(
|
||||
"Keyword '%s' doesn't match keyword '%s'"
|
||||
% (text, self.control_line[-1].keyword),
|
||||
**self.exception_kwargs
|
||||
)
|
||||
self.append_node(parsetree.ControlLine, keyword, isend, text)
|
||||
else:
|
||||
self.append_node(parsetree.Comment, text)
|
||||
return True
|
||||
else:
|
||||
if not match:
|
||||
return False
|
||||
|
||||
operator = match.group(1)
|
||||
text = match.group(2)
|
||||
if operator == "%":
|
||||
m2 = re.match(r"(end)?(\w+)\s*(.*)", text)
|
||||
if not m2:
|
||||
raise exceptions.SyntaxException(
|
||||
"Invalid control line: '%s'" % text,
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
isend, keyword = m2.group(1, 2)
|
||||
isend = isend is not None
|
||||
|
||||
if isend:
|
||||
if not len(self.control_line):
|
||||
raise exceptions.SyntaxException(
|
||||
"No starting keyword '%s' for '%s'" % (keyword, text),
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
elif self.control_line[-1].keyword != keyword:
|
||||
raise exceptions.SyntaxException(
|
||||
"Keyword '%s' doesn't match keyword '%s'"
|
||||
% (text, self.control_line[-1].keyword),
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
self.append_node(parsetree.ControlLine, keyword, isend, text)
|
||||
else:
|
||||
self.append_node(parsetree.Comment, text)
|
||||
return True
|
||||
|
||||
def match_comment(self):
|
||||
"""matches the multiline version of a comment"""
|
||||
match = self.match(r"<%doc>(.*?)</%doc>", re.S)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/lookup.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -8,18 +8,14 @@ import os
|
|||
import posixpath
|
||||
import re
|
||||
import stat
|
||||
import threading
|
||||
|
||||
from mako import exceptions
|
||||
from mako import util
|
||||
from mako.template import Template
|
||||
|
||||
try:
|
||||
import threading
|
||||
except:
|
||||
import dummy_threading as threading
|
||||
|
||||
|
||||
class TemplateCollection(object):
|
||||
class TemplateCollection:
|
||||
|
||||
"""Represent a collection of :class:`.Template` objects,
|
||||
identifiable via URI.
|
||||
|
@ -34,7 +30,7 @@ class TemplateCollection(object):
|
|||
:class:`.TemplateCollection` is an abstract class,
|
||||
with the usual default implementation being :class:`.TemplateLookup`.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def has_template(self, uri):
|
||||
"""Return ``True`` if this :class:`.TemplateLookup` is
|
||||
|
@ -68,7 +64,7 @@ class TemplateCollection(object):
|
|||
|
||||
def filename_to_uri(self, uri, filename):
|
||||
"""Convert the given ``filename`` to a URI relative to
|
||||
this :class:`.TemplateCollection`."""
|
||||
this :class:`.TemplateCollection`."""
|
||||
|
||||
return uri
|
||||
|
||||
|
@ -161,8 +157,6 @@ class TemplateLookup(TemplateCollection):
|
|||
collection_size=-1,
|
||||
format_exceptions=False,
|
||||
error_handler=None,
|
||||
disable_unicode=False,
|
||||
bytestring_passthrough=False,
|
||||
output_encoding=None,
|
||||
encoding_errors="strict",
|
||||
cache_args=None,
|
||||
|
@ -207,8 +201,6 @@ class TemplateLookup(TemplateCollection):
|
|||
"format_exceptions": format_exceptions,
|
||||
"error_handler": error_handler,
|
||||
"include_error_handler": include_error_handler,
|
||||
"disable_unicode": disable_unicode,
|
||||
"bytestring_passthrough": bytestring_passthrough,
|
||||
"output_encoding": output_encoding,
|
||||
"cache_impl": cache_impl,
|
||||
"encoding_errors": encoding_errors,
|
||||
|
@ -249,7 +241,7 @@ class TemplateLookup(TemplateCollection):
|
|||
return self._check(uri, self._collection[uri])
|
||||
else:
|
||||
return self._collection[uri]
|
||||
except KeyError:
|
||||
except KeyError as e:
|
||||
u = re.sub(r"^\/+", "", uri)
|
||||
for dir_ in self.directories:
|
||||
# make sure the path seperators are posix - os.altsep is empty
|
||||
|
@ -260,8 +252,8 @@ class TemplateLookup(TemplateCollection):
|
|||
return self._load(srcfile, uri)
|
||||
else:
|
||||
raise exceptions.TopLevelLookupException(
|
||||
"Cant locate template for uri %r" % uri
|
||||
)
|
||||
"Can't locate template for uri %r" % uri
|
||||
) from e
|
||||
|
||||
def adjust_uri(self, uri, relativeto):
|
||||
"""Adjust the given ``uri`` based on the given relative URI."""
|
||||
|
@ -270,20 +262,19 @@ class TemplateLookup(TemplateCollection):
|
|||
if key in self._uri_cache:
|
||||
return self._uri_cache[key]
|
||||
|
||||
if uri[0] != "/":
|
||||
if relativeto is not None:
|
||||
v = self._uri_cache[key] = posixpath.join(
|
||||
posixpath.dirname(relativeto), uri
|
||||
)
|
||||
else:
|
||||
v = self._uri_cache[key] = "/" + uri
|
||||
else:
|
||||
if uri[0] == "/":
|
||||
v = self._uri_cache[key] = uri
|
||||
elif relativeto is not None:
|
||||
v = self._uri_cache[key] = posixpath.join(
|
||||
posixpath.dirname(relativeto), uri
|
||||
)
|
||||
else:
|
||||
v = self._uri_cache[key] = "/" + uri
|
||||
return v
|
||||
|
||||
def filename_to_uri(self, filename):
|
||||
"""Convert the given ``filename`` to a URI relative to
|
||||
this :class:`.TemplateCollection`."""
|
||||
this :class:`.TemplateCollection`."""
|
||||
|
||||
try:
|
||||
return self._uri_cache[filename]
|
||||
|
@ -294,7 +285,7 @@ class TemplateLookup(TemplateCollection):
|
|||
|
||||
def _relativeize(self, filename):
|
||||
"""Return the portion of a filename that is 'relative'
|
||||
to the directories in this lookup.
|
||||
to the directories in this lookup.
|
||||
|
||||
"""
|
||||
|
||||
|
@ -324,7 +315,7 @@ class TemplateLookup(TemplateCollection):
|
|||
filename=posixpath.normpath(filename),
|
||||
lookup=self,
|
||||
module_filename=module_filename,
|
||||
**self.template_args
|
||||
**self.template_args,
|
||||
)
|
||||
return template
|
||||
except:
|
||||
|
@ -342,16 +333,15 @@ class TemplateLookup(TemplateCollection):
|
|||
|
||||
try:
|
||||
template_stat = os.stat(template.filename)
|
||||
if template.module._modified_time < template_stat[stat.ST_MTIME]:
|
||||
self._collection.pop(uri, None)
|
||||
return self._load(template.filename, uri)
|
||||
else:
|
||||
if template.module._modified_time >= template_stat[stat.ST_MTIME]:
|
||||
return template
|
||||
except OSError:
|
||||
self._collection.pop(uri, None)
|
||||
return self._load(template.filename, uri)
|
||||
except OSError as e:
|
||||
self._collection.pop(uri, None)
|
||||
raise exceptions.TemplateLookupException(
|
||||
"Cant locate template for uri %r" % uri
|
||||
)
|
||||
"Can't locate template for uri %r" % uri
|
||||
) from e
|
||||
|
||||
def put_string(self, uri, text):
|
||||
"""Place a new :class:`.Template` object into this
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/parsetree.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -9,13 +9,12 @@
|
|||
import re
|
||||
|
||||
from mako import ast
|
||||
from mako import compat
|
||||
from mako import exceptions
|
||||
from mako import filters
|
||||
from mako import util
|
||||
|
||||
|
||||
class Node(object):
|
||||
class Node:
|
||||
|
||||
"""base class for a Node in the parse tree."""
|
||||
|
||||
|
@ -51,7 +50,7 @@ class TemplateNode(Node):
|
|||
"""a 'container' node that stores the overall collection of nodes."""
|
||||
|
||||
def __init__(self, filename):
|
||||
super(TemplateNode, self).__init__("", 0, 0, filename)
|
||||
super().__init__("", 0, 0, filename)
|
||||
self.nodes = []
|
||||
self.page_attributes = {}
|
||||
|
||||
|
@ -80,7 +79,7 @@ class ControlLine(Node):
|
|||
has_loop_context = False
|
||||
|
||||
def __init__(self, keyword, isend, text, **kwargs):
|
||||
super(ControlLine, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
self.text = text
|
||||
self.keyword = keyword
|
||||
self.isend = isend
|
||||
|
@ -107,11 +106,13 @@ class ControlLine(Node):
|
|||
"""return true if the given keyword is a ternary keyword
|
||||
for this ControlLine"""
|
||||
|
||||
return keyword in {
|
||||
"if": set(["else", "elif"]),
|
||||
"try": set(["except", "finally"]),
|
||||
"for": set(["else"]),
|
||||
}.get(self.keyword, [])
|
||||
cases = {
|
||||
"if": {"else", "elif"},
|
||||
"try": {"except", "finally"},
|
||||
"for": {"else"},
|
||||
}
|
||||
|
||||
return keyword in cases.get(self.keyword, set())
|
||||
|
||||
def __repr__(self):
|
||||
return "ControlLine(%r, %r, %r, %r)" % (
|
||||
|
@ -123,11 +124,10 @@ class ControlLine(Node):
|
|||
|
||||
|
||||
class Text(Node):
|
||||
|
||||
"""defines plain text in the template."""
|
||||
|
||||
def __init__(self, content, **kwargs):
|
||||
super(Text, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
self.content = content
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -135,7 +135,6 @@ class Text(Node):
|
|||
|
||||
|
||||
class Code(Node):
|
||||
|
||||
"""defines a Python code block, either inline or module level.
|
||||
|
||||
e.g.::
|
||||
|
@ -153,7 +152,7 @@ class Code(Node):
|
|||
"""
|
||||
|
||||
def __init__(self, text, ismodule, **kwargs):
|
||||
super(Code, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
self.text = text
|
||||
self.ismodule = ismodule
|
||||
self.code = ast.PythonCode(text, **self.exception_kwargs)
|
||||
|
@ -173,7 +172,6 @@ class Code(Node):
|
|||
|
||||
|
||||
class Comment(Node):
|
||||
|
||||
"""defines a comment line.
|
||||
|
||||
# this is a comment
|
||||
|
@ -181,7 +179,7 @@ class Comment(Node):
|
|||
"""
|
||||
|
||||
def __init__(self, text, **kwargs):
|
||||
super(Comment, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
self.text = text
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -189,7 +187,6 @@ class Comment(Node):
|
|||
|
||||
|
||||
class Expression(Node):
|
||||
|
||||
"""defines an inline expression.
|
||||
|
||||
${x+y}
|
||||
|
@ -197,7 +194,7 @@ class Expression(Node):
|
|||
"""
|
||||
|
||||
def __init__(self, text, escapes, **kwargs):
|
||||
super(Expression, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
self.text = text
|
||||
self.escapes = escapes
|
||||
self.escapes_code = ast.ArgumentList(escapes, **self.exception_kwargs)
|
||||
|
@ -210,7 +207,7 @@ class Expression(Node):
|
|||
# TODO: make the "filter" shortcut list configurable at parse/gen time
|
||||
return self.code.undeclared_identifiers.union(
|
||||
self.escapes_code.undeclared_identifiers.difference(
|
||||
set(filters.DEFAULT_ESCAPES.keys())
|
||||
filters.DEFAULT_ESCAPES
|
||||
)
|
||||
).difference(self.code.declared_identifiers)
|
||||
|
||||
|
@ -223,7 +220,6 @@ class Expression(Node):
|
|||
|
||||
|
||||
class _TagMeta(type):
|
||||
|
||||
"""metaclass to allow Tag to produce a subclass according to
|
||||
its keyword"""
|
||||
|
||||
|
@ -232,7 +228,7 @@ class _TagMeta(type):
|
|||
def __init__(cls, clsname, bases, dict_):
|
||||
if getattr(cls, "__keyword__", None) is not None:
|
||||
cls._classmap[cls.__keyword__] = cls
|
||||
super(_TagMeta, cls).__init__(clsname, bases, dict_)
|
||||
super().__init__(clsname, bases, dict_)
|
||||
|
||||
def __call__(cls, keyword, attributes, **kwargs):
|
||||
if ":" in keyword:
|
||||
|
@ -254,7 +250,7 @@ class _TagMeta(type):
|
|||
return type.__call__(cls, keyword, attributes, **kwargs)
|
||||
|
||||
|
||||
class Tag(compat.with_metaclass(_TagMeta, Node)):
|
||||
class Tag(Node, metaclass=_TagMeta):
|
||||
"""abstract base class for tags.
|
||||
|
||||
e.g.::
|
||||
|
@ -276,7 +272,7 @@ class Tag(compat.with_metaclass(_TagMeta, Node)):
|
|||
expressions,
|
||||
nonexpressions,
|
||||
required,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
r"""construct a new Tag instance.
|
||||
|
||||
|
@ -297,17 +293,20 @@ class Tag(compat.with_metaclass(_TagMeta, Node)):
|
|||
other arguments passed to the Node superclass (lineno, pos)
|
||||
|
||||
"""
|
||||
super(Tag, self).__init__(**kwargs)
|
||||
super().__init__(**kwargs)
|
||||
self.keyword = keyword
|
||||
self.attributes = attributes
|
||||
self._parse_attributes(expressions, nonexpressions)
|
||||
missing = [r for r in required if r not in self.parsed_attributes]
|
||||
if len(missing):
|
||||
raise exceptions.CompileException(
|
||||
"Missing attribute(s): %s"
|
||||
% ",".join([repr(m) for m in missing]),
|
||||
**self.exception_kwargs
|
||||
(
|
||||
"Missing attribute(s): %s"
|
||||
% ",".join(repr(m) for m in missing)
|
||||
),
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
|
||||
self.parent = None
|
||||
self.nodes = []
|
||||
|
||||
|
@ -339,23 +338,22 @@ class Tag(compat.with_metaclass(_TagMeta, Node)):
|
|||
code.undeclared_identifiers
|
||||
)
|
||||
expr.append("(%s)" % m.group(1))
|
||||
else:
|
||||
if x:
|
||||
expr.append(repr(x))
|
||||
elif x:
|
||||
expr.append(repr(x))
|
||||
self.parsed_attributes[key] = " + ".join(expr) or repr("")
|
||||
elif key in nonexpressions:
|
||||
if re.search(r"\${.+?}", self.attributes[key]):
|
||||
raise exceptions.CompileException(
|
||||
"Attibute '%s' in tag '%s' does not allow embedded "
|
||||
"Attribute '%s' in tag '%s' does not allow embedded "
|
||||
"expressions" % (key, self.keyword),
|
||||
**self.exception_kwargs
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
self.parsed_attributes[key] = repr(self.attributes[key])
|
||||
else:
|
||||
raise exceptions.CompileException(
|
||||
"Invalid attribute for tag '%s': '%s'"
|
||||
% (self.keyword, key),
|
||||
**self.exception_kwargs
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
self.expression_undeclared_identifiers = undeclared_identifiers
|
||||
|
||||
|
@ -379,13 +377,13 @@ class IncludeTag(Tag):
|
|||
__keyword__ = "include"
|
||||
|
||||
def __init__(self, keyword, attributes, **kwargs):
|
||||
super(IncludeTag, self).__init__(
|
||||
super().__init__(
|
||||
keyword,
|
||||
attributes,
|
||||
("file", "import", "args"),
|
||||
(),
|
||||
("file",),
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
self.page_args = ast.PythonCode(
|
||||
"__DUMMY(%s)" % attributes.get("args", ""), **self.exception_kwargs
|
||||
|
@ -396,24 +394,22 @@ class IncludeTag(Tag):
|
|||
|
||||
def undeclared_identifiers(self):
|
||||
identifiers = self.page_args.undeclared_identifiers.difference(
|
||||
set(["__DUMMY"])
|
||||
{"__DUMMY"}
|
||||
).difference(self.page_args.declared_identifiers)
|
||||
return identifiers.union(
|
||||
super(IncludeTag, self).undeclared_identifiers()
|
||||
)
|
||||
return identifiers.union(super().undeclared_identifiers())
|
||||
|
||||
|
||||
class NamespaceTag(Tag):
|
||||
__keyword__ = "namespace"
|
||||
|
||||
def __init__(self, keyword, attributes, **kwargs):
|
||||
super(NamespaceTag, self).__init__(
|
||||
super().__init__(
|
||||
keyword,
|
||||
attributes,
|
||||
("file",),
|
||||
("name", "inheritable", "import", "module"),
|
||||
(),
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.name = attributes.get("name", "__anon_%s" % hex(abs(id(self))))
|
||||
|
@ -421,12 +417,12 @@ class NamespaceTag(Tag):
|
|||
raise exceptions.CompileException(
|
||||
"'name' and/or 'import' attributes are required "
|
||||
"for <%namespace>",
|
||||
**self.exception_kwargs
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
if "file" in attributes and "module" in attributes:
|
||||
raise exceptions.CompileException(
|
||||
"<%namespace> may only have one of 'file' or 'module'",
|
||||
**self.exception_kwargs
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
|
||||
def declared_identifiers(self):
|
||||
|
@ -437,9 +433,7 @@ class TextTag(Tag):
|
|||
__keyword__ = "text"
|
||||
|
||||
def __init__(self, keyword, attributes, **kwargs):
|
||||
super(TextTag, self).__init__(
|
||||
keyword, attributes, (), ("filter"), (), **kwargs
|
||||
)
|
||||
super().__init__(keyword, attributes, (), ("filter"), (), **kwargs)
|
||||
self.filter_args = ast.ArgumentList(
|
||||
attributes.get("filter", ""), **self.exception_kwargs
|
||||
)
|
||||
|
@ -458,13 +452,13 @@ class DefTag(Tag):
|
|||
c for c in attributes if c.startswith("cache_")
|
||||
]
|
||||
|
||||
super(DefTag, self).__init__(
|
||||
super().__init__(
|
||||
keyword,
|
||||
attributes,
|
||||
expressions,
|
||||
("name", "filter", "decorator"),
|
||||
("name",),
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
name = attributes["name"]
|
||||
if re.match(r"^[\w_]+$", name):
|
||||
|
@ -521,19 +515,19 @@ class BlockTag(Tag):
|
|||
c for c in attributes if c.startswith("cache_")
|
||||
]
|
||||
|
||||
super(BlockTag, self).__init__(
|
||||
super().__init__(
|
||||
keyword,
|
||||
attributes,
|
||||
expressions,
|
||||
("name", "filter", "decorator"),
|
||||
(),
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
name = attributes.get("name")
|
||||
if name and not re.match(r"^[\w_]+$", name):
|
||||
raise exceptions.CompileException(
|
||||
"%block may not specify an argument signature",
|
||||
**self.exception_kwargs
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
if not name and attributes.get("args", None):
|
||||
raise exceptions.CompileException(
|
||||
|
@ -577,7 +571,7 @@ class CallTag(Tag):
|
|||
__keyword__ = "call"
|
||||
|
||||
def __init__(self, keyword, attributes, **kwargs):
|
||||
super(CallTag, self).__init__(
|
||||
super().__init__(
|
||||
keyword, attributes, ("args"), ("expr",), ("expr",), **kwargs
|
||||
)
|
||||
self.expression = attributes["expr"]
|
||||
|
@ -597,26 +591,25 @@ class CallTag(Tag):
|
|||
|
||||
class CallNamespaceTag(Tag):
|
||||
def __init__(self, namespace, defname, attributes, **kwargs):
|
||||
super(CallNamespaceTag, self).__init__(
|
||||
super().__init__(
|
||||
namespace + ":" + defname,
|
||||
attributes,
|
||||
tuple(attributes.keys()) + ("args",),
|
||||
(),
|
||||
(),
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.expression = "%s.%s(%s)" % (
|
||||
namespace,
|
||||
defname,
|
||||
",".join(
|
||||
[
|
||||
"%s=%s" % (k, v)
|
||||
for k, v in self.parsed_attributes.items()
|
||||
if k != "args"
|
||||
]
|
||||
"%s=%s" % (k, v)
|
||||
for k, v in self.parsed_attributes.items()
|
||||
if k != "args"
|
||||
),
|
||||
)
|
||||
|
||||
self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
|
||||
self.body_decl = ast.FunctionArgs(
|
||||
attributes.get("args", ""), **self.exception_kwargs
|
||||
|
@ -635,7 +628,7 @@ class InheritTag(Tag):
|
|||
__keyword__ = "inherit"
|
||||
|
||||
def __init__(self, keyword, attributes, **kwargs):
|
||||
super(InheritTag, self).__init__(
|
||||
super().__init__(
|
||||
keyword, attributes, ("file",), (), ("file",), **kwargs
|
||||
)
|
||||
|
||||
|
@ -651,9 +644,7 @@ class PageTag(Tag):
|
|||
"enable_loop",
|
||||
] + [c for c in attributes if c.startswith("cache_")]
|
||||
|
||||
super(PageTag, self).__init__(
|
||||
keyword, attributes, expressions, (), (), **kwargs
|
||||
)
|
||||
super().__init__(keyword, attributes, expressions, (), (), **kwargs)
|
||||
self.body_decl = ast.FunctionArgs(
|
||||
attributes.get("args", ""), **self.exception_kwargs
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/pygen.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -11,7 +11,7 @@ import re
|
|||
from mako import exceptions
|
||||
|
||||
|
||||
class PythonPrinter(object):
|
||||
class PythonPrinter:
|
||||
def __init__(self, stream):
|
||||
# indentation counter
|
||||
self.indent = 0
|
||||
|
@ -96,18 +96,19 @@ class PythonPrinter(object):
|
|||
is_comment = line and len(line) and line[0] == "#"
|
||||
|
||||
# see if this line should decrease the indentation level
|
||||
if not is_comment and (not hastext or self._is_unindentor(line)):
|
||||
|
||||
if self.indent > 0:
|
||||
self.indent -= 1
|
||||
# if the indent_detail stack is empty, the user
|
||||
# probably put extra closures - the resulting
|
||||
# module wont compile.
|
||||
if len(self.indent_detail) == 0:
|
||||
raise exceptions.SyntaxException(
|
||||
"Too many whitespace closures"
|
||||
)
|
||||
self.indent_detail.pop()
|
||||
if (
|
||||
not is_comment
|
||||
and (not hastext or self._is_unindentor(line))
|
||||
and self.indent > 0
|
||||
):
|
||||
self.indent -= 1
|
||||
# if the indent_detail stack is empty, the user
|
||||
# probably put extra closures - the resulting
|
||||
# module wont compile.
|
||||
if len(self.indent_detail) == 0:
|
||||
# TODO: no coverage here
|
||||
raise exceptions.MakoException("Too many whitespace closures")
|
||||
self.indent_detail.pop()
|
||||
|
||||
if line is None:
|
||||
return
|
||||
|
@ -167,13 +168,10 @@ class PythonPrinter(object):
|
|||
# if the current line doesnt have one of the "unindentor" keywords,
|
||||
# return False
|
||||
match = re.match(r"^\s*(else|elif|except|finally).*\:", line)
|
||||
if not match:
|
||||
return False
|
||||
|
||||
# whitespace matches up, we have a compound indentor,
|
||||
# if True, whitespace matches up, we have a compound indentor,
|
||||
# and this line has an unindentor, this
|
||||
# is probably good enough
|
||||
return True
|
||||
return bool(match)
|
||||
|
||||
# should we decide that its not good enough, heres
|
||||
# more stuff to check.
|
||||
|
@ -218,11 +216,7 @@ class PythonPrinter(object):
|
|||
|
||||
current_state = self.backslashed or self.triplequoted
|
||||
|
||||
if re.search(r"\\$", line):
|
||||
self.backslashed = True
|
||||
else:
|
||||
self.backslashed = False
|
||||
|
||||
self.backslashed = bool(re.search(r"\\$", line))
|
||||
triples = len(re.findall(r"\"\"\"|\'\'\'", line))
|
||||
if triples == 1 or triples % 2 != 0:
|
||||
self.triplequoted = not self.triplequoted
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/pyparser.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -18,22 +18,13 @@ from mako import _ast_util
|
|||
from mako import compat
|
||||
from mako import exceptions
|
||||
from mako import util
|
||||
from mako.compat import arg_stringname
|
||||
|
||||
if compat.py3k:
|
||||
# words that cannot be assigned to (notably
|
||||
# smaller than the total keys in __builtins__)
|
||||
reserved = set(["True", "False", "None", "print"])
|
||||
# words that cannot be assigned to (notably
|
||||
# smaller than the total keys in __builtins__)
|
||||
reserved = {"True", "False", "None", "print"}
|
||||
|
||||
# the "id" attribute on a function node
|
||||
arg_id = operator.attrgetter("arg")
|
||||
else:
|
||||
# words that cannot be assigned to (notably
|
||||
# smaller than the total keys in __builtins__)
|
||||
reserved = set(["True", "False", "None"])
|
||||
|
||||
# the "id" attribute on a function node
|
||||
arg_id = operator.attrgetter("id")
|
||||
# the "id" attribute on a function node
|
||||
arg_id = operator.attrgetter("arg")
|
||||
|
||||
util.restore__ast(_ast)
|
||||
|
||||
|
@ -43,7 +34,7 @@ def parse(code, mode="exec", **exception_kwargs):
|
|||
|
||||
try:
|
||||
return _ast_util.parse(code, "<unknown>", mode)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
raise exceptions.SyntaxException(
|
||||
"(%s) %s (%r)"
|
||||
% (
|
||||
|
@ -51,8 +42,8 @@ def parse(code, mode="exec", **exception_kwargs):
|
|||
compat.exception_as(),
|
||||
code[0:50],
|
||||
),
|
||||
**exception_kwargs
|
||||
)
|
||||
**exception_kwargs,
|
||||
) from e
|
||||
|
||||
|
||||
class FindIdentifiers(_ast_util.NodeVisitor):
|
||||
|
@ -85,18 +76,13 @@ class FindIdentifiers(_ast_util.NodeVisitor):
|
|||
self.visit(n)
|
||||
self.in_assign_targets = in_a
|
||||
|
||||
if compat.py3k:
|
||||
|
||||
# ExceptHandler is in Python 2, but this block only works in
|
||||
# Python 3 (and is required there)
|
||||
|
||||
def visit_ExceptHandler(self, node):
|
||||
if node.name is not None:
|
||||
self._add_declared(node.name)
|
||||
if node.type is not None:
|
||||
self.visit(node.type)
|
||||
for statement in node.body:
|
||||
self.visit(statement)
|
||||
def visit_ExceptHandler(self, node):
|
||||
if node.name is not None:
|
||||
self._add_declared(node.name)
|
||||
if node.type is not None:
|
||||
self.visit(node.type)
|
||||
for statement in node.body:
|
||||
self.visit(statement)
|
||||
|
||||
def visit_Lambda(self, node, *args):
|
||||
self._visit_function(node, True)
|
||||
|
@ -108,8 +94,7 @@ class FindIdentifiers(_ast_util.NodeVisitor):
|
|||
def _expand_tuples(self, args):
|
||||
for arg in args:
|
||||
if isinstance(arg, _ast.Tuple):
|
||||
for n in arg.elts:
|
||||
yield n
|
||||
yield from arg.elts
|
||||
else:
|
||||
yield arg
|
||||
|
||||
|
@ -170,15 +155,15 @@ class FindIdentifiers(_ast_util.NodeVisitor):
|
|||
for name in node.names:
|
||||
if name.asname is not None:
|
||||
self._add_declared(name.asname)
|
||||
elif name.name == "*":
|
||||
raise exceptions.CompileException(
|
||||
"'import *' is not supported, since all identifier "
|
||||
"names must be explicitly declared. Please use the "
|
||||
"form 'from <modulename> import <name1>, <name2>, "
|
||||
"...' instead.",
|
||||
**self.exception_kwargs,
|
||||
)
|
||||
else:
|
||||
if name.name == "*":
|
||||
raise exceptions.CompileException(
|
||||
"'import *' is not supported, since all identifier "
|
||||
"names must be explicitly declared. Please use the "
|
||||
"form 'from <modulename> import <name1>, <name2>, "
|
||||
"...' instead.",
|
||||
**self.exception_kwargs
|
||||
)
|
||||
self._add_declared(name.name)
|
||||
|
||||
|
||||
|
@ -213,27 +198,20 @@ class ParseFunc(_ast_util.NodeVisitor):
|
|||
|
||||
argnames = [arg_id(arg) for arg in node.args.args]
|
||||
if node.args.vararg:
|
||||
argnames.append(arg_stringname(node.args.vararg))
|
||||
argnames.append(node.args.vararg.arg)
|
||||
|
||||
if compat.py2k:
|
||||
# kw-only args don't exist in Python 2
|
||||
kwargnames = []
|
||||
else:
|
||||
kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs]
|
||||
kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs]
|
||||
if node.args.kwarg:
|
||||
kwargnames.append(arg_stringname(node.args.kwarg))
|
||||
kwargnames.append(node.args.kwarg.arg)
|
||||
self.listener.argnames = argnames
|
||||
self.listener.defaults = node.args.defaults # ast
|
||||
self.listener.kwargnames = kwargnames
|
||||
if compat.py2k:
|
||||
self.listener.kwdefaults = []
|
||||
else:
|
||||
self.listener.kwdefaults = node.args.kw_defaults
|
||||
self.listener.kwdefaults = node.args.kw_defaults
|
||||
self.listener.varargs = node.args.vararg
|
||||
self.listener.kwargs = node.args.kwarg
|
||||
|
||||
|
||||
class ExpressionGenerator(object):
|
||||
class ExpressionGenerator:
|
||||
def __init__(self, astnode):
|
||||
self.generator = _ast_util.SourceGenerator(" " * 4)
|
||||
self.generator.visit(astnode)
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
"""provides runtime services for templates, including Context,
|
||||
Namespace, and various helper functions."""
|
||||
|
||||
import builtins
|
||||
import functools
|
||||
import sys
|
||||
|
||||
from mako import compat
|
||||
from mako import exceptions
|
||||
from mako import util
|
||||
from mako.compat import compat_builtins
|
||||
|
||||
|
||||
class Context(object):
|
||||
class Context:
|
||||
|
||||
"""Provides runtime namespace, output buffer, and various
|
||||
callstacks for templates.
|
||||
|
@ -24,7 +24,7 @@ class Context(object):
|
|||
See :ref:`runtime_toplevel` for detail on the usage of
|
||||
:class:`.Context`.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, buffer, **data):
|
||||
self._buffer_stack = [buffer]
|
||||
|
@ -103,7 +103,7 @@ class Context(object):
|
|||
if key in self._data:
|
||||
return self._data[key]
|
||||
else:
|
||||
return compat_builtins.__dict__[key]
|
||||
return builtins.__dict__[key]
|
||||
|
||||
def _push_writer(self):
|
||||
"""push a capturing buffer onto this Context and return
|
||||
|
@ -135,7 +135,7 @@ class Context(object):
|
|||
def get(self, key, default=None):
|
||||
"""Return a value from this :class:`.Context`."""
|
||||
|
||||
return self._data.get(key, compat_builtins.__dict__.get(key, default))
|
||||
return self._data.get(key, builtins.__dict__.get(key, default))
|
||||
|
||||
def write(self, string):
|
||||
"""Write a string to this :class:`.Context` object's
|
||||
|
@ -216,7 +216,7 @@ class CallerStack(list):
|
|||
self.nextcaller = self.pop()
|
||||
|
||||
|
||||
class Undefined(object):
|
||||
class Undefined:
|
||||
|
||||
"""Represents an undefined value in a template.
|
||||
|
||||
|
@ -240,7 +240,7 @@ UNDEFINED = Undefined()
|
|||
STOP_RENDERING = ""
|
||||
|
||||
|
||||
class LoopStack(object):
|
||||
class LoopStack:
|
||||
|
||||
"""a stack for LoopContexts that implements the context manager protocol
|
||||
to automatically pop off the top of the stack on context exit
|
||||
|
@ -280,7 +280,7 @@ class LoopStack(object):
|
|||
return iter(self._top)
|
||||
|
||||
|
||||
class LoopContext(object):
|
||||
class LoopContext:
|
||||
|
||||
"""A magic loop variable.
|
||||
Automatically accessible in any ``% for`` block.
|
||||
|
@ -339,14 +339,13 @@ class LoopContext(object):
|
|||
return bool(self.index % 2)
|
||||
|
||||
def cycle(self, *values):
|
||||
"""Cycle through values as the loop progresses.
|
||||
"""
|
||||
"""Cycle through values as the loop progresses."""
|
||||
if not values:
|
||||
raise ValueError("You must provide values to cycle through")
|
||||
return values[self.index % len(values)]
|
||||
|
||||
|
||||
class _NSAttr(object):
|
||||
class _NSAttr:
|
||||
def __init__(self, parent):
|
||||
self.__parent = parent
|
||||
|
||||
|
@ -360,22 +359,22 @@ class _NSAttr(object):
|
|||
raise AttributeError(key)
|
||||
|
||||
|
||||
class Namespace(object):
|
||||
class Namespace:
|
||||
|
||||
"""Provides access to collections of rendering methods, which
|
||||
can be local, from other templates, or from imported modules.
|
||||
can be local, from other templates, or from imported modules.
|
||||
|
||||
To access a particular rendering method referenced by a
|
||||
:class:`.Namespace`, use plain attribute access:
|
||||
To access a particular rendering method referenced by a
|
||||
:class:`.Namespace`, use plain attribute access:
|
||||
|
||||
.. sourcecode:: mako
|
||||
.. sourcecode:: mako
|
||||
|
||||
${some_namespace.foo(x, y, z)}
|
||||
${some_namespace.foo(x, y, z)}
|
||||
|
||||
:class:`.Namespace` also contains several built-in attributes
|
||||
described here.
|
||||
:class:`.Namespace` also contains several built-in attributes
|
||||
described here.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -390,7 +389,7 @@ class Namespace(object):
|
|||
self.context = context
|
||||
self.inherits = inherits
|
||||
if callables is not None:
|
||||
self.callables = dict([(c.__name__, c) for c in callables])
|
||||
self.callables = {c.__name__: c for c in callables}
|
||||
|
||||
callables = ()
|
||||
|
||||
|
@ -482,15 +481,14 @@ class Namespace(object):
|
|||
key = (self, uri)
|
||||
if key in self.context.namespaces:
|
||||
return self.context.namespaces[key]
|
||||
else:
|
||||
ns = TemplateNamespace(
|
||||
uri,
|
||||
self.context._copy(),
|
||||
templateuri=uri,
|
||||
calling_uri=self._templateuri,
|
||||
)
|
||||
self.context.namespaces[key] = ns
|
||||
return ns
|
||||
ns = TemplateNamespace(
|
||||
uri,
|
||||
self.context._copy(),
|
||||
templateuri=uri,
|
||||
calling_uri=self._templateuri,
|
||||
)
|
||||
self.context.namespaces[key] = ns
|
||||
return ns
|
||||
|
||||
def get_template(self, uri):
|
||||
"""Return a :class:`.Template` from the given ``uri``.
|
||||
|
@ -574,7 +572,7 @@ class TemplateNamespace(Namespace):
|
|||
self.context = context
|
||||
self.inherits = inherits
|
||||
if callables is not None:
|
||||
self.callables = dict([(c.__name__, c) for c in callables])
|
||||
self.callables = {c.__name__: c for c in callables}
|
||||
|
||||
if templateuri is not None:
|
||||
self.template = _lookup_template(context, templateuri, calling_uri)
|
||||
|
@ -666,7 +664,7 @@ class ModuleNamespace(Namespace):
|
|||
self.context = context
|
||||
self.inherits = inherits
|
||||
if callables is not None:
|
||||
self.callables = dict([(c.__name__, c) for c in callables])
|
||||
self.callables = {c.__name__: c for c in callables}
|
||||
|
||||
mod = __import__(module)
|
||||
for token in module.split(".")[1:]:
|
||||
|
@ -790,7 +788,7 @@ def _include_file(context, uri, calling_uri, **kwargs):
|
|||
except Exception:
|
||||
result = template.include_error_handler(ctx, compat.exception_as())
|
||||
if not result:
|
||||
compat.reraise(*sys.exc_info())
|
||||
raise
|
||||
else:
|
||||
callable_(ctx, **kwargs)
|
||||
|
||||
|
@ -837,8 +835,10 @@ def _lookup_template(context, uri, relativeto):
|
|||
uri = lookup.adjust_uri(uri, relativeto)
|
||||
try:
|
||||
return lookup.get_template(uri)
|
||||
except exceptions.TopLevelLookupException:
|
||||
raise exceptions.TemplateLookupException(str(compat.exception_as()))
|
||||
except exceptions.TopLevelLookupException as e:
|
||||
raise exceptions.TemplateLookupException(
|
||||
str(compat.exception_as())
|
||||
) from e
|
||||
|
||||
|
||||
def _populate_self_namespace(context, template, self_ns=None):
|
||||
|
@ -862,14 +862,10 @@ def _render(template, callable_, args, data, as_unicode=False):
|
|||
output of the given template and template callable."""
|
||||
|
||||
if as_unicode:
|
||||
buf = util.FastEncodingBuffer(as_unicode=True)
|
||||
elif template.bytestring_passthrough:
|
||||
buf = compat.StringIO()
|
||||
buf = util.FastEncodingBuffer()
|
||||
else:
|
||||
buf = util.FastEncodingBuffer(
|
||||
as_unicode=as_unicode,
|
||||
encoding=template.output_encoding,
|
||||
errors=template.encoding_errors,
|
||||
encoding=template.output_encoding, errors=template.encoding_errors
|
||||
)
|
||||
context = Context(buf, **data)
|
||||
context._outputting_as_unicode = as_unicode
|
||||
|
@ -880,7 +876,7 @@ def _render(template, callable_, args, data, as_unicode=False):
|
|||
callable_,
|
||||
context,
|
||||
*args,
|
||||
**_kwargs_for_callable(callable_, data)
|
||||
**_kwargs_for_callable(callable_, data),
|
||||
)
|
||||
return context._pop_buffer().getvalue()
|
||||
|
||||
|
@ -951,13 +947,15 @@ def _render_error(template, context, error):
|
|||
if template.error_handler:
|
||||
result = template.error_handler(context, error)
|
||||
if not result:
|
||||
compat.reraise(*sys.exc_info())
|
||||
tp, value, tb = sys.exc_info()
|
||||
if value and tb:
|
||||
raise value.with_traceback(tb)
|
||||
else:
|
||||
raise error
|
||||
else:
|
||||
error_template = exceptions.html_error_template()
|
||||
if context._outputting_as_unicode:
|
||||
context._buffer_stack[:] = [
|
||||
util.FastEncodingBuffer(as_unicode=True)
|
||||
]
|
||||
context._buffer_stack[:] = [util.FastEncodingBuffer()]
|
||||
else:
|
||||
context._buffer_stack[:] = [
|
||||
util.FastEncodingBuffer(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# mako/template.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
@ -25,7 +25,7 @@ from mako import util
|
|||
from mako.lexer import Lexer
|
||||
|
||||
|
||||
class Template(object):
|
||||
class Template:
|
||||
|
||||
r"""Represents a compiled template.
|
||||
|
||||
|
@ -53,17 +53,6 @@ class Template(object):
|
|||
of return-valued ``%def``\ s "opt out" of that filtering via
|
||||
passing special attributes or objects.
|
||||
|
||||
:param bytestring_passthrough: When ``True``, and ``output_encoding`` is
|
||||
set to ``None``, and :meth:`.Template.render` is used to render,
|
||||
the `StringIO` or `cStringIO` buffer will be used instead of the
|
||||
default "fast" buffer. This allows raw bytestrings in the
|
||||
output stream, such as in expressions, to pass straight
|
||||
through to the buffer. This flag is forced
|
||||
to ``True`` if ``disable_unicode`` is also configured.
|
||||
|
||||
.. versionadded:: 0.4
|
||||
Added to provide the same behavior as that of the previous series.
|
||||
|
||||
:param cache_args: Dictionary of cache configuration arguments that
|
||||
will be passed to the :class:`.CacheImpl`. See :ref:`caching_toplevel`.
|
||||
|
||||
|
@ -94,9 +83,6 @@ class Template(object):
|
|||
:param default_filters: List of string filter names that will
|
||||
be applied to all expressions. See :ref:`filtering_default_filters`.
|
||||
|
||||
:param disable_unicode: Disables all awareness of Python Unicode
|
||||
objects. See :ref:`unicode_disabled`.
|
||||
|
||||
:param enable_loop: When ``True``, enable the ``loop`` context variable.
|
||||
This can be set to ``False`` to support templates that may
|
||||
be making usage of the name "``loop``". Individual templates can
|
||||
|
@ -255,9 +241,7 @@ class Template(object):
|
|||
cache_url=None,
|
||||
module_filename=None,
|
||||
input_encoding=None,
|
||||
disable_unicode=False,
|
||||
module_writer=None,
|
||||
bytestring_passthrough=False,
|
||||
default_filters=None,
|
||||
buffer_filters=(),
|
||||
strict_undefined=False,
|
||||
|
@ -294,26 +278,12 @@ class Template(object):
|
|||
self.input_encoding = input_encoding
|
||||
self.output_encoding = output_encoding
|
||||
self.encoding_errors = encoding_errors
|
||||
self.disable_unicode = disable_unicode
|
||||
self.bytestring_passthrough = bytestring_passthrough or disable_unicode
|
||||
self.enable_loop = enable_loop
|
||||
self.strict_undefined = strict_undefined
|
||||
self.module_writer = module_writer
|
||||
|
||||
if compat.py3k and disable_unicode:
|
||||
raise exceptions.UnsupportedError(
|
||||
"Mako for Python 3 does not " "support disabling Unicode"
|
||||
)
|
||||
elif output_encoding and disable_unicode:
|
||||
raise exceptions.UnsupportedError(
|
||||
"output_encoding must be set to "
|
||||
"None when disable_unicode is used."
|
||||
)
|
||||
if default_filters is None:
|
||||
if compat.py3k or self.disable_unicode:
|
||||
self.default_filters = ["str"]
|
||||
else:
|
||||
self.default_filters = ["unicode"]
|
||||
self.default_filters = ["str"]
|
||||
else:
|
||||
self.default_filters = default_filters
|
||||
self.buffer_filters = buffer_filters
|
||||
|
@ -387,11 +357,7 @@ class Template(object):
|
|||
):
|
||||
self.cache_impl = cache_impl
|
||||
self.cache_enabled = cache_enabled
|
||||
if cache_args:
|
||||
self.cache_args = cache_args
|
||||
else:
|
||||
self.cache_args = {}
|
||||
|
||||
self.cache_args = cache_args or {}
|
||||
# transfer deprecated cache_* args
|
||||
if cache_type:
|
||||
self.cache_args["type"] = cache_type
|
||||
|
@ -463,7 +429,7 @@ class Template(object):
|
|||
|
||||
If the template specifies an output encoding, the string
|
||||
will be encoded accordingly, else the output is raw (raw
|
||||
output uses `cStringIO` and can't handle multibyte
|
||||
output uses `StringIO` and can't handle multibyte
|
||||
characters). A :class:`.Context` object is created corresponding
|
||||
to the given data. Arguments that are explicitly declared
|
||||
by this template's internal rendering method are also
|
||||
|
@ -517,17 +483,17 @@ class ModuleTemplate(Template):
|
|||
|
||||
"""A Template which is constructed given an existing Python module.
|
||||
|
||||
e.g.::
|
||||
e.g.::
|
||||
|
||||
t = Template("this is a template")
|
||||
f = file("mymodule.py", "w")
|
||||
f.write(t.code)
|
||||
f.close()
|
||||
t = Template("this is a template")
|
||||
f = file("mymodule.py", "w")
|
||||
f.write(t.code)
|
||||
f.close()
|
||||
|
||||
import mymodule
|
||||
import mymodule
|
||||
|
||||
t = ModuleTemplate(mymodule)
|
||||
print(t.render())
|
||||
t = ModuleTemplate(mymodule)
|
||||
print(t.render())
|
||||
|
||||
"""
|
||||
|
||||
|
@ -541,8 +507,6 @@ class ModuleTemplate(Template):
|
|||
template_source=None,
|
||||
output_encoding=None,
|
||||
encoding_errors="strict",
|
||||
disable_unicode=False,
|
||||
bytestring_passthrough=False,
|
||||
format_exceptions=False,
|
||||
error_handler=None,
|
||||
lookup=None,
|
||||
|
@ -559,20 +523,8 @@ class ModuleTemplate(Template):
|
|||
self.input_encoding = module._source_encoding
|
||||
self.output_encoding = output_encoding
|
||||
self.encoding_errors = encoding_errors
|
||||
self.disable_unicode = disable_unicode
|
||||
self.bytestring_passthrough = bytestring_passthrough or disable_unicode
|
||||
self.enable_loop = module._enable_loop
|
||||
|
||||
if compat.py3k and disable_unicode:
|
||||
raise exceptions.UnsupportedError(
|
||||
"Mako for Python 3 does not " "support disabling Unicode"
|
||||
)
|
||||
elif output_encoding and disable_unicode:
|
||||
raise exceptions.UnsupportedError(
|
||||
"output_encoding must be set to "
|
||||
"None when disable_unicode is used."
|
||||
)
|
||||
|
||||
self.module = module
|
||||
self.filename = template_filename
|
||||
ModuleInfo(
|
||||
|
@ -616,19 +568,18 @@ class DefTemplate(Template):
|
|||
self.include_error_handler = parent.include_error_handler
|
||||
self.enable_loop = parent.enable_loop
|
||||
self.lookup = parent.lookup
|
||||
self.bytestring_passthrough = parent.bytestring_passthrough
|
||||
|
||||
def get_def(self, name):
|
||||
return self.parent.get_def(name)
|
||||
|
||||
|
||||
class ModuleInfo(object):
|
||||
class ModuleInfo:
|
||||
|
||||
"""Stores information about a module currently loaded into
|
||||
memory, provides reverse lookups of template source, module
|
||||
source code based on a module's identifier.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
_modules = weakref.WeakValueDictionary()
|
||||
|
||||
|
@ -658,9 +609,9 @@ class ModuleInfo(object):
|
|||
r"__M_BEGIN_METADATA(.+?)__M_END_METADATA", module_source, re.S
|
||||
).group(1)
|
||||
source_map = json.loads(source_map)
|
||||
source_map["line_map"] = dict(
|
||||
(int(k), int(v)) for k, v in source_map["line_map"].items()
|
||||
)
|
||||
source_map["line_map"] = {
|
||||
int(k): int(v) for k, v in source_map["line_map"].items()
|
||||
}
|
||||
if full_line_map:
|
||||
f_line_map = source_map["full_line_map"] = []
|
||||
line_map = source_map["line_map"]
|
||||
|
@ -681,28 +632,25 @@ class ModuleInfo(object):
|
|||
|
||||
@property
|
||||
def source(self):
|
||||
if self.template_source is not None:
|
||||
if self.module._source_encoding and not isinstance(
|
||||
self.template_source, compat.text_type
|
||||
):
|
||||
return self.template_source.decode(
|
||||
self.module._source_encoding
|
||||
)
|
||||
else:
|
||||
return self.template_source
|
||||
else:
|
||||
if self.template_source is None:
|
||||
data = util.read_file(self.template_filename)
|
||||
if self.module._source_encoding:
|
||||
return data.decode(self.module._source_encoding)
|
||||
else:
|
||||
return data
|
||||
|
||||
elif self.module._source_encoding and not isinstance(
|
||||
self.template_source, str
|
||||
):
|
||||
return self.template_source.decode(self.module._source_encoding)
|
||||
else:
|
||||
return self.template_source
|
||||
|
||||
|
||||
def _compile(template, text, filename, generate_magic_comment):
|
||||
lexer = template.lexer_cls(
|
||||
text,
|
||||
filename,
|
||||
disable_unicode=template.disable_unicode,
|
||||
input_encoding=template.input_encoding,
|
||||
preprocessor=template.preprocessor,
|
||||
)
|
||||
|
@ -717,7 +665,6 @@ def _compile(template, text, filename, generate_magic_comment):
|
|||
future_imports=template.future_imports,
|
||||
source_encoding=lexer.encoding,
|
||||
generate_magic_comment=generate_magic_comment,
|
||||
disable_unicode=template.disable_unicode,
|
||||
strict_undefined=template.strict_undefined,
|
||||
enable_loop=template.enable_loop,
|
||||
reserved_names=template.reserved_names,
|
||||
|
@ -728,15 +675,10 @@ def _compile(template, text, filename, generate_magic_comment):
|
|||
def _compile_text(template, text, filename):
|
||||
identifier = template.module_id
|
||||
source, lexer = _compile(
|
||||
template,
|
||||
text,
|
||||
filename,
|
||||
generate_magic_comment=template.disable_unicode,
|
||||
template, text, filename, generate_magic_comment=False
|
||||
)
|
||||
|
||||
cid = identifier
|
||||
if not compat.py3k and isinstance(cid, compat.text_type):
|
||||
cid = cid.encode()
|
||||
module = types.ModuleType(cid)
|
||||
code = compile(source, cid, "exec")
|
||||
|
||||
|
@ -750,7 +692,7 @@ def _compile_module_file(template, text, filename, outputpath, module_writer):
|
|||
template, text, filename, generate_magic_comment=True
|
||||
)
|
||||
|
||||
if isinstance(source, compat.text_type):
|
||||
if isinstance(source, str):
|
||||
source = source.encode(lexer.encoding or "ascii")
|
||||
|
||||
if module_writer:
|
||||
|
@ -767,10 +709,7 @@ def _compile_module_file(template, text, filename, outputpath, module_writer):
|
|||
|
||||
|
||||
def _get_module_info_from_callable(callable_):
|
||||
if compat.py3k:
|
||||
return _get_module_info(callable_.__globals__["__name__"])
|
||||
else:
|
||||
return _get_module_info(callable_.func_globals["__name__"])
|
||||
return _get_module_info(callable_.__globals__["__name__"])
|
||||
|
||||
|
||||
def _get_module_info(filename):
|
||||
|
|
0
lib/mako/testing/__init__.py
Normal file
0
lib/mako/testing/__init__.py
Normal file
128
lib/mako/testing/_config.py
Normal file
128
lib/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)
|
167
lib/mako/testing/assertions.py
Normal file
167
lib/mako/testing/assertions.py
Normal file
|
@ -0,0 +1,167 @@
|
|||
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
lib/mako/testing/config.py
Normal file
17
lib/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
lib/mako/testing/exclusions.py
Normal file
80
lib/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)
|
109
lib/mako/testing/fixtures.py
Normal file
109
lib/mako/testing/fixtures.py
Normal file
|
@ -0,0 +1,109 @@
|
|||
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)
|
||||
|
||||
|
||||
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")
|
67
lib/mako/testing/helpers.py
Normal file
67
lib/mako/testing/helpers.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
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 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
|
|
@ -1,10 +1,8 @@
|
|||
# mako/util.py
|
||||
# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
|
||||
# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of Mako and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
from __future__ import absolute_import
|
||||
|
||||
from ast import parse
|
||||
import codecs
|
||||
import collections
|
||||
|
@ -13,7 +11,7 @@ import os
|
|||
import re
|
||||
import timeit
|
||||
|
||||
from mako import compat
|
||||
from .compat import importlib_metadata_get
|
||||
|
||||
|
||||
def update_wrapper(decorated, fn):
|
||||
|
@ -22,7 +20,7 @@ def update_wrapper(decorated, fn):
|
|||
return decorated
|
||||
|
||||
|
||||
class PluginLoader(object):
|
||||
class PluginLoader:
|
||||
def __init__(self, group):
|
||||
self.group = group
|
||||
self.impls = {}
|
||||
|
@ -30,18 +28,17 @@ class PluginLoader(object):
|
|||
def load(self, name):
|
||||
if name in self.impls:
|
||||
return self.impls[name]()
|
||||
else:
|
||||
import pkg_resources
|
||||
|
||||
for impl in pkg_resources.iter_entry_points(self.group, name):
|
||||
for impl in importlib_metadata_get(self.group):
|
||||
if impl.name == name:
|
||||
self.impls[name] = impl.load
|
||||
return impl.load()
|
||||
else:
|
||||
from mako import exceptions
|
||||
|
||||
raise exceptions.RuntimeException(
|
||||
"Can't load plugin %s %s" % (self.group, name)
|
||||
)
|
||||
from mako import exceptions
|
||||
|
||||
raise exceptions.RuntimeException(
|
||||
"Can't load plugin %s %s" % (self.group, name)
|
||||
)
|
||||
|
||||
def register(self, name, modulepath, objname):
|
||||
def load():
|
||||
|
@ -61,7 +58,7 @@ def verify_directory(dir_):
|
|||
while not os.path.exists(dir_):
|
||||
try:
|
||||
tries += 1
|
||||
os.makedirs(dir_, compat.octal("0775"))
|
||||
os.makedirs(dir_, 0o755)
|
||||
except:
|
||||
if tries > 5:
|
||||
raise
|
||||
|
@ -76,7 +73,7 @@ def to_list(x, default=None):
|
|||
return x
|
||||
|
||||
|
||||
class memoized_property(object):
|
||||
class memoized_property:
|
||||
|
||||
"""A read-only @property that is only evaluated once."""
|
||||
|
||||
|
@ -92,7 +89,7 @@ class memoized_property(object):
|
|||
return result
|
||||
|
||||
|
||||
class memoized_instancemethod(object):
|
||||
class memoized_instancemethod:
|
||||
|
||||
"""Decorate a method memoize its return value.
|
||||
|
||||
|
@ -140,19 +137,15 @@ class SetLikeDict(dict):
|
|||
return x
|
||||
|
||||
|
||||
class FastEncodingBuffer(object):
|
||||
class FastEncodingBuffer:
|
||||
|
||||
"""a very rudimentary buffer that is faster than StringIO,
|
||||
but doesn't crash on unicode data like cStringIO."""
|
||||
and supports unicode data."""
|
||||
|
||||
def __init__(self, encoding=None, errors="strict", as_unicode=False):
|
||||
def __init__(self, encoding=None, errors="strict"):
|
||||
self.data = collections.deque()
|
||||
self.encoding = encoding
|
||||
if as_unicode:
|
||||
self.delim = compat.u("")
|
||||
else:
|
||||
self.delim = ""
|
||||
self.as_unicode = as_unicode
|
||||
self.delim = ""
|
||||
self.errors = errors
|
||||
self.write = self.data.append
|
||||
|
||||
|
@ -179,7 +172,7 @@ class LRUCache(dict):
|
|||
is inexact.
|
||||
"""
|
||||
|
||||
class _Item(object):
|
||||
class _Item:
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
@ -203,9 +196,8 @@ class LRUCache(dict):
|
|||
def setdefault(self, key, value):
|
||||
if key in self:
|
||||
return self[key]
|
||||
else:
|
||||
self[key] = value
|
||||
return value
|
||||
self[key] = value
|
||||
return value
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
item = dict.get(self, key)
|
||||
|
@ -295,7 +287,7 @@ def sorted_dict_repr(d):
|
|||
"""
|
||||
keys = list(d.keys())
|
||||
keys.sort()
|
||||
return "{" + ", ".join(["%r: %r" % (k, d[k]) for k in keys]) + "}"
|
||||
return "{" + ", ".join("%r: %r" % (k, d[k]) for k in keys) + "}"
|
||||
|
||||
|
||||
def restore__ast(_ast):
|
||||
|
@ -308,7 +300,7 @@ def restore__ast(_ast):
|
|||
m = compile(
|
||||
"""\
|
||||
def foo(): pass
|
||||
class Bar(object): pass
|
||||
class Bar: pass
|
||||
if False: pass
|
||||
baz = 'mako'
|
||||
1 + 2 - 3 * 4 / 5
|
||||
|
@ -380,12 +372,8 @@ mako in baz not in mako""",
|
|||
|
||||
|
||||
def read_file(path, mode="rb"):
|
||||
fp = open(path, mode)
|
||||
try:
|
||||
data = fp.read()
|
||||
return data
|
||||
finally:
|
||||
fp.close()
|
||||
with open(path, mode) as fp:
|
||||
return fp.read()
|
||||
|
||||
|
||||
def read_python_file(path):
|
||||
|
|
|
@ -11,9 +11,10 @@ if t.TYPE_CHECKING:
|
|||
pass
|
||||
|
||||
|
||||
__version__ = "2.0.1"
|
||||
__version__ = "2.1.1"
|
||||
|
||||
_striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)")
|
||||
_strip_comments_re = re.compile(r"<!--.*?-->")
|
||||
_strip_tags_re = re.compile(r"<.*?>")
|
||||
|
||||
|
||||
def _simple_escaping_wrapper(name: str) -> t.Callable[..., "Markup"]:
|
||||
|
@ -92,19 +93,24 @@ class Markup(str):
|
|||
|
||||
return NotImplemented
|
||||
|
||||
def __mul__(self, num: int) -> "Markup":
|
||||
def __mul__(self, num: "te.SupportsIndex") -> "Markup":
|
||||
if isinstance(num, int):
|
||||
return self.__class__(super().__mul__(num))
|
||||
|
||||
return NotImplemented # type: ignore
|
||||
return NotImplemented
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __mod__(self, arg: t.Any) -> "Markup":
|
||||
if isinstance(arg, tuple):
|
||||
# a tuple of arguments, each wrapped
|
||||
arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
|
||||
else:
|
||||
elif hasattr(type(arg), "__getitem__") and not isinstance(arg, str):
|
||||
# a mapping of arguments, wrapped
|
||||
arg = _MarkupEscapeHelper(arg, self.escape)
|
||||
else:
|
||||
# a single argument, wrapped with the helper and a tuple
|
||||
arg = (_MarkupEscapeHelper(arg, self.escape),)
|
||||
|
||||
return self.__class__(super().__mod__(arg))
|
||||
|
||||
|
@ -153,8 +159,11 @@ class Markup(str):
|
|||
>>> Markup("Main »\t<em>About</em>").striptags()
|
||||
'Main » About'
|
||||
"""
|
||||
stripped = " ".join(_striptags_re.sub("", self).split())
|
||||
return Markup(stripped).unescape()
|
||||
# Use two regexes to avoid ambiguous matches.
|
||||
value = _strip_comments_re.sub("", self)
|
||||
value = _strip_tags_re.sub("", value)
|
||||
value = " ".join(value.split())
|
||||
return Markup(value).unescape()
|
||||
|
||||
@classmethod
|
||||
def escape(cls, s: t.Any) -> "Markup":
|
||||
|
@ -280,9 +289,7 @@ try:
|
|||
from ._speedups import escape as escape
|
||||
from ._speedups import escape_silent as escape_silent
|
||||
from ._speedups import soft_str as soft_str
|
||||
from ._speedups import soft_unicode
|
||||
except ImportError:
|
||||
from ._native import escape as escape
|
||||
from ._native import escape_silent as escape_silent # noqa: F401
|
||||
from ._native import soft_str as soft_str # noqa: F401
|
||||
from ._native import soft_unicode # noqa: F401
|
||||
|
|
|
@ -61,15 +61,3 @@ def soft_str(s: t.Any) -> str:
|
|||
return str(s)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
def soft_unicode(s: t.Any) -> str:
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'soft_unicode' has been renamed to 'soft_str'. The old name"
|
||||
" will be removed in MarkupSafe 2.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return soft_str(s)
|
||||
|
|
320
lib/markupsafe/_speedups.c
Normal file
320
lib/markupsafe/_speedups.c
Normal file
|
@ -0,0 +1,320 @@
|
|||
#include <Python.h>
|
||||
|
||||
static PyObject* markup;
|
||||
|
||||
static int
|
||||
init_constants(void)
|
||||
{
|
||||
PyObject *module;
|
||||
|
||||
/* import markup type so that we can mark the return value */
|
||||
module = PyImport_ImportModule("markupsafe");
|
||||
if (!module)
|
||||
return 0;
|
||||
markup = PyObject_GetAttrString(module, "Markup");
|
||||
Py_DECREF(module);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define GET_DELTA(inp, inp_end, delta) \
|
||||
while (inp < inp_end) { \
|
||||
switch (*inp++) { \
|
||||
case '"': \
|
||||
case '\'': \
|
||||
case '&': \
|
||||
delta += 4; \
|
||||
break; \
|
||||
case '<': \
|
||||
case '>': \
|
||||
delta += 3; \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DO_ESCAPE(inp, inp_end, outp) \
|
||||
{ \
|
||||
Py_ssize_t ncopy = 0; \
|
||||
while (inp < inp_end) { \
|
||||
switch (*inp) { \
|
||||
case '"': \
|
||||
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
|
||||
outp += ncopy; ncopy = 0; \
|
||||
*outp++ = '&'; \
|
||||
*outp++ = '#'; \
|
||||
*outp++ = '3'; \
|
||||
*outp++ = '4'; \
|
||||
*outp++ = ';'; \
|
||||
break; \
|
||||
case '\'': \
|
||||
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
|
||||
outp += ncopy; ncopy = 0; \
|
||||
*outp++ = '&'; \
|
||||
*outp++ = '#'; \
|
||||
*outp++ = '3'; \
|
||||
*outp++ = '9'; \
|
||||
*outp++ = ';'; \
|
||||
break; \
|
||||
case '&': \
|
||||
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
|
||||
outp += ncopy; ncopy = 0; \
|
||||
*outp++ = '&'; \
|
||||
*outp++ = 'a'; \
|
||||
*outp++ = 'm'; \
|
||||
*outp++ = 'p'; \
|
||||
*outp++ = ';'; \
|
||||
break; \
|
||||
case '<': \
|
||||
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
|
||||
outp += ncopy; ncopy = 0; \
|
||||
*outp++ = '&'; \
|
||||
*outp++ = 'l'; \
|
||||
*outp++ = 't'; \
|
||||
*outp++ = ';'; \
|
||||
break; \
|
||||
case '>': \
|
||||
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
|
||||
outp += ncopy; ncopy = 0; \
|
||||
*outp++ = '&'; \
|
||||
*outp++ = 'g'; \
|
||||
*outp++ = 't'; \
|
||||
*outp++ = ';'; \
|
||||
break; \
|
||||
default: \
|
||||
ncopy++; \
|
||||
} \
|
||||
inp++; \
|
||||
} \
|
||||
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
escape_unicode_kind1(PyUnicodeObject *in)
|
||||
{
|
||||
Py_UCS1 *inp = PyUnicode_1BYTE_DATA(in);
|
||||
Py_UCS1 *inp_end = inp + PyUnicode_GET_LENGTH(in);
|
||||
Py_UCS1 *outp;
|
||||
PyObject *out;
|
||||
Py_ssize_t delta = 0;
|
||||
|
||||
GET_DELTA(inp, inp_end, delta);
|
||||
if (!delta) {
|
||||
Py_INCREF(in);
|
||||
return (PyObject*)in;
|
||||
}
|
||||
|
||||
out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta,
|
||||
PyUnicode_IS_ASCII(in) ? 127 : 255);
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
||||
inp = PyUnicode_1BYTE_DATA(in);
|
||||
outp = PyUnicode_1BYTE_DATA(out);
|
||||
DO_ESCAPE(inp, inp_end, outp);
|
||||
return out;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
escape_unicode_kind2(PyUnicodeObject *in)
|
||||
{
|
||||
Py_UCS2 *inp = PyUnicode_2BYTE_DATA(in);
|
||||
Py_UCS2 *inp_end = inp + PyUnicode_GET_LENGTH(in);
|
||||
Py_UCS2 *outp;
|
||||
PyObject *out;
|
||||
Py_ssize_t delta = 0;
|
||||
|
||||
GET_DELTA(inp, inp_end, delta);
|
||||
if (!delta) {
|
||||
Py_INCREF(in);
|
||||
return (PyObject*)in;
|
||||
}
|
||||
|
||||
out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 65535);
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
||||
inp = PyUnicode_2BYTE_DATA(in);
|
||||
outp = PyUnicode_2BYTE_DATA(out);
|
||||
DO_ESCAPE(inp, inp_end, outp);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
escape_unicode_kind4(PyUnicodeObject *in)
|
||||
{
|
||||
Py_UCS4 *inp = PyUnicode_4BYTE_DATA(in);
|
||||
Py_UCS4 *inp_end = inp + PyUnicode_GET_LENGTH(in);
|
||||
Py_UCS4 *outp;
|
||||
PyObject *out;
|
||||
Py_ssize_t delta = 0;
|
||||
|
||||
GET_DELTA(inp, inp_end, delta);
|
||||
if (!delta) {
|
||||
Py_INCREF(in);
|
||||
return (PyObject*)in;
|
||||
}
|
||||
|
||||
out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 1114111);
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
||||
inp = PyUnicode_4BYTE_DATA(in);
|
||||
outp = PyUnicode_4BYTE_DATA(out);
|
||||
DO_ESCAPE(inp, inp_end, outp);
|
||||
return out;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
escape_unicode(PyUnicodeObject *in)
|
||||
{
|
||||
if (PyUnicode_READY(in))
|
||||
return NULL;
|
||||
|
||||
switch (PyUnicode_KIND(in)) {
|
||||
case PyUnicode_1BYTE_KIND:
|
||||
return escape_unicode_kind1(in);
|
||||
case PyUnicode_2BYTE_KIND:
|
||||
return escape_unicode_kind2(in);
|
||||
case PyUnicode_4BYTE_KIND:
|
||||
return escape_unicode_kind4(in);
|
||||
}
|
||||
assert(0); /* shouldn't happen */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
escape(PyObject *self, PyObject *text)
|
||||
{
|
||||
static PyObject *id_html;
|
||||
PyObject *s = NULL, *rv = NULL, *html;
|
||||
|
||||
if (id_html == NULL) {
|
||||
id_html = PyUnicode_InternFromString("__html__");
|
||||
if (id_html == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* we don't have to escape integers, bools or floats */
|
||||
if (PyLong_CheckExact(text) ||
|
||||
PyFloat_CheckExact(text) || PyBool_Check(text) ||
|
||||
text == Py_None)
|
||||
return PyObject_CallFunctionObjArgs(markup, text, NULL);
|
||||
|
||||
/* if the object has an __html__ method that performs the escaping */
|
||||
html = PyObject_GetAttr(text ,id_html);
|
||||
if (html) {
|
||||
s = PyObject_CallObject(html, NULL);
|
||||
Py_DECREF(html);
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
/* Convert to Markup object */
|
||||
rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
|
||||
Py_DECREF(s);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* otherwise make the object unicode if it isn't, then escape */
|
||||
PyErr_Clear();
|
||||
if (!PyUnicode_Check(text)) {
|
||||
PyObject *unicode = PyObject_Str(text);
|
||||
if (!unicode)
|
||||
return NULL;
|
||||
s = escape_unicode((PyUnicodeObject*)unicode);
|
||||
Py_DECREF(unicode);
|
||||
}
|
||||
else
|
||||
s = escape_unicode((PyUnicodeObject*)text);
|
||||
|
||||
/* convert the unicode string into a markup object. */
|
||||
rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
|
||||
Py_DECREF(s);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
escape_silent(PyObject *self, PyObject *text)
|
||||
{
|
||||
if (text != Py_None)
|
||||
return escape(self, text);
|
||||
return PyObject_CallFunctionObjArgs(markup, NULL);
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
soft_str(PyObject *self, PyObject *s)
|
||||
{
|
||||
if (!PyUnicode_Check(s))
|
||||
return PyObject_Str(s);
|
||||
Py_INCREF(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{
|
||||
"escape",
|
||||
(PyCFunction)escape,
|
||||
METH_O,
|
||||
"Replace the characters ``&``, ``<``, ``>``, ``'``, and ``\"`` in"
|
||||
" the string with HTML-safe sequences. Use this if you need to display"
|
||||
" text that might contain such characters in HTML.\n\n"
|
||||
"If the object has an ``__html__`` method, it is called and the"
|
||||
" return value is assumed to already be safe for HTML.\n\n"
|
||||
":param s: An object to be converted to a string and escaped.\n"
|
||||
":return: A :class:`Markup` string with the escaped text.\n"
|
||||
},
|
||||
{
|
||||
"escape_silent",
|
||||
(PyCFunction)escape_silent,
|
||||
METH_O,
|
||||
"Like :func:`escape` but treats ``None`` as the empty string."
|
||||
" Useful with optional values, as otherwise you get the string"
|
||||
" ``'None'`` when the value is ``None``.\n\n"
|
||||
">>> escape(None)\n"
|
||||
"Markup('None')\n"
|
||||
">>> escape_silent(None)\n"
|
||||
"Markup('')\n"
|
||||
},
|
||||
{
|
||||
"soft_str",
|
||||
(PyCFunction)soft_str,
|
||||
METH_O,
|
||||
"Convert an object to a string if it isn't already. This preserves"
|
||||
" a :class:`Markup` string rather than converting it back to a basic"
|
||||
" string, so it will still be marked as safe and won't be escaped"
|
||||
" again.\n\n"
|
||||
">>> value = escape(\"<User 1>\")\n"
|
||||
">>> value\n"
|
||||
"Markup('<User 1>')\n"
|
||||
">>> escape(str(value))\n"
|
||||
"Markup('&lt;User 1&gt;')\n"
|
||||
">>> escape(soft_str(value))\n"
|
||||
"Markup('<User 1>')\n"
|
||||
},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static struct PyModuleDef module_definition = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"markupsafe._speedups",
|
||||
NULL,
|
||||
-1,
|
||||
module_methods,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__speedups(void)
|
||||
{
|
||||
if (!init_constants())
|
||||
return NULL;
|
||||
|
||||
return PyModule_Create(&module_definition);
|
||||
}
|
|
@ -18,11 +18,12 @@ gntp==1.0.3
|
|||
html5lib==1.1
|
||||
httpagentparser==1.9.2
|
||||
idna==3.3
|
||||
importlib-metadata==4.11.3
|
||||
importlib-resources==5.6.0
|
||||
git+https://github.com/Tautulli/ipwhois.git@master#egg=ipwhois
|
||||
IPy==1.01
|
||||
Mako==1.1.6
|
||||
MarkupSafe==2.0.1
|
||||
Mako==1.2.0
|
||||
MarkupSafe==2.1.1
|
||||
musicbrainzngs==0.7.1
|
||||
packaging==21.3
|
||||
paho-mqtt==1.6.1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue