Skip to content

Commit 3d30463

Browse files
committed
WIP turning on some more mypy options and cleaning up the resultant mess
1 parent cf49c87 commit 3d30463

27 files changed

+162
-154
lines changed

bikeshed/caniuse/caniuse.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def addCanIUsePanels(doc: t.SpecT) -> list[t.ElementT]:
6060
return panels
6161

6262

63-
def canIUsePanelFor(id: str, data: t.JSONT, update: str, classFromBrowser: dict[str, str]) -> t.ElementT:
63+
def canIUsePanelFor(id: str, data: t.JSONObject, update: str, classFromBrowser: dict[str, str]) -> t.ElementT:
6464
panel = h.E.details(
6565
{"class": "caniuse-status unpositioned", "data-deco": ""},
6666
h.E.summary({}, "CanIUse"),
@@ -157,19 +157,19 @@ def __init__(self, dataFile: t.DataFileRequester) -> None:
157157
self.updated = data["updated"]
158158
self.agents = data["agents"]
159159
self.urlFromFeature = data["features"]
160-
self.features: t.JSONT = {}
160+
self.features: t.JSONObject = {}
161161

162162
def hasFeature(self, featureName: str) -> bool:
163163
return featureName in self.urlFromFeature
164164

165-
def getFeature(self, featureName: str) -> t.JSONT:
165+
def getFeature(self, featureName: str) -> t.JSONObject:
166166
if featureName in self.features:
167-
return t.cast("t.JSONT", self.features[featureName])
167+
return t.cast("t.JSONObject", self.features[featureName])
168168
if not self.hasFeature(featureName):
169169
return {}
170170
data = json.loads(
171171
self.dataFile.fetch("caniuse", f"feature-{featureName}.json", str=True),
172172
object_pairs_hook=OrderedDict,
173173
)
174174
self.features[featureName] = data
175-
return t.cast("t.JSONT", data)
175+
return t.cast("t.JSONObject", data)

bikeshed/cddl.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import dataclasses
34
from typing import get_args
45

56
import cddlparser
@@ -8,6 +9,13 @@
89
from . import messages as m
910

1011

12+
@dataclasses.dataclass
13+
class CDDLTerm:
14+
type: str
15+
name: str
16+
dfnFor: str | None
17+
18+
1119
class CDDLMarker(cddlparser.ast.Marker):
1220
"""
1321
Marker that wraps CDDL definitions and references in <cddl> and <a> blocks
@@ -20,7 +28,7 @@ class CDDLMarker(cddlparser.ast.Marker):
2028
currentParameters: list[str]
2129

2230
# List of all CDDL terms defined so far to track and report duplicates
23-
defined: list
31+
defined: list[CDDLTerm]
2432

2533
def __init__(self) -> None:
2634
self.currentRule = None
@@ -29,21 +37,21 @@ def __init__(self) -> None:
2937

3038
def _recordDefinition(self, type: str, name: str, dfnFor: str | None = None) -> bool:
3139
for term in self.defined:
32-
if term["type"] == type and term["name"] == name and term["dfnFor"] == dfnFor:
40+
if term.type == type and term.name == name and term.dfnFor == dfnFor:
3341
forText = "" if dfnFor is None else f' defined in type "{dfnFor}"'
3442
m.die(
3543
f"CDDL {type} {name}{forText} creates a duplicate and cannot be referenced.\nPlease create additional CDDL types to disambiguate.",
3644
)
3745
return False
3846
if type != "parameter":
3947
for term in self.defined:
40-
if term["type"] != "parameter" and term["name"] == name and term["dfnFor"] == dfnFor:
48+
if term.type != "parameter" and term.name == name and term.dfnFor == dfnFor:
4149
forText = "" if dfnFor is None else f' defined in type "{dfnFor}"'
4250
m.warn(
43-
f"CDDL {type} {name}{forText} creates a duplicate with a CDDL {term['type']}.\nLink type needs to be specified to reference the term.\nConsider creating additional CDDL types to disambiguate.",
51+
f"CDDL {type} {name}{forText} creates a duplicate with a CDDL {term.type}.\nLink type needs to be specified to reference the term.\nConsider creating additional CDDL types to disambiguate.",
4452
)
4553
break
46-
term = {"type": type, "name": name, "dfnFor": dfnFor}
54+
term = CDDLTerm(type=type, name=name, dfnFor=dfnFor)
4755
self.defined.append(term)
4856
return True
4957

bikeshed/config/BoolSet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from .. import t
66

77

8-
class BoolSet(collections.abc.MutableMapping):
8+
class BoolSet(collections.abc.MutableMapping[str, bool]):
99
"""
1010
Implements a "boolean set",
1111
where keys can be explicitly set to True or False,

bikeshed/config/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
docPath,
3030
doEvery,
3131
englishFromList,
32-
flatten,
3332
groupFromKey,
3433
intersperse,
3534
processTextNodes,

bikeshed/config/dfnTypes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def linkTypeIn(startTypes: str | t.AbstractSet[str], targetTypes: str | t.Abstra
221221

222222
# Some of the more significant types and their patterns
223223
trivialPattern = re.compile(r".+")
224-
typeRe: dict[str, re.Pattern]
224+
typeRe: dict[str, re.Pattern[str]]
225225
typeRe = defaultdict(lambda: trivialPattern)
226226
typeRe["property"] = re.compile(r"^[\w-]+$")
227227
typeRe["at-rule"] = re.compile(r"^@[\w-]+$")

bikeshed/config/main.py

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,10 @@ def englishFromList(items: t.Iterable[str], conjunction: str = "or") -> str:
2121
return "{0}, {2} {1}".format(", ".join(items[:-1]), items[-1], conjunction)
2222

2323

24-
if t.TYPE_CHECKING:
25-
IntersperseU = t.TypeVar("IntersperseU")
26-
IntersperseV = t.TypeVar("IntersperseV")
27-
28-
29-
def intersperse(
30-
iterable: t.Iterable[IntersperseU],
31-
delimiter: IntersperseV,
32-
) -> t.Generator[IntersperseU | IntersperseV, None, None]:
24+
def intersperse[IterT, DelimT](
25+
iterable: t.Iterable[IterT],
26+
delimiter: DelimT,
27+
) -> t.Iterator[IterT | DelimT]:
3328
first = True
3429
for x in iterable:
3530
if not first:
@@ -38,13 +33,13 @@ def intersperse(
3833
yield x
3934

4035

41-
if t.TYPE_CHECKING:
42-
ProcessTextNodesU = t.TypeVar("ProcessTextNodesU")
43-
44-
45-
def processTextNodes(nodes: list[t.NodeT], regex: re.Pattern, replacer: t.Callable) -> list[t.NodeT]:
36+
def processTextNodes(
37+
nodes: t.Iterable[t.NodeT],
38+
regex: t.Pattern,
39+
replacer: t.Callable[[t.Match], t.NodeT],
40+
) -> list[t.NodeT]:
4641
"""
47-
Takes an array of text/objects,
42+
Takes an array of nodes,
4843
and flatmaps reSubObject over the text parts.
4944
"""
5045
ret: list[t.NodeT] = []
@@ -56,35 +51,35 @@ def processTextNodes(nodes: list[t.NodeT], regex: re.Pattern, replacer: t.Callab
5651
return ret
5752

5853

59-
if t.TYPE_CHECKING:
60-
ReSubObjectU = t.TypeVar("ReSubObjectU")
61-
62-
6354
@t.overload
64-
def reSubObject(pattern: re.Pattern, string: str, repl: None) -> list[str | re.Match]: ...
55+
def reSubObject(
56+
pattern: t.Pattern,
57+
string: str,
58+
repl: None = None,
59+
) -> list[str | t.Match]: ...
6560

6661

6762
@t.overload
68-
def reSubObject(
69-
pattern: re.Pattern,
63+
def reSubObject[SubT](
64+
pattern: t.Pattern,
7065
string: str,
71-
repl: t.Callable[[re.Match], ReSubObjectU],
72-
) -> list[str | ReSubObjectU]: ...
66+
repl: t.Callable[[t.Match], SubT],
67+
) -> list[str | SubT]: ...
7368

7469

75-
def reSubObject(
76-
pattern: re.Pattern,
70+
def reSubObject[SubT](
71+
pattern: t.Pattern,
7772
string: str,
78-
repl: t.Callable[[re.Match], ReSubObjectU] | None = None,
79-
) -> list[str | re.Match] | list[str | ReSubObjectU] | list[str | re.Match | ReSubObjectU]:
73+
repl: t.Callable[[t.Match], SubT] | None = None,
74+
) -> list[str | t.Match] | list[str | SubT] | list[str | t.Match | SubT]:
8075
"""
8176
like re.sub, but replacements don't have to be text;
8277
returns an array of alternating unmatched text and match objects instead.
8378
If repl is specified, it's called with each match object,
8479
and the result then shows up in the array instead.
8580
"""
8681
lastEnd = 0
87-
pieces: list[str | re.Match | ReSubObjectU] = []
82+
pieces: list[str | t.Match | SubT] = []
8883
for match in pattern.finditer(string):
8984
pieces.append(string[lastEnd : match.start()])
9085
if repl:
@@ -150,14 +145,6 @@ def groupFromKey(key: str, length: int = 2) -> str:
150145
_groupFromKeyCache = {}
151146

152147

153-
def flatten(arr: t.Iterable) -> t.Generator:
154-
for el in arr:
155-
if isinstance(el, collections.abc.Iterable) and not isinstance(el, str) and not lxml.etree.iselement(el):
156-
yield from flatten(el)
157-
else:
158-
yield el
159-
160-
161148
def scriptPath(*pathSegs: str) -> str:
162149
startPath = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
163150
path = os.path.join(startPath, *pathSegs)
@@ -183,7 +170,7 @@ def chrootPath(rootPath: str, path: str) -> str:
183170
return path
184171

185172

186-
def doEvery(s: float, action: t.Callable, lastTime: float | None = None) -> float:
173+
def doEvery(s: float, action: t.Callable[[], t.Any], lastTime: float | None = None) -> float:
187174
# Takes an action every N seconds.
188175
# Pass it the duration and the last time it took the action;
189176
# it returns the time it last took the action
@@ -204,7 +191,19 @@ def doEvery(s: float, action: t.Callable, lastTime: float | None = None) -> floa
204191
SafeIndexDefaultT = t.TypeVar("SafeIndexDefaultT")
205192

206193

207-
def safeIndex(coll: t.Sequence, needle: t.Any, default: SafeIndexDefaultT = None) -> int | SafeIndexDefaultT: # type: ignore[assignment]
194+
@t.overload
195+
def safeIndex[ValT](coll: t.Sequence[ValT], needle: ValT) -> int | None: ...
196+
197+
198+
@t.overload
199+
def safeIndex[ValT, DefaultT](coll: t.Sequence[ValT], needle: ValT, default: DefaultT) -> int | DefaultT: ...
200+
201+
202+
def safeIndex[ValT, DefaultT](
203+
coll: t.Sequence[ValT],
204+
needle: ValT,
205+
default: DefaultT | None = None,
206+
) -> int | DefaultT | None:
208207
try:
209208
return coll.index(needle)
210209
except ValueError:

bikeshed/datablocks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ def extendData(datas: InfoTreeT, infoLevels: InfoTreeT) -> None:
893893
# HTML comment filling the whole line,
894894
# go ahead and strip it
895895
continue
896-
ws, text = t.cast("re.Match", re.match(r"(\s*)(.*)", line)).groups()
896+
ws, text = t.cast("t.Match", re.match(r"(\s*)(.*)", line)).groups()
897897
if text.startswith("#"): # comment
898898
continue
899899
wsLen = len(ws.replace("\t", indentSpace))

bikeshed/h/dom.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
from .parser import StartTag
2020

2121

22-
def flatten(arr: t.Iterable) -> t.Iterator:
22+
def flatten(arr: t.NodesT | tuple[t.NodesT, ...]) -> t.Iterator[t.NodeT]:
2323
for el in arr:
24-
if isinstance(el, collections.abc.Iterable) and not isinstance(el, str) and not etree.iselement(el):
24+
if isinstance(el, collections.abc.Iterable) and not isinstance(el, str) and not lxml.etree.iselement(el):
2525
yield from flatten(el)
2626
else:
2727
yield el
@@ -535,7 +535,7 @@ def replaceContents(el: t.ElementT, newElements: t.NodesT | t.Iterable[t.NodesT]
535535

536536

537537
def replaceWithContents(el: t.ElementT) -> t.NodesT | None:
538-
return replaceNode(el, childNodes(el, clear=True))
538+
return replaceNode(el, t.cast("t.NodesT", childNodes(el, clear=True)))
539539

540540

541541
def moveContents(toEl: t.ElementT, fromEl: t.ElementT) -> None:
@@ -1006,8 +1006,8 @@ def replaceMacrosTextly(text: str, macros: t.Mapping[str, str], context: str) ->
10061006
# Same as replaceMacros(), but does the substitution
10071007
# directly on the text, rather than relying on the
10081008
# html parser to have preparsed the macro syntax
1009-
def macroReplacer(match: re.Match) -> str:
1010-
fullText = t.cast(str, match.group(0))
1009+
def macroReplacer(match: t.Match) -> str:
1010+
fullText = match.group(0)
10111011
innerText = match.group(2).lower() or ""
10121012
optional = match.group(3) == "?"
10131013
if fullText.startswith("\\"):

bikeshed/h/merge.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class TagStr(Tag):
3838
TagStream: t.TypeAlias = t.Generator[Tag, None, None]
3939

4040

41-
def mergeTrees(tree1: t.ElementT, tree2: t.ElementT) -> list:
41+
def mergeTrees(tree1: t.ElementT, tree2: t.ElementT) -> list[t.NodeT]:
4242
"""
4343
Attempts to merge two HTML trees
4444
of the same text content.

bikeshed/h/parser/parser.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,7 +2410,7 @@ def parseLinkInfo(
24102410
innerStart: int,
24112411
startSigil: str,
24122412
endSigil: str,
2413-
infoRe: re.Pattern,
2413+
infoRe: t.Pattern,
24142414
) -> ResultT[LinkData]:
24152415
start = innerStart - len(startSigil)
24162416
match, innerEnd, _ = s.searchRe(innerStart, infoRe)
@@ -2518,11 +2518,11 @@ def parseBiblioInner(s: Stream, innerStart: int) -> ResultT[tuple[StartTag, str]
25182518
normative = match[1] == "!"
25192519
lt = match[2]
25202520
modifierSequence = match[3].strip()
2521-
attrs = {
2522-
"data-lt": lt,
2523-
"data-link-type": "biblio",
2524-
"data-biblio-type": "normative" if normative else "informative",
2525-
"bs-autolink-syntax": s.slice(nodeStart, innerEnd) + "]]",
2521+
attrs: t.SafeAttrDict = {
2522+
"data-lt": escapeAttr(lt),
2523+
"data-link-type": t.SafeAttrStr("biblio"),
2524+
"data-biblio-type": t.SafeAttrStr("normative" if normative else "informative"),
2525+
"bs-autolink-syntax": escapeAttr(s.slice(nodeStart, innerEnd) + "]]"),
25262526
}
25272527

25282528
failureStart = StartTag.fromStream(s, nodeStart, innerStart, "span")
@@ -2535,7 +2535,7 @@ def parseBiblioInner(s: Stream, innerStart: int) -> ResultT[tuple[StartTag, str]
25352535
for modifier in re.split(r"\s+", modifierSequence):
25362536
if modifier in ("current", "snapshot"):
25372537
if "data-biblio-status" not in attrs:
2538-
attrs["data-biblio-status"] = modifier
2538+
attrs["data-biblio-status"] = t.SafeAttrStr(modifier)
25392539
else:
25402540
m.die(
25412541
f"Biblio shorthand [{lt} ...] contains multiple current/snapshot keywords. Please use only one.",
@@ -2544,7 +2544,7 @@ def parseBiblioInner(s: Stream, innerStart: int) -> ResultT[tuple[StartTag, str]
25442544
return failureResult
25452545
elif modifier in ("inline", "index", "direct"):
25462546
if "data-biblio-display" not in attrs:
2547-
attrs["data-biblio-display"] = modifier
2547+
attrs["data-biblio-display"] = t.SafeAttrStr(modifier)
25482548
else:
25492549
m.die(
25502550
f"Biblio shorthand [{lt} ...] contains multiple inline/index/direct keywords. Please use only one.",
@@ -2876,15 +2876,15 @@ def replaceMacrosInText(text: str, macros: dict[str, str], s: Stream, start: int
28762876
msc = constants.macroStartChar
28772877
mec = constants.macroEndChar
28782878

2879-
def doRep(match: re.Match) -> str:
2879+
def doRep(match: t.Match) -> str:
28802880
escaped = match[1] == "\\"
28812881
biblio = match[1] == "["
28822882
macroName = match[2].lower()
28832883
optional = match[3] == "?"
28842884
if escaped:
2885-
return msc + "&#91;" + t.cast("str", match[0][2:-1]) + "&#93;" + mec
2885+
return msc + "&#91;" + match[0][2:-1] + "&#93;" + mec
28862886
if biblio:
2887-
return msc + "&#91;&#91;" + t.cast("str", match[0][2:-1]) + "&#93;" + mec
2887+
return msc + "&#91;&#91;" + match[0][2:-1] + "&#93;" + mec
28882888
if macroName in macros:
28892889
return msc + macros[macroName] + mec
28902890
if optional:
@@ -2893,7 +2893,7 @@ def doRep(match: re.Match) -> str:
28932893
f"Found unmatched text macro {match[0]} in {context}. Correct the macro, or escape it by replacing the opening [ with &bs[;.",
28942894
lineNum=s.loc(start),
28952895
)
2896-
return msc + "&#91;" + t.cast("str", match[0][1:-1]) + "&#93;" + mec
2896+
return msc + "&#91;" + match[0][1:-1] + "&#93;" + mec
28972897

28982898
text, count = MACRO_ATTRIBUTE_RE.subn(doRep, text)
28992899
if count > 0:
@@ -3305,5 +3305,5 @@ def htmlifyMarkdownEscapes(val: str) -> str:
33053305
return re.sub(MARKDOWN_ESCAPE_RE, markdownEscapeReplacer, val)
33063306

33073307

3308-
def markdownEscapeReplacer(match: re.Match) -> str:
3308+
def markdownEscapeReplacer(match: t.Match) -> str:
33093309
return f"&#{ord(match[1])};"

0 commit comments

Comments
 (0)