Skip to content

Commit f450c3d

Browse files
committed
Add runtime type checks with omlish.check and annotate ignored type errors
Replace manual isinstance calls with check.isinstance across the parser, update imports, add type: ignore comments where needed, simplify list initializations, and tighten docstring and comment formatting. Minor whitespace and line‑break adjustments are also applied.
1 parent 5dd2474 commit f450c3d

18 files changed

Lines changed: 67 additions & 78 deletions

File tree

omxtra/text/pdcmark/blocks/machine.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
4. Dispatch the remaining line content to leaf-block recognition (ATX heading, hrule, fence,
2020
HTML block, indented code, paragraph).
2121
"""
22+
from omlish import check
2223
from omlish import dataclasses as dc
2324

2425
from ..events import BlockQuote
@@ -229,7 +230,7 @@ def _process_line(self, bl: BufferedLine, events: list[Event]) -> None:
229230
while (
230231
matched_depth > 0
231232
and isinstance(self._stack[matched_depth - 1], OpenList)
232-
and not self._line_opens_matching_item(self._stack[matched_depth - 1], ls)
233+
and not self._line_opens_matching_item(check.isinstance(self._stack[matched_depth - 1], OpenList), ls)
233234
):
234235
matched_depth -= 1
235236

omxtra/text/pdcmark/inlines/emphasis.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ def resolve_emphasis(nodes: list[InlineNode]) -> list[InlineNode]:
4343
match_s = _find_matching_opener(nodes, delim_stack, node)
4444
if match_s is not None:
4545
opener_i = delim_stack[match_s]
46-
opener = nodes[opener_i]
47-
check.isinstance(opener, DelimNode)
46+
opener = check.isinstance(nodes[opener_i], DelimNode)
4847

4948
# GFM strikethrough: `~` and `~~` both produce <del>; longer runs (`~~~+`) don't pair at all. Equivalent
5049
# to the GFM "tilde must be ≤ 2 chars" rule.

omxtra/text/pdcmark/inlines/links.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ def resolve_links(
9595
continue
9696
match_s = len(stack) - 1
9797
entry = stack[match_s]
98-
opener = nodes[entry.node_index]
99-
check.isinstance(opener, LinkOpenNode)
98+
opener = check.isinstance(nodes[entry.node_index], LinkOpenNode)
10099

101100
# Inner children = nodes strictly between opener and closer.
102101
children = nodes[entry.node_index + 1:i]

omxtra/text/pdcmark/inlines/parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def _resolve_emphasis_recursive(nodes: list[InlineNode]) -> None:
104104

105105

106106
def _emit_events(nodes: ta.Iterable[InlineNode], out: list[Event]) -> None:
107+
tag: Tag
107108
for n in nodes:
108109
if isinstance(n, TextNode):
109110
if n.text:
@@ -124,7 +125,6 @@ def _emit_events(nodes: ta.Iterable[InlineNode], out: list[Event]) -> None:
124125
elif isinstance(n, HardBreakNode):
125126
out.append(HardBreak(offset=n.offset))
126127
elif isinstance(n, EmphasisGroup):
127-
tag: Tag
128128
if n.kind == 'strong':
129129
tag = Strong()
130130
elif n.kind == 'strikethrough':
@@ -135,7 +135,7 @@ def _emit_events(nodes: ta.Iterable[InlineNode], out: list[Event]) -> None:
135135
_emit_events(n.children, out)
136136
out.append(End(offset=n.offset, tag=tag))
137137
elif isinstance(n, LinkGroup):
138-
tag: Tag = (
138+
tag = (
139139
Image(link_type=n.link_type, dest_url=n.dest_url, title=n.title, id=n.id)
140140
if n.is_image
141141
else Link(link_type=n.link_type, dest_url=n.dest_url, title=n.title, id=n.id)

omxtra/text/pdcmark/scanning/links.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def scan_link_destination(text: str, start: int, *, max_parens: int = 32) -> Lin
101101
# Bare form: balanced parens; terminates at first ASCII control / space / unbalanced ')'.
102102
i = start
103103
depth = 0
104-
out: list[str] = []
104+
out = []
105105
while i < n:
106106
c = text[i]
107107
if ord(c) < 0x20 or c == ' ':

omxtra/text/pdcmark/tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import pytest
44

55

6-
# Resolve a directory from a test-relative path. The pulldown-cmark submodule lives at the repo
7-
# root; tests reference fixture files there.
6+
# Resolve a directory from a test-relative path. The pulldown-cmark submodule lives at the repo root; tests reference
7+
# fixture files there.
88
REPO_ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..'))
99
PDCMARK_SUBMODULE = os.path.join(REPO_ROOT, 'pulldown-cmark', 'pulldown-cmark')
1010

omxtra/text/pdcmark/tests/spec_runner.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99
<expected html>
1010
````````````````````````````````
1111
12-
The fence is a long run of backticks (>= 3, in practice 32). The opening fence is followed by a
13-
keyword — `example` for tests that run, `DISABLED example` (or `... DISABLED`) for tests skipped.
14-
Inside, a `.` on its own line separates the markdown source from the expected HTML. Section titles
15-
(setext-style `=== ` underlines or `## …` headings) are picked up as group names for nicer pytest
16-
IDs.
17-
18-
Tabs in spec fixtures are written as the literal arrow character `→` (U+2192) and substituted back
19-
to `\\t` at load time, matching how the CommonMark spec source is authored.
12+
The fence is a long run of backticks (>= 3, in practice 32). The opening fence is followed by a keyword — `example` for
13+
tests that run, `DISABLED example` (or `... DISABLED`) for tests skipped. Inside, a `.` on its own line separates the
14+
markdown source from the expected HTML. Section titles (setext-style `=== ` underlines or `## …` headings) are picked up
15+
as group names for nicer pytest IDs.
16+
17+
Tabs in spec fixtures are written as the literal arrow character `→` (U+2192) and substituted back to `\\t` at load
18+
time, matching how the CommonMark spec source is authored.
2019
"""
2120
import io
2221
import os.path
@@ -29,8 +28,8 @@
2928
##
3029

3130

32-
# Opening fence: at least 3 backticks followed by anything before EOL. We match `example` and
33-
# `DISABLED example` (in either order, case-insensitive).
31+
# Opening fence: at least 3 backticks followed by anything before EOL. We match `example` and `DISABLED example` (in
32+
# either order, case-insensitive).
3433
_FENCE_RE = re.compile(r'^(`{3,})\s*(.*)$')
3534

3635
_SEPARATOR_LINE = '.'

omxtra/text/pdcmark/tests/test_blocks/test_containers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,19 @@ def test_simple_bullet_list():
8282
tags = [s.tag.__class__.__name__ for s in starts]
8383
assert tags == ['List', 'Item', 'Paragraph', 'Item', 'Paragraph']
8484
list_start = next(e for e in out if isinstance(e, m.Start) and isinstance(e.tag, m.List))
85-
assert list_start.tag.start is None # unordered
85+
assert list_start.tag.start is None # type: ignore # unordered
8686

8787

8888
def test_ordered_list():
8989
out = feed('1. foo\n2. bar\n')
9090
list_start = next(e for e in out if isinstance(e, m.Start) and isinstance(e.tag, m.List))
91-
assert list_start.tag.start == 1
91+
assert list_start.tag.start == 1 # type: ignore
9292

9393

9494
def test_ordered_list_arbitrary_start():
9595
out = feed('42. foo\n')
9696
list_start = next(e for e in out if isinstance(e, m.Start) and isinstance(e.tag, m.List))
97-
assert list_start.tag.start == 42
97+
assert list_start.tag.start == 42 # type: ignore
9898

9999

