diff options
author | David Lord <davidism@gmail.com> | 2018-11-03 15:26:05 -0700 |
---|---|---|
committer | David Lord <davidism@gmail.com> | 2018-11-03 15:31:24 -0700 |
commit | b41c96e00afd09eca075a98589886c78666d4d33 (patch) | |
tree | bf372cf97c44210717b8954919436023f275627b | |
parent | 6247e015ebe0006ee16e69da7aaaba20de18ec94 (diff) | |
download | markupsafe-b41c96e00afd09eca075a98589886c78666d4d33.tar.gz |
add style checks
-rw-r--r-- | .editorconfig | 13 | ||||
-rw-r--r-- | .pre-commit-config.yaml | 18 | ||||
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | bench/bench_basic.py | 2 | ||||
-rw-r--r-- | bench/bench_largestring.py | 2 | ||||
-rw-r--r-- | bench/bench_long_empty_string.py | 2 | ||||
-rw-r--r-- | bench/bench_long_suffix.py | 2 | ||||
-rw-r--r-- | bench/bench_short_empty_string.py | 2 | ||||
-rw-r--r-- | bench/runbench.py | 25 | ||||
-rw-r--r-- | setup.cfg | 16 | ||||
-rw-r--r-- | setup.py | 112 | ||||
-rw-r--r-- | src/markupsafe/__init__.py | 144 | ||||
-rw-r--r-- | src/markupsafe/_compat.py | 11 | ||||
-rw-r--r-- | src/markupsafe/_constants.py | 506 | ||||
-rw-r--r-- | src/markupsafe/_native.py | 19 | ||||
-rw-r--r-- | tests/conftest.py | 23 | ||||
-rw-r--r-- | tests/test_escape.py | 39 | ||||
-rw-r--r-- | tests/test_leak.py | 10 | ||||
-rw-r--r-- | tests/test_markupsafe.py | 127 | ||||
-rw-r--r-- | tox.ini | 6 |
20 files changed, 590 insertions, 491 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e32c802 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +charset = utf-8 +max_line_length = 88 + +[*.{yml,yaml,json,js,css,html}] +indent_size = 2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..7be015d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +repos: + - repo: https://github.com/asottile/reorder_python_imports + rev: v1.3.1 + hooks: + - id: reorder-python-imports + args: ["--application-directories", "src"] + - repo: https://github.com/ambv/black + rev: 18.9b0 + hooks: + - id: black + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.0.0 + hooks: + - id: check-byte-order-marker + - id: trailing-whitespace + - id: end-of-file-fixer + - id: flake8 + additional_dependencies: [flake8-bugbear] diff --git a/.travis.yml b/.travis.yml index 367c8de..7abf38b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ matrix: - dist: xenial sudo: required python: "3.7" - - env: TOXENV=docs-html + - env: TOXENV=stylecheck,docs-html - stage: wheel sudo: required services: diff --git a/bench/bench_basic.py b/bench/bench_basic.py index 1172d15..b67eda4 100644 --- a/bench/bench_basic.py +++ b/bench/bench_basic.py @@ -2,4 +2,4 @@ from markupsafe import escape def run(): - escape('<strong>Hello World!</strong>') + escape("<strong>Hello World!</strong>") diff --git a/bench/bench_largestring.py b/bench/bench_largestring.py index 7bde595..9ced67f 100644 --- a/bench/bench_largestring.py +++ b/bench/bench_largestring.py @@ -2,5 +2,5 @@ from markupsafe import escape def run(): - string = '<strong>Hello World!</strong>' * 1000 + string = "<strong>Hello World!</strong>" * 1000 escape(string) diff --git a/bench/bench_long_empty_string.py b/bench/bench_long_empty_string.py index 320fb0a..ad2480c 100644 --- a/bench/bench_long_empty_string.py +++ b/bench/bench_long_empty_string.py @@ -2,5 +2,5 @@ from markupsafe import escape def run(): - string = 'Hello World!' * 1000 + string = "Hello World!" * 1000 escape(string) diff --git a/bench/bench_long_suffix.py b/bench/bench_long_suffix.py index 43fe563..35f38da 100644 --- a/bench/bench_long_suffix.py +++ b/bench/bench_long_suffix.py @@ -2,5 +2,5 @@ from markupsafe import escape def run(): - string = '<strong>Hello World!</strong>' + 'x' * 100000 + string = "<strong>Hello World!</strong>" + "x" * 100000 escape(string) diff --git a/bench/bench_short_empty_string.py b/bench/bench_short_empty_string.py index 91cccc4..0664a6f 100644 --- a/bench/bench_short_empty_string.py +++ b/bench/bench_short_empty_string.py @@ -2,4 +2,4 @@ from markupsafe import escape def run(): - escape('Hello World!') + escape("Hello World!") diff --git a/bench/runbench.py b/bench/runbench.py index ac2cbb1..38cf128 100644 --- a/bench/runbench.py +++ b/bench/runbench.py @@ -3,12 +3,13 @@ Runs the benchmarks """ from __future__ import print_function -import sys + import os import re +import sys from subprocess import Popen -_filename_re = re.compile(r'^bench_(.*?)\.py$') +_filename_re = re.compile(r"^bench_(.*?)\.py$") bench_directory = os.path.abspath(os.path.dirname(__file__)) @@ -18,27 +19,27 @@ def list_benchmarks(): match = _filename_re.match(name) if match is not None: result.append(match.group(1)) - result.sort(key=lambda x: (x.startswith('logging_'), x.lower())) + result.sort(key=lambda x: (x.startswith("logging_"), x.lower())) return result def run_bench(name): - sys.stdout.write('%-32s' % name) + sys.stdout.write("%-32s" % name) sys.stdout.flush() - Popen([sys.executable, '-mtimeit', '-s', - 'from bench_%s import run' % name, - 'run()']).wait() + Popen( + [sys.executable, "-mtimeit", "-s", "from bench_%s import run" % name, "run()"] + ).wait() def main(): - print('=' * 80) - print('Running benchmark for MarkupSafe') - print('-' * 80) + print("=" * 80) + print("Running benchmark for MarkupSafe") + print("-" * 80) os.chdir(bench_directory) for bench in list_benchmarks(): run_bench(bench) - print('-' * 80) + print("-" * 80) -if __name__ == '__main__': +if __name__ == "__main__": main() @@ -14,3 +14,19 @@ source = src/markupsafe .tox/*/lib/python*/site-packages/markupsafe .tox/*/site-packages/markupsafe + +[flake8] +# B = bugbear +# E = pycodestyle errors +# F = flake8 pyflakes +# W = pycodestyle warnings +# B9 = bugbear opinions +select = B, E, F, W, B9 +# E203 = slice notation whitespace, invalid +# E501 = line length, handled by bugbear B950 +# W503 = bin op line break, invalid +ignore = E203, E501, W503 +# up to 88 allowed by bugbear B950 +max-line-length = 80 +# _compat names and imports will always look bad, ignore warnings +exclude = src/markupsafe/_compat.py @@ -1,33 +1,27 @@ from __future__ import print_function -import sys - import io import re -from distutils.errors import ( - CCompilerError, - DistutilsExecError, - DistutilsPlatformError, -) -from setuptools import ( - Extension, - find_packages, - setup, -) +import sys +from distutils.errors import CCompilerError +from distutils.errors import DistutilsExecError +from distutils.errors import DistutilsPlatformError + +from setuptools import Extension +from setuptools import find_packages +from setuptools import setup from setuptools.command.build_ext import build_ext -with io.open('README.rst', 'rt', encoding='utf8') as f: +with io.open("README.rst", "rt", encoding="utf8") as f: readme = f.read() -with io.open('src/markupsafe/__init__.py', 'rt', encoding='utf8') as f: - version = re.search(r'__version__ = \'(.*?)\'', f.read()).group(1) +with io.open("src/markupsafe/__init__.py", "rt", encoding="utf8") as f: + version = re.search(r'__version__ = "(.*?)"', f.read()).group(1) -is_jython = 'java' in sys.platform -is_pypy = hasattr(sys, 'pypy_version_info') +is_jython = "java" in sys.platform +is_pypy = hasattr(sys, "pypy_version_info") -ext_modules = [ - Extension('markupsafe._speedups', ['src/markupsafe/_speedups.c']), -] +ext_modules = [Extension("markupsafe._speedups", ["src/markupsafe/_speedups.c"])] class BuildFailed(Exception): @@ -50,60 +44,60 @@ class ve_build_ext(build_ext): raise BuildFailed() except ValueError: # this can happen on Windows 64 bit, see Python issue 7511 - if "'path'" in str(sys.exc_info()[1]): # works with Python 2 and 3 + if "'path'" in str(sys.exc_info()[1]): # works with Python 2 and 3 raise BuildFailed() raise def run_setup(with_binary): setup( - name='MarkupSafe', + name="MarkupSafe", version=version, - url='https://www.palletsprojects.com/p/markupsafe/', + url="https://www.palletsprojects.com/p/markupsafe/", project_urls={ "Documentation": "https://markupsafe.palletsprojects.com/", "Code": "https://github.com/pallets/markupsafe", "Issue tracker": "https://github.com/pallets/markupsafe/issues", }, - license='BSD', - author='Armin Ronacher', - author_email='armin.ronacher@active-4.com', - maintainer='Pallets Team', - maintainer_email='contact@palletsprojects.com', - description='Safely add untrusted strings to HTML/XML markup.', + license="BSD", + author="Armin Ronacher", + author_email="armin.ronacher@active-4.com", + maintainer="Pallets Team", + maintainer_email="contact@palletsprojects.com", + description="Safely add untrusted strings to HTML/XML markup.", long_description=readme, classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Topic :: Text Processing :: Markup :: HTML', + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Text Processing :: Markup :: HTML", ], packages=find_packages("src"), package_dir={"": "src"}, include_package_data=True, - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', - cmdclass={'build_ext': ve_build_ext}, + python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", + cmdclass={"build_ext": ve_build_ext}, ext_modules=ext_modules if with_binary else [], ) def show_message(*lines): - print('=' * 74) + print("=" * 74) for line in lines: print(line) - print('=' * 74) + print("=" * 74) if not (is_pypy or is_jython): @@ -111,21 +105,21 @@ if not (is_pypy or is_jython): run_setup(True) except BuildFailed: show_message( - 'WARNING: The C extension could not be compiled, speedups' - ' are not enabled.', - 'Failure information, if any, is above.', - 'Retrying the build without the C extension now.' + "WARNING: The C extension could not be compiled, speedups" + " are not enabled.", + "Failure information, if any, is above.", + "Retrying the build without the C extension now.", ) run_setup(False) show_message( - 'WARNING: The C extension could not be compiled, speedups' - ' are not enabled.', - 'Plain-Python build succeeded.' + "WARNING: The C extension could not be compiled, speedups" + " are not enabled.", + "Plain-Python build succeeded.", ) else: run_setup(False) show_message( - 'WARNING: C extensions are not supported on this Python' - ' platform, speedups are not enabled.', - 'Plain-Python build succeeded.' + "WARNING: C extensions are not supported on this Python" + " platform, speedups are not enabled.", + "Plain-Python build succeeded.", ) diff --git a/src/markupsafe/__init__.py b/src/markupsafe/__init__.py index 29fe1bd..e02c15d 100644 --- a/src/markupsafe/__init__.py +++ b/src/markupsafe/__init__.py @@ -12,16 +12,20 @@ special characters with safe representations. import re import string -from markupsafe._compat import ( - PY2, int_types, iteritems, string_types, text_type, unichr, Mapping -) +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.dev' +__version__ = "1.1.dev" -__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent'] +__all__ = ["Markup", "soft_unicode", "escape", "escape_silent"] -_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)') -_entity_re = re.compile(r'&([^& ;]+);') +_striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)") +_entity_re = re.compile(r"&([^& ;]+);") class Markup(text_type): @@ -60,10 +64,11 @@ class Markup(text_type): >>> Markup('<em>Hello</em> ') + '<foo>' Markup('<em>Hello</em> <foo>') """ + __slots__ = () - def __new__(cls, base=u'', encoding=None, errors='strict'): - if hasattr(base, '__html__'): + 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) @@ -73,12 +78,12 @@ class Markup(text_type): return self def __add__(self, other): - if isinstance(other, string_types) or hasattr(other, '__html__'): + 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): + if hasattr(other, "__html__") or isinstance(other, string_types): return self.escape(other).__add__(self) return NotImplemented @@ -86,6 +91,7 @@ class Markup(text_type): if isinstance(num, int_types): return self.__class__(text_type.__mul__(self, num)) return NotImplemented + __rmul__ = __mul__ def __mod__(self, arg): @@ -96,26 +102,26 @@ class Markup(text_type): return self.__class__(text_type.__mod__(self, arg)) def __repr__(self): - return '%s(%s)' % ( - self.__class__.__name__, - text_type.__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))) + return list(map(self.__class__, text_type.splitlines(self, *args, **kwargs))) + splitlines.__doc__ = text_type.splitlines.__doc__ def unescape(self): @@ -125,20 +131,22 @@ class Markup(text_type): >>> Markup('Main » <em>About</em>').unescape() 'Main » <em>About</em>' """ - from markupsafe._constants import HTML_ENTITIES + 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'): + if name[:2] in ("#x", "#X"): return unichr(int(name[2:], 16)) - elif name.startswith('#'): + 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): @@ -148,7 +156,7 @@ class Markup(text_type): >>> Markup('Main »\t<em>About</em>').striptags() 'Main » About' """ - stripped = u' '.join(_striptags_re.sub('', self).split()) + stripped = u" ".join(_striptags_re.sub("", self).split()) return Markup(stripped).unescape() @classmethod @@ -161,45 +169,57 @@ class Markup(text_type): return cls(rv) return rv - def make_simple_escaping_wrapper(name): + 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': + 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)))) + 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)))) + return tuple(map(self.__class__, text_type.rpartition(self, self.escape(sep)))) - def format(*args, **kwargs): - self, args = args[0], args[1:] + 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.') + raise ValueError("Unsupported format specification " "for Markup.") return self # not in python 3 - if hasattr(text_type, '__getslice__'): - __getslice__ = make_simple_escaping_wrapper('__getslice__') + if hasattr(text_type, "__getslice__"): + __getslice__ = make_simple_escaping_wrapper("__getslice__") del method, make_simple_escaping_wrapper @@ -218,7 +238,7 @@ class _MagicFormatMapping(Mapping): self._last_index = 0 def __getitem__(self, key): - if key == '': + if key == "": idx = self._last_index self._last_index += 1 try: @@ -235,38 +255,37 @@ class _MagicFormatMapping(Mapping): return len(self._kwargs) -if hasattr(text_type, 'format'): - class EscapeFormatter(string.Formatter): +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__'): + if hasattr(value, "__html_format__"): rv = value.__html_format__(format_spec) - elif hasattr(value, '__html__'): + 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))) + "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)) + 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): + if hasattr(value, "__html__") or isinstance(value, string_types): obj[key] = escape(value) return obj @@ -278,20 +297,31 @@ class _MarkupEscapeHelper(object): self.obj = obj self.escape = escape - __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape) - __unicode__ = __str__ = lambda s: text_type(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) + 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 markupsafe._speedups import escape, escape_silent, soft_unicode + from ._speedups import escape, escape_silent, soft_unicode except ImportError: - from markupsafe._native import escape, escape_silent, soft_unicode + from ._native import escape, escape_silent, soft_unicode if not PY2: soft_str = soft_unicode - __all__.append('soft_str') + __all__.append("soft_str") diff --git a/src/markupsafe/_compat.py b/src/markupsafe/_compat.py index 4d2e2cd..e51d57d 100644 --- a/src/markupsafe/_compat.py +++ b/src/markupsafe/_compat.py @@ -15,12 +15,19 @@ if not PY2: string_types = (str,) unichr = chr int_types = (int,) - iteritems = lambda x: iter(x.items()) + + 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) - iteritems = lambda x: x.iteritems() + + def iteritems(x): + return x.iteritems() + from collections import Mapping diff --git a/src/markupsafe/_constants.py b/src/markupsafe/_constants.py index ea6ac2f..83670cb 100644 --- a/src/markupsafe/_constants.py +++ b/src/markupsafe/_constants.py @@ -8,257 +8,257 @@ markupsafe._constants """ 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 + "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, } diff --git a/src/markupsafe/_native.py b/src/markupsafe/_native.py index 801f285..245f03a 100644 --- a/src/markupsafe/_native.py +++ b/src/markupsafe/_native.py @@ -8,8 +8,8 @@ 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 markupsafe import Markup -from markupsafe._compat import text_type +from . import Markup +from ._compat import text_type def escape(s): @@ -23,14 +23,15 @@ def escape(s): :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__'): + if hasattr(s, "__html__"): return Markup(s.__html__()) - return Markup(text_type(s) - .replace('&', '&') - .replace('>', '>') - .replace('<', '<') - .replace("'", ''') - .replace('"', '"') + return Markup( + text_type(s) + .replace("&", "&") + .replace(">", ">") + .replace("<", "<") + .replace("'", "'") + .replace('"', """) ) diff --git a/tests/conftest.py b/tests/conftest.py index 06614cc..296cd58 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,27 +8,30 @@ except ImportError: _speedups = None -@pytest.fixture(scope='session', params=( - _native, - pytest.param(_speedups, marks=pytest.mark.skipif( - _speedups is None, - reason='speedups unavailable', - )) -)) +@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') +@pytest.fixture(scope="session") def escape(_mod): return _mod.escape -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def escape_silent(_mod): return _mod.escape_silent -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def soft_str(_mod): return _mod.soft_unicode diff --git a/tests/test_escape.py b/tests/test_escape.py index 337cd19..788134a 100644 --- a/tests/test_escape.py +++ b/tests/test_escape.py @@ -4,22 +4,27 @@ 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&><'"'), -)) +@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) diff --git a/tests/test_leak.py b/tests/test_leak.py index 29e1ec1..b36a4ce 100644 --- a/tests/test_leak.py +++ b/tests/test_leak.py @@ -8,20 +8,20 @@ from markupsafe import escape @pytest.mark.skipif( - escape.__module__ == 'markupsafe._native', - reason='only test memory leak with speedups' + escape.__module__ == "markupsafe._native", + reason="only test memory leak with speedups", ) def test_markup_leaks(): counts = set() - for count in range(20): - for item in range(1000): + for _i in range(20): + for _j in range(1000): escape("foo") escape("<foo>") escape(u"foo") escape(u"<foo>") - if hasattr(sys, 'pypy_version_info'): + if hasattr(sys, "pypy_version_info"): gc.collect() counts.add(len(gc.get_objects())) diff --git a/tests/test_markupsafe.py b/tests/test_markupsafe.py index ffbf8a3..5b08006 100644 --- a/tests/test_markupsafe.py +++ b/tests/test_markupsafe.py @@ -1,32 +1,38 @@ # -*- coding: utf-8 -*- import pytest -from markupsafe import Markup, escape, escape_silent -from markupsafe._compat import PY2, text_type +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 = '<script type="application/x-some-script">alert("foo");</script>' - safe = Markup('<em>username</em>') + safe = Markup("<em>username</em>") assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe) -@pytest.mark.parametrize(('template', 'data', 'expect'), ( - ('<em>%s</em>', '<bad user>', '<em><bad user></em>'), +@pytest.mark.parametrize( + ("template", "data", "expect"), ( - '<em>%(username)s</em>', - {'username': '<bad user>'}, - '<em><bad user></em>' + ("<em>%s</em>", "<bad user>", "<em><bad user></em>"), + ( + "<em>%(username)s</em>", + {"username": "<bad user>"}, + "<em><bad user></em>", + ), + ("%i", 3.14, "3"), + ("%.2f", 3.14, "3.14"), ), - ('%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 + assert type(Markup("foo") + "bar") is Markup x = Markup("foo") assert x.__html__() is x @@ -34,39 +40,36 @@ def test_type_behavior(): def test_html_interop(): class Foo(object): def __html__(self): - return '<em>awesome</em>' + return "<em>awesome</em>" def __unicode__(self): - return 'awesome' + return "awesome" __str__ = __unicode__ - assert Markup(Foo()) == '<em>awesome</em>' - result = Markup('<strong>%s</strong>') % Foo() - assert result == '<strong><em>awesome</em></strong>' + assert Markup(Foo()) == "<em>awesome</em>" + result = Markup("<strong>%s</strong>") % Foo() + assert result == "<strong><em>awesome</em></strong>" def test_tuple_interpol(): - result = Markup('<em>%s:%s</em>') % ('<foo>', '<bar>') - expect = Markup(u'<em><foo>:<bar></em>') + result = Markup("<em>%s:%s</em>") % ("<foo>", "<bar>") + expect = Markup(u"<em><foo>:<bar></em>") assert result == expect def test_dict_interpol(): - result = Markup('<em>%(foo)s</em>') % {'foo': '<foo>'} - expect = Markup(u'<em><foo></em>') + result = Markup("<em>%(foo)s</em>") % {"foo": "<foo>"} + expect = Markup(u"<em><foo></em>") assert result == expect - result = Markup('<em>%(foo)s:%(bar)s</em>') % { - 'foo': '<foo>', - 'bar': '<bar>', - } - expect = Markup(u'<em><foo>:<bar></em>') + result = Markup("<em>%(foo)s:%(bar)s</em>") % {"foo": "<foo>", "bar": "<bar>"} + expect = Markup(u"<em><foo>:<bar></em>") assert result == expect def test_escaping(): - assert escape('"<>&\'') == '"<>&'' + assert escape("\"<>&'") == ""<>&'" assert Markup("<em>Foo & Bar</em>").striptags() == "Foo & Bar" @@ -77,7 +80,7 @@ def test_unescape(): expect = "jack & tavi are cooler than mike & russ" assert result == expect - original = '&foo;' + original = "&foo;" once = Markup(original).unescape() twice = Markup(once).unescape() expect = "&foo;" @@ -86,35 +89,35 @@ def test_unescape(): def test_format(): - result = Markup('<em>{awesome}</em>').format(awesome='<awesome>') - assert result == '<em><awesome></em>' + result = Markup("<em>{awesome}</em>").format(awesome="<awesome>") + assert result == "<em><awesome></em>" - result = Markup('{0[1][bar]}').format([0, {'bar': '<bar/>'}]) - assert result == '<bar/>' + result = Markup("{0[1][bar]}").format([0, {"bar": "<bar/>"}]) + assert result == "<bar/>" - result = Markup('{0[1][bar]}').format([0, {'bar': Markup('<bar/>')}]) - assert result == '<bar/>' + result = Markup("{0[1][bar]}").format([0, {"bar": Markup("<bar/>")}]) + assert result == "<bar/>" def test_formatting_empty(): - formatted = Markup('{}').format(0) - assert formatted == Markup('0') + formatted = Markup("{}").format(0) + assert formatted == Markup("0") def test_custom_formatting(): class HasHTMLOnly(object): def __html__(self): - return Markup('<foo>') + return Markup("<foo>") class HasHTMLAndFormat(object): def __html__(self): - return Markup('<foo>') + return Markup("<foo>") def __html_format__(self, spec): - return Markup('<FORMAT>') + return Markup("<FORMAT>") - assert Markup('{0}').format(HasHTMLOnly()) == Markup('<foo>') - assert Markup('{0}').format(HasHTMLAndFormat()) == Markup('<FORMAT>') + assert Markup("{0}").format(HasHTMLOnly()) == Markup("<foo>") + assert Markup("{0}").format(HasHTMLAndFormat()) == Markup("<FORMAT>") def test_complex_custom_formatting(): @@ -124,36 +127,38 @@ def test_complex_custom_formatting(): self.username = username def __html_format__(self, format_spec): - if format_spec == 'link': + if format_spec == "link": return Markup('<a href="/user/{0}">{1}</a>').format( - self.id, self.__html__()) + self.id, self.__html__() + ) elif format_spec: - raise ValueError('Invalid format spec') + raise ValueError("Invalid format spec") return self.__html__() def __html__(self): - return Markup('<span class=user>{0}</span>').format(self.username) + return Markup("<span class=user>{0}</span>").format(self.username) - user = User(1, 'foo') - result = Markup('<p>User: {0:link}').format(user) - expect = Markup( - '<p>User: <a href="/user/1"><span class=user>foo</span></a>') + user = User(1, "foo") + result = Markup("<p>User: {0:link}").format(user) + expect = Markup('<p>User: <a href="/user/1"><span class=user>foo</span></a>') assert result == expect def test_formatting_with_objects(): class Stringable(object): def __unicode__(self): - return u'строка' + return u"строка" if PY2: + def __str__(self): - return 'some other value' + return "some other value" + else: __str__ = __unicode__ - assert Markup('{s}').format(s=Stringable()) == Markup(u'строка') + assert Markup("{s}").format(s=Stringable()) == Markup(u"строка") def test_all_set(): @@ -166,26 +171,26 @@ def test_all_set(): def test_escape_silent(): assert escape_silent(None) == Markup() assert escape(None) == Markup(None) - assert escape_silent('<foo>') == Markup(u'<foo>') + assert escape_silent("<foo>") == 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 + 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') + assert Markup("a") * 3 == Markup("aaa") def test_escape_return_type(): - assert isinstance(escape('a'), Markup) - assert isinstance(escape(Markup('a')), Markup) + assert isinstance(escape("a"), Markup) + assert isinstance(escape(Markup("a")), Markup) class Foo: def __html__(self): - return '<strong>Foo</strong>' + return "<strong>Foo</strong>" assert isinstance(escape(Foo()), Markup) @@ -1,6 +1,7 @@ [tox] envlist = py{37,36,35,34,27,py3,py} + stylecheck docs-html coverage-report skip_missing_interpreters = true @@ -12,6 +13,11 @@ 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 |