Skip to content

Commit e5f5853

Browse files
Integrate Ruff docstring linting for Arm public APIs (#19934)
Add a RUFF_DOCS lintrunner target scoped to Arm public API files. Signed-off-by: Sebastian Larsson <sebastian.larsson@arm.com>
1 parent 1227757 commit e5f5853

18 files changed

Lines changed: 183 additions & 70 deletions

.lintrunner.toml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,41 @@ command = [
683683
'@{{PATHSFILE}}',
684684
]
685685

686+
[[linter]]
687+
code = 'RUFF_DOCS'
688+
include_patterns = [
689+
'backends/arm/__init__.py',
690+
'backends/arm/common/arm_compile_spec.py',
691+
'backends/arm/ethosu/**/*.py',
692+
'backends/arm/quantizer/__init__.py',
693+
'backends/arm/quantizer/arm_quantizer.py',
694+
'backends/arm/tosa/partitioner.py',
695+
'backends/arm/vgf/**/*.py',
696+
]
697+
exclude_patterns = [
698+
'third-party/**',
699+
'**/third-party/**',
700+
]
701+
command = [
702+
'python',
703+
'-m',
704+
'lintrunner_adapters',
705+
'run',
706+
'ruff_linter',
707+
'--config=pyproject.toml',
708+
'--',
709+
'@{{PATHSFILE}}',
710+
]
711+
init_command = [
712+
'python',
713+
'-m',
714+
'lintrunner_adapters',
715+
'run',
716+
'pip_init',
717+
'--dry-run={{DRYRUN}}',
718+
'--requirement=requirements-lintrunner.txt',
719+
]
720+
686721
[[linter]]
687722
code = 'DOCFORMATTER'
688723
include_patterns = ['backends/arm/**/*.py']

backends/arm/common/arm_compile_spec.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@
2828

2929
@dataclass(init=False)
3030
class ArmCompileSpec(ABC):
31+
"""Base compile specification for Arm backend targets."""
32+
3133
class DebugMode(Enum):
34+
"""Debug artifact formats emitted during Arm lowering."""
35+
3236
JSON = 1
3337
TOSA = 2
3438

@@ -272,8 +276,10 @@ def _set_preserve_io_quantization(self, enabled: bool) -> "ArmCompileSpec":
272276
return self
273277

274278
def _warn_if_redundant_preserve_io_quantization(self) -> None:
275-
"""Warn when preserve_io_quantization has no effect for INT-only
276-
specs.
279+
"""Warn when preserve_io_quantization has no effect.
280+
281+
INT-only specs already preserve IO quantization naturally.
282+
277283
"""
278284
if (
279285
self.preserve_io_quantization
@@ -287,8 +293,7 @@ def _warn_if_redundant_preserve_io_quantization(self) -> None:
287293
)
288294

289295
def _get_pass_pipeline_config(self) -> ArmPassPipelineConfig:
290-
"""Returns configuration that controls how the Arm pass pipeline should
291-
behave.
296+
"""Return the configuration for the Arm pass pipeline.
292297
293298
Subclasses may override to tweak defaults for specific targets.
294299
@@ -298,8 +303,7 @@ def _get_pass_pipeline_config(self) -> ArmPassPipelineConfig:
298303
return self._pipeline_config
299304

300305
def set_pass_pipeline_config(self, config: ArmPassPipelineConfig) -> None:
301-
"""Sets the configuration that controls how the Arm pass pipeline should
302-
behave. Subclasses may override to tweak defaults for specific targets.
306+
"""Set the configuration for the Arm pass pipeline.
303307
304308
Args:
305309
config: The custom ArmPassPipelineConfig to set.
@@ -317,18 +321,16 @@ def _create_default_pipeline_config(self) -> ArmPassPipelineConfig:
317321
return config
318322

319323
def _get_intermediate_path(self) -> str | None:
320-
"""Gets the path used for dumping intermediate results such as tosa and
321-
pte.
324+
"""Get the path used for dumping intermediate results.
322325
323326
Returns:
324-
Path where intermediate results are saved.
327+
Path where TOSA and PTE intermediate results are saved.
325328
326329
"""
327330
return self.path_for_intermediates
328331

329332
def dump_intermediate_artifacts_to(self, output_path: str | None):
330-
"""Sets a path for dumping intermediate results during such as tosa and
331-
pte.
333+
"""Set a path for dumping TOSA and PTE intermediate results.
332334
333335
Args:
334336
output_path: Path to dump intermediate results to.

backends/arm/ethosu/compile_spec.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
# LICENSE file in the root directory of this source tree.
55

66
from executorch.backends.arm.common.arm_compile_spec import ArmCompileSpec
7-
from executorch.backends.arm.common.pipeline_config import ( # noqa: unused
8-
ArmPassPipelineConfig,
9-
)
7+
from executorch.backends.arm.common.pipeline_config import ArmPassPipelineConfig
108
from executorch.backends.arm.tosa import ( # type: ignore[import-not-found]
119
TosaSpecification,
1210
)

backends/arm/quantizer/arm_quantizer.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ def get_cond_while_submodules_ao(
117117
only the ``while_loop`` cond function is processed explicitly there.
118118
119119
"""
120-
121120
if not apply_quantization:
122121
return get_cond_while_submodules(graph_module)
123122

@@ -156,6 +155,7 @@ def get_symmetric_quantization_config(
156155
act_qmax (int): Maximum activation quantization value.
157156
weight_qmin (int): Minimum weight quantization value.
158157
weight_qmax (int): Maximum weight quantization value.
158+
eps (float): Minimum scale value used by observers.
159159
160160
Returns:
161161
QuantizationConfig: Quantization settings for activations, weights, and
@@ -525,14 +525,17 @@ def __init__(
525525

526526
@property
527527
def tosa_spec(self):
528+
"""Return the TOSA specification used by the active quantizer."""
528529
return self.quantizer.tosa_spec
529530

530531
@property
531532
def compile_spec(self):
533+
"""Return the compile specification used by the active quantizer."""
532534
return self.quantizer.compile_spec
533535

534536
@property
535537
def global_config(self):
538+
"""Return the fallback quantization configuration."""
536539
return self.quantizer.global_config
537540

538541
@global_config.setter
@@ -546,6 +549,7 @@ def global_config(self, value: Optional[QuantizationConfig]) -> None:
546549

547550
@property
548551
def io_config(self):
552+
"""Return the input and output quantization configuration."""
549553
if isinstance(self.quantizer, _TOSAQuantizerV1):
550554
return self.quantizer.io_config
551555
else:
@@ -564,6 +568,7 @@ def io_config(self, value: Optional[QuantizationConfig]) -> None:
564568

565569
@property
566570
def module_type_config(self):
571+
"""Return quantization configuration overrides by module type."""
567572
if isinstance(self.quantizer, _TOSAQuantizerV1):
568573
return self.quantizer.module_type_config
569574
else:
@@ -584,6 +589,7 @@ def module_type_config(
584589

585590
@property
586591
def module_name_config(self):
592+
"""Return quantization configuration overrides by module name."""
587593
if isinstance(self.quantizer, _TOSAQuantizerV1):
588594
return getattr(self.quantizer, "module_name_config", {})
589595
else:
@@ -692,6 +698,7 @@ def set_node_finder(
692698
quantization_config (Optional[QuantizationConfig]): Configuration
693699
describing quantization settings for nodes matched by the provided
694700
NodeFinder. ``None`` indicates no quantization.
701+
node_finder (NodeFinder): Predicate used to select nodes.
695702
696703
"""
697704
if self.use_composable_quantizer:
@@ -757,14 +764,18 @@ def annotate(self, model: GraphModule) -> GraphModule:
757764
return self.quantizer.annotate(model)
758765

759766
def validate(self, model: GraphModule) -> None:
760-
"""Validate the quantization results. Currently, this includes:
761-
- Ensure tensor inputs to each operator live on the same device.
767+
"""Validate the quantization results.
768+
769+
Currently, this ensures tensor inputs to each operator live on the same
770+
device.
762771
763772
Args:
764773
model (GraphModule): GraphModule being validated.
774+
765775
Raises:
766776
ValueError: If tensor inputs for any operator span more than one
767777
device.
778+
768779
"""
769780
for node in model.graph.nodes:
770781
if node.op != "call_function":
@@ -809,8 +820,7 @@ def _quantize_with_submodules(
809820
is_qat: bool = False,
810821
fold_quantize: bool = True,
811822
):
812-
"""Quantizes a GraphModule in a way such that conditional submodules are
813-
handled properly.
823+
"""Quantize a GraphModule with conditional submodule handling.
814824
815825
Note: torchao's prepare_pt2e and convert_pt2e natively handle
816826
while_loop body_fn submodules, so we only manually process cond
@@ -823,8 +833,8 @@ def _quantize_with_submodules(
823833
model with submodules, at least one sample per code path is
824834
needed.
825835
is_qat (bool): Whether to do quantization aware training or not.
826-
fold_quantize (bool): Enables or disables constant folding when quantization
827-
is completed.
836+
fold_quantize (bool): Enables or disables constant folding when
837+
quantization is completed.
828838
829839
Returns:
830840
GraphModule: The quantized model.
@@ -949,7 +959,6 @@ def _set_disallow_tfa_for_nodes(self, model: GraphModule) -> None:
949959
quantized models.
950960
951961
"""
952-
953962
# First, set all nodes according to global config
954963
for node in model.graph.nodes:
955964
node.meta[DISALLOW_TFA_META_KEY] = self.global_config is None
@@ -1104,10 +1113,10 @@ def __init__(
11041113

11051114
@property
11061115
def quantizers(self) -> List[Quantizer]:
1107-
"""Returns the configured quantizers in order of precedence, ensuring
1108-
the global config and shared_qspec_quantizer are applied last.
1116+
"""Return the configured quantizers in order of precedence.
11091117
1110-
The returned list is a shallow copy; quantizer instances are shared.
1118+
The returned list is a shallow copy; quantizer instances are shared. The
1119+
global config and shared_qspec_quantizer are applied last.
11111120
11121121
"""
11131122
quantizers = self._quantizers.copy()
@@ -1119,9 +1128,7 @@ def quantizers(self) -> List[Quantizer]:
11191128

11201129
@quantizers.setter
11211130
def quantizers(self, value: List[Quantizer]) -> None:
1122-
"""Override of quantizers setter to allow for dynamic updating of
1123-
quantizers without accessing self._quantizers.
1124-
"""
1131+
"""Update quantizers without accessing self._quantizers directly."""
11251132
self._quantizers = value
11261133

11271134
def annotate(self, model):

backends/arm/tosa/partitioner.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,7 @@ def reject_partition(
155155

156156

157157
def _validate_partition(nodes: set[torch.fx.Node]) -> bool:
158-
"""Check whether a set of nodes can be extracted as a subgraph without
159-
cycles.
158+
"""Check whether a set of nodes can be extracted.
160159
161160
Perform a BFS from the external users of partition nodes. If any node
162161
reached by BFS is itself inside the partition, then extracting the
@@ -260,9 +259,7 @@ def __init__(
260259
self.intermediate_path = compile_spec._get_intermediate_path()
261260

262261
def register_custom_partition_op(self, op: torch._ops.OpOverload) -> None:
263-
"""Register a custom op to be considered supported by this
264-
partitioner.
265-
"""
262+
"""Register a custom op to be considered supported."""
266263
self._custom_partition_ops.add(op)
267264

268265
def _detag_boundary_nodes(
@@ -284,9 +281,10 @@ def _detag_boundary_nodes(
284281
tag: The delegation tag assigned to the partition.
285282
reporter: A reporter to log rejected nodes.
286283
module: The GraphModule containing the partition.
284+
detag_first_fp_node: Whether to de-tag the first floating-point
285+
node in a partition.
287286
288287
"""
289-
290288
# De-tag outermost q-nodes upwards and dq-nodes downwards.
291289
# De-tag if at least one input/output is not part of the partition.
292290
for node in module.graph.nodes:
@@ -322,9 +320,7 @@ def _detag_boundary_nodes(
322320
break
323321

324322
def _preserve_io_quantization_enabled(self) -> bool:
325-
"""Return True if IO quantization should be preserved from compile
326-
specs.
327-
"""
323+
"""Return True if compile specs preserve IO quantization."""
328324
for spec in self.delegation_spec.compile_specs:
329325
if spec.key != "preserve_io_quantization":
330326
continue
@@ -622,9 +618,10 @@ def ops_to_not_decompose( # noqa: C901
622618
}
623619

624620
def filter_fn(node: torch.fx.Node) -> bool:
625-
"""Filter function applied to ops in 'ops_to_not_decompose'. Returns
626-
True if the op should not be decomposed. If this function returns
627-
True, the partitioner *must* accept the node, or the lowering fails.
621+
"""Return True if an op should not be decomposed.
622+
623+
If this function returns True, the partitioner *must* accept the
624+
node, or the lowering fails.
628625
629626
Args:
630627
node (torch.fx.Node): FX node to evaluate.

backends/arm/vgf/backend.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,11 @@ class VgfRuntimeEnvironmentCheck:
7575

7676
@property
7777
def ok(self) -> bool:
78+
"""Return True when the check did not fail."""
7879
return self.status != STATUS_FAIL
7980

8081
def to_dict(self) -> dict[str, str | None]:
82+
"""Return the check as a JSON-serializable dictionary."""
8183
return {
8284
"name": self.name,
8385
"status": self.status,
@@ -94,7 +96,6 @@ def _load_runtime() -> Any:
9496

9597
def check_vgf_runtime_backend_environment() -> VgfRuntimeEnvironmentCheck:
9698
"""Check whether the installed runtime exposes the VGF backend."""
97-
9899
try:
99100
runtime = _load_runtime()
100101
except Exception as exc:

0 commit comments

Comments
 (0)