Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 3 commits
  • 2 files changed
  • 0 commit comments
  • 2 contributors
Commits on May 15, 2012
@bukzor tests for subclassing Markup, overriding escape 9312325
Commits on May 27, 2012
@mitsuhiko mitsuhiko Merge pull request #6 from bukzor/subclass_tests
tests for subclassing Markup, overriding escape
7415f6f
Commits on Jul 06, 2012
@bukzor idempotent unescape
If we examine the XML spec for entities, we find that ampersand and
space are not allowed characters in an entity name. I've also modified
the unescape function to not modify unexpected inputs (such as &foo;).
This is a common best practice when dealing with layered systems.

http://www.w3.org/TR/REC-xml/#sec-references

EntityRef   ::=   '&' Name ';'
Name   ::=   NameStartChar (NameChar)*
NameChar   ::=   NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
NameStartChar   ::=   ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
0256da7
Showing with 79 additions and 19 deletions.
  1. +21 −18 markupsafe/__init__.py
  2. +58 −1 markupsafe/tests.py
View
39 markupsafe/__init__.py
@@ -16,7 +16,7 @@
_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
-_entity_re = re.compile(r'&([^;]+);')
+_entity_re = re.compile(r'&([^& ;]+);')
class Markup(unicode):
@@ -75,13 +75,13 @@ def __html__(self):
return self
def __add__(self, other):
- if hasattr(other, '__html__') or isinstance(other, basestring):
- return self.__class__(unicode(self) + unicode(escape(other)))
+ if isinstance(other, basestring) 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, basestring):
- return self.__class__(unicode(escape(other)) + unicode(self))
+ return self.escape(other).__add__(self)
return NotImplemented
def __mul__(self, num):
@@ -92,9 +92,9 @@ def __mul__(self, num):
def __mod__(self, arg):
if isinstance(arg, tuple):
- arg = tuple(imap(_MarkupEscapeHelper, arg))
+ arg = tuple(imap(_MarkupEscapeHelper, arg, self.escape))
else:
- arg = _MarkupEscapeHelper(arg)
+ arg = _MarkupEscapeHelper(arg, self.escape)
return self.__class__(unicode.__mod__(self, arg))
def __repr__(self):
@@ -104,7 +104,7 @@ def __repr__(self):
)
def join(self, seq):
- return self.__class__(unicode.join(self, imap(escape, seq)))
+ return self.__class__(unicode.join(self, imap(self.escape, seq)))
join.__doc__ = unicode.join.__doc__
def split(self, *args, **kwargs):
@@ -138,7 +138,8 @@ def handle_match(m):
return unichr(int(name[1:]))
except ValueError:
pass
- return u''
+ # Don't modify unexpected input.
+ return m.group()
return _entity_re.sub(handle_match, unicode(self))
def striptags(self):
@@ -166,8 +167,8 @@ def escape(cls, s):
def make_wrapper(name):
orig = getattr(unicode, name)
def func(self, *args, **kwargs):
- args = _escape_argspec(list(args), enumerate(args))
- _escape_argspec(kwargs, kwargs.iteritems())
+ args = _escape_argspec(list(args), enumerate(args), self.escape)
+ #_escape_argspec(kwargs, kwargs.iteritems(), None)
return self.__class__(orig(self, *args, **kwargs))
func.__name__ = orig.__name__
func.__doc__ = orig.__doc__
@@ -183,10 +184,10 @@ def func(self, *args, **kwargs):
if hasattr(unicode, 'partition'):
def partition(self, sep):
return tuple(map(self.__class__,
- unicode.partition(self, escape(sep))))
+ unicode.partition(self, self.escape(sep))))
def rpartition(self, sep):
return tuple(map(self.__class__,
- unicode.rpartition(self, escape(sep))))
+ unicode.rpartition(self, self.escape(sep))))
# new in python 2.6
if hasattr(unicode, 'format'):
@@ -199,7 +200,7 @@ def rpartition(self, sep):
del method, make_wrapper
-def _escape_argspec(obj, iterable):
+def _escape_argspec(obj, iterable, escape):
"""Helper for various string-wrapped functions."""
for key, value in iterable:
if hasattr(value, '__html__') or isinstance(value, basestring):
@@ -210,13 +211,13 @@ def _escape_argspec(obj, iterable):
class _MarkupEscapeHelper(object):
"""Helper for Markup.__mod__"""
- def __init__(self, obj):
+ def __init__(self, obj, escape):
self.obj = obj
+ self.escape = escape
- __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
- __str__ = lambda s: str(escape(s.obj))
- __unicode__ = lambda s: unicode(escape(s.obj))
- __repr__ = lambda s: str(escape(repr(s.obj)))
+ __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape)
+ __unicode__ = lambda s: unicode(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)
@@ -227,3 +228,5 @@ def __init__(self, obj):
from markupsafe._speedups import escape, escape_silent, soft_unicode
except ImportError:
from markupsafe._native import escape, escape_silent, soft_unicode
+
+# vim:sts=4:sw=4:et:
View
59 markupsafe/tests.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
import gc
import unittest
from markupsafe import Markup, escape, escape_silent
@@ -18,6 +19,9 @@ def test_markup_operations(self):
'username': '<bad user>'
} == '<em>&lt;bad user&gt;</em>'
+ assert Markup('%i') % 3.14 == '3'
+ assert Markup('%.2f') % 3.14 == '3.14'
+
# an escaped object is markup too
assert type(Markup('foo') + 'bar') is Markup
@@ -35,11 +39,24 @@ def __unicode__(self):
assert Markup('<strong>%s</strong>') % Foo() == \
'<strong><em>awesome</em></strong>'
- # escaping and unescaping
+ # escaping
assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
+
+ def test_unescape(self):
assert Markup("&lt;test&gt;").unescape() == "<test>"
+ assert "jack & tavi are cooler than mike & russ" == \
+ Markup("jack & tavi are cooler than mike &amp; russ").unescape(), \
+ Markup("jack & tavi are cooler than mike &amp; russ").unescape()
+
+ # Test that unescape is idempotent
+ original = '&foo&#x3b;'
+ once = Markup(original).unescape()
+ twice = Markup(once).unescape()
+ expected = "&foo;"
+ assert expected == once == twice, (once, twice)
+
def test_all_set(self):
import markupsafe as markup
for item in markup.__all__:
@@ -64,10 +81,48 @@ def test_markup_leaks(self):
counts.add(len(gc.get_objects()))
assert len(counts) == 1, 'ouch, c extension seems to leak objects'
+class EncodedMarkup(Markup):
+ __slots__ = ()
+ encoding = 'utf8'
+
+ @classmethod
+ def escape(cls, s):
+ if isinstance(s, str):
+ s = s.decode('utf8')
+ return super(EncodedMarkup, cls).escape(s)
+
+class MarkupSubclassTestCase(unittest.TestCase):
+ # The Russian name of Russia (Rossija)
+ russia = u'Россия'
+ utf8 = russia.encode('utf8')
+
+ def test_escape(self):
+ myval = EncodedMarkup.escape(self.utf8)
+ assert myval == self.russia, repr(myval)
+ def test_add(self):
+ myval = EncodedMarkup() + self.utf8
+ assert myval == self.russia, repr(myval)
+ def test_radd(self):
+ myval = self.utf8 + EncodedMarkup()
+ assert myval == self.russia, repr(myval)
+ def test_join(self):
+ myval = EncodedMarkup().join([self.utf8])
+ assert myval == self.russia, repr(myval)
+ def test_partition(self):
+ assert EncodedMarkup(self.russia).partition(self.utf8)[1] == self.russia
+ assert EncodedMarkup(self.russia).rpartition(self.utf8)[1] == self.russia
+ def test_mod(self):
+ assert EncodedMarkup('%s') % self.utf8 == self.russia
+ assert EncodedMarkup('%r') % self.utf8 == escape(repr(self.utf8))
+ def test_strip(self):
+ assert EncodedMarkup(self.russia).strip(self.utf8) == u''
+ assert EncodedMarkup(self.russia).rstrip(self.utf8) == u''
+
def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(MarkupTestCase))
+ suite.addTest(unittest.makeSuite(MarkupSubclassTestCase))
# this test only tests the c extension
if not hasattr(escape, 'func_code'):
@@ -78,3 +133,5 @@ def suite():
if __name__ == '__main__':
unittest.main(defaultTest='suite')
+
+# vim:sts=4:sw=4:et:

No commit comments for this range

Something went wrong with that request. Please try again.