Skip to content
Merged

dev #605

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 82 additions & 33 deletions speedwagon/config/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
import collections.abc
import os
import io
import logging
import pathlib
from typing import (
Optional,
Any,
Callable,
Dict,
Iterator,
List,
TYPE_CHECKING,
Callable,
Optional,
Protocol,
TextIO,
Any,
Iterator,
TYPE_CHECKING,
)

import yaml
Expand Down Expand Up @@ -45,6 +48,8 @@
"AbsWorkflowBackend"
]

logger = logging.getLogger(__name__)


class AbsSettingsSerializer(abc.ABC): # pylint: disable=R0903
@abc.abstractmethod
Expand Down Expand Up @@ -411,39 +416,83 @@
raise speedwagon.exceptions.MissingConfiguration(
f"Workflow '{workflow_name}' not found in YAML data."
)
return parse_workflow_yaml_config(yaml_data[workflow_name])

Check warning on line 419 in speedwagon/config/workflow.py

View check run for this annotation

Jenkins - UIUCLibrary / MyPy Coverage

Not covered line

Line 419 is not covered by tests
except yaml.YAMLError as yaml_error:
raise speedwagon.exceptions.FileFormatError(
"Error parsing YAML file"
) from yaml_error


def parse_workflow_yaml_config(
workflow_config: List[Dict[str, str]]
) -> SettingsData:
try:
for section in workflow_config:
if any((("name" not in section), ("value" not in section))):
raise speedwagon.exceptions.FileFormatError(
f"{section} missing 'name' or 'value'"
)
except (
TypeError,
speedwagon.exceptions.FileFormatError,
) as format_error:
raise speedwagon.exceptions.FileFormatError(
"Config file format not valid"
) from format_error

return {entry["name"]: entry["value"] for entry in workflow_config}


def get_workflow_config_from_yaml_ignore_incomplete(
fp: TextIO, workflow_name: str
) -> SettingsData:
start_point = fp.tell()
fp.seek(0)
res = fp.read()
if res.strip() == "":
return {}
fp.seek(start_point)
try:
yaml_data = yaml.safe_load(fp)
if workflow_name not in yaml_data:

Check warning on line 457 in speedwagon/config/workflow.py

View check run for this annotation

Jenkins - UIUCLibrary / MyPy Coverage

Not covered lines

Lines 456-457 are not covered by tests
logger.debug(
"Workflow '%s' not found in YAML data.", workflow_name
)
return {}
workflow_config = yaml_data[workflow_name]
try:
for section in workflow_config:
if any(
(
("name" not in section),
("value" not in section)
)
):
raise speedwagon.exceptions.FileFormatError(
f"{section} missing 'name' or 'value'"
)
except (
TypeError,
speedwagon.exceptions.FileFormatError,
) as format_error:
raise speedwagon.exceptions.FileFormatError(
"Config file format not valid"
) from format_error
if not workflow_config:
return {}
return parse_workflow_yaml_config(workflow_config)

Check warning on line 465 in speedwagon/config/workflow.py

View check run for this annotation

Jenkins - UIUCLibrary / MyPy Coverage

Not covered lines

Lines 462-465 are not covered by tests

return {
entry['name']: entry['value']
for entry in workflow_config
}
except yaml.YAMLError as yaml_error:
raise speedwagon.exceptions.FileFormatError(
"Error parsing YAML file"
) from yaml_error


def get_workflow_options(yml_file: str, workflow_name: str) -> SettingsData:
with open(yml_file, "r", encoding="utf-8") as fp:
return speedwagon.config.workflow.get_workflow_config_from_yaml_file(
fp,
workflow_name
)
# pylint: disable-next=too-few-public-methods
class GetWorkflowOptionsFromYamlProtocol(Protocol):
"""Protocol for getting workflow options from YAML file."""

Check warning on line 475 in speedwagon/config/workflow.py

View check run for this annotation

Jenkins - UIUCLibrary / PyDocStyle

