From f3cf4acee36a83137c33a4b8e3f3eebf19499d40 Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Fri, 16 Feb 2024 10:40:12 -0500 Subject: [PATCH] Consider regex match when issuing "unrecognized flow operation warning. (#804) * Consider regex match when issuing "unrecognized flow operation warning. * Update change log. * Attempt to increase test coverage. --------- Co-authored-by: Corwin Kerr --- changelog.txt | 6 +++++- flow/project.py | 46 ++++++++++++++++++------------------------- tests/test_project.py | 2 +- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/changelog.txt b/changelog.txt index 795743d6a..f83f45fdc 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,7 +5,7 @@ Changelog The **signac-flow** package follows `semantic versioning `_. The numbers in brackets denote the related GitHub issue and/or pull request. -Version 0.27 +Version 0.28 ============ [next] -- not yet released @@ -20,6 +20,7 @@ Fixed +++++ - Generate correct SLURM scripts on SDSC Expanse (#810). +- Suppress warning for regex operation names that match groups (#804). Changed +++++++ @@ -33,6 +34,9 @@ Removed - Remove Zenodo (#816). +Version 0.27 +============ + [0.27.0] -- 2024-01-15 ---------------------- diff --git a/flow/project.py b/flow/project.py index 57557a8c9..6948f1a59 100644 --- a/flow/project.py +++ b/flow/project.py @@ -2676,14 +2676,6 @@ def _fetch_status( only has to occur one time. """ - if names is not None: - absent_ops = (set(self._groups.keys()) ^ set(names)) & set(names) - if absent_ops: - print( - f"Unrecognized flow operation(s): {', '.join(absent_ops)}", - file=sys.stderr, - ) - if status_parallelization not in ("thread", "process", "none"): raise RuntimeError( "Configuration value status_parallelization is invalid. " @@ -3645,13 +3637,6 @@ def run( ) if names is None: names = list(self.operations) - else: - absent_ops = (set(self._groups.keys()) ^ set(names)) & set(names) - if absent_ops: - print( - f"Unrecognized flow operation(s): {', '.join(absent_ops)}", - file=sys.stderr, - ) # Get default directives default_directives = self._get_default_directives() @@ -3731,7 +3716,7 @@ def select(operation): # Generate _JobOperation instances for selected groups and aggregates. with self._buffered(): operations = [] - run_groups = set(self._gather_executable_flow_groups(names)) + run_groups = set(self._gather_executable_flow_groups(names, i_pass)) for ( aggregate_id, aggregate, @@ -3793,7 +3778,7 @@ def key_func_by_job(operation): operations, pretend=pretend, np=np, timeout=timeout, progress=progress ) - def _gather_selected_flow_groups(self, names=None): + def _gather_selected_flow_groups(self, names=None, i_pass=1): r"""Grabs :class:`~.FlowGroup`\ s that match any of a set of names. The provided names can be any regular expression that fully matches a group name. @@ -3803,6 +3788,8 @@ def _gather_selected_flow_groups(self, names=None): names : iterable of :class:`str` Only select groups that match the provided set of names (interpreted as regular expressions), or all if the argument is None. (Default value = None) + i_pass : int + The current pass in `run`. Used to print the warning message only once. Returns ------- @@ -3813,6 +3800,7 @@ def _gather_selected_flow_groups(self, names=None): if names is None: return list(self._groups.values()) operations = {} + absent_ops = [] for name in names: if name in operations: continue @@ -3821,11 +3809,21 @@ def _gather_selected_flow_groups(self, names=None): for group_name, group in self.groups.items() if re.fullmatch(name, group_name) ] + + if i_pass == 1 and len(groups) == 0: + absent_ops.append(name) + for group in groups: operations[group.name] = group + + if len(absent_ops) > 0: + print( + f"Unrecognized flow operation(s): {', '.join(absent_ops)}", + file=sys.stderr, + ) return list(operations.values()) - def _gather_executable_flow_groups(self, names=None): + def _gather_executable_flow_groups(self, names=None, i_pass=1): r"""Grabs immediately executable flow groups that match any given name. The provided names can be any regular expression that fully match a group name. @@ -3841,6 +3839,8 @@ def _gather_executable_flow_groups(self, names=None): names : iterable of :class:`str` Only select groups that match the provided set of names (interpreted as regular expressions), or all singleton groups if the argument is None. (Default value = None) + i_pass : int + The current pass in `run`. Used to print the warning message only once. Returns ------- @@ -3850,7 +3850,7 @@ def _gather_executable_flow_groups(self, names=None): """ if names is None: return [self._groups[op_name] for op_name in self.operations] - operations = self._gather_selected_flow_groups(names) + operations = self._gather_selected_flow_groups(names, i_pass) # Have to verify no overlap to ensure all returned groups are # simultaneously executable. if not FlowProject._verify_group_compatibility(operations): @@ -4189,14 +4189,6 @@ def submit( Additional keyword arguments forwarded to :meth:`~.ComputeEnvironment.submit`. """ - if names is not None: - absent_ops = (set(self._groups.keys()) ^ set(names)) & set(names) - if absent_ops: - print( - f"Unrecognized flow operation(s): {', '.join(absent_ops)}", - file=sys.stderr, - ) - aggregates = self._convert_jobs_to_aggregates(jobs) # Regular argument checks and expansion diff --git a/tests/test_project.py b/tests/test_project.py index 5eb021b5f..68dd4fbbf 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -1042,7 +1042,7 @@ def test_run_with_operation_selection(self): project.run(names=["op1", "non-existent-op"]) assert all(job.isfile("world.txt") for job in even_jobs) assert not any(job.doc.get("test") for job in project) - project.run(names=["op[^4]", "non-existent-op"]) + project.run(names=["op[^4]", "non-existent-op", "non-existent.*"]) assert all(job.isfile("world.txt") for job in even_jobs) assert all(job.doc.get("test") for job in project) assert all("dynamic" not in job.doc for job in project)