Skip to content
Merged
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
11 changes: 11 additions & 0 deletions fireworks/core/firework.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,10 @@ def __init__(
created_on (datetime): time of creation
updated_on (datetime): time of update
fw_states (dict): leave this alone unless you are purposefully creating a Lazy-style WF.

Raises:
ValueError: when Firework IDs are duplicated or inconsistent,
or links dictionary is invalid
"""
name = name or "unnamed WF" # prevent None names

Expand Down Expand Up @@ -894,6 +898,9 @@ def apply_action(self, action: FWAction, fw_id: int) -> list[int]:

Returns:
list[int]: list of Firework ids that were updated or new.

Raises:
ValueError: when duplicated Firework IDs are found in additions or detours
"""
updated_ids = []

Expand Down Expand Up @@ -1021,6 +1028,10 @@ def append_wf(self, new_wf, fw_ids, detour=False, pull_spec_mods=False):

Returns:
list[int]: list of Firework ids that were updated or new.

Raises:
TypeError: when detour is not boolean
ValueError: when detour or fw_ids inputs are invalid
"""
updated_ids = []

Expand Down
31 changes: 30 additions & 1 deletion fireworks/core/launchpad.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
WFLOCK_EXPIRATION_SECS,
MongoClient,
)
from fireworks.utilities.exceptions import FWValueError
from fireworks.utilities.fw_serializers import FWSerializable, reconstitute_dates, recursive_dict
from fireworks.utilities.fw_utilities import get_fw_logger

Expand Down Expand Up @@ -312,6 +313,9 @@ def reset(self, password, require_password=True, max_reset_wo_password=25) -> No
max_reset_wo_password to minimize risk.
max_reset_wo_password (int): A failsafe; when require_password is set to False,
FWS will not clear DBs that contain more workflows than this parameter

Raises:
ValueError: in case of invalid password or failed password override
"""
m_password = datetime.datetime.now().strftime("%Y-%m-%d")

Expand Down Expand Up @@ -461,6 +465,9 @@ def get_launch_by_id(self, launch_id):

Returns:
Launch object

Raises:
ValueError: in case of invalid launch_id
"""
m_launch = self.launches.find_one({"launch_id": launch_id})
if m_launch:
Expand All @@ -476,6 +483,9 @@ def get_fw_dict_by_id(self, fw_id):

Returns:
dict

Raises:
ValueError: in case of invalid fw_ids
"""
fw_dict = self.fireworks.find_one({"fw_id": fw_id})
if not fw_dict:
Expand Down Expand Up @@ -514,6 +524,9 @@ def get_wf_by_fw_id(self, fw_id):

Returns:
A Workflow object

Raises:
ValueError: in case of invalid fw_id
"""
links_dict = self.workflows.find_one({"nodes": fw_id})
if not links_dict:
Expand All @@ -536,6 +549,9 @@ def get_wf_by_fw_id_lzyfw(self, fw_id: int) -> Workflow:

Returns:
A Workflow object

Raises:
ValueError: in case of invalid fw_id
"""
links_dict = self.workflows.find_one({"nodes": fw_id})
if not links_dict:
Expand Down Expand Up @@ -1561,6 +1577,9 @@ def complete_launch(self, launch_id, action=None, state="COMPLETED"):

Returns:
dict: updated launch

Raises:
DocumentTooLarge: in some cases when the document size limit is exceeded
"""
# update the launch data to COMPLETED, set end time, etc
m_launch = self.get_launch_by_id(launch_id)
Expand Down Expand Up @@ -1628,6 +1647,9 @@ def get_new_fw_id(self, quantity=1):
Args:
quantity (int): optionally ask for many ids, otherwise defaults to 1
this then returns the *first* fw_id in that range

Raises:
ValueError: if next Firework id cannot be found
"""
try:
return self.fw_id_assigner.find_one_and_update({}, {"$inc": {"next_fw_id": quantity}})["next_fw_id"]
Expand Down Expand Up @@ -1799,6 +1821,10 @@ def _refresh_wf(self, fw_id) -> None:

Args:
fw_id (int): the parent fw_id - children will be refreshed

Raises:
RuntimeError: in case of an error when refreshing the workflow
different from LockedWorkflowError
"""
# TODO: time how long it took to refresh the WF!
# TODO: need a try-except here, high probability of failure if incorrect action supplied
Expand Down Expand Up @@ -1827,6 +1853,9 @@ def _update_wf(self, wf, updated_ids) -> None:
Args:
wf (Workflow)
updated_ids ([int]): list of firework ids

Raises:
FWValueError: when the query finds no matching workflow
"""
updated_fws = [wf.id_fw[fid] for fid in updated_ids]
old_new = self._upsert_fws(updated_fws)
Expand All @@ -1841,7 +1870,7 @@ def _update_wf(self, wf, updated_ids) -> None:

assert query_node is not None
if not self.workflows.find_one({"nodes": query_node}):
raise ValueError(f"BAD QUERY_NODE! {query_node}")
raise FWValueError(f"BAD QUERY_NODE! {query_node}")
# redo the links and fw_states
wf = wf.to_db_dict()
wf["locked"] = True # preserve the lock!
Expand Down
3 changes: 3 additions & 0 deletions fireworks/core/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ def run(self, pdb_on_exception: bool = False, err_file: IO = None) -> bool:

Returns:
bool: True if the rocket ran successfully, False is if it failed or no job in the DB was ready to run.

Raises:
OSError: when creation of launch directory fails
"""
all_stored_data = {} # combined stored data for *all* the Tasks
all_update_spec = {} # combined update_spec for *all* the Tasks
Expand Down
5 changes: 4 additions & 1 deletion fireworks/fw_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import pymongo
from monty.design_patterns import singleton
from monty.serialization import dumpfn, loadfn
from fireworks.utilities.exceptions import FWConfigurationError

__author__ = "Anubhav Jain"
__copyright__ = "Copyright 2012, The Materials Project"
Expand Down Expand Up @@ -137,13 +138,15 @@ def override_user_settings() -> None:

if os.path.exists(config_paths[0]):
overrides = loadfn(config_paths[0])
if not isinstance(overrides, dict):
raise FWConfigurationError(f"Invalid FW_config file, type must be dict but is {type(overrides)}")
for key, v in overrides.items():
if key == "ADD_USER_PACKAGES":
USER_PACKAGES.extend(v)
elif key == "ECHO_TEST":
print(v)
elif key not in globals():
raise ValueError(f"Invalid FW_config file has unknown parameter: {key}")
raise FWConfigurationError(f"Invalid FW_config file has unknown parameter: {key}")
else:
globals()[key] = v

Expand Down
10 changes: 10 additions & 0 deletions fireworks/queue/queue_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ def launch_rocket_to_queue(
fill_mode (bool): whether to submit jobs even when there is nothing to run
(only in non-reservation mode)
fw_id (int): specific fw_id to reserve (reservation mode only)

Raises:
ValueError: in case of invalid or inconsistent parameters
RuntimeError: when job script submission fails
"""
fworker = fworker or FWorker()
launcher_dir = os.path.abspath(launcher_dir)
Expand Down Expand Up @@ -204,6 +208,9 @@ def rapidfire(
timeout (int): Number of seconds after which to stop the rapidfire process
fill_mode (bool): Whether to submit jobs even when there is nothing to run (only in
non-reservation mode)
Raises:
ValueError: when launch_dir does not exist or block_dir has invalid name
RuntimeError: when launch fails
"""
sleep_time = sleep_time or RAPIDFIRE_SLEEP_SECS
launch_dir = os.path.abspath(launch_dir)
Expand Down Expand Up @@ -315,6 +322,9 @@ def _get_number_of_jobs_in_queue(qadapter: "QueueAdapterBase", njobs_queue: int,

Return:
(int)

Raises:
RuntimeError: if determination of number of jobs fails
"""
RETRY_INTERVAL = 30 # initial retry in 30 sec upon failure

Expand Down
3 changes: 2 additions & 1 deletion fireworks/scripts/lpad_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from typing import TYPE_CHECKING, Any

from pymongo import ASCENDING, DESCENDING
from pymongo.errors import PyMongoError
from ruamel.yaml import YAML

from fireworks import FW_INSTALL_DIR
Expand Down Expand Up @@ -124,7 +125,7 @@ def get_lp(args: Namespace) -> LaunchPad:
lp.connection.admin.command("ping")
return lp

except Exception:
except PyMongoError:
err_message = (
f"FireWorks was not able to connect to MongoDB at {lp.host}:{lp.port}. Is the server running? "
f"The database file specified was {args.launchpad_file}."
Expand Down
38 changes: 37 additions & 1 deletion fireworks/tests/test_fw_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,48 @@
__email__ = "ongsp@ucsd.edu"
__date__ = "2/3/14"

import os
import unittest
from tempfile import mkdtemp

from fireworks.fw_config import config_to_dict
import pytest

from fireworks.fw_config import config_to_dict, override_user_settings
from fireworks.utilities.exceptions import FWConfigurationError


class ConfigTest(unittest.TestCase):
def test_config(self) -> None:
d = config_to_dict()
assert "NEGATIVE_FWID_CTR" not in d


class FWConfigTest(unittest.TestCase):
"""Tests for the fw_config module."""

def setUp(self):
self.init_dir = os.getcwd()
self.fw_config_dir = mkdtemp()
os.chdir(self.fw_config_dir)
self.fw_config = os.path.join(self.fw_config_dir, "FW_config.yaml")
with open(self.fw_config, "w", encoding="utf-8"):
pass

def tearDown(self):
os.chdir(self.init_dir)
os.unlink(self.fw_config)
os.rmdir(self.fw_config_dir)

def test_override_user_settings_empty_yaml(self) -> None:
"""Test with empty fw_config file."""
msg = "Invalid FW_config file, type must be dict but is <class 'NoneType'>"
with pytest.raises(FWConfigurationError, match=msg):
override_user_settings()

def test_override_user_settings_invalid_key(self) -> None:
"""Test fw_config file with invalid key."""
with open(self.fw_config, "a", encoding="utf-8") as fh:
fh.write("blah: true")
msg = "Invalid FW_config file has unknown parameter: blah"
with pytest.raises(FWConfigurationError, match=msg):
override_user_settings()
21 changes: 21 additions & 0 deletions fireworks/utilities/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""FireWorks exceptions."""


class FWError(Exception):
"""Base exception for all other FireWorks exceptions."""


class FWConfigurationError(FWError):
"""Raise for errors related to fw_config."""


class FWSerializationError(FWError):
"""Raise for errors related to serialization/deserialization."""


class FWFormatError(FWError):
"""Raise for errors related to file format."""


class FWValueError(FWError, ValueError):
"""FireWorks specialization of ValueError."""
Loading
Loading