mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-06 21:21: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
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# 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
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# 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 UAdd
|
||||||
from _ast import USub
|
from _ast import USub
|
||||||
|
|
||||||
from mako.compat import arg_stringname
|
|
||||||
|
|
||||||
BOOLOP_SYMBOLS = {And: "and", Or: "or"}
|
BOOLOP_SYMBOLS = {And: "and", Or: "or"}
|
||||||
|
|
||||||
|
@ -94,9 +93,7 @@ def parse(expr, filename="<unknown>", mode="exec"):
|
||||||
|
|
||||||
def iter_fields(node):
|
def iter_fields(node):
|
||||||
"""Iterate over all fields of a node, only yielding existing fields."""
|
"""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:
|
for field in node._fields:
|
||||||
try:
|
try:
|
||||||
yield field, getattr(node, field)
|
yield field, getattr(node, field)
|
||||||
|
@ -104,7 +101,7 @@ def iter_fields(node):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NodeVisitor(object):
|
class NodeVisitor:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Walks the abstract syntax tree and call visitor functions for every node
|
Walks the abstract syntax tree and call visitor functions for every node
|
||||||
|
@ -266,10 +263,10 @@ class SourceGenerator(NodeVisitor):
|
||||||
self.visit(default)
|
self.visit(default)
|
||||||
if node.vararg is not None:
|
if node.vararg is not None:
|
||||||
write_comma()
|
write_comma()
|
||||||
self.write("*" + arg_stringname(node.vararg))
|
self.write("*" + node.vararg.arg)
|
||||||
if node.kwarg is not None:
|
if node.kwarg is not None:
|
||||||
write_comma()
|
write_comma()
|
||||||
self.write("**" + arg_stringname(node.kwarg))
|
self.write("**" + node.kwarg.arg)
|
||||||
|
|
||||||
def decorators(self, node):
|
def decorators(self, node):
|
||||||
for decorator in node.decorator_list:
|
for decorator in node.decorator_list:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# mako/ast.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# 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
|
import re
|
||||||
|
|
||||||
from mako import compat
|
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
from mako import pyparser
|
from mako import pyparser
|
||||||
|
|
||||||
|
|
||||||
class PythonCode(object):
|
class PythonCode:
|
||||||
|
|
||||||
"""represents information about a string containing Python code"""
|
"""represents information about a string containing Python code"""
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ class PythonCode(object):
|
||||||
# - AST is less likely to break with version changes
|
# - AST is less likely to break with version changes
|
||||||
# (for example, the behavior of co_names changed a little bit
|
# (for example, the behavior of co_names changed a little bit
|
||||||
# in python version 2.5)
|
# in python version 2.5)
|
||||||
if isinstance(code, compat.string_types):
|
if isinstance(code, str):
|
||||||
expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
|
expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
|
||||||
else:
|
else:
|
||||||
expr = code
|
expr = code
|
||||||
|
@ -48,7 +47,7 @@ class PythonCode(object):
|
||||||
f.visit(expr)
|
f.visit(expr)
|
||||||
|
|
||||||
|
|
||||||
class ArgumentList(object):
|
class ArgumentList:
|
||||||
|
|
||||||
"""parses a fragment of code as a comma-separated list of expressions"""
|
"""parses a fragment of code as a comma-separated list of expressions"""
|
||||||
|
|
||||||
|
@ -57,7 +56,7 @@ class ArgumentList(object):
|
||||||
self.args = []
|
self.args = []
|
||||||
self.declared_identifiers = set()
|
self.declared_identifiers = set()
|
||||||
self.undeclared_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 re.match(r"\S", code) and not re.match(r",\s*$", code):
|
||||||
# if theres text and no trailing comma, insure its parsed
|
# if theres text and no trailing comma, insure its parsed
|
||||||
# as a tuple by adding a trailing comma
|
# as a tuple by adding a trailing comma
|
||||||
|
@ -88,7 +87,7 @@ class PythonFragment(PythonCode):
|
||||||
if not m:
|
if not m:
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"Fragment '%s' is not a partial control statement" % code,
|
"Fragment '%s' is not a partial control statement" % code,
|
||||||
**exception_kwargs
|
**exception_kwargs,
|
||||||
)
|
)
|
||||||
if m.group(3):
|
if m.group(3):
|
||||||
code = code[: m.start(3)]
|
code = code[: m.start(3)]
|
||||||
|
@ -97,7 +96,7 @@ class PythonFragment(PythonCode):
|
||||||
code = code + "pass"
|
code = code + "pass"
|
||||||
elif keyword == "try":
|
elif keyword == "try":
|
||||||
code = code + "pass\nexcept:pass"
|
code = code + "pass\nexcept:pass"
|
||||||
elif keyword == "elif" or keyword == "else":
|
elif keyword in ["elif", "else"]:
|
||||||
code = "if False:pass\n" + code + "pass"
|
code = "if False:pass\n" + code + "pass"
|
||||||
elif keyword == "except":
|
elif keyword == "except":
|
||||||
code = "try:pass\n" + code + "pass"
|
code = "try:pass\n" + code + "pass"
|
||||||
|
@ -106,12 +105,12 @@ class PythonFragment(PythonCode):
|
||||||
else:
|
else:
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"Unsupported control keyword: '%s'" % keyword,
|
"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"""
|
"""function declaration"""
|
||||||
|
|
||||||
|
@ -124,13 +123,13 @@ class FunctionDecl(object):
|
||||||
if not hasattr(self, "funcname"):
|
if not hasattr(self, "funcname"):
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"Code '%s' is not a function declaration" % code,
|
"Code '%s' is not a function declaration" % code,
|
||||||
**exception_kwargs
|
**exception_kwargs,
|
||||||
)
|
)
|
||||||
if not allow_kwargs and self.kwargs:
|
if not allow_kwargs and self.kwargs:
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"'**%s' keyword argument not allowed here"
|
"'**%s' keyword argument not allowed here"
|
||||||
% self.kwargnames[-1],
|
% self.kwargnames[-1],
|
||||||
**exception_kwargs
|
**exception_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_argument_expressions(self, as_call=False):
|
def get_argument_expressions(self, as_call=False):
|
||||||
|
@ -200,6 +199,4 @@ class FunctionArgs(FunctionDecl):
|
||||||
"""the argument portion of a function declaration"""
|
"""the argument portion of a function declaration"""
|
||||||
|
|
||||||
def __init__(self, code, **kwargs):
|
def __init__(self, code, **kwargs):
|
||||||
super(FunctionArgs, self).__init__(
|
super().__init__("def ANON(%s):pass" % code, **kwargs)
|
||||||
"def ANON(%s):pass" % code, **kwargs
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
# mako/cache.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
||||||
from mako import compat
|
|
||||||
from mako import util
|
from mako import util
|
||||||
|
|
||||||
_cache_plugins = util.PluginLoader("mako.cache")
|
_cache_plugins = util.PluginLoader("mako.cache")
|
||||||
|
@ -13,7 +12,7 @@ register_plugin = _cache_plugins.register
|
||||||
register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl")
|
register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl")
|
||||||
|
|
||||||
|
|
||||||
class Cache(object):
|
class Cache:
|
||||||
|
|
||||||
"""Represents a data content cache made available to the module
|
"""Represents a data content cache made available to the module
|
||||||
space of a specific :class:`.Template` object.
|
space of a specific :class:`.Template` object.
|
||||||
|
@ -66,7 +65,7 @@ class Cache(object):
|
||||||
def __init__(self, template, *args):
|
def __init__(self, template, *args):
|
||||||
# check for a stale template calling the
|
# check for a stale template calling the
|
||||||
# constructor
|
# constructor
|
||||||
if isinstance(template, compat.string_types) and args:
|
if isinstance(template, str) and args:
|
||||||
return
|
return
|
||||||
self.template = template
|
self.template = template
|
||||||
self.id = template.module.__name__
|
self.id = template.module.__name__
|
||||||
|
@ -181,7 +180,7 @@ class Cache(object):
|
||||||
return tmpl_kw
|
return tmpl_kw
|
||||||
|
|
||||||
|
|
||||||
class CacheImpl(object):
|
class CacheImpl:
|
||||||
|
|
||||||
"""Provide a cache implementation for use by :class:`.Cache`."""
|
"""Provide a cache implementation for use by :class:`.Cache`."""
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
# mako/cmd.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
import io
|
|
||||||
from os.path import dirname
|
from os.path import dirname
|
||||||
from os.path import isfile
|
from os.path import isfile
|
||||||
import sys
|
import sys
|
||||||
|
@ -85,16 +84,14 @@ def cmdline(argv=None):
|
||||||
except:
|
except:
|
||||||
_exit()
|
_exit()
|
||||||
|
|
||||||
kw = dict([varsplit(var) for var in options.var])
|
kw = dict(varsplit(var) for var in options.var)
|
||||||
try:
|
try:
|
||||||
rendered = template.render(**kw)
|
rendered = template.render(**kw)
|
||||||
except:
|
except:
|
||||||
_exit()
|
_exit()
|
||||||
else:
|
else:
|
||||||
if output_file:
|
if output_file:
|
||||||
io.open(output_file, "wt", encoding=output_encoding).write(
|
open(output_file, "wt", encoding=output_encoding).write(rendered)
|
||||||
rendered
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
sys.stdout.write(rendered)
|
sys.stdout.write(rendered)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# mako/codegen.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
@ -12,7 +12,6 @@ import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from mako import ast
|
from mako import ast
|
||||||
from mako import compat
|
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
from mako import filters
|
from mako import filters
|
||||||
from mako import parsetree
|
from mako import parsetree
|
||||||
|
@ -25,8 +24,8 @@ MAGIC_NUMBER = 10
|
||||||
# names which are hardwired into the
|
# names which are hardwired into the
|
||||||
# template and are not accessed via the
|
# template and are not accessed via the
|
||||||
# context itself
|
# context itself
|
||||||
TOPLEVEL_DECLARED = set(["UNDEFINED", "STOP_RENDERING"])
|
TOPLEVEL_DECLARED = {"UNDEFINED", "STOP_RENDERING"}
|
||||||
RESERVED_NAMES = set(["context", "loop"]).union(TOPLEVEL_DECLARED)
|
RESERVED_NAMES = {"context", "loop"}.union(TOPLEVEL_DECLARED)
|
||||||
|
|
||||||
|
|
||||||
def compile( # noqa
|
def compile( # noqa
|
||||||
|
@ -39,7 +38,6 @@ def compile( # noqa
|
||||||
future_imports=None,
|
future_imports=None,
|
||||||
source_encoding=None,
|
source_encoding=None,
|
||||||
generate_magic_comment=True,
|
generate_magic_comment=True,
|
||||||
disable_unicode=False,
|
|
||||||
strict_undefined=False,
|
strict_undefined=False,
|
||||||
enable_loop=True,
|
enable_loop=True,
|
||||||
reserved_names=frozenset(),
|
reserved_names=frozenset(),
|
||||||
|
@ -47,13 +45,6 @@ def compile( # noqa
|
||||||
"""Generate module source code given a parsetree node,
|
"""Generate module source code given a parsetree node,
|
||||||
uri, and optional source filename"""
|
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)
|
|
||||||
|
|
||||||
buf = util.FastEncodingBuffer()
|
buf = util.FastEncodingBuffer()
|
||||||
|
|
||||||
printer = PythonPrinter(buf)
|
printer = PythonPrinter(buf)
|
||||||
|
@ -68,7 +59,6 @@ def compile( # noqa
|
||||||
future_imports,
|
future_imports,
|
||||||
source_encoding,
|
source_encoding,
|
||||||
generate_magic_comment,
|
generate_magic_comment,
|
||||||
disable_unicode,
|
|
||||||
strict_undefined,
|
strict_undefined,
|
||||||
enable_loop,
|
enable_loop,
|
||||||
reserved_names,
|
reserved_names,
|
||||||
|
@ -78,7 +68,7 @@ def compile( # noqa
|
||||||
return buf.getvalue()
|
return buf.getvalue()
|
||||||
|
|
||||||
|
|
||||||
class _CompileContext(object):
|
class _CompileContext:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
uri,
|
uri,
|
||||||
|
@ -89,7 +79,6 @@ class _CompileContext(object):
|
||||||
future_imports,
|
future_imports,
|
||||||
source_encoding,
|
source_encoding,
|
||||||
generate_magic_comment,
|
generate_magic_comment,
|
||||||
disable_unicode,
|
|
||||||
strict_undefined,
|
strict_undefined,
|
||||||
enable_loop,
|
enable_loop,
|
||||||
reserved_names,
|
reserved_names,
|
||||||
|
@ -102,13 +91,12 @@ class _CompileContext(object):
|
||||||
self.future_imports = future_imports
|
self.future_imports = future_imports
|
||||||
self.source_encoding = source_encoding
|
self.source_encoding = source_encoding
|
||||||
self.generate_magic_comment = generate_magic_comment
|
self.generate_magic_comment = generate_magic_comment
|
||||||
self.disable_unicode = disable_unicode
|
|
||||||
self.strict_undefined = strict_undefined
|
self.strict_undefined = strict_undefined
|
||||||
self.enable_loop = enable_loop
|
self.enable_loop = enable_loop
|
||||||
self.reserved_names = reserved_names
|
self.reserved_names = reserved_names
|
||||||
|
|
||||||
|
|
||||||
class _GenerateRenderMethod(object):
|
class _GenerateRenderMethod:
|
||||||
|
|
||||||
"""A template visitor object which generates the
|
"""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
|
self.compiler.pagetag = None
|
||||||
|
|
||||||
class FindTopLevel(object):
|
class FindTopLevel:
|
||||||
def visitInheritTag(s, node):
|
def visitInheritTag(s, node):
|
||||||
inherit.append(node)
|
inherit.append(node)
|
||||||
|
|
||||||
|
@ -392,7 +380,7 @@ class _GenerateRenderMethod(object):
|
||||||
identifiers = self.compiler.identifiers.branch(node)
|
identifiers = self.compiler.identifiers.branch(node)
|
||||||
self.in_def = True
|
self.in_def = True
|
||||||
|
|
||||||
class NSDefVisitor(object):
|
class NSDefVisitor:
|
||||||
def visitDefTag(s, node):
|
def visitDefTag(s, node):
|
||||||
s.visitDefOrBase(node)
|
s.visitDefOrBase(node)
|
||||||
|
|
||||||
|
@ -404,7 +392,7 @@ class _GenerateRenderMethod(object):
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"Can't put anonymous blocks inside "
|
"Can't put anonymous blocks inside "
|
||||||
"<%namespace>",
|
"<%namespace>",
|
||||||
**node.exception_kwargs
|
**node.exception_kwargs,
|
||||||
)
|
)
|
||||||
self.write_inline_def(node, identifiers, nested=False)
|
self.write_inline_def(node, identifiers, nested=False)
|
||||||
export.append(node.funcname)
|
export.append(node.funcname)
|
||||||
|
@ -481,7 +469,7 @@ class _GenerateRenderMethod(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# collection of all defs available to us in this scope
|
# 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()
|
to_write = set()
|
||||||
|
|
||||||
# write "context.get()" for all variables we are going to
|
# write "context.get()" for all variables we are going to
|
||||||
|
@ -794,8 +782,6 @@ class _GenerateRenderMethod(object):
|
||||||
def locate_encode(name):
|
def locate_encode(name):
|
||||||
if re.match(r"decode\..+", name):
|
if re.match(r"decode\..+", name):
|
||||||
return "filters." + name
|
return "filters." + name
|
||||||
elif self.compiler.disable_unicode:
|
|
||||||
return filters.NON_UNICODE_ESCAPES.get(name, name)
|
|
||||||
else:
|
else:
|
||||||
return filters.DEFAULT_ESCAPES.get(name, name)
|
return filters.DEFAULT_ESCAPES.get(name, name)
|
||||||
|
|
||||||
|
@ -859,11 +845,11 @@ class _GenerateRenderMethod(object):
|
||||||
# and end control lines, and
|
# and end control lines, and
|
||||||
# 3) any control line with no content other than comments
|
# 3) any control line with no content other than comments
|
||||||
if not children or (
|
if not children or (
|
||||||
compat.all(
|
all(
|
||||||
isinstance(c, (parsetree.Comment, parsetree.ControlLine))
|
isinstance(c, (parsetree.Comment, parsetree.ControlLine))
|
||||||
for c in children
|
for c in children
|
||||||
)
|
)
|
||||||
and compat.all(
|
and all(
|
||||||
(node.is_ternary(c.keyword) or c.isend)
|
(node.is_ternary(c.keyword) or c.isend)
|
||||||
for c in children
|
for c in children
|
||||||
if isinstance(c, parsetree.ControlLine)
|
if isinstance(c, parsetree.ControlLine)
|
||||||
|
@ -969,7 +955,7 @@ class _GenerateRenderMethod(object):
|
||||||
|
|
||||||
self.identifier_stack.append(body_identifiers)
|
self.identifier_stack.append(body_identifiers)
|
||||||
|
|
||||||
class DefVisitor(object):
|
class DefVisitor:
|
||||||
def visitDefTag(s, node):
|
def visitDefTag(s, node):
|
||||||
s.visitDefOrBase(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."""
|
"""tracks the status of identifier names as template code is rendered."""
|
||||||
|
|
||||||
|
@ -1170,7 +1156,7 @@ class _Identifiers(object):
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"%%def or %%block named '%s' already "
|
"%%def or %%block named '%s' already "
|
||||||
"exists in this template." % node.funcname,
|
"exists in this template." % node.funcname,
|
||||||
**node.exception_kwargs
|
**node.exception_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def visitDefTag(self, node):
|
def visitDefTag(self, node):
|
||||||
|
@ -1200,7 +1186,7 @@ class _Identifiers(object):
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"Named block '%s' not allowed inside of def '%s'"
|
"Named block '%s' not allowed inside of def '%s'"
|
||||||
% (node.name, self.node.name),
|
% (node.name, self.node.name),
|
||||||
**node.exception_kwargs
|
**node.exception_kwargs,
|
||||||
)
|
)
|
||||||
elif isinstance(
|
elif isinstance(
|
||||||
self.node, (parsetree.CallTag, parsetree.CallNamespaceTag)
|
self.node, (parsetree.CallTag, parsetree.CallNamespaceTag)
|
||||||
|
@ -1208,7 +1194,7 @@ class _Identifiers(object):
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"Named block '%s' not allowed inside of <%%call> tag"
|
"Named block '%s' not allowed inside of <%%call> tag"
|
||||||
% (node.name,),
|
% (node.name,),
|
||||||
**node.exception_kwargs
|
**node.exception_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
for ident in node.undeclared_identifiers():
|
for ident in node.undeclared_identifiers():
|
||||||
|
@ -1293,7 +1279,7 @@ def mangle_mako_loop(node, printer):
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
class LoopVariable(object):
|
class LoopVariable:
|
||||||
|
|
||||||
"""A node visitor which looks for the name 'loop' within undeclared
|
"""A node visitor which looks for the name 'loop' within undeclared
|
||||||
identifiers."""
|
identifiers."""
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
# mako/compat.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
from importlib import util
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
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")
|
win32 = sys.platform.startswith("win")
|
||||||
pypy = hasattr(sys, "pypy_version_info")
|
pypy = hasattr(sys, "pypy_version_info")
|
||||||
|
py38 = sys.version_info >= (3, 8)
|
||||||
|
|
||||||
ArgSpec = collections.namedtuple(
|
ArgSpec = collections.namedtuple(
|
||||||
"ArgSpec", ["args", "varargs", "keywords", "defaults"]
|
"ArgSpec", ["args", "varargs", "keywords", "defaults"]
|
||||||
|
@ -26,15 +24,15 @@ def inspect_getargspec(func):
|
||||||
if inspect.ismethod(func):
|
if inspect.ismethod(func):
|
||||||
func = func.__func__
|
func = func.__func__
|
||||||
if not inspect.isfunction(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__
|
co = func.__code__
|
||||||
if not inspect.iscode(co):
|
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
|
nargs = co.co_argcount
|
||||||
names = co.co_varnames
|
names = co.co_varnames
|
||||||
nkwargs = co.co_kwonlyargcount if py3k else 0
|
nkwargs = co.co_kwonlyargcount
|
||||||
args = list(names[:nargs])
|
args = list(names[:nargs])
|
||||||
|
|
||||||
nargs += nkwargs
|
nargs += nkwargs
|
||||||
|
@ -49,129 +47,30 @@ def inspect_getargspec(func):
|
||||||
return ArgSpec(args, varargs, varkw, func.__defaults__)
|
return ArgSpec(args, varargs, varkw, func.__defaults__)
|
||||||
|
|
||||||
|
|
||||||
if py3k:
|
def load_module(module_id, path):
|
||||||
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)
|
spec = util.spec_from_file_location(module_id, path)
|
||||||
module = util.module_from_spec(spec)
|
module = util.module_from_spec(spec)
|
||||||
spec.loader.exec_module(module)
|
spec.loader.exec_module(module)
|
||||||
return 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 exception_as():
|
def exception_as():
|
||||||
return sys.exc_info()[1]
|
return sys.exc_info()[1]
|
||||||
|
|
||||||
|
|
||||||
all = all # noqa
|
|
||||||
|
|
||||||
|
|
||||||
def exception_name(exc):
|
def exception_name(exc):
|
||||||
return exc.__class__.__name__
|
return exc.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
################################################
|
if py38:
|
||||||
# cross-compatible metaclass implementation
|
from importlib import metadata as importlib_metadata
|
||||||
# Copyright (c) 2010-2012 Benjamin Peterson
|
else:
|
||||||
def with_metaclass(meta, base=object):
|
import importlib_metadata # noqa
|
||||||
"""Create a base class with a metaclass."""
|
|
||||||
return meta("%sBase" % meta.__name__, (base,), {})
|
|
||||||
|
|
||||||
|
|
||||||
################################################
|
def importlib_metadata_get(group):
|
||||||
|
ep = importlib_metadata.entry_points()
|
||||||
|
if hasattr(ep, "select"):
|
||||||
def arg_stringname(func_arg):
|
return ep.select(group=group)
|
||||||
"""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
|
|
||||||
else:
|
else:
|
||||||
return str(func_arg)
|
return ep.get(group, ())
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# mako/exceptions.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
@ -68,7 +68,7 @@ class TopLevelLookupException(TemplateLookupException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RichTraceback(object):
|
class RichTraceback:
|
||||||
|
|
||||||
"""Pull the current exception from the ``sys`` traceback and extracts
|
"""Pull the current exception from the ``sys`` traceback and extracts
|
||||||
Mako-specific template information.
|
Mako-specific template information.
|
||||||
|
@ -106,7 +106,7 @@ class RichTraceback(object):
|
||||||
def _init_message(self):
|
def _init_message(self):
|
||||||
"""Find a unicode representation of self.error"""
|
"""Find a unicode representation of self.error"""
|
||||||
try:
|
try:
|
||||||
self.message = compat.text_type(self.error)
|
self.message = str(self.error)
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
try:
|
try:
|
||||||
self.message = str(self.error)
|
self.message = str(self.error)
|
||||||
|
@ -114,8 +114,8 @@ class RichTraceback(object):
|
||||||
# Fallback to args as neither unicode nor
|
# Fallback to args as neither unicode nor
|
||||||
# str(Exception(u'\xe6')) work in Python < 2.6
|
# str(Exception(u'\xe6')) work in Python < 2.6
|
||||||
self.message = self.error.args[0]
|
self.message = self.error.args[0]
|
||||||
if not isinstance(self.message, compat.text_type):
|
if not isinstance(self.message, str):
|
||||||
self.message = compat.text_type(self.message, "ascii", "replace")
|
self.message = str(self.message, "ascii", "replace")
|
||||||
|
|
||||||
def _get_reformatted_records(self, records):
|
def _get_reformatted_records(self, records):
|
||||||
for rec in records:
|
for rec in records:
|
||||||
|
@ -139,8 +139,7 @@ class RichTraceback(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reverse_traceback(self):
|
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))
|
return list(self._get_reformatted_records(self.reverse_records))
|
||||||
|
|
||||||
|
@ -170,17 +169,6 @@ class RichTraceback(object):
|
||||||
)
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# A normal .py file (not a Template)
|
# 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(
|
new_trcback.append(
|
||||||
(
|
(
|
||||||
filename,
|
filename,
|
||||||
|
@ -236,13 +224,12 @@ class RichTraceback(object):
|
||||||
if new_trcback:
|
if new_trcback:
|
||||||
try:
|
try:
|
||||||
# A normal .py file (not a Template)
|
# A normal .py file (not a Template)
|
||||||
fp = open(new_trcback[-1][0], "rb")
|
with open(new_trcback[-1][0], "rb") as fp:
|
||||||
encoding = util.parse_encoding(fp)
|
encoding = util.parse_encoding(fp)
|
||||||
if compat.py3k and not encoding:
|
if not encoding:
|
||||||
encoding = "utf-8"
|
encoding = "utf-8"
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
self.source = fp.read()
|
self.source = fp.read()
|
||||||
fp.close()
|
|
||||||
if encoding:
|
if encoding:
|
||||||
self.source = self.source.decode(encoding)
|
self.source = self.source.decode(encoding)
|
||||||
except IOError:
|
except IOError:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ext/autohandler.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# ext/babelplugin.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# 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 babel.messages.extract import extract_python
|
||||||
|
|
||||||
from mako.ext.extract import MessageExtractor
|
from mako.ext.extract import MessageExtractor
|
||||||
|
@ -15,12 +15,12 @@ class BabelMakoExtractor(MessageExtractor):
|
||||||
self.keywords = keywords
|
self.keywords = keywords
|
||||||
self.options = options
|
self.options = options
|
||||||
self.config = {
|
self.config = {
|
||||||
"comment-tags": u" ".join(comment_tags),
|
"comment-tags": " ".join(comment_tags),
|
||||||
"encoding": options.get(
|
"encoding": options.get(
|
||||||
"input_encoding", options.get("encoding", None)
|
"input_encoding", options.get("encoding", None)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
super(BabelMakoExtractor, self).__init__()
|
super().__init__()
|
||||||
|
|
||||||
def __call__(self, fileobj):
|
def __call__(self, fileobj):
|
||||||
return self.process_file(fileobj)
|
return self.process_file(fileobj)
|
||||||
|
@ -54,5 +54,4 @@ def extract(fileobj, keywords, comment_tags, options):
|
||||||
:rtype: ``iterator``
|
:rtype: ``iterator``
|
||||||
"""
|
"""
|
||||||
extractor = BabelMakoExtractor(keywords, comment_tags, options)
|
extractor = BabelMakoExtractor(keywords, comment_tags, options)
|
||||||
for message in extractor(fileobj):
|
yield from extractor(fileobj)
|
||||||
yield message
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ext/beaker_cache.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# 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"]
|
_beaker_cache = cache.template.cache_args["manager"]
|
||||||
else:
|
else:
|
||||||
_beaker_cache = beaker_cache.CacheManager()
|
_beaker_cache = beaker_cache.CacheManager()
|
||||||
super(BeakerCacheImpl, self).__init__(cache)
|
super().__init__(cache)
|
||||||
|
|
||||||
def _get_cache(self, **kw):
|
def _get_cache(self, **kw):
|
||||||
expiretime = kw.pop("timeout", None)
|
expiretime = kw.pop("timeout", None)
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
# ext/extract.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
||||||
|
from io import BytesIO
|
||||||
|
from io import StringIO
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from mako import compat
|
|
||||||
from mako import lexer
|
from mako import lexer
|
||||||
from mako import parsetree
|
from mako import parsetree
|
||||||
|
|
||||||
|
|
||||||
class MessageExtractor(object):
|
class MessageExtractor:
|
||||||
|
use_bytes = True
|
||||||
|
|
||||||
def process_file(self, fileobj):
|
def process_file(self, fileobj):
|
||||||
template_node = lexer.Lexer(
|
template_node = lexer.Lexer(
|
||||||
fileobj.read(), input_encoding=self.config["encoding"]
|
fileobj.read(), input_encoding=self.config["encoding"]
|
||||||
).parse()
|
).parse()
|
||||||
for extracted in self.extract_nodes(template_node.get_children()):
|
yield from self.extract_nodes(template_node.get_children())
|
||||||
yield extracted
|
|
||||||
|
|
||||||
def extract_nodes(self, nodes):
|
def extract_nodes(self, nodes):
|
||||||
translator_comments = []
|
translator_comments = []
|
||||||
|
@ -90,7 +92,7 @@ class MessageExtractor(object):
|
||||||
comment[1] for comment in translator_comments
|
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")
|
code = code.encode(input_encoding, "backslashreplace")
|
||||||
|
|
||||||
used_translator_comments = False
|
used_translator_comments = False
|
||||||
|
@ -99,7 +101,10 @@ class MessageExtractor(object):
|
||||||
# input string of the input is non-ascii)
|
# input string of the input is non-ascii)
|
||||||
# Also, because we added it, we have to subtract one from
|
# Also, because we added it, we have to subtract one from
|
||||||
# node.lineno
|
# 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(
|
for message in self.process_python(
|
||||||
code, node.lineno - 1, translator_strings
|
code, node.lineno - 1, translator_strings
|
||||||
|
@ -112,8 +117,7 @@ class MessageExtractor(object):
|
||||||
in_translator_comments = False
|
in_translator_comments = False
|
||||||
|
|
||||||
if child_nodes:
|
if child_nodes:
|
||||||
for extracted in self.extract_nodes(child_nodes):
|
yield from self.extract_nodes(child_nodes)
|
||||||
yield extracted
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _split_comment(lineno, comment):
|
def _split_comment(lineno, comment):
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
# ext/linguaplugin.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import io
|
import io
|
||||||
|
|
||||||
from lingua.extractors import Extractor
|
from lingua.extractors import Extractor
|
||||||
from lingua.extractors import get_extractor
|
from lingua.extractors import get_extractor
|
||||||
from lingua.extractors import Message
|
from lingua.extractors import Message
|
||||||
|
|
||||||
from mako import compat
|
|
||||||
from mako.ext.extract import MessageExtractor
|
from mako.ext.extract import MessageExtractor
|
||||||
|
|
||||||
|
|
||||||
class LinguaMakoExtractor(Extractor, MessageExtractor):
|
class LinguaMakoExtractor(Extractor, MessageExtractor):
|
||||||
|
|
||||||
"""Mako templates"""
|
"""Mako templates"""
|
||||||
|
|
||||||
|
use_bytes = False
|
||||||
extensions = [".mako"]
|
extensions = [".mako"]
|
||||||
default_config = {"encoding": "utf-8", "comment-tags": ""}
|
default_config = {"encoding": "utf-8", "comment-tags": ""}
|
||||||
|
|
||||||
|
@ -26,29 +26,21 @@ class LinguaMakoExtractor(Extractor, MessageExtractor):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.python_extractor = get_extractor("x.py")
|
self.python_extractor = get_extractor("x.py")
|
||||||
if fileobj is None:
|
if fileobj is None:
|
||||||
fileobj = open(filename, "rb")
|
ctx = open(filename, "r")
|
||||||
must_close = True
|
|
||||||
else:
|
else:
|
||||||
must_close = False
|
ctx = contextlib.nullcontext(fileobj)
|
||||||
try:
|
with ctx as file_:
|
||||||
for message in self.process_file(fileobj):
|
yield from self.process_file(file_)
|
||||||
yield message
|
|
||||||
finally:
|
|
||||||
if must_close:
|
|
||||||
fileobj.close()
|
|
||||||
|
|
||||||
def process_python(self, code, code_lineno, translator_strings):
|
def process_python(self, code, code_lineno, translator_strings):
|
||||||
source = code.getvalue().strip()
|
source = code.getvalue().strip()
|
||||||
if source.endswith(compat.b(":")):
|
if source.endswith(":"):
|
||||||
if source in (
|
if source in ("try:", "else:") or source.startswith("except"):
|
||||||
compat.b("try:"),
|
source = "" # Ignore try/except and else
|
||||||
compat.b("else:"),
|
elif source.startswith("elif"):
|
||||||
) or source.startswith(compat.b("except")):
|
|
||||||
source = compat.b("") # Ignore try/except and else
|
|
||||||
elif source.startswith(compat.b("elif")):
|
|
||||||
source = source[2:] # Replace "elif" with "if"
|
source = source[2:] # Replace "elif" with "if"
|
||||||
source += compat.b("pass")
|
source += "pass"
|
||||||
code = io.BytesIO(source)
|
code = io.StringIO(source)
|
||||||
for msg in self.python_extractor(
|
for msg in self.python_extractor(
|
||||||
self.filename, self.options, code, code_lineno - 1
|
self.filename, self.options, code, code_lineno - 1
|
||||||
):
|
):
|
||||||
|
@ -58,7 +50,7 @@ class LinguaMakoExtractor(Extractor, MessageExtractor):
|
||||||
msg.msgid,
|
msg.msgid,
|
||||||
msg.msgid_plural,
|
msg.msgid_plural,
|
||||||
msg.flags,
|
msg.flags,
|
||||||
compat.u(" ").join(translator_strings + [msg.comment]),
|
" ".join(translator_strings + [msg.comment]),
|
||||||
msg.tcomment,
|
msg.tcomment,
|
||||||
msg.location,
|
msg.location,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ext/preprocessors.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ext/pygmentplugin.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# 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 String
|
||||||
from pygments.token import Text
|
from pygments.token import Text
|
||||||
|
|
||||||
from mako import compat
|
|
||||||
|
|
||||||
|
|
||||||
class MakoLexer(RegexLexer):
|
class MakoLexer(RegexLexer):
|
||||||
name = "Mako"
|
name = "Mako"
|
||||||
|
@ -108,7 +106,7 @@ class MakoHtmlLexer(DelegatingLexer):
|
||||||
aliases = ["html+mako"]
|
aliases = ["html+mako"]
|
||||||
|
|
||||||
def __init__(self, **options):
|
def __init__(self, **options):
|
||||||
super(MakoHtmlLexer, self).__init__(HtmlLexer, MakoLexer, **options)
|
super().__init__(HtmlLexer, MakoLexer, **options)
|
||||||
|
|
||||||
|
|
||||||
class MakoXmlLexer(DelegatingLexer):
|
class MakoXmlLexer(DelegatingLexer):
|
||||||
|
@ -116,7 +114,7 @@ class MakoXmlLexer(DelegatingLexer):
|
||||||
aliases = ["xml+mako"]
|
aliases = ["xml+mako"]
|
||||||
|
|
||||||
def __init__(self, **options):
|
def __init__(self, **options):
|
||||||
super(MakoXmlLexer, self).__init__(XmlLexer, MakoLexer, **options)
|
super().__init__(XmlLexer, MakoLexer, **options)
|
||||||
|
|
||||||
|
|
||||||
class MakoJavascriptLexer(DelegatingLexer):
|
class MakoJavascriptLexer(DelegatingLexer):
|
||||||
|
@ -124,9 +122,7 @@ class MakoJavascriptLexer(DelegatingLexer):
|
||||||
aliases = ["js+mako", "javascript+mako"]
|
aliases = ["js+mako", "javascript+mako"]
|
||||||
|
|
||||||
def __init__(self, **options):
|
def __init__(self, **options):
|
||||||
super(MakoJavascriptLexer, self).__init__(
|
super().__init__(JavascriptLexer, MakoLexer, **options)
|
||||||
JavascriptLexer, MakoLexer, **options
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class MakoCssLexer(DelegatingLexer):
|
class MakoCssLexer(DelegatingLexer):
|
||||||
|
@ -134,7 +130,7 @@ class MakoCssLexer(DelegatingLexer):
|
||||||
aliases = ["css+mako"]
|
aliases = ["css+mako"]
|
||||||
|
|
||||||
def __init__(self, **options):
|
def __init__(self, **options):
|
||||||
super(MakoCssLexer, self).__init__(CssLexer, MakoLexer, **options)
|
super().__init__(CssLexer, MakoLexer, **options)
|
||||||
|
|
||||||
|
|
||||||
pygments_html_formatter = HtmlFormatter(
|
pygments_html_formatter = HtmlFormatter(
|
||||||
|
@ -144,10 +140,7 @@ pygments_html_formatter = HtmlFormatter(
|
||||||
|
|
||||||
def syntax_highlight(filename="", language=None):
|
def syntax_highlight(filename="", language=None):
|
||||||
mako_lexer = MakoLexer()
|
mako_lexer = MakoLexer()
|
||||||
if compat.py3k:
|
|
||||||
python_lexer = Python3Lexer()
|
python_lexer = Python3Lexer()
|
||||||
else:
|
|
||||||
python_lexer = PythonLexer()
|
|
||||||
if filename.startswith("memory:") or language == "mako":
|
if filename.startswith("memory:") or language == "mako":
|
||||||
return lambda string: highlight(
|
return lambda string: highlight(
|
||||||
string, mako_lexer, pygments_html_formatter
|
string, mako_lexer, pygments_html_formatter
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ext/turbogears.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# 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
|
from mako.template import Template
|
||||||
|
|
||||||
|
|
||||||
class TGPlugin(object):
|
class TGPlugin:
|
||||||
|
|
||||||
"""TurboGears compatible Template Plugin."""
|
"""TurboGears compatible Template Plugin."""
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class TGPlugin(object):
|
||||||
def render(
|
def render(
|
||||||
self, info, format="html", fragment=False, template=None # noqa
|
self, info, format="html", fragment=False, template=None # noqa
|
||||||
):
|
):
|
||||||
if isinstance(template, compat.string_types):
|
if isinstance(template, str):
|
||||||
template = self.load_template(template)
|
template = self.load_template(template)
|
||||||
|
|
||||||
# Load extra vars func if provided
|
# Load extra vars func if provided
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
# mako/filters.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
|
from html.entities import codepoint2name
|
||||||
|
from html.entities import name2codepoint
|
||||||
import re
|
import re
|
||||||
|
from urllib.parse import quote_plus
|
||||||
|
|
||||||
from mako import compat
|
import markupsafe
|
||||||
from mako.compat import codepoint2name
|
|
||||||
from mako.compat import name2codepoint
|
html_escape = markupsafe.escape
|
||||||
from mako.compat import quote_plus
|
|
||||||
from mako.compat import unquote_plus
|
|
||||||
|
|
||||||
xml_escapes = {
|
xml_escapes = {
|
||||||
"&": "&",
|
"&": "&",
|
||||||
|
@ -22,27 +23,6 @@ xml_escapes = {
|
||||||
"'": "'", # also ' in html-only
|
"'": "'", # 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):
|
def xml_escape(string):
|
||||||
return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string)
|
return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string)
|
||||||
|
@ -54,31 +34,19 @@ def url_escape(string):
|
||||||
return quote_plus(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):
|
def trim(string):
|
||||||
return string.strip()
|
return string.strip()
|
||||||
|
|
||||||
|
|
||||||
class Decode(object):
|
class Decode:
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
def decode(x):
|
def decode(x):
|
||||||
if isinstance(x, compat.text_type):
|
if isinstance(x, str):
|
||||||
return x
|
return x
|
||||||
elif not isinstance(x, compat.binary_type):
|
elif not isinstance(x, bytes):
|
||||||
return decode(str(x))
|
return decode(str(x))
|
||||||
else:
|
else:
|
||||||
return compat.text_type(x, encoding=key)
|
return str(x, encoding=key)
|
||||||
|
|
||||||
return decode
|
return decode
|
||||||
|
|
||||||
|
@ -86,24 +54,11 @@ class Decode(object):
|
||||||
decode = Decode()
|
decode = Decode()
|
||||||
|
|
||||||
|
|
||||||
_ASCII_re = re.compile(r"\A[\x00-\x7f]*\Z")
|
class XMLEntityEscaper:
|
||||||
|
|
||||||
|
|
||||||
def is_ascii_str(text):
|
|
||||||
return isinstance(text, str) and _ASCII_re.match(text)
|
|
||||||
|
|
||||||
|
|
||||||
################################################################
|
|
||||||
|
|
||||||
|
|
||||||
class XMLEntityEscaper(object):
|
|
||||||
def __init__(self, codepoint2name, name2codepoint):
|
def __init__(self, codepoint2name, name2codepoint):
|
||||||
self.codepoint2entity = dict(
|
self.codepoint2entity = {
|
||||||
[
|
c: str("&%s;" % n) for c, n in codepoint2name.items()
|
||||||
(c, compat.text_type("&%s;" % n))
|
}
|
||||||
for c, n in codepoint2name.items()
|
|
||||||
]
|
|
||||||
)
|
|
||||||
self.name2codepoint = name2codepoint
|
self.name2codepoint = name2codepoint
|
||||||
|
|
||||||
def escape_entities(self, text):
|
def escape_entities(self, text):
|
||||||
|
@ -111,7 +66,7 @@ class XMLEntityEscaper(object):
|
||||||
|
|
||||||
Only characters corresponding to a named entity are replaced.
|
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):
|
def __escape(self, m):
|
||||||
codepoint = ord(m.group())
|
codepoint = ord(m.group())
|
||||||
|
@ -131,9 +86,7 @@ class XMLEntityEscaper(object):
|
||||||
|
|
||||||
The return value is guaranteed to be ASCII.
|
The return value is guaranteed to be ASCII.
|
||||||
"""
|
"""
|
||||||
return self.__escapable.sub(
|
return self.__escapable.sub(self.__escape, str(text)).encode("ascii")
|
||||||
self.__escape, compat.text_type(text)
|
|
||||||
).encode("ascii")
|
|
||||||
|
|
||||||
# XXX: This regexp will not match all valid XML entity names__.
|
# XXX: This regexp will not match all valid XML entity names__.
|
||||||
# (It punts on details involving involving CombiningChars and Extenders.)
|
# (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
|
characters with HTML entities, or, if no HTML entity exists for
|
||||||
the character, XML character references::
|
the character, XML character references::
|
||||||
|
|
||||||
>>> u'The cost was \u20ac12.'.encode('latin1', 'htmlentityreplace')
|
>>> 'The cost was \u20ac12.'.encode('latin1', 'htmlentityreplace')
|
||||||
'The cost was €12.'
|
'The cost was €12.'
|
||||||
"""
|
"""
|
||||||
if isinstance(ex, UnicodeEncodeError):
|
if isinstance(ex, UnicodeEncodeError):
|
||||||
# Handle encoding errors
|
# Handle encoding errors
|
||||||
bad_text = ex.object[ex.start : ex.end]
|
bad_text = ex.object[ex.start : ex.end]
|
||||||
text = _html_entities_escaper.escape(bad_text)
|
text = _html_entities_escaper.escape(bad_text)
|
||||||
return (compat.text_type(text), ex.end)
|
return (str(text), ex.end)
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
|
|
||||||
codecs.register_error("htmlentityreplace", htmlentityreplace_errors)
|
codecs.register_error("htmlentityreplace", htmlentityreplace_errors)
|
||||||
|
|
||||||
|
|
||||||
# TODO: options to make this dynamic per-compilation will be added in a later
|
|
||||||
# release
|
|
||||||
DEFAULT_ESCAPES = {
|
DEFAULT_ESCAPES = {
|
||||||
"x": "filters.xml_escape",
|
"x": "filters.xml_escape",
|
||||||
"h": "filters.html_escape",
|
"h": "filters.html_escape",
|
||||||
"u": "filters.url_escape",
|
"u": "filters.url_escape",
|
||||||
"trim": "filters.trim",
|
"trim": "filters.trim",
|
||||||
"entity": "filters.html_entities_escape",
|
"entity": "filters.html_entities_escape",
|
||||||
"unicode": "unicode",
|
"unicode": "str",
|
||||||
"decode": "decode",
|
"decode": "decode",
|
||||||
"str": "str",
|
"str": "str",
|
||||||
"n": "n",
|
"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
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
@ -9,7 +9,6 @@
|
||||||
import codecs
|
import codecs
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from mako import compat
|
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
from mako import parsetree
|
from mako import parsetree
|
||||||
from mako.pygen import adjust_whitespace
|
from mako.pygen import adjust_whitespace
|
||||||
|
@ -17,14 +16,9 @@ from mako.pygen import adjust_whitespace
|
||||||
_regexp_cache = {}
|
_regexp_cache = {}
|
||||||
|
|
||||||
|
|
||||||
class Lexer(object):
|
class Lexer:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, text, filename=None, input_encoding=None, preprocessor=None
|
||||||
text,
|
|
||||||
filename=None,
|
|
||||||
disable_unicode=False,
|
|
||||||
input_encoding=None,
|
|
||||||
preprocessor=None,
|
|
||||||
):
|
):
|
||||||
self.text = text
|
self.text = text
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
@ -36,14 +30,8 @@ class Lexer(object):
|
||||||
self.tag = []
|
self.tag = []
|
||||||
self.control_line = []
|
self.control_line = []
|
||||||
self.ternary_stack = []
|
self.ternary_stack = []
|
||||||
self.disable_unicode = disable_unicode
|
|
||||||
self.encoding = input_encoding
|
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:
|
if preprocessor is None:
|
||||||
self.preprocessor = []
|
self.preprocessor = []
|
||||||
elif not hasattr(preprocessor, "__iter__"):
|
elif not hasattr(preprocessor, "__iter__"):
|
||||||
|
@ -66,10 +54,7 @@ class Lexer(object):
|
||||||
try:
|
try:
|
||||||
reg = _regexp_cache[(regexp, flags)]
|
reg = _regexp_cache[(regexp, flags)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if flags:
|
reg = re.compile(regexp, flags) if flags else re.compile(regexp)
|
||||||
reg = re.compile(regexp, flags)
|
|
||||||
else:
|
|
||||||
reg = re.compile(regexp)
|
|
||||||
_regexp_cache[(regexp, flags)] = reg
|
_regexp_cache[(regexp, flags)] = reg
|
||||||
|
|
||||||
return self.match_reg(reg)
|
return self.match_reg(reg)
|
||||||
|
@ -87,10 +72,7 @@ class Lexer(object):
|
||||||
match = reg.match(self.text, self.match_position)
|
match = reg.match(self.text, self.match_position)
|
||||||
if match:
|
if match:
|
||||||
(start, end) = match.span()
|
(start, end) = match.span()
|
||||||
if end == start:
|
self.match_position = end + 1 if end == start else end
|
||||||
self.match_position = end + 1
|
|
||||||
else:
|
|
||||||
self.match_position = end
|
|
||||||
self.matched_lineno = self.lineno
|
self.matched_lineno = self.lineno
|
||||||
lines = re.findall(r"\n", self.text[mp : self.match_position])
|
lines = re.findall(r"\n", self.text[mp : self.match_position])
|
||||||
cp = mp - 1
|
cp = mp - 1
|
||||||
|
@ -98,10 +80,6 @@ class Lexer(object):
|
||||||
cp -= 1
|
cp -= 1
|
||||||
self.matched_charpos = mp - cp
|
self.matched_charpos = mp - cp
|
||||||
self.lineno += len(lines)
|
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
|
return match
|
||||||
|
|
||||||
def parse_until_text(self, watch_nesting, *text):
|
def parse_until_text(self, watch_nesting, *text):
|
||||||
|
@ -161,11 +139,14 @@ class Lexer(object):
|
||||||
if self.control_line:
|
if self.control_line:
|
||||||
control_frame = self.control_line[-1]
|
control_frame = self.control_line[-1]
|
||||||
control_frame.nodes.append(node)
|
control_frame.nodes.append(node)
|
||||||
if not (
|
if (
|
||||||
|
not (
|
||||||
isinstance(node, parsetree.ControlLine)
|
isinstance(node, parsetree.ControlLine)
|
||||||
and control_frame.is_ternary(node.keyword)
|
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 isinstance(node, parsetree.Tag):
|
||||||
if len(self.tag):
|
if len(self.tag):
|
||||||
|
@ -188,7 +169,7 @@ class Lexer(object):
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
"Keyword '%s' not a legal ternary for keyword '%s'"
|
"Keyword '%s' not a legal ternary for keyword '%s'"
|
||||||
% (node.keyword, self.control_line[-1].keyword),
|
% (node.keyword, self.control_line[-1].keyword),
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
_coding_re = re.compile(r"#.*coding[:=]\s*([-\w.]+).*\r?\n")
|
_coding_re = re.compile(r"#.*coding[:=]\s*([-\w.]+).*\r?\n")
|
||||||
|
@ -199,7 +180,7 @@ class Lexer(object):
|
||||||
or raw if decode_raw=False
|
or raw if decode_raw=False
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(text, compat.text_type):
|
if isinstance(text, str):
|
||||||
m = self._coding_re.match(text)
|
m = self._coding_re.match(text)
|
||||||
encoding = m and m.group(1) or known_encoding or "utf-8"
|
encoding = m and m.group(1) or known_encoding or "utf-8"
|
||||||
return encoding, text
|
return encoding, text
|
||||||
|
@ -219,11 +200,7 @@ class Lexer(object):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
m = self._coding_re.match(text.decode("utf-8", "ignore"))
|
m = self._coding_re.match(text.decode("utf-8", "ignore"))
|
||||||
if m:
|
parsed_encoding = m.group(1) if m else known_encoding or "utf-8"
|
||||||
parsed_encoding = m.group(1)
|
|
||||||
else:
|
|
||||||
parsed_encoding = known_encoding or "utf-8"
|
|
||||||
|
|
||||||
if decode_raw:
|
if decode_raw:
|
||||||
try:
|
try:
|
||||||
text = text.decode(parsed_encoding)
|
text = text.decode(parsed_encoding)
|
||||||
|
@ -241,7 +218,7 @@ class Lexer(object):
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
self.encoding, self.text = self.decode_raw_stream(
|
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:
|
for preproc in self.preprocessor:
|
||||||
|
@ -276,12 +253,13 @@ class Lexer(object):
|
||||||
|
|
||||||
if self.match_position > self.textlength:
|
if self.match_position > self.textlength:
|
||||||
break
|
break
|
||||||
raise exceptions.CompileException("assertion failed")
|
# TODO: no coverage here
|
||||||
|
raise exceptions.MakoException("assertion failed")
|
||||||
|
|
||||||
if len(self.tag):
|
if len(self.tag):
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
"Unclosed tag: <%%%s>" % self.tag[-1].keyword,
|
"Unclosed tag: <%%%s>" % self.tag[-1].keyword,
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
if len(self.control_line):
|
if len(self.control_line):
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
|
@ -312,7 +290,9 @@ class Lexer(object):
|
||||||
re.I | re.S | re.X,
|
re.I | re.S | re.X,
|
||||||
)
|
)
|
||||||
|
|
||||||
if match:
|
if not match:
|
||||||
|
return False
|
||||||
|
|
||||||
keyword, attr, isend = match.groups()
|
keyword, attr, isend = match.groups()
|
||||||
self.keyword = keyword
|
self.keyword = keyword
|
||||||
attributes = {}
|
attributes = {}
|
||||||
|
@ -327,19 +307,16 @@ class Lexer(object):
|
||||||
self.append_node(parsetree.Tag, keyword, attributes)
|
self.append_node(parsetree.Tag, keyword, attributes)
|
||||||
if isend:
|
if isend:
|
||||||
self.tag.pop()
|
self.tag.pop()
|
||||||
else:
|
elif keyword == "text":
|
||||||
if keyword == "text":
|
|
||||||
match = self.match(r"(.*?)(?=\</%text>)", re.S)
|
match = self.match(r"(.*?)(?=\</%text>)", re.S)
|
||||||
if not match:
|
if not match:
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
"Unclosed tag: <%%%s>" % self.tag[-1].keyword,
|
"Unclosed tag: <%%%s>" % self.tag[-1].keyword,
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
self.append_node(parsetree.Text, match.group(1))
|
self.append_node(parsetree.Text, match.group(1))
|
||||||
return self.match_tag_end()
|
return self.match_tag_end()
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def match_tag_end(self):
|
def match_tag_end(self):
|
||||||
match = self.match(r"\</%[\t ]*(.+?)[\t ]*>")
|
match = self.match(r"\</%[\t ]*(.+?)[\t ]*>")
|
||||||
|
@ -348,13 +325,13 @@ class Lexer(object):
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
"Closing tag without opening tag: </%%%s>"
|
"Closing tag without opening tag: </%%%s>"
|
||||||
% match.group(1),
|
% match.group(1),
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
elif self.tag[-1].keyword != match.group(1):
|
elif self.tag[-1].keyword != match.group(1):
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
"Closing tag </%%%s> does not match tag: <%%%s>"
|
"Closing tag </%%%s> does not match tag: <%%%s>"
|
||||||
% (match.group(1), self.tag[-1].keyword),
|
% (match.group(1), self.tag[-1].keyword),
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
self.tag.pop()
|
self.tag.pop()
|
||||||
return True
|
return True
|
||||||
|
@ -363,14 +340,14 @@ class Lexer(object):
|
||||||
|
|
||||||
def match_end(self):
|
def match_end(self):
|
||||||
match = self.match(r"\Z", re.S)
|
match = self.match(r"\Z", re.S)
|
||||||
if match:
|
if not match:
|
||||||
|
return False
|
||||||
|
|
||||||
string = match.group()
|
string = match.group()
|
||||||
if string:
|
if string:
|
||||||
return string
|
return string
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def match_text(self):
|
def match_text(self):
|
||||||
match = self.match(
|
match = self.match(
|
||||||
|
@ -422,7 +399,9 @@ class Lexer(object):
|
||||||
|
|
||||||
def match_expression(self):
|
def match_expression(self):
|
||||||
match = self.match(r"\${")
|
match = self.match(r"\${")
|
||||||
if match:
|
if not match:
|
||||||
|
return False
|
||||||
|
|
||||||
line, pos = self.matched_lineno, self.matched_charpos
|
line, pos = self.matched_lineno, self.matched_charpos
|
||||||
text, end = self.parse_until_text(True, r"\|", r"}")
|
text, end = self.parse_until_text(True, r"\|", r"}")
|
||||||
if end == "|":
|
if end == "|":
|
||||||
|
@ -438,8 +417,6 @@ class Lexer(object):
|
||||||
pos=pos,
|
pos=pos,
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def match_control_line(self):
|
def match_control_line(self):
|
||||||
match = self.match(
|
match = self.match(
|
||||||
|
@ -447,7 +424,9 @@ class Lexer(object):
|
||||||
r"(?:\r?\n|\Z)",
|
r"(?:\r?\n|\Z)",
|
||||||
re.M,
|
re.M,
|
||||||
)
|
)
|
||||||
if match:
|
if not match:
|
||||||
|
return False
|
||||||
|
|
||||||
operator = match.group(1)
|
operator = match.group(1)
|
||||||
text = match.group(2)
|
text = match.group(2)
|
||||||
if operator == "%":
|
if operator == "%":
|
||||||
|
@ -455,7 +434,7 @@ class Lexer(object):
|
||||||
if not m2:
|
if not m2:
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
"Invalid control line: '%s'" % text,
|
"Invalid control line: '%s'" % text,
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
isend, keyword = m2.group(1, 2)
|
isend, keyword = m2.group(1, 2)
|
||||||
isend = isend is not None
|
isend = isend is not None
|
||||||
|
@ -463,22 +442,19 @@ class Lexer(object):
|
||||||
if isend:
|
if isend:
|
||||||
if not len(self.control_line):
|
if not len(self.control_line):
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
"No starting keyword '%s' for '%s'"
|
"No starting keyword '%s' for '%s'" % (keyword, text),
|
||||||
% (keyword, text),
|
**self.exception_kwargs,
|
||||||
**self.exception_kwargs
|
|
||||||
)
|
)
|
||||||
elif self.control_line[-1].keyword != keyword:
|
elif self.control_line[-1].keyword != keyword:
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
"Keyword '%s' doesn't match keyword '%s'"
|
"Keyword '%s' doesn't match keyword '%s'"
|
||||||
% (text, self.control_line[-1].keyword),
|
% (text, self.control_line[-1].keyword),
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
self.append_node(parsetree.ControlLine, keyword, isend, text)
|
self.append_node(parsetree.ControlLine, keyword, isend, text)
|
||||||
else:
|
else:
|
||||||
self.append_node(parsetree.Comment, text)
|
self.append_node(parsetree.Comment, text)
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def match_comment(self):
|
def match_comment(self):
|
||||||
"""matches the multiline version of a comment"""
|
"""matches the multiline version of a comment"""
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# mako/lookup.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
@ -8,18 +8,14 @@ import os
|
||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
import stat
|
import stat
|
||||||
|
import threading
|
||||||
|
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
from mako import util
|
from mako import util
|
||||||
from mako.template import Template
|
from mako.template import Template
|
||||||
|
|
||||||
try:
|
|
||||||
import threading
|
|
||||||
except:
|
|
||||||
import dummy_threading as threading
|
|
||||||
|
|
||||||
|
class TemplateCollection:
|
||||||
class TemplateCollection(object):
|
|
||||||
|
|
||||||
"""Represent a collection of :class:`.Template` objects,
|
"""Represent a collection of :class:`.Template` objects,
|
||||||
identifiable via URI.
|
identifiable via URI.
|
||||||
|
@ -161,8 +157,6 @@ class TemplateLookup(TemplateCollection):
|
||||||
collection_size=-1,
|
collection_size=-1,
|
||||||
format_exceptions=False,
|
format_exceptions=False,
|
||||||
error_handler=None,
|
error_handler=None,
|
||||||
disable_unicode=False,
|
|
||||||
bytestring_passthrough=False,
|
|
||||||
output_encoding=None,
|
output_encoding=None,
|
||||||
encoding_errors="strict",
|
encoding_errors="strict",
|
||||||
cache_args=None,
|
cache_args=None,
|
||||||
|
@ -207,8 +201,6 @@ class TemplateLookup(TemplateCollection):
|
||||||
"format_exceptions": format_exceptions,
|
"format_exceptions": format_exceptions,
|
||||||
"error_handler": error_handler,
|
"error_handler": error_handler,
|
||||||
"include_error_handler": include_error_handler,
|
"include_error_handler": include_error_handler,
|
||||||
"disable_unicode": disable_unicode,
|
|
||||||
"bytestring_passthrough": bytestring_passthrough,
|
|
||||||
"output_encoding": output_encoding,
|
"output_encoding": output_encoding,
|
||||||
"cache_impl": cache_impl,
|
"cache_impl": cache_impl,
|
||||||
"encoding_errors": encoding_errors,
|
"encoding_errors": encoding_errors,
|
||||||
|
@ -249,7 +241,7 @@ class TemplateLookup(TemplateCollection):
|
||||||
return self._check(uri, self._collection[uri])
|
return self._check(uri, self._collection[uri])
|
||||||
else:
|
else:
|
||||||
return self._collection[uri]
|
return self._collection[uri]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
u = re.sub(r"^\/+", "", uri)
|
u = re.sub(r"^\/+", "", uri)
|
||||||
for dir_ in self.directories:
|
for dir_ in self.directories:
|
||||||
# make sure the path seperators are posix - os.altsep is empty
|
# make sure the path seperators are posix - os.altsep is empty
|
||||||
|
@ -260,8 +252,8 @@ class TemplateLookup(TemplateCollection):
|
||||||
return self._load(srcfile, uri)
|
return self._load(srcfile, uri)
|
||||||
else:
|
else:
|
||||||
raise exceptions.TopLevelLookupException(
|
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):
|
def adjust_uri(self, uri, relativeto):
|
||||||
"""Adjust the given ``uri`` based on the given relative URI."""
|
"""Adjust the given ``uri`` based on the given relative URI."""
|
||||||
|
@ -270,15 +262,14 @@ class TemplateLookup(TemplateCollection):
|
||||||
if key in self._uri_cache:
|
if key in self._uri_cache:
|
||||||
return self._uri_cache[key]
|
return self._uri_cache[key]
|
||||||
|
|
||||||
if uri[0] != "/":
|
if uri[0] == "/":
|
||||||
if relativeto is not None:
|
v = self._uri_cache[key] = uri
|
||||||
|
elif relativeto is not None:
|
||||||
v = self._uri_cache[key] = posixpath.join(
|
v = self._uri_cache[key] = posixpath.join(
|
||||||
posixpath.dirname(relativeto), uri
|
posixpath.dirname(relativeto), uri
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
v = self._uri_cache[key] = "/" + uri
|
v = self._uri_cache[key] = "/" + uri
|
||||||
else:
|
|
||||||
v = self._uri_cache[key] = uri
|
|
||||||
return v
|
return v
|
||||||
|
|
||||||
def filename_to_uri(self, filename):
|
def filename_to_uri(self, filename):
|
||||||
|
@ -324,7 +315,7 @@ class TemplateLookup(TemplateCollection):
|
||||||
filename=posixpath.normpath(filename),
|
filename=posixpath.normpath(filename),
|
||||||
lookup=self,
|
lookup=self,
|
||||||
module_filename=module_filename,
|
module_filename=module_filename,
|
||||||
**self.template_args
|
**self.template_args,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
except:
|
except:
|
||||||
|
@ -342,16 +333,15 @@ class TemplateLookup(TemplateCollection):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
template_stat = os.stat(template.filename)
|
template_stat = os.stat(template.filename)
|
||||||
if template.module._modified_time < template_stat[stat.ST_MTIME]:
|
if template.module._modified_time >= template_stat[stat.ST_MTIME]:
|
||||||
|
return template
|
||||||
self._collection.pop(uri, None)
|
self._collection.pop(uri, None)
|
||||||
return self._load(template.filename, uri)
|
return self._load(template.filename, uri)
|
||||||
else:
|
except OSError as e:
|
||||||
return template
|
|
||||||
except OSError:
|
|
||||||
self._collection.pop(uri, None)
|
self._collection.pop(uri, None)
|
||||||
raise exceptions.TemplateLookupException(
|
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):
|
def put_string(self, uri, text):
|
||||||
"""Place a new :class:`.Template` object into this
|
"""Place a new :class:`.Template` object into this
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# mako/parsetree.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
@ -9,13 +9,12 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from mako import ast
|
from mako import ast
|
||||||
from mako import compat
|
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
from mako import filters
|
from mako import filters
|
||||||
from mako import util
|
from mako import util
|
||||||
|
|
||||||
|
|
||||||
class Node(object):
|
class Node:
|
||||||
|
|
||||||
"""base class for a Node in the parse tree."""
|
"""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."""
|
"""a 'container' node that stores the overall collection of nodes."""
|
||||||
|
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
super(TemplateNode, self).__init__("", 0, 0, filename)
|
super().__init__("", 0, 0, filename)
|
||||||
self.nodes = []
|
self.nodes = []
|
||||||
self.page_attributes = {}
|
self.page_attributes = {}
|
||||||
|
|
||||||
|
@ -80,7 +79,7 @@ class ControlLine(Node):
|
||||||
has_loop_context = False
|
has_loop_context = False
|
||||||
|
|
||||||
def __init__(self, keyword, isend, text, **kwargs):
|
def __init__(self, keyword, isend, text, **kwargs):
|
||||||
super(ControlLine, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.text = text
|
self.text = text
|
||||||
self.keyword = keyword
|
self.keyword = keyword
|
||||||
self.isend = isend
|
self.isend = isend
|
||||||
|
@ -107,11 +106,13 @@ class ControlLine(Node):
|
||||||
"""return true if the given keyword is a ternary keyword
|
"""return true if the given keyword is a ternary keyword
|
||||||
for this ControlLine"""
|
for this ControlLine"""
|
||||||
|
|
||||||
return keyword in {
|
cases = {
|
||||||
"if": set(["else", "elif"]),
|
"if": {"else", "elif"},
|
||||||
"try": set(["except", "finally"]),
|
"try": {"except", "finally"},
|
||||||
"for": set(["else"]),
|
"for": {"else"},
|
||||||
}.get(self.keyword, [])
|
}
|
||||||
|
|
||||||
|
return keyword in cases.get(self.keyword, set())
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "ControlLine(%r, %r, %r, %r)" % (
|
return "ControlLine(%r, %r, %r, %r)" % (
|
||||||
|
@ -123,11 +124,10 @@ class ControlLine(Node):
|
||||||
|
|
||||||
|
|
||||||
class Text(Node):
|
class Text(Node):
|
||||||
|
|
||||||
"""defines plain text in the template."""
|
"""defines plain text in the template."""
|
||||||
|
|
||||||
def __init__(self, content, **kwargs):
|
def __init__(self, content, **kwargs):
|
||||||
super(Text, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.content = content
|
self.content = content
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -135,7 +135,6 @@ class Text(Node):
|
||||||
|
|
||||||
|
|
||||||
class Code(Node):
|
class Code(Node):
|
||||||
|
|
||||||
"""defines a Python code block, either inline or module level.
|
"""defines a Python code block, either inline or module level.
|
||||||
|
|
||||||
e.g.::
|
e.g.::
|
||||||
|
@ -153,7 +152,7 @@ class Code(Node):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, text, ismodule, **kwargs):
|
def __init__(self, text, ismodule, **kwargs):
|
||||||
super(Code, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.text = text
|
self.text = text
|
||||||
self.ismodule = ismodule
|
self.ismodule = ismodule
|
||||||
self.code = ast.PythonCode(text, **self.exception_kwargs)
|
self.code = ast.PythonCode(text, **self.exception_kwargs)
|
||||||
|
@ -173,7 +172,6 @@ class Code(Node):
|
||||||
|
|
||||||
|
|
||||||
class Comment(Node):
|
class Comment(Node):
|
||||||
|
|
||||||
"""defines a comment line.
|
"""defines a comment line.
|
||||||
|
|
||||||
# this is a comment
|
# this is a comment
|
||||||
|
@ -181,7 +179,7 @@ class Comment(Node):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, text, **kwargs):
|
def __init__(self, text, **kwargs):
|
||||||
super(Comment, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.text = text
|
self.text = text
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -189,7 +187,6 @@ class Comment(Node):
|
||||||
|
|
||||||
|
|
||||||
class Expression(Node):
|
class Expression(Node):
|
||||||
|
|
||||||
"""defines an inline expression.
|
"""defines an inline expression.
|
||||||
|
|
||||||
${x+y}
|
${x+y}
|
||||||
|
@ -197,7 +194,7 @@ class Expression(Node):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, text, escapes, **kwargs):
|
def __init__(self, text, escapes, **kwargs):
|
||||||
super(Expression, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.text = text
|
self.text = text
|
||||||
self.escapes = escapes
|
self.escapes = escapes
|
||||||
self.escapes_code = ast.ArgumentList(escapes, **self.exception_kwargs)
|
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
|
# TODO: make the "filter" shortcut list configurable at parse/gen time
|
||||||
return self.code.undeclared_identifiers.union(
|
return self.code.undeclared_identifiers.union(
|
||||||
self.escapes_code.undeclared_identifiers.difference(
|
self.escapes_code.undeclared_identifiers.difference(
|
||||||
set(filters.DEFAULT_ESCAPES.keys())
|
filters.DEFAULT_ESCAPES
|
||||||
)
|
)
|
||||||
).difference(self.code.declared_identifiers)
|
).difference(self.code.declared_identifiers)
|
||||||
|
|
||||||
|
@ -223,7 +220,6 @@ class Expression(Node):
|
||||||
|
|
||||||
|
|
||||||
class _TagMeta(type):
|
class _TagMeta(type):
|
||||||
|
|
||||||
"""metaclass to allow Tag to produce a subclass according to
|
"""metaclass to allow Tag to produce a subclass according to
|
||||||
its keyword"""
|
its keyword"""
|
||||||
|
|
||||||
|
@ -232,7 +228,7 @@ class _TagMeta(type):
|
||||||
def __init__(cls, clsname, bases, dict_):
|
def __init__(cls, clsname, bases, dict_):
|
||||||
if getattr(cls, "__keyword__", None) is not None:
|
if getattr(cls, "__keyword__", None) is not None:
|
||||||
cls._classmap[cls.__keyword__] = cls
|
cls._classmap[cls.__keyword__] = cls
|
||||||
super(_TagMeta, cls).__init__(clsname, bases, dict_)
|
super().__init__(clsname, bases, dict_)
|
||||||
|
|
||||||
def __call__(cls, keyword, attributes, **kwargs):
|
def __call__(cls, keyword, attributes, **kwargs):
|
||||||
if ":" in keyword:
|
if ":" in keyword:
|
||||||
|
@ -254,7 +250,7 @@ class _TagMeta(type):
|
||||||
return type.__call__(cls, keyword, attributes, **kwargs)
|
return type.__call__(cls, keyword, attributes, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Tag(compat.with_metaclass(_TagMeta, Node)):
|
class Tag(Node, metaclass=_TagMeta):
|
||||||
"""abstract base class for tags.
|
"""abstract base class for tags.
|
||||||
|
|
||||||
e.g.::
|
e.g.::
|
||||||
|
@ -276,7 +272,7 @@ class Tag(compat.with_metaclass(_TagMeta, Node)):
|
||||||
expressions,
|
expressions,
|
||||||
nonexpressions,
|
nonexpressions,
|
||||||
required,
|
required,
|
||||||
**kwargs
|
**kwargs,
|
||||||
):
|
):
|
||||||
r"""construct a new Tag instance.
|
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)
|
other arguments passed to the Node superclass (lineno, pos)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
super(Tag, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.keyword = keyword
|
self.keyword = keyword
|
||||||
self.attributes = attributes
|
self.attributes = attributes
|
||||||
self._parse_attributes(expressions, nonexpressions)
|
self._parse_attributes(expressions, nonexpressions)
|
||||||
missing = [r for r in required if r not in self.parsed_attributes]
|
missing = [r for r in required if r not in self.parsed_attributes]
|
||||||
if len(missing):
|
if len(missing):
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
|
(
|
||||||
"Missing attribute(s): %s"
|
"Missing attribute(s): %s"
|
||||||
% ",".join([repr(m) for m in missing]),
|
% ",".join(repr(m) for m in missing)
|
||||||
**self.exception_kwargs
|
),
|
||||||
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.parent = None
|
self.parent = None
|
||||||
self.nodes = []
|
self.nodes = []
|
||||||
|
|
||||||
|
@ -339,23 +338,22 @@ class Tag(compat.with_metaclass(_TagMeta, Node)):
|
||||||
code.undeclared_identifiers
|
code.undeclared_identifiers
|
||||||
)
|
)
|
||||||
expr.append("(%s)" % m.group(1))
|
expr.append("(%s)" % m.group(1))
|
||||||
else:
|
elif x:
|
||||||
if x:
|
|
||||||
expr.append(repr(x))
|
expr.append(repr(x))
|
||||||
self.parsed_attributes[key] = " + ".join(expr) or repr("")
|
self.parsed_attributes[key] = " + ".join(expr) or repr("")
|
||||||
elif key in nonexpressions:
|
elif key in nonexpressions:
|
||||||
if re.search(r"\${.+?}", self.attributes[key]):
|
if re.search(r"\${.+?}", self.attributes[key]):
|
||||||
raise exceptions.CompileException(
|
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),
|
"expressions" % (key, self.keyword),
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
self.parsed_attributes[key] = repr(self.attributes[key])
|
self.parsed_attributes[key] = repr(self.attributes[key])
|
||||||
else:
|
else:
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"Invalid attribute for tag '%s': '%s'"
|
"Invalid attribute for tag '%s': '%s'"
|
||||||
% (self.keyword, key),
|
% (self.keyword, key),
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
self.expression_undeclared_identifiers = undeclared_identifiers
|
self.expression_undeclared_identifiers = undeclared_identifiers
|
||||||
|
|
||||||
|
@ -379,13 +377,13 @@ class IncludeTag(Tag):
|
||||||
__keyword__ = "include"
|
__keyword__ = "include"
|
||||||
|
|
||||||
def __init__(self, keyword, attributes, **kwargs):
|
def __init__(self, keyword, attributes, **kwargs):
|
||||||
super(IncludeTag, self).__init__(
|
super().__init__(
|
||||||
keyword,
|
keyword,
|
||||||
attributes,
|
attributes,
|
||||||
("file", "import", "args"),
|
("file", "import", "args"),
|
||||||
(),
|
(),
|
||||||
("file",),
|
("file",),
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
self.page_args = ast.PythonCode(
|
self.page_args = ast.PythonCode(
|
||||||
"__DUMMY(%s)" % attributes.get("args", ""), **self.exception_kwargs
|
"__DUMMY(%s)" % attributes.get("args", ""), **self.exception_kwargs
|
||||||
|
@ -396,24 +394,22 @@ class IncludeTag(Tag):
|
||||||
|
|
||||||
def undeclared_identifiers(self):
|
def undeclared_identifiers(self):
|
||||||
identifiers = self.page_args.undeclared_identifiers.difference(
|
identifiers = self.page_args.undeclared_identifiers.difference(
|
||||||
set(["__DUMMY"])
|
{"__DUMMY"}
|
||||||
).difference(self.page_args.declared_identifiers)
|
).difference(self.page_args.declared_identifiers)
|
||||||
return identifiers.union(
|
return identifiers.union(super().undeclared_identifiers())
|
||||||
super(IncludeTag, self).undeclared_identifiers()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NamespaceTag(Tag):
|
class NamespaceTag(Tag):
|
||||||
__keyword__ = "namespace"
|
__keyword__ = "namespace"
|
||||||
|
|
||||||
def __init__(self, keyword, attributes, **kwargs):
|
def __init__(self, keyword, attributes, **kwargs):
|
||||||
super(NamespaceTag, self).__init__(
|
super().__init__(
|
||||||
keyword,
|
keyword,
|
||||||
attributes,
|
attributes,
|
||||||
("file",),
|
("file",),
|
||||||
("name", "inheritable", "import", "module"),
|
("name", "inheritable", "import", "module"),
|
||||||
(),
|
(),
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = attributes.get("name", "__anon_%s" % hex(abs(id(self))))
|
self.name = attributes.get("name", "__anon_%s" % hex(abs(id(self))))
|
||||||
|
@ -421,12 +417,12 @@ class NamespaceTag(Tag):
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"'name' and/or 'import' attributes are required "
|
"'name' and/or 'import' attributes are required "
|
||||||
"for <%namespace>",
|
"for <%namespace>",
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
if "file" in attributes and "module" in attributes:
|
if "file" in attributes and "module" in attributes:
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"<%namespace> may only have one of 'file' or 'module'",
|
"<%namespace> may only have one of 'file' or 'module'",
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def declared_identifiers(self):
|
def declared_identifiers(self):
|
||||||
|
@ -437,9 +433,7 @@ class TextTag(Tag):
|
||||||
__keyword__ = "text"
|
__keyword__ = "text"
|
||||||
|
|
||||||
def __init__(self, keyword, attributes, **kwargs):
|
def __init__(self, keyword, attributes, **kwargs):
|
||||||
super(TextTag, self).__init__(
|
super().__init__(keyword, attributes, (), ("filter"), (), **kwargs)
|
||||||
keyword, attributes, (), ("filter"), (), **kwargs
|
|
||||||
)
|
|
||||||
self.filter_args = ast.ArgumentList(
|
self.filter_args = ast.ArgumentList(
|
||||||
attributes.get("filter", ""), **self.exception_kwargs
|
attributes.get("filter", ""), **self.exception_kwargs
|
||||||
)
|
)
|
||||||
|
@ -458,13 +452,13 @@ class DefTag(Tag):
|
||||||
c for c in attributes if c.startswith("cache_")
|
c for c in attributes if c.startswith("cache_")
|
||||||
]
|
]
|
||||||
|
|
||||||
super(DefTag, self).__init__(
|
super().__init__(
|
||||||
keyword,
|
keyword,
|
||||||
attributes,
|
attributes,
|
||||||
expressions,
|
expressions,
|
||||||
("name", "filter", "decorator"),
|
("name", "filter", "decorator"),
|
||||||
("name",),
|
("name",),
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
name = attributes["name"]
|
name = attributes["name"]
|
||||||
if re.match(r"^[\w_]+$", name):
|
if re.match(r"^[\w_]+$", name):
|
||||||
|
@ -521,19 +515,19 @@ class BlockTag(Tag):
|
||||||
c for c in attributes if c.startswith("cache_")
|
c for c in attributes if c.startswith("cache_")
|
||||||
]
|
]
|
||||||
|
|
||||||
super(BlockTag, self).__init__(
|
super().__init__(
|
||||||
keyword,
|
keyword,
|
||||||
attributes,
|
attributes,
|
||||||
expressions,
|
expressions,
|
||||||
("name", "filter", "decorator"),
|
("name", "filter", "decorator"),
|
||||||
(),
|
(),
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
name = attributes.get("name")
|
name = attributes.get("name")
|
||||||
if name and not re.match(r"^[\w_]+$", name):
|
if name and not re.match(r"^[\w_]+$", name):
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"%block may not specify an argument signature",
|
"%block may not specify an argument signature",
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
if not name and attributes.get("args", None):
|
if not name and attributes.get("args", None):
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
|
@ -577,7 +571,7 @@ class CallTag(Tag):
|
||||||
__keyword__ = "call"
|
__keyword__ = "call"
|
||||||
|
|
||||||
def __init__(self, keyword, attributes, **kwargs):
|
def __init__(self, keyword, attributes, **kwargs):
|
||||||
super(CallTag, self).__init__(
|
super().__init__(
|
||||||
keyword, attributes, ("args"), ("expr",), ("expr",), **kwargs
|
keyword, attributes, ("args"), ("expr",), ("expr",), **kwargs
|
||||||
)
|
)
|
||||||
self.expression = attributes["expr"]
|
self.expression = attributes["expr"]
|
||||||
|
@ -597,26 +591,25 @@ class CallTag(Tag):
|
||||||
|
|
||||||
class CallNamespaceTag(Tag):
|
class CallNamespaceTag(Tag):
|
||||||
def __init__(self, namespace, defname, attributes, **kwargs):
|
def __init__(self, namespace, defname, attributes, **kwargs):
|
||||||
super(CallNamespaceTag, self).__init__(
|
super().__init__(
|
||||||
namespace + ":" + defname,
|
namespace + ":" + defname,
|
||||||
attributes,
|
attributes,
|
||||||
tuple(attributes.keys()) + ("args",),
|
tuple(attributes.keys()) + ("args",),
|
||||||
(),
|
(),
|
||||||
(),
|
(),
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.expression = "%s.%s(%s)" % (
|
self.expression = "%s.%s(%s)" % (
|
||||||
namespace,
|
namespace,
|
||||||
defname,
|
defname,
|
||||||
",".join(
|
",".join(
|
||||||
[
|
|
||||||
"%s=%s" % (k, v)
|
"%s=%s" % (k, v)
|
||||||
for k, v in self.parsed_attributes.items()
|
for k, v in self.parsed_attributes.items()
|
||||||
if k != "args"
|
if k != "args"
|
||||||
]
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
|
self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
|
||||||
self.body_decl = ast.FunctionArgs(
|
self.body_decl = ast.FunctionArgs(
|
||||||
attributes.get("args", ""), **self.exception_kwargs
|
attributes.get("args", ""), **self.exception_kwargs
|
||||||
|
@ -635,7 +628,7 @@ class InheritTag(Tag):
|
||||||
__keyword__ = "inherit"
|
__keyword__ = "inherit"
|
||||||
|
|
||||||
def __init__(self, keyword, attributes, **kwargs):
|
def __init__(self, keyword, attributes, **kwargs):
|
||||||
super(InheritTag, self).__init__(
|
super().__init__(
|
||||||
keyword, attributes, ("file",), (), ("file",), **kwargs
|
keyword, attributes, ("file",), (), ("file",), **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -651,9 +644,7 @@ class PageTag(Tag):
|
||||||
"enable_loop",
|
"enable_loop",
|
||||||
] + [c for c in attributes if c.startswith("cache_")]
|
] + [c for c in attributes if c.startswith("cache_")]
|
||||||
|
|
||||||
super(PageTag, self).__init__(
|
super().__init__(keyword, attributes, expressions, (), (), **kwargs)
|
||||||
keyword, attributes, expressions, (), (), **kwargs
|
|
||||||
)
|
|
||||||
self.body_decl = ast.FunctionArgs(
|
self.body_decl = ast.FunctionArgs(
|
||||||
attributes.get("args", ""), **self.exception_kwargs
|
attributes.get("args", ""), **self.exception_kwargs
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# mako/pygen.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
@ -11,7 +11,7 @@ import re
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
|
|
||||||
|
|
||||||
class PythonPrinter(object):
|
class PythonPrinter:
|
||||||
def __init__(self, stream):
|
def __init__(self, stream):
|
||||||
# indentation counter
|
# indentation counter
|
||||||
self.indent = 0
|
self.indent = 0
|
||||||
|
@ -96,17 +96,18 @@ class PythonPrinter(object):
|
||||||
is_comment = line and len(line) and line[0] == "#"
|
is_comment = line and len(line) and line[0] == "#"
|
||||||
|
|
||||||
# see if this line should decrease the indentation level
|
# see if this line should decrease the indentation level
|
||||||
if not is_comment and (not hastext or self._is_unindentor(line)):
|
if (
|
||||||
|
not is_comment
|
||||||
if self.indent > 0:
|
and (not hastext or self._is_unindentor(line))
|
||||||
|
and self.indent > 0
|
||||||
|
):
|
||||||
self.indent -= 1
|
self.indent -= 1
|
||||||
# if the indent_detail stack is empty, the user
|
# if the indent_detail stack is empty, the user
|
||||||
# probably put extra closures - the resulting
|
# probably put extra closures - the resulting
|
||||||
# module wont compile.
|
# module wont compile.
|
||||||
if len(self.indent_detail) == 0:
|
if len(self.indent_detail) == 0:
|
||||||
raise exceptions.SyntaxException(
|
# TODO: no coverage here
|
||||||
"Too many whitespace closures"
|
raise exceptions.MakoException("Too many whitespace closures")
|
||||||
)
|
|
||||||
self.indent_detail.pop()
|
self.indent_detail.pop()
|
||||||
|
|
||||||
if line is None:
|
if line is None:
|
||||||
|
@ -167,13 +168,10 @@ class PythonPrinter(object):
|
||||||
# if the current line doesnt have one of the "unindentor" keywords,
|
# if the current line doesnt have one of the "unindentor" keywords,
|
||||||
# return False
|
# return False
|
||||||
match = re.match(r"^\s*(else|elif|except|finally).*\:", line)
|
match = re.match(r"^\s*(else|elif|except|finally).*\:", line)
|
||||||
if not match:
|
# if True, whitespace matches up, we have a compound indentor,
|
||||||
return False
|
|
||||||
|
|
||||||
# whitespace matches up, we have a compound indentor,
|
|
||||||
# and this line has an unindentor, this
|
# and this line has an unindentor, this
|
||||||
# is probably good enough
|
# is probably good enough
|
||||||
return True
|
return bool(match)
|
||||||
|
|
||||||
# should we decide that its not good enough, heres
|
# should we decide that its not good enough, heres
|
||||||
# more stuff to check.
|
# more stuff to check.
|
||||||
|
@ -218,11 +216,7 @@ class PythonPrinter(object):
|
||||||
|
|
||||||
current_state = self.backslashed or self.triplequoted
|
current_state = self.backslashed or self.triplequoted
|
||||||
|
|
||||||
if re.search(r"\\$", line):
|
self.backslashed = bool(re.search(r"\\$", line))
|
||||||
self.backslashed = True
|
|
||||||
else:
|
|
||||||
self.backslashed = False
|
|
||||||
|
|
||||||
triples = len(re.findall(r"\"\"\"|\'\'\'", line))
|
triples = len(re.findall(r"\"\"\"|\'\'\'", line))
|
||||||
if triples == 1 or triples % 2 != 0:
|
if triples == 1 or triples % 2 != 0:
|
||||||
self.triplequoted = not self.triplequoted
|
self.triplequoted = not self.triplequoted
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# mako/pyparser.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# 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 compat
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
from mako import util
|
from mako import util
|
||||||
from mako.compat import arg_stringname
|
|
||||||
|
|
||||||
if compat.py3k:
|
# words that cannot be assigned to (notably
|
||||||
# words that cannot be assigned to (notably
|
# smaller than the total keys in __builtins__)
|
||||||
# smaller than the total keys in __builtins__)
|
reserved = {"True", "False", "None", "print"}
|
||||||
reserved = set(["True", "False", "None", "print"])
|
|
||||||
|
|
||||||
# the "id" attribute on a function node
|
# the "id" attribute on a function node
|
||||||
arg_id = operator.attrgetter("arg")
|
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")
|
|
||||||
|
|
||||||
util.restore__ast(_ast)
|
util.restore__ast(_ast)
|
||||||
|
|
||||||
|
@ -43,7 +34,7 @@ def parse(code, mode="exec", **exception_kwargs):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return _ast_util.parse(code, "<unknown>", mode)
|
return _ast_util.parse(code, "<unknown>", mode)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
raise exceptions.SyntaxException(
|
raise exceptions.SyntaxException(
|
||||||
"(%s) %s (%r)"
|
"(%s) %s (%r)"
|
||||||
% (
|
% (
|
||||||
|
@ -51,8 +42,8 @@ def parse(code, mode="exec", **exception_kwargs):
|
||||||
compat.exception_as(),
|
compat.exception_as(),
|
||||||
code[0:50],
|
code[0:50],
|
||||||
),
|
),
|
||||||
**exception_kwargs
|
**exception_kwargs,
|
||||||
)
|
) from e
|
||||||
|
|
||||||
|
|
||||||
class FindIdentifiers(_ast_util.NodeVisitor):
|
class FindIdentifiers(_ast_util.NodeVisitor):
|
||||||
|
@ -85,11 +76,6 @@ class FindIdentifiers(_ast_util.NodeVisitor):
|
||||||
self.visit(n)
|
self.visit(n)
|
||||||
self.in_assign_targets = in_a
|
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):
|
def visit_ExceptHandler(self, node):
|
||||||
if node.name is not None:
|
if node.name is not None:
|
||||||
self._add_declared(node.name)
|
self._add_declared(node.name)
|
||||||
|
@ -108,8 +94,7 @@ class FindIdentifiers(_ast_util.NodeVisitor):
|
||||||
def _expand_tuples(self, args):
|
def _expand_tuples(self, args):
|
||||||
for arg in args:
|
for arg in args:
|
||||||
if isinstance(arg, _ast.Tuple):
|
if isinstance(arg, _ast.Tuple):
|
||||||
for n in arg.elts:
|
yield from arg.elts
|
||||||
yield n
|
|
||||||
else:
|
else:
|
||||||
yield arg
|
yield arg
|
||||||
|
|
||||||
|
@ -170,15 +155,15 @@ class FindIdentifiers(_ast_util.NodeVisitor):
|
||||||
for name in node.names:
|
for name in node.names:
|
||||||
if name.asname is not None:
|
if name.asname is not None:
|
||||||
self._add_declared(name.asname)
|
self._add_declared(name.asname)
|
||||||
else:
|
elif name.name == "*":
|
||||||
if name.name == "*":
|
|
||||||
raise exceptions.CompileException(
|
raise exceptions.CompileException(
|
||||||
"'import *' is not supported, since all identifier "
|
"'import *' is not supported, since all identifier "
|
||||||
"names must be explicitly declared. Please use the "
|
"names must be explicitly declared. Please use the "
|
||||||
"form 'from <modulename> import <name1>, <name2>, "
|
"form 'from <modulename> import <name1>, <name2>, "
|
||||||
"...' instead.",
|
"...' instead.",
|
||||||
**self.exception_kwargs
|
**self.exception_kwargs,
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
self._add_declared(name.name)
|
self._add_declared(name.name)
|
||||||
|
|
||||||
|
|
||||||
|
@ -213,27 +198,20 @@ class ParseFunc(_ast_util.NodeVisitor):
|
||||||
|
|
||||||
argnames = [arg_id(arg) for arg in node.args.args]
|
argnames = [arg_id(arg) for arg in node.args.args]
|
||||||
if node.args.vararg:
|
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:
|
if node.args.kwarg:
|
||||||
kwargnames.append(arg_stringname(node.args.kwarg))
|
kwargnames.append(node.args.kwarg.arg)
|
||||||
self.listener.argnames = argnames
|
self.listener.argnames = argnames
|
||||||
self.listener.defaults = node.args.defaults # ast
|
self.listener.defaults = node.args.defaults # ast
|
||||||
self.listener.kwargnames = kwargnames
|
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.varargs = node.args.vararg
|
||||||
self.listener.kwargs = node.args.kwarg
|
self.listener.kwargs = node.args.kwarg
|
||||||
|
|
||||||
|
|
||||||
class ExpressionGenerator(object):
|
class ExpressionGenerator:
|
||||||
def __init__(self, astnode):
|
def __init__(self, astnode):
|
||||||
self.generator = _ast_util.SourceGenerator(" " * 4)
|
self.generator = _ast_util.SourceGenerator(" " * 4)
|
||||||
self.generator.visit(astnode)
|
self.generator.visit(astnode)
|
||||||
|
|
|
@ -7,16 +7,16 @@
|
||||||
"""provides runtime services for templates, including Context,
|
"""provides runtime services for templates, including Context,
|
||||||
Namespace, and various helper functions."""
|
Namespace, and various helper functions."""
|
||||||
|
|
||||||
|
import builtins
|
||||||
import functools
|
import functools
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from mako import compat
|
from mako import compat
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
from mako import util
|
from mako import util
|
||||||
from mako.compat import compat_builtins
|
|
||||||
|
|
||||||
|
|
||||||
class Context(object):
|
class Context:
|
||||||
|
|
||||||
"""Provides runtime namespace, output buffer, and various
|
"""Provides runtime namespace, output buffer, and various
|
||||||
callstacks for templates.
|
callstacks for templates.
|
||||||
|
@ -103,7 +103,7 @@ class Context(object):
|
||||||
if key in self._data:
|
if key in self._data:
|
||||||
return self._data[key]
|
return self._data[key]
|
||||||
else:
|
else:
|
||||||
return compat_builtins.__dict__[key]
|
return builtins.__dict__[key]
|
||||||
|
|
||||||
def _push_writer(self):
|
def _push_writer(self):
|
||||||
"""push a capturing buffer onto this Context and return
|
"""push a capturing buffer onto this Context and return
|
||||||
|
@ -135,7 +135,7 @@ class Context(object):
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
"""Return a value from this :class:`.Context`."""
|
"""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):
|
def write(self, string):
|
||||||
"""Write a string to this :class:`.Context` object's
|
"""Write a string to this :class:`.Context` object's
|
||||||
|
@ -216,7 +216,7 @@ class CallerStack(list):
|
||||||
self.nextcaller = self.pop()
|
self.nextcaller = self.pop()
|
||||||
|
|
||||||
|
|
||||||
class Undefined(object):
|
class Undefined:
|
||||||
|
|
||||||
"""Represents an undefined value in a template.
|
"""Represents an undefined value in a template.
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ UNDEFINED = Undefined()
|
||||||
STOP_RENDERING = ""
|
STOP_RENDERING = ""
|
||||||
|
|
||||||
|
|
||||||
class LoopStack(object):
|
class LoopStack:
|
||||||
|
|
||||||
"""a stack for LoopContexts that implements the context manager protocol
|
"""a stack for LoopContexts that implements the context manager protocol
|
||||||
to automatically pop off the top of the stack on context exit
|
to automatically pop off the top of the stack on context exit
|
||||||
|
@ -280,7 +280,7 @@ class LoopStack(object):
|
||||||
return iter(self._top)
|
return iter(self._top)
|
||||||
|
|
||||||
|
|
||||||
class LoopContext(object):
|
class LoopContext:
|
||||||
|
|
||||||
"""A magic loop variable.
|
"""A magic loop variable.
|
||||||
Automatically accessible in any ``% for`` block.
|
Automatically accessible in any ``% for`` block.
|
||||||
|
@ -339,14 +339,13 @@ class LoopContext(object):
|
||||||
return bool(self.index % 2)
|
return bool(self.index % 2)
|
||||||
|
|
||||||
def cycle(self, *values):
|
def cycle(self, *values):
|
||||||
"""Cycle through values as the loop progresses.
|
"""Cycle through values as the loop progresses."""
|
||||||
"""
|
|
||||||
if not values:
|
if not values:
|
||||||
raise ValueError("You must provide values to cycle through")
|
raise ValueError("You must provide values to cycle through")
|
||||||
return values[self.index % len(values)]
|
return values[self.index % len(values)]
|
||||||
|
|
||||||
|
|
||||||
class _NSAttr(object):
|
class _NSAttr:
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.__parent = parent
|
self.__parent = parent
|
||||||
|
|
||||||
|
@ -360,7 +359,7 @@ class _NSAttr(object):
|
||||||
raise AttributeError(key)
|
raise AttributeError(key)
|
||||||
|
|
||||||
|
|
||||||
class Namespace(object):
|
class Namespace:
|
||||||
|
|
||||||
"""Provides access to collections of rendering methods, which
|
"""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.
|
||||||
|
@ -390,7 +389,7 @@ class Namespace(object):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.inherits = inherits
|
self.inherits = inherits
|
||||||
if callables is not None:
|
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 = ()
|
callables = ()
|
||||||
|
|
||||||
|
@ -482,7 +481,6 @@ class Namespace(object):
|
||||||
key = (self, uri)
|
key = (self, uri)
|
||||||
if key in self.context.namespaces:
|
if key in self.context.namespaces:
|
||||||
return self.context.namespaces[key]
|
return self.context.namespaces[key]
|
||||||
else:
|
|
||||||
ns = TemplateNamespace(
|
ns = TemplateNamespace(
|
||||||
uri,
|
uri,
|
||||||
self.context._copy(),
|
self.context._copy(),
|
||||||
|
@ -574,7 +572,7 @@ class TemplateNamespace(Namespace):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.inherits = inherits
|
self.inherits = inherits
|
||||||
if callables is not None:
|
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:
|
if templateuri is not None:
|
||||||
self.template = _lookup_template(context, templateuri, calling_uri)
|
self.template = _lookup_template(context, templateuri, calling_uri)
|
||||||
|
@ -666,7 +664,7 @@ class ModuleNamespace(Namespace):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.inherits = inherits
|
self.inherits = inherits
|
||||||
if callables is not None:
|
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)
|
mod = __import__(module)
|
||||||
for token in module.split(".")[1:]:
|
for token in module.split(".")[1:]:
|
||||||
|
@ -790,7 +788,7 @@ def _include_file(context, uri, calling_uri, **kwargs):
|
||||||
except Exception:
|
except Exception:
|
||||||
result = template.include_error_handler(ctx, compat.exception_as())
|
result = template.include_error_handler(ctx, compat.exception_as())
|
||||||
if not result:
|
if not result:
|
||||||
compat.reraise(*sys.exc_info())
|
raise
|
||||||
else:
|
else:
|
||||||
callable_(ctx, **kwargs)
|
callable_(ctx, **kwargs)
|
||||||
|
|
||||||
|
@ -837,8 +835,10 @@ def _lookup_template(context, uri, relativeto):
|
||||||
uri = lookup.adjust_uri(uri, relativeto)
|
uri = lookup.adjust_uri(uri, relativeto)
|
||||||
try:
|
try:
|
||||||
return lookup.get_template(uri)
|
return lookup.get_template(uri)
|
||||||
except exceptions.TopLevelLookupException:
|
except exceptions.TopLevelLookupException as e:
|
||||||
raise exceptions.TemplateLookupException(str(compat.exception_as()))
|
raise exceptions.TemplateLookupException(
|
||||||
|
str(compat.exception_as())
|
||||||
|
) from e
|
||||||
|
|
||||||
|
|
||||||
def _populate_self_namespace(context, template, self_ns=None):
|
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."""
|
output of the given template and template callable."""
|
||||||
|
|
||||||
if as_unicode:
|
if as_unicode:
|
||||||
buf = util.FastEncodingBuffer(as_unicode=True)
|
buf = util.FastEncodingBuffer()
|
||||||
elif template.bytestring_passthrough:
|
|
||||||
buf = compat.StringIO()
|
|
||||||
else:
|
else:
|
||||||
buf = util.FastEncodingBuffer(
|
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 = Context(buf, **data)
|
||||||
context._outputting_as_unicode = as_unicode
|
context._outputting_as_unicode = as_unicode
|
||||||
|
@ -880,7 +876,7 @@ def _render(template, callable_, args, data, as_unicode=False):
|
||||||
callable_,
|
callable_,
|
||||||
context,
|
context,
|
||||||
*args,
|
*args,
|
||||||
**_kwargs_for_callable(callable_, data)
|
**_kwargs_for_callable(callable_, data),
|
||||||
)
|
)
|
||||||
return context._pop_buffer().getvalue()
|
return context._pop_buffer().getvalue()
|
||||||
|
|
||||||
|
@ -951,13 +947,15 @@ def _render_error(template, context, error):
|
||||||
if template.error_handler:
|
if template.error_handler:
|
||||||
result = template.error_handler(context, error)
|
result = template.error_handler(context, error)
|
||||||
if not result:
|
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:
|
else:
|
||||||
error_template = exceptions.html_error_template()
|
error_template = exceptions.html_error_template()
|
||||||
if context._outputting_as_unicode:
|
if context._outputting_as_unicode:
|
||||||
context._buffer_stack[:] = [
|
context._buffer_stack[:] = [util.FastEncodingBuffer()]
|
||||||
util.FastEncodingBuffer(as_unicode=True)
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
context._buffer_stack[:] = [
|
context._buffer_stack[:] = [
|
||||||
util.FastEncodingBuffer(
|
util.FastEncodingBuffer(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# mako/template.py
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
|
@ -25,7 +25,7 @@ from mako import util
|
||||||
from mako.lexer import Lexer
|
from mako.lexer import Lexer
|
||||||
|
|
||||||
|
|
||||||
class Template(object):
|
class Template:
|
||||||
|
|
||||||
r"""Represents a compiled template.
|
r"""Represents a compiled template.
|
||||||
|
|
||||||
|
@ -53,17 +53,6 @@ class Template(object):
|
||||||
of return-valued ``%def``\ s "opt out" of that filtering via
|
of return-valued ``%def``\ s "opt out" of that filtering via
|
||||||
passing special attributes or objects.
|
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
|
:param cache_args: Dictionary of cache configuration arguments that
|
||||||
will be passed to the :class:`.CacheImpl`. See :ref:`caching_toplevel`.
|
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
|
:param default_filters: List of string filter names that will
|
||||||
be applied to all expressions. See :ref:`filtering_default_filters`.
|
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.
|
:param enable_loop: When ``True``, enable the ``loop`` context variable.
|
||||||
This can be set to ``False`` to support templates that may
|
This can be set to ``False`` to support templates that may
|
||||||
be making usage of the name "``loop``". Individual templates can
|
be making usage of the name "``loop``". Individual templates can
|
||||||
|
@ -255,9 +241,7 @@ class Template(object):
|
||||||
cache_url=None,
|
cache_url=None,
|
||||||
module_filename=None,
|
module_filename=None,
|
||||||
input_encoding=None,
|
input_encoding=None,
|
||||||
disable_unicode=False,
|
|
||||||
module_writer=None,
|
module_writer=None,
|
||||||
bytestring_passthrough=False,
|
|
||||||
default_filters=None,
|
default_filters=None,
|
||||||
buffer_filters=(),
|
buffer_filters=(),
|
||||||
strict_undefined=False,
|
strict_undefined=False,
|
||||||
|
@ -294,26 +278,12 @@ class Template(object):
|
||||||
self.input_encoding = input_encoding
|
self.input_encoding = input_encoding
|
||||||
self.output_encoding = output_encoding
|
self.output_encoding = output_encoding
|
||||||
self.encoding_errors = encoding_errors
|
self.encoding_errors = encoding_errors
|
||||||
self.disable_unicode = disable_unicode
|
|
||||||
self.bytestring_passthrough = bytestring_passthrough or disable_unicode
|
|
||||||
self.enable_loop = enable_loop
|
self.enable_loop = enable_loop
|
||||||
self.strict_undefined = strict_undefined
|
self.strict_undefined = strict_undefined
|
||||||
self.module_writer = module_writer
|
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 default_filters is None:
|
||||||
if compat.py3k or self.disable_unicode:
|
|
||||||
self.default_filters = ["str"]
|
self.default_filters = ["str"]
|
||||||
else:
|
|
||||||
self.default_filters = ["unicode"]
|
|
||||||
else:
|
else:
|
||||||
self.default_filters = default_filters
|
self.default_filters = default_filters
|
||||||
self.buffer_filters = buffer_filters
|
self.buffer_filters = buffer_filters
|
||||||
|
@ -387,11 +357,7 @@ class Template(object):
|
||||||
):
|
):
|
||||||
self.cache_impl = cache_impl
|
self.cache_impl = cache_impl
|
||||||
self.cache_enabled = cache_enabled
|
self.cache_enabled = cache_enabled
|
||||||
if cache_args:
|
self.cache_args = cache_args or {}
|
||||||
self.cache_args = cache_args
|
|
||||||
else:
|
|
||||||
self.cache_args = {}
|
|
||||||
|
|
||||||
# transfer deprecated cache_* args
|
# transfer deprecated cache_* args
|
||||||
if cache_type:
|
if cache_type:
|
||||||
self.cache_args["type"] = cache_type
|
self.cache_args["type"] = cache_type
|
||||||
|
@ -463,7 +429,7 @@ class Template(object):
|
||||||
|
|
||||||
If the template specifies an output encoding, the string
|
If the template specifies an output encoding, the string
|
||||||
will be encoded accordingly, else the output is raw (raw
|
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
|
characters). A :class:`.Context` object is created corresponding
|
||||||
to the given data. Arguments that are explicitly declared
|
to the given data. Arguments that are explicitly declared
|
||||||
by this template's internal rendering method are also
|
by this template's internal rendering method are also
|
||||||
|
@ -541,8 +507,6 @@ class ModuleTemplate(Template):
|
||||||
template_source=None,
|
template_source=None,
|
||||||
output_encoding=None,
|
output_encoding=None,
|
||||||
encoding_errors="strict",
|
encoding_errors="strict",
|
||||||
disable_unicode=False,
|
|
||||||
bytestring_passthrough=False,
|
|
||||||
format_exceptions=False,
|
format_exceptions=False,
|
||||||
error_handler=None,
|
error_handler=None,
|
||||||
lookup=None,
|
lookup=None,
|
||||||
|
@ -559,20 +523,8 @@ class ModuleTemplate(Template):
|
||||||
self.input_encoding = module._source_encoding
|
self.input_encoding = module._source_encoding
|
||||||
self.output_encoding = output_encoding
|
self.output_encoding = output_encoding
|
||||||
self.encoding_errors = encoding_errors
|
self.encoding_errors = encoding_errors
|
||||||
self.disable_unicode = disable_unicode
|
|
||||||
self.bytestring_passthrough = bytestring_passthrough or disable_unicode
|
|
||||||
self.enable_loop = module._enable_loop
|
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.module = module
|
||||||
self.filename = template_filename
|
self.filename = template_filename
|
||||||
ModuleInfo(
|
ModuleInfo(
|
||||||
|
@ -616,13 +568,12 @@ class DefTemplate(Template):
|
||||||
self.include_error_handler = parent.include_error_handler
|
self.include_error_handler = parent.include_error_handler
|
||||||
self.enable_loop = parent.enable_loop
|
self.enable_loop = parent.enable_loop
|
||||||
self.lookup = parent.lookup
|
self.lookup = parent.lookup
|
||||||
self.bytestring_passthrough = parent.bytestring_passthrough
|
|
||||||
|
|
||||||
def get_def(self, name):
|
def get_def(self, name):
|
||||||
return self.parent.get_def(name)
|
return self.parent.get_def(name)
|
||||||
|
|
||||||
|
|
||||||
class ModuleInfo(object):
|
class ModuleInfo:
|
||||||
|
|
||||||
"""Stores information about a module currently loaded into
|
"""Stores information about a module currently loaded into
|
||||||
memory, provides reverse lookups of template source, module
|
memory, provides reverse lookups of template source, module
|
||||||
|
@ -658,9 +609,9 @@ class ModuleInfo(object):
|
||||||
r"__M_BEGIN_METADATA(.+?)__M_END_METADATA", module_source, re.S
|
r"__M_BEGIN_METADATA(.+?)__M_END_METADATA", module_source, re.S
|
||||||
).group(1)
|
).group(1)
|
||||||
source_map = json.loads(source_map)
|
source_map = json.loads(source_map)
|
||||||
source_map["line_map"] = dict(
|
source_map["line_map"] = {
|
||||||
(int(k), int(v)) for k, v in source_map["line_map"].items()
|
int(k): int(v) for k, v in source_map["line_map"].items()
|
||||||
)
|
}
|
||||||
if full_line_map:
|
if full_line_map:
|
||||||
f_line_map = source_map["full_line_map"] = []
|
f_line_map = source_map["full_line_map"] = []
|
||||||
line_map = source_map["line_map"]
|
line_map = source_map["line_map"]
|
||||||
|
@ -681,28 +632,25 @@ class ModuleInfo(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source(self):
|
def source(self):
|
||||||
if self.template_source is not None:
|
if self.template_source is 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:
|
|
||||||
data = util.read_file(self.template_filename)
|
data = util.read_file(self.template_filename)
|
||||||
if self.module._source_encoding:
|
if self.module._source_encoding:
|
||||||
return data.decode(self.module._source_encoding)
|
return data.decode(self.module._source_encoding)
|
||||||
else:
|
else:
|
||||||
return data
|
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):
|
def _compile(template, text, filename, generate_magic_comment):
|
||||||
lexer = template.lexer_cls(
|
lexer = template.lexer_cls(
|
||||||
text,
|
text,
|
||||||
filename,
|
filename,
|
||||||
disable_unicode=template.disable_unicode,
|
|
||||||
input_encoding=template.input_encoding,
|
input_encoding=template.input_encoding,
|
||||||
preprocessor=template.preprocessor,
|
preprocessor=template.preprocessor,
|
||||||
)
|
)
|
||||||
|
@ -717,7 +665,6 @@ def _compile(template, text, filename, generate_magic_comment):
|
||||||
future_imports=template.future_imports,
|
future_imports=template.future_imports,
|
||||||
source_encoding=lexer.encoding,
|
source_encoding=lexer.encoding,
|
||||||
generate_magic_comment=generate_magic_comment,
|
generate_magic_comment=generate_magic_comment,
|
||||||
disable_unicode=template.disable_unicode,
|
|
||||||
strict_undefined=template.strict_undefined,
|
strict_undefined=template.strict_undefined,
|
||||||
enable_loop=template.enable_loop,
|
enable_loop=template.enable_loop,
|
||||||
reserved_names=template.reserved_names,
|
reserved_names=template.reserved_names,
|
||||||
|
@ -728,15 +675,10 @@ def _compile(template, text, filename, generate_magic_comment):
|
||||||
def _compile_text(template, text, filename):
|
def _compile_text(template, text, filename):
|
||||||
identifier = template.module_id
|
identifier = template.module_id
|
||||||
source, lexer = _compile(
|
source, lexer = _compile(
|
||||||
template,
|
template, text, filename, generate_magic_comment=False
|
||||||
text,
|
|
||||||
filename,
|
|
||||||
generate_magic_comment=template.disable_unicode,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cid = identifier
|
cid = identifier
|
||||||
if not compat.py3k and isinstance(cid, compat.text_type):
|
|
||||||
cid = cid.encode()
|
|
||||||
module = types.ModuleType(cid)
|
module = types.ModuleType(cid)
|
||||||
code = compile(source, cid, "exec")
|
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
|
template, text, filename, generate_magic_comment=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(source, compat.text_type):
|
if isinstance(source, str):
|
||||||
source = source.encode(lexer.encoding or "ascii")
|
source = source.encode(lexer.encoding or "ascii")
|
||||||
|
|
||||||
if module_writer:
|
if module_writer:
|
||||||
|
@ -767,10 +709,7 @@ def _compile_module_file(template, text, filename, outputpath, module_writer):
|
||||||
|
|
||||||
|
|
||||||
def _get_module_info_from_callable(callable_):
|
def _get_module_info_from_callable(callable_):
|
||||||
if compat.py3k:
|
|
||||||
return _get_module_info(callable_.__globals__["__name__"])
|
return _get_module_info(callable_.__globals__["__name__"])
|
||||||
else:
|
|
||||||
return _get_module_info(callable_.func_globals["__name__"])
|
|
||||||
|
|
||||||
|
|
||||||
def _get_module_info(filename):
|
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
|
# 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
|
# This module is part of Mako and is released under
|
||||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from ast import parse
|
from ast import parse
|
||||||
import codecs
|
import codecs
|
||||||
import collections
|
import collections
|
||||||
|
@ -13,7 +11,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import timeit
|
import timeit
|
||||||
|
|
||||||
from mako import compat
|
from .compat import importlib_metadata_get
|
||||||
|
|
||||||
|
|
||||||
def update_wrapper(decorated, fn):
|
def update_wrapper(decorated, fn):
|
||||||
|
@ -22,7 +20,7 @@ def update_wrapper(decorated, fn):
|
||||||
return decorated
|
return decorated
|
||||||
|
|
||||||
|
|
||||||
class PluginLoader(object):
|
class PluginLoader:
|
||||||
def __init__(self, group):
|
def __init__(self, group):
|
||||||
self.group = group
|
self.group = group
|
||||||
self.impls = {}
|
self.impls = {}
|
||||||
|
@ -30,13 +28,12 @@ class PluginLoader(object):
|
||||||
def load(self, name):
|
def load(self, name):
|
||||||
if name in self.impls:
|
if name in self.impls:
|
||||||
return self.impls[name]()
|
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
|
self.impls[name] = impl.load
|
||||||
return impl.load()
|
return impl.load()
|
||||||
else:
|
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
|
|
||||||
raise exceptions.RuntimeException(
|
raise exceptions.RuntimeException(
|
||||||
|
@ -61,7 +58,7 @@ def verify_directory(dir_):
|
||||||
while not os.path.exists(dir_):
|
while not os.path.exists(dir_):
|
||||||
try:
|
try:
|
||||||
tries += 1
|
tries += 1
|
||||||
os.makedirs(dir_, compat.octal("0775"))
|
os.makedirs(dir_, 0o755)
|
||||||
except:
|
except:
|
||||||
if tries > 5:
|
if tries > 5:
|
||||||
raise
|
raise
|
||||||
|
@ -76,7 +73,7 @@ def to_list(x, default=None):
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
class memoized_property(object):
|
class memoized_property:
|
||||||
|
|
||||||
"""A read-only @property that is only evaluated once."""
|
"""A read-only @property that is only evaluated once."""
|
||||||
|
|
||||||
|
@ -92,7 +89,7 @@ class memoized_property(object):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class memoized_instancemethod(object):
|
class memoized_instancemethod:
|
||||||
|
|
||||||
"""Decorate a method memoize its return value.
|
"""Decorate a method memoize its return value.
|
||||||
|
|
||||||
|
@ -140,19 +137,15 @@ class SetLikeDict(dict):
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
class FastEncodingBuffer(object):
|
class FastEncodingBuffer:
|
||||||
|
|
||||||
"""a very rudimentary buffer that is faster than StringIO,
|
"""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.data = collections.deque()
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
if as_unicode:
|
|
||||||
self.delim = compat.u("")
|
|
||||||
else:
|
|
||||||
self.delim = ""
|
self.delim = ""
|
||||||
self.as_unicode = as_unicode
|
|
||||||
self.errors = errors
|
self.errors = errors
|
||||||
self.write = self.data.append
|
self.write = self.data.append
|
||||||
|
|
||||||
|
@ -179,7 +172,7 @@ class LRUCache(dict):
|
||||||
is inexact.
|
is inexact.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class _Item(object):
|
class _Item:
|
||||||
def __init__(self, key, value):
|
def __init__(self, key, value):
|
||||||
self.key = key
|
self.key = key
|
||||||
self.value = value
|
self.value = value
|
||||||
|
@ -203,7 +196,6 @@ class LRUCache(dict):
|
||||||
def setdefault(self, key, value):
|
def setdefault(self, key, value):
|
||||||
if key in self:
|
if key in self:
|
||||||
return self[key]
|
return self[key]
|
||||||
else:
|
|
||||||
self[key] = value
|
self[key] = value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -295,7 +287,7 @@ def sorted_dict_repr(d):
|
||||||
"""
|
"""
|
||||||
keys = list(d.keys())
|
keys = list(d.keys())
|
||||||
keys.sort()
|
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):
|
def restore__ast(_ast):
|
||||||
|
@ -308,7 +300,7 @@ def restore__ast(_ast):
|
||||||
m = compile(
|
m = compile(
|
||||||
"""\
|
"""\
|
||||||
def foo(): pass
|
def foo(): pass
|
||||||
class Bar(object): pass
|
class Bar: pass
|
||||||
if False: pass
|
if False: pass
|
||||||
baz = 'mako'
|
baz = 'mako'
|
||||||
1 + 2 - 3 * 4 / 5
|
1 + 2 - 3 * 4 / 5
|
||||||
|
@ -380,12 +372,8 @@ mako in baz not in mako""",
|
||||||
|
|
||||||
|
|
||||||
def read_file(path, mode="rb"):
|
def read_file(path, mode="rb"):
|
||||||
fp = open(path, mode)
|
with open(path, mode) as fp:
|
||||||
try:
|
return fp.read()
|
||||||
data = fp.read()
|
|
||||||
return data
|
|
||||||
finally:
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
|
|
||||||
def read_python_file(path):
|
def read_python_file(path):
|
||||||
|
|
|
@ -11,9 +11,10 @@ if t.TYPE_CHECKING:
|
||||||
pass
|
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"]:
|
def _simple_escaping_wrapper(name: str) -> t.Callable[..., "Markup"]:
|
||||||
|
@ -92,19 +93,24 @@ class Markup(str):
|
||||||
|
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __mul__(self, num: int) -> "Markup":
|
def __mul__(self, num: "te.SupportsIndex") -> "Markup":
|
||||||
if isinstance(num, int):
|
if isinstance(num, int):
|
||||||
return self.__class__(super().__mul__(num))
|
return self.__class__(super().__mul__(num))
|
||||||
|
|
||||||
return NotImplemented # type: ignore
|
return NotImplemented
|
||||||
|
|
||||||
__rmul__ = __mul__
|
__rmul__ = __mul__
|
||||||
|
|
||||||
def __mod__(self, arg: t.Any) -> "Markup":
|
def __mod__(self, arg: t.Any) -> "Markup":
|
||||||
if isinstance(arg, tuple):
|
if isinstance(arg, tuple):
|
||||||
|
# a tuple of arguments, each wrapped
|
||||||
arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
|
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)
|
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))
|
return self.__class__(super().__mod__(arg))
|
||||||
|
|
||||||
|
@ -153,8 +159,11 @@ class Markup(str):
|
||||||
>>> Markup("Main »\t<em>About</em>").striptags()
|
>>> Markup("Main »\t<em>About</em>").striptags()
|
||||||
'Main » About'
|
'Main » About'
|
||||||
"""
|
"""
|
||||||
stripped = " ".join(_striptags_re.sub("", self).split())
|
# Use two regexes to avoid ambiguous matches.
|
||||||
return Markup(stripped).unescape()
|
value = _strip_comments_re.sub("", self)
|
||||||
|
value = _strip_tags_re.sub("", value)
|
||||||
|
value = " ".join(value.split())
|
||||||
|
return Markup(value).unescape()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def escape(cls, s: t.Any) -> "Markup":
|
def escape(cls, s: t.Any) -> "Markup":
|
||||||
|
@ -280,9 +289,7 @@ try:
|
||||||
from ._speedups import escape as escape
|
from ._speedups import escape as escape
|
||||||
from ._speedups import escape_silent as escape_silent
|
from ._speedups import escape_silent as escape_silent
|
||||||
from ._speedups import soft_str as soft_str
|
from ._speedups import soft_str as soft_str
|
||||||
from ._speedups import soft_unicode
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from ._native import escape as escape
|
from ._native import escape as escape
|
||||||
from ._native import escape_silent as escape_silent # noqa: F401
|
from ._native import escape_silent as escape_silent # noqa: F401
|
||||||
from ._native import soft_str as soft_str # 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 str(s)
|
||||||
|
|
||||||
return 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
|
html5lib==1.1
|
||||||
httpagentparser==1.9.2
|
httpagentparser==1.9.2
|
||||||
idna==3.3
|
idna==3.3
|
||||||
|
importlib-metadata==4.11.3
|
||||||
importlib-resources==5.6.0
|
importlib-resources==5.6.0
|
||||||
git+https://github.com/Tautulli/ipwhois.git@master#egg=ipwhois
|
git+https://github.com/Tautulli/ipwhois.git@master#egg=ipwhois
|
||||||
IPy==1.01
|
IPy==1.01
|
||||||
Mako==1.1.6
|
Mako==1.2.0
|
||||||
MarkupSafe==2.0.1
|
MarkupSafe==2.1.1
|
||||||
musicbrainzngs==0.7.1
|
musicbrainzngs==0.7.1
|
||||||
packaging==21.3
|
packaging==21.3
|
||||||
paho-mqtt==1.6.1
|
paho-mqtt==1.6.1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue