Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 90 lines (79 sloc) 2.513 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
import array
from __pypy__.builders import UnicodeBuilder

from markupsafe import Markup


ESCAPED_CHARS_TABLE_SIZE = 256
ESCAPED_CHARS_DELTA_LEN = array.array("l", [0]) * ESCAPED_CHARS_TABLE_SIZE
ESCAPED_CHARS_REPL = [""] * ESCAPED_CHARS_TABLE_SIZE
for c, repl in [
    ('"', u"""),
    ("'", u"'"),
    ("&", u"&"),
    ("<", u"&lt;"),
    (">", u"&gt;"),
]:
    ESCAPED_CHARS_DELTA_LEN[ord(c)] = len(repl)
    ESCAPED_CHARS_REPL[ord(c)] = repl

def escape(s):
    if hasattr(s, '__html__'):
        return s.__html__()
    s = unicode(s)

    i = 0
    delta = 0
    repls = 0
    # Find the total number of replacements, and their total distance.
    while i < len(s):
        c = ord(s[i])
        try:
            d = ESCAPED_CHARS_DELTA_LEN[c]
        except IndexError:
            pass
        else:
            delta += d
            # Obscene performance hack, d >> 2 returns 0 for 0 or 1 for 4 and 5
            # which are the only values that can be here.
            repls += d >> 2
        i += 1
        # Performance hack, can go away when PyPy's bridges are better
        # optimized
        del c
        del d
    # If there are no replacements just return the original string.
    if not repls:
        return s

    res = UnicodeBuilder(len(s) + delta)
    in_idx = 0
    # While there are still replcaements in the string.
    while repls > 0:
        repls -= 1
        next_escp = in_idx
        # Find the next escape
        while next_escp < len(s):
            if (ord(s[next_escp]) < len(ESCAPED_CHARS_DELTA_LEN) and
                ESCAPED_CHARS_DELTA_LEN[ord(s[next_escp])]):
                break
            next_escp += 1
        # If we moved anywhere between escapes, copy that data.
        if next_escp > in_idx:
            res.append_slice(s, in_idx, next_escp)
        res.append(ESCAPED_CHARS_REPL[ord(s[next_escp])])
        in_idx = next_escp + 1
        # Another performance hack
        del next_escp
    # If there's anything left of the string, copy it over.
    if in_idx < len(s):
        res.append_slice(s, in_idx, len(s))
    return Markup(res.build())


def escape_silent(s):
    """Like :func:`escape` but converts `None` into an empty
markup string.
"""
    if s is None:
        return Markup()
    return escape(s)


def soft_unicode(s):
    """Make a string unicode if it isn't already. That way a markup
string is not converted back to unicode.
"""
    if not isinstance(s, unicode):
        s = unicode(s)
    return s
Something went wrong with that request. Please try again.