100100
def test_different_marker_starts_new_list():

omxtra/text/pdcmark/tests/test_blocks/test_leaf_machine.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def test_atx_emits_heading():
4444
def test_atx_levels():
4545
for level in range(1, 7):
4646
out = feed('#' * level + ' h\n')
47-
assert isinstance(out[0], m.Start) and out[0].tag.level == level
47+
assert isinstance(out[0], m.Start) and out[0].tag.level == level # type: ignore
4848

4949

5050
def test_paragraph_emits_paragraph():
@@ -78,9 +78,9 @@ def test_hrule_alternatives():
7878

7979
def test_setext_h1_and_h2():
8080
out = feed('Title\n===\n')
81-
assert isinstance(out[0], m.Start) and out[0].tag.level == 1
81+
assert isinstance(out[0], m.Start) and out[0].tag.level == 1 # type: ignore
8282
out = feed('Title\n---\n')
83-
assert isinstance(out[0], m.Start) and out[0].tag.level == 2
83+
assert isinstance(out[0], m.Start) and out[0].tag.level == 2 # type: ignore
8484

8585

8686
def test_setext_only_applies_to_open_paragraph():
@@ -156,8 +156,8 @@ def test_atx_interrupts_paragraph():
156156

157157
def test_hrule_interrupts_paragraph():
158158
out = feed('para\n---\n')
159-
# Tricky case: `para\n---\n` is actually a setext-H2, NOT paragraph + hrule, per CM spec.
160-
# Setext promotion wins because the underline is checked before paragraph-interrupters.
159+
# Tricky case: `para\n---\n` is actually a setext-H2, NOT paragraph + hrule, per CM spec. Setext promotion wins
160+
# because the underline is checked before paragraph-interrupters.
161161
assert isinstance(out[0].tag, m.Heading) and out[0].tag.level == 2
162162

163163

omxtra/text/pdcmark/tests/test_blocks/test_refdefs.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ def test_refdef_then_blank_then_use():
114114
tags = [type(s.tag).__name__ for s in starts]
115115
assert tags == ['Paragraph', 'Link']
116116
link_start = starts[1]
117-
assert link_start.tag.dest_url == '/url'
118-
assert link_start.tag.link_type == m.LinkType.SHORTCUT
117+
assert link_start.tag.dest_url == '/url' # type: ignore
118+
assert link_start.tag.link_type == m.LinkType.SHORTCUT # type: ignore
119119
assert 'foo' in refdefs
120120

121121

@@ -125,17 +125,17 @@ def test_first_definition_wins():
125125

126126

127127
def test_setext_paragraph_is_not_refdef():
128-
# A paragraph that becomes a setext heading should NOT have refdefs peeled —
129-
# `[foo]: /url\n===` is a level-1 heading with content `[foo]: /url`.
128+
# A paragraph that becomes a setext heading should NOT have refdefs peeled — `[foo]: /url\n===` is a level-1 heading
129+
# with content `[foo]: /url`.
130130
events, refdefs = _feed('[foo]: /url\n===\n')
131131
assert any(isinstance(e, m.Start) and isinstance(e.tag, m.Heading) for e in events)
132132
assert 'foo' not in refdefs
133133

134134

135135
def test_non_refdef_first_line_stops_consumption():
136136
events, refdefs = _feed('not a refdef\n[foo]: /url\n')
137-
# The whole thing is ONE paragraph (the second line is paragraph continuation, not a fresh
138-
# block). Refdef is NOT peeled because it's mid-paragraph.
137+
# The whole thing is ONE paragraph (the second line is paragraph continuation, not a fresh block). Refdef is NOT
138+
# peeled because it's mid-paragraph.
139139
paragraphs = [e for e in events if isinstance(e, m.Start) and isinstance(e.tag, m.Paragraph)]
140140
assert len(paragraphs) == 1
141141
assert 'foo' not in refdefs

0 commit comments

Comments
 (0)