Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/markupsafe/__init__.py: 37%
137 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1import functools
2import re
3import string
4import typing as t
6if t.TYPE_CHECKING: 6 ↛ 7line 6 didn't jump to line 7, because the condition on line 6 was never true
7 import typing_extensions as te
9 class HasHTML(te.Protocol):
10 def __html__(self) -> str:
11 pass
14__version__ = "2.1.1"
16_strip_comments_re = re.compile(r"<!--.*?-->")
17_strip_tags_re = re.compile(r"<.*?>")
20def _simple_escaping_wrapper(name: str) -> t.Callable[..., "Markup"]:
21 orig = getattr(str, name)
23 @functools.wraps(orig)
24 def wrapped(self: "Markup", *args: t.Any, **kwargs: t.Any) -> "Markup":
25 args = _escape_argspec(list(args), enumerate(args), self.escape) # type: ignore
26 _escape_argspec(kwargs, kwargs.items(), self.escape)
27 return self.__class__(orig(self, *args, **kwargs))
29 return wrapped
32class Markup(str):
33 """A string that is ready to be safely inserted into an HTML or XML
34 document, either because it was escaped or because it was marked
35 safe.
37 Passing an object to the constructor converts it to text and wraps
38 it to mark it safe without escaping. To escape the text, use the
39 :meth:`escape` class method instead.
41 >>> Markup("Hello, <em>World</em>!")
42 Markup('Hello, <em>World</em>!')
43 >>> Markup(42)
44 Markup('42')
45 >>> Markup.escape("Hello, <em>World</em>!")
46 Markup('Hello <em>World</em>!')
48 This implements the ``__html__()`` interface that some frameworks
49 use. Passing an object that implements ``__html__()`` will wrap the
50 output of that method, marking it safe.
52 >>> class Foo:
53 ... def __html__(self):
54 ... return '<a href="/foo">foo</a>'
55 ...
56 >>> Markup(Foo())
57 Markup('<a href="/foo">foo</a>')
59 This is a subclass of :class:`str`. It has the same methods, but
60 escapes their arguments and returns a ``Markup`` instance.
62 >>> Markup("<em>%s</em>") % ("foo & bar",)
63 Markup('<em>foo & bar</em>')
64 >>> Markup("<em>Hello</em> ") + "<foo>"
65 Markup('<em>Hello</em> <foo>')
66 """
68 __slots__ = ()
70 def __new__(
71 cls, base: t.Any = "", encoding: t.Optional[str] = None, errors: str = "strict"
72 ) -> "Markup":
73 if hasattr(base, "__html__"):
74 base = base.__html__()
76 if encoding is None:
77 return super().__new__(cls, base)
79 return super().__new__(cls, base, encoding, errors)
81 def __html__(self) -> "Markup":
82 return self
84 def __add__(self, other: t.Union[str, "HasHTML"]) -> "Markup":
85 if isinstance(other, str) or hasattr(other, "__html__"):
86 return self.__class__(super().__add__(self.escape(other)))
88 return NotImplemented
90 def __radd__(self, other: t.Union[str, "HasHTML"]) -> "Markup":
91 if isinstance(other, str) or hasattr(other, "__html__"):
92 return self.escape(other).__add__(self)
94 return NotImplemented
96 def __mul__(self, num: "te.SupportsIndex") -> "Markup":
97 if isinstance(num, int):
98 return self.__class__(super().__mul__(num))
100 return NotImplemented
102 __rmul__ = __mul__
104 def __mod__(self, arg: t.Any) -> "Markup":
105 if isinstance(arg, tuple):
106 # a tuple of arguments, each wrapped
107 arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
108 elif hasattr(type(arg), "__getitem__") and not isinstance(arg, str):
109 # a mapping of arguments, wrapped
110 arg = _MarkupEscapeHelper(arg, self.escape)
111 else:
112 # a single argument, wrapped with the helper and a tuple
113 arg = (_MarkupEscapeHelper(arg, self.escape),)
115 return self.__class__(super().__mod__(arg))
117 def __repr__(self) -> str:
118 return f"{self.__class__.__name__}({super().__repr__()})"
120 def join(self, seq: t.Iterable[t.Union[str, "HasHTML"]]) -> "Markup":
121 return self.__class__(super().join(map(self.escape, seq)))
123 join.__doc__ = str.join.__doc__
125 def split( # type: ignore
126 self, sep: t.Optional[str] = None, maxsplit: int = -1
127 ) -> t.List["Markup"]:
128 return [self.__class__(v) for v in super().split(sep, maxsplit)]
130 split.__doc__ = str.split.__doc__
132 def rsplit( # type: ignore
133 self, sep: t.Optional[str] = None, maxsplit: int = -1
134 ) -> t.List["Markup"]:
135 return [self.__class__(v) for v in super().rsplit(sep, maxsplit)]
137 rsplit.__doc__ = str.rsplit.__doc__
139 def splitlines(self, keepends: bool = False) -> t.List["Markup"]: # type: ignore
140 return [self.__class__(v) for v in super().splitlines(keepends)]
142 splitlines.__doc__ = str.splitlines.__doc__
144 def unescape(self) -> str:
145 """Convert escaped markup back into a text string. This replaces
146 HTML entities with the characters they represent.
148 >>> Markup("Main » <em>About</em>").unescape()
149 'Main » <em>About</em>'
150 """
151 from html import unescape
153 return unescape(str(self))
155 def striptags(self) -> str:
156 """:meth:`unescape` the markup, remove tags, and normalize
157 whitespace to single spaces.
159 >>> Markup("Main »\t<em>About</em>").striptags()
160 'Main » About'
161 """
162 # Use two regexes to avoid ambiguous matches.
163 value = _strip_comments_re.sub("", self)
164 value = _strip_tags_re.sub("", value)
165 value = " ".join(value.split())
166 return Markup(value).unescape()
168 @classmethod
169 def escape(cls, s: t.Any) -> "Markup":
170 """Escape a string. Calls :func:`escape` and ensures that for
171 subclasses the correct type is returned.
172 """
173 rv = escape(s)
175 if rv.__class__ is not cls:
176 return cls(rv)
178 return rv
180 for method in (
181 "__getitem__",
182 "capitalize",
183 "title",
184 "lower",
185 "upper",
186 "replace",
187 "ljust",
188 "rjust",
189 "lstrip",
190 "rstrip",
191 "center",
192 "strip",
193 "translate",
194 "expandtabs",
195 "swapcase",
196 "zfill",
197 ):
198 locals()[method] = _simple_escaping_wrapper(method)
200 del method
202 def partition(self, sep: str) -> t.Tuple["Markup", "Markup", "Markup"]:
203 l, s, r = super().partition(self.escape(sep))
204 cls = self.__class__
205 return cls(l), cls(s), cls(r)
207 def rpartition(self, sep: str) -> t.Tuple["Markup", "Markup", "Markup"]:
208 l, s, r = super().rpartition(self.escape(sep))
209 cls = self.__class__
210 return cls(l), cls(s), cls(r)
212 def format(self, *args: t.Any, **kwargs: t.Any) -> "Markup":
213 formatter = EscapeFormatter(self.escape)
214 return self.__class__(formatter.vformat(self, args, kwargs))
216 def __html_format__(self, format_spec: str) -> "Markup":
217 if format_spec:
218 raise ValueError("Unsupported format specification for Markup.")
220 return self
223class EscapeFormatter(string.Formatter):
224 __slots__ = ("escape",)
226 def __init__(self, escape: t.Callable[[t.Any], Markup]) -> None:
227 self.escape = escape
228 super().__init__()
230 def format_field(self, value: t.Any, format_spec: str) -> str:
231 if hasattr(value, "__html_format__"):
232 rv = value.__html_format__(format_spec)
233 elif hasattr(value, "__html__"):
234 if format_spec:
235 raise ValueError(
236 f"Format specifier {format_spec} given, but {type(value)} does not"
237 " define __html_format__. A class that defines __html__ must define"
238 " __html_format__ to work with format specifiers."
239 )
240 rv = value.__html__()
241 else:
242 # We need to make sure the format spec is str here as
243 # otherwise the wrong callback methods are invoked.
244 rv = string.Formatter.format_field(self, value, str(format_spec))
245 return str(self.escape(rv))
248_ListOrDict = t.TypeVar("_ListOrDict", list, dict)
251def _escape_argspec(
252 obj: _ListOrDict, iterable: t.Iterable[t.Any], escape: t.Callable[[t.Any], Markup]
253) -> _ListOrDict:
254 """Helper for various string-wrapped functions."""
255 for key, value in iterable:
256 if isinstance(value, str) or hasattr(value, "__html__"):
257 obj[key] = escape(value)
259 return obj
262class _MarkupEscapeHelper:
263 """Helper for :meth:`Markup.__mod__`."""
265 __slots__ = ("obj", "escape")
267 def __init__(self, obj: t.Any, escape: t.Callable[[t.Any], Markup]) -> None:
268 self.obj = obj
269 self.escape = escape
271 def __getitem__(self, item: t.Any) -> "_MarkupEscapeHelper":
272 return _MarkupEscapeHelper(self.obj[item], self.escape)
274 def __str__(self) -> str:
275 return str(self.escape(self.obj))
277 def __repr__(self) -> str:
278 return str(self.escape(repr(self.obj)))
280 def __int__(self) -> int:
281 return int(self.obj)
283 def __float__(self) -> float:
284 return float(self.obj)
287# circular import
288try:
289 from ._speedups import escape as escape
290 from ._speedups import escape_silent as escape_silent
291 from ._speedups import soft_str as soft_str
292except ImportError:
293 from ._native import escape as escape
294 from ._native import escape_silent as escape_silent # noqa: F401
295 from ._native import soft_str as soft_str # noqa: F401