mirror of
https://github.com/servo/servo.git
synced 2025-08-27 08:08:19 +01:00
script_bindings(python): Add ruff rule for strict typing in function (#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 <jerensslensun@gmail.com>
This commit is contained in:
parent
27dededa65
commit
788d6db94d
4 changed files with 73 additions and 54 deletions
|
@ -4,11 +4,14 @@
|
|||
|
||||
# Common codegen classes.
|
||||
|
||||
# fmt: off
|
||||
|
||||
from WebIDL import IDLUnionType
|
||||
from WebIDL import IDLSequenceType
|
||||
from collections import defaultdict
|
||||
from itertools import groupby
|
||||
from typing import Generator, Optional, cast
|
||||
from typing import Optional
|
||||
from collections.abc import Generator
|
||||
from abc import abstractmethod
|
||||
|
||||
import operator
|
||||
|
@ -2679,6 +2682,7 @@ def getAllTypes(
|
|||
for d in descriptors:
|
||||
for t in getTypesFromDescriptor(d):
|
||||
if t.isRecord():
|
||||
# pyrefly: ignore # missing-attribute
|
||||
yield (t.inner, d)
|
||||
yield (t, d)
|
||||
for dictionary in dictionaries:
|
||||
|
@ -4722,7 +4726,7 @@ pub(crate) fn init_{infoName}<D: DomTypes>() {{
|
|||
assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached")
|
||||
isLazilyCachedInSlot = not isAlwaysInSlot
|
||||
# pyrefly: ignore # unknown-name
|
||||
slotIndex = memberReservedSlot(self.member) # noqa:FIXME: memberReservedSlot is not defined
|
||||
slotIndex = memberReservedSlot(self.member) # noqa: F821 FIXME: memberReservedSlot is not defined
|
||||
# We'll statically assert that this is not too big in
|
||||
# CGUpdateMemberSlotsMethod, in the case when
|
||||
# isAlwaysInSlot is true.
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
# fmt: off
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import os
|
||||
from typing import Any
|
||||
|
@ -30,7 +34,7 @@ class Configuration:
|
|||
enumConfig: dict[str, Any]
|
||||
dictConfig: dict[str, Any]
|
||||
unionConfig: dict[str, Any]
|
||||
descriptors: list["Descriptor"]
|
||||
descriptors: list[Descriptor]
|
||||
interfaces: dict[str, IDLInterface]
|
||||
|
||||
def __init__(self, filename: str, parseData: list[IDLInterface]) -> None:
|
||||
|
@ -86,46 +90,46 @@ class Configuration:
|
|||
c.isCallback() and not c.isInterface()]
|
||||
|
||||
# Keep the descriptor list sorted for determinism.
|
||||
def cmp(x, y):
|
||||
def cmp(x: str, y: str) -> int:
|
||||
return (x > y) - (x < y)
|
||||
self.descriptors.sort(key=functools.cmp_to_key(lambda x, y: cmp(x.name, y.name)))
|
||||
|
||||
def getInterface(self, ifname: str) -> IDLInterface:
|
||||
return self.interfaces[ifname]
|
||||
|
||||
def getDescriptors(self, **filters) -> list["Descriptor"]:
|
||||
def getDescriptors(self, **filters: IDLInterface) -> list[Descriptor]:
|
||||
"""Gets the descriptors that match the given filters."""
|
||||
curr = self.descriptors
|
||||
for key, val in filters.items():
|
||||
if key == 'webIDLFile':
|
||||
def getter(x: Descriptor):
|
||||
def getter(x: Descriptor) -> str:
|
||||
return x.interface.location.filename
|
||||
elif key == 'hasInterfaceObject':
|
||||
def getter(x):
|
||||
def getter(x: Descriptor) -> bool:
|
||||
return x.interface.hasInterfaceObject()
|
||||
elif key == 'isCallback':
|
||||
def getter(x):
|
||||
def getter(x: Descriptor) -> bool:
|
||||
return x.interface.isCallback()
|
||||
elif key == 'isNamespace':
|
||||
def getter(x):
|
||||
def getter(x: Descriptor) -> bool:
|
||||
return x.interface.isNamespace()
|
||||
elif key == 'isJSImplemented':
|
||||
def getter(x):
|
||||
def getter(x: Descriptor) -> bool:
|
||||
return x.interface.isJSImplemented()
|
||||
elif key == 'isGlobal':
|
||||
def getter(x):
|
||||
def getter(x: Descriptor) -> bool:
|
||||
return x.isGlobal()
|
||||
elif key == 'isInline':
|
||||
def getter(x) -> bool:
|
||||
def getter(x: Descriptor) -> bool:
|
||||
return x.interface.getExtendedAttribute('Inline') is not None
|
||||
elif key == 'isExposedConditionally':
|
||||
def getter(x):
|
||||
def getter(x: Descriptor) -> bool:
|
||||
return x.interface.isExposedConditionally()
|
||||
elif key == 'isIteratorInterface':
|
||||
def getter(x):
|
||||
def getter(x: Descriptor) -> bool:
|
||||
return x.interface.isIteratorInterface()
|
||||
else:
|
||||
def getter(x):
|
||||
def getter(x: Descriptor) -> Any:
|
||||
return getattr(x, key)
|
||||
curr = [x for x in curr if getter(x) == val]
|
||||
return curr
|
||||
|
@ -159,7 +163,7 @@ class Configuration:
|
|||
def getCallbacks(self, webIDLFile: str = "") -> list[IDLInterface]:
|
||||
return self._filterForFile(self.callbacks, webIDLFile=webIDLFile)
|
||||
|
||||
def getDescriptor(self, interfaceName: str) -> "Descriptor":
|
||||
def getDescriptor(self, interfaceName: str) -> Descriptor:
|
||||
"""
|
||||
Gets the appropriate descriptor for the given interface name.
|
||||
"""
|
||||
|
@ -172,7 +176,7 @@ class Configuration:
|
|||
+ str(len(descriptors)) + " matches")
|
||||
return descriptors[0]
|
||||
|
||||
def getDescriptorProvider(self) -> "DescriptorProvider":
|
||||
def getDescriptorProvider(self) -> DescriptorProvider:
|
||||
"""
|
||||
Gets a descriptor provider that can provide descriptors as needed.
|
||||
"""
|
||||
|
@ -180,7 +184,7 @@ class Configuration:
|
|||
|
||||
|
||||
class NoSuchDescriptorError(TypeError):
|
||||
def __init__(self, str) -> None:
|
||||
def __init__(self, str: str) -> None:
|
||||
TypeError.__init__(self, str)
|
||||
|
||||
|
||||
|
@ -188,10 +192,10 @@ class DescriptorProvider:
|
|||
"""
|
||||
A way of getting descriptors for interface names
|
||||
"""
|
||||
def __init__(self, config) -> None:
|
||||
def __init__(self, config: Configuration) -> None:
|
||||
self.config = config
|
||||
|
||||
def getDescriptor(self, interfaceName: str) -> "Descriptor":
|
||||
def getDescriptor(self, interfaceName: str) -> Descriptor:
|
||||
"""
|
||||
Gets the appropriate descriptor for the given interface name given the
|
||||
context of the current descriptor.
|
||||
|
@ -199,7 +203,7 @@ class DescriptorProvider:
|
|||
return self.config.getDescriptor(interfaceName)
|
||||
|
||||
|
||||
def MemberIsLegacyUnforgeable(member: IDLAttribute | IDLMethod, descriptor: "Descriptor") -> bool:
|
||||
def MemberIsLegacyUnforgeable(member: IDLAttribute | IDLMethod, descriptor: Descriptor) -> bool:
|
||||
return ((member.isAttr() or member.isMethod())
|
||||
and not member.isStatic()
|
||||
and (member.isLegacyUnforgeable()
|
||||
|
@ -213,7 +217,7 @@ class Descriptor(DescriptorProvider):
|
|||
interface: IDLInterface
|
||||
uniqueImplementation: bool
|
||||
|
||||
def __init__(self, config, interface: IDLInterface, desc: dict[str, Any]) -> None:
|
||||
def __init__(self, config: Configuration, interface: IDLInterface, desc: dict[str, Any]) -> None:
|
||||
DescriptorProvider.__init__(self, config)
|
||||
self.interface = interface
|
||||
|
||||
|
@ -289,7 +293,7 @@ class Descriptor(DescriptorProvider):
|
|||
and any(MemberIsLegacyUnforgeable(m, self) for m in
|
||||
self.interface.members))
|
||||
|
||||
self.operations = {
|
||||
self.operations: dict[str, IDLMethod | None] = {
|
||||
'IndexedGetter': None,
|
||||
'IndexedSetter': None,
|
||||
'IndexedDeleter': None,
|
||||
|
@ -301,7 +305,7 @@ class Descriptor(DescriptorProvider):
|
|||
|
||||
self.hasDefaultToJSON = False
|
||||
|
||||
def addOperation(operation: str, m) -> None:
|
||||
def addOperation(operation: str, m: IDLMethod) -> None:
|
||||
if not self.operations[operation]:
|
||||
self.operations[operation] = m
|
||||
|
||||
|
@ -320,7 +324,7 @@ class Descriptor(DescriptorProvider):
|
|||
if not m.isMethod():
|
||||
continue
|
||||
|
||||
def addIndexedOrNamedOperation(operation: str, m) -> None:
|
||||
def addIndexedOrNamedOperation(operation: str, m: IDLMethod) -> None:
|
||||
if not self.isGlobal():
|
||||
self.proxy = True
|
||||
if m.isIndexed():
|
||||
|
@ -357,8 +361,8 @@ class Descriptor(DescriptorProvider):
|
|||
# array of extended attributes.
|
||||
self.extendedAttributes = {'all': {}, 'getterOnly': {}, 'setterOnly': {}}
|
||||
|
||||
def addExtendedAttribute(attribute, config) -> None:
|
||||
def add(key: str, members, attribute) -> None:
|
||||
def addExtendedAttribute(attribute: dict[str, Any], config: Configuration) -> None:
|
||||
def add(key: str, members: list[str], attribute: dict[str, Any]) -> None:
|
||||
for member in members:
|
||||
self.extendedAttributes[key].setdefault(member, []).append(attribute)
|
||||
|
||||
|
@ -405,7 +409,7 @@ class Descriptor(DescriptorProvider):
|
|||
config.maxProtoChainLength = max(config.maxProtoChainLength,
|
||||
len(self.prototypeChain))
|
||||
|
||||
def maybeGetSuperModule(self):
|
||||
def maybeGetSuperModule(self) -> str | None:
|
||||
"""
|
||||
Returns name of super module if self is part of it
|
||||
"""
|
||||
|
@ -416,10 +420,10 @@ class Descriptor(DescriptorProvider):
|
|||
return filename
|
||||
return None
|
||||
|
||||
def binaryNameFor(self, name: str, isStatic: bool):
|
||||
def binaryNameFor(self, name: str, isStatic: bool) -> str:
|
||||
return self._binaryNames.get((name, isStatic), name)
|
||||
|
||||
def internalNameFor(self, name: str):
|
||||
def internalNameFor(self, name: str) -> str:
|
||||
return self._internalNames.get(name, name)
|
||||
|
||||
def hasNamedPropertiesObject(self) -> bool:
|
||||
|
@ -431,8 +435,8 @@ class Descriptor(DescriptorProvider):
|
|||
def supportsNamedProperties(self) -> bool:
|
||||
return self.operations['NamedGetter'] is not None
|
||||
|
||||
def getExtendedAttributes(self, member, getter=False, setter=False):
|
||||
def maybeAppendInfallibleToAttrs(attrs, throws) -> None:
|
||||
def getExtendedAttributes(self, member: IDLMethod, getter: bool = False, setter: bool = False) -> list[str]:
|
||||
def maybeAppendInfallibleToAttrs(attrs: list[str], throws: bool | None) -> None:
|
||||
if throws is None:
|
||||
attrs.append("infallible")
|
||||
elif throws is True:
|
||||
|
@ -458,7 +462,7 @@ class Descriptor(DescriptorProvider):
|
|||
maybeAppendInfallibleToAttrs(attrs, throws)
|
||||
return attrs
|
||||
|
||||
def getParentName(self):
|
||||
def getParentName(self) -> str | None:
|
||||
parent = self.interface.parent
|
||||
while parent:
|
||||
if not parent.getExtendedAttribute("Inline"):
|
||||
|
@ -469,30 +473,30 @@ class Descriptor(DescriptorProvider):
|
|||
def supportsIndexedProperties(self) -> bool:
|
||||
return self.operations['IndexedGetter'] is not None
|
||||
|
||||
def isMaybeCrossOriginObject(self):
|
||||
def isMaybeCrossOriginObject(self) -> bool:
|
||||
# If we're isGlobal and have cross-origin members, we're a Window, and
|
||||
# that's not a cross-origin object. The WindowProxy is.
|
||||
return self.concrete and self.interface.hasCrossOriginMembers and not self.isGlobal()
|
||||
|
||||
def hasDescendants(self):
|
||||
def hasDescendants(self) -> bool:
|
||||
return (self.interface.getUserData("hasConcreteDescendant", False)
|
||||
or self.interface.getUserData("hasProxyDescendant", False))
|
||||
or self.interface.getUserData("hasProxyDescendant", False) or False)
|
||||
|
||||
def hasHTMLConstructor(self):
|
||||
def hasHTMLConstructor(self) -> bool:
|
||||
ctor = self.interface.ctor()
|
||||
return ctor and ctor.isHTMLConstructor()
|
||||
return (ctor and ctor.isHTMLConstructor()) or False
|
||||
|
||||
def shouldHaveGetConstructorObjectMethod(self):
|
||||
def shouldHaveGetConstructorObjectMethod(self) -> bool:
|
||||
assert self.interface.hasInterfaceObject()
|
||||
if self.interface.getExtendedAttribute("Inline"):
|
||||
return False
|
||||
return (self.interface.isCallback() or self.interface.isNamespace()
|
||||
or self.hasDescendants() or self.hasHTMLConstructor())
|
||||
or self.hasDescendants() or self.hasHTMLConstructor() or False)
|
||||
|
||||
def shouldCacheConstructor(self):
|
||||
def shouldCacheConstructor(self) -> bool:
|
||||
return self.hasDescendants() or self.hasHTMLConstructor()
|
||||
|
||||
def isExposedConditionally(self):
|
||||
def isExposedConditionally(self) -> bool:
|
||||
return self.interface.isExposedConditionally()
|
||||
|
||||
def isGlobal(self) -> bool:
|
||||
|
@ -507,19 +511,19 @@ class Descriptor(DescriptorProvider):
|
|||
# Some utility methods
|
||||
|
||||
|
||||
def MakeNativeName(name):
|
||||
def MakeNativeName(name: str) -> str:
|
||||
return name[0].upper() + name[1:]
|
||||
|
||||
|
||||
def getIdlFileName(object: IDLObject):
|
||||
def getIdlFileName(object: IDLObject) -> str:
|
||||
return os.path.basename(object.location.filename).split('.webidl')[0]
|
||||
|
||||
|
||||
def getModuleFromObject(object: IDLObject):
|
||||
def getModuleFromObject(object: IDLObject) -> str:
|
||||
return ('crate::codegen::GenericBindings::' + getIdlFileName(object) + 'Binding')
|
||||
|
||||
|
||||
def getTypesFromDescriptor(descriptor: Descriptor):
|
||||
def getTypesFromDescriptor(descriptor: Descriptor) -> list[IDLType]:
|
||||
"""
|
||||
Get all argument and return types for all members of the descriptor
|
||||
"""
|
||||
|
@ -570,7 +574,7 @@ def getUnwrappedType(type: IDLType) -> IDLType:
|
|||
return type
|
||||
|
||||
|
||||
def iteratorNativeType(descriptor: Descriptor, infer: bool = False):
|
||||
def iteratorNativeType(descriptor: Descriptor, infer: bool = False) -> str:
|
||||
assert descriptor.interface.maplikeOrSetlikeOrIterable is not None
|
||||
iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
|
||||
assert (iterableDecl.isIterable() and iterableDecl.isPairIterator()) \
|
||||
|
|
|
@ -2,16 +2,25 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
# fmt: off
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
from typing import TYPE_CHECKING
|
||||
from collections.abc import Iterator
|
||||
|
||||
SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
SERVO_ROOT = os.path.abspath(os.path.join(SCRIPT_PATH, "..", "..", ".."))
|
||||
|
||||
FILTER_PATTERN = re.compile("// skip-unless ([A-Z_]+)\n")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from configuration import Configuration
|
||||
from WebIDL import Parser
|
||||
|
||||
def main() -> None:
|
||||
os.chdir(os.path.join(os.path.dirname(__file__)))
|
||||
|
@ -84,13 +93,13 @@ def main() -> None:
|
|||
f.write(module.encode("utf-8"))
|
||||
|
||||
|
||||
def make_dir(path: str):
|
||||
def make_dir(path: str)-> str:
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
return path
|
||||
|
||||
|
||||
def generate(config, name: str, filename: str) -> None:
|
||||
def generate(config: Configuration, name: str, filename: str) -> None:
|
||||
from codegen import GlobalGenRoots
|
||||
root = getattr(GlobalGenRoots, name)(config)
|
||||
code = root.define()
|
||||
|
@ -98,7 +107,7 @@ def generate(config, name: str, filename: str) -> None:
|
|||
f.write(code.encode("utf-8"))
|
||||
|
||||
|
||||
def add_css_properties_attributes(css_properties_json: str, parser) -> None:
|
||||
def add_css_properties_attributes(css_properties_json: str, parser: Parser) -> None:
|
||||
def map_preference_name(preference_name: str) -> str:
|
||||
"""Map between Stylo preference names and Servo preference names as the
|
||||
`css-properties.json` file is generated by Stylo. This should be kept in sync with the
|
||||
|
@ -132,7 +141,7 @@ def add_css_properties_attributes(css_properties_json: str, parser) -> None:
|
|||
parser.parse(idl, "CSSStyleDeclaration_generated.webidl")
|
||||
|
||||
|
||||
def attribute_names(property_name: str):
|
||||
def attribute_names(property_name: str) -> Iterator[str]:
|
||||
# https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-dashed-attribute
|
||||
if property_name != "float":
|
||||
yield property_name
|
||||
|
@ -149,7 +158,7 @@ def attribute_names(property_name: str):
|
|||
|
||||
|
||||
# https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
|
||||
def camel_case(chars: str, webkit_prefixed: bool = False):
|
||||
def camel_case(chars: str, webkit_prefixed: bool = False) -> Iterator[str]:
|
||||
if webkit_prefixed:
|
||||
chars = chars[1:]
|
||||
next_is_uppercase = False
|
||||
|
|
|
@ -8,7 +8,8 @@ extend-exclude = [
|
|||
# upstream
|
||||
"third_party/**",
|
||||
"python/mach/**",
|
||||
"components/**",
|
||||
"components/net/**",
|
||||
"components/shared/**",
|
||||
"tests/**",
|
||||
]
|
||||
|
||||
|
@ -30,7 +31,8 @@ ignore = [
|
|||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"!python/**/**.py" = ["ANN"]
|
||||
"etc/**" = ["ANN"]
|
||||
"components/script_bindings/codegen/codegen.py" = ["ANN"]
|
||||
"**/test.py" = ["ANN"]
|
||||
"**/*_tests.py" = ["ANN"]
|
||||
"**/tests/**/*.py" = ["ANN"]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue