Skip to content

Commit

Permalink
Fix FA100 (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
hofbi authored Nov 28, 2024
1 parent bf9ae61 commit 4a1e45e
Show file tree
Hide file tree
Showing 18 changed files with 136 additions and 69 deletions.
14 changes: 9 additions & 5 deletions dev_tools/check_jira_reference_in_todo.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# Copyright (c) Luminar Technologies, Inc. All rights reserved.
# Licensed under the MIT License.

from __future__ import annotations

import re
import sys
from pathlib import Path
from typing import Dict, List, Optional, Sequence
from typing import TYPE_CHECKING, Sequence

from dev_tools.git_hook_utils import parse_arguments

if TYPE_CHECKING:
from pathlib import Path


def line_has_incorrect_todo(line: str) -> bool:
return (
Expand All @@ -17,7 +21,7 @@ def line_has_incorrect_todo(line: str) -> bool:
)


def find_files_with_incorrect_jira_reference_in_todo(files: List[Path]) -> List[Dict[str, object]]:
def find_files_with_incorrect_jira_reference_in_todo(files: list[Path]) -> list[dict[str, object]]:
incorrect_files = []
for file in files:
lines = file.read_text(errors="ignore").splitlines()
Expand All @@ -28,7 +32,7 @@ def find_files_with_incorrect_jira_reference_in_todo(files: List[Path]) -> List[
return incorrect_files


def has_any_file_incorrect_jira_reference_in_todo(files: List[Path]) -> bool:
def has_any_file_incorrect_jira_reference_in_todo(files: list[Path]) -> bool:
if incorrect_files := find_files_with_incorrect_jira_reference_in_todo(files):
print("\nThe following TODOs do not correspond to the JIRA-Ticket TODO format 'TODO(ABC-1234):':")
for file in incorrect_files:
Expand All @@ -38,7 +42,7 @@ def has_any_file_incorrect_jira_reference_in_todo(files: List[Path]) -> bool:
return False


def main(argv: Optional[Sequence[str]] = None) -> int:
def main(argv: Sequence[str] | None = None) -> int:
return 1 if has_any_file_incorrect_jira_reference_in_todo(parse_arguments(argv).filenames) else 0


Expand Down
12 changes: 8 additions & 4 deletions dev_tools/check_number_of_lines_count.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# Copyright (c) Luminar Technologies, Inc. All rights reserved.
# Licensed under the MIT License.

import argparse
from __future__ import annotations

import sys
from typing import Optional, Sequence
from typing import TYPE_CHECKING, Sequence

from dev_tools.git_hook_utils import create_default_parser

if TYPE_CHECKING:
import argparse


def parse_arguments(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
def parse_arguments(argv: Sequence[str] | None = None) -> argparse.Namespace:
parser = create_default_parser()
parser.add_argument(
"--max-lines",
Expand All @@ -20,7 +24,7 @@ def parse_arguments(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
return parser.parse_args(argv)


def main(argv: Optional[Sequence[str]] = None) -> int:
def main(argv: Sequence[str] | None = None) -> int:
args = parse_arguments(argv)

are_all_files_ok = True
Expand Down
24 changes: 14 additions & 10 deletions dev_tools/check_ownership.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# Copyright (c) Luminar Technologies, Inc. All rights reserved.
# Licensed under the MIT License.

from __future__ import annotations

import sys
from argparse import Namespace
from enum import IntFlag, auto
from pathlib import Path
from typing import Dict, Generator, Iterable, Iterator, List, Optional, Set
from typing import TYPE_CHECKING, Generator, Iterable, Iterator

from dev_tools.git_hook_utils import create_default_parser
from dev_tools.ownership_utils import GithubOwnerShip, OwnerShipEntry, check_git, get_ownership_entries

if TYPE_CHECKING:
from argparse import Namespace


class ReturnCode(IntFlag):
SUCCESS = 0
Expand Down Expand Up @@ -49,19 +53,19 @@ class OwnerShipTreeNode:
"""Represents a node in filesystem tree."""

def __init__(self) -> None:
self.children: Dict[str, OwnerShipTreeNode] = {}
self.owners: Set[str] = set()
self.line_number: Optional[int] = None
self.children: dict[str, OwnerShipTreeNode] = {}
self.owners: set[str] = set()
self.line_number: int | None = None

def add_or_return_child(self, child_name: str) -> "OwnerShipTreeNode":
def add_or_return_child(self, child_name: str) -> OwnerShipTreeNode:
if child_name not in self.children:
node = OwnerShipTreeNode()
self.children[child_name] = node
return node
return self.children[child_name]


def check_if_codeowners_has_ineffective_rules(all_entries: List[OwnerShipEntry]) -> ReturnCode:
def check_if_codeowners_has_ineffective_rules(all_entries: list[OwnerShipEntry]) -> ReturnCode:
def _populate_tree(entry: OwnerShipEntry, path_parts: Iterator[str], tree_node: OwnerShipTreeNode) -> ReturnCode:
"""Add an OwnerShipEntry to the tree representation of all ownership entries.
Expand Down Expand Up @@ -94,7 +98,7 @@ def _populate_tree(entry: OwnerShipEntry, path_parts: Iterator[str], tree_node:
return return_code

def _find_ineffective_rules(
tree_node: OwnerShipTreeNode, first_ancestor: Optional[OwnerShipTreeNode], current_path: Path
tree_node: OwnerShipTreeNode, first_ancestor: OwnerShipTreeNode | None, current_path: Path
) -> ReturnCode:
"""Search the ownership tree for rules which are fully contained in another
rule. They are ineffective (redundant).
Expand Down Expand Up @@ -149,13 +153,13 @@ def is_empty(iterable: Generator[Path, None, None]) -> bool:
return next(iterable, None) is None


def get_git_tracked_files(folder: Path) -> List[Path]:
def get_git_tracked_files(folder: Path) -> list[Path]:
files = check_git(f"ls-files {folder.relative_to(folder)}", folder)
return [folder / file for file in files.splitlines()]


def check_for_files_without_team_ownership(
repo_dir: Path, changed_files: List[Path], codeowners_owner: Optional[str]
repo_dir: Path, changed_files: list[Path], codeowners_owner: str | None
) -> ReturnCode:
"""The codeowners_owner should own ONLY the CODEOWNERS file."""
if codeowners_owner is None:
Expand Down
10 changes: 7 additions & 3 deletions dev_tools/check_shellscript_set_options.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# Copyright (c) Luminar Technologies, Inc. All rights reserved.
# Licensed under the MIT License.

from __future__ import annotations

import sys
from pathlib import Path
from typing import Optional, Sequence
from typing import TYPE_CHECKING, Sequence

from dev_tools.git_hook_utils import parse_arguments

if TYPE_CHECKING:
from pathlib import Path


def _sets_options_or_is_nolint(line: str) -> bool:
return line.strip() in ["set -euxo pipefail", "# nolint(set_options)"]
Expand All @@ -16,7 +20,7 @@ def _is_valid_bash_file(filename: Path) -> bool:
return any(_sets_options_or_is_nolint(line) for line in filename.open().readlines())


def main(argv: Optional[Sequence[str]] = None) -> int:
def main(argv: Sequence[str] | None = None) -> int:
args = parse_arguments(argv)

invalid_bash_files = [filename for filename in args.filenames if not _is_valid_bash_file(filename)]
Expand Down
24 changes: 13 additions & 11 deletions dev_tools/check_useless_exclude_paths_hooks.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
# Copyright (c) Luminar Technologies, Inc. All rights reserved.
# Licensed under the MIT License.

from __future__ import annotations

import itertools
import sys
from collections import Counter
from pathlib import Path
from typing import Any, Dict, List, Tuple, Type
from typing import Any

from pre_commit.clientlib import load_config

PRE_COMMIT_CONFIG_YAML = ".pre-commit-config.yaml"


class Hook:
def __init__(self, id: str, exclude_paths: List[Path]) -> None:
def __init__(self, id: str, exclude_paths: list[Path]) -> None:
self.__id = id
self.__exclude_paths = exclude_paths

@classmethod
def from_hook_config(cls: Type["Hook"], root_directory: Path, hook_config: Dict[str, str]) -> "Hook":
def from_hook_config(cls: type[Hook], root_directory: Path, hook_config: dict[str, str]) -> Hook:
exclude_list = (
hook_config["exclude"]
.replace("\n", "")
Expand All @@ -40,15 +42,15 @@ def id(self) -> str:
return self.__id

@property
def exclude_paths(self) -> List[Path]:
def exclude_paths(self) -> list[Path]:
return self.__exclude_paths

def find_duplicates(self) -> List[Path]:
def find_duplicates(self) -> list[Path]:
counter = Counter(self.exclude_paths)

return [path for path in counter if counter[path] > 1]

def find_non_existing_paths(self) -> List[Path]:
def find_non_existing_paths(self) -> list[Path]:
return [path for path in self.exclude_paths if not path.resolve().exists()]

def has_duplicates(self) -> bool:
Expand All @@ -62,25 +64,25 @@ def is_regex_pattern(exclude: str) -> bool:
return any(regex_key in exclude for regex_key in ["*", "$", "^"])


def has_excludes(hook_config: Dict[str, str]) -> bool:
def has_excludes(hook_config: dict[str, str]) -> bool:
return bool(hook_config.get("exclude")) and hook_config.get("exclude") != "^$"


def load_hooks(root_directory: Path, config_file: Path) -> List[Hook]:
def load_hooks(root_directory: Path, config_file: Path) -> list[Hook]:
config = load_config(config_file)
hook_configs = itertools.chain(*[repo["hooks"] for repo in config["repos"]])

return [Hook.from_hook_config(root_directory, hook) for hook in hook_configs if has_excludes(hook)]


def have_non_existent_paths_or_duplicates(hooks_list: List[Any]) -> bool:
non_existing_paths: List[Tuple[str, str]] = [
def have_non_existent_paths_or_duplicates(hooks_list: list[Any]) -> bool:
non_existing_paths: list[tuple[str, str]] = [
(hook_instance.id, path)
for hook_instance in hooks_list
if hook_instance.has_non_existing_paths()
for path in hook_instance.find_non_existing_paths()
]
duplicates: List[Tuple[str, str]] = [
duplicates: list[tuple[str, str]] = [
(hook_instance.id, duplicate)
for hook_instance in hooks_list
if hook_instance.has_duplicates()
Expand Down
22 changes: 12 additions & 10 deletions dev_tools/configure_vscode_for_bazel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
"""Generate a VSCode's launch.json debug configurations for selected Bazel C++
targets."""

from __future__ import annotations

import argparse
import json
import logging
import shutil
import subprocess
import sys
from pathlib import Path
from typing import Any, Dict, List, Optional, Sequence, Set
from typing import Any, Sequence

MAX_TARGETS_WITHOUT_CONFIRMATION = 20


def parse_arguments(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
def parse_arguments(argv: Sequence[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"bazel_pattern",
Expand Down Expand Up @@ -49,7 +51,7 @@ def confirm_or_abort(message: str = "") -> None:
sys.exit(1)


def confirm_if_too_many_labels(labels: Set[str], force: bool) -> None: # noqa: FBT001
def confirm_if_too_many_labels(labels: set[str], force: bool) -> None: # noqa: FBT001
if len(labels) > MAX_TARGETS_WITHOUT_CONFIRMATION and not force:
logging.warning("Found %d bazel targets. Are you sure you want to add them all to launch.json?", len(labels))
confirm_or_abort()
Expand All @@ -61,13 +63,13 @@ def query_bazel_for_labels(pattern: str) -> str:
return subprocess.check_output(cmd).decode(sys.stdout.encoding)


def get_label_from_bazel_query_line(line: str) -> Optional[str]:
def get_label_from_bazel_query_line(line: str) -> str | None:
vals = line.split()
kind, entity, label = vals[0], vals[1], " ".join(vals[2:])
return label if is_executable_rule(kind, entity) else None


def get_labels_from_bazel_query_output(output: str, pattern: str) -> Set[str]:
def get_labels_from_bazel_query_output(output: str, pattern: str) -> set[str]:
labels = {
label for label in (get_label_from_bazel_query_line(line) for line in output.splitlines()) if label is not None
}
Expand All @@ -82,7 +84,7 @@ def quit_if_no_labels_found(all_labels: set) -> None:
sys.exit(1)


def find_executable_labels(patterns: Sequence[str], force: bool) -> Set[str]: # noqa: FBT001
def find_executable_labels(patterns: Sequence[str], force: bool) -> set[str]: # noqa: FBT001
logging.info("Searching for executable targets to generate launch.json...")
labels_nested = (
get_labels_from_bazel_query_output(query_bazel_for_labels(pattern), pattern) for pattern in patterns
Expand All @@ -105,7 +107,7 @@ def get_path_from_label(bazel_label: str) -> str:
return remove_prefix_if_present(bazel_label, "//").replace(":", "/")


def get_new_config(executable_labels: Set[str]) -> Dict[str, Any]:
def get_new_config(executable_labels: set[str]) -> dict[str, Any]:
return {
"version": "0.2.0",
"configurations": [
Expand Down Expand Up @@ -138,7 +140,7 @@ def get_new_config(executable_labels: Set[str]) -> Dict[str, Any]:
}


def save_new_config(new_config: Dict[str, Any], config_location: Path, force: bool) -> None: # noqa: FBT001
def save_new_config(new_config: dict[str, Any], config_location: Path, force: bool) -> None: # noqa: FBT001
"""Serializes the new_configuration to config_location.
If the file already exists, asks for confirmation, unless force is set.
Expand All @@ -150,14 +152,14 @@ def save_new_config(new_config: Dict[str, Any], config_location: Path, force: bo
logging.info("Saved new configuration to %s", config_location)


def print_build_reminder(bazel_patterns: List[str]) -> None:
def print_build_reminder(bazel_patterns: list[str]) -> None:
infix = "eg. " if len(bazel_patterns) > 1 else ""
logging.info(
"Remember to build the target(s) beforehand with %s `bazel build --config=debug %s`.", infix, bazel_patterns[0]
)


def update_launch_json(bazel_patterns: List[str], config_location: Path, force: bool) -> None: # noqa: FBT001
def update_launch_json(bazel_patterns: list[str], config_location: Path, force: bool) -> None: # noqa: FBT001
executable_labels = find_executable_labels(bazel_patterns, force)
new_config = get_new_config(executable_labels)
save_new_config(new_config, config_location, force)
Expand Down
9 changes: 5 additions & 4 deletions dev_tools/find_owner.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
Can also find owners of children, if a level is given.
"""

from __future__ import annotations

import argparse
import sys
from pathlib import Path
from typing import Dict, List, Tuple

from dev_tools.ownership_utils import GithubOwnerShip, check_git

Expand Down Expand Up @@ -38,14 +39,14 @@ def parse_arguments() -> argparse.Namespace:
return parser.parse_args()


def get_subitems(item: Path, level: int) -> List[Path]:
def get_subitems(item: Path, level: int) -> list[Path]:
if level == 0:
return [item.resolve()]
pattern = "/".join(["*"] * level)
return sorted(item.resolve() for item in item.glob(pattern))


def get_owners(item: Path, level: int) -> Dict[str, Tuple[str, ...]]:
def get_owners(item: Path, level: int) -> dict[str, tuple[str, ...]]:
if not item.exists():
msg = f"Item {item} does not exist. Please provide a valid path to an existing file or folder as item."
raise FileNotFoundError(msg)
Expand All @@ -56,7 +57,7 @@ def get_owners(item: Path, level: int) -> Dict[str, Tuple[str, ...]]:
return {str(item.relative_to(repo_dir)): ownership.get_owners(item) for item in items}


def print_owners(owners: Dict[str, Tuple[str, ...]]) -> None:
def print_owners(owners: dict[str, tuple[str, ...]]) -> None:
max_path_length = max(len(item) for item in owners)
for item, owner in owners.items():
print(f'{item:{max_path_length}} -> {", ".join(owner)}')
Expand Down
Loading

0 comments on commit 4a1e45e

Please sign in to comment.