MarkupSafe-1.1.0/0000755000175000017500000000000013370074515013754 5ustar daviddavid00000000000000MarkupSafe-1.1.0/CHANGES.rst0000644000175000017500000000312513370074202015550 0ustar daviddavid00000000000000Version 1.1.0 ------------- Released 2018-11-05 - Drop support for Python 2.6 and 3.3. - Build wheels for Linux, Mac, and Windows, allowing systems without a compiler to take advantage of the C extension speedups. (`#104`_) - Use newer CPython API on Python 3, resulting in a 1.5x speedup. (`#64`_) - ``escape`` wraps ``__html__`` result in ``Markup``, consistent with documented behavior. (`#69`_) .. _#64: https://github.com/pallets/markupsafe/pull/64 .. _#69: https://github.com/pallets/markupsafe/pull/69 .. _#104: https://github.com/pallets/markupsafe/pull/104 Version 1.0 ----------- Released 2017-03-07 - Fixed custom types not invoking ``__unicode__`` when used with ``format()``. - Added ``__version__`` module attribute. - Improve unescape code to leave lone ampersands alone. Version 0.18 ------------ Released 2013-05-22 - Fixed ``__mul__`` and string splitting on Python 3. Version 0.17 ------------ Released 2013-05-21 - Fixed a bug with broken interpolation on tuples. Version 0.16 ------------ Released 2013-05-20 - Improved Python 3 Support and removed 2to3. - Removed support for Python 3.2 and 2.5. Version 0.15 ------------ Released 2011-07-20 - Fixed a typo that caused the library to fail to install on pypy and jython. Version 0.14 ------------ Released 2011-07-20 - Release fix for 0.13. Version 0.13 ------------ Released 2011-07-20 - Do not attempt to compile extension for PyPy or Jython. - Work around some 64bit Windows issues. Version 0.12 ------------ Released 2011-02-17 - Improved PyPy compatibility. MarkupSafe-1.1.0/LICENSE.rst0000644000175000017500000000311613370050016015557 0ustar daviddavid00000000000000`BSD 3-Clause `_ Copyright © 2010 by the Pallets team. Some rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MarkupSafe-1.1.0/MANIFEST.in0000644000175000017500000000021413370074202015500 0ustar daviddavid00000000000000include CHANGES.rst include LICENSE.rst include README.rst include tox.ini graft docs prune docs/_build graft tests global-exclude *.py[co] MarkupSafe-1.1.0/PKG-INFO0000644000175000017500000001006413370074515015052 0ustar daviddavid00000000000000Metadata-Version: 1.2 Name: MarkupSafe Version: 1.1.0 Summary: Safely add untrusted strings to HTML/XML markup. Home-page: https://www.palletsprojects.com/p/markupsafe/ Author: Armin Ronacher Author-email: armin.ronacher@active-4.com Maintainer: Pallets Team Maintainer-email: contact@palletsprojects.com License: BSD Project-URL: Documentation, https://markupsafe.palletsprojects.com/ Project-URL: Code, https://github.com/pallets/markupsafe Project-URL: Issue tracker, https://github.com/pallets/markupsafe/issues Description: MarkupSafe ========== MarkupSafe implements a text object that escapes characters so it is safe to use in HTML and XML. Characters that have special meanings are replaced so that they display as the actual characters. This mitigates injection attacks, meaning untrusted user input can safely be displayed on a page. Installing ---------- Install and update using `pip`_: .. code-block:: text pip install -U MarkupSafe .. _pip: https://pip.pypa.io/en/stable/quickstart/ Examples -------- .. code-block:: pycon >>> from markupsafe import Markup, escape >>> # escape replaces special characters and wraps in Markup >>> escape('') Markup(u'<script>alert(document.cookie);</script>') >>> # wrap in Markup to mark text "safe" and prevent escaping >>> Markup('Hello') Markup('hello') >>> escape(Markup('Hello')) Markup('hello') >>> # Markup is a text subclass (str on Python 3, unicode on Python 2) >>> # methods and operators escape their arguments >>> template = Markup("Hello %s") >>> template % '"World"' Markup('Hello "World"') Donate ------ The Pallets organization develops and supports MarkupSafe and other libraries that use it. In order to grow the community of contributors and users, and allow the maintainers to devote more time to the projects, `please donate today`_. .. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20 Links ----- * Website: https://www.palletsprojects.com/p/markupsafe/ * Documentation: https://markupsafe.palletsprojects.com/ * License: `BSD `_ * Releases: https://pypi.org/project/MarkupSafe/ * Code: https://github.com/pallets/markupsafe * Issue tracker: https://github.com/pallets/markupsafe/issues * Test status: * Linux, Mac: https://travis-ci.org/pallets/markupsafe * Windows: https://ci.appveyor.com/project/pallets/markupsafe * Test coverage: https://codecov.io/gh/pallets/markupsafe Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing :: Markup :: HTML Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* MarkupSafe-1.1.0/README.rst0000644000175000017500000000417613370074202015444 0ustar daviddavid00000000000000MarkupSafe ========== MarkupSafe implements a text object that escapes characters so it is safe to use in HTML and XML. Characters that have special meanings are replaced so that they display as the actual characters. This mitigates injection attacks, meaning untrusted user input can safely be displayed on a page. Installing ---------- Install and update using `pip`_: .. code-block:: text pip install -U MarkupSafe .. _pip: https://pip.pypa.io/en/stable/quickstart/ Examples -------- .. code-block:: pycon >>> from markupsafe import Markup, escape >>> # escape replaces special characters and wraps in Markup >>> escape('') Markup(u'<script>alert(document.cookie);</script>') >>> # wrap in Markup to mark text "safe" and prevent escaping >>> Markup('Hello') Markup('hello') >>> escape(Markup('Hello')) Markup('hello') >>> # Markup is a text subclass (str on Python 3, unicode on Python 2) >>> # methods and operators escape their arguments >>> template = Markup("Hello %s") >>> template % '"World"' Markup('Hello "World"') Donate ------ The Pallets organization develops and supports MarkupSafe and other libraries that use it. In order to grow the community of contributors and users, and allow the maintainers to devote more time to the projects, `please donate today`_. .. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20 Links ----- * Website: https://www.palletsprojects.com/p/markupsafe/ * Documentation: https://markupsafe.palletsprojects.com/ * License: `BSD `_ * Releases: https://pypi.org/project/MarkupSafe/ * Code: https://github.com/pallets/markupsafe * Issue tracker: https://github.com/pallets/markupsafe/issues * Test status: * Linux, Mac: https://travis-ci.org/pallets/markupsafe * Windows: https://ci.appveyor.com/project/pallets/markupsafe * Test coverage: https://codecov.io/gh/pallets/markupsafe MarkupSafe-1.1.0/docs/0000755000175000017500000000000013370074515014704 5ustar daviddavid00000000000000MarkupSafe-1.1.0/docs/Makefile0000644000175000017500000000110513366626772016356 0ustar daviddavid00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) MarkupSafe-1.1.0/docs/changes.rst0000644000175000017500000000005513370050016017034 0ustar daviddavid00000000000000Changes ======= .. include:: ../CHANGES.rst MarkupSafe-1.1.0/docs/conf.py0000644000175000017500000000313513370050016016173 0ustar daviddavid00000000000000from pallets_sphinx_themes import get_version from pallets_sphinx_themes import ProjectLink # Project -------------------------------------------------------------- project = "MarkupSafe" copyright = "2010 Pallets Team" author = "Pallets Team" release, version = get_version("MarkupSafe") # General -------------------------------------------------------------- master_doc = "index" extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", "pallets_sphinx_themes"] intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} # HTML ----------------------------------------------------------------- html_theme = "flask" html_theme_options = {"index_sidebar_logo": False} html_context = { "project_links": [ ProjectLink("Donate to Pallets", "https://palletsprojects.com/donate"), ProjectLink("Website", "https://palletsprojects.com/p/markupsafe/"), ProjectLink("PyPI releases", "https://pypi.org/project/MarkupSafe/"), ProjectLink("Source Code", "https://github.com/pallets/markupsafe/"), ProjectLink("Issue Tracker", "https://github.com/pallets/markupsafe/issues/"), ] } html_sidebars = { "index": ["project.html", "localtoc.html", "searchbox.html"], "**": ["localtoc.html", "relations.html", "searchbox.html"], } singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]} html_title = "MarkupSafe Documentation ({})".format(version) html_show_sourcelink = False # LaTeX ---------------------------------------------------------------- latex_documents = [ (master_doc, "MarkupSafe-{}.tex".format(version), html_title, author, "manual") ] MarkupSafe-1.1.0/docs/escaping.rst0000644000175000017500000000050213357411204017217 0ustar daviddavid00000000000000.. module:: markupsafe Working With Safe Text ====================== .. autofunction:: escape .. autoclass:: Markup :members: escape, unescape, striptags Optional Values --------------- .. autofunction:: escape_silent Convert an Object to a String ----------------------------- .. autofunction:: soft_unicode MarkupSafe-1.1.0/docs/formatting.rst0000644000175000017500000000444713357411204017614 0ustar daviddavid00000000000000.. currentmodule:: markupsafe String Formatting ================= The :class:`Markup` class can be used as a format string. Objects formatted into a markup string will be escaped first. Format Method ------------- The ``format`` method extends the standard :meth:`str.format` behavior to use an ``__html_format__`` method. #. If an object has an ``__html_format__`` method, it is called as a replacement for the ``__format__`` method. It is passed a format specifier if it's given. The method must return a string or :class:`Markup` instance. #. If an object has an ``__html__`` method, it is called. If a format specifier was passed and the class defined ``__html__`` but not ``__html_format__``, a ``ValueError`` is raised. #. Otherwise Python's default format behavior is used and the result is escaped. For example, to implement a ``User`` that wraps its ``name`` in a ``span`` tag, and adds a link when using the ``'link'`` format specifier: .. code-block:: python class User(object): def __init__(self, id, name): self.id = id self.name = name def __html_format__(self, format_spec): if format_spec == 'link': return Markup( '{}' ).format(self.id, self.__html__()) elif format_spec: raise ValueError('Invalid format spec') return self.__html__() def __html__(self): return Markup( '{0}' ).format(self.name) .. code-block:: pycon >>> user = User(3, '') Markup(u'<script>alert(document.cookie);</script>') >>> # wrap in Markup to mark text "safe" and prevent escaping >>> Markup('Hello') Markup('hello') >>> escape(Markup('Hello')) Markup('hello') >>> # Markup is a text subclass (str on Python 3, unicode on Python 2) >>> # methods and operators escape their arguments >>> template = Markup("Hello %s") >>> template % '"World"' Markup('Hello "World"') Donate ------ The Pallets organization develops and supports MarkupSafe and other libraries that use it. In order to grow the community of contributors and users, and allow the maintainers to devote more time to the projects, `please donate today`_. .. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20 Links ----- * Website: https://www.palletsprojects.com/p/markupsafe/ * Documentation: https://markupsafe.palletsprojects.com/ * License: `BSD `_ * Releases: https://pypi.org/project/MarkupSafe/ * Code: https://github.com/pallets/markupsafe * Issue tracker: https://github.com/pallets/markupsafe/issues * Test status: * Linux, Mac: https://travis-ci.org/pallets/markupsafe * Windows: https://ci.appveyor.com/project/pallets/markupsafe * Test coverage: https://codecov.io/gh/pallets/markupsafe Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing :: Markup :: HTML Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* MarkupSafe-1.1.0/src/MarkupSafe.egg-info/SOURCES.txt0000644000175000017500000000113713370074515022161 0ustar daviddavid00000000000000CHANGES.rst LICENSE.rst MANIFEST.in README.rst setup.cfg setup.py tox.ini docs/Makefile docs/changes.rst docs/conf.py docs/escaping.rst docs/formatting.rst docs/html.rst docs/index.rst docs/license.rst docs/make.bat docs/requirements.txt src/MarkupSafe.egg-info/PKG-INFO src/MarkupSafe.egg-info/SOURCES.txt src/MarkupSafe.egg-info/dependency_links.txt src/MarkupSafe.egg-info/top_level.txt src/markupsafe/__init__.py src/markupsafe/_compat.py src/markupsafe/_constants.py src/markupsafe/_native.py src/markupsafe/_speedups.c tests/conftest.py tests/test_escape.py tests/test_leak.py tests/test_markupsafe.pyMarkupSafe-1.1.0/src/MarkupSafe.egg-info/dependency_links.txt0000644000175000017500000000000113370074515024341 0ustar daviddavid00000000000000 MarkupSafe-1.1.0/src/MarkupSafe.egg-info/top_level.txt0000644000175000017500000000001313370074515023017 0ustar daviddavid00000000000000markupsafe MarkupSafe-1.1.0/src/markupsafe/0000755000175000017500000000000013370074515016701 5ustar daviddavid00000000000000MarkupSafe-1.1.0/src/markupsafe/__init__.py0000644000175000017500000002366413370074202021016 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ markupsafe ~~~~~~~~~~ Implements an escape function and a Markup string to replace HTML special characters with safe representations. :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ import re import string from ._compat import int_types from ._compat import iteritems from ._compat import Mapping from ._compat import PY2 from ._compat import string_types from ._compat import text_type from ._compat import unichr __version__ = "1.1.0" __all__ = ["Markup", "soft_unicode", "escape", "escape_silent"] _striptags_re = re.compile(r"(|<[^>]*>)") _entity_re = re.compile(r"&([^& ;]+);") class Markup(text_type): """A string that is ready to be safely inserted into an HTML or XML document, either because it was escaped or because it was marked safe. Passing an object to the constructor converts it to text and wraps it to mark it safe without escaping. To escape the text, use the :meth:`escape` class method instead. >>> Markup('Hello, World!') Markup('Hello, World!') >>> Markup(42) Markup('42') >>> Markup.escape('Hello, World!') Markup('Hello <em>World</em>!') This implements the ``__html__()`` interface that some frameworks use. Passing an object that implements ``__html__()`` will wrap the output of that method, marking it safe. >>> class Foo: ... def __html__(self): ... return 'foo' ... >>> Markup(Foo()) Markup('foo') This is a subclass of the text type (``str`` in Python 3, ``unicode`` in Python 2). It has the same methods as that type, but all methods escape their arguments and return a ``Markup`` instance. >>> Markup('%s') % 'foo & bar' Markup('foo & bar') >>> Markup('Hello ') + '' Markup('Hello <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): """Convert escaped markup back into a text string. This replaces HTML entities with the characters they represent. >>> Markup('Main » About').unescape() 'Main » About' """ from ._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 # Don't modify unexpected input. return m.group() return _entity_re.sub(handle_match, text_type(self)) def striptags(self): """:meth:`unescape` the markup, remove tags, and normalize whitespace to single spaces. >>> Markup('Main »\tAbout').striptags() 'Main » About' """ stripped = u" ".join(_striptags_re.sub("", self).split()) return Markup(stripped).unescape() @classmethod def escape(cls, s): """Escape a string. Calls :func:`escape` and ensures that for subclasses the correct type is returned. """ rv = escape(s) if rv.__class__ is not cls: return cls(rv) return rv def make_simple_escaping_wrapper(name): # noqa: B902 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) 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)))) def format(self, *args, **kwargs): 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( "Format specifier {0} given, but {1} does not" " define __html_format__. A class that defines" " __html__ must define __html_format__ to work" " with format specifiers.".format(format_spec, type(value)) ) rv = value.__html__() else: # We need to make sure the format spec is unicode here as # otherwise the wrong callback methods are invoked. For # instance a byte string there would invoke __str__ and # not __unicode__. rv = string.Formatter.format_field(self, value, text_type(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 def __getitem__(self, item): return _MarkupEscapeHelper(self.obj[item], self.escape) def __str__(self): return text_type(self.escape(self.obj)) __unicode__ = __str__ def __repr__(self): return str(self.escape(repr(self.obj))) def __int__(self): return int(self.obj) def __float__(self): return float(self.obj) # we have to import it down here as the speedups and native # modules imports the markup type which is define above. try: from ._speedups import escape, escape_silent, soft_unicode except ImportError: from ._native import escape, escape_silent, soft_unicode if not PY2: soft_str = soft_unicode __all__.append("soft_str") MarkupSafe-1.1.0/src/markupsafe/_compat.py0000644000175000017500000000112413370050016020661 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ markupsafe._compat ~~~~~~~~~~~~~~~~~~ :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ import sys PY2 = sys.version_info[0] == 2 if not PY2: text_type = str string_types = (str,) unichr = chr int_types = (int,) def iteritems(x): return iter(x.items()) from collections.abc import Mapping else: text_type = unicode string_types = (str, unicode) unichr = unichr int_types = (int, long) def iteritems(x): return x.iteritems() from collections import Mapping MarkupSafe-1.1.0/src/markupsafe/_constants.py0000644000175000017500000001117013370050016021414 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ markupsafe._constants ~~~~~~~~~~~~~~~~~~~~~ :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ HTML_ENTITIES = { "AElig": 198, "Aacute": 193, "Acirc": 194, "Agrave": 192, "Alpha": 913, "Aring": 197, "Atilde": 195, "Auml": 196, "Beta": 914, "Ccedil": 199, "Chi": 935, "Dagger": 8225, "Delta": 916, "ETH": 208, "Eacute": 201, "Ecirc": 202, "Egrave": 200, "Epsilon": 917, "Eta": 919, "Euml": 203, "Gamma": 915, "Iacute": 205, "Icirc": 206, "Igrave": 204, "Iota": 921, "Iuml": 207, "Kappa": 922, "Lambda": 923, "Mu": 924, "Ntilde": 209, "Nu": 925, "OElig": 338, "Oacute": 211, "Ocirc": 212, "Ograve": 210, "Omega": 937, "Omicron": 927, "Oslash": 216, "Otilde": 213, "Ouml": 214, "Phi": 934, "Pi": 928, "Prime": 8243, "Psi": 936, "Rho": 929, "Scaron": 352, "Sigma": 931, "THORN": 222, "Tau": 932, "Theta": 920, "Uacute": 218, "Ucirc": 219, "Ugrave": 217, "Upsilon": 933, "Uuml": 220, "Xi": 926, "Yacute": 221, "Yuml": 376, "Zeta": 918, "aacute": 225, "acirc": 226, "acute": 180, "aelig": 230, "agrave": 224, "alefsym": 8501, "alpha": 945, "amp": 38, "and": 8743, "ang": 8736, "apos": 39, "aring": 229, "asymp": 8776, "atilde": 227, "auml": 228, "bdquo": 8222, "beta": 946, "brvbar": 166, "bull": 8226, "cap": 8745, "ccedil": 231, "cedil": 184, "cent": 162, "chi": 967, "circ": 710, "clubs": 9827, "cong": 8773, "copy": 169, "crarr": 8629, "cup": 8746, "curren": 164, "dArr": 8659, "dagger": 8224, "darr": 8595, "deg": 176, "delta": 948, "diams": 9830, "divide": 247, "eacute": 233, "ecirc": 234, "egrave": 232, "empty": 8709, "emsp": 8195, "ensp": 8194, "epsilon": 949, "equiv": 8801, "eta": 951, "eth": 240, "euml": 235, "euro": 8364, "exist": 8707, "fnof": 402, "forall": 8704, "frac12": 189, "frac14": 188, "frac34": 190, "frasl": 8260, "gamma": 947, "ge": 8805, "gt": 62, "hArr": 8660, "harr": 8596, "hearts": 9829, "hellip": 8230, "iacute": 237, "icirc": 238, "iexcl": 161, "igrave": 236, "image": 8465, "infin": 8734, "int": 8747, "iota": 953, "iquest": 191, "isin": 8712, "iuml": 239, "kappa": 954, "lArr": 8656, "lambda": 955, "lang": 9001, "laquo": 171, "larr": 8592, "lceil": 8968, "ldquo": 8220, "le": 8804, "lfloor": 8970, "lowast": 8727, "loz": 9674, "lrm": 8206, "lsaquo": 8249, "lsquo": 8216, "lt": 60, "macr": 175, "mdash": 8212, "micro": 181, "middot": 183, "minus": 8722, "mu": 956, "nabla": 8711, "nbsp": 160, "ndash": 8211, "ne": 8800, "ni": 8715, "not": 172, "notin": 8713, "nsub": 8836, "ntilde": 241, "nu": 957, "oacute": 243, "ocirc": 244, "oelig": 339, "ograve": 242, "oline": 8254, "omega": 969, "omicron": 959, "oplus": 8853, "or": 8744, "ordf": 170, "ordm": 186, "oslash": 248, "otilde": 245, "otimes": 8855, "ouml": 246, "para": 182, "part": 8706, "permil": 8240, "perp": 8869, "phi": 966, "pi": 960, "piv": 982, "plusmn": 177, "pound": 163, "prime": 8242, "prod": 8719, "prop": 8733, "psi": 968, "quot": 34, "rArr": 8658, "radic": 8730, "rang": 9002, "raquo": 187, "rarr": 8594, "rceil": 8969, "rdquo": 8221, "real": 8476, "reg": 174, "rfloor": 8971, "rho": 961, "rlm": 8207, "rsaquo": 8250, "rsquo": 8217, "sbquo": 8218, "scaron": 353, "sdot": 8901, "sect": 167, "shy": 173, "sigma": 963, "sigmaf": 962, "sim": 8764, "spades": 9824, "sub": 8834, "sube": 8838, "sum": 8721, "sup": 8835, "sup1": 185, "sup2": 178, "sup3": 179, "supe": 8839, "szlig": 223, "tau": 964, "there4": 8756, "theta": 952, "thetasym": 977, "thinsp": 8201, "thorn": 254, "tilde": 732, "times": 215, "trade": 8482, "uArr": 8657, "uacute": 250, "uarr": 8593, "ucirc": 251, "ugrave": 249, "uml": 168, "upsih": 978, "upsilon": 965, "uuml": 252, "weierp": 8472, "xi": 958, "yacute": 253, "yen": 165, "yuml": 255, "zeta": 950, "zwj": 8205, "zwnj": 8204, } MarkupSafe-1.1.0/src/markupsafe/_native.py0000644000175000017500000000356713370050016020701 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ markupsafe._native ~~~~~~~~~~~~~~~~~~ Native Python implementation used when the C module is not compiled. :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ from . import Markup from ._compat import text_type def escape(s): """Replace the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in the string with HTML-safe sequences. Use this if you need to display text that might contain such characters in HTML. If the object has an ``__html__`` method, it is called and the return value is assumed to already be safe for HTML. :param s: An object to be converted to a string and escaped. :return: A :class:`Markup` string with the escaped text. """ if hasattr(s, "__html__"): return Markup(s.__html__()) return Markup( text_type(s) .replace("&", "&") .replace(">", ">") .replace("<", "<") .replace("'", "'") .replace('"', """) ) def escape_silent(s): """Like :func:`escape` but treats ``None`` as the empty string. Useful with optional values, as otherwise you get the string ``'None'`` when the value is ``None``. >>> escape(None) Markup('None') >>> escape_silent(None) Markup('') """ if s is None: return Markup() return escape(s) def soft_unicode(s): """Convert an object to a string if it isn't already. This preserves a :class:`Markup` string rather than converting it back to a basic string, so it will still be marked as safe and won't be escaped again. >>> value = escape('') >>> value Markup('<User 1>') >>> escape(str(value)) Markup('&lt;User 1&gt;') >>> escape(soft_unicode(value)) Markup('<User 1>') """ if not isinstance(s, text_type): s = text_type(s) return s MarkupSafe-1.1.0/src/markupsafe/_speedups.c0000644000175000017500000002323313366626772021054 0ustar daviddavid00000000000000/** * markupsafe._speedups * ~~~~~~~~~~~~~~~~~~~~ * * C implementation of escaping for better performance. Used instead of * the native Python implementation when compiled. * * :copyright: © 2010 by the Pallets team. * :license: BSD, see LICENSE for more details. */ #include #if PY_MAJOR_VERSION < 3 #define ESCAPED_CHARS_TABLE_SIZE 63 #define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL))); static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE]; static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE]; #endif static PyObject* markup; static int init_constants(void) { PyObject *module; #if PY_MAJOR_VERSION < 3 /* mapping of characters to replace */ escaped_chars_repl['"'] = UNICHR("""); escaped_chars_repl['\''] = UNICHR("'"); escaped_chars_repl['&'] = UNICHR("&"); escaped_chars_repl['<'] = UNICHR("<"); escaped_chars_repl['>'] = UNICHR(">"); /* lengths of those characters when replaced - 1 */ memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len)); escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \ escaped_chars_delta_len['&'] = 4; escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3; #endif /* import markup type so that we can mark the return value */ module = PyImport_ImportModule("markupsafe"); if (!module) return 0; markup = PyObject_GetAttrString(module, "Markup"); Py_DECREF(module); return 1; } #if PY_MAJOR_VERSION < 3 static PyObject* escape_unicode(PyUnicodeObject *in) { PyUnicodeObject *out; Py_UNICODE *inp = PyUnicode_AS_UNICODE(in); const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in); Py_UNICODE *next_escp; Py_UNICODE *outp; Py_ssize_t delta=0, erepl=0, delta_len=0; /* First we need to figure out how long the escaped string will be */ while (*(inp) || inp < inp_end) { if (*inp < ESCAPED_CHARS_TABLE_SIZE) { delta += escaped_chars_delta_len[*inp]; erepl += !!escaped_chars_delta_len[*inp]; } ++inp; } /* Do we need to escape anything at all? */ if (!erepl) { Py_INCREF(in); return (PyObject*)in; } out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta); if (!out) return NULL; outp = PyUnicode_AS_UNICODE(out); inp = PyUnicode_AS_UNICODE(in); while (erepl-- > 0) { /* look for the next substitution */ next_escp = inp; while (next_escp < inp_end) { if (*next_escp < ESCAPED_CHARS_TABLE_SIZE && (delta_len = escaped_chars_delta_len[*next_escp])) { ++delta_len; break; } ++next_escp; } if (next_escp > inp) { /* copy unescaped chars between inp and next_escp */ Py_UNICODE_COPY(outp, inp, next_escp-inp); outp += next_escp - inp; } /* escape 'next_escp' */ Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len); outp += delta_len; inp = next_escp + 1; } if (inp < inp_end) Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in))); return (PyObject*)out; } #else /* PY_MAJOR_VERSION < 3 */ #define GET_DELTA(inp, inp_end, delta) \ while (inp < inp_end) { \ switch (*inp++) { \ case '"': \ case '\'': \ case '&': \ delta += 4; \ break; \ case '<': \ case '>': \ delta += 3; \ break; \ } \ } #define DO_ESCAPE(inp, inp_end, outp) \ { \ Py_ssize_t ncopy = 0; \ while (inp < inp_end) { \ switch (*inp) { \ case '"': \ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ outp += ncopy; ncopy = 0; \ *outp++ = '&'; \ *outp++ = '#'; \ *outp++ = '3'; \ *outp++ = '4'; \ *outp++ = ';'; \ break; \ case '\'': \ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ outp += ncopy; ncopy = 0; \ *outp++ = '&'; \ *outp++ = '#'; \ *outp++ = '3'; \ *outp++ = '9'; \ *outp++ = ';'; \ break; \ case '&': \ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ outp += ncopy; ncopy = 0; \ *outp++ = '&'; \ *outp++ = 'a'; \ *outp++ = 'm'; \ *outp++ = 'p'; \ *outp++ = ';'; \ break; \ case '<': \ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ outp += ncopy; ncopy = 0; \ *outp++ = '&'; \ *outp++ = 'l'; \ *outp++ = 't'; \ *outp++ = ';'; \ break; \ case '>': \ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ outp += ncopy; ncopy = 0; \ *outp++ = '&'; \ *outp++ = 'g'; \ *outp++ = 't'; \ *outp++ = ';'; \ break; \ default: \ ncopy++; \ } \ inp++; \ } \ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ } static PyObject* escape_unicode_kind1(PyUnicodeObject *in) { Py_UCS1 *inp = PyUnicode_1BYTE_DATA(in); Py_UCS1 *inp_end = inp + PyUnicode_GET_LENGTH(in); Py_UCS1 *outp; PyObject *out; Py_ssize_t delta = 0; GET_DELTA(inp, inp_end, delta); if (!delta) { Py_INCREF(in); return (PyObject*)in; } out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, PyUnicode_IS_ASCII(in) ? 127 : 255); if (!out) return NULL; inp = PyUnicode_1BYTE_DATA(in); outp = PyUnicode_1BYTE_DATA(out); DO_ESCAPE(inp, inp_end, outp); return out; } static PyObject* escape_unicode_kind2(PyUnicodeObject *in) { Py_UCS2 *inp = PyUnicode_2BYTE_DATA(in); Py_UCS2 *inp_end = inp + PyUnicode_GET_LENGTH(in); Py_UCS2 *outp; PyObject *out; Py_ssize_t delta = 0; GET_DELTA(inp, inp_end, delta); if (!delta) { Py_INCREF(in); return (PyObject*)in; } out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 65535); if (!out) return NULL; inp = PyUnicode_2BYTE_DATA(in); outp = PyUnicode_2BYTE_DATA(out); DO_ESCAPE(inp, inp_end, outp); return out; } static PyObject* escape_unicode_kind4(PyUnicodeObject *in) { Py_UCS4 *inp = PyUnicode_4BYTE_DATA(in); Py_UCS4 *inp_end = inp + PyUnicode_GET_LENGTH(in); Py_UCS4 *outp; PyObject *out; Py_ssize_t delta = 0; GET_DELTA(inp, inp_end, delta); if (!delta) { Py_INCREF(in); return (PyObject*)in; } out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 1114111); if (!out) return NULL; inp = PyUnicode_4BYTE_DATA(in); outp = PyUnicode_4BYTE_DATA(out); DO_ESCAPE(inp, inp_end, outp); return out; } static PyObject* escape_unicode(PyUnicodeObject *in) { if (PyUnicode_READY(in)) return NULL; switch (PyUnicode_KIND(in)) { case PyUnicode_1BYTE_KIND: return escape_unicode_kind1(in); case PyUnicode_2BYTE_KIND: return escape_unicode_kind2(in); case PyUnicode_4BYTE_KIND: return escape_unicode_kind4(in); } assert(0); /* shouldn't happen */ return NULL; } #endif /* PY_MAJOR_VERSION < 3 */ static PyObject* escape(PyObject *self, PyObject *text) { static PyObject *id_html; PyObject *s = NULL, *rv = NULL, *html; if (id_html == NULL) { #if PY_MAJOR_VERSION < 3 id_html = PyString_InternFromString("__html__"); #else id_html = PyUnicode_InternFromString("__html__"); #endif if (id_html == NULL) { return NULL; } } /* we don't have to escape integers, bools or floats */ if (PyLong_CheckExact(text) || #if PY_MAJOR_VERSION < 3 PyInt_CheckExact(text) || #endif PyFloat_CheckExact(text) || PyBool_Check(text) || text == Py_None) return PyObject_CallFunctionObjArgs(markup, text, NULL); /* if the object has an __html__ method that performs the escaping */ html = PyObject_GetAttr(text ,id_html); if (html) { s = PyObject_CallObject(html, NULL); Py_DECREF(html); /* Convert to Markup object */ rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); Py_DECREF(s); return rv; } /* otherwise make the object unicode if it isn't, then escape */ PyErr_Clear(); if (!PyUnicode_Check(text)) { #if PY_MAJOR_VERSION < 3 PyObject *unicode = PyObject_Unicode(text); #else PyObject *unicode = PyObject_Str(text); #endif if (!unicode) return NULL; s = escape_unicode((PyUnicodeObject*)unicode); Py_DECREF(unicode); } else s = escape_unicode((PyUnicodeObject*)text); /* convert the unicode string into a markup object. */ rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); Py_DECREF(s); return rv; } static PyObject* escape_silent(PyObject *self, PyObject *text) { if (text != Py_None) return escape(self, text); return PyObject_CallFunctionObjArgs(markup, NULL); } static PyObject* soft_unicode(PyObject *self, PyObject *s) { if (!PyUnicode_Check(s)) #if PY_MAJOR_VERSION < 3 return PyObject_Unicode(s); #else return PyObject_Str(s); #endif Py_INCREF(s); return s; } static PyMethodDef module_methods[] = { {"escape", (PyCFunction)escape, METH_O, "escape(s) -> markup\n\n" "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n" "sequences. Use this if you need to display text that might contain\n" "such characters in HTML. Marks return value as markup string."}, {"escape_silent", (PyCFunction)escape_silent, METH_O, "escape_silent(s) -> markup\n\n" "Like escape but converts None to an empty string."}, {"soft_unicode", (PyCFunction)soft_unicode, METH_O, "soft_unicode(object) -> string\n\n" "Make a string unicode if it isn't already. That way a markup\n" "string is not converted back to unicode."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; #if PY_MAJOR_VERSION < 3 #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif PyMODINIT_FUNC init_speedups(void) { if (!init_constants()) return; Py_InitModule3("markupsafe._speedups", module_methods, ""); } #else /* Python 3.x module initialization */ static struct PyModuleDef module_definition = { PyModuleDef_HEAD_INIT, "markupsafe._speedups", NULL, -1, module_methods, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__speedups(void) { if (!init_constants()) return NULL; return PyModule_Create(&module_definition); } #endif MarkupSafe-1.1.0/tests/0000755000175000017500000000000013370074515015116 5ustar daviddavid00000000000000MarkupSafe-1.1.0/tests/conftest.py0000644000175000017500000000120713370050016017303 0ustar daviddavid00000000000000import pytest from markupsafe import _native try: from markupsafe import _speedups except ImportError: _speedups = None @pytest.fixture( scope="session", params=( _native, pytest.param( _speedups, marks=pytest.mark.skipif(_speedups is None, reason="speedups unavailable"), ), ), ) def _mod(request): return request.param @pytest.fixture(scope="session") def escape(_mod): return _mod.escape @pytest.fixture(scope="session") def escape_silent(_mod): return _mod.escape_silent @pytest.fixture(scope="session") def soft_str(_mod): return _mod.soft_unicode MarkupSafe-1.1.0/tests/test_escape.py0000644000175000017500000000205613370050016017760 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- import pytest from markupsafe import Markup @pytest.mark.parametrize( ("value", "expect"), ( # empty (u"", u""), # ascii (u"abcd&><'\"efgh", u"abcd&><'"efgh"), (u"&><'\"efgh", u"&><'"efgh"), (u"abcd&><'\"", u"abcd&><'""), # 2 byte (u"こんにちは&><'\"こんばんは", u"こんにちは&><'"こんばんは"), (u"&><'\"こんばんは", u"&><'"こんばんは"), (u"こんにちは&><'\"", u"こんにちは&><'""), # 4 byte ( u"\U0001F363\U0001F362&><'\"\U0001F37A xyz", u"\U0001F363\U0001F362&><'"\U0001F37A xyz", ), (u"&><'\"\U0001F37A xyz", u"&><'"\U0001F37A xyz"), (u"\U0001F363\U0001F362&><'\"", u"\U0001F363\U0001F362&><'""), ), ) def test_escape(escape, value, expect): assert escape(value) == Markup(expect) MarkupSafe-1.1.0/tests/test_leak.py0000644000175000017500000000107013370050016017427 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- import gc import sys import pytest from markupsafe import escape @pytest.mark.skipif( escape.__module__ == "markupsafe._native", reason="only test memory leak with speedups", ) def test_markup_leaks(): counts = set() for _i in range(20): for _j in range(1000): escape("foo") escape("") escape(u"foo") escape(u"") if hasattr(sys, "pypy_version_info"): gc.collect() counts.add(len(gc.get_objects())) assert len(counts) == 1 MarkupSafe-1.1.0/tests/test_markupsafe.py0000644000175000017500000001226413370050016020660 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- import pytest from markupsafe import escape from markupsafe import escape_silent from markupsafe import Markup from markupsafe._compat import PY2 from markupsafe._compat import text_type def test_adding(): unsafe = '' safe = Markup("username") assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe) @pytest.mark.parametrize( ("template", "data", "expect"), ( ("%s", "", "<bad user>"), ( "%(username)s", {"username": ""}, "<bad user>", ), ("%i", 3.14, "3"), ("%.2f", 3.14, "3.14"), ), ) def test_string_interpolation(template, data, expect): assert Markup(template) % data == expect def test_type_behavior(): assert type(Markup("foo") + "bar") is Markup x = Markup("foo") assert x.__html__() is x def test_html_interop(): class Foo(object): def __html__(self): return "awesome" def __unicode__(self): return "awesome" __str__ = __unicode__ assert Markup(Foo()) == "awesome" result = Markup("%s") % Foo() assert result == "awesome" def test_tuple_interpol(): result = Markup("%s:%s") % ("", "") expect = Markup(u"<foo>:<bar>") assert result == expect def test_dict_interpol(): result = Markup("%(foo)s") % {"foo": ""} expect = Markup(u"<foo>") assert result == expect result = Markup("%(foo)s:%(bar)s") % {"foo": "", "bar": ""} expect = Markup(u"<foo>:<bar>") assert result == expect def test_escaping(): assert escape("\"<>&'") == ""<>&'" assert Markup("Foo & Bar").striptags() == "Foo & Bar" def test_unescape(): assert Markup("<test>").unescape() == "" result = Markup("jack & tavi are cooler than mike & russ").unescape() expect = "jack & tavi are cooler than mike & russ" assert result == expect original = "&foo;" once = Markup(original).unescape() twice = Markup(once).unescape() expect = "&foo;" assert once == expect assert twice == expect def test_format(): result = Markup("{awesome}").format(awesome="") assert result == "<awesome>" result = Markup("{0[1][bar]}").format([0, {"bar": ""}]) assert result == "<bar/>" result = Markup("{0[1][bar]}").format([0, {"bar": Markup("")}]) assert result == "" def test_formatting_empty(): formatted = Markup("{}").format(0) assert formatted == Markup("0") def test_custom_formatting(): class HasHTMLOnly(object): def __html__(self): return Markup("") class HasHTMLAndFormat(object): def __html__(self): return Markup("") def __html_format__(self, spec): return Markup("") assert Markup("{0}").format(HasHTMLOnly()) == Markup("") assert Markup("{0}").format(HasHTMLAndFormat()) == Markup("") def test_complex_custom_formatting(): class User(object): def __init__(self, id, username): self.id = id self.username = username def __html_format__(self, format_spec): if format_spec == "link": return Markup('{1}').format( self.id, self.__html__() ) elif format_spec: raise ValueError("Invalid format spec") return self.__html__() def __html__(self): return Markup("{0}").format(self.username) user = User(1, "foo") result = Markup("

