From a967c006993adb277c92c5dcbcf60cc037e456c0 Mon Sep 17 00:00:00 2001 From: Tim Pillinger <26465611+wxtim@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:05:31 +0100 Subject: [PATCH 01/28] Master Points to 8.4.0 (#6159) --- cylc/flow/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cylc/flow/__init__.py b/cylc/flow/__init__.py index 40718ebdf7e..34897137506 100644 --- a/cylc/flow/__init__.py +++ b/cylc/flow/__init__.py @@ -53,7 +53,7 @@ def environ_init(): environ_init() -__version__ = '8.3.1.dev' +__version__ = '8.4.0.dev' def iter_entry_points(entry_point_name): From c3d5f1d158e06609d39c5a05b6cfa7952f4c34da Mon Sep 17 00:00:00 2001 From: Oliver Sanders Date: Wed, 19 Jun 2024 16:34:27 +0100 Subject: [PATCH 02/28] lint: address RUF015 --- cylc/flow/broadcast_report.py | 2 +- cylc/flow/id_cli.py | 4 ++-- cylc/flow/scripts/show.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cylc/flow/broadcast_report.py b/cylc/flow/broadcast_report.py index 72fedb4cbef..cb44f2212f3 100644 --- a/cylc/flow/broadcast_report.py +++ b/cylc/flow/broadcast_report.py @@ -72,7 +72,7 @@ def get_broadcast_change_iter(modified_settings, is_cancel=False): value = setting keys_str = "" while isinstance(value, dict): - key, value = list(value.items())[0] + key, value = next(iter(value.items())) if isinstance(value, dict): keys_str += "[" + key + "]" else: diff --git a/cylc/flow/id_cli.py b/cylc/flow/id_cli.py index 9c7493fd612..cad615885e4 100644 --- a/cylc/flow/id_cli.py +++ b/cylc/flow/id_cli.py @@ -347,7 +347,7 @@ async def parse_ids_async( if src: if not flow_file_path: # get the workflow file path from the run dir - flow_file_path = get_flow_file(list(workflows)[0]) + flow_file_path = get_flow_file(next(iter(workflows))) return workflows, flow_file_path return workflows, multi_mode @@ -375,7 +375,7 @@ async def parse_id_async( 'max_tasks': 1, }, ) - workflow_id = list(workflows)[0] + workflow_id = next(iter(workflows)) tokens_list = workflows[workflow_id] tokens: Optional[Tokens] if tokens_list: diff --git a/cylc/flow/scripts/show.py b/cylc/flow/scripts/show.py index b146f339e10..2fdfaf7fde4 100755 --- a/cylc/flow/scripts/show.py +++ b/cylc/flow/scripts/show.py @@ -490,7 +490,7 @@ def main(_, options: 'Values', *ids) -> None: constraint='mixed', max_workflows=1, ) - workflow_id = list(workflow_args)[0] + workflow_id = next(iter(workflow_args)) tokens_list = workflow_args[workflow_id] if tokens_list and options.task_defs: From fcabe74e83de14d2b9fa09c6279696216a1892a0 Mon Sep 17 00:00:00 2001 From: Oliver Sanders Date: Wed, 19 Jun 2024 17:11:42 +0100 Subject: [PATCH 03/28] lint: address B904 --- cylc/flow/clean.py | 9 ++-- cylc/flow/command_validation.py | 2 +- cylc/flow/commands.py | 4 +- cylc/flow/config.py | 55 +++++++++++++---------- cylc/flow/cycling/integer.py | 2 +- cylc/flow/cycling/iso8601.py | 6 +-- cylc/flow/dbstatecheck.py | 6 +-- cylc/flow/graph_parser.py | 8 ++-- cylc/flow/host_select.py | 2 +- cylc/flow/id.py | 2 +- cylc/flow/id_cli.py | 4 +- cylc/flow/install.py | 4 +- cylc/flow/install_plugins/log_vc_info.py | 2 +- cylc/flow/loggingutil.py | 2 +- cylc/flow/main_loop/__init__.py | 4 +- cylc/flow/main_loop/health_check.py | 2 +- cylc/flow/network/__init__.py | 15 ++++--- cylc/flow/network/client.py | 2 +- cylc/flow/network/resolvers.py | 2 +- cylc/flow/network/schema.py | 2 +- cylc/flow/network/ssh_client.py | 2 +- cylc/flow/param_expand.py | 14 +++--- cylc/flow/parsec/config.py | 6 ++- cylc/flow/parsec/empysupport.py | 2 +- cylc/flow/parsec/fileparse.py | 12 +++-- cylc/flow/parsec/jinja2support.py | 6 +-- cylc/flow/parsec/upgrade.py | 2 +- cylc/flow/parsec/validate.py | 42 +++++++++++------ cylc/flow/pathutil.py | 6 +-- cylc/flow/prerequisite.py | 3 +- cylc/flow/scripts/broadcast.py | 2 +- cylc/flow/scripts/cat_log.py | 2 +- cylc/flow/scripts/clean.py | 2 +- cylc/flow/scripts/cylc.py | 2 +- cylc/flow/scripts/dump.py | 3 +- cylc/flow/scripts/ext_trigger.py | 2 +- cylc/flow/scripts/get_workflow_contact.py | 4 +- cylc/flow/scripts/lint.py | 2 +- cylc/flow/scripts/report_timings.py | 4 +- cylc/flow/scripts/validate.py | 21 +++++---- cylc/flow/scripts/workflow_state.py | 5 ++- cylc/flow/task_id.py | 3 +- cylc/flow/time_parser.py | 2 +- cylc/flow/tui/data.py | 4 +- cylc/flow/util.py | 4 +- cylc/flow/workflow_db_mgr.py | 4 +- cylc/flow/workflow_files.py | 8 ++-- cylc/flow/xtrigger_mgr.py | 12 ++--- cylc/flow/xtriggers/wall_clock.py | 4 +- 49 files changed, 186 insertions(+), 134 deletions(-) diff --git a/cylc/flow/clean.py b/cylc/flow/clean.py index aec2c4efeb4..3378cfd1591 100644 --- a/cylc/flow/clean.py +++ b/cylc/flow/clean.py @@ -129,7 +129,7 @@ def _clean_check(opts: 'Values', id_: str, run_dir: Path) -> None: except ContactFileExists as exc: raise ServiceFileError( f"Cannot clean running workflow {id_}.\n\n{exc}" - ) + ) from None def init_clean(id_: str, opts: 'Values') -> None: @@ -173,7 +173,7 @@ def init_clean(id_: str, opts: 'Values') -> None: try: platform_names = get_platforms_from_db(local_run_dir) except ServiceFileError as exc: - raise ServiceFileError(f"Cannot clean {id_} - {exc}") + raise ServiceFileError(f"Cannot clean {id_} - {exc}") from None except sqlite3.OperationalError as exc: # something went wrong with the query # e.g. the table/field we need isn't there @@ -184,7 +184,7 @@ def init_clean(id_: str, opts: 'Values') -> None: ' with to remove it.' '\nOtherwise please delete the database file.' ) - raise ServiceFileError(f"Cannot clean {id_} - {exc}") + raise ServiceFileError(f"Cannot clean {id_} - {exc}") from exc if platform_names and platform_names != {'localhost'}: remote_clean( @@ -359,7 +359,8 @@ def remote_clean( except PlatformLookupError as exc: raise PlatformLookupError( f"Cannot clean {id_} on remote platforms as the workflow database " - f"is out of date/inconsistent with the global config - {exc}") + f"is out of date/inconsistent with the global config - {exc}" + ) from None queue: Deque[RemoteCleanQueueTuple] = deque() remote_clean_cmd = partial( diff --git a/cylc/flow/command_validation.py b/cylc/flow/command_validation.py index 1c57d452c43..ea67f00206e 100644 --- a/cylc/flow/command_validation.py +++ b/cylc/flow/command_validation.py @@ -70,7 +70,7 @@ def flow_opts(flows: List[str], flow_wait: bool) -> None: try: int(val) except ValueError: - raise InputError(ERR_OPT_FLOW_VAL.format(val)) + raise InputError(ERR_OPT_FLOW_VAL.format(val)) from None if flow_wait and flows[0] in [FLOW_NEW, FLOW_NONE]: raise InputError(ERR_OPT_FLOW_WAIT) diff --git a/cylc/flow/commands.py b/cylc/flow/commands.py index a4ea43df5cf..2abf7f1c773 100644 --- a/cylc/flow/commands.py +++ b/cylc/flow/commands.py @@ -208,7 +208,7 @@ async def stop( try: mode = StopMode(mode) except ValueError: - raise CommandFailedError(f"Invalid stop mode: '{mode}'") + raise CommandFailedError(f"Invalid stop mode: '{mode}'") from None schd._set_stop(mode) if mode is StopMode.REQUEST_KILL: schd.time_next_kill = time() @@ -304,7 +304,7 @@ async def set_verbosity(schd: 'Scheduler', level: Union[int, str]): lvl = int(level) LOG.setLevel(lvl) except (TypeError, ValueError) as exc: - raise CommandFailedError(exc) + raise CommandFailedError(exc) from None cylc.flow.flags.verbosity = log_level_to_verbosity(lvl) diff --git a/cylc/flow/config.py b/cylc/flow/config.py index 5b7738f3e6c..fdcc10adb05 100644 --- a/cylc/flow/config.py +++ b/cylc/flow/config.py @@ -199,11 +199,11 @@ def interpolate_template(tmpl, params_dict): try: return tmpl % params_dict except KeyError: - raise ParamExpandError('bad parameter') + raise ParamExpandError('bad parameter') from None except TypeError: - raise ParamExpandError('wrong data type for parameter') + raise ParamExpandError('wrong data type for parameter') from None except ValueError: - raise ParamExpandError('bad template syntax') + raise ParamExpandError('bad template syntax') from None class WorkflowConfig: @@ -480,8 +480,8 @@ def __init__( get_interval(offset_string).standardise()) except IntervalParsingError: raise WorkflowConfigError( - "Illegal %s spec: %s" % ( - s_type, offset_string)) + "Illegal %s spec: %s" % (s_type, offset_string) + ) from None extn = "(" + offset_string + ")" # Replace family names with members. @@ -709,7 +709,7 @@ def process_initial_cycle_point(self) -> None: try: icp = ingest_time(orig_icp, get_current_time_string()) except IsodatetimeError as exc: - raise WorkflowConfigError(str(exc)) + raise WorkflowConfigError(str(exc)) from None if orig_icp != icp: # now/next()/previous() was used, need to store # evaluated point in DB @@ -761,7 +761,7 @@ def process_start_cycle_point(self) -> None: for taskid in self.options.starttask ] except ValueError as exc: - raise InputError(str(exc)) + raise InputError(str(exc)) from None self.start_point = min( get_point(cycle).standardise() for cycle in cycle_points if cycle @@ -1114,7 +1114,7 @@ def _check_completion_expression(self, task_name: str, expr: str) -> None: f'\n {expr}' '\nThe "finished" output cannot be used in completion' ' expressions, use "succeeded or failed".' - ) + ) from None for alt_qualifier, qualifier in ALT_QUALIFIERS.items(): _alt_compvar = trigger_to_completion_variable(alt_qualifier) @@ -1125,21 +1125,21 @@ def _check_completion_expression(self, task_name: str, expr: str) -> None: f'\n {expr}' f'\nUse "{_compvar}" not "{_alt_compvar}" ' 'in completion expressions.' - ) + ) from None raise WorkflowConfigError( # NOTE: str(exc) == "name 'x' is not defined" tested in # tests/integration/test_optional_outputs.py f'Error in [runtime][{task_name}]completion:' f'\n{error}' - ) + ) from None except Exception as exc: # includes InvalidCompletionExpression # expression contains non-whitelisted syntax or any other error in # the expression e.g. SyntaxError raise WorkflowConfigError( f'Error in [runtime][{task_name}]completion:' f'\n{str(exc)}' - ) + ) from None # ensure consistency between the graph and the completion expression for compvar in ( @@ -1415,11 +1415,12 @@ def compute_family_tree(self): c3_single.mro(name)) except RecursionError: raise WorkflowConfigError( - "circular [runtime] inheritance?") + "circular [runtime] inheritance?" + ) from None except Exception as exc: # catch inheritance errors # TODO - specialise MRO exceptions - raise WorkflowConfigError(str(exc)) + raise WorkflowConfigError(str(exc)) from None for name in self.cfg['runtime']: ancestors = self.runtime['linearized ancestors'][name] @@ -1758,7 +1759,7 @@ def _check_task_event_handlers(self): f' {taskdef.name}:' f' {handler_template}:' f' {repr(exc)}' - ) + ) from None def _check_special_tasks(self): """Check declared special tasks are valid, and detect special @@ -1865,7 +1866,9 @@ def generate_triggers(self, lexpression, left_nodes, right, seq, try: expr_list = listify(lexpression) except SyntaxError: - raise WorkflowConfigError('Error in expression "%s"' % lexpression) + raise WorkflowConfigError( + 'Error in expression "%s"' % lexpression + ) from None triggers = {} xtrig_labels = set() @@ -1942,7 +1945,9 @@ def generate_triggers(self, lexpression, left_nodes, right, seq, xtrig = xtrigs[label] except KeyError: if label != 'wall_clock': - raise WorkflowConfigError(f"xtrigger not defined: {label}") + raise WorkflowConfigError( + f"xtrigger not defined: {label}" + ) from None else: # Allow "@wall_clock" in graph as implicit zero-offset. xtrig = SubFuncContext('wall_clock', 'wall_clock', [], {}) @@ -2276,7 +2281,7 @@ def load_graph(self): msg += ' (final cycle point=%s)' % fcp if isinstance(exc, CylcError): msg += ' %s' % exc.args[0] - raise WorkflowConfigError(msg) + raise WorkflowConfigError(msg) from None self.sequences.append(seq) parser = GraphParser( family_map, @@ -2431,7 +2436,7 @@ def get_taskdef( except TaskDefError as exc: if orig_expr: LOG.error(orig_expr) - raise WorkflowConfigError(str(exc)) + raise WorkflowConfigError(str(exc)) from None else: # Record custom message outputs from [runtime]. for output, message in ( @@ -2443,14 +2448,14 @@ def get_taskdef( f'Invalid task output "' f'[runtime][{name}][outputs]' f'{output} = {message}" - {msg}' - ) + ) from None valid, msg = TaskMessageValidator.validate(message) if not valid: raise WorkflowConfigError( f'Invalid task message "' f'[runtime][{name}][outputs]' f'{output} = {message}" - {msg}' - ) + ) from None self.taskdefs[name].add_output(output, message) return self.taskdefs[name] @@ -2462,7 +2467,7 @@ def _get_taskdef(self, name: str) -> TaskDef: try: rtcfg = self.cfg['runtime'][name] except KeyError: - raise WorkflowConfigError("Task not defined: %s" % name) + raise WorkflowConfigError("Task not defined: %s" % name) from None # We may want to put in some handling for cases of changing the # initial cycle via restart (accidentally or otherwise). @@ -2554,7 +2559,9 @@ def process_metadata_urls(self): 'workflow': self.workflow, } except (KeyError, ValueError): - raise InputError(f'Invalid template [meta]URL: {url}') + raise InputError( + f'Invalid template [meta]URL: {url}' + ) from None else: LOG.warning( 'Detected deprecated template variables in [meta]URL.' @@ -2590,7 +2597,9 @@ def process_metadata_urls(self): 'task': name, } except (KeyError, ValueError): - raise InputError(f'Invalid template [meta]URL: {url}') + raise InputError( + f'Invalid template [meta]URL: {url}' + ) from None else: LOG.warning( 'Detected deprecated template variables in' diff --git a/cylc/flow/cycling/integer.py b/cylc/flow/cycling/integer.py index 749c651fc08..20cc3765670 100644 --- a/cylc/flow/cycling/integer.py +++ b/cylc/flow/cycling/integer.py @@ -150,7 +150,7 @@ def standardise(self): try: self.value = str(int(self)) except (TypeError, ValueError) as exc: - raise PointParsingError(type(self), self.value, exc) + raise PointParsingError(type(self), self.value, exc) from None return self def __int__(self): diff --git a/cylc/flow/cycling/iso8601.py b/cylc/flow/cycling/iso8601.py index a66ce3f5ba0..2ab311df425 100644 --- a/cylc/flow/cycling/iso8601.py +++ b/cylc/flow/cycling/iso8601.py @@ -102,7 +102,7 @@ def standardise(self): WorkflowSpecifics.NUM_EXPANDED_YEAR_DIGITS) else: message = str(exc) - raise PointParsingError(type(self), self.value, message) + raise PointParsingError(type(self), self.value, message) from None return self def sub(self, other): @@ -176,7 +176,7 @@ def standardise(self): try: self.value = str(interval_parse(self.value)) except IsodatetimeError: - raise IntervalParsingError(type(self), self.value) + raise IntervalParsingError(type(self), self.value) from None return self def add(self, other): @@ -782,7 +782,7 @@ def prev_next( raise WorkflowConfigError( f'Invalid offset: {my_time}:' f' Offset lists are semicolon separated, try {suggest}' - ) + ) from None timepoints.append(parsed_point + now) diff --git a/cylc/flow/dbstatecheck.py b/cylc/flow/dbstatecheck.py index b38c394ccdb..1890c697c71 100644 --- a/cylc/flow/dbstatecheck.py +++ b/cylc/flow/dbstatecheck.py @@ -78,7 +78,7 @@ def __init__(self, rund, workflow, db_path=None): try: self.db_point_fmt = self._get_db_point_format() self.c7_back_compat_mode = False - except sqlite3.OperationalError as exc: + except sqlite3.OperationalError: # BACK COMPAT: Cylc 7 DB (see method below). try: self.db_point_fmt = self._get_db_point_format_compat() @@ -86,7 +86,7 @@ def __init__(self, rund, workflow, db_path=None): except sqlite3.OperationalError: with suppress(Exception): self.conn.close() - raise exc # original error + raise def __enter__(self): return self @@ -137,7 +137,7 @@ def adjust_point_to_db(self, cycle, offset): raise InputError( f'Cycle point "{cycle}" is not compatible' f' with DB point format "{self.db_point_fmt}"' - ) + ) from None return cycle @staticmethod diff --git a/cylc/flow/graph_parser.py b/cylc/flow/graph_parser.py index efbce16fb36..3fbfd8c1754 100644 --- a/cylc/flow/graph_parser.py +++ b/cylc/flow/graph_parser.py @@ -346,7 +346,7 @@ def parse_graph(self, graph_string: str) -> None: raise GraphParseError( f"Dangling {seq}:" f"{this_line}" - ) + ) from None part_lines.append(this_line) # Check that a continuation sequence doesn't end this line and @@ -638,7 +638,8 @@ def _proc_dep_pair( except KeyError: # "FAM:bad => foo" in LHS (includes "FAM => bar" too). raise GraphParseError( - f"Illegal family trigger in {expr}") + f"Illegal family trigger in {expr}" + ) from None else: # Not a family. if trig in self.__class__.fam_to_mem_trigger_map: @@ -911,7 +912,8 @@ def _compute_triggers( except KeyError: # Illegal family trigger on RHS of a pair. raise GraphParseError( - f"Illegal family trigger: {name}:{output}") + f"Illegal family trigger: {name}:{output}" + ) from None else: fam = False if not output: diff --git a/cylc/flow/host_select.py b/cylc/flow/host_select.py index 0eb34d088ca..69e32c68a71 100644 --- a/cylc/flow/host_select.py +++ b/cylc/flow/host_select.py @@ -373,7 +373,7 @@ def _filter_by_ranking(hosts, rankings, results, data=None): f'\n Expression: {item}' f'\n Configuration: {GLBL_CFG_STR}' f'\n Error: {exc}' - ) + ) from None if isinstance(result, bool): host_rankings[item] = result data[host][item] = result diff --git a/cylc/flow/id.py b/cylc/flow/id.py index 58fff7fa7bc..f2c8b05b4a1 100644 --- a/cylc/flow/id.py +++ b/cylc/flow/id.py @@ -128,7 +128,7 @@ def __getitem__(self, key): return dict.__getitem__(self, key) except KeyError: if key not in self._KEYS: - raise ValueError(f'Invalid token: {key}') + raise ValueError(f'Invalid token: {key}') from None return None def __str__(self): diff --git a/cylc/flow/id_cli.py b/cylc/flow/id_cli.py index cad615885e4..35afbf80d5a 100644 --- a/cylc/flow/id_cli.py +++ b/cylc/flow/id_cli.py @@ -167,9 +167,9 @@ def _parse_cli(*ids: str) -> List[Tokens]: # this ID is invalid with or without the trailing slash tokens = cli_tokenise(id_[:-1]) except ValueError: - raise InputError(f'Invalid ID: {id_}') + raise InputError(f'Invalid ID: {id_}') from None else: - raise InputError(f'Invalid ID: {id_}') + raise InputError(f'Invalid ID: {id_}') from None is_partial = tokens.get('workflow') and not tokens.get('cycle') is_relative = not tokens.get('workflow') diff --git a/cylc/flow/install.py b/cylc/flow/install.py index 27810f72e97..2e94943d9d6 100644 --- a/cylc/flow/install.py +++ b/cylc/flow/install.py @@ -328,7 +328,7 @@ def install_workflow( # This occurs when the file exists but is _not_ a directory. raise WorkflowFilesError( f"Cannot install as there is an existing file at {rundir}." - ) + ) from None if relink: link_runN(rundir) rsync_cmd = get_rsync_rund_cmd(source, rundir) @@ -529,7 +529,7 @@ def parse_cli_sym_dirs(symlink_dirs: str) -> Dict[str, Dict[str, Any]]: 'There is an error in --symlink-dirs option:' f' {pair}. Try entering option in the form ' '--symlink-dirs=\'log=$DIR, share=$DIR2, ...\'' - ) + ) from None if key not in possible_symlink_dirs: dirs = ', '.join(possible_symlink_dirs) raise InputError( diff --git a/cylc/flow/install_plugins/log_vc_info.py b/cylc/flow/install_plugins/log_vc_info.py index 29d861f7654..41479d6f1ed 100644 --- a/cylc/flow/install_plugins/log_vc_info.py +++ b/cylc/flow/install_plugins/log_vc_info.py @@ -253,7 +253,7 @@ def _run_cmd( except FileNotFoundError as exc: # This will only be raised if the VCS command is not installed, # otherwise Popen() will succeed with a non-zero return code - raise VCSNotInstalledError(vcs, exc) + raise VCSNotInstalledError(vcs, exc) from None if stdout == PIPE: out, err = pipe_poller(proc, proc.stdout, proc.stderr) else: diff --git a/cylc/flow/loggingutil.py b/cylc/flow/loggingutil.py index 3d77bdcb037..a6f1fbee558 100644 --- a/cylc/flow/loggingutil.py +++ b/cylc/flow/loggingutil.py @@ -206,7 +206,7 @@ def should_rollover(self, record: logging.LogRecord) -> bool: self.stream.seek(0, 2) except ValueError as exc: # intended to catch - ValueError: I/O operation on closed file - raise SystemExit(exc) + raise SystemExit(exc) from None return self.stream.tell() + len(msg.encode('utf8')) >= max_bytes @property diff --git a/cylc/flow/main_loop/__init__.py b/cylc/flow/main_loop/__init__.py index 2350153842c..e9f9f35f5da 100644 --- a/cylc/flow/main_loop/__init__.py +++ b/cylc/flow/main_loop/__init__.py @@ -329,14 +329,14 @@ def load(config, additional_plugins=None): f'No main-loop plugin: "{plugin_name}"\n' + ' Available plugins:\n' + indent('\n'.join(sorted(entry_points)), ' ') - ) + ) from None # load plugin try: module = entry_point.load() except Exception as exc: raise PluginError( 'cylc.main_loop', entry_point.name, exc - ) + ) from None # load coroutines log = [] for coro_name, coro in getmembers(module): diff --git a/cylc/flow/main_loop/health_check.py b/cylc/flow/main_loop/health_check.py index b488c878c0e..a470fd3cb88 100644 --- a/cylc/flow/main_loop/health_check.py +++ b/cylc/flow/main_loop/health_check.py @@ -56,4 +56,4 @@ def _check_contact_file(scheduler): raise CylcError( '%s: contact file corrupted/modified and may be left' % workflow_files.get_contact_file_path(scheduler.workflow) - ) + ) from None diff --git a/cylc/flow/network/__init__.py b/cylc/flow/network/__init__.py index 916b129e244..315a57a23ba 100644 --- a/cylc/flow/network/__init__.py +++ b/cylc/flow/network/__init__.py @@ -78,7 +78,7 @@ def get_location(workflow: str) -> Tuple[str, int, int]: contact = load_contact_file(workflow) except (IOError, ValueError, ServiceFileError): # Contact file does not exist or corrupted, workflow should be dead - raise WorkflowStopped(workflow) + raise WorkflowStopped(workflow) from None host = contact[ContactFileFields.HOST] host = get_fqdn_by_host(host) @@ -179,13 +179,13 @@ def _socket_bind(self, min_port, max_port, srv_prv_key_loc=None): f"Failed to find server's public " f"key in " f"{srv_prv_key_info.full_key_path}." - ) + ) from None except OSError: raise ServiceFileError( f"IO error opening server's private " f"key from " f"{srv_prv_key_info.full_key_path}." - ) + ) from None if server_private_key is None: # this can't be caught by exception raise ServiceFileError( f"Failed to find server's private " @@ -204,7 +204,9 @@ def _socket_bind(self, min_port, max_port, srv_prv_key_loc=None): self.port = self.socket.bind_to_random_port( 'tcp://*', min_port, max_port) except (zmq.error.ZMQError, zmq.error.ZMQBindError) as exc: - raise CylcError(f'could not start Cylc ZMQ server: {exc}') + raise CylcError( + f'could not start Cylc ZMQ server: {exc}' + ) from None # Keeping srv_public_key_loc as optional arg so as to not break interface def _socket_connect(self, host, port, srv_public_key_loc=None): @@ -237,7 +239,7 @@ def _socket_connect(self, host, port, srv_public_key_loc=None): client_public_key, client_priv_key = zmq.auth.load_certificate( client_priv_key_info.full_key_path) except (OSError, ValueError): - raise ClientError(error_msg) + raise ClientError(error_msg) from None if client_priv_key is None: # this can't be caught by exception raise ClientError(error_msg) self.socket.curve_publickey = client_public_key @@ -256,7 +258,8 @@ def _socket_connect(self, host, port, srv_public_key_loc=None): self.socket.curve_serverkey = server_public_key except (OSError, ValueError): # ValueError raised w/ no public key raise ClientError( - "Failed to load the workflow's public key, so cannot connect.") + "Failed to load the workflow's public key, so cannot connect." + ) from None self.socket.connect(f'tcp://{host}:{port}') diff --git a/cylc/flow/network/client.py b/cylc/flow/network/client.py index e7e26954d56..099ef8bc0ff 100644 --- a/cylc/flow/network/client.py +++ b/cylc/flow/network/client.py @@ -326,7 +326,7 @@ async def async_request( raise ClientError( error.get('message'), # type: ignore error.get('traceback'), # type: ignore - ) + ) from None def get_header(self) -> dict: """Return "header" data to attach to each request for traceability. diff --git a/cylc/flow/network/resolvers.py b/cylc/flow/network/resolvers.py index 11eafd8bea5..fc9b67eeef5 100644 --- a/cylc/flow/network/resolvers.py +++ b/cylc/flow/network/resolvers.py @@ -750,7 +750,7 @@ async def _mutation_mapper( try: meth = COMMANDS[command] except KeyError: - raise ValueError(f"Command '{command}' not found") + raise ValueError(f"Command '{command}' not found") from None try: # Initiate the command. Validation may be performed at this point, diff --git a/cylc/flow/network/schema.py b/cylc/flow/network/schema.py index 70e40232c1d..b962b582b70 100644 --- a/cylc/flow/network/schema.py +++ b/cylc/flow/network/schema.py @@ -271,7 +271,7 @@ def field_name_from_type( try: return NODE_MAP[named_type.name] except KeyError: - raise ValueError(f"'{named_type.name}' is not a node type") + raise ValueError(f"'{named_type.name}' is not a node type") from None def get_resolvers(info: 'ResolveInfo') -> 'BaseResolvers': diff --git a/cylc/flow/network/ssh_client.py b/cylc/flow/network/ssh_client.py index d1dd4fb6da4..d2ed0dd33e9 100644 --- a/cylc/flow/network/ssh_client.py +++ b/cylc/flow/network/ssh_client.py @@ -90,7 +90,7 @@ async def async_request( f"Command exceeded the timeout {timeout}s. " "This could be due to network problems. " "Check the workflow log." - ) + ) from None def prepare_command( self, command: str, args: Optional[dict], timeout: Union[float, str] diff --git a/cylc/flow/param_expand.py b/cylc/flow/param_expand.py index 0707a46e1a3..6ab104404e4 100644 --- a/cylc/flow/param_expand.py +++ b/cylc/flow/param_expand.py @@ -195,8 +195,9 @@ def _expand_name(self, results, tmpl, params, spec_vals=None): try: results.append((tmpl % current_values, current_values)) except KeyError as exc: - raise ParamExpandError('parameter %s is not ' - 'defined.' % str(exc.args[0])) + raise ParamExpandError( + 'parameter %s is not ' 'defined.' % str(exc.args[0]) + ) from None else: for param_val in params[0][1]: spec_vals[params[0][0]] = param_val @@ -306,8 +307,8 @@ def expand_parent_params(self, parent, param_values, origin): used[item] = param_values[item] except KeyError: raise ParamExpandError( - "parameter '%s' undefined in '%s'" % ( - item, origin)) + "parameter '%s' undefined in '%s'" % (item, origin) + ) from None # For each parameter substitute the param_tmpl_cfg. tmpl = tmpl.format(**self.param_tmpl_cfg) @@ -425,8 +426,9 @@ def _expand_graph(self, line, all_params, try: repl = tmpl % param_values except KeyError as exc: - raise ParamExpandError('parameter %s is not ' - 'defined.' % str(exc.args[0])) + raise ParamExpandError( + 'parameter %s is not ' 'defined.' % str(exc.args[0]) + ) from None line = line.replace('<' + p_group + '>', repl) if line: line_set.add(line) diff --git a/cylc/flow/parsec/config.py b/cylc/flow/parsec/config.py index 19f937d8e5b..29944c03b30 100644 --- a/cylc/flow/parsec/config.py +++ b/cylc/flow/parsec/config.py @@ -150,10 +150,12 @@ def get(self, keys: Optional[Iterable[str]] = None, sparse: bool = False): # setting not present in __MANY__ section: key in self.spec.get(*parents) ): - raise ItemNotFoundError(itemstr(parents, key)) + raise ItemNotFoundError( + itemstr(parents, key) + ) from None raise InvalidConfigError( itemstr(parents, key), self.spec.name - ) + ) from None else: parents.append(key) diff --git a/cylc/flow/parsec/empysupport.py b/cylc/flow/parsec/empysupport.py index b4164894e0f..e3dc5e28df4 100644 --- a/cylc/flow/parsec/empysupport.py +++ b/cylc/flow/parsec/empysupport.py @@ -66,7 +66,7 @@ def empyprocess( raise EmPyError( str(exc), lines={'