Skip to content

Commit

Permalink
style: apply automated linter fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
megalinter-bot committed Nov 21, 2023
1 parent 5d4d2b0 commit 33aa2f6
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 63 deletions.
38 changes: 21 additions & 17 deletions src/safeds_runner/server/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Module containing the main entry point, for starting the Safe-DS runner."""
import argparse

import argparse
import json
import logging
import typing
Expand All @@ -12,12 +12,16 @@
from flask_sock import Sock

from safeds_runner.server import messages
from safeds_runner.server.pipeline_manager import execute_pipeline, get_placeholder, set_new_websocket_target, \
setup_pipeline_execution
from safeds_runner.server.pipeline_manager import (
execute_pipeline,
get_placeholder,
set_new_websocket_target,
setup_pipeline_execution,
)

app = Flask(__name__)
# Websocket Configuration
app.config['SOCK_SERVER_OPTIONS'] = {'ping_interval': 25}
app.config["SOCK_SERVER_OPTIONS"] = {"ping_interval": 25}
sock = Sock(app)
# Allow access from VSCode extension
CORS(app, resources={r"/*": {"origins": "vscode-webview://*"}})
Expand Down Expand Up @@ -47,27 +51,27 @@ def ws_run_program(ws: simple_websocket.Server) -> None:
try:
received_object: dict[str, Any] = json.loads(received_message)
except json.JSONDecodeError:
logging.warn("Invalid message received: %s", received_message)
logging.warning("Invalid message received: %s", received_message)
ws.close(None)
return
if "type" not in received_object:
logging.warn("No message type specified in: %s", received_message)
logging.warning("No message type specified in: %s", received_message)
ws.close(None)
return
if "id" not in received_object:
logging.warn("No message id specified in: %s", received_message)
logging.warning("No message id specified in: %s", received_message)
ws.close(None)
return
if "data" not in received_object:
logging.warn("No message data specified in: %s", received_message)
logging.warning("No message data specified in: %s", received_message)
ws.close(None)
return
if not isinstance(received_object["type"], str):
logging.warn("Message type is not a string: %s", received_message)
logging.warning("Message type is not a string: %s", received_message)
ws.close(None)
return
if not isinstance(received_object["id"], str):
logging.warn("Message id is not a string: %s", received_message)
logging.warning("Message id is not a string: %s", received_message)
ws.close(None)
return
request_data = received_object["data"]
Expand All @@ -77,17 +81,17 @@ def ws_run_program(ws: simple_websocket.Server) -> None:
case "program":
valid, invalid_message = messages.validate_program_message(request_data)
if not valid:
logging.warn("Invalid message data specified in: %s (%s)", received_message, invalid_message)
logging.warning("Invalid message data specified in: %s (%s)", received_message, invalid_message)
ws.close(None)
return
code = request_data["code"]
main = request_data["main"]

Check warning on line 88 in src/safeds_runner/server/main.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/main.py#L49-L88

Added lines #L49 - L88 were not covered by tests
# This should only be called from the extension as it is a security risk
execute_pipeline(code, main['package'], main['module'], main['pipeline'], execution_id)
execute_pipeline(code, main["package"], main["module"], main["pipeline"], execution_id)
case "placeholder_query":
valid, invalid_message = messages.validate_placeholder_query_message(request_data)
if not valid:
logging.warn("Invalid message data specified in: %s (%s)", received_message, invalid_message)
logging.warning("Invalid message data specified in: %s (%s)", received_message, invalid_message)
ws.close(None)
return
placeholder_type, placeholder_value = get_placeholder(execution_id, request_data)
Expand All @@ -98,7 +102,7 @@ def ws_run_program(ws: simple_websocket.Server) -> None:
send_websocket_value(ws, request_data, "", "")
case _:
if message_type not in messages.message_types:
logging.warn("Invalid message type: %s", message_type)
logging.warning("Invalid message type: %s", message_type)

Check warning on line 105 in src/safeds_runner/server/main.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/main.py#L102-L105

Added lines #L102 - L105 were not covered by tests


def send_websocket_value(connection: simple_websocket.Server, name: str, var_type: str, value: str) -> None:
Expand Down Expand Up @@ -127,18 +131,18 @@ def send_websocket_message(connection: simple_websocket.Server, msg_type: str, m

if __name__ == "__main__":
# Allow prints to be unbuffered by default
import functools
import builtins
import functools

Check warning on line 135 in src/safeds_runner/server/main.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/main.py#L134-L135

Added lines #L134 - L135 were not covered by tests

builtins.print = functools.partial(print, flush=True) # type: ignore[assignment]

Check warning on line 137 in src/safeds_runner/server/main.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/main.py#L137

Added line #L137 was not covered by tests

logging.getLogger().setLevel(logging.DEBUG)
from gevent.pywsgi import WSGIServer

Check warning on line 140 in src/safeds_runner/server/main.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/main.py#L139-L140

Added lines #L139 - L140 were not covered by tests

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("--port", type=int, default=5000, help="Port on which to run the python server.")
args = parser.parse_args()
setup_pipeline_execution()
logging.info("Starting Safe-DS Runner on port %s", str(args.port))

Check warning on line 146 in src/safeds_runner/server/main.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/main.py#L142-L146

Added lines #L142 - L146 were not covered by tests
# 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()
WSGIServer(("127.0.0.1", args.port), app).serve_forever()

Check warning on line 148 in src/safeds_runner/server/main.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/main.py#L148

Added line #L148 was not covered by tests
22 changes: 16 additions & 6 deletions src/safeds_runner/server/messages.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
"""Module that contains functions for creating and validating messages exchanged with the vscode extension."""

import typing

message_types = ["program", "placeholder_query", "placeholder", "placeholder_value", "runtime_error",
"runtime_progress"]
message_types = [
"program",
"placeholder_query",
"placeholder",
"placeholder_value",
"runtime_error",
"runtime_progress",
]


def create_placeholder_description(name: str, placeholder_type: str) -> dict[str, typing.Any]:
Expand All @@ -25,16 +32,19 @@ def create_runtime_progress_done() -> str:
return "done"

Check warning on line 32 in src/safeds_runner/server/messages.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/messages.py#L32

Added line #L32 was not covered by tests


def validate_program_message(message_data: dict[str, typing.Any] | str) -> typing.Tuple[bool, str | None]:
def validate_program_message(message_data: dict[str, typing.Any] | str) -> tuple[bool, str | None]:
"""Validate the message data of a program message."""
if not isinstance(message_data, dict):
return False, "Message data is not a JSON object"
if "code" not in message_data:
return False, "No 'code' parameter given"
if "main" not in message_data:
return False, "No 'main' parameter given"
if "package" not in message_data["main"] or "module" not in message_data["main"] or "pipeline" not in message_data[
"main"]:
if (

Check warning on line 43 in src/safeds_runner/server/messages.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/messages.py#L37-L43

Added lines #L37 - L43 were not covered by tests
"package" not in message_data["main"]
or "module" not in message_data["main"]
or "pipeline" not in message_data["main"]
):
return False, "Invalid 'main' parameter given"
if len(message_data["main"]) != 3:
return False, "Invalid 'main' parameter given"
Expand All @@ -61,7 +71,7 @@ def validate_program_message(message_data: dict[str, typing.Any] | str) -> typin
return True, None

Check warning on line 71 in src/safeds_runner/server/messages.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/messages.py#L48-L71

Added lines #L48 - L71 were not covered by tests


def validate_placeholder_query_message(message_data: dict[str, typing.Any] | str) -> typing.Tuple[bool, str | None]:
def validate_placeholder_query_message(message_data: dict[str, typing.Any] | str) -> tuple[bool, str | None]:
"""Validate the message data of a placeholder query message."""
if not isinstance(message_data, str):
return False, "Message data is not a string"
Expand Down
34 changes: 22 additions & 12 deletions src/safeds_runner/server/module_manager.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
"""Module that contains the infrastructure for finding and loading modules in-memory."""

import importlib.abc
import importlib.util
import logging
import sys
import types
import typing
from abc import ABC
from importlib.machinery import ModuleSpec
import sys
import importlib.util
import types
import logging


class InMemoryLoader(importlib.abc.SourceLoader, ABC):
"""Load a virtual python module from a byte array and a filename."""

def __init__(self, code_bytes: bytes, filename: str):
"""
Create a new in-memory loader.
Expand Down Expand Up @@ -43,6 +44,7 @@ def get_filename(self, _fullname: str) -> str:

