Skip to content

Commit c277826

Browse files
jerenslPotatoCP
authored andcommitted
script_bindings(python): Add ruff rule for strict typing in function (servo#38694)
This is part of introducing the Ruff rule [flake8-annotations (ANN)](https://docs.astral.sh/ruff/rules/#flake8-annotations-ann) into the script_bindings folder. Since `codegen.py` has 10k lines of code, the strategy is to introduce the rule first while ignoring the `ANN` Ruff rule for `codegen.py`. We will then gradually add type annotations slice by slice to ensure no new errors occur outside of `ANN`. Testing: `./mach test-wpt webidl` --------- Signed-off-by: Jerens Lensun <[email protected]>
1 parent 73bd5ec commit c277826

File tree

4 files changed

+73
-54
lines changed

4 files changed

+73
-54
lines changed

components/script_bindings/codegen/codegen.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44

55
# Common codegen classes.
66

7+
# fmt: off
8+
79
from WebIDL import IDLUnionType
810
from WebIDL import IDLSequenceType
911
from collections import defaultdict
1012
from itertools import groupby
11-
from typing import Generator, Optional, cast
13+
from typing import Optional
14+
from collections.abc import Generator
1215
from abc import abstractmethod
1316

1417
import operator
@@ -2679,6 +2682,7 @@ def getAllTypes(
26792682
for d in descriptors:
26802683
for t in getTypesFromDescriptor(d):
26812684
if t.isRecord():
2685+
# pyrefly: ignore # missing-attribute
26822686
yield (t.inner, d)
26832687
yield (t, d)
26842688
for dictionary in dictionaries:
@@ -4722,7 +4726,7 @@ def define(self):
47224726
assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached")
47234727
isLazilyCachedInSlot = not isAlwaysInSlot
47244728
# pyrefly: ignore # unknown-name
4725-
slotIndex = memberReservedSlot(self.member) # noqa:FIXME: memberReservedSlot is not defined
4729+
slotIndex = memberReservedSlot(self.member) # noqa: F821 FIXME: memberReservedSlot is not defined
47264730
# We'll statically assert that this is not too big in
47274731
# CGUpdateMemberSlotsMethod, in the case when
47284732
# isAlwaysInSlot is true.

components/script_bindings/codegen/configuration.py

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
# License, v. 2.0. If a copy of the MPL was not distributed with this
33
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5+
# fmt: off
6+
7+
from __future__ import annotations
8+
59
import functools
610
import os
711
from typing import Any
@@ -30,7 +34,7 @@ class Configuration:
3034
enumConfig: dict[str, Any]
3135
dictConfig: dict[str, Any]
3236
unionConfig: dict[str, Any]
33-
descriptors: list["Descriptor"]
37+
descriptors: list[Descriptor]
3438
interfaces: dict[str, IDLInterface]
3539

3640
def __init__(self, filename: str, parseData: list[IDLInterface]) -> None:
@@ -86,46 +90,46 @@ def __init__(self, filename: str, parseData: list[IDLInterface]) -> None:
8690
c.isCallback() and not c.isInterface()]
8791

8892
# Keep the descriptor list sorted for determinism.
89-
def cmp(x, y):
93+
def cmp(x: str, y: str) -> int:
9094
return (x > y) - (x < y)
9195
self.descriptors.sort(key=functools.cmp_to_key(lambda x, y: cmp(x.name, y.name)))
9296

9397
def getInterface(self, ifname: str) -> IDLInterface:
9498
return self.interfaces[ifname]
9599

96-
def getDescriptors(self, **filters) -> list["Descriptor"]:
100+
def getDescriptors(self, **filters: IDLInterface) -> list[Descriptor]:
97101
"""Gets the descriptors that match the given filters."""
98102
curr = self.descriptors
99103
for key, val in filters.items():
100104
if key == 'webIDLFile':
101-
def getter(x: Descriptor):
105+
def getter(x: Descriptor) -> str:
102106
return x.interface.location.filename
103107
elif key == 'hasInterfaceObject':
104-
def getter(x):
108+
def getter(x: Descriptor) -> bool:
105109
return x.interface.hasInterfaceObject()
106110
elif key == 'isCallback':
107-
def getter(x):
111+
def getter(x: Descriptor) -> bool:
108112
return x.interface.isCallback()
109113
elif key == 'isNamespace':
110-
def getter(x):
114+
def getter(x: Descriptor) -> bool:
111115
return x.interface.isNamespace()
112116
elif key == 'isJSImplemented':
113-
def getter(x):
117+
def getter(x: Descriptor) -> bool:
114118
return x.interface.isJSImplemented()
115119
elif key == 'isGlobal':
116-
def getter(x):
120+
def getter(x: Descriptor) -> bool:
117121
return x.isGlobal()
118122
elif key == 'isInline':
119-
def getter(x) -> bool:
123+
def getter(x: Descriptor) -> bool:
120124
return x.interface.getExtendedAttribute('Inline') is not None
121125
elif key == 'isExposedConditionally':
122-
def getter(x):
126+
def getter(x: Descriptor) -> bool:
123127
return x.interface.isExposedConditionally()
124128
elif key == 'isIteratorInterface':
125-
def getter(x):
129+
def getter(x: Descriptor) -> bool:
126130
return x.interface.isIteratorInterface()
127131
else:
128-
def getter(x):
132+
def getter(x: Descriptor) -> Any:
129133
return getattr(x, key)
130134
curr = [x for x in curr if getter(x) == val]
131135
return curr
@@ -159,7 +163,7 @@ def getDictConfig(self, name: str) -> dict[str, Any]:
159163
def getCallbacks(self, webIDLFile: str = "") -> list[IDLInterface]:
160164
return self._filterForFile(self.callbacks, webIDLFile=webIDLFile)
161165

162-
def getDescriptor(self, interfaceName: str) -> "Descriptor":
166+
def getDescriptor(self, interfaceName: str) -> Descriptor:
163167
"""
164168
Gets the appropriate descriptor for the given interface name.
165169
"""
@@ -172,34 +176,34 @@ def getDescriptor(self, interfaceName: str) -> "Descriptor":
172176
+ str(len(descriptors)) + " matches")
173177
return descriptors[0]
174178

175-
def getDescriptorProvider(self) -> "DescriptorProvider":
179+
def getDescriptorProvider(self) -> DescriptorProvider:
176180
"""
177181
Gets a descriptor provider that can provide descriptors as needed.
178182
"""
179183
return DescriptorProvider(self)
180184

181185

182186
class NoSuchDescriptorError(TypeError):
183-
def __init__(self, str) -> None:
187+
def __init__(self, str: str) -> None:
184188
TypeError.__init__(self, str)
185189

186190

187191
class DescriptorProvider:
188192
"""
189193
A way of getting descriptors for interface names
190194
"""
191-
def __init__(self, config) -> None:
195+
def __init__(self, config: Configuration) -> None:
192196
self.config = config
193197

194-
def getDescriptor(self, interfaceName: str) -> "Descriptor":
198+
def getDescriptor(self, interfaceName: str) -> Descriptor:
195199
"""
196200
Gets the appropriate descriptor for the given interface name given the
197201
context of the current descriptor.
198202
"""
199203
return self.config.getDescriptor(interfaceName)
200204

201205

202-
def MemberIsLegacyUnforgeable(member: IDLAttribute | IDLMethod, descriptor: "Descriptor") -> bool:
206+
def MemberIsLegacyUnforgeable(member: IDLAttribute | IDLMethod, descriptor: Descriptor) -> bool:
203207
return ((member.isAttr() or member.isMethod())
204208
and not member.isStatic()
205209
and (member.isLegacyUnforgeable()
@@ -213,7 +217,7 @@ class Descriptor(DescriptorProvider):
213217
interface: IDLInterface
214218
uniqueImplementation: bool
215219

216-
def __init__(self, config, interface: IDLInterface, desc: dict[str, Any]) -> None:
220+
def __init__(self, config: Configuration, interface: IDLInterface, desc: dict[str, Any]) -> None:
217221
DescriptorProvider.__init__(self, config)
218222
self.interface = interface
219223

@@ -289,7 +293,7 @@ def __init__(self, config, interface: IDLInterface, desc: dict[str, Any]) -> Non
289293
and any(MemberIsLegacyUnforgeable(m, self) for m in
290294
self.interface.members))
291295

292-
self.operations = {
296+
self.operations: dict[str, IDLMethod | None] = {
293297
'IndexedGetter': None,
294298
'IndexedSetter': None,
295299
'IndexedDeleter': None,
@@ -301,7 +305,7 @@ def __init__(self, config, interface: IDLInterface, desc: dict[str, Any]) -> Non
301305

302306
self.hasDefaultToJSON = False
303307

304-
def addOperation(operation: str, m) -> None:
308+
def addOperation(operation: str, m: IDLMethod) -> None:
305309
if not self.operations[operation]:
306310
self.operations[operation] = m
307311

@@ -320,7 +324,7 @@ def addOperation(operation: str, m) -> None:
320324
if not m.isMethod():
321325
continue
322326

323-
def addIndexedOrNamedOperation(operation: str, m) -> None:
327+
def addIndexedOrNamedOperation(operation: str, m: IDLMethod) -> None:
324328
if not self.isGlobal():
325329
self.proxy = True
326330
if m.isIndexed():
@@ -357,8 +361,8 @@ def addIndexedOrNamedOperation(operation: str, m) -> None:
357361
# array of extended attributes.
358362
self.extendedAttributes = {'all': {}, 'getterOnly': {}, 'setterOnly': {}}
359363

360-
def addExtendedAttribute(attribute, config) -> None:
361-
def add(key: str, members, attribute) -> None:
364+
def addExtendedAttribute(attribute: dict[str, Any], config: Configuration) -> None:
365+
def add(key: str, members: list[str], attribute: dict[str, Any]) -> None:
362366
for member in members:
363367
self.extendedAttributes[key].setdefault(member, []).append(attribute)
364368

@@ -405,7 +409,7 @@ def add(key: str, members, attribute) -> None:
405409
config.maxProtoChainLength = max(config.maxProtoChainLength,
406410
len(self.prototypeChain))
407411

408-
def maybeGetSuperModule(self):
412+
def maybeGetSuperModule(self) -> str | None:
409413
"""
410414
Returns name of super module if self is part of it
411415
"""
@@ -416,10 +420,10 @@ def maybeGetSuperModule(self):
416420
return filename
417421
return None
418422

419-
def binaryNameFor(self, name: str, isStatic: bool):
423+
def binaryNameFor(self, name: str, isStatic: bool) -> str:
420424
return self._binaryNames.get((name, isStatic), name)
421425

422-
def internalNameFor(self, name: str):
426+
def internalNameFor(self, name: str) -> str:
423427
return self._internalNames.get(name, name)
424428

425429
def hasNamedPropertiesObject(self) -> bool:
@@ -431,8 +435,8 @@ def hasNamedPropertiesObject(self) -> bool:
431435
def supportsNamedProperties(self) -> bool:
432436
return self.operations['NamedGetter'] is not None
433437

434-
def getExtendedAttributes(self, member, getter=False, setter=False):
435-
def maybeAppendInfallibleToAttrs(attrs, throws) -> None:
438+
def getExtendedAttributes(self, member: IDLMethod, getter: bool = False, setter: bool = False) -> list[str]:
439+
def maybeAppendInfallibleToAttrs(attrs: list[str], throws: bool | None) -> None:
436440
if throws is None:
437441
attrs.append("infallible")
438442
elif throws is True:
@@ -458,7 +462,7 @@ def maybeAppendInfallibleToAttrs(attrs, throws) -> None:
458462
maybeAppendInfallibleToAttrs(attrs, throws)
459463
return attrs
460464

461-
def getParentName(self):
465+
def getParentName(self) -> str | None:
462466
parent = self.interface.parent
463467
while parent:
464468
if not parent.getExtendedAttribute("Inline"):
@@ -469,30 +473,30 @@ def getParentName(self):
469473
def supportsIndexedProperties(self) -> bool:
470474
return self.operations['IndexedGetter'] is not None
471475

472-
def isMaybeCrossOriginObject(self):
476+
def isMaybeCrossOriginObject(self) -> bool:
473477
# If we're isGlobal and have cross-origin members, we're a Window, and
474478
# that's not a cross-origin object. The WindowProxy is.
475479
return self.concrete and self.interface.hasCrossOriginMembers and not self.isGlobal()
476480

477-
def hasDescendants(self):
481+
def hasDescendants(self) -> bool:
478482
return (self.interface.getUserData("hasConcreteDescendant", False)
479-
or self.interface.getUserData("hasProxyDescendant", False))
483+
or self.interface.getUserData("hasProxyDescendant", False) or False)
480484

481-
def hasHTMLConstructor(self):
485+
def hasHTMLConstructor(self) -> bool:
482486
ctor = self.interface.ctor()
483-
return ctor and ctor.isHTMLConstructor()
487+
return (ctor and ctor.isHTMLConstructor()) or False
484488

485-
def shouldHaveGetConstructorObjectMethod(self):
489+
def shouldHaveGetConstructorObjectMethod(self) -> bool:
486490
assert self.interface.hasInterfaceObject()
487491
if self.interface.getExtendedAttribute("Inline"):
488492
return False
489493
return (self.interface.isCallback() or self.interface.isNamespace()
490-
or self.hasDescendants() or self.hasHTMLConstructor())
494+
or self.hasDescendants() or self.hasHTMLConstructor() or False)
491495

492-
def shouldCacheConstructor(self):
496+
def shouldCacheConstructor(self) -> bool:
493497
return self.hasDescendants() or self.hasHTMLConstructor()
494498

495-
def isExposedConditionally(self):
499+
def isExposedConditionally(self) -> bool:
496500
return self.interface.isExposedConditionally()
497501

498502
def isGlobal(self) -> bool:
@@ -507,19 +511,19 @@ def isGlobal(self) -> bool:
507511
# Some utility methods
508512

509513

510-
def MakeNativeName(name):
514+
def MakeNativeName(name: str) -> str:
511515
return name[0].upper() + name[1:]
512516

513517

514-
def getIdlFileName(object: IDLObject):
518+
def getIdlFileName(object: IDLObject) -> str:
515519
return os.path.basename(object.location.filename).split('.webidl')[0]
516520

517521

518-
def getModuleFromObject(object: IDLObject):
522+
def getModuleFromObject(object: IDLObject) -> str:
519523
return ('crate::codegen::GenericBindings::' + getIdlFileName(object) + 'Binding')
520524

521525

522-
def getTypesFromDescriptor(descriptor: Descriptor):
526+
def getTypesFromDescriptor(descriptor: Descriptor) -> list[IDLType]:
523527
"""
524528
Get all argument and return types for all members of the descriptor
525529
"""
@@ -570,7 +574,7 @@ def getUnwrappedType(type: IDLType) -> IDLType:
570574
return type
571575

572576

573-
def iteratorNativeType(descriptor: Descriptor, infer: bool = False):
577+
def iteratorNativeType(descriptor: Descriptor, infer: bool = False) -> str:
574578
assert descriptor.interface.maplikeOrSetlikeOrIterable is not None
575579
iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
576580
assert (iterableDecl.isIterable() and iterableDecl.isPairIterator()) \

components/script_bindings/codegen/run.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,25 @@
22
# License, v. 2.0. If a copy of the MPL was not distributed with this
33
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5+
# fmt: off
6+
7+
from __future__ import annotations
8+
59
import os
610
import sys
711
import json
812
import re
13+
from typing import TYPE_CHECKING
14+
from collections.abc import Iterator
915

1016
SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__))
1117
SERVO_ROOT = os.path.abspath(os.path.join(SCRIPT_PATH, "..", "..", ".."))
1218