D204

HIGH: 1 blank line required after class docstring (found 0)
def __call__(self, fp: TextIO, workflow_name: str) -> SettingsData:
...


def get_workflow_options(
yml_file: str,
workflow_name: str,
strategy: GetWorkflowOptionsFromYamlProtocol = (
get_workflow_config_from_yaml_file
),
allow_missing: bool = False,
) -> SettingsData:
try:
with pathlib.Path(yml_file).open("r", encoding="utf-8") as fp:
return strategy(
fp,
workflow_name
)
except FileNotFoundError:
if allow_missing:
logger.warning('Expected config file missing: %s', yml_file)
return {}
raise
51 changes: 38 additions & 13 deletions speedwagon/frontend/qtwidgets/gui_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
)
from speedwagon.config.common import DEFAULT_CONFIG_DIRECTORY_NAME
from speedwagon.config import plugins as plugin_config
from speedwagon.config.workflow import WORKFLOWS_SETTINGS_YML_FILE_NAME
from speedwagon.config import workflow as workflow_config
from speedwagon.utils import (
get_desktop_path,
validate_user_input,
Expand Down Expand Up @@ -91,6 +91,8 @@

T = TypeVar("T")

module_logger = logging.getLogger(__name__)

system_info_report_formatters: DefaultDict[
str, Callable[[info.SystemInfo], str]
] = collections.defaultdict(
Expand Down Expand Up @@ -686,6 +688,27 @@
parent.logger.error("Failed to load workflow. Reason: %s", error)


def get_workflow_options_if_config_file_exists(
config_file: str,
workflow_name: str
) -> SettingsData:

if not os.path.exists(config_file):
module_logger.debug(
"Unable to locate workflow settings. "
'Reason: "%s" file does not exist.',
config_file,
)
return {}
return speedwagon.config.workflow.get_workflow_options(

Check warning on line 703 in speedwagon/frontend/qtwidgets/gui_startup.py

View check run for this annotation

Jenkins - UIUCLibrary / Test Coverage

Not covered lines

Lines 696-703 are not covered by tests
config_file,
workflow_name,
strategy=(
workflow_config.get_workflow_config_from_yaml_ignore_incomplete
)
)


class StartQtThreaded(GuiStarter):
"""Start a Qt Widgets base app using threads for job workers."""

Expand Down Expand Up @@ -766,16 +789,17 @@
source_application_settings=settings_resolver,
workflow_backend=factory,
)
config_file = os.path.join(
self.config_files_locator.get_app_data_dir(),
workflow_config.WORKFLOWS_SETTINGS_YML_FILE_NAME,
)

self.get_workflow_options_strategy = lambda workflow_name: (
speedwagon.config.workflow.get_workflow_options(
os.path.join(
self.config_files_locator.get_app_data_dir(),
WORKFLOWS_SETTINGS_YML_FILE_NAME,
),
workflow_name,
self.get_workflow_options_strategy =\
lambda workflow: get_workflow_options_if_config_file_exists(
config_file,
workflow
)
)

self.get_plugin_data_strategy = lambda: (
runner_strategies.get_plugin_data(
self.config_files_locator.get_config_file()
Expand Down Expand Up @@ -1244,7 +1268,7 @@
return speedwagon.config.workflow.get_workflow_options(
os.path.join(
config_files_locator.get_app_data_dir(),
speedwagon.config.workflow.WORKFLOWS_SETTINGS_YML_FILE_NAME,
workflow_config.WORKFLOWS_SETTINGS_YML_FILE_NAME,
),
workflow_name,
)
Expand Down Expand Up @@ -1344,7 +1368,7 @@
] = None
self.options: typing.Optional[SettingsData] = None
self.workflow: typing.Optional[AbsWorkflow] = None
self.logger = logger or logging.getLogger(__name__)
self.logger = logger or logging.getLogger()

def load_json_string(self, data: str) -> None:
"""Load json data containing options and workflow info.
Expand Down Expand Up @@ -1446,7 +1470,7 @@
dialog_box
)
)
dialog_box.attach_logger(job_manager.logger)
dialog_box.attach_logger(self.logger)

job_manager.workflow_loader_strategy = self.load_workflow_strategy

Expand Down Expand Up @@ -1627,7 +1651,8 @@
) -> QtWidgets.QDialog:
settings_builder = LocalSettingsBuilder()
workflow_settings_yaml = os.path.join(
settings_locator.get_app_data_dir(), WORKFLOWS_SETTINGS_YML_FILE_NAME
settings_locator.get_app_data_dir(),
workflow_config.WORKFLOWS_SETTINGS_YML_FILE_NAME
)
settings_builder.on_open_config_dir = functools.partial(
dialog.settings.open_settings_dir,
Expand Down
3 changes: 2 additions & 1 deletion speedwagon/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,8 @@ def run(
[
WorkerLogHandler(
lambda record: callbacks.log(
text=record.message, level=record.levelno
text=record.getMessage(),
level=record.levelno
)
)
],
Expand Down
25 changes: 15 additions & 10 deletions speedwagon/startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@
import speedwagon.job
import speedwagon.config
import speedwagon.info
from speedwagon.config.workflow import (
default_backend_factory,
AbsWorkflowBackend,
)
from speedwagon.config import workflow as workflow_config

from speedwagon.config.plugins import (
get_whitelisted_plugins_from_config_data,
read_settings_data_plugins
Expand Down Expand Up @@ -232,7 +230,7 @@
)

config_backend_factory = functools.partial(
speedwagon.config.workflow.default_backend_factory,
workflow_config.default_backend_factory,
config_directory_name=self.application_config_directory_name,
)
self.strategy = strategy or StartQtThreaded(
Expand All @@ -256,7 +254,7 @@
"""Run Speedwagon."""
self.strategy.set_application_name(self.application_name)
config_backend = functools.partial(
default_backend_factory,
workflow_config.default_backend_factory,
config_directory_name=self.application_config_directory_name,
)

Expand Down Expand Up @@ -397,14 +395,18 @@
try:
default_yaml_file_name =\
speedwagon.config.workflow.WORKFLOWS_SETTINGS_YML_FILE_NAME
config_loader_strategy =\
workflow_config.get_workflow_config_from_yaml_ignore_incomplete
startup_strategy.get_workflow_options_strategy = (
lambda workflow_name: (
speedwagon.config.workflow.get_workflow_options(
workflow_config.get_workflow_options(
os.path.join(
config_file_locator.get_app_data_dir(),
default_yaml_file_name,
),
workflow_name,
allow_missing=True,
strategy=config_loader_strategy,
)
)
)
Expand Down Expand Up @@ -550,7 +552,10 @@
"""

def set_workflow_config_backend_factory( # noqa: B027
self, factory: Callable[[speedwagon.job.Workflow], AbsWorkflowBackend]
self,
factory: Callable[
[speedwagon.job.Workflow], workflow_config.AbsWorkflowBackend
]
) -> None:
"""Set the workflow config backend factory.

Expand Down Expand Up @@ -588,10 +593,10 @@
config_files_locator = config_files_locator or StandardConfigFileLocator(
config_directory_prefix=DEFAULT_CONFIG_DIRECTORY_NAME
)
return speedwagon.config.workflow.get_workflow_options(
return workflow_config.get_workflow_options(

Check warning on line 596 in speedwagon/startup.py

View check run for this annotation

Jenkins - UIUCLibrary / Test Coverage

Not covered line

Line 596 is not covered by tests
os.path.join(
config_files_locator.get_app_data_dir(),
speedwagon.config.workflow.WORKFLOWS_SETTINGS_YML_FILE_NAME,
workflow_config.WORKFLOWS_SETTINGS_YML_FILE_NAME,
),
workflow_name,
)
Expand Down
Loading
Loading