class InMemoryFinder(importlib.abc.MetaPathFinder):
"""Find python modules in an in-memory dictionary."""

def __init__(self, code: dict[str, dict[str, str]]):
"""
Create a new in-memory finder.
Expand All @@ -52,7 +54,7 @@ def __init__(self, code: dict[str, dict[str, str]]):
"""
self.code = code
self.allowed_packages = set(code.keys())
self.imports_to_remove: typing.Set[str] = set()
self.imports_to_remove: set[str] = set()
for key in code:
self._add_possible_packages_for_package_path(key)

Check warning on line 59 in src/safeds_runner/server/module_manager.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/module_manager.py#L55-L59

Added lines #L55 - L59 were not covered by tests

Expand All @@ -61,8 +63,9 @@ def _add_possible_packages_for_package_path(self, package_path: str) -> None:
package_path = package_path.rpartition(".")[0]
self.allowed_packages.add(package_path)

Check warning on line 64 in src/safeds_runner/server/module_manager.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/module_manager.py#L62-L64

Added lines #L62 - L64 were not covered by tests

def find_spec(self, fullname: str, path: typing.Sequence[str] | None = None,
target: types.ModuleType | None = None) -> ModuleSpec | None:
def find_spec(
self, fullname: str, path: typing.Sequence[str] | None = None, target: types.ModuleType | None = None,
) -> ModuleSpec | None:
"""
Find a module which may be registered in the code dictionary.
Expand All @@ -74,7 +77,8 @@ def find_spec(self, fullname: str, path: typing.Sequence[str] | None = None,
logging.debug("Find Spec: %s %s %s", fullname, path, target)
if fullname in self.allowed_packages:
parent_package = importlib.util.spec_from_loader(

Check warning on line 79 in src/safeds_runner/server/module_manager.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/module_manager.py#L77-L79

Added lines #L77 - L79 were not covered by tests
fullname, loader=InMemoryLoader("".encode("utf-8"), fullname.replace(".", "/")))
fullname, loader=InMemoryLoader(b"", fullname.replace(".", "/")),
)
if parent_package is None:
return None
if parent_package.submodule_search_locations is None:
Expand All @@ -86,15 +90,21 @@ def find_spec(self, fullname: str, path: typing.Sequence[str] | None = None,
if len(module_path) == 1 and "" in self.code and fullname in self.code[""]:
self.imports_to_remove.add(fullname)
return importlib.util.spec_from_loader(

Check warning on line 92 in src/safeds_runner/server/module_manager.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/module_manager.py#L82-L92

Added lines #L82 - L92 were not covered by tests
fullname, loader=InMemoryLoader(self.code[""][fullname].encode("utf-8"), fullname.replace(".", "/")),
origin="")
fullname,
loader=InMemoryLoader(self.code[""][fullname].encode("utf-8"), fullname.replace(".", "/")),
origin="",
)
parent_package_path = ".".join(module_path[:-1])
submodule_name = module_path[-1]
if parent_package_path in self.code and submodule_name in self.code[parent_package_path]:
self.imports_to_remove.add(fullname)
return importlib.util.spec_from_loader(

Check warning on line 101 in src/safeds_runner/server/module_manager.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/module_manager.py#L97-L101

Added lines #L97 - L101 were not covered by tests
fullname, loader=InMemoryLoader(self.code[parent_package_path][submodule_name].encode("utf-8"),
fullname.replace(".", "/")), origin=parent_package_path)
fullname,
loader=InMemoryLoader(
self.code[parent_package_path][submodule_name].encode("utf-8"), fullname.replace(".", "/"),
),
origin=parent_package_path,
)
return None

Check warning on line 108 in src/safeds_runner/server/module_manager.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/module_manager.py#L108

Added line #L108 was not covered by tests

def attach(self) -> None:
Expand Down
35 changes: 23 additions & 12 deletions src/safeds_runner/server/pipeline_manager.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
"""Module that contains the infrastructure for pipeline execution in child processes."""

import queue
import json
import logging
import multiprocessing
import queue
import runpy
import threading
import json
import typing
import runpy
from multiprocessing.managers import SyncManager

import simple_websocket
import stack_data
import logging

from safeds_runner.server.module_manager import InMemoryFinder

Expand Down Expand Up @@ -54,7 +54,7 @@ def _handle_queue_messages() -> None:
if websocket_target is not None:
websocket_target.send(json.dumps(message))
except BaseException as error: # noqa: BLE001
logging.warn("Message queue terminated: %s", error.__repr__())
logging.warning("Message queue terminated: %s", error.__repr__())

Check warning on line 57 in src/safeds_runner/server/pipeline_manager.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/pipeline_manager.py#L55-L57

Added lines #L55 - L57 were not covered by tests


def set_new_websocket_target(ws: simple_websocket.Server) -> None:
Expand All @@ -69,8 +69,17 @@ def set_new_websocket_target(ws: simple_websocket.Server) -> None:

class PipelineProcess:
"""Represent a process that executes a Safe-DS pipeline."""
def __init__(self, code: dict[str, dict[str, str]], sdspackage: str, sdsmodule: str, sdspipeline: str,
execution_id: str, messages_queue: queue.Queue, placeholder_map: dict[str, typing.Any]):

def __init__(
self,
code: dict[str, dict[str, str]],
sdspackage: str,
sdsmodule: str,
sdspipeline: str,
execution_id: str,
messages_queue: queue.Queue,
placeholder_map: dict[str, typing.Any],
):
"""
Create a new process which will execute the given pipeline, when started.
Expand Down Expand Up @@ -142,8 +151,9 @@ def get_backtrace_info(error: BaseException) -> list[dict[str, typing.Any]]:
return backtrace_list

