diff --git a/grizzly/main.py b/grizzly/main.py index 15b18cfe..d5509044 100644 --- a/grizzly/main.py +++ b/grizzly/main.py @@ -38,7 +38,16 @@ def main(args: Namespace) -> int: + """CLI for `grizzly`. + + Arguments: + args: Result from `GrizzlyArgs.parse_args`. + + Returns: + Exit.SUCCESS (0) for success otherwise a different Exit code is returned. + """ configure_logging(args.log_level) + LOG.info("Starting Grizzly (%d)", getpid()) LOG.debug("grizzly-framework version: %s", package_version("grizzly-framework")) diff --git a/grizzly/reduce/__main__.py b/grizzly/reduce/__main__.py index 83fcb407..4ce5d15c 100644 --- a/grizzly/reduce/__main__.py +++ b/grizzly/reduce/__main__.py @@ -4,6 +4,5 @@ """Grizzly reducer CLI. """ from . import ReduceManager -from .args import ReduceArgs -raise SystemExit(ReduceManager.main(ReduceArgs().parse_args())) +raise SystemExit(ReduceManager.main()) diff --git a/grizzly/reduce/bucket.py b/grizzly/reduce/bucket.py index 2cb428f4..dece4481 100644 --- a/grizzly/reduce/bucket.py +++ b/grizzly/reduce/bucket.py @@ -1,10 +1,22 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from ..replay.bucket import bucket_main +from .args import ReduceFuzzManagerIDQualityArgs +from .crash import main as crash_main -if __name__ == "__main__": - from ..replay.bucket import bucket_main - from .args import ReduceFuzzManagerIDQualityArgs - from .crash import main - raise SystemExit(bucket_main(ReduceFuzzManagerIDQualityArgs().parse_args(), main)) +def main() -> int: + """Wrapper for bucket_main() which is the CLI for `grizzly.reduce.bucket`. + + Arguments: + None + + Returns: + Exit.SUCCESS (0) for success otherwise a different Exit code is returned. + """ + return bucket_main(ReduceFuzzManagerIDQualityArgs().parse_args(), crash_main) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/grizzly/reduce/core.py b/grizzly/reduce/core.py index 1bbc6332..b52e8a99 100644 --- a/grizzly/reduce/core.py +++ b/grizzly/reduce/core.py @@ -39,6 +39,7 @@ ) from ..replay import ReplayManager, ReplayResult from ..target import AssetManager, Target, TargetLaunchError, TargetLaunchTimeout +from .args import ReduceArgs from .exceptions import GrizzlyReduceBaseException, NotReproducible from .strategies import STRATEGIES @@ -752,16 +753,17 @@ def report( self._status.last_reports = new_reports @classmethod - def main(cls, args: Namespace) -> int: + def main(cls, args: Namespace | None = None) -> int: """CLI for `grizzly.reduce`. Arguments: args: Result from `ReduceArgs.parse_args`. Returns: - 0 for success. non-0 indicates a problem. + Exit.SUCCESS (0) for success otherwise a different Exit code is returned. """ # pylint: disable=too-many-return-statements + args = args or ReduceArgs().parse_args() configure_logging(args.log_level) setlocale(LC_ALL, "") diff --git a/grizzly/reduce/crash.py b/grizzly/reduce/crash.py index e8b4c2d0..d04083e7 100644 --- a/grizzly/reduce/crash.py +++ b/grizzly/reduce/crash.py @@ -1,8 +1,10 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -from argparse import Namespace +from __future__ import annotations + from logging import getLogger +from typing import TYPE_CHECKING from ..common.fuzzmanager import load_fm_data from ..common.reporter import Quality @@ -11,18 +13,22 @@ from .args import ReduceFuzzManagerIDArgs from .core import ReduceManager +if TYPE_CHECKING: + from argparse import Namespace + LOG = getLogger(__name__) -def main(args: Namespace) -> int: +def main(args: Namespace | None = None) -> int: """CLI for `grizzly.reduce.crash`. Arguments: args: Result from `ReduceArgs.parse_args`. Returns: - 0 for success. non-0 indicates a problem. + Exit.SUCCESS (0) for success otherwise a different Exit code is returned. """ + args = args or ReduceFuzzManagerIDArgs().parse_args() configure_logging(args.log_level) with load_fm_data(args.input, load_bucket=not args.sig) as (crash, bucket): LOG.info( @@ -61,4 +67,4 @@ def main(args: Namespace) -> int: if __name__ == "__main__": - raise SystemExit(main(ReduceFuzzManagerIDArgs().parse_args())) + raise SystemExit(main()) diff --git a/grizzly/replay/__main__.py b/grizzly/replay/__main__.py index 9ae5fc9b..21122a44 100644 --- a/grizzly/replay/__main__.py +++ b/grizzly/replay/__main__.py @@ -1,7 +1,6 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -from .args import ReplayArgs from .replay import ReplayManager -raise SystemExit(ReplayManager.main(ReplayArgs().parse_args())) +raise SystemExit(ReplayManager.main()) diff --git a/grizzly/replay/bucket.py b/grizzly/replay/bucket.py index 4b728b2e..6782b23d 100644 --- a/grizzly/replay/bucket.py +++ b/grizzly/replay/bucket.py @@ -1,15 +1,19 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -from argparse import Namespace +from __future__ import annotations + from logging import getLogger -from typing import Callable +from typing import TYPE_CHECKING, Callable from ..common.fuzzmanager import Bucket from ..common.utils import Exit, configure_logging from .args import ReplayFuzzManagerIDQualityArgs from .crash import main as crash_main +if TYPE_CHECKING: + from argparse import Namespace + LOG = getLogger(__name__) @@ -21,7 +25,7 @@ def bucket_main(args: Namespace, tool_main: Callable[[Namespace], int]) -> int: tool_main: Main function from a supported Grizzly tool. Returns: - 0 for success. non-0 indicates a problem. + Exit.SUCCESS (0) for success otherwise a different Exit code is returned. """ assert callable(tool_main) configure_logging(args.log_level) @@ -49,7 +53,17 @@ def bucket_main(args: Namespace, tool_main: Callable[[Namespace], int]) -> int: return result +def main() -> int: + """Wrapper for bucket_main() which is the CLI for `grizzly.replay.bucket`. + + Arguments: + None + + Returns: + Exit.SUCCESS (0) for success otherwise a different Exit code is returned. + """ + return bucket_main(ReplayFuzzManagerIDQualityArgs().parse_args(), crash_main) + + if __name__ == "__main__": - raise SystemExit( - bucket_main(ReplayFuzzManagerIDQualityArgs().parse_args(), crash_main) - ) + raise SystemExit(main()) diff --git a/grizzly/replay/bugzilla.py b/grizzly/replay/bugzilla.py index d80c2507..7eec4e8c 100644 --- a/grizzly/replay/bugzilla.py +++ b/grizzly/replay/bugzilla.py @@ -1,31 +1,37 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -from argparse import Namespace +from __future__ import annotations + from logging import getLogger +from typing import TYPE_CHECKING from ..common.bugzilla import BugzillaBug -from ..common.utils import configure_logging +from ..common.utils import Exit, configure_logging from .args import ReplayFuzzBugzillaArgs from .replay import ReplayManager +if TYPE_CHECKING: + from argparse import Namespace + LOG = getLogger(__name__) -def main(args: Namespace) -> int: +def main(args: Namespace | None = None) -> int: """CLI for `grizzly.replay.bugzilla`. Arguments: args: Result from `ReplayArgs.parse_args`. Returns: - 0 for success. non-0 indicates a problem. + Exit.SUCCESS (0) for success otherwise a different Exit code is returned. """ + args = args or ReplayFuzzBugzillaArgs().parse_args() configure_logging(args.log_level) bug = BugzillaBug.load(args.input) if bug is None: LOG.info("Failed to load Bug %d from Bugzilla", args.input) - return 1 + return Exit.ERROR LOG.info("Loaded Bug %d from Bugzilla", args.input) with bug: args.asset.extend( @@ -35,10 +41,10 @@ def main(args: Namespace) -> int: testcases = bug.testcases() if not testcases: LOG.error("No test case data attached to bug %d", args.input) - return 1 + return Exit.ERROR args.input = testcases return ReplayManager.main(args) if __name__ == "__main__": - raise SystemExit(main(ReplayFuzzBugzillaArgs().parse_args())) + raise SystemExit(main()) diff --git a/grizzly/replay/crash.py b/grizzly/replay/crash.py index 7ddd0560..e78757b1 100644 --- a/grizzly/replay/crash.py +++ b/grizzly/replay/crash.py @@ -18,15 +18,16 @@ LOG = getLogger(__name__) -def main(args: Namespace) -> int: +def main(args: Namespace | None = None) -> int: """CLI for `grizzly.replay.crash`. Arguments: - args: Result from `ReplayArgs.parse_args`. + args: Result from `ReplayFuzzManagerIDArgs.parse_args`. Returns: - 0 for success. non-0 indicates a problem. + Exit.SUCCESS (0) for success otherwise a different Exit code is returned. """ + args = args or ReplayFuzzManagerIDArgs().parse_args() configure_logging(args.log_level) with load_fm_data(args.input, load_bucket=not args.sig) as (crash, bucket): LOG.info("Loaded crash %d from FuzzManager", crash.crash_id) @@ -38,7 +39,7 @@ def modify_args(args: Namespace, crash: CrashEntry, bucket: Bucket | None) -> Na """ Arguments: - args: Result from `ReplayArgs.parse_args`. + args: Result from `ReplayFuzzManagerIDArgs.parse_args`. crash: Crash entry to process. bucket: Bucket that contains crash. @@ -77,4 +78,4 @@ def modify_args(args: Namespace, crash: CrashEntry, bucket: Bucket | None) -> Na if __name__ == "__main__": - raise SystemExit(main(ReplayFuzzManagerIDArgs().parse_args())) + raise SystemExit(main()) diff --git a/grizzly/replay/replay.py b/grizzly/replay/replay.py index 43f96ec2..4b0602f4 100644 --- a/grizzly/replay/replay.py +++ b/grizzly/replay/replay.py @@ -40,6 +40,7 @@ TargetLaunchError, TargetLaunchTimeout, ) +from .args import ReplayArgs if TYPE_CHECKING: from argparse import Namespace @@ -600,8 +601,18 @@ def harness_fn(_: str) -> bytes: # pragma: no cover result.report.cleanup() @classmethod - def main(cls, args: Namespace) -> int: + def main(cls, args: Namespace | None = None) -> int: + """CLI for `grizzly.reduce`. + + Arguments: + args: Result from `ReplayArgs.parse_args`. + + Returns: + Exit.SUCCESS (0) for success otherwise a different Exit code is returned. + """ + args = args or ReplayArgs().parse_args() configure_logging(args.log_level) + LOG.info("Starting Grizzly Replay") LOG.debug("grizzly-framework version: %s", package_version("grizzly-framework")) diff --git a/grizzly/test_main.py b/grizzly/test_main.py index 2ee0a554..0465dcb5 100644 --- a/grizzly/test_main.py +++ b/grizzly/test_main.py @@ -2,7 +2,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. """test Grizzly main""" -from pathlib import Path from platform import system from pytest import mark, skip @@ -86,7 +85,7 @@ def test_main_02(mocker, session_setup, exit_code, to_raise): session_obj.run.side_effect = TargetLaunchError("test", mocker.Mock()) else: session_obj.run.side_effect = to_raise() - args = mocker.MagicMock(adapter="fake", binary=Path("bin"), time_limit=1, timeout=1) + args = mocker.MagicMock(adapter="fake", time_limit=1, timeout=1) assert main(args) == exit_code assert target_cls.return_value.cleanup.call_count == 1 @@ -111,7 +110,6 @@ def test_main_03(mocker, session_setup, test_limit, timeout): # no_harness=False for code coverage args = mocker.MagicMock( adapter="fake", - binary=Path("bin"), no_harness=False, time_limit=test_limit, timeout=timeout, @@ -142,7 +140,6 @@ def test_main_04( assert sum((pernosco, rr, valgrind)) < 2, "test broken!" args = mocker.MagicMock( adapter="fake", - binary=Path("bin"), pernosco=pernosco, rr=rr, time_limit=1, @@ -162,7 +159,6 @@ def test_main_05(mocker, session_setup): target_cls.return_value.https.return_value = False args = mocker.MagicMock( adapter="fake", - binary=Path("bin"), use_http=False, time_limit=1, timeout=1, diff --git a/setup.cfg b/setup.cfg index abb01e3c..4fee5c18 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,7 +44,15 @@ zip_safe = False [options.entry_points] console_scripts = - grizzly.status = grizzly.common.status_reporter:main + grizzly = grizzly:__main__ + grizzly-reduce = grizzly.reduce:__main__ + grizzly-reduce-bucket = grizzly.reduce.bucket:main + grizzly-reduce-crash = grizzly.reduce.crash:main + grizzly-replay = grizzly.replay:__main__ + grizzly-replay-bucket = grizzly.replay.bucket:main + grizzly-replay-bugzilla = grizzly.replay.bugzilla:main + grizzly-replay-crash = grizzly.replay.crash:main + grizzly-status = grizzly.common.status_reporter:main grizzly_adapters = no-op = grizzly.adapter.no_op_adapter:NoOpAdapter grizzly_targets =