diff --git a/grizzly/adapter/adapter.py b/grizzly/adapter/adapter.py index 481fb2a0..8c7e62bb 100644 --- a/grizzly/adapter/adapter.py +++ b/grizzly/adapter/adapter.py @@ -5,13 +5,15 @@ from abc import ABCMeta, abstractmethod from pathlib import Path -from typing import Any, Generator, final +from typing import TYPE_CHECKING, Any, Generator, final -from sapphire import ServerMap - -from ..common.storage import TestCase from ..common.utils import DEFAULT_TIME_LIMIT, HARNESS_FILE -from ..target.target_monitor import TargetMonitor + +if TYPE_CHECKING: + from sapphire import ServerMap + + from ..common.storage import TestCase + from ..target.target_monitor import TargetMonitor __all__ = ("Adapter", "AdapterError") __author__ = "Tyson Smith" @@ -65,7 +67,7 @@ def __init__(self, name: str) -> None: def __enter__(self) -> Adapter: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() @final diff --git a/grizzly/adapter/no_op_adapter/__init__.py b/grizzly/adapter/no_op_adapter/__init__.py index a9bed7a8..d91f847c 100644 --- a/grizzly/adapter/no_op_adapter/__init__.py +++ b/grizzly/adapter/no_op_adapter/__init__.py @@ -3,11 +3,15 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import annotations -from sapphire import ServerMap +from typing import TYPE_CHECKING -from ...common.storage import TestCase from ..adapter import Adapter +if TYPE_CHECKING: + from sapphire import ServerMap + + from ...common.storage import TestCase + __author__ = "Tyson Smith" __credits__ = ["Tyson Smith"] diff --git a/grizzly/args.py b/grizzly/args.py index e7b58885..8dac2709 100644 --- a/grizzly/args.py +++ b/grizzly/args.py @@ -268,13 +268,11 @@ def __init__(self) -> None: @staticmethod def is_headless() -> bool: - if ( + return ( system().startswith("Linux") and not getenv("DISPLAY") and not getenv("WAYLAND_DISPLAY") - ): - return True - return False + ) def parse_args(self, argv: list[str] | None = None) -> Namespace: args = self.parser.parse_args(argv) diff --git a/grizzly/common/bugzilla.py b/grizzly/common/bugzilla.py index a1fd6a17..2f32b23e 100644 --- a/grizzly/common/bugzilla.py +++ b/grizzly/common/bugzilla.py @@ -10,7 +10,7 @@ from pathlib import Path from shutil import rmtree from tempfile import mkdtemp -from typing import Any, Generator +from typing import Generator from zipfile import ZipFile from bugsy import Bug, Bugsy @@ -37,7 +37,7 @@ def __init__(self, bug: Bug) -> None: def __enter__(self) -> BugzillaBug: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() def _fetch_attachments(self) -> None: @@ -150,7 +150,7 @@ def testcases(self) -> list[Path]: """ # unpack archives self._unpack_archives() - testcases = list(x for x in self._data.iterdir() if x.is_dir()) + testcases = [x for x in self._data.iterdir() if x.is_dir()] # scan base directory for files, filtering out assets files = tuple( x diff --git a/grizzly/common/fuzzmanager.py b/grizzly/common/fuzzmanager.py index c5145eb3..6317476d 100644 --- a/grizzly/common/fuzzmanager.py +++ b/grizzly/common/fuzzmanager.py @@ -71,7 +71,7 @@ def crash_id(self) -> int: def __enter__(self) -> CrashEntry: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() def __getattr__(self, name: str) -> Any: @@ -287,7 +287,7 @@ def bucket_id(self) -> int: def __enter__(self) -> Bucket: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() def __getattr__(self, name: str) -> Any: diff --git a/grizzly/common/iomanager.py b/grizzly/common/iomanager.py index 0f8c07a1..fc96b8a7 100644 --- a/grizzly/common/iomanager.py +++ b/grizzly/common/iomanager.py @@ -3,8 +3,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import annotations -from typing import Any - from sapphire.server_map import ServerMap from .storage import TestCase @@ -36,7 +34,7 @@ def __init__(self, report_size: int = 1) -> None: def __enter__(self) -> IOManager: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() def cleanup(self) -> None: diff --git a/grizzly/common/report.py b/grizzly/common/report.py index 60ee893b..9fa5ab88 100644 --- a/grizzly/common/report.py +++ b/grizzly/common/report.py @@ -216,10 +216,10 @@ def crash_signature_max_frames( ignore = 0 for count, entry in enumerate(crash_info.backtrace, start=1): # Rust panics add frames of noise to the top of the stack (std::panicking) - if any(entry.startswith(x) for x in IGNORED_FRAMES): - ignore += 1 # Sanitizer heap profile also have more noise on the stack (alloc::alloc) - elif entry.startswith("alloc::alloc"): + if any(entry.startswith(x) for x in IGNORED_FRAMES) or entry.startswith( + "alloc::alloc" + ): ignore += 1 # only look at the top of the stack if count - ignore == suggested_frames: diff --git a/grizzly/common/reporter.py b/grizzly/common/reporter.py index 64e3051a..c0ede817 100644 --- a/grizzly/common/reporter.py +++ b/grizzly/common/reporter.py @@ -11,7 +11,7 @@ from pathlib import Path from shutil import copyfile, move, rmtree from tempfile import TemporaryDirectory -from typing import Any, cast, final +from typing import TYPE_CHECKING, Any, cast, final from zipfile import ZIP_DEFLATED, ZipFile from Collector.Collector import Collector @@ -19,9 +19,11 @@ from psutil import disk_usage from .report import Report -from .storage import TestCase from .utils import grz_tmp +if TYPE_CHECKING: + from .storage import TestCase + __all__ = ("FilesystemReporter", "FuzzManagerReporter", "Quality") __author__ = "Tyson Smith" __credits__ = ["Tyson Smith"] diff --git a/grizzly/common/stack_hasher.py b/grizzly/common/stack_hasher.py index d6289310..113ad0a0 100644 --- a/grizzly/common/stack_hasher.py +++ b/grizzly/common/stack_hasher.py @@ -14,6 +14,7 @@ from __future__ import annotations from abc import ABC, abstractmethod +from contextlib import suppress from hashlib import sha1 from logging import DEBUG, INFO, basicConfig, getLogger from os.path import basename @@ -155,10 +156,8 @@ def from_line(cls, input_line: str) -> GdbStackFrame | None: # find file name and line number if ") at " in input_line: input_line = input_line.split(") at ")[-1] - try: + with suppress(ValueError): input_line, sframe.offset = input_line.split(":") - except ValueError: - pass sframe.location = basename(input_line).split()[0] return sframe @@ -379,14 +378,14 @@ def _calculate_hash(self, major: bool = False) -> str | None: major_depth = 0 for frame in self.frames[offset:]: # only track depth when needed - if major and self._major_depth > 0: - # don't count ignored frames towards major hash depth - if not frame.function or not any( - frame.function.startswith(x) for x in IGNORED_FRAMES - ): - major_depth += 1 - if major_depth > self._major_depth: - break + # and don't count ignored frames towards major hash depth + if (major and self._major_depth > 0) and ( + not frame.function + or not any(frame.function.startswith(x) for x in IGNORED_FRAMES) + ): + major_depth += 1 + if major_depth > self._major_depth: + break if frame.location is not None: bucket_hash.update(frame.location.encode(errors="replace")) diff --git a/grizzly/common/status.py b/grizzly/common/status.py index 10806ecc..feb3bd60 100644 --- a/grizzly/common/status.py +++ b/grizzly/common/status.py @@ -12,14 +12,17 @@ from json import dumps, loads from logging import getLogger from os import getpid -from pathlib import Path from sqlite3 import Connection, OperationalError, connect from time import perf_counter, time -from typing import Any, Callable, Generator, cast +from typing import TYPE_CHECKING, Any, Callable, Generator, cast -from .reporter import FuzzManagerReporter from .utils import grz_tmp +if TYPE_CHECKING: + from pathlib import Path + + from .reporter import FuzzManagerReporter + __all__ = ("ReadOnlyStatus", "ReductionStatus", "Status", "SimpleStatus") __author__ = "Tyson Smith" __credits__ = ["Tyson Smith"] diff --git a/grizzly/common/status_reporter.py b/grizzly/common/status_reporter.py index 65686b2b..0133dafb 100644 --- a/grizzly/common/status_reporter.py +++ b/grizzly/common/status_reporter.py @@ -675,10 +675,7 @@ def _format_seconds(duration: float) -> str: def _format_duration(duration: int | None, total: float = 0) -> str: result = "" if duration is not None: - if total == 0: - percent = 0 - else: - percent = int(100 * duration / total) + percent = 0 if total == 0 else int(100 * duration / total) result = _format_seconds(duration) result += f" ({percent:3d}%)" return result @@ -687,10 +684,7 @@ def _format_duration(duration: int | None, total: float = 0) -> str: def _format_number(number: int | None, total: float = 0) -> str: result = "" if number is not None: - if total == 0: - percent = 0 - else: - percent = int(100 * number / total) + percent = 0 if total == 0 else int(100 * number / total) result = f"{number:n} ({percent:3d}%)" return result diff --git a/grizzly/common/storage.py b/grizzly/common/storage.py index 09ba3590..2ca5f27e 100644 --- a/grizzly/common/storage.py +++ b/grizzly/common/storage.py @@ -12,7 +12,7 @@ from shutil import copyfile, copytree, move, rmtree from tempfile import NamedTemporaryFile, mkdtemp from time import time -from typing import Any, Generator, cast +from typing import Any, Generator, Iterator, cast from .utils import grz_tmp, package_version @@ -114,10 +114,10 @@ def __getitem__(self, key: str) -> Path: def __enter__(self) -> TestCase: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() - def __iter__(self) -> Generator[str, None, None]: + def __iter__(self) -> Iterator[str]: """TestCase contents. Args: diff --git a/grizzly/common/test_fuzzmanager.py b/grizzly/common/test_fuzzmanager.py index 92f3671f..db764528 100644 --- a/grizzly/common/test_fuzzmanager.py +++ b/grizzly/common/test_fuzzmanager.py @@ -190,7 +190,7 @@ def test_crash_03(mocker, tmp_path, file_name, passed_ext, expected): "id": 234, "testcase": file_name, } - with open(tmp_path / file_name, "w") as zip_fp: + with (tmp_path / file_name).open("w") as zip_fp: zip_fp.write("data") with CrashEntry(234) as crash: assert crash.testcase == file_name # pre-load data dict so I can re-patch get diff --git a/grizzly/common/test_report.py b/grizzly/common/test_report.py index a6917113..9d591c4b 100644 --- a/grizzly/common/test_report.py +++ b/grizzly/common/test_report.py @@ -33,8 +33,8 @@ def test_report_01(tmp_path): assert report._logs.stdout.name == "log_stdout.txt" assert report.preferred.name == "log_stderr.txt" assert report.stack is None - assert Report.DEFAULT_MAJOR == report.major - assert Report.DEFAULT_MINOR == report.minor + assert report.major == Report.DEFAULT_MAJOR + assert report.minor == Report.DEFAULT_MINOR assert report.prefix is not None report.cleanup() assert not tmp_path.exists() @@ -53,8 +53,8 @@ def test_report_02(tmp_path): assert report._logs.stdout.name == "log_stdout.txt" assert report.preferred.name == "log_asan_blah.txt" assert report.stack is not None - assert Report.DEFAULT_MAJOR != report.major - assert Report.DEFAULT_MINOR != report.minor + assert report.major != Report.DEFAULT_MAJOR + assert report.minor != Report.DEFAULT_MINOR assert report.prefix is not None report.cleanup() diff --git a/grizzly/common/test_reporter.py b/grizzly/common/test_reporter.py index 900e3ab7..5af179c9 100644 --- a/grizzly/common/test_reporter.py +++ b/grizzly/common/test_reporter.py @@ -89,7 +89,7 @@ def test_filesystem_reporter_02(tmp_path, mocker): (log_path / "log_stderr.txt").write_bytes(b"STDERR log") (log_path / "log_stdout.txt").write_bytes(b"STDOUT log") _create_crash_log(log_path / "log_asan_blah.txt") - tests = list(mocker.Mock(spec_set=TestCase, timestamp=x) for x in range(10)) + tests = [mocker.Mock(spec_set=TestCase, timestamp=x) for x in range(10)] report_path = tmp_path / "reports" assert not report_path.exists() reporter = FilesystemReporter(report_path) @@ -102,7 +102,7 @@ def test_filesystem_reporter_02(tmp_path, mocker): log_path.mkdir() (log_path / "log_stderr.txt").write_bytes(b"STDERR log") (log_path / "log_stdout.txt").write_bytes(b"STDOUT log") - tests = list(mocker.Mock(spec_set=TestCase, timestamp=1) for _ in range(2)) + tests = [mocker.Mock(spec_set=TestCase, timestamp=1) for _ in range(2)] reporter.submit(tests, Report(log_path, Path("bin"))) assert all(x.dump.call_count == 1 for x in tests) assert len(tuple(report_path.iterdir())) == 2 diff --git a/grizzly/main.py b/grizzly/main.py index 6b63cc1b..15b18cfe 100644 --- a/grizzly/main.py +++ b/grizzly/main.py @@ -3,10 +3,9 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import annotations -from argparse import Namespace from logging import DEBUG, getLogger from os import getpid -from typing import cast +from typing import TYPE_CHECKING, cast from sapphire import CertificateBundle, Sapphire @@ -28,6 +27,9 @@ from .session import LogRate, Session from .target import Target, TargetLaunchError, TargetLaunchTimeout +if TYPE_CHECKING: + from argparse import Namespace + __author__ = "Tyson Smith" __credits__ = ["Tyson Smith", "Jesse Schwartzentruber"] diff --git a/grizzly/reduce/core.py b/grizzly/reduce/core.py index 0c3f8bf0..3b02ec02 100644 --- a/grizzly/reduce/core.py +++ b/grizzly/reduce/core.py @@ -6,14 +6,13 @@ import json import os -from argparse import Namespace from itertools import chain from locale import LC_ALL, setlocale from logging import getLogger from math import ceil, log from pathlib import Path from time import time -from typing import Any +from typing import TYPE_CHECKING from FTB.Signatures.CrashInfo import CrashSignature @@ -43,6 +42,9 @@ from .exceptions import GrizzlyReduceBaseException, NotReproducible from .strategies import STRATEGIES +if TYPE_CHECKING: + from argparse import Namespace + __author__ = "Jesse Schwartzentruber" __credits__ = ["Jesse Schwartzentruber", "Tyson Smith"] @@ -159,7 +161,7 @@ def __init__( def __enter__(self) -> ReduceManager: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() def cleanup(self) -> None: diff --git a/grizzly/reduce/strategies/__init__.py b/grizzly/reduce/strategies/__init__.py index 92dc132c..fe158a8a 100644 --- a/grizzly/reduce/strategies/__init__.py +++ b/grizzly/reduce/strategies/__init__.py @@ -19,11 +19,13 @@ from pathlib import Path from shutil import rmtree from tempfile import mkdtemp -from typing import Any, Generator, Iterable, Type, cast +from typing import TYPE_CHECKING, Iterable, Iterator, Type, cast -from ...common.storage import TestCase from ...common.utils import grz_tmp +if TYPE_CHECKING: + from ...common.storage import TestCase + try: from pkg_resources import iter_entry_points except ImportError: @@ -142,7 +144,7 @@ def dump_testcases( testcase.dump(self._testcase_root / f"{idx:03d}", include_details=True) @abstractmethod - def __iter__(self) -> Generator[list[TestCase], None, None]: + def __iter__(self) -> Iterator[list[TestCase]]: """Iterate over potential reductions of testcases according to this strategy. The caller should evaluate each reduction yielded, and call `update` with the @@ -172,7 +174,7 @@ def __enter__(self) -> Strategy: """ return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: """Exit the runtime context. `cleanup` is called. Arguments: diff --git a/grizzly/reduce/strategies/beautify.py b/grizzly/reduce/strategies/beautify.py index 2db8f76f..5ea57e5e 100644 --- a/grizzly/reduce/strategies/beautify.py +++ b/grizzly/reduce/strategies/beautify.py @@ -12,8 +12,7 @@ import re from abc import ABC, abstractmethod from logging import getLogger -from pathlib import Path -from typing import Generator, Match, cast +from typing import TYPE_CHECKING, Generator, Match, cast from lithium.testcases import TestcaseLine @@ -34,6 +33,9 @@ from ...common.storage import TEST_INFO, TestCase from . import Strategy, _contains_dd +if TYPE_CHECKING: + from pathlib import Path + LOG = getLogger(__name__) @@ -88,9 +90,9 @@ def __init__(self, testcases: list[TestCase]) -> None: path.is_file() and path.suffix in self.all_extensions and path.name not in self.ignore_files + and _contains_dd(path) ): - if _contains_dd(path): - self._files_to_beautify.append(path) + self._files_to_beautify.append(path) self._current_feedback: bool | None = None tag_bytes = self.tag_name.encode("ascii") self._re_tag_start = re.compile( diff --git a/grizzly/reduce/strategies/lithium.py b/grizzly/reduce/strategies/lithium.py index fc222521..f6f83451 100644 --- a/grizzly/reduce/strategies/lithium.py +++ b/grizzly/reduce/strategies/lithium.py @@ -6,8 +6,7 @@ from abc import ABC from logging import getLogger -from pathlib import Path -from typing import Generator +from typing import TYPE_CHECKING, Iterator from lithium.strategies import CheckOnly from lithium.strategies import CollapseEmptyBraces as LithCollapseEmptyBraces @@ -19,6 +18,9 @@ from ...common.storage import TestCase from . import Strategy, _contains_dd +if TYPE_CHECKING: + from pathlib import Path + LOG = getLogger(__name__) @@ -81,7 +83,7 @@ def update(self, success: bool) -> None: self._current_reducer.feedback(success) self._current_feedback = success - def __iter__(self) -> Generator[list[TestCase], None, None]: + def __iter__(self) -> Iterator[list[TestCase]]: """Iterate over potential reductions of testcases according to this strategy. The caller should evaluate each testcase set yielded, and call `update` with the @@ -183,7 +185,7 @@ def __init__(self, testcases: list[TestCase]) -> None: # just once per Grizzly TestCase set is enough. self._files_to_reduce = self._files_to_reduce[:1] - def __iter__(self) -> Generator[list[TestCase], None, None]: + def __iter__(self) -> Iterator[list[TestCase]]: yield from super().__iter__() diff --git a/grizzly/replay/crash.py b/grizzly/replay/crash.py index 2a3c083c..7ddd0560 100644 --- a/grizzly/replay/crash.py +++ b/grizzly/replay/crash.py @@ -3,15 +3,18 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import annotations -from argparse import Namespace from json import loads from logging import getLogger +from typing import TYPE_CHECKING from ..common.fuzzmanager import Bucket, CrashEntry, load_fm_data from ..common.utils import configure_logging from .args import ReplayFuzzManagerIDArgs from .replay import ReplayManager +if TYPE_CHECKING: + from argparse import Namespace + LOG = getLogger(__name__) diff --git a/grizzly/replay/replay.py b/grizzly/replay/replay.py index 8e58b581..43f96ec2 100644 --- a/grizzly/replay/replay.py +++ b/grizzly/replay/replay.py @@ -3,12 +3,11 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import annotations -from argparse import Namespace from dataclasses import dataclass from logging import getLogger from pathlib import Path from tempfile import mkdtemp -from typing import Any, Callable, cast +from typing import TYPE_CHECKING, Callable, cast from FTB.Signatures.CrashInfo import CrashSignature @@ -42,6 +41,9 @@ TargetLaunchTimeout, ) +if TYPE_CHECKING: + from argparse import Namespace + __author__ = "Tyson Smith" __credits__ = ["Tyson Smith"] @@ -102,7 +104,7 @@ def __init__( def __enter__(self) -> ReplayManager: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() @property diff --git a/grizzly/session.py b/grizzly/session.py index 8383015c..ac6e4ec4 100644 --- a/grizzly/session.py +++ b/grizzly/session.py @@ -6,18 +6,20 @@ from enum import IntEnum, unique from logging import getLogger from time import time -from typing import Any +from typing import TYPE_CHECKING -from sapphire import Sapphire - -from .adapter import Adapter from .common.iomanager import IOManager -from .common.reporter import Reporter from .common.runner import Runner from .common.status import STATUS_DB_FUZZ, Status -from .common.storage import TestCase from .target import Result, Target +if TYPE_CHECKING: + from sapphire import Sapphire + + from .adapter import Adapter + from .common.reporter import Reporter + from .common.storage import TestCase + __all__ = ("SessionError", "LogOutputLimiter", "Session") __author__ = "Tyson Smith" __credits__ = ["Tyson Smith", "Jesse Schwartzentruber"] @@ -69,9 +71,7 @@ def ready(self, cur_iter: int, launches: int) -> bool: if cur_iter >= self._iterations: ready = True self._iterations *= self._multiplier - elif launches >= self._launches: - ready = True - elif time() - self._delay >= self._time: + elif launches >= self._launches or time() - self._delay >= self._time: ready = True if ready: self._time = time() @@ -126,7 +126,7 @@ def __init__( def __enter__(self) -> Session: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.close() def close(self) -> None: diff --git a/grizzly/target/assets.py b/grizzly/target/assets.py index d652f6e1..c550ba3c 100644 --- a/grizzly/target/assets.py +++ b/grizzly/target/assets.py @@ -7,7 +7,6 @@ from pathlib import Path from shutil import copyfile, copytree, move, rmtree from tempfile import mkdtemp -from typing import Any __all__ = ("AssetError", "AssetManager") __author__ = "Tyson Smith" @@ -30,7 +29,7 @@ def __init__(self, base_path: Path | None = None) -> None: def __enter__(self) -> AssetManager: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() def add(self, asset: str, path: Path, copy: bool = True) -> Path: diff --git a/grizzly/target/puppet_target.py b/grizzly/target/puppet_target.py index 752abfe6..a6cb4517 100644 --- a/grizzly/target/puppet_target.py +++ b/grizzly/target/puppet_target.py @@ -42,10 +42,7 @@ def is_healthy(self) -> bool: def is_idle(self, threshold: int) -> bool: # assert 0 <= threshold <= 100 - for _, cpu in self._puppet.cpu_usage(): - if cpu >= threshold: - return False - return True + return all(cpu < threshold for _, cpu in self._puppet.cpu_usage()) def is_running(self) -> bool: return self._puppet.is_running() diff --git a/grizzly/target/target.py b/grizzly/target/target.py index 78c43e01..2d16d969 100644 --- a/grizzly/target/target.py +++ b/grizzly/target/target.py @@ -7,16 +7,19 @@ from enum import IntEnum, unique from logging import getLogger from os import environ -from pathlib import Path from threading import Lock -from typing import Any, final +from typing import TYPE_CHECKING, final from sapphire import CertificateBundle -from ..common.report import Report from ..common.utils import grz_tmp from .assets import AssetManager -from .target_monitor import TargetMonitor + +if TYPE_CHECKING: + from pathlib import Path + + from ..common.report import Report + from .target_monitor import TargetMonitor __all__ = ("Result", "Target", "TargetError", "TargetLaunchError") __author__ = "Tyson Smith" @@ -94,7 +97,7 @@ def __init__( def __enter__(self) -> Target: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.cleanup() @property diff --git a/grizzly/target/target_monitor.py b/grizzly/target/target_monitor.py index 09a6e43d..2bfc7254 100644 --- a/grizzly/target/target_monitor.py +++ b/grizzly/target/target_monitor.py @@ -4,7 +4,10 @@ from __future__ import annotations from abc import ABCMeta, abstractmethod -from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from pathlib import Path __all__ = ("TargetMonitor",) __author__ = "Tyson Smith" diff --git a/loki/loki.py b/loki/loki.py index b07e9a34..274756bb 100644 --- a/loki/loki.py +++ b/loki/loki.py @@ -6,7 +6,6 @@ """ from __future__ import annotations -from argparse import Namespace from logging import ERROR, INFO, basicConfig, getLogger from os import SEEK_END from pathlib import Path @@ -15,7 +14,10 @@ from struct import pack, unpack from tempfile import SpooledTemporaryFile, mkdtemp from time import perf_counter, strftime -from typing import IO +from typing import IO, TYPE_CHECKING + +if TYPE_CHECKING: + from argparse import Namespace __author__ = "Tyson Smith" diff --git a/pyproject.toml b/pyproject.toml index 4f5a48f0..c7a78e99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ exclude_lines = [ "@(abc.)?abstract*", "except ImportError(.*):", "if __name__ == .__main__.:", + "if TYPE_CHECKING:", "pragma: no cover", ] @@ -65,6 +66,8 @@ log_level = "DEBUG" [tool.ruff.lint] select = [ + # flake8-comprehensions + "C4", # pycodestyle "E", # Pyflakes @@ -73,10 +76,15 @@ select = [ "FLY", # Perflint "PERF", + # flake8-simplify + "SIM", + # flake8-type-checking + "TCH", # Ruff-specific rules "RUF", # pycodestyle "W", ] +ignore = ["SIM117"] [tool.setuptools_scm] diff --git a/sapphire/conftest.py b/sapphire/conftest.py index d0205aac..cb403c40 100644 --- a/sapphire/conftest.py +++ b/sapphire/conftest.py @@ -181,10 +181,11 @@ def _handle_request( exc_tb.tb_lineno if exc_tb else None, t_file.file, ) - if indicate_failure: - if not skip_served or t_file.code is None: - t_file.code = 0 - break + if indicate_failure and ( + not skip_served or t_file.code is None + ): + t_file.code = 0 + break self._idle.set() def wait(self, timeout=None): diff --git a/sapphire/connection_manager.py b/sapphire/connection_manager.py index 3645c479..aa596b89 100644 --- a/sapphire/connection_manager.py +++ b/sapphire/connection_manager.py @@ -4,14 +4,17 @@ from __future__ import annotations from logging import getLogger -from socket import socket from time import time from traceback import format_exception -from typing import Any, Callable +from typing import TYPE_CHECKING, Callable -from .job import Job from .worker import Worker +if TYPE_CHECKING: + from socket import socket + + from .job import Job + __author__ = "Tyson Smith" __credits__ = ["Tyson Smith"] @@ -48,7 +51,7 @@ def __init__( def __enter__(self) -> ConnectionManager: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.close() def _can_continue(self, continue_cb: Callable[[], bool] | None) -> bool: diff --git a/sapphire/core.py b/sapphire/core.py index 385aee17..1ac4610d 100644 --- a/sapphire/core.py +++ b/sapphire/core.py @@ -6,18 +6,21 @@ """ from __future__ import annotations -from argparse import Namespace from logging import getLogger from pathlib import Path from socket import SO_REUSEADDR, SOL_SOCKET, gethostname, socket from ssl import PROTOCOL_TLS_SERVER, SSLContext, SSLSocket from time import perf_counter, sleep -from typing import Any, Callable, Iterable, Mapping, cast +from typing import TYPE_CHECKING, Callable, Iterable, Mapping, cast -from .certificate_bundle import CertificateBundle from .connection_manager import ConnectionManager from .job import Job, Served -from .server_map import ServerMap + +if TYPE_CHECKING: + from argparse import Namespace + + from .certificate_bundle import CertificateBundle + from .server_map import ServerMap __all__ = ( "BLOCKED_PORTS", @@ -138,7 +141,7 @@ def __init__( def __enter__(self) -> Sapphire: return self - def __exit__(self, *exc: Any) -> None: + def __exit__(self, *exc: object) -> None: self.close() def clear_backlog(self, timeout: float = 10) -> bool: diff --git a/sapphire/job.py b/sapphire/job.py index 8317d130..5ee65922 100644 --- a/sapphire/job.py +++ b/sapphire/job.py @@ -12,14 +12,16 @@ from logging import getLogger from mimetypes import guess_type from os.path import splitext -from pathlib import Path from queue import Queue from threading import Event, Lock from types import MappingProxyType -from typing import Any, Iterable, Mapping, NamedTuple, Tuple, Union, cast +from typing import TYPE_CHECKING, Any, Iterable, Mapping, NamedTuple, Tuple, Union, cast from .server_map import DynamicResource, FileResource, RedirectResource, ServerMap +if TYPE_CHECKING: + from pathlib import Path + __author__ = "Tyson Smith" __credits__ = ["Tyson Smith"] diff --git a/sapphire/server_map.py b/sapphire/server_map.py index e8f668a6..3106ee90 100644 --- a/sapphire/server_map.py +++ b/sapphire/server_map.py @@ -6,9 +6,11 @@ from dataclasses import dataclass from inspect import signature from logging import getLogger -from pathlib import Path from re import search as re_search -from typing import Callable +from typing import TYPE_CHECKING, Callable + +if TYPE_CHECKING: + from pathlib import Path __all__ = ("Resource", "ServerMap") __author__ = "Tyson Smith" diff --git a/sapphire/worker.py b/sapphire/worker.py index 9c0a97f7..a261d4d7 100644 --- a/sapphire/worker.py +++ b/sapphire/worker.py @@ -14,11 +14,14 @@ from sys import exc_info from threading import Thread, ThreadError, active_count from time import sleep +from typing import TYPE_CHECKING from urllib.parse import ParseResult, quote, unquote, urlparse -from .job import Job from .server_map import DynamicResource, FileResource, RedirectResource +if TYPE_CHECKING: + from .job import Job + # TODO: urlparse -> urlsplit