Check warning on line 151 in src/safeds_runner/server/pipeline_manager.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/pipeline_manager.py#L148-L151

Added lines #L148 - L151 were not covered by tests


def execute_pipeline(code: dict[str, dict[str, str]], sdspackage: str, sdsmodule: str, sdspipeline: str,
exec_id: str) -> None:
def execute_pipeline(
code: dict[str, dict[str, str]], sdspackage: str, sdsmodule: str, sdspipeline: str, exec_id: str,
) -> None:
"""
Run a Safe-DS pipeline.
Expand All @@ -156,8 +166,9 @@ def execute_pipeline(code: dict[str, dict[str, str]], sdspackage: str, sdsmodule
if global_placeholder_map is not None and global_messages_queue is not None and multiprocessing_manager is not None:
if exec_id not in global_placeholder_map:
global_placeholder_map[exec_id] = multiprocessing_manager.dict()
process = PipelineProcess(code, sdspackage, sdsmodule, sdspipeline, exec_id, global_messages_queue,
global_placeholder_map[exec_id])
process = PipelineProcess(
code, sdspackage, sdsmodule, sdspipeline, exec_id, global_messages_queue, global_placeholder_map[exec_id],
)
process.execute()


Expand All @@ -181,7 +192,7 @@ def _get_placeholder_type(value: typing.Any) -> str:
return "Any"

Check warning on line 192 in src/safeds_runner/server/pipeline_manager.py

View check run for this annotation

Codecov / codecov/patch

src/safeds_runner/server/pipeline_manager.py#L182-L192

Added lines #L182 - L192 were not covered by tests


def get_placeholder(exec_id: str, placeholder_name: str) -> typing.Tuple[str | None, typing.Any]:
def get_placeholder(exec_id: str, placeholder_name: str) -> tuple[str | None, typing.Any]:
"""
Get a placeholder type and value for an execution id and placeholder name.
Expand Down
49 changes: 33 additions & 16 deletions tests/safeds_runner/server/test_execute_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,38 @@

