bbf6d9b026
Bugfix for feeds - removed categories related and up - load new books now working - category random now working login page is free of non accessible elements boolean custom column is vivible in UI books with only with certain languages can be shown book shelfs can be deleted from UI Anonymous user view is more resticted Added browse of series in sidebar Dependencys in vendor folder are updated to newer versions (licencs files are now present) Bugfix editing Authors names Made upload on windows working
299 lines
10 KiB
Python
299 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
markupsafe
|
|
~~~~~~~~~~
|
|
|
|
Implements a Markup string.
|
|
|
|
:copyright: (c) 2010 by Armin Ronacher.
|
|
:license: BSD, see LICENSE for more details.
|
|
"""
|
|
import re
|
|
import string
|
|
from collections import Mapping
|
|
from markupsafe._compat import text_type, string_types, int_types, \
|
|
unichr, iteritems, PY2
|
|
|
|
|
|
__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
|
|
|
|
|
|
_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
|
|
_entity_re = re.compile(r'&([^;]+);')
|
|
|
|
|
|
class Markup(text_type):
|
|
r"""Marks a string as being safe for inclusion in HTML/XML output without
|
|
needing to be escaped. This implements the `__html__` interface a couple
|
|
of frameworks and web applications use. :class:`Markup` is a direct
|
|
subclass of `unicode` and provides all the methods of `unicode` just that
|
|
it escapes arguments passed and always returns `Markup`.
|
|
|
|
The `escape` function returns markup objects so that double escaping can't
|
|
happen.
|
|
|
|
The constructor of the :class:`Markup` class can be used for three
|
|
different things: When passed an unicode object it's assumed to be safe,
|
|
when passed an object with an HTML representation (has an `__html__`
|
|
method) that representation is used, otherwise the object passed is
|
|
converted into a unicode string and then assumed to be safe:
|
|
|
|
>>> Markup("Hello <em>World</em>!")
|
|
Markup(u'Hello <em>World</em>!')
|
|
>>> class Foo(object):
|
|
... def __html__(self):
|
|
... return '<a href="#">foo</a>'
|
|
...
|
|
>>> Markup(Foo())
|
|
Markup(u'<a href="#">foo</a>')
|
|
|
|
If you want object passed being always treated as unsafe you can use the
|
|
:meth:`escape` classmethod to create a :class:`Markup` object:
|
|
|
|
>>> Markup.escape("Hello <em>World</em>!")
|
|
Markup(u'Hello <em>World</em>!')
|
|
|
|
Operations on a markup string are markup aware which means that all
|
|
arguments are passed through the :func:`escape` function:
|
|
|
|
>>> em = Markup("<em>%s</em>")
|
|
>>> em % "foo & bar"
|
|
Markup(u'<em>foo & bar</em>')
|
|
>>> strong = Markup("<strong>%(text)s</strong>")
|
|
>>> strong % {'text': '<blink>hacker here</blink>'}
|
|
Markup(u'<strong><blink>hacker here</blink></strong>')
|
|
>>> Markup("<em>Hello</em> ") + "<foo>"
|
|
Markup(u'<em>Hello</em> <foo>')
|
|
"""
|
|
__slots__ = ()
|
|
|
|
def __new__(cls, base=u'', encoding=None, errors='strict'):
|
|
if hasattr(base, '__html__'):
|
|
base = base.__html__()
|
|
if encoding is None:
|
|
return text_type.__new__(cls, base)
|
|
return text_type.__new__(cls, base, encoding, errors)
|
|
|
|
def __html__(self):
|
|
return self
|
|
|
|
def __add__(self, other):
|
|
if isinstance(other, string_types) or hasattr(other, '__html__'):
|
|
return self.__class__(super(Markup, self).__add__(self.escape(other)))
|
|
return NotImplemented
|
|
|
|
def __radd__(self, other):
|
|
if hasattr(other, '__html__') or isinstance(other, string_types):
|
|
return self.escape(other).__add__(self)
|
|
return NotImplemented
|
|
|
|
def __mul__(self, num):
|
|
if isinstance(num, int_types):
|
|
return self.__class__(text_type.__mul__(self, num))
|
|
return NotImplemented
|
|
__rmul__ = __mul__
|
|
|
|
def __mod__(self, arg):
|
|
if isinstance(arg, tuple):
|
|
arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
|
|
else:
|
|
arg = _MarkupEscapeHelper(arg, self.escape)
|
|
return self.__class__(text_type.__mod__(self, arg))
|
|
|
|
def __repr__(self):
|
|
return '%s(%s)' % (
|
|
self.__class__.__name__,
|
|
text_type.__repr__(self)
|
|
)
|
|
|
|
def join(self, seq):
|
|
return self.__class__(text_type.join(self, map(self.escape, seq)))
|
|
join.__doc__ = text_type.join.__doc__
|
|
|
|
def split(self, *args, **kwargs):
|
|
return list(map(self.__class__, text_type.split(self, *args, **kwargs)))
|
|
split.__doc__ = text_type.split.__doc__
|
|
|
|
def rsplit(self, *args, **kwargs):
|
|
return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs)))
|
|
rsplit.__doc__ = text_type.rsplit.__doc__
|
|
|
|
def splitlines(self, *args, **kwargs):
|
|
return list(map(self.__class__, text_type.splitlines(
|
|
self, *args, **kwargs)))
|
|
splitlines.__doc__ = text_type.splitlines.__doc__
|
|
|
|
def unescape(self):
|
|
r"""Unescape markup again into an text_type string. This also resolves
|
|
known HTML4 and XHTML entities:
|
|
|
|
>>> Markup("Main » <em>About</em>").unescape()
|
|
u'Main \xbb <em>About</em>'
|
|
"""
|
|
from markupsafe._constants import HTML_ENTITIES
|
|
def handle_match(m):
|
|
name = m.group(1)
|
|
if name in HTML_ENTITIES:
|
|
return unichr(HTML_ENTITIES[name])
|
|
try:
|
|
if name[:2] in ('#x', '#X'):
|
|
return unichr(int(name[2:], 16))
|
|
elif name.startswith('#'):
|
|
return unichr(int(name[1:]))
|
|
except ValueError:
|
|
pass
|
|
return u''
|
|
return _entity_re.sub(handle_match, text_type(self))
|
|
|
|
def striptags(self):
|
|
r"""Unescape markup into an text_type string and strip all tags. This
|
|
also resolves known HTML4 and XHTML entities. Whitespace is
|
|
normalized to one:
|
|
|
|
>>> Markup("Main » <em>About</em>").striptags()
|
|
u'Main \xbb About'
|
|
"""
|
|
stripped = u' '.join(_striptags_re.sub('', self).split())
|
|
return Markup(stripped).unescape()
|
|
|
|
@classmethod
|
|
def escape(cls, s):
|
|
"""Escape the string. Works like :func:`escape` with the difference
|
|
that for subclasses of :class:`Markup` this function would return the
|
|
correct subclass.
|
|
"""
|
|
rv = escape(s)
|
|
if rv.__class__ is not cls:
|
|
return cls(rv)
|
|
return rv
|
|
|
|
def make_simple_escaping_wrapper(name):
|
|
orig = getattr(text_type, name)
|
|
def func(self, *args, **kwargs):
|
|
args = _escape_argspec(list(args), enumerate(args), self.escape)
|
|
_escape_argspec(kwargs, iteritems(kwargs), self.escape)
|
|
return self.__class__(orig(self, *args, **kwargs))
|
|
func.__name__ = orig.__name__
|
|
func.__doc__ = orig.__doc__
|
|
return func
|
|
|
|
for method in '__getitem__', 'capitalize', \
|
|
'title', 'lower', 'upper', 'replace', 'ljust', \
|
|
'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
|
|
'translate', 'expandtabs', 'swapcase', 'zfill':
|
|
locals()[method] = make_simple_escaping_wrapper(method)
|
|
|
|
# new in python 2.5
|
|
if hasattr(text_type, 'partition'):
|
|
def partition(self, sep):
|
|
return tuple(map(self.__class__,
|
|
text_type.partition(self, self.escape(sep))))
|
|
def rpartition(self, sep):
|
|
return tuple(map(self.__class__,
|
|
text_type.rpartition(self, self.escape(sep))))
|
|
|
|
# new in python 2.6
|
|
if hasattr(text_type, 'format'):
|
|
def format(*args, **kwargs):
|
|
self, args = args[0], args[1:]
|
|
formatter = EscapeFormatter(self.escape)
|
|
kwargs = _MagicFormatMapping(args, kwargs)
|
|
return self.__class__(formatter.vformat(self, args, kwargs))
|
|
|
|
def __html_format__(self, format_spec):
|
|
if format_spec:
|
|
raise ValueError('Unsupported format specification '
|
|
'for Markup.')
|
|
return self
|
|
|
|
# not in python 3
|
|
if hasattr(text_type, '__getslice__'):
|
|
__getslice__ = make_simple_escaping_wrapper('__getslice__')
|
|
|
|
del method, make_simple_escaping_wrapper
|
|
|
|
|
|
class _MagicFormatMapping(Mapping):
|
|
"""This class implements a dummy wrapper to fix a bug in the Python
|
|
standard library for string formatting.
|
|
|
|
See http://bugs.python.org/issue13598 for information about why
|
|
this is necessary.
|
|
"""
|
|
|
|
def __init__(self, args, kwargs):
|
|
self._args = args
|
|
self._kwargs = kwargs
|
|
self._last_index = 0
|
|
|
|
def __getitem__(self, key):
|
|
if key == '':
|
|
idx = self._last_index
|
|
self._last_index += 1
|
|
try:
|
|
return self._args[idx]
|
|
except LookupError:
|
|
pass
|
|
key = str(idx)
|
|
return self._kwargs[key]
|
|
|
|
def __iter__(self):
|
|
return iter(self._kwargs)
|
|
|
|
def __len__(self):
|
|
return len(self._kwargs)
|
|
|
|
|
|
if hasattr(text_type, 'format'):
|
|
class EscapeFormatter(string.Formatter):
|
|
|
|
def __init__(self, escape):
|
|
self.escape = escape
|
|
|
|
def format_field(self, value, format_spec):
|
|
if hasattr(value, '__html_format__'):
|
|
rv = value.__html_format__(format_spec)
|
|
elif hasattr(value, '__html__'):
|
|
if format_spec:
|
|
raise ValueError('No format specification allowed '
|
|
'when formatting an object with '
|
|
'its __html__ method.')
|
|
rv = value.__html__()
|
|
else:
|
|
rv = string.Formatter.format_field(self, value, format_spec)
|
|
return text_type(self.escape(rv))
|
|
|
|
|
|
def _escape_argspec(obj, iterable, escape):
|
|
"""Helper for various string-wrapped functions."""
|
|
for key, value in iterable:
|
|
if hasattr(value, '__html__') or isinstance(value, string_types):
|
|
obj[key] = escape(value)
|
|
return obj
|
|
|
|
|
|
class _MarkupEscapeHelper(object):
|
|
"""Helper for Markup.__mod__"""
|
|
|
|
def __init__(self, obj, escape):
|
|
self.obj = obj
|
|
self.escape = escape
|
|
|
|
__getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape)
|
|
__unicode__ = __str__ = lambda s: text_type(s.escape(s.obj))
|
|
__repr__ = lambda s: str(s.escape(repr(s.obj)))
|
|
__int__ = lambda s: int(s.obj)
|
|
__float__ = lambda s: float(s.obj)
|
|
|
|
|
|
# we have to import it down here as the speedups and native
|
|
# modules imports the markup type which is define above.
|
|
try:
|
|
from markupsafe._speedups import escape, escape_silent, soft_unicode
|
|
except ImportError:
|
|
from markupsafe._native import escape, escape_silent, soft_unicode
|
|
|
|
if not PY2:
|
|
soft_str = soft_unicode
|
|
__all__.append('soft_str')
|