tags.
"""
yield 0, ""
- for tup in inner:
- yield tup
+ yield from inner
yield 0, "
"
def _add_newline(self, inner):
@@ -2099,7 +2095,7 @@ def _code_block_sub(self, match: re.Match) -> str:
codeblock = self._encode_code(codeblock)
- return "\n%s\n
\n" % (
+ return "\n{}\n
\n".format(
pre_class_str, code_class_str, codeblock)
def _html_class_str_from_tag(self, tag: str) -> str:
@@ -2158,7 +2154,7 @@ def _do_code_blocks(self, text: str) -> str:
def _code_span_sub(self, match: re.Match) -> str:
c = match.group(2).strip(" \t")
c = self._encode_code(c)
- return "%s
" % (self._html_class_str_from_tag("code"), c)
+ return "{}
".format(self._html_class_str_from_tag("code"), c)
@mark_stage(Stage.CODE_SPANS)
def _do_code_spans(self, text: str) -> str:
@@ -2398,7 +2394,7 @@ def _encode_backslash_escapes(self, text: str) -> str:
_auto_link_re = re.compile(r'<((https?|ftp):[^\'">\s]+)>', re.I)
def _auto_link_sub(self, match: re.Match) -> str:
g1 = match.group(1)
- return '%s' % (self._protect_url(g1), g1)
+ return '{}'.format(self._protect_url(g1), g1)
_auto_email_link_re = re.compile(r"""
<
@@ -2470,7 +2466,7 @@ def _uniform_outdent(
text: str,
min_outdent: Optional[str] = None,
max_outdent: Optional[str] = None
- ) -> Tuple[str, str]:
+ ) -> tuple[str, str]:
'''
Removes the smallest common leading indentation from each (non empty)
line of `text` and returns said indent along with the outdented text.
@@ -2481,7 +2477,7 @@ def _uniform_outdent(
'''
# find the leading whitespace for every line
- whitespace: List[Union[str, None]] = [
+ whitespace: list[Union[str, None]] = [
re.findall(r'^[ \t]*', line)[0] if line else None
for line in text.splitlines()
]
@@ -2575,15 +2571,15 @@ class MarkdownWithExtras(Markdown):
# ----------------------------------------------------------
class Extra(ABC):
- _registry: Dict[str, Type['Extra']] = {}
- _exec_order: Dict[Stage, Tuple[List[Type['Extra']], List[Type['Extra']]]] = {}
+ _registry: dict[str, type['Extra']] = {}
+ _exec_order: dict[Stage, tuple[list[type['Extra']], list[type['Extra']]]] = {}
name: str
'''
An identifiable name that users can use to invoke the extra
in the Markdown class
'''
- order: Tuple[Collection[Union[Stage, Type['Extra']]], Collection[Union[Stage, Type['Extra']]]]
+ order: tuple[Collection[Union[Stage, type['Extra']]], Collection[Union[Stage, type['Extra']]]]
'''
Tuple of two iterables containing the stages/extras this extra will run before and
after, respectively
@@ -2756,11 +2752,11 @@ def sub(self, match: re.Match) -> str:
# indent the body before placing inside the aside block
admonition = self.md._uniform_indent(
- '%s\n%s\n\n%s\n' % (admonition_type, title, body),
+ '{}\n{}\n\n{}\n'.format(admonition_type, title, body),
self.md.tab, False
)
# wrap it in an aside
- admonition = '' % (admonition_class, admonition)
+ admonition = ''.format(admonition_class, admonition)
# now indent the whole admonition back to where it started
return self.md._uniform_indent(admonition, lead_indent, False)
@@ -2921,7 +2917,7 @@ def unhash_code(codeblock):
# add back the indent to all lines
return "\n%s\n" % self.md._uniform_indent(colored, leading_indent, True)
- def tags(self, lexer_name: str) -> Tuple[str, str]:
+ def tags(self, lexer_name: str) -> tuple[str, str]:
'''
Returns the tags that the encoded code block will be wrapped in, based
upon the lexer name.
@@ -2934,10 +2930,10 @@ def tags(self, lexer_name: str) -> Tuple[str, str]:
'''
pre_class = self.md._html_class_str_from_tag('pre')
if "highlightjs-lang" in self.md.extras and lexer_name:
- code_class = ' class="%s language-%s"' % (lexer_name, lexer_name)
+ code_class = ' class="{} language-{}"'.format(lexer_name, lexer_name)
else:
code_class = self.md._html_class_str_from_tag('code')
- return ('' % (pre_class, code_class), '
')
+ return (''.format(pre_class, code_class), '
')
def sub(self, match: re.Match) -> str:
lexer_name = match.group(2)
@@ -2961,7 +2957,7 @@ def sub(self, match: re.Match) -> str:
tags = self.tags(lexer_name)
- return "\n%s%s%s\n%s%s\n" % (leading_indent, tags[0], codeblock, leading_indent, tags[1])
+ return "\n{}{}{}\n{}{}\n".format(leading_indent, tags[0], codeblock, leading_indent, tags[1])
def run(self, text):
return self.fenced_code_block_re.sub(self.sub, text)
@@ -3078,7 +3074,7 @@ def run(self, text):
# To avoid markdown and :
.replace('*', self.md._escape_table['*'])
.replace('_', self.md._escape_table['_']))
- link = '%s' % (escaped_href, text[start:end])
+ link = '{}'.format(escaped_href, text[start:end])
hash = _hash_text(link)
link_from_hash[hash] = link
text = text[:start] + hash + text[end:]
@@ -3412,7 +3408,7 @@ def sub(self, match: re.Match) -> str:
hlines = ['' % self.md._html_class_str_from_tag('table'), '' % self.md._html_class_str_from_tag('thead'), '']
cols = [re.sub(escape_bar_re, '|', cell.strip()) for cell in re.split(split_bar_re, re.sub(trim_bar_re, "", re.sub(trim_space_re, "", head)))]
for col_idx, col in enumerate(cols):
- hlines.append(' %s | ' % (
+ hlines.append(' {} | '.format(
align_from_col_idx.get(col_idx, ''),
self.md._run_span_gamut(col)
))
@@ -3425,7 +3421,7 @@ def sub(self, match: re.Match) -> str:
hlines.append('
')
cols = [re.sub(escape_bar_re, '|', cell.strip()) for cell in re.split(split_bar_re, re.sub(trim_bar_re, "", re.sub(trim_space_re, "", line)))]
for col_idx, col in enumerate(cols):
- hlines.append(' %s | ' % (
+ hlines.append(' {} | '.format(
align_from_col_idx.get(col_idx, ''),
self.md._run_span_gamut(col)
))
@@ -3509,7 +3505,7 @@ def sub(self, match: re.Match) -> str:
self.md._escape_table[waves] = _hash_text(waves)
return self.md._uniform_indent(
- '\n%s%s%s\n' % (open_tag, self.md._escape_table[waves], close_tag),
+ '\n{}{}{}\n'.format(open_tag, self.md._escape_table[waves], close_tag),
lead_indent, include_empty_lines=True
)
@@ -3556,7 +3552,7 @@ def format_cell(text):
add_hline('' % self.md._html_class_str_from_tag('thead'), 1)
add_hline('', 2)
for cell in rows[0]:
- add_hline("{} | ".format(format_cell(cell)), 3)
+ add_hline(f"{format_cell(cell)} | ", 3)
add_hline('
', 2)
add_hline('', 1)
# Only one header row allowed.
@@ -3567,7 +3563,7 @@ def format_cell(text):
for row in rows:
add_hline('
', 2)
for cell in row:
- add_hline('{} | '.format(format_cell(cell)), 3)
+ add_hline(f'{format_cell(cell)} | ', 3)
add_hline('
', 2)
add_hline('', 1)
add_hline('
')
@@ -3605,7 +3601,7 @@ def test(self, text):
# ---- internal support functions
-def calculate_toc_html(toc: Union[List[Tuple[int, str, str]], None]) -> Optional[str]:
+def calculate_toc_html(toc: Union[list[tuple[int, str, str]], None]) -> Optional[str]:
"""Return the HTML for the current TOC.
This expects the `_toc` attribute to have been set on this instance.
@@ -3629,7 +3625,7 @@ def indent():
if not lines[-1].endswith(""):
lines[-1] += ""
lines.append("%s" % indent())
- lines.append('%s%s' % (
+ lines.append('{}{}'.format(
indent(), id, name))
while len(h_stack) > 1:
h_stack.pop()
@@ -3644,7 +3640,7 @@ class UnicodeWithAttrs(str):
possibly attach some attributes. E.g. the "toc_html" attribute when
the "toc" extra is used.
"""
- metadata: Optional[Dict[str, str]] = None
+ metadata: Optional[dict[str, str]] = None
toc_html: Optional[str] = None
## {{{ http://code.activestate.com/recipes/577257/ (r1)
@@ -3704,7 +3700,7 @@ def _regex_from_encoded_pattern(s: str) -> re.Pattern:
# Recipe: dedent (0.1.2)
-def _dedentlines(lines: List[str], tabsize: int = 8, skip_first_line: bool = False) -> List[str]:
+def _dedentlines(lines: list[str], tabsize: int = 8, skip_first_line: bool = False) -> list[str]:
"""_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines
"lines" is a list of lines to dedent.
@@ -3795,7 +3791,7 @@ def _dedent(text: str, tabsize: int = 8, skip_first_line: bool = False) -> str:
return ''.join(lines)
-class _memoized(object):
+class _memoized:
"""Decorator that caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned, and
not re-evaluated.
@@ -3942,7 +3938,7 @@ def main(argv=None):
formatter_class=_NoReflowFormatter
)
parser.add_argument('--version', action='version',
- version='%(prog)s {version}'.format(version=__version__))
+ version=f'%(prog)s {__version__}')
parser.add_argument('paths', nargs='*',
help=(
'optional list of files to convert.'
diff --git a/perf/gen_perf_cases.py b/perf/gen_perf_cases.py
index 28b9e18c..8a45f3e4 100755
--- a/perf/gen_perf_cases.py
+++ b/perf/gen_perf_cases.py
@@ -104,9 +104,9 @@ def _markdown_from_aspn_html(html):
title = None
escaped_href = href.replace('(', '\\(').replace(')', '\\)')
if title is None:
- replacement = '[%s](%s)' % (content, escaped_href)
+ replacement = '[{}]({})'.format(content, escaped_href)
else:
- replacement = '[%s](%s "%s")' % (content, escaped_href,
+ replacement = '[{}]({} "{}")'.format(content, escaped_href,
title.replace('"', "'"))
markdown = markdown[:start] + replacement + markdown[end:]
diff --git a/perf/perf.py b/perf/perf.py
index ad500d8e..ed8bd864 100755
--- a/perf/perf.py
+++ b/perf/perf.py
@@ -34,7 +34,7 @@ def time_markdown_py(cases_dir, repeat):
for i in range(repeat):
start = clock()
for path in glob(join(cases_dir, "*.text")):
- f = open(path, 'r')
+ f = open(path)
content = f.read()
f.close()
try:
@@ -59,7 +59,7 @@ def time_markdown2_py(cases_dir, repeat):
for i in range(repeat):
start = clock()
for path in glob(join(cases_dir, "*.text")):
- f = open(path, 'r')
+ f = open(path)
content = f.read()
f.close()
markdowner.convert(content)
diff --git a/perf/strip_cookbook_data.py b/perf/strip_cookbook_data.py
index 47cceb79..d92597b8 100644
--- a/perf/strip_cookbook_data.py
+++ b/perf/strip_cookbook_data.py
@@ -1,4 +1,3 @@
-
from os.path import *
from pprint import pformat
diff --git a/perf/util.py b/perf/util.py
index e32d0f8b..7fcc862f 100644
--- a/perf/util.py
+++ b/perf/util.py
@@ -30,7 +30,7 @@ def wrapper(*args, **kw):
return func(*args, **kw)
finally:
total_time = clock() - start_time
- print("%s took %.3fs" % (func.__name__, total_time))
+ print("{} took {:.3f}s".format(func.__name__, total_time))
return wrapper
def hotshotit(func):
diff --git a/sandbox/wiki.py b/sandbox/wiki.py
index f270b636..fbe0894a 100644
--- a/sandbox/wiki.py
+++ b/sandbox/wiki.py
@@ -1,4 +1,3 @@
-
import sys
import re
from os.path import *
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 5e409001..00000000
--- a/setup.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-[wheel]
-universal = 1
diff --git a/setup.py b/setup.py
index b408e3a6..14cd4564 100755
--- a/setup.py
+++ b/setup.py
@@ -18,11 +18,11 @@
License :: OSI Approved :: MIT License
Programming Language :: Python
Programming Language :: Python :: 3
-Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
+Programming Language :: Python :: 3.13
Operating System :: OS Independent
Topic :: Software Development :: Libraries :: Python Modules
Topic :: Software Development :: Documentation
@@ -32,7 +32,7 @@
extras_require = {
"code_syntax_highlighting": ["pygments>=2.7.3"],
- "wavedrom": ["wavedrom; python_version>='3.7'"],
+ "wavedrom": ["wavedrom"],
"latex": ['latex2mathml; python_version>="3.8.1"'],
}
# nested listcomp to combine all optional extras into convenient "all" option
@@ -56,7 +56,7 @@
]
},
description="A fast and complete Python implementation of Markdown",
- python_requires=">=3.8, <4",
+ python_requires=">=3.9, <4",
extras_require=extras_require,
classifiers=classifiers.strip().split("\n"),
long_description="""markdown2: A fast and complete Python implementation of Markdown.
diff --git a/test/markdown.py b/test/markdown.py
index e18336b1..b2ef85da 100644
--- a/test/markdown.py
+++ b/test/markdown.py
@@ -116,7 +116,7 @@ def is_block_level (tag) :
(re.compile(">"), ">"),
(re.compile("\""), """)]
-ENTITY_NORMALIZATION_EXPRESSIONS_SOFT = [ (re.compile("&(?!\#)"), "&"),
+ENTITY_NORMALIZATION_EXPRESSIONS_SOFT = [ (re.compile(r"&(?!\#)"), "&"),
(re.compile("<"), "<"),
(re.compile(">"), ">"),
(re.compile("\""), """)]
@@ -325,7 +325,7 @@ def toxml(self):
value = self.attribute_values[attr]
value = self.doc.normalizeEntities(value,
avoidDoubleNormalizing=True)
- buffer += ' %s="%s"' % (attr, value)
+ buffer += ' {}="{}"'.format(attr, value)
# Now let's actually append the children
@@ -672,7 +672,7 @@ def run (self, lines) :
LINK_ANGLED_RE = BRK + r'\s*\(<([^\)]*)>\)' # [text]()
IMAGE_LINK_RE = r'\!' + BRK + r'\s*\(([^\)]*)\)' # ![alttxt](http://x.com/)
REFERENCE_RE = BRK+ r'\s*\[([^\]]*)\]' # [Google][3]
-IMAGE_REFERENCE_RE = r'\!' + BRK + '\s*\[([^\]]*)\]' # ![alt text][2]
+IMAGE_REFERENCE_RE = r'\!' + BRK + r'\s*\[([^\]]*)\]' # ![alt text][2]
NOT_STRONG_RE = r'( \* )' # stand-alone * or _
AUTOLINK_RE = r'<(http://[^>]*)>' #
AUTOMAIL_RE = r'<([^> \!]*@[^> ]*)>' #
diff --git a/test/test.py b/test/test.py
index 871cd2e1..995db47a 100755
--- a/test/test.py
+++ b/test/test.py
@@ -27,10 +27,7 @@ def setup():
import pygments # noqa
except ImportError:
pygments_dir = join(top_dir, "deps", "pygments")
- if sys.version_info[0] <= 2:
- sys.path.insert(0, pygments_dir)
- else:
- sys.path.insert(0, pygments_dir + "3")
+ sys.path.insert(0, pygments_dir + "3")
if __name__ == "__main__":
logging.basicConfig()
@@ -42,7 +39,7 @@ def setup():
try:
mod = importlib.import_module(extra_lib)
except ImportError:
- warnings.append("skipping %s tests ('%s' module not found)" % (extra_lib, extra_lib))
+ warnings.append("skipping {} tests ('{}' module not found)".format(extra_lib, extra_lib))
default_tags.append("-%s" % extra_lib)
else:
if extra_lib == 'pygments':
@@ -51,7 +48,7 @@ def setup():
tag = "pygments<2.14"
else:
tag = "pygments>=2.14"
- warnings.append("skipping %s tests (pygments %s found)" % (tag, mod.__version__))
+ warnings.append("skipping {} tests (pygments {} found)".format(tag, mod.__version__))
default_tags.append("-%s" % tag)
retval = testlib.harness(testdir_from_ns=testdir_from_ns,
diff --git a/test/test_markdown2.py b/test/test_markdown2.py
index d6618052..06313af9 100755
--- a/test/test_markdown2.py
+++ b/test/test_markdown2.py
@@ -152,7 +152,7 @@ def generate_tests(cls):
if exists(opts_path):
try:
with warnings.catch_warnings(record=True) as caught_warnings:
- opts = eval(open(opts_path, 'r').read())
+ opts = eval(open(opts_path).read())
for warning in caught_warnings:
print("WARNING: loading %s generated warning: %s - lineno %d" % (opts_path, warning.message, warning.lineno), file=sys.stderr)
except Exception:
@@ -335,7 +335,7 @@ def _markdown_email_link_sub(match):
href, text = match.groups()
href = _xml_escape_re.sub(_xml_escape_sub, href)
text = _xml_escape_re.sub(_xml_escape_sub, text)
- return '%s' % (href, text)
+ return '{}'.format(href, text)
def norm_html_from_html(html):
"""Normalize (somewhat) Markdown'd HTML.
diff --git a/test/testall.py b/test/testall.py
index 02ff4895..1158f529 100644
--- a/test/testall.py
+++ b/test/testall.py
@@ -53,14 +53,14 @@ def testall():
# Don't support Python < 3.5
continue
ver_str = "%s.%s" % ver
- print("-- test with Python %s (%s)" % (ver_str, python))
+ print("-- test with Python {} ({})".format(ver_str, python))
assert ' ' not in python
env_args = 'MACOSX_DEPLOYMENT_TARGET= ' if sys.platform == 'darwin' else ''
proc = subprocess.Popen(
# pass "-u" option to force unbuffered output
- "%s%s -u test.py -- -knownfailure" % (env_args, python),
+ "{}{} -u test.py -- -knownfailure".format(env_args, python),
shell=True, stderr=subprocess.PIPE
)
@@ -77,6 +77,6 @@ def testall():
for python, ver_str, warning in all_warnings:
# now re-print all warnings to make sure they are seen
- print('-- warning raised by Python %s (%s) -- %s' % (ver_str, python, warning))
+ print('-- warning raised by Python {} ({}) -- {}'.format(ver_str, python, warning))
testall()
diff --git a/test/testlib.py b/test/testlib.py
index c6244dc4..f0e38663 100644
--- a/test/testlib.py
+++ b/test/testlib.py
@@ -130,8 +130,8 @@ def wrapper(*args, **kw):
finally:
total_time = time.time() - start_time
if total_time > max_time + tolerance:
- raise DurationError(('Test was too long (%.2f s)'
- % total_time))
+ raise DurationError('Test was too long (%.2f s)'
+ % total_time)
return wrapper
return _timedtest
@@ -140,7 +140,7 @@ def wrapper(*args, **kw):
#---- module api
-class Test(object):
+class Test:
def __init__(self, ns, testmod, testcase, testfn_name,
testsuite_class=None):
self.ns = ns
@@ -439,7 +439,7 @@ def list_tests(testdir_from_ns, tags):
if testfile.endswith(".pyc"):
testfile = testfile[:-1]
print("%s:" % t.shortname())
- print(" from: %s#%s.%s" % (testfile,
+ print(" from: {}#{}.{}".format(testfile,
t.testcase.__class__.__name__, t.testfn_name))
wrapped = textwrap.fill(' '.join(t.tags()), WIDTH-10)
print(" tags: %s" % _indent(wrapped, 8, True))
@@ -470,7 +470,7 @@ def __init__(self, stream):
def getDescription(self, test):
if test._testlib_explicit_tags_:
- return "%s [%s]" % (test._testlib_shortname_,
+ return "{} [{}]".format(test._testlib_shortname_,
', '.join(test._testlib_explicit_tags_))
else:
return test._testlib_shortname_
@@ -514,7 +514,7 @@ def printErrorList(self, flavour, errors):
self.stream.write("%s\n" % err)
-class ConsoleTestRunner(object):
+class ConsoleTestRunner:
"""A test runner class that displays results on the console.
It prints out the names of tests as they are run, errors as they
diff --git a/tools/cutarelease.py b/tools/cutarelease.py
index 86e058fa..54201e22 100755
--- a/tools/cutarelease.py
+++ b/tools/cutarelease.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
# Copyright (c) 2009-2012 Trent Mick
"""cutarelease -- Cut a release of your project.
@@ -154,10 +153,10 @@ def cutarelease(project_name, version_files, dry_run=False):
% (changes_path, version))
# Tag version and push.
- curr_tags = set(t for t in _capture_stdout(["git", "tag", "-l"]).split(b'\n') if t)
+ curr_tags = {t for t in _capture_stdout(["git", "tag", "-l"]).split(b'\n') if t}
if not dry_run and version not in curr_tags:
log.info("tag the release")
- run('git tag -a "%s" -m "version %s"' % (version, version))
+ run('git tag -a "{}" -m "version {}"'.format(version, version))
run('git push --tags')
# Optionally release.
@@ -193,9 +192,9 @@ def cutarelease(project_name, version_files, dry_run=False):
if marker not in changes_txt:
raise Error("couldn't find `%s' marker in `%s' "
"content: can't prep for subsequent dev" % (marker, changes_path))
- next_verline = "%s %s%s" % (marker.rsplit(None, 1)[0], next_version, nyr)
+ next_verline = "{} {}{}".format(marker.rsplit(None, 1)[0], next_version, nyr)
changes_txt = changes_txt.replace(marker + '\n',
- "%s\n\n(nothing yet)\n\n\n%s\n" % (next_verline, marker))
+ "{}\n\n(nothing yet)\n\n\n{}\n".format(next_verline, marker))
if not dry_run:
f = codecs.open(changes_path, 'w', 'utf-8')
f.write(changes_txt)
@@ -221,12 +220,12 @@ def cutarelease(project_name, version_files, dry_run=False):
ver_content = ver_content.replace(marker,
'var VERSION = "%s";' % next_version)
elif ver_file_type == "python":
- marker = "__version_info__ = %r" % (version_info,)
+ marker = "__version_info__ = {!r}".format(version_info)
if marker not in ver_content:
raise Error("couldn't find `%s' version marker in `%s' "
"content: can't prep for subsequent dev" % (marker, ver_file))
ver_content = ver_content.replace(marker,
- "__version_info__ = %r" % (next_version_tuple,))
+ "__version_info__ = {!r}".format(next_version_tuple))
elif ver_file_type == "version":
ver_content = next_version
else:
@@ -238,7 +237,7 @@ def cutarelease(project_name, version_files, dry_run=False):
f.close()
if not dry_run:
- run('git commit %s %s -m "prep for future dev"' % (
+ run('git commit {} {} -m "prep for future dev"'.format(
changes_path, ' '.join(version_files)))
run('git push')
diff --git a/tox.ini b/tox.ini
index 0d85e20a..815b5bd7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,7 +4,8 @@
# and then run "tox" from this directory.
[tox]
-envlist = py36, py37, py38, py39, py310, py311, py312, pypy
+envlist = py{39, 310, 311, 312, 313, py}
[testenv]
+allowlist_externals = make
commands = make testone