From e1cff5fe34a508a5939234c5932f5981bcafc5d9 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Thu, 25 Jan 2024 16:17:51 -0500 Subject: [PATCH 1/7] Add missing expression case and tests of signac find for all filters --- signac/filterparse.py | 2 ++ tests/test_find_command_line_interface.py | 3 +++ tests/test_shell.py | 8 ++++++++ 3 files changed, 13 insertions(+) diff --git a/signac/filterparse.py b/signac/filterparse.py index 86540a409..9951ae0df 100644 --- a/signac/filterparse.py +++ b/signac/filterparse.py @@ -203,6 +203,8 @@ def _add_prefix(filter): raise ValueError( "The argument to a logical operator must be a list or a tuple!" ) + elif key == "$not": + yield key, dict(_add_prefix(value)) elif "." in key and key.split(".", 1)[0] in ("sp", "doc"): yield key, value elif key in ("sp", "doc"): diff --git a/tests/test_find_command_line_interface.py b/tests/test_find_command_line_interface.py index e28fd8840..ae9c0ad00 100644 --- a/tests/test_find_command_line_interface.py +++ b/tests/test_find_command_line_interface.py @@ -11,6 +11,7 @@ from signac.filterparse import parse_filter_arg, parse_simple +# also used in test_shell FILTERS = [ {"a": 0}, {"a.b": 0}, @@ -70,6 +71,8 @@ def _parse(args): def test_interpret_json(self): def _assert_equal(q): + # TODO: full code path not tested with this test. + # _assert_equal and _find_expression, are not tested assert q == self._parse([json.dumps(q)]) for f in FILTERS: diff --git a/tests/test_shell.py b/tests/test_shell.py index 695fdada1..55300e918 100644 --- a/tests/test_shell.py +++ b/tests/test_shell.py @@ -280,6 +280,14 @@ def test_find(self): == [job.id for job in project.find_jobs({"doc.b": i})][0] ) + # ensure that there are no errors due to adding sp and doc prefixes + # by testing on all the example complex expressions + from test_find_command_line_interface import FILTERS + for f in FILTERS: + command = "python -m signac find ".split() + [json.dumps(f)] + self.call(command).strip() + + def test_diff(self): self.call("python -m signac init".split()) project = signac.Project() From 3370d58f9150ea06414397030dae4985dc73b37a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 21:24:56 +0000 Subject: [PATCH 2/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shell.py b/tests/test_shell.py index 55300e918..a6554398c 100644 --- a/tests/test_shell.py +++ b/tests/test_shell.py @@ -283,11 +283,11 @@ def test_find(self): # ensure that there are no errors due to adding sp and doc prefixes # by testing on all the example complex expressions from test_find_command_line_interface import FILTERS + for f in FILTERS: command = "python -m signac find ".split() + [json.dumps(f)] self.call(command).strip() - def test_diff(self): self.call("python -m signac init".split()) project = signac.Project() From 04ff52e413a71189a2c208710faacff4ad7e9ab2 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Fri, 26 Jan 2024 14:39:54 -0500 Subject: [PATCH 3/7] Address code review --- signac/filterparse.py | 2 ++ tests/test_shell.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/signac/filterparse.py b/signac/filterparse.py index 9951ae0df..c2bc3a2df 100644 --- a/signac/filterparse.py +++ b/signac/filterparse.py @@ -195,6 +195,8 @@ def parse_filter_arg(args): def _add_prefix(filter): """Add prefix "sp." to a (possibly nested) filter.""" + + # Logical operators ($and, $or, $not) should not be prefixed, but their values should. for key, value in filter.items(): if key in ("$and", "$or"): if isinstance(value, list) or isinstance(value, tuple): diff --git a/tests/test_shell.py b/tests/test_shell.py index 55300e918..5ef74bf3b 100644 --- a/tests/test_shell.py +++ b/tests/test_shell.py @@ -9,6 +9,7 @@ from tempfile import TemporaryDirectory import pytest +from test_find_command_line_interface import FILTERS from test_project import WINDOWS, _initialize_v1_project, skip_windows_without_symlinks import signac @@ -282,7 +283,6 @@ def test_find(self): # ensure that there are no errors due to adding sp and doc prefixes # by testing on all the example complex expressions - from test_find_command_line_interface import FILTERS for f in FILTERS: command = "python -m signac find ".split() + [json.dumps(f)] self.call(command).strip() From d9dd4ef5ca39d5b43c1c32f3ca9ea13bc68146f5 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Wed, 31 Jan 2024 12:18:52 -0500 Subject: [PATCH 4/7] Use fixture for find_filter --- tests/conftest.py | 30 +++++++++++++++++ tests/test_find_command_line_interface.py | 41 ++++------------------- tests/test_shell.py | 6 ++-- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index fac004d01..e7d086fb3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,3 +6,33 @@ @pytest.fixture def testdata(): return str(uuid.uuid4()) + +@pytest.fixture +def find_filter(): + return [ + {"a": 0}, + {"a.b": 0}, + {"a.b": {"$lt": 42}}, + {"a.b.$lt": 42}, + {"$or": [{"a.b": 41}, {"a.b.$lt": 42}]}, + {"$or": [{"a.b": 42}, {"a.b.$lt": 42}]}, + {"$and": [{"a.b": 42}, {"a.b.$lt": 42}]}, + {"$and": [{"a.b": 0}, {"a.b.$lt": 42}]}, + {"$and": [{"a.b.$gte": 0}, {"a.b.$lt": 42}]}, + {"$not": {"a.b": 0}}, + {"$and": [{"a.b.$gte": 0}, {"$not": {"a.b.$lt": 42}}]}, + {"$not": {"$not": {"a.b": 0}}}, + {"a.b": {"$in": [0, 1]}}, + {"a.b": {"$nin": [0, 1]}}, + {"$not": {"a.b": {"$in": [0, 1]}}}, + {"a.b": {"$exists": True}}, + {"a.b": {"$exists": False}}, + {"a": {"$exists": True}}, + {"a": {"$exists": False}}, + {"c": {"$regex": r"^\d$"}}, + {"c": {"$type": "str"}}, + {"d": {"$type": "list"}}, + {"a.b": {"$where": "lambda x: x < 10"}}, + {"a.b": {"$where": "lambda x: isinstance(x, int)"}}, + {"a": {"$regex": "[a][b][c]"}}, + ] diff --git a/tests/test_find_command_line_interface.py b/tests/test_find_command_line_interface.py index ae9c0ad00..0243c6f3f 100644 --- a/tests/test_find_command_line_interface.py +++ b/tests/test_find_command_line_interface.py @@ -11,35 +11,6 @@ from signac.filterparse import parse_filter_arg, parse_simple -# also used in test_shell -FILTERS = [ - {"a": 0}, - {"a.b": 0}, - {"a.b": {"$lt": 42}}, - {"a.b.$lt": 42}, - {"$or": [{"a.b": 41}, {"a.b.$lt": 42}]}, - {"$or": [{"a.b": 42}, {"a.b.$lt": 42}]}, - {"$and": [{"a.b": 42}, {"a.b.$lt": 42}]}, - {"$and": [{"a.b": 0}, {"a.b.$lt": 42}]}, - {"$and": [{"a.b.$gte": 0}, {"a.b.$lt": 42}]}, - {"$not": {"a.b": 0}}, - {"$and": [{"a.b.$gte": 0}, {"$not": {"a.b.$lt": 42}}]}, - {"$not": {"$not": {"a.b": 0}}}, - {"a.b": {"$in": [0, 1]}}, - {"a.b": {"$nin": [0, 1]}}, - {"$not": {"a.b": {"$in": [0, 1]}}}, - {"a.b": {"$exists": True}}, - {"a.b": {"$exists": False}}, - {"a": {"$exists": True}}, - {"a": {"$exists": False}}, - {"c": {"$regex": r"^\d$"}}, - {"c": {"$type": "str"}}, - {"d": {"$type": "list"}}, - {"a.b": {"$where": "lambda x: x < 10"}}, - {"a.b": {"$where": "lambda x: isinstance(x, int)"}}, - {"a": {"$regex": "[a][b][c]"}}, -] - VALUES = {"1": 1, "1.0": 1.0, "abc": "abc", "true": True, "false": False, "null": None} @@ -68,23 +39,25 @@ class TestFindCommandLineInterface: def _parse(args): with redirect_stderr(StringIO()): return parse_filter_arg(args) - - def test_interpret_json(self): + + @pytest.mark.usefixtures("find_filter") + def test_interpret_json(self, find_filter): def _assert_equal(q): # TODO: full code path not tested with this test. # _assert_equal and _find_expression, are not tested assert q == self._parse([json.dumps(q)]) - for f in FILTERS: + for f in find_filter: _assert_equal(f) - def test_interpret_simple(self): + @pytest.mark.usefixtures("find_filter") + def test_interpret_simple(self, find_filter): assert self._parse(["a"]) == {"a": {"$exists": True}} assert next(parse_simple(["a"])) == ("a", {"$exists": True}) for s, v in VALUES.items(): assert self._parse(["a", s]) == {"a": v} - for f in FILTERS: + for f in find_filter: f_ = f.copy() key, value = f.popitem() if key.startswith("$"): diff --git a/tests/test_shell.py b/tests/test_shell.py index 8770d8a9f..172fb1e45 100644 --- a/tests/test_shell.py +++ b/tests/test_shell.py @@ -9,7 +9,6 @@ from tempfile import TemporaryDirectory import pytest -from test_find_command_line_interface import FILTERS from test_project import WINDOWS, _initialize_v1_project, skip_windows_without_symlinks import signac @@ -218,7 +217,8 @@ def test_view_incomplete_path_spec(self): ) assert "duplicate paths" in err - def test_find(self): + @pytest.mark.usefixtures("find_filter") + def test_find(self, find_filter): self.call("python -m signac init".split()) project = signac.Project() sps = [{"a": i} for i in range(3)] @@ -283,7 +283,7 @@ def test_find(self): # ensure that there are no errors due to adding sp and doc prefixes # by testing on all the example complex expressions - for f in FILTERS: + for f in find_filter: command = "python -m signac find ".split() + [json.dumps(f)] self.call(command).strip() From 28336e7ac832948e88100bea4307a13d6877c974 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:20:38 +0000 Subject: [PATCH 5/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/conftest.py | 1 + tests/test_find_command_line_interface.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index e7d086fb3..bda36c69f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,6 +7,7 @@ def testdata(): return str(uuid.uuid4()) + @pytest.fixture def find_filter(): return [ diff --git a/tests/test_find_command_line_interface.py b/tests/test_find_command_line_interface.py index 0243c6f3f..5513d3774 100644 --- a/tests/test_find_command_line_interface.py +++ b/tests/test_find_command_line_interface.py @@ -11,7 +11,6 @@ from signac.filterparse import parse_filter_arg, parse_simple - VALUES = {"1": 1, "1.0": 1.0, "abc": "abc", "true": True, "false": False, "null": None} ARITHMETIC_EXPRESSIONS = [ @@ -39,7 +38,7 @@ class TestFindCommandLineInterface: def _parse(args): with redirect_stderr(StringIO()): return parse_filter_arg(args) - + @pytest.mark.usefixtures("find_filter") def test_interpret_json(self, find_filter): def _assert_equal(q): From 297e85ba7e41c08a37cbc6946620c7800d117461 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Wed, 31 Jan 2024 13:56:27 -0500 Subject: [PATCH 6/7] Fix pydocstyle --- signac/filterparse.py | 1 - 1 file changed, 1 deletion(-) diff --git a/signac/filterparse.py b/signac/filterparse.py index c2bc3a2df..c495ced63 100644 --- a/signac/filterparse.py +++ b/signac/filterparse.py @@ -195,7 +195,6 @@ def parse_filter_arg(args): def _add_prefix(filter): """Add prefix "sp." to a (possibly nested) filter.""" - # Logical operators ($and, $or, $not) should not be prefixed, but their values should. for key, value in filter.items(): if key in ("$and", "$or"): From b4b0cc20dc2aa49cca7c93375e020d6c8c0949bb Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Fri, 2 Feb 2024 16:55:44 -0500 Subject: [PATCH 7/7] Update changelog --- changelog.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.txt b/changelog.txt index 801b6a3b4..671ccdd94 100644 --- a/changelog.txt +++ b/changelog.txt @@ -22,6 +22,11 @@ Changed - linked views now can contain spaces and other characters except directory separators (#926). - linked views now can be created on Windows, if 'Developer mode' is enabled (#430). +Fixed ++++++ + + - Fixed parsing of ``$not`` query expressions on command line (#970). + [2.1.0] -- 2023-07-12 ---------------------