Plain is headed towards 1.0! Subscribe for development updates →

 1"""
 2Functions for working with "safe strings": strings that can be displayed safely
 3without further escaping in HTML. Marking something as a "safe string" means
 4that the producer of the string has already turned characters that should not
 5be interpreted by the HTML engine (e.g. '<') into the appropriate entities.
 6"""
 7
 8from functools import wraps
 9
10from plain.utils.functional import keep_lazy
11
12
13class SafeData:
14    __slots__ = ()
15
16    def __html__(self):
17        """
18        Return the html representation of a string for interoperability.
19
20        This allows other template engines to understand Plain's SafeData.
21        """
22        return self
23
24
25class SafeString(str, SafeData):
26    """
27    A str subclass that has been specifically marked as "safe" for HTML output
28    purposes.
29    """
30
31    __slots__ = ()
32
33    def __add__(self, rhs):
34        """
35        Concatenating a safe string with another safe bytestring or
36        safe string is safe. Otherwise, the result is no longer safe.
37        """
38        t = super().__add__(rhs)
39        if isinstance(rhs, SafeData):
40            return SafeString(t)
41        return t
42
43    def __str__(self):
44        return self
45
46
47def _safety_decorator(safety_marker, func):
48    @wraps(func)
49    def wrapper(*args, **kwargs):
50        return safety_marker(func(*args, **kwargs))
51
52    return wrapper
53
54
55@keep_lazy(SafeString)
56def mark_safe(s):
57    """
58    Explicitly mark a string as safe for (HTML) output purposes. The returned
59    object can be used everywhere a string is appropriate.
60
61    If used on a method as a decorator, mark the returned data as safe.
62
63    Can be called multiple times on a single string.
64    """
65    if hasattr(s, "__html__"):
66        return s
67    if callable(s):
68        return _safety_decorator(mark_safe, s)
69    return SafeString(s)