././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1621357471.5748494 MarkupSafe-2.0.1/0000755000175000017500000000000000000000000012511 5ustar00daviddavid././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1621357398.0 MarkupSafe-2.0.1/CHANGES.rst0000644000175000017500000000413000000000000014311 0ustar00daviddavidVersion 2.0.1 ------------- Released 2021-05-18 - Mark top-level names as exported so type checking understands imports in user projects. :pr:`215` - Fix some types that weren't available in Python 3.6.0. :pr:`215` Version 2.0.0 ------------- Released 2021-05-11 - Drop Python 2.7, 3.4, and 3.5 support. - ``Markup.unescape`` uses :func:`html.unescape` to support HTML5 character references. :pr:`117` - Add type annotations for static typing tools. :pr:`149` Version 1.1.1 ------------- Released 2019-02-23 - Fix segfault when ``__html__`` method raises an exception when using the C speedups. The exception is now propagated correctly. :pr:`109` Version 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. :pr:`104` - Use newer CPython API on Python 3, resulting in a 1.5x speedup. :pr`64` - ``escape`` wraps ``__html__`` result in ``Markup``, consistent with documented behavior. :pr:`69` 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1580065037.0 MarkupSafe-2.0.1/LICENSE.rst0000644000175000017500000000270300000000000014327 0ustar00daviddavidCopyright 2010 Pallets Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. 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 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, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1614200118.0 MarkupSafe-2.0.1/MANIFEST.in0000644000175000017500000000027200000000000014250 0ustar00daviddavidinclude CHANGES.rst include tox.ini include requirements/*.txt graft docs prune docs/_build graft tests include src/markupsafe/py.typed include src/markupsafe/*.pyi global-exclude *.pyc ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1621357471.5748494 MarkupSafe-2.0.1/PKG-INFO0000644000175000017500000000727600000000000013622 0ustar00daviddavidMetadata-Version: 2.1 Name: MarkupSafe Version: 2.0.1 Summary: Safely add untrusted strings to HTML/XML markup. Home-page: https://palletsprojects.com/p/markupsafe/ Author: Armin Ronacher Author-email: armin.ronacher@active-4.com Maintainer: Pallets Maintainer-email: contact@palletsprojects.com License: BSD-3-Clause Project-URL: Donate, https://palletsprojects.com/donate Project-URL: Documentation, https://markupsafe.palletsprojects.com/ Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ Project-URL: Source Code, https://github.com/pallets/markupsafe/ Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ Project-URL: Twitter, https://twitter.com/PalletsTeam Project-URL: Chat, https://discord.gg/pallets 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('<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 str subclass >>> # methods and operators escape their arguments >>> template = Markup("Hello {name}") >>> template.format(name='"World"') Markup('Hello "World"') Donate ------ The Pallets organization develops and supports MarkupSafe and other popular packages. 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://palletsprojects.com/donate Links ----- - Documentation: https://markupsafe.palletsprojects.com/ - Changes: https://markupsafe.palletsprojects.com/changes/ - PyPI Releases: https://pypi.org/project/MarkupSafe/ - Source Code: https://github.com/pallets/markupsafe/ - Issue Tracker: https://github.com/pallets/markupsafe/issues/ - Website: https://palletsprojects.com/p/markupsafe/ - Twitter: https://twitter.com/PalletsTeam - Chat: https://discord.gg/pallets 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: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Text Processing :: Markup :: HTML Requires-Python: >=3.6 Description-Content-Type: text/x-rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1614200118.0 MarkupSafe-2.0.1/README.rst0000644000175000017500000000367300000000000014211 0ustar00daviddavidMarkupSafe ========== 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('<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 str subclass >>> # methods and operators escape their arguments >>> template = Markup("Hello {name}") >>> template.format(name='"World"') Markup('Hello "World"') Donate ------ The Pallets organization develops and supports MarkupSafe and other popular packages. 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://palletsprojects.com/donate Links ----- - Documentation: https://markupsafe.palletsprojects.com/ - Changes: https://markupsafe.palletsprojects.com/changes/ - PyPI Releases: https://pypi.org/project/MarkupSafe/ - Source Code: https://github.com/pallets/markupsafe/ - Issue Tracker: https://github.com/pallets/markupsafe/issues/ - Website: https://palletsprojects.com/p/markupsafe/ - Twitter: https://twitter.com/PalletsTeam - Chat: https://discord.gg/pallets ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1621357471.571516 MarkupSafe-2.0.1/docs/0000755000175000017500000000000000000000000013441 5ustar00daviddavid././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1580064897.0 MarkupSafe-2.0.1/docs/Makefile0000644000175000017500000000110500000000000015076 0ustar00daviddavid# 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1580064897.0 MarkupSafe-2.0.1/docs/changes.rst0000644000175000017500000000005500000000000015603 0ustar00daviddavidChanges ======= .. include:: ../CHANGES.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1618585382.0 MarkupSafe-2.0.1/docs/conf.py0000644000175000017500000000350700000000000014745 0ustar00daviddavidfrom pallets_sphinx_themes import get_version from pallets_sphinx_themes import ProjectLink # Project -------------------------------------------------------------- project = "MarkupSafe" copyright = "2010 Pallets" author = "Pallets" release, version = get_version("MarkupSafe") # General -------------------------------------------------------------- master_doc = "index" extensions = [ "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "pallets_sphinx_themes", "sphinxcontrib.log_cabinet", "sphinx_issues", ] autodoc_typehints = "description" intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} issues_github_path = "pallets/markupsafe" # HTML ----------------------------------------------------------------- html_theme = "jinja" html_theme_options = {"index_sidebar_logo": False} html_context = { "project_links": [ ProjectLink("Donate", "https://palletsprojects.com/donate"), 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/"), ProjectLink("Website", "https://palletsprojects.com/p/markupsafe/"), ProjectLink("Twitter", "https://twitter.com/PalletsTeam"), ProjectLink("Chat", "https://discord.gg/pallets"), ] } html_sidebars = { "index": ["project.html", "localtoc.html", "searchbox.html"], "**": ["localtoc.html", "relations.html", "searchbox.html"], } singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]} html_title = f"MarkupSafe Documentation ({version})" html_show_sourcelink = False # LaTeX ---------------------------------------------------------------- latex_documents = [ (master_doc, f"MarkupSafe-{version}.tex", html_title, author, "manual") ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1611961764.0 MarkupSafe-2.0.1/docs/escaping.rst0000644000175000017500000000047600000000000015773 0ustar00daviddavid.. 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_str ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1611961764.0 MarkupSafe-2.0.1/docs/formatting.rst0000644000175000017500000000444600000000000016355 0ustar00daviddavid.. 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('<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 str subclass >>> # methods and operators escape their arguments >>> template = Markup("Hello {name}") >>> template.format(name='"World"') Markup('Hello "World"') Donate ------ The Pallets organization develops and supports MarkupSafe and other popular packages. 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://palletsprojects.com/donate Links ----- - Documentation: https://markupsafe.palletsprojects.com/ - Changes: https://markupsafe.palletsprojects.com/changes/ - PyPI Releases: https://pypi.org/project/MarkupSafe/ - Source Code: https://github.com/pallets/markupsafe/ - Issue Tracker: https://github.com/pallets/markupsafe/issues/ - Website: https://palletsprojects.com/p/markupsafe/ - Twitter: https://twitter.com/PalletsTeam - Chat: https://discord.gg/pallets 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: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Text Processing :: Markup :: HTML Requires-Python: >=3.6 Description-Content-Type: text/x-rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1621357471.0 MarkupSafe-2.0.1/src/MarkupSafe.egg-info/SOURCES.txt0000644000175000017500000000130500000000000020713 0ustar00daviddavidCHANGES.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 requirements/dev.txt requirements/docs.txt requirements/tests.txt requirements/typing.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/_native.py src/markupsafe/_speedups.c src/markupsafe/_speedups.pyi src/markupsafe/py.typed tests/conftest.py tests/test_escape.py tests/test_exception_custom_html.py tests/test_leak.py tests/test_markupsafe.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1621357471.0 MarkupSafe-2.0.1/src/MarkupSafe.egg-info/dependency_links.txt0000644000175000017500000000000100000000000023076 0ustar00daviddavid ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1621357471.0 MarkupSafe-2.0.1/src/MarkupSafe.egg-info/top_level.txt0000644000175000017500000000001300000000000021554 0ustar00daviddavidmarkupsafe ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1621357471.571516 MarkupSafe-2.0.1/src/markupsafe/0000755000175000017500000000000000000000000015436 5ustar00daviddavid././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1621357398.0 MarkupSafe-2.0.1/src/markupsafe/__init__.py0000644000175000017500000002133300000000000017551 0ustar00daviddavidimport functools import re import string import typing as t if t.TYPE_CHECKING: import typing_extensions as te class HasHTML(te.Protocol): def __html__(self) -> str: pass __version__ = "2.0.1" _striptags_re = re.compile(r"(|<[^>]*>)") def _simple_escaping_wrapper(name: str) -> t.Callable[..., "Markup"]: orig = getattr(str, name) @functools.wraps(orig) def wrapped(self: "Markup", *args: t.Any, **kwargs: t.Any) -> "Markup": args = _escape_argspec(list(args), enumerate(args), self.escape) # type: ignore _escape_argspec(kwargs, kwargs.items(), self.escape) return self.__class__(orig(self, *args, **kwargs)) return wrapped class Markup(str): """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 :class:`str`. It has the same methods, but escapes their arguments and returns a ``Markup`` instance. >>> Markup("%s") % ("foo & bar",) Markup('foo & bar') >>> Markup("Hello ") + "" Markup('Hello <foo>') """ __slots__ = () def __new__( cls, base: t.Any = "", encoding: t.Optional[str] = None, errors: str = "strict" ) -> "Markup": if hasattr(base, "__html__"): base = base.__html__() if encoding is None: return super().__new__(cls, base) return super().__new__(cls, base, encoding, errors) def __html__(self) -> "Markup": return self def __add__(self, other: t.Union[str, "HasHTML"]) -> "Markup": if isinstance(other, str) or hasattr(other, "__html__"): return self.__class__(super().__add__(self.escape(other))) return NotImplemented def __radd__(self, other: t.Union[str, "HasHTML"]) -> "Markup": if isinstance(other, str) or hasattr(other, "__html__"): return self.escape(other).__add__(self) return NotImplemented def __mul__(self, num: int) -> "Markup": if isinstance(num, int): return self.__class__(super().__mul__(num)) return NotImplemented # type: ignore __rmul__ = __mul__ def __mod__(self, arg: t.Any) -> "Markup": if isinstance(arg, tuple): arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg) else: arg = _MarkupEscapeHelper(arg, self.escape) return self.__class__(super().__mod__(arg)) def __repr__(self) -> str: return f"{self.__class__.__name__}({super().__repr__()})" def join(self, seq: t.Iterable[t.Union[str, "HasHTML"]]) -> "Markup": return self.__class__(super().join(map(self.escape, seq))) join.__doc__ = str.join.__doc__ def split( # type: ignore self, sep: t.Optional[str] = None, maxsplit: int = -1 ) -> t.List["Markup"]: return [self.__class__(v) for v in super().split(sep, maxsplit)] split.__doc__ = str.split.__doc__ def rsplit( # type: ignore self, sep: t.Optional[str] = None, maxsplit: int = -1 ) -> t.List["Markup"]: return [self.__class__(v) for v in super().rsplit(sep, maxsplit)] rsplit.__doc__ = str.rsplit.__doc__ def splitlines(self, keepends: bool = False) -> t.List["Markup"]: # type: ignore return [self.__class__(v) for v in super().splitlines(keepends)] splitlines.__doc__ = str.splitlines.__doc__ def unescape(self) -> str: """Convert escaped markup back into a text string. This replaces HTML entities with the characters they represent. >>> Markup("Main » About").unescape() 'Main » About' """ from html import unescape return unescape(str(self)) def striptags(self) -> str: """:meth:`unescape` the markup, remove tags, and normalize whitespace to single spaces. >>> Markup("Main »\tAbout").striptags() 'Main » About' """ stripped = " ".join(_striptags_re.sub("", self).split()) return Markup(stripped).unescape() @classmethod def escape(cls, s: t.Any) -> "Markup": """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 for method in ( "__getitem__", "capitalize", "title", "lower", "upper", "replace", "ljust", "rjust", "lstrip", "rstrip", "center", "strip", "translate", "expandtabs", "swapcase", "zfill", ): locals()[method] = _simple_escaping_wrapper(method) del method def partition(self, sep: str) -> t.Tuple["Markup", "Markup", "Markup"]: l, s, r = super().partition(self.escape(sep)) cls = self.__class__ return cls(l), cls(s), cls(r) def rpartition(self, sep: str) -> t.Tuple["Markup", "Markup", "Markup"]: l, s, r = super().rpartition(self.escape(sep)) cls = self.__class__ return cls(l), cls(s), cls(r) def format(self, *args: t.Any, **kwargs: t.Any) -> "Markup": formatter = EscapeFormatter(self.escape) return self.__class__(formatter.vformat(self, args, kwargs)) def __html_format__(self, format_spec: str) -> "Markup": if format_spec: raise ValueError("Unsupported format specification for Markup.") return self class EscapeFormatter(string.Formatter): __slots__ = ("escape",) def __init__(self, escape: t.Callable[[t.Any], Markup]) -> None: self.escape = escape super().__init__() def format_field(self, value: t.Any, format_spec: str) -> str: if hasattr(value, "__html_format__"): rv = value.__html_format__(format_spec) elif hasattr(value, "__html__"): if format_spec: raise ValueError( f"Format specifier {format_spec} given, but {type(value)} does not" " define __html_format__. A class that defines __html__ must define" " __html_format__ to work with format specifiers." ) rv = value.__html__() else: # We need to make sure the format spec is str here as # otherwise the wrong callback methods are invoked. rv = string.Formatter.format_field(self, value, str(format_spec)) return str(self.escape(rv)) _ListOrDict = t.TypeVar("_ListOrDict", list, dict) def _escape_argspec( obj: _ListOrDict, iterable: t.Iterable[t.Any], escape: t.Callable[[t.Any], Markup] ) -> _ListOrDict: """Helper for various string-wrapped functions.""" for key, value in iterable: if isinstance(value, str) or hasattr(value, "__html__"): obj[key] = escape(value) return obj class _MarkupEscapeHelper: """Helper for :meth:`Markup.__mod__`.""" __slots__ = ("obj", "escape") def __init__(self, obj: t.Any, escape: t.Callable[[t.Any], Markup]) -> None: self.obj = obj self.escape = escape def __getitem__(self, item: t.Any) -> "_MarkupEscapeHelper": return _MarkupEscapeHelper(self.obj[item], self.escape) def __str__(self) -> str: return str(self.escape(self.obj)) def __repr__(self) -> str: return str(self.escape(repr(self.obj))) def __int__(self) -> int: return int(self.obj) def __float__(self) -> float: return float(self.obj) # circular import try: from ._speedups import escape as escape from ._speedups import escape_silent as escape_silent from ._speedups import soft_str as soft_str from ._speedups import soft_unicode except ImportError: from ._native import escape as escape from ._native import escape_silent as escape_silent # noqa: F401 from ._native import soft_str as soft_str # noqa: F401 from ._native import soft_unicode # noqa: F401 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1618525644.0 MarkupSafe-2.0.1/src/markupsafe/_native.py0000644000175000017500000000370200000000000017437 0ustar00daviddavidimport typing as t from . import Markup def escape(s: t.Any) -> Markup: """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( str(s) .replace("&", "&") .replace(">", ">") .replace("<", "<") .replace("'", "'") .replace('"', """) ) def escape_silent(s: t.Optional[t.Any]) -> Markup: """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_str(s: t.Any) -> str: """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_str(value)) Markup('<User 1>') """ if not isinstance(s, str): return str(s) return s def soft_unicode(s: t.Any) -> str: import warnings warnings.warn( "'soft_unicode' has been renamed to 'soft_str'. The old name" " will be removed in MarkupSafe 2.1.", DeprecationWarning, stacklevel=2, ) return soft_str(s) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1618525644.0 MarkupSafe-2.0.1/src/markupsafe/_speedups.c0000644000175000017500000001635000000000000017576 0ustar00daviddavid#include static PyObject* markup; static int init_constants(void) { PyObject *module; /* 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; } #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; } static PyObject* escape(PyObject *self, PyObject *text) { static PyObject *id_html; PyObject *s = NULL, *rv = NULL, *html; if (id_html == NULL) { id_html = PyUnicode_InternFromString("__html__"); if (id_html == NULL) { return NULL; } } /* we don't have to escape integers, bools or floats */ if (PyLong_CheckExact(text) || 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); if (s == NULL) { return NULL; } /* 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)) { PyObject *unicode = PyObject_Str(text); 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_str(PyObject *self, PyObject *s) { if (!PyUnicode_Check(s)) return PyObject_Str(s); Py_INCREF(s); return s; } static PyObject* soft_unicode(PyObject *self, PyObject *s) { PyErr_WarnEx( PyExc_DeprecationWarning, "'soft_unicode' has been renamed to 'soft_str'. The old name" " will be removed in MarkupSafe 2.1.", 2 ); return soft_str(self, s); } static PyMethodDef module_methods[] = { { "escape", (PyCFunction)escape, METH_O, "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.\n\n" "If the object has an ``__html__`` method, it is called and the" " return value is assumed to already be safe for HTML.\n\n" ":param s: An object to be converted to a string and escaped.\n" ":return: A :class:`Markup` string with the escaped text.\n" }, { "escape_silent", (PyCFunction)escape_silent, METH_O, "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``.\n\n" ">>> escape(None)\n" "Markup('None')\n" ">>> escape_silent(None)\n" "Markup('')\n" }, { "soft_str", (PyCFunction)soft_str, METH_O, "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.\n\n" ">>> value = escape(\"\")\n" ">>> value\n" "Markup('<User 1>')\n" ">>> escape(str(value))\n" "Markup('&lt;User 1&gt;')\n" ">>> escape(soft_str(value))\n" "Markup('<User 1>')\n" }, { "soft_unicode", (PyCFunction)soft_unicode, METH_O, "" }, {NULL, NULL, 0, NULL} /* Sentinel */ }; 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); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1620698502.0 MarkupSafe-2.0.1/src/markupsafe/_speedups.pyi0000644000175000017500000000034500000000000020152 0ustar00daviddavidfrom typing import Any from typing import Optional from . import Markup def escape(s: Any) -> Markup: ... def escape_silent(s: Optional[Any]) -> Markup: ... def soft_str(s: Any) -> str: ... def soft_unicode(s: Any) -> str: ... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1611974802.0 MarkupSafe-2.0.1/src/markupsafe/py.typed0000644000175000017500000000000000000000000017123 0ustar00daviddavid././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1621357471.5748494 MarkupSafe-2.0.1/tests/0000755000175000017500000000000000000000000013653 5ustar00daviddavid././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1611974802.0 MarkupSafe-2.0.1/tests/conftest.py0000644000175000017500000000135300000000000016054 0ustar00daviddavidimport pytest from markupsafe import _native try: from markupsafe import _speedups except ImportError: _speedups = None # type: ignore @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_str @pytest.fixture(scope="session") def soft_unicode(_mod): return _mod.soft_unicode ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1611961764.0 MarkupSafe-2.0.1/tests/test_escape.py0000644000175000017500000000200200000000000016516 0ustar00daviddavidimport pytest from markupsafe import Markup @pytest.mark.parametrize( ("value", "expect"), ( # empty ("", ""), # ascii ("abcd&><'\"efgh", "abcd&><'"efgh"), ("&><'\"efgh", "&><'"efgh"), ("abcd&><'\"", "abcd&><'""), # 2 byte ("こんにちは&><'\"こんばんは", "こんにちは&><'"こんばんは"), ("&><'\"こんばんは", "&><'"こんばんは"), ("こんにちは&><'\"", "こんにちは&><'""), # 4 byte ( "\U0001F363\U0001F362&><'\"\U0001F37A xyz", "\U0001F363\U0001F362&><'"\U0001F37A xyz", ), ("&><'\"\U0001F37A xyz", "&><'"\U0001F37A xyz"), ("\U0001F363\U0001F362&><'\"", "\U0001F363\U0001F362&><'""), ), ) def test_escape(escape, value, expect): assert escape(value) == Markup(expect) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1611961764.0 MarkupSafe-2.0.1/tests/test_exception_custom_html.py0000644000175000017500000000070500000000000021702 0ustar00daviddavidimport pytest class CustomHtmlThatRaises: def __html__(self): raise ValueError(123) def test_exception_custom_html(escape): """Checks whether exceptions in custom __html__ implementations are propagated correctly. There was a bug in the native implementation at some point: https://github.com/pallets/markupsafe/issues/108 """ obj = CustomHtmlThatRaises() with pytest.raises(ValueError): escape(obj) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1611961764.0 MarkupSafe-2.0.1/tests/test_leak.py0000644000175000017500000000105400000000000016200 0ustar00daviddavidimport gc import platform 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("foo") escape("") if platform.python_implementation() == "PyPy": gc.collect() counts.add(len(gc.get_objects())) assert len(counts) == 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1611961764.0 MarkupSafe-2.0.1/tests/test_markupsafe.py0000644000175000017500000001177400000000000017434 0ustar00daviddavidimport pytest from markupsafe import Markup def test_adding(escape): unsafe = '' safe = Markup("username") assert unsafe + safe == str(escape(unsafe)) + str(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: def __html__(self): return "awesome" def __str__(self): return "awesome" assert Markup(Foo()) == "awesome" result = Markup("%s") % Foo() assert result == "awesome" def test_tuple_interpol(): result = Markup("%s:%s") % ("", "") expect = Markup("<foo>:<bar>") assert result == expect def test_dict_interpol(): result = Markup("%(foo)s") % {"foo": ""} expect = Markup("<foo>") assert result == expect result = Markup("%(foo)s:%(bar)s") % {"foo": "", "bar": ""} expect = Markup("<foo>:<bar>") assert result == expect def test_escaping(escape): 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: def __html__(self): return Markup("") class HasHTMLAndFormat: 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: 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: def __str__(self): return "строка" assert Markup("{s}").format(s=Stringable()) == Markup("строка") def test_escape_silent(escape, escape_silent): assert escape_silent(None) == Markup() assert escape(None) == Markup(None) assert escape_silent("") == Markup("<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(escape): assert isinstance(escape("a"), Markup) assert isinstance(escape(Markup("a")), Markup) class Foo: def __html__(self): return "Foo" assert isinstance(escape(Foo()), Markup) def test_soft_str(soft_str): assert type(soft_str("")) is str assert type(soft_str(Markup())) is Markup assert type(soft_str(15)) is str def test_soft_unicode_deprecated(soft_unicode): with pytest.warns(DeprecationWarning): assert type(soft_unicode(Markup())) is Markup ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1614200118.0 MarkupSafe-2.0.1/tox.ini0000644000175000017500000000101500000000000014021 0ustar00daviddavid[tox] envlist = py{39,38,37,36,py3} style typing docs skip_missing_interpreters = true [testenv] deps = -r requirements/tests.txt commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs} [testenv:style] deps = pre-commit skip_install = true commands = pre-commit run --all-files --show-diff-on-failure [testenv:typing] deps = -r requirements/typing.txt commands = mypy [testenv:docs] deps = -r requirements/docs.txt commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html