User: {0:link}").format(user) expect = Markup('

User: foo') assert result == expect def test_formatting_with_objects(): class Stringable(object): def __unicode__(self): return u"строка" if PY2: def __str__(self): return "some other value" else: __str__ = __unicode__ assert Markup("{s}").format(s=Stringable()) == Markup(u"строка") def test_all_set(): import markupsafe as markup for item in markup.__all__: getattr(markup, item) def test_escape_silent(): assert escape_silent(None) == Markup() assert escape(None) == Markup(None) assert escape_silent("") == Markup(u"<foo>") def test_splitting(): expect = [Markup("a"), Markup("b")] assert Markup("a b").split() == expect assert Markup("a b").rsplit() == expect assert Markup("a\nb").splitlines() == expect def test_mul(): assert Markup("a") * 3 == Markup("aaa") def test_escape_return_type(): assert isinstance(escape("a"), Markup) assert isinstance(escape(Markup("a")), Markup) class Foo: def __html__(self): return "Foo" assert isinstance(escape(Foo()), Markup) MarkupSafe-1.1.0/tox.ini0000644000175000017500000000156213370050016015261 0ustar daviddavid00000000000000[tox] envlist = py{37,36,35,34,27,py3,py} stylecheck docs-html coverage-report skip_missing_interpreters = true [testenv] setenv = COVERAGE_FILE = .coverage.{envname} deps = pytest-cov commands = pytest --tb=short --cov --cov-report= {posargs} [testenv:stylecheck] deps = pre-commit skip_install = true commands = pre-commit run --all-files --show-diff-on-failure [testenv:docs-html] deps = -r docs/requirements.txt commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html [testenv:coverage-report] setenv = COVERAGE_FILE = .coverage deps = coverage skip_install = true commands = coverage combine coverage html coverage report [testenv:codecov] passenv = CI TRAVIS TRAVIS_* setenv = COVERAGE_FILE = .coverage deps = codecov skip_install = true commands = coverage combine codecov coverage report