diff --git a/docs/README.md b/docs/README.md index 27176ee..67e3ef6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,10 +17,10 @@ pip install safe-ds-runner ## Usage -Launch the runner: +Start the runner server: ```shell -safe-ds-runner +safe-ds-runner start ``` ## Documentation diff --git a/pyproject.toml b/pyproject.toml index 3150e4e..d8b33e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ packages = [ ] [tool.poetry.scripts] -safe-ds-runner = "safeds_runner.server.main:main" +safe-ds-runner = "safeds_runner.main:main" [tool.poetry.dependencies] python = "^3.11,<3.13" diff --git a/src/safeds_runner/cli/__init__.py b/src/safeds_runner/cli/__init__.py new file mode 100644 index 0000000..84688d4 --- /dev/null +++ b/src/safeds_runner/cli/__init__.py @@ -0,0 +1,5 @@ +"""The command line interface of the application.""" + +from ._cli import cli + +__all__ = ["cli"] diff --git a/src/safeds_runner/cli/_cli.py b/src/safeds_runner/cli/_cli.py new file mode 100644 index 0000000..6d23ea0 --- /dev/null +++ b/src/safeds_runner/cli/_cli.py @@ -0,0 +1,47 @@ +import argparse +import logging +from importlib.metadata import version + +from safeds_runner.server.main import start_server + + +class Commands: + START = "start" + + +def cli() -> None: # pragma: no cover + """Run the application via the command line.""" + args = _get_args() + + # Set logging level + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.INFO) + + # Run command + match args.command: + case Commands.START: + start_server(args.port) + + +def _get_args() -> argparse.Namespace: # pragma: no cover + parser = argparse.ArgumentParser(description="Execute Safe-DS programs that were compiled to Python.") + parser.add_argument( + "-V", + "--version", + action="version", + version=f"%(prog)s {version('safe-ds-runner')}", + ) + parser.add_argument("-v", "--verbose", help="increase logging verbosity", action="store_true") + + # Commands + subparsers = parser.add_subparsers(dest="command") + _add_start_subparser(subparsers) + + return parser.parse_args() + + +def _add_start_subparser(subparsers: argparse._SubParsersAction) -> None: # pragma: no cover + parser = subparsers.add_parser(Commands.START, help="start the Safe-DS Runner server") + parser.add_argument("-p", "--port", type=int, default=5000, help="the port to use") diff --git a/src/safeds_runner/main.py b/src/safeds_runner/main.py new file mode 100644 index 0000000..ffce851 --- /dev/null +++ b/src/safeds_runner/main.py @@ -0,0 +1,8 @@ +"""The main entry point of the application.""" + +from safeds_runner.cli import cli + + +def main() -> None: # pragma: no cover + """Run the application.""" + cli() diff --git a/src/safeds_runner/server/main.py b/src/safeds_runner/server/main.py index 31dadd7..4266feb 100644 --- a/src/safeds_runner/server/main.py +++ b/src/safeds_runner/server/main.py @@ -1,9 +1,7 @@ """Module containing the main entry point, for starting the Safe-DS runner.""" -import argparse import json import logging -from importlib.metadata import version import flask.app import flask_sock @@ -162,12 +160,8 @@ def send_websocket_message(connection: simple_websocket.Server, message: Message connection.send(json.dumps(message.to_dict())) -def main() -> None: # pragma: no cover - """ - Execute the runner application. - - Main entry point of the runner application. - """ +def start_server(port: int) -> None: # pragma: no cover + """Start the Safe-DS Runner server.""" # Allow prints to be unbuffered by default import builtins import functools @@ -177,19 +171,6 @@ def main() -> None: # pragma: no cover logging.getLogger().setLevel(logging.DEBUG) from gevent.pywsgi import WSGIServer - parser = argparse.ArgumentParser(description="Start Safe-DS Runner on a specific port.") - parser.add_argument("--port", type=int, default=5000, help="Port on which to run the python server.") - parser.add_argument( - "-V", - "--version", - action="version", - version="%(prog)s {version}".format(version=version("safe-ds-runner")), - ) - args = parser.parse_args() - logging.info("Starting Safe-DS Runner on port %s", str(args.port)) + logging.info("Starting Safe-DS Runner on port %s", str(port)) # Only bind to host=127.0.0.1. Connections from other devices should not be accepted - WSGIServer(("127.0.0.1", args.port), app).serve_forever() - - -if __name__ == "__main__": - main() # pragma: no cover + WSGIServer(("127.0.0.1", port), app).serve_forever() diff --git a/tests/safeds_runner/server/test_runner_main.py b/tests/safeds_runner/server/test_runner_main.py index 8e4cd79..0e69e22 100644 --- a/tests/safeds_runner/server/test_runner_main.py +++ b/tests/safeds_runner/server/test_runner_main.py @@ -7,7 +7,7 @@ def test_should_runner_start_successfully() -> None: - process = subprocess.Popen(["poetry", "run", "safe-ds-runner"], cwd=_project_root, stderr=subprocess.PIPE) + process = subprocess.Popen(["poetry", "run", "safe-ds-runner", "start"], cwd=_project_root, stderr=subprocess.PIPE) while process.poll() is None: process_line = str(typing.cast(IO[bytes], process.stderr).readline(), "utf-8").strip() # Wait for first line of log