From dab931f597cdc5b57d4e2458840fb991606506bc Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 24 Oct 2019 16:36:04 +0300 Subject: [PATCH] Drop support for Python 2.7 (#865) * Python syntax upgraded using `pyupgrade --py3-plus` * Travis no longer uses `sudo`. See https://blog.travis-ci.com/2018-11-19-required-linux-infrastructure-migration See #760 for Python Version Support Timeline and related dicussion. --- .travis.yml | 11 +--- docs/change_log/release-3.2.md | 7 +- docs/index.md | 10 +-- markdown/__init__.py | 3 - markdown/__main__.py | 3 +- markdown/__meta__.py | 1 - markdown/blockparser.py | 3 - markdown/blockprocessors.py | 24 +++---- markdown/core.py | 19 +++--- markdown/extensions/__init__.py | 8 +-- markdown/extensions/abbr.py | 4 +- markdown/extensions/admonition.py | 4 +- markdown/extensions/attr_list.py | 4 +- markdown/extensions/codehilite.py | 6 +- markdown/extensions/def_list.py | 4 +- markdown/extensions/extra.py | 2 - markdown/extensions/fenced_code.py | 10 ++- markdown/extensions/footnotes.py | 14 ++-- markdown/extensions/legacy_attrs.py | 1 - markdown/extensions/legacy_em.py | 2 - markdown/extensions/md_in_html.py | 2 - markdown/extensions/meta.py | 2 - markdown/extensions/nl2br.py | 2 - markdown/extensions/sane_lists.py | 6 +- markdown/extensions/smarty.py | 4 +- markdown/extensions/tables.py | 4 +- markdown/extensions/toc.py | 15 ++--- markdown/extensions/wikilinks.py | 8 +-- markdown/inlinepatterns.py | 17 ++--- markdown/pep562.py | 5 +- markdown/postprocessors.py | 7 +- markdown/preprocessors.py | 3 - markdown/serializers.py | 6 +- markdown/test_tools.py | 15 +---- markdown/treeprocessors.py | 17 ++--- markdown/util.py | 64 +++++++++++-------- setup.py | 12 +--- tests/__init__.py | 1 - tests/test_apis.py | 20 +----- tests/test_extensions.py | 10 ++- tests/test_legacy.py | 1 - tests/test_syntax/__init__.py | 1 - tests/test_syntax/blocks/__init__.py | 1 - tests/test_syntax/blocks/test_code_blocks.py | 1 - tests/test_syntax/blocks/test_headers.py | 1 - tests/test_syntax/blocks/test_hr.py | 1 - tests/test_syntax/blocks/test_paragraphs.py | 1 - tests/test_syntax/extensions/__init__.py | 1 - .../extensions/test_fenced_code.py | 2 - .../test_syntax/extensions/test_footnotes.py | 2 - .../extensions/test_legacy_attrs.py | 2 - .../test_syntax/extensions/test_legacy_em.py | 2 - tests/test_syntax/extensions/test_tables.py | 2 - tests/test_syntax/inline/__init__.py | 1 - tests/test_syntax/inline/test_emphasis.py | 1 - tests/test_syntax/inline/test_entities.py | 1 - tests/test_syntax/inline/test_images.py | 1 - tests/test_syntax/inline/test_links.py | 1 - tests/test_syntax/inline/test_raw_html.py | 1 - tox.ini | 3 +- 60 files changed, 132 insertions(+), 255 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2cade8658..e6127e5bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,16 @@ -dist: xenial -sudo: false language: python cache: pip matrix: include: - - python: '2.7' - env: TOXENV=py27 - python: '3.5' env: TOXENV=py35 - python: '3.6' env: TOXENV=py36 - python: '3.7' env: TOXENV=py37 - - python: 'pypy' - env: TOXENV=pypy - dist: trusty - python: 'pypy3' env: TOXENV=pypy3 - dist: trusty - env: TOXENV=flake8 - env: TOXENV=checkspelling addons: @@ -34,8 +26,7 @@ addons: - libtidy-0.99-0 install: - # NOTE: setuptools needs to be installed explicitly for py34 (trusty). - - pip install 'setuptools>=36' tox + - pip install tox script: - tox diff --git a/docs/change_log/release-3.2.md b/docs/change_log/release-3.2.md index 49f7649f6..375831e2f 100644 --- a/docs/change_log/release-3.2.md +++ b/docs/change_log/release-3.2.md @@ -2,11 +2,16 @@ title: Release Notes for v3.2 # Python-Markdown 3.2 Release Notes -Python-Markdown version 3.2 supports Python versions 2.7, 3.5, 3.6, 3.7, +Python-Markdown version 3.2 supports Python versions 3.5, 3.6, 3.7, PyPy and PyPy3. ## Backwards-incompatible changes +### Drop support for Python 2.7 + +Python 2.7 reaches end-of-life on 2020-01-01 and Python-Markdown 3.2 has dropped +support for it. Please upgrade to Python 3, or use Python-Markdown 3.1. + ### `em` and `strong` inline processor changes In order to fix issue #792, `em`/`strong` inline processors were refactored. This diff --git a/docs/index.md b/docs/index.md index 994188b9f..0388070ac 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,11 +18,11 @@ Goals The Python-Markdown project is developed with the following goals in mind: -* Maintain a Python 2 *and* Python 3 library (with an optional CLI wrapper) - suited to use in web server environments (never raise an exception, never - write to stdout, etc.) as an implementation of the markdown parser that - follows the [syntax rules](https://daringfireball.net/projects/markdown/syntax) - and the behavior of the original (markdown.pl) implementation as reasonably as +* Maintain a Python library (with an optional CLI wrapper) suited to use in web + server environments (never raise an exception, never write to stdout, etc.) as + an implementation of the markdown parser that follows the + [syntax rules](https://daringfireball.net/projects/markdown/syntax) and the + behavior of the original (markdown.pl) implementation as reasonably as possible (see [differences](#differences) for a few exceptions). * Provide an [Extension API](extensions/api.md) which makes it possible diff --git a/markdown/__init__.py b/markdown/__init__.py index dd128f07e..dec73fec4 100644 --- a/markdown/__init__.py +++ b/markdown/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,8 +19,6 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import absolute_import -from __future__ import unicode_literals from .core import Markdown, markdown, markdownFromFile from .util import PY37 from .pep562 import Pep562 diff --git a/markdown/__main__.py b/markdown/__main__.py index 43e486c9a..7d78b7e7b 100644 --- a/markdown/__main__.py +++ b/markdown/__main__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -148,5 +147,5 @@ def run(): # pragma: no cover if __name__ == '__main__': # pragma: no cover # Support running module as a commandline command. - # Python 2.7 & 3.x do: `python -m markdown [options] [args]`. + # `python -m markdown [options] [args]`. run() diff --git a/markdown/__meta__.py b/markdown/__meta__.py index 1b7d60a24..cc8525708 100644 --- a/markdown/__meta__.py +++ b/markdown/__meta__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/markdown/blockparser.py b/markdown/blockparser.py index 667d68376..326f90f79 100644 --- a/markdown/blockparser.py +++ b/markdown/blockparser.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,8 +19,6 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import unicode_literals -from __future__ import absolute_import from . import util diff --git a/markdown/blockprocessors.py b/markdown/blockprocessors.py index 0bb6967a3..3e52195dc 100644 --- a/markdown/blockprocessors.py +++ b/markdown/blockprocessors.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -31,9 +30,6 @@ as they need to alter how markdown blocks are parsed. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import logging import re from . import util @@ -58,7 +54,7 @@ def build_block_parser(md, **kwargs): return parser -class BlockProcessor(object): +class BlockProcessor: """ Base class for block processors. Each subclass will provide the methods below to work with the source and @@ -161,7 +157,7 @@ class ListIndentProcessor(BlockProcessor): LIST_TYPES = ['ul', 'ol'] def __init__(self, *args): - super(ListIndentProcessor, self).__init__(*args) + super().__init__(*args) self.INDENT_RE = re.compile(r'^(([ ]{%s})+)' % self.tab_length) def test(self, parent, block): @@ -259,7 +255,7 @@ def run(self, parent, blocks): code = sibling[0] block, theRest = self.detab(block) code.text = util.AtomicString( - '%s\n%s\n' % (code.text, util.code_escape(block.rstrip())) + '{}\n{}\n'.format(code.text, util.code_escape(block.rstrip())) ) else: # This is a new codeblock. Create the elements and insert text. @@ -331,7 +327,7 @@ class OListProcessor(BlockProcessor): SIBLING_TAGS = ['ol', 'ul'] def __init__(self, parser): - super(OListProcessor, self).__init__(parser) + super().__init__(parser) # Detect an item (``1. item``). ``group(1)`` contains contents of item. self.RE = re.compile(r'^[ ]{0,%d}\d+\.[ ]+(.*)' % (self.tab_length - 1)) # Detect items on secondary lines. they can be of either list type. @@ -421,12 +417,12 @@ def get_items(self, block): # This is an indented (possibly nested) item. if items[-1].startswith(' '*self.tab_length): # Previous item was indented. Append to that item. - items[-1] = '%s\n%s' % (items[-1], line) + items[-1] = '{}\n{}'.format(items[-1], line) else: items.append(line) else: # This is another line of previous item. Append to that item. - items[-1] = '%s\n%s' % (items[-1], line) + items[-1] = '{}\n{}'.format(items[-1], line) return items @@ -436,7 +432,7 @@ class UListProcessor(OListProcessor): TAG = 'ul' def __init__(self, parser): - super(UListProcessor, self).__init__(parser) + super().__init__(parser) # Detect an item (``1. item``). ``group(1)`` contains contents of item. self.RE = re.compile(r'^[ ]{0,%d}[*+-][ ]+(.*)' % (self.tab_length - 1)) @@ -553,7 +549,7 @@ def run(self, parent, blocks): len(sibling) and sibling[0].tag == 'code'): # Last block is a codeblock. Append to preserve whitespace. sibling[0].text = util.AtomicString( - '%s%s' % (sibling[0].text, filler) + '{}{}'.format(sibling[0].text, filler) ) @@ -580,13 +576,13 @@ def run(self, parent, blocks): if sibling is not None: # Insetrt after sibling. if sibling.tail: - sibling.tail = '%s\n%s' % (sibling.tail, block) + sibling.tail = '{}\n{}'.format(sibling.tail, block) else: sibling.tail = '\n%s' % block else: # Append to parent.text if parent.text: - parent.text = '%s\n%s' % (parent.text, block) + parent.text = '{}\n{}'.format(parent.text, block) else: parent.text = block.lstrip() else: diff --git a/markdown/core.py b/markdown/core.py index 6527d2e67..6c7822cba 100644 --- a/markdown/core.py +++ b/markdown/core.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,8 +19,6 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import absolute_import -from __future__ import unicode_literals import codecs import sys import logging @@ -42,7 +39,7 @@ logger = logging.getLogger('MARKDOWN') -class Markdown(object): +class Markdown: """Convert Markdown to HTML.""" doc_tag = "div" # Element used to wrap document - later removed @@ -122,7 +119,7 @@ def registerExtensions(self, extensions, configs): """ for ext in extensions: - if isinstance(ext, util.string_type): + if isinstance(ext, str): ext = self.build_extension(ext, configs.get(ext, {})) if isinstance(ext, Extension): ext._extendMarkdown(self) @@ -132,7 +129,7 @@ def registerExtensions(self, extensions, configs): ) elif ext is not None: raise TypeError( - 'Extension "%s.%s" must be of type: "%s.%s"' % ( + 'Extension "{}.{}" must be of type: "{}.{}"'.format( ext.__class__.__module__, ext.__class__.__name__, Extension.__module__, Extension.__name__ ) @@ -221,7 +218,7 @@ def set_output_format(self, format): def is_block_level(self, tag): """Check if the tag is a block level HTML tag.""" - if isinstance(tag, util.string_type): + if isinstance(tag, str): return tag.lower().rstrip('/') in self.block_level_elements # Some ElementTree tags are not strings, so return False. return False @@ -253,7 +250,7 @@ def convert(self, source): return '' # a blank unicode string try: - source = util.text_type(source) + source = str(source) except UnicodeDecodeError as e: # pragma: no cover # Customise error message while maintaining original trackback e.reason += '. -- Note: Markdown only accepts unicode input!' @@ -321,7 +318,7 @@ def convertFile(self, input=None, output=None, encoding=None): # Read the source if input: - if isinstance(input, util.string_type): + if isinstance(input, str): input_file = codecs.open(input, mode="r", encoding=encoding) else: input_file = codecs.getreader(encoding)(input) @@ -329,7 +326,7 @@ def convertFile(self, input=None, output=None, encoding=None): input_file.close() else: text = sys.stdin.read() - if not isinstance(text, util.text_type): # pragma: no cover + if not isinstance(text, str): # pragma: no cover text = text.decode(encoding) text = text.lstrip('\ufeff') # remove the byte-order mark @@ -339,7 +336,7 @@ def convertFile(self, input=None, output=None, encoding=None): # Write to file or stdout if output: - if isinstance(output, util.string_type): + if isinstance(output, str): output_file = codecs.open(output, "w", encoding=encoding, errors="xmlcharrefreplace") diff --git a/markdown/extensions/__init__.py b/markdown/extensions/__init__.py index 3ec89acfd..010e310c3 100644 --- a/markdown/extensions/__init__.py +++ b/markdown/extensions/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,12 +19,11 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import unicode_literals import warnings from ..util import parseBoolValue -class Extension(object): +class Extension: """ Base class for extensions to subclass. """ # Default config -- to be overriden by a subclass @@ -50,7 +48,7 @@ def getConfig(self, key, default=''): def getConfigs(self): """ Return all configs settings as a dict. """ - return dict([(key, self.getConfig(key)) for key in self.config.keys()]) + return {key: self.getConfig(key) for key in self.config.keys()} def getConfigInfo(self): """ Return all config descriptions as a list of tuples. """ @@ -81,7 +79,7 @@ def _extendMarkdown(self, *args): # Must be a 2.x extension. Pass in a dumby md_globals. self.extendMarkdown(md, {}) warnings.warn( - "The 'md_globals' parameter of '{0}.{1}.extendMarkdown' is " + "The 'md_globals' parameter of '{}.{}.extendMarkdown' is " "deprecated.".format(self.__class__.__module__, self.__class__.__name__), category=DeprecationWarning, stacklevel=2 diff --git a/markdown/extensions/abbr.py b/markdown/extensions/abbr.py index 01ee724d0..6e0529af2 100644 --- a/markdown/extensions/abbr.py +++ b/markdown/extensions/abbr.py @@ -16,8 +16,6 @@ ''' -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..preprocessors import Preprocessor from ..inlinepatterns import InlineProcessor @@ -81,7 +79,7 @@ class AbbrInlineProcessor(InlineProcessor): """ Abbreviation inline pattern. """ def __init__(self, pattern, title): - super(AbbrInlineProcessor, self).__init__(pattern) + super().__init__(pattern) self.title = title def handleMatch(self, m, data): diff --git a/markdown/extensions/admonition.py b/markdown/extensions/admonition.py index 003ba622e..5696224bd 100644 --- a/markdown/extensions/admonition.py +++ b/markdown/extensions/admonition.py @@ -17,8 +17,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..blockprocessors import BlockProcessor from ..util import etree @@ -61,7 +59,7 @@ def run(self, parent, blocks): if m: klass, title = self.get_class_and_title(m) div = etree.SubElement(parent, 'div') - div.set('class', '%s %s' % (self.CLASSNAME, klass)) + div.set('class', '{} {}'.format(self.CLASSNAME, klass)) if title: p = etree.SubElement(div, 'p') p.text = title diff --git a/markdown/extensions/attr_list.py b/markdown/extensions/attr_list.py index d53f7b4e6..23c6ad0ec 100644 --- a/markdown/extensions/attr_list.py +++ b/markdown/extensions/attr_list.py @@ -17,8 +17,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..treeprocessors import Treeprocessor import re @@ -145,7 +143,7 @@ def assign_attrs(self, elem, attrs): # add to class cls = elem.get('class') if cls: - elem.set('class', '%s %s' % (cls, v)) + elem.set('class', '{} {}'.format(cls, v)) else: elem.set('class', v) else: diff --git a/markdown/extensions/codehilite.py b/markdown/extensions/codehilite.py index 243c73543..c3f32571e 100644 --- a/markdown/extensions/codehilite.py +++ b/markdown/extensions/codehilite.py @@ -15,8 +15,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..treeprocessors import Treeprocessor @@ -45,7 +43,7 @@ def parse_hl_lines(expr): # ------------------ The Main CodeHilite Class ---------------------- -class CodeHilite(object): +class CodeHilite: """ Determine language of source code, and pass it into pygments hilighter. @@ -257,7 +255,7 @@ def __init__(self, **kwargs): 'Default: True'] } - super(CodeHiliteExtension, self).__init__(**kwargs) + super().__init__(**kwargs) def extendMarkdown(self, md): """ Add HilitePostprocessor to Markdown instance. """ diff --git a/markdown/extensions/def_list.py b/markdown/extensions/def_list.py index 885c7b702..34c3c55fc 100644 --- a/markdown/extensions/def_list.py +++ b/markdown/extensions/def_list.py @@ -15,8 +15,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..blockprocessors import BlockProcessor, ListIndentProcessor from ..util import etree @@ -45,7 +43,7 @@ def run(self, parent, blocks): else: d, theRest = self.detab(block) if d: - d = '%s\n%s' % (m.group(2), d) + d = '{}\n{}'.format(m.group(2), d) else: d = m.group(2) sibling = self.lastChild(parent) diff --git a/markdown/extensions/extra.py b/markdown/extensions/extra.py index ea26fcbe5..ebd168c3f 100644 --- a/markdown/extensions/extra.py +++ b/markdown/extensions/extra.py @@ -29,8 +29,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension extensions = [ diff --git a/markdown/extensions/fenced_code.py b/markdown/extensions/fenced_code.py index 47487a1fc..71fac1ada 100644 --- a/markdown/extensions/fenced_code.py +++ b/markdown/extensions/fenced_code.py @@ -15,8 +15,6 @@ License: [BSD](https://opensource.org/licenses/bsd-license.php) """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..preprocessors import Preprocessor from .codehilite import CodeHilite, CodeHiliteExtension, parse_hl_lines @@ -45,7 +43,7 @@ class FencedBlockPreprocessor(Preprocessor): LANG_TAG = ' class="%s"' def __init__(self, md): - super(FencedBlockPreprocessor, self).__init__(md) + super().__init__(md) self.checked_for_codehilite = False self.codehilite_conf = {} @@ -91,9 +89,9 @@ def run(self, lines): self._escape(m.group('code'))) placeholder = self.md.htmlStash.store(code) - text = '%s\n%s\n%s' % (text[:m.start()], - placeholder, - text[m.end():]) + text = '{}\n{}\n{}'.format(text[:m.start()], + placeholder, + text[m.end():]) else: break return text.split("\n") diff --git a/markdown/extensions/footnotes.py b/markdown/extensions/footnotes.py index 95044563b..18466ea43 100644 --- a/markdown/extensions/footnotes.py +++ b/markdown/extensions/footnotes.py @@ -13,8 +13,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..preprocessors import Preprocessor from ..inlinepatterns import InlineProcessor @@ -59,7 +57,7 @@ def __init__(self, **kwargs): [":", "Footnote separator."] } - super(FootnoteExtension, self).__init__(**kwargs) + super().__init__(**kwargs) # In multiple invocations, emit links that don't get tangled. self.unique_prefix = 0 @@ -152,14 +150,14 @@ def makeFootnoteId(self, id): if self.getConfig("UNIQUE_IDS"): return 'fn%s%d-%s' % (self.get_separator(), self.unique_prefix, id) else: - return 'fn%s%s' % (self.get_separator(), id) + return 'fn{}{}'.format(self.get_separator(), id) def makeFootnoteRefId(self, id, found=False): """ Return footnote back-link id. """ if self.getConfig("UNIQUE_IDS"): return self.unique_ref('fnref%s%d-%s' % (self.get_separator(), self.unique_prefix, id), found) else: - return self.unique_ref('fnref%s%s' % (self.get_separator(), id), found) + return self.unique_ref('fnref{}{}'.format(self.get_separator(), id), found) def makeFootnotesDiv(self, root): """ Return div of footnotes as et Element. """ @@ -309,7 +307,7 @@ class FootnoteInlineProcessor(InlineProcessor): """ InlinePattern for footnote markers in a document's body text. """ def __init__(self, pattern, footnotes): - super(FootnoteInlineProcessor, self).__init__(pattern) + super().__init__(pattern) self.footnotes = footnotes def handleMatch(self, m, data): @@ -320,7 +318,7 @@ def handleMatch(self, m, data): sup.set('id', self.footnotes.makeFootnoteRefId(id, found=True)) a.set('href', '#' + self.footnotes.makeFootnoteId(id)) a.set('class', 'footnote-ref') - a.text = util.text_type(list(self.footnotes.footnotes.keys()).index(id) + 1) + a.text = str(list(self.footnotes.footnotes.keys()).index(id) + 1) return sup, m.start(0), m.end(0) else: return None, None, None @@ -355,7 +353,7 @@ def add_duplicates(self, li, duplicates): def get_num_duplicates(self, li): """ Get the number of duplicate refs of the footnote. """ fn, rest = li.attrib.get('id', '').split(self.footnotes.get_separator(), 1) - link_id = '%sref%s%s' % (fn, self.footnotes.get_separator(), rest) + link_id = '{}ref{}{}'.format(fn, self.footnotes.get_separator(), rest) return self.footnotes.found_refs.get(link_id, 0) def handle_duplicates(self, parent): diff --git a/markdown/extensions/legacy_attrs.py b/markdown/extensions/legacy_attrs.py index 1f3b50891..b51d77807 100644 --- a/markdown/extensions/legacy_attrs.py +++ b/markdown/extensions/legacy_attrs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/markdown/extensions/legacy_em.py b/markdown/extensions/legacy_em.py index 71ad14f49..c3d7b54a9 100644 --- a/markdown/extensions/legacy_em.py +++ b/markdown/extensions/legacy_em.py @@ -10,8 +10,6 @@ ''' -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..inlinepatterns import UnderscoreProcessor, EmStrongItem, EM_STRONG2_RE, STRONG_EM2_RE import re diff --git a/markdown/extensions/md_in_html.py b/markdown/extensions/md_in_html.py index bca14f228..2a0c443f8 100644 --- a/markdown/extensions/md_in_html.py +++ b/markdown/extensions/md_in_html.py @@ -14,8 +14,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..blockprocessors import BlockProcessor from .. import util diff --git a/markdown/extensions/meta.py b/markdown/extensions/meta.py index 3b84fbb56..10dee1184 100644 --- a/markdown/extensions/meta.py +++ b/markdown/extensions/meta.py @@ -15,8 +15,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..preprocessors import Preprocessor import re diff --git a/markdown/extensions/nl2br.py b/markdown/extensions/nl2br.py index 18338367a..6c7491bca 100644 --- a/markdown/extensions/nl2br.py +++ b/markdown/extensions/nl2br.py @@ -16,8 +16,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..inlinepatterns import SubstituteTagInlineProcessor diff --git a/markdown/extensions/sane_lists.py b/markdown/extensions/sane_lists.py index fd045437e..e27eb1803 100644 --- a/markdown/extensions/sane_lists.py +++ b/markdown/extensions/sane_lists.py @@ -15,8 +15,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..blockprocessors import OListProcessor, UListProcessor import re @@ -28,7 +26,7 @@ class SaneOListProcessor(OListProcessor): LAZY_OL = False def __init__(self, parser): - super(SaneOListProcessor, self).__init__(parser) + super().__init__(parser) self.CHILD_RE = re.compile(r'^[ ]{0,%d}((\d+\.))[ ]+(.*)' % (self.tab_length - 1)) @@ -38,7 +36,7 @@ class SaneUListProcessor(UListProcessor): SIBLING_TAGS = ['ul'] def __init__(self, parser): - super(SaneUListProcessor, self).__init__(parser) + super().__init__(parser) self.CHILD_RE = re.compile(r'^[ ]{0,%d}(([*+-]))[ ]+(.*)' % (self.tab_length - 1)) diff --git a/markdown/extensions/smarty.py b/markdown/extensions/smarty.py index d6287a619..894805f90 100644 --- a/markdown/extensions/smarty.py +++ b/markdown/extensions/smarty.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ''' Smarty extension for Python-Markdown ==================================== @@ -81,7 +80,6 @@ ''' -from __future__ import unicode_literals from . import Extension from ..inlinepatterns import HtmlInlineProcessor, HTML_RE from ..treeprocessors import InlineProcessor @@ -182,7 +180,7 @@ def __init__(self, **kwargs): 'smart_ellipses': [True, 'Educate ellipses'], 'substitutions': [{}, 'Overwrite default substitutions'], } - super(SmartyExtension, self).__init__(**kwargs) + super().__init__(**kwargs) self.substitutions = dict(substitutions) self.substitutions.update(self.getConfig('substitutions', default={})) diff --git a/markdown/extensions/tables.py b/markdown/extensions/tables.py index 79fc5704b..54942f86c 100644 --- a/markdown/extensions/tables.py +++ b/markdown/extensions/tables.py @@ -15,8 +15,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..blockprocessors import BlockProcessor from ..util import etree @@ -35,7 +33,7 @@ class TableProcessor(BlockProcessor): def __init__(self, parser): self.border = False self.separator = '' - super(TableProcessor, self).__init__(parser) + super().__init__(parser) def test(self, parent, block): """ diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py index 5e6a60a2f..6d45239fb 100644 --- a/markdown/extensions/toc.py +++ b/markdown/extensions/toc.py @@ -13,11 +13,9 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..treeprocessors import Treeprocessor -from ..util import etree, parseBoolValue, AMP_SUBSTITUTE, HTML_PLACEHOLDER_RE, string_type +from ..util import etree, parseBoolValue, AMP_SUBSTITUTE, HTML_PLACEHOLDER_RE import re import unicodedata @@ -123,7 +121,7 @@ def nest_toc_tokens(toc_list): class TocTreeprocessor(Treeprocessor): def __init__(self, md, config): - super(TocTreeprocessor, self).__init__(md) + super().__init__(md) self.marker = config["marker"] self.title = config["title"] @@ -135,7 +133,7 @@ def __init__(self, md, config): if self.use_permalinks is None: self.use_permalinks = config["permalink"] self.header_rgx = re.compile("[Hh][123456]") - if isinstance(config["toc_depth"], string_type) and '-' in config["toc_depth"]: + if isinstance(config["toc_depth"], str) and '-' in config["toc_depth"]: self.toc_top, self.toc_bottom = [int(x) for x in config["toc_depth"].split('-')] else: self.toc_top = 1 @@ -150,8 +148,7 @@ def iterparent(self, node): for child in node: if not self.header_rgx.match(child.tag) and child.tag not in ['pre', 'code']: yield node, child - for p, c in self.iterparent(child): - yield p, c + yield from self.iterparent(child) def replace_marker(self, root, elem): ''' Replace marker with elem. ''' @@ -237,7 +234,7 @@ def run(self, doc): toc_tokens = [] for el in doc.iter(): - if isinstance(el.tag, string_type) and self.header_rgx.match(el.tag): + if isinstance(el.tag, str) and self.header_rgx.match(el.tag): self.set_level(el) if int(el.tag[-1]) < self.toc_top or int(el.tag[-1]) > self.toc_bottom: continue @@ -308,7 +305,7 @@ def __init__(self, **kwargs): 'bottom (b) (..). Defaults to `6` (bottom).'], } - super(TocExtension, self).__init__(**kwargs) + super().__init__(**kwargs) def extendMarkdown(self, md): md.registerExtension(self) diff --git a/markdown/extensions/wikilinks.py b/markdown/extensions/wikilinks.py index 4c797fad7..5f81ee16a 100644 --- a/markdown/extensions/wikilinks.py +++ b/markdown/extensions/wikilinks.py @@ -15,8 +15,6 @@ ''' -from __future__ import absolute_import -from __future__ import unicode_literals from . import Extension from ..inlinepatterns import InlineProcessor from ..util import etree @@ -26,7 +24,7 @@ def build_url(label, base, end): """ Build a url from the label, a base, and an end. """ clean_label = re.sub(r'([ ]+_)|(_[ ]+)|([ ]+)', '_', label) - return '%s%s%s' % (base, clean_label, end) + return '{}{}{}'.format(base, clean_label, end) class WikiLinkExtension(Extension): @@ -39,7 +37,7 @@ def __init__(self, **kwargs): 'build_url': [build_url, 'Callable formats URL from label.'], } - super(WikiLinkExtension, self).__init__(**kwargs) + super().__init__(**kwargs) def extendMarkdown(self, md): self.md = md @@ -53,7 +51,7 @@ def extendMarkdown(self, md): class WikiLinksInlineProcessor(InlineProcessor): def __init__(self, pattern, config): - super(WikiLinksInlineProcessor, self).__init__(pattern) + super().__init__(pattern) self.config = config def handleMatch(self, m, data): diff --git a/markdown/inlinepatterns.py b/markdown/inlinepatterns.py index 030cf9b05..0b04a32d9 100644 --- a/markdown/inlinepatterns.py +++ b/markdown/inlinepatterns.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -61,8 +60,6 @@ * finally we apply strong and emphasis """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import util from collections import namedtuple import re @@ -190,7 +187,7 @@ class EmStrongItem(namedtuple('EmStrongItem', ['pattern', 'builder', 'tags'])): """ -class Pattern(object): # pragma: no cover +class Pattern: # pragma: no cover """Base class that inline patterns subclass. """ ANCESTOR_EXCLUDES = tuple() @@ -247,7 +244,7 @@ def get_stash(m): id = m.group(1) if id in stash: value = stash.get(id) - if isinstance(value, util.string_type): + if isinstance(value, str): return value else: # An etree Element - return text content only @@ -321,7 +318,7 @@ class EscapeInlineProcessor(InlineProcessor): def handleMatch(self, m, data): char = m.group(1) if char in self.md.ESCAPED_CHARS: - return '%s%s%s' % (util.STX, ord(char), util.ETX), m.start(0), m.end(0) + return '{}{}{}'.format(util.STX, ord(char), util.ETX), m.start(0), m.end(0) else: return None, m.start(0), m.end(0) @@ -374,7 +371,7 @@ class BacktickInlineProcessor(InlineProcessor): """ Return a `` element containing the matching text. """ def __init__(self, pattern): InlineProcessor.__init__(self, pattern) - self.ESCAPED_BSLASH = '%s%s%s' % (util.STX, ord('\\'), util.ETX) + self.ESCAPED_BSLASH = '{}{}{}'.format(util.STX, ord('\\'), util.ETX) self.tag = 'code' def handleMatch(self, m, data): @@ -654,7 +651,7 @@ def getLink(self, data, index): # Track last character last = '' - for pos in util.iterrange(index, len(data)): + for pos in range(index, len(data)): c = data[pos] if c == '(': # Count nested ( @@ -740,7 +737,7 @@ def getText(self, data, index): """ bracket_count = 1 text = [] - for pos in util.iterrange(index, len(data)): + for pos in range(index, len(data)): c = data[pos] if c == ']': bracket_count -= 1 @@ -869,7 +866,7 @@ def codepoint2name(code): """Return entity definition by code, or the code if not defined.""" entity = entities.codepoint2name.get(code) if entity: - return "%s%s;" % (util.AMP_SUBSTITUTE, entity) + return "{}{};".format(util.AMP_SUBSTITUTE, entity) else: return "%s#%d;" % (util.AMP_SUBSTITUTE, code) diff --git a/markdown/pep562.py b/markdown/pep562.py index 8add471df..b130d3b1d 100644 --- a/markdown/pep562.py +++ b/markdown/pep562.py @@ -20,7 +20,6 @@ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from __future__ import unicode_literals import sys from collections import namedtuple import re @@ -138,7 +137,7 @@ def __new__(cls, major, minor, micro, release="final", pre=0, post=0, dev=0): elif dev: raise ValueError("Version is not a development release.") - return super(Version, cls).__new__(cls, major, minor, micro, release, pre, post, dev) + return super().__new__(cls, major, minor, micro, release, pre, post, dev) def _is_pre(self): """Is prerelease.""" @@ -210,7 +209,7 @@ def parse_version(ver, pre=False): return Version(major, minor, micro, release, pre, post, dev) -class Pep562(object): +class Pep562: """ Backport of PEP 562 . diff --git a/markdown/postprocessors.py b/markdown/postprocessors.py index 5cfb4e6f5..95b85cd1e 100644 --- a/markdown/postprocessors.py +++ b/markdown/postprocessors.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -28,8 +27,6 @@ """ -from __future__ import absolute_import -from __future__ import unicode_literals from collections import OrderedDict from . import util import re @@ -111,10 +108,10 @@ def run(self, text): class UnescapePostprocessor(Postprocessor): """ Restore escaped chars """ - RE = re.compile(r'%s(\d+)%s' % (util.STX, util.ETX)) + RE = re.compile(r'{}(\d+){}'.format(util.STX, util.ETX)) def unescape(self, m): - return util.int2str(int(m.group(1))) + return chr(int(m.group(1))) def run(self, text): return self.RE.sub(self.unescape, text) diff --git a/markdown/preprocessors.py b/markdown/preprocessors.py index 89f4baaab..f12a02a94 100644 --- a/markdown/preprocessors.py +++ b/markdown/preprocessors.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -26,8 +25,6 @@ complicated. """ -from __future__ import absolute_import -from __future__ import unicode_literals from . import util import re diff --git a/markdown/serializers.py b/markdown/serializers.py index a5a5e1700..dcd3029d6 100644 --- a/markdown/serializers.py +++ b/markdown/serializers.py @@ -37,8 +37,6 @@ # -------------------------------------------------------------------- -from __future__ import absolute_import -from __future__ import unicode_literals from xml.etree.ElementTree import ProcessingInstruction from . import util import re @@ -63,7 +61,7 @@ def _raise_serialization_error(text): # pragma: no cover raise TypeError( - "cannot serialize %r (type %s)" % (text, type(text).__name__) + "cannot serialize {!r} (type {})".format(text, type(text).__name__) ) @@ -158,7 +156,7 @@ def _serialize_html(write, elem, format): # handle boolean attributes write(" %s" % v) else: - write(' %s="%s"' % (k, v)) + write(' {}="{}"'.format(k, v)) if namespace_uri: write(' xmlns="%s"' % (_escape_attrib(namespace_uri))) if format == "xhtml" and tag.lower() in HTML_EMPTY: diff --git a/markdown/test_tools.py b/markdown/test_tools.py index f3f9d487b..a42b14bc0 100644 --- a/markdown/test_tools.py +++ b/markdown/test_tools.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,9 +19,7 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import absolute_import import os -import io import unittest import textwrap from . import markdown @@ -108,9 +105,9 @@ def __new__(cls, name, bases, dct): def generate_test(infile, outfile, normalize, kwargs): def test(self): - with io.open(infile, encoding="utf-8") as f: + with open(infile, encoding="utf-8") as f: input = f.read() - with io.open(outfile, encoding="utf-8") as f: + with open(outfile, encoding="utf-8") as f: # Normalize line endings # (on Windows, git may have altered line endings). expected = f.read().replace("\r\n", "\n") @@ -150,13 +147,7 @@ def test(self): return type.__new__(cls, name, bases, dct) -# Define LegacyTestCase class with metaclass in Py2 & Py3 compatible way. -# See https://stackoverflow.com/a/38668373/866026 -# TODO: If/when py2 support is dropped change to: -# class LegacyTestCase(unittest.Testcase, metaclass=LegacyTestMeta) - - -class LegacyTestCase(LegacyTestMeta('LegacyTestCase', (unittest.TestCase,), {'__slots__': ()})): +class LegacyTestCase(unittest.TestCase, metaclass=LegacyTestMeta): """ A `unittest.TestCase` subclass for running Markdown's legacy file-based tests. diff --git a/markdown/treeprocessors.py b/markdown/treeprocessors.py index 5c2be2d0f..f80f570b4 100644 --- a/markdown/treeprocessors.py +++ b/markdown/treeprocessors.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,8 +19,6 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import unicode_literals -from __future__ import absolute_import from . import util from . import inlinepatterns @@ -37,7 +34,7 @@ def build_treeprocessors(md, **kwargs): def isString(s): """ Check if it's string """ if not isinstance(s, util.AtomicString): - return isinstance(s, util.string_type) + return isinstance(s, str) return False @@ -310,12 +307,12 @@ def __applyPattern(self, pattern, data, patternIndex, startIndex=0): placeholder = self.__stashNode(node, pattern.type()) if new_style: - return "%s%s%s" % (data[:start], - placeholder, data[end:]), True, 0 + return "{}{}{}".format(data[:start], + placeholder, data[end:]), True, 0 else: # pragma: no cover - return "%s%s%s%s" % (leftData, - match.group(1), - placeholder, match.groups()[-1]), True, 0 + return "{}{}{}{}".format(leftData, + match.group(1), + placeholder, match.groups()[-1]), True, 0 def __build_ancestors(self, parent, parents): """Build the ancestor list.""" @@ -351,7 +348,7 @@ def run(self, tree, ancestors=None): # to ensure we don't have the user accidentally change it on us. tree_parents = [] if ancestors is None else ancestors[:] - self.parent_map = dict((c, p) for p in tree.iter() for c in p) + self.parent_map = {c: p for p in tree.iter() for c in p} stack = [(tree, tree_parents)] while stack: diff --git a/markdown/util.py b/markdown/util.py index 7b96d694a..b53491710 100644 --- a/markdown/util.py +++ b/markdown/util.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,31 +19,24 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import unicode_literals import re import sys from collections import namedtuple from functools import wraps import warnings +from .pep562 import Pep562 -""" -Python 3 Stuff -============================================================================= -""" -PY3 = sys.version_info[0] == 3 PY37 = (3, 7) <= sys.version_info -if PY3: # pragma: no cover - string_type = str - text_type = str - int2str = chr - iterrange = range -else: # pragma: no cover - string_type = basestring # noqa - text_type = unicode # noqa - int2str = unichr # noqa - iterrange = xrange # noqa + +# TODO: Remove deprecated variables in a future release. +__deprecated__ = { + 'string_type': ('str', str), + 'text_type': ('str', str), + 'int2str': ('chr', chr), + 'iterrange': ('range', range) +} """ @@ -136,7 +128,7 @@ def deprecated_func(*args, **kwargs): @deprecated("Use 'Markdown.is_block_level' instead.") def isBlockLevel(tag): """Check if the tag is a block level HTML tag.""" - if isinstance(tag, string_type): + if isinstance(tag, str): return tag.lower().rstrip('/') in BLOCK_LEVEL_ELEMENTS # Some ElementTree tags are not strings, so return False. return False @@ -147,7 +139,7 @@ def parseBoolValue(value, fail_on_errors=True, preserve_none=False): returns True or False. If preserve_none=True, returns True, False, or None. If parsing was not successful, raises ValueError, or, if fail_on_errors=False, returns None.""" - if not isinstance(value, string_type): + if not isinstance(value, str): if preserve_none and value is None: return value return bool(value) @@ -178,12 +170,12 @@ def code_escape(text): """ -class AtomicString(text_type): +class AtomicString(str): """A string which should not be further processed.""" pass -class Processor(object): +class Processor: def __init__(self, md=None): self.md = md @@ -194,7 +186,7 @@ def markdown(self): return self.md -class HtmlStash(object): +class HtmlStash: """ This class is used for stashing HTML objects that we extract in the beginning and replace with place-holders. @@ -248,7 +240,7 @@ def store_tag(self, tag, attrs, left_index, right_index): _PriorityItem = namedtuple('PriorityItem', ['name', 'priority']) -class Registry(object): +class Registry: """ A priority sorted registry. @@ -294,7 +286,7 @@ def __init__(self): self._is_sorted = False def __contains__(self, item): - if isinstance(item, string_type): + if isinstance(item, str): # Check if an item exists by this name. return item in self._data.keys() # Check if this instance exists. @@ -319,7 +311,7 @@ def __len__(self): return len(self._priority) def __repr__(self): - return '<{0}({1})>'.format(self.__class__.__name__, list(self)) + return '<{}({})>'.format(self.__class__.__name__, list(self)) def get_index_for_name(self, name): """ @@ -330,7 +322,7 @@ def get_index_for_name(self, name): return self._priority.index( [x for x in self._priority if x.name == name][0] ) - raise ValueError('No item named "{0}" exists.'.format(name)) + raise ValueError('No item named "{}" exists.'.format(name)) def register(self, item, name, priority): """ @@ -383,7 +375,7 @@ def _sort(self): def __setitem__(self, key, value): """ Register item with priorty 5 less than lowest existing priority. """ - if isinstance(key, string_type): + if isinstance(key, str): warnings.warn( 'Using setitem to register a processor or pattern is deprecated. ' 'Use the `register` method instead.', @@ -459,3 +451,21 @@ def add(self, key, value, location): DeprecationWarning, stacklevel=2, ) + + +def __getattr__(name): + """Get attribute.""" + + deprecated = __deprecated__.get(name) + if deprecated: + warnings.warn( + "'{}' is deprecated. Use '{}' instead.".format(name, deprecated[0]), + category=DeprecationWarning, + stacklevel=(3 if PY37 else 4) + ) + return deprecated[1] + raise AttributeError("module '{}' has no attribute '{}'".format(__name__, name)) + + +if not PY37: + Pep562(__name__) diff --git a/setup.py b/setup.py index 6b9167daa..1c74567b1 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Python Markdown @@ -23,7 +22,6 @@ import os -import sys from setuptools import setup @@ -31,11 +29,6 @@ def get_version(): """Get version and version_info from markdown/__meta__.py file.""" module_path = os.path.join(os.path.dirname('__file__'), 'markdown', '__meta__.py') - if sys.version_info[0] == 2: - import imp - meta = imp.load_source('__meta__', module_path) - return meta.__version__, meta.__version_info__ - import importlib.util spec = importlib.util.spec_from_file_location('__meta__', module_path) meta = importlib.util.module_from_spec(spec) @@ -94,7 +87,7 @@ def get_version(): maintainer_email='waylan.limberg@icloud.com', license='BSD License', packages=['markdown', 'markdown.extensions'], - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*', + python_requires='>=3.5', install_requires=['setuptools >= 36'], extras_require={ 'testing': [ @@ -133,12 +126,11 @@ def get_version(): '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.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Communications :: Email :: Filters', diff --git a/tests/__init__.py b/tests/__init__.py index f2a783760..564ba3be4 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_apis.py b/tests/test_apis.py index e6f36e3d6..597f780ca 100644 --- a/tests/test_apis.py +++ b/tests/test_apis.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -25,7 +24,6 @@ Tests of the various APIs with the python markdown lib. """ -from __future__ import unicode_literals import unittest import sys import os @@ -39,14 +37,6 @@ from xml.etree.ElementTree import ProcessingInstruction -PY3 = sys.version_info[0] == 3 - - -if not PY3: - def bytes(string, encoding): - return string.encode(encoding) - - class TestMarkdownBasics(unittest.TestCase): """ Tests basics of the Markdown class. """ @@ -220,7 +210,7 @@ def testReset(self): self.assertEqual(self.stash.rawHtmlBlocks, []) -class Item(object): +class Item: """ A dummy Registry item object for testing. """ def __init__(self, data): self.data = data @@ -444,12 +434,6 @@ def tearDown(self): # Reset warning behavior back to default warnings.simplefilter('default') - def testNonUnicodeSource(self): - """ Test falure on non-unicode source text. """ - if not PY3: - source = "foo".encode('utf-16') - self.assertRaises(UnicodeDecodeError, markdown.markdown, source) - def testBadOutputFormat(self): """ Test failure on bad output_format. """ self.assertRaises(KeyError, markdown.Markdown, output_format='invalid') @@ -865,7 +849,7 @@ def testMultipleExtensionOptions(self): def create_config_file(self, config): """ Helper to create temp config files. """ - if not isinstance(config, markdown.util.string_type): + if not isinstance(config, str): # convert to string config = yaml.dump(config) fd, self.tempfile = tempfile.mkstemp('.yml') diff --git a/tests/test_extensions.py b/tests/test_extensions.py index 1e4cf106f..3ff4f7f7d 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -26,7 +25,6 @@ continue to work as advertised. This used to be accomplished by doctests. """ -from __future__ import unicode_literals import unittest import markdown @@ -37,8 +35,8 @@ def assertStartsWith(self, expectedPrefix, text, msg=None): if not text.startswith(expectedPrefix): if len(expectedPrefix) + 5 < len(text): text = text[:len(expectedPrefix) + 5] + '...' - standardMsg = '%s not found at the start of %s' % (repr(expectedPrefix), - repr(text)) + standardMsg = '{} not found at the start of {}'.format(repr(expectedPrefix), + repr(text)) self.fail(self._formatMessage(msg, standardMsg)) @@ -982,9 +980,9 @@ def testWithAttrList(self): def testUniqueFunc(self): """ Test 'unique' function. """ from markdown.extensions.toc import unique - ids = set(['foo']) + ids = {'foo'} self.assertEqual(unique('foo', ids), 'foo_1') - self.assertEqual(ids, set(['foo', 'foo_1'])) + self.assertEqual(ids, {'foo', 'foo_1'}) def testTocInHeaders(self): diff --git a/tests/test_legacy.py b/tests/test_legacy.py index f4e807e5f..a52c9971d 100644 --- a/tests/test_legacy.py +++ b/tests/test_legacy.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/__init__.py b/tests/test_syntax/__init__.py index f2a783760..564ba3be4 100644 --- a/tests/test_syntax/__init__.py +++ b/tests/test_syntax/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/blocks/__init__.py b/tests/test_syntax/blocks/__init__.py index f2a783760..564ba3be4 100644 --- a/tests/test_syntax/blocks/__init__.py +++ b/tests/test_syntax/blocks/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/blocks/test_code_blocks.py b/tests/test_syntax/blocks/test_code_blocks.py index d42037941..54b686069 100644 --- a/tests/test_syntax/blocks/test_code_blocks.py +++ b/tests/test_syntax/blocks/test_code_blocks.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/blocks/test_headers.py b/tests/test_syntax/blocks/test_headers.py index 4afa4ec6a..be511d4c2 100644 --- a/tests/test_syntax/blocks/test_headers.py +++ b/tests/test_syntax/blocks/test_headers.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/blocks/test_hr.py b/tests/test_syntax/blocks/test_hr.py index e02ba9ef2..6f6cc8899 100644 --- a/tests/test_syntax/blocks/test_hr.py +++ b/tests/test_syntax/blocks/test_hr.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/blocks/test_paragraphs.py b/tests/test_syntax/blocks/test_paragraphs.py index 41d33129d..9b7ba0357 100644 --- a/tests/test_syntax/blocks/test_paragraphs.py +++ b/tests/test_syntax/blocks/test_paragraphs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/extensions/__init__.py b/tests/test_syntax/extensions/__init__.py index f2a783760..564ba3be4 100644 --- a/tests/test_syntax/extensions/__init__.py +++ b/tests/test_syntax/extensions/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/extensions/test_fenced_code.py b/tests/test_syntax/extensions/test_fenced_code.py index 5f1d73692..64d662e36 100644 --- a/tests/test_syntax/extensions/test_fenced_code.py +++ b/tests/test_syntax/extensions/test_fenced_code.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,7 +19,6 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import unicode_literals from markdown.test_tools import TestCase diff --git a/tests/test_syntax/extensions/test_footnotes.py b/tests/test_syntax/extensions/test_footnotes.py index 9008aec95..7785a2b2a 100644 --- a/tests/test_syntax/extensions/test_footnotes.py +++ b/tests/test_syntax/extensions/test_footnotes.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,7 +19,6 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import unicode_literals from markdown.test_tools import TestCase diff --git a/tests/test_syntax/extensions/test_legacy_attrs.py b/tests/test_syntax/extensions/test_legacy_attrs.py index e3d3aae53..b4ba5e715 100644 --- a/tests/test_syntax/extensions/test_legacy_attrs.py +++ b/tests/test_syntax/extensions/test_legacy_attrs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,7 +19,6 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import unicode_literals from markdown.test_tools import TestCase diff --git a/tests/test_syntax/extensions/test_legacy_em.py b/tests/test_syntax/extensions/test_legacy_em.py index e173242f3..efad43d9b 100644 --- a/tests/test_syntax/extensions/test_legacy_em.py +++ b/tests/test_syntax/extensions/test_legacy_em.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,7 +19,6 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import unicode_literals from markdown.test_tools import TestCase diff --git a/tests/test_syntax/extensions/test_tables.py b/tests/test_syntax/extensions/test_tables.py index 7003eb1f3..b01ea0c42 100644 --- a/tests/test_syntax/extensions/test_tables.py +++ b/tests/test_syntax/extensions/test_tables.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown @@ -20,7 +19,6 @@ License: BSD (see LICENSE.md for details). """ -from __future__ import unicode_literals from markdown.test_tools import TestCase diff --git a/tests/test_syntax/inline/__init__.py b/tests/test_syntax/inline/__init__.py index f2a783760..564ba3be4 100644 --- a/tests/test_syntax/inline/__init__.py +++ b/tests/test_syntax/inline/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/inline/test_emphasis.py b/tests/test_syntax/inline/test_emphasis.py index ba01d708b..b4f1d0d39 100644 --- a/tests/test_syntax/inline/test_emphasis.py +++ b/tests/test_syntax/inline/test_emphasis.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/inline/test_entities.py b/tests/test_syntax/inline/test_entities.py index 67b06477d..34cc2e725 100644 --- a/tests/test_syntax/inline/test_entities.py +++ b/tests/test_syntax/inline/test_entities.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/inline/test_images.py b/tests/test_syntax/inline/test_images.py index 52ce33049..261c24bab 100644 --- a/tests/test_syntax/inline/test_images.py +++ b/tests/test_syntax/inline/test_images.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/inline/test_links.py b/tests/test_syntax/inline/test_links.py index 988e041f2..be4237db0 100644 --- a/tests/test_syntax/inline/test_links.py +++ b/tests/test_syntax/inline/test_links.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tests/test_syntax/inline/test_raw_html.py b/tests/test_syntax/inline/test_raw_html.py index 23ce8e5ef..a9c4857e0 100644 --- a/tests/test_syntax/inline/test_raw_html.py +++ b/tests/test_syntax/inline/test_raw_html.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Python Markdown diff --git a/tox.ini b/tox.ini index 7be4552fd..793bd676e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,5 @@ [tox] -envlist = py27, py35, py36, py37, pypy, pypy3, flake8, checkspelling, pep517check -requires = setuptools>=36 +envlist = py35, py36, py37, pypy3, flake8, checkspelling, pep517check isolated_build = True min_verison = 1.9