def test_execute_pipeline() -> None:
setup_pipeline_execution()
execute_pipeline({
"": {
"gen_b": "import safeds_runner.codegen\n\ndef c():\n\ta1 = 1\n\ta2 = safeds_runner.codegen.eager_or(True, False)\n\tprint('test')\n\tprint('dynamic pipeline output')\n\treturn a1 + a2\n",
"gen_b_c": "from gen_b import c\n\nif __name__ == '__main__':\n\tc()"}
}, "a.test", "b", "c", "test-id")

execute_pipeline({
"": {
"gen_b": "import safeds_runner.codegen\nfrom a.stub import u\nfrom v.u.s.testing import add1\n\ndef c():\n\ta1 = 1\n\ta2 = safeds_runner.codegen.eager_or(True, False)\n\tprint('test2')\n\tprint('new dynamic output')\n\tprint(f'Add1: {add1(1, 2)}')\n\treturn a1 + a2\n",
"gen_b_c": "from gen_b import c\n\nif __name__ == '__main__':\n\tc()"
execute_pipeline(
{
"": {
"gen_b": (
"import safeds_runner.codegen\n\ndef c():\n\ta1 = 1\n\ta2 = safeds_runner.codegen.eager_or(True,"
" False)\n\tprint('test')\n\tprint('dynamic pipeline output')\n\treturn a1 + a2\n"
),
"gen_b_c": "from gen_b import c\n\nif __name__ == '__main__':\n\tc()",
},
},
"a": {
"stub": "def u():\n\treturn 1"
"a.test",
"b",
"c",
"test-id",
)

execute_pipeline(
{
"": {
"gen_b": (
"import safeds_runner.codegen\nfrom a.stub import u\nfrom v.u.s.testing import add1\n\ndef"
" c():\n\ta1 = 1\n\ta2 = safeds_runner.codegen.eager_or(True,"
" False)\n\tprint('test2')\n\tprint('new dynamic output')\n\tprint(f'Add1: {add1(1, 2)}')\n\treturn"
" a1 + a2\n"
),
"gen_b_c": "from gen_b import c\n\nif __name__ == '__main__':\n\tc()",
},
"a": {"stub": "def u():\n\treturn 1"},
"v.u.s": {"testing": "import a.stub;\n\ndef add1(v1, v2):\n\treturn v1 + v2 + a.stub.u()\n"},
},
"v.u.s": {
"testing": "import a.stub;\n\ndef add1(v1, v2):\n\treturn v1 + v2 + a.stub.u()\n"
}
}, "a.test", "b", "c", "test-id2")
"a.test",
"b",
"c",
"test-id2",
)

0 comments on commit 33aa2f6

Please sign in to comment.