Skip to content

Commit 85d0c18

Browse files
authored
Add and improve type annotations
1 parent 5cbabe1 commit 85d0c18

25 files changed

+278
-212
lines changed

docs/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414

1515
* Fix type annotations for `convertFile` - it accepts only bytes-based buffers.
1616
Also remove legacy checks from Python 2 (#1400)
17+
* Improve and expand type annotations in the code base (#1401).
1718

1819
## [3.5.1] -- 2023-10-31
1920

markdown/blockprocessors.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,14 @@ def __init__(self, *args):
171171
super().__init__(*args)
172172
self.INDENT_RE = re.compile(r'^(([ ]{%s})+)' % self.tab_length)
173173

174-
def test(self, parent, block):
174+
def test(self, parent: etree.Element, block: str) -> bool:
175175
return block.startswith(' '*self.tab_length) and \
176176
not self.parser.state.isstate('detabbed') and \
177177
(parent.tag in self.ITEM_TYPES or
178178
(len(parent) and parent[-1] is not None and
179179
(parent[-1].tag in self.LIST_TYPES)))
180180

181-
def run(self, parent, blocks):
181+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
182182
block = blocks.pop(0)
183183
level, sibling = self.get_level(parent, block)
184184
block = self.looseDetab(block, level)
@@ -251,10 +251,10 @@ def get_level(self, parent: etree.Element, block: str) -> tuple[int, etree.Eleme
251251
class CodeBlockProcessor(BlockProcessor):
252252
""" Process code blocks. """
253253

254-
def test(self, parent, block):
254+
def test(self, parent: etree.Element, block: str) -> bool:
255255
return block.startswith(' '*self.tab_length)
256256

257-
def run(self, parent, blocks):
257+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
258258
sibling = self.lastChild(parent)
259259
block = blocks.pop(0)
260260
theRest = ''
@@ -286,10 +286,10 @@ class BlockQuoteProcessor(BlockProcessor):
286286

287287
RE = re.compile(r'(^|\n)[ ]{0,3}>[ ]?(.*)')
288288

289-
def test(self, parent, block):
289+
def test(self, parent: etree.Element, block: str) -> bool:
290290
return bool(self.RE.search(block)) and not util.nearing_recursion_limit()
291291

292-
def run(self, parent, blocks):
292+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
293293
block = blocks.pop(0)
294294
m = self.RE.search(block)
295295
if m:
@@ -353,10 +353,10 @@ def __init__(self, parser: BlockParser):
353353
self.INDENT_RE = re.compile(r'^[ ]{%d,%d}((\d+\.)|[*+-])[ ]+.*' %
354354
(self.tab_length, self.tab_length * 2 - 1))
355355

356-
def test(self, parent, block):
356+
def test(self, parent: etree.Element, block: str) -> bool:
357357
return bool(self.RE.match(block))
358358

359-
def run(self, parent, blocks):
359+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
360360
# Check for multiple items in one block.
361361
items = self.get_items(blocks.pop(0))
362362
sibling = self.lastChild(parent)
@@ -460,10 +460,10 @@ class HashHeaderProcessor(BlockProcessor):
460460
# Detect a header at start of any line in block
461461
RE = re.compile(r'(?:^|\n)(?P<level>#{1,6})(?P<header>(?:\\.|[^\\])*?)#*(?:\n|$)')
462462

463-
def test(self, parent, block):
463+
def test(self, parent: etree.Element, block: str) -> bool:
464464
return bool(self.RE.search(block))
465465

466-
def run(self, parent, blocks):
466+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
467467
block = blocks.pop(0)
468468
m = self.RE.search(block)
469469
if m:
@@ -491,10 +491,10 @@ class SetextHeaderProcessor(BlockProcessor):
491491
# Detect Setext-style header. Must be first 2 lines of block.
492492
RE = re.compile(r'^.*?\n[=-]+[ ]*(\n|$)', re.MULTILINE)
493493

494-
def test(self, parent, block):
494+
def test(self, parent: etree.Element, block: str) -> bool:
495495
return bool(self.RE.match(block))
496496

497-
def run(self, parent, blocks):
497+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
498498
lines = blocks.pop(0).split('\n')
499499
# Determine level. `=` is 1 and `-` is 2.
500500
if lines[1].startswith('='):
@@ -517,15 +517,15 @@ class HRProcessor(BlockProcessor):
517517
# Detect hr on any line of a block.
518518
SEARCH_RE = re.compile(RE, re.MULTILINE)
519519

520-
def test(self, parent, block):
520+
def test(self, parent: etree.Element, block: str) -> bool:
521521
m = self.SEARCH_RE.search(block)
522522
if m:
523523
# Save match object on class instance so we can use it later.
524524
self.match = m
525525
return True
526526
return False
527527

528-
def run(self, parent, blocks):
528+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
529529
block = blocks.pop(0)
530530
match = self.match
531531
# Check for lines in block before `hr`.
@@ -545,10 +545,10 @@ def run(self, parent, blocks):
545545
class EmptyBlockProcessor(BlockProcessor):
546546
""" Process blocks that are empty or start with an empty line. """
547547

548-
def test(self, parent, block):
548+
def test(self, parent: etree.Element, block: str) -> bool:
549549
return not block or block.startswith('\n')
550550

551-
def run(self, parent, blocks):
551+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
552552
block = blocks.pop(0)
553553
filler = '\n\n'
554554
if block:
@@ -575,10 +575,10 @@ class ReferenceProcessor(BlockProcessor):
575575
r'^[ ]{0,3}\[([^\[\]]*)\]:[ ]*\n?[ ]*([^\s]+)[ ]*(?:\n[ ]*)?((["\'])(.*)\4[ ]*|\((.*)\)[ ]*)?$', re.MULTILINE
576576
)
577577

578-
def test(self, parent, block):
578+
def test(self, parent: etree.Element, block: str) -> bool:
579579
return True
580580

581-
def run(self, parent, blocks):
581+
def run(self, parent: etree.Element, blocks: list[str]) -> bool:
582582
block = blocks.pop(0)
583583
m = self.RE.search(block)
584584
if m:
@@ -601,10 +601,10 @@ def run(self, parent, blocks):
601601
class ParagraphProcessor(BlockProcessor):
602602
""" Process Paragraph blocks. """
603603

604-
def test(self, parent, block):
604+
def test(self, parent: etree.Element, block: str) -> bool:
605605
return True
606606

607-
def run(self, parent, blocks):
607+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
608608
block = blocks.pop(0)
609609
if block.strip():
610610
# Not a blank block. Add to parent, otherwise throw it away.

markdown/core.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def build_parser(self) -> Markdown:
159159
def registerExtensions(
160160
self,
161161
extensions: Sequence[Extension | str],
162-
configs: Mapping[str, Mapping[str, Any]]
162+
configs: Mapping[str, dict[str, Any]]
163163
) -> Markdown:
164164
"""
165165
Load a list of extensions into an instance of the `Markdown` class.
@@ -491,8 +491,8 @@ def markdownFromFile(**kwargs: Any):
491491
[`convert`][markdown.Markdown.convert].
492492
493493
Keyword arguments:
494-
input (str | TextIO): A file name or readable object.
495-
output (str | TextIO): A file name or writable object.
494+
input (str | BinaryIO): A file name or readable object.
495+
output (str | BinaryIO): A file name or writable object.
496496
encoding (str): Encoding of input and output.
497497
**kwargs: Any arguments accepted by the `Markdown` class.
498498

markdown/extensions/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
from __future__ import annotations
2929

30-
from typing import TYPE_CHECKING, Any, Mapping, Sequence
30+
from typing import TYPE_CHECKING, Any, Iterable, Mapping
3131
from ..util import parseBoolValue
3232

3333
if TYPE_CHECKING: # pragma: no cover
@@ -112,7 +112,7 @@ def setConfig(self, key: str, value: Any) -> None:
112112
value = parseBoolValue(value, preserve_none=True)
113113
self.config[key][0] = value
114114

115-
def setConfigs(self, items: Mapping[str, Any] | Sequence[tuple[str, Any]]):
115+
def setConfigs(self, items: Mapping[str, Any] | Iterable[tuple[str, Any]]) -> None:
116116
"""
117117
Loop through a collection of configuration options, passing each to
118118
[`setConfig`][markdown.extensions.Extension.setConfig].

markdown/extensions/abbr.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ class AbbrPreprocessor(BlockProcessor):
4343

4444
RE = re.compile(r'^[*]\[(?P<abbr>[^\]]*)\][ ]?:[ ]*\n?[ ]*(?P<title>.*)$', re.MULTILINE)
4545

46-
def test(self, parent, block):
46+
def test(self, parent: etree.Element, block: str) -> bool:
4747
return True
4848

49-
def run(self, parent, blocks):
49+
def run(self, parent: etree.Element, blocks: list[str]) -> bool:
5050
"""
5151
Find and remove all Abbreviation references from the text.
5252
Each reference is set as a new `AbbrPattern` in the markdown instance.
@@ -71,7 +71,7 @@ def run(self, parent, blocks):
7171
blocks.insert(0, block)
7272
return False
7373

74-
def _generate_pattern(self, text):
74+
def _generate_pattern(self, text: str) -> str:
7575
"""
7676
Given a string, returns an regex pattern to match that string.
7777
@@ -90,11 +90,11 @@ def _generate_pattern(self, text):
9090
class AbbrInlineProcessor(InlineProcessor):
9191
""" Abbreviation inline pattern. """
9292

93-
def __init__(self, pattern, title):
93+
def __init__(self, pattern: str, title: str):
9494
super().__init__(pattern)
9595
self.title = title
9696

97-
def handleMatch(self, m, data):
97+
def handleMatch(self, m: re.Match[str], data: str) -> tuple[etree.Element, int, int]:
9898
abbr = etree.Element('abbr')
9999
abbr.text = AtomicString(m.group('abbr'))
100100
abbr.set('title', self.title)

markdown/extensions/admonition.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
from ..blockprocessors import BlockProcessor
3131
import xml.etree.ElementTree as etree
3232
import re
33+
from typing import TYPE_CHECKING
34+
35+
if TYPE_CHECKING: # pragma: no cover
36+
from markdown import blockparser
3337

3438

3539
class AdmonitionExtension(Extension):
@@ -49,15 +53,15 @@ class AdmonitionProcessor(BlockProcessor):
4953
RE = re.compile(r'(?:^|\n)!!! ?([\w\-]+(?: +[\w\-]+)*)(?: +"(.*?)")? *(?:\n|$)')
5054
RE_SPACES = re.compile(' +')
5155

52-
def __init__(self, parser):
56+
def __init__(self, parser: blockparser.BlockParser):
5357
"""Initialization."""
5458

5559
super().__init__(parser)
5660

57-
self.current_sibling = None
61+
self.current_sibling: etree.Element | None = None
5862
self.content_indention = 0
5963

60-
def parse_content(self, parent, block):
64+
def parse_content(self, parent: etree.Element, block: str) -> tuple[etree.Element | None, str, str]:
6165
"""Get sibling admonition.
6266
6367
Retrieve the appropriate sibling element. This can get tricky when
@@ -115,14 +119,14 @@ def parse_content(self, parent, block):
115119

116120
return sibling, block, the_rest
117121

118-
def test(self, parent, block):
122+
def test(self, parent: etree.Element, block: str) -> bool:
119123

120124
if self.RE.search(block):
121125
return True
122126
else:
123127
return self.parse_content(parent, block)[0] is not None
124128

125-
def run(self, parent, blocks):
129+
def run(self, parent: etree.Element, blocks: list[str]) -> None:
126130
block = blocks.pop(0)
127131
m = self.RE.search(block)
128132

@@ -160,7 +164,7 @@ def run(self, parent, blocks):
160164
# list for future processing.
161165
blocks.insert(0, theRest)
162166

163-
def get_class_and_title(self, match):
167+
def get_class_and_title(self, match: re.Match[str]) -> tuple[str, str | None]:
164168
klass, title = match.group(1).lower(), match.group(2)
165169
klass = self.RE_SPACES.sub(' ', klass)
166170
if title is None:

markdown/extensions/attr_list.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class AttrListTreeprocessor(Treeprocessor):
8686
r'\uf900-\ufdcf\ufdf0-\ufffd'
8787
r'\:\-\.0-9\u00b7\u0300-\u036f\u203f-\u2040]+')
8888

89-
def run(self, doc: Element):
89+
def run(self, doc: Element) -> None:
9090
for elem in doc.iter():
9191
if self.md.is_block_level(elem.tag):
9292
# Block level: check for `attrs` on last line of text

markdown/extensions/codehilite.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
from . import Extension
2525
from ..treeprocessors import Treeprocessor
2626
from ..util import parseBoolValue
27+
from typing import TYPE_CHECKING, Callable, Any
28+
29+
if TYPE_CHECKING: # pragma: no cover
30+
import xml.etree.ElementTree as etree
2731

2832
try: # pragma: no cover
2933
from pygments import highlight
@@ -110,11 +114,11 @@ class CodeHilite:
110114

111115
def __init__(self, src: str, **options):
112116
self.src = src
113-
self.lang = options.pop('lang', None)
114-
self.guess_lang = options.pop('guess_lang', True)
115-
self.use_pygments = options.pop('use_pygments', True)
116-
self.lang_prefix = options.pop('lang_prefix', 'language-')
117-
self.pygments_formatter = options.pop('pygments_formatter', 'html')
117+
self.lang: str | None = options.pop('lang', None)
118+
self.guess_lang: bool = options.pop('guess_lang', True)
119+
self.use_pygments: bool = options.pop('use_pygments', True)
120+
self.lang_prefix: str = options.pop('lang_prefix', 'language-')
121+
self.pygments_formatter: str | Callable = options.pop('pygments_formatter', 'html')
118122

119123
if 'linenos' not in options:
120124
options['linenos'] = options.pop('linenums', None)
@@ -128,7 +132,7 @@ def __init__(self, src: str, **options):
128132

129133
self.options = options
130134

131-
def hilite(self, shebang=True) -> str:
135+
def hilite(self, shebang: bool = True) -> str:
132136
"""
133137
Pass code to the [Pygments](https://pygments.org/) highlighter with
134138
optional line numbers. The output should then be styled with CSS to
@@ -187,7 +191,7 @@ def hilite(self, shebang=True) -> str:
187191
txt
188192
)
189193

190-
def _parseHeader(self):
194+
def _parseHeader(self) -> None:
191195
"""
192196
Determines language of a code block from shebang line and whether the
193197
said line should be removed or left in place. If the shebang line
@@ -249,7 +253,9 @@ def _parseHeader(self):
249253
class HiliteTreeprocessor(Treeprocessor):
250254
""" Highlight source code in code blocks. """
251255

252-
def code_unescape(self, text):
256+
config: dict[str, Any]
257+
258+
def code_unescape(self, text: str) -> str:
253259
"""Unescape code."""
254260
text = text.replace("&lt;", "<")
255261
text = text.replace("&gt;", ">")
@@ -258,7 +264,7 @@ def code_unescape(self, text):
258264
text = text.replace("&amp;", "&")
259265
return text
260266

261-
def run(self, root):
267+
def run(self, root: etree.Element) -> None:
262268
""" Find code blocks and store in `htmlStash`. """
263269
blocks = root.iter('pre')
264270
for block in blocks:

markdown/extensions/def_list.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ class DefListProcessor(BlockProcessor):
3333
RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)')
3434
NO_INDENT_RE = re.compile(r'^[ ]{0,3}[^ :]')
3535

36-
def test(self, parent, block):
36+
def test(self, parent: etree.Element, block: str) -> bool:
3737
return bool(self.RE.search(block))
3838

39-
def run(self, parent, blocks):
39+
def run(self, parent: etree.Element, blocks: list[str]) -> bool | None:
4040

4141
raw_block = blocks.pop(0)
4242
m = self.RE.search(raw_block)
@@ -99,7 +99,7 @@ class DefListIndentProcessor(ListIndentProcessor):
9999
LIST_TYPES = ['dl', 'ol', 'ul']
100100
""" Include `dl` is list types. """
101101

102-
def create_item(self, parent, block):
102+
def create_item(self, parent: etree.Element, block: str) -> None:
103103
""" Create a new `dd` or `li` (depending on parent) and parse the block with it as the parent. """
104104

105105
dd = etree.SubElement(parent, 'dd')

0 commit comments

Comments
 (0)