1319
FILTER_PATTERN = re.compile("// skip-unless ([A-Z_]+)\n")
1420

21+
if TYPE_CHECKING:
22+
from configuration import Configuration
23+
from WebIDL import Parser
1524

1625
def main() -> None:
1726
os.chdir(os.path.join(os.path.dirname(__file__)))
@@ -84,21 +93,21 @@ def main() -> None:
8493
f.write(module.encode("utf-8"))
8594

8695

87-
def make_dir(path: str):
96+
def make_dir(path: str)-> str:
8897
if not os.path.exists(path):
8998
os.makedirs(path)
9099
return path
91100

92101

93-
def generate(config, name: str, filename: str) -> None:
102+
def generate(config: Configuration, name: str, filename: str) -> None:
94103
from codegen import GlobalGenRoots
95104
root = getattr(GlobalGenRoots, name)(config)
96105
code = root.define()
97106
with open(filename, "wb") as f:
98107
f.write(code.encode("utf-8"))
99108

100109

101-
def add_css_properties_attributes(css_properties_json: str, parser) -> None:
110+
def add_css_properties_attributes(css_properties_json: str, parser: Parser) -> None:
102111
def map_preference_name(preference_name: str) -> str:
103112
"""Map between Stylo preference names and Servo preference names as the
104113
`css-properties.json` file is generated by Stylo. This should be kept in sync with the
@@ -132,7 +141,7 @@ def map_preference_name(preference_name: str) -> str:
132141
parser.parse(idl, "CSSStyleDeclaration_generated.webidl")
133142

134143

135-
def attribute_names(property_name: str):
144+
def attribute_names(property_name: str) -> Iterator[str]:
136145
# https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-dashed-attribute
137146
if property_name != "float":
138147
yield property_name
@@ -149,7 +158,7 @@ def attribute_names(property_name: str):
149158

150159

151160
# https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
152-
def camel_case(chars: str, webkit_prefixed: bool = False):
161+
def camel_case(chars: str, webkit_prefixed: bool = False) -> Iterator[str]:
153162
if webkit_prefixed:
154163
chars = chars[1:]
155164
next_is_uppercase = False

0 commit comments

Comments
 (0)