From b2617be7b4f4d994231e352d99ad25141d9f168c Mon Sep 17 00:00:00 2001 From: Albert Sola Date: Thu, 7 Aug 2025 13:58:22 +0100 Subject: [PATCH] MPT-12584 Fix use of private arguments in RQL Query constructor --- mpt_api_client/rql/query_builder.py | 58 ++++++++++++++-------- tests/rql/query_builder/test_create_rql.py | 40 +++++++++++++++ 2 files changed, 76 insertions(+), 22 deletions(-) diff --git a/mpt_api_client/rql/query_builder.py b/mpt_api_client/rql/query_builder.py index 9f3d43be..9ffde656 100644 --- a/mpt_api_client/rql/query_builder.py +++ b/mpt_api_client/rql/query_builder.py @@ -148,29 +148,43 @@ class RQLQuery: # noqa: WPS214 def __init__( # noqa: WPS211 self, - _field: str | None = None, - *, - _op: str = EXPRESSION, - _children: list["RQLQuery"] | set["RQLQuery"] | None = None, - _negated: bool = False, - _expr: str | None = None, + namespace_: str | None = None, # noqa: WPS120 **kwargs: QueryValue, ) -> None: - self.op = _op - self.children: list[RQLQuery] = list(_children) if _children else [] - self.negated = _negated - self.expr = _expr + self.op: str = self.EXPRESSION + self.children: list[RQLQuery] = [] + self.negated: bool = False + self.expr: str | None = None self._path: list[str] = [] self._field: str | None = None - if _field: - self.n(_field) + if namespace_: + self.n(namespace_) if len(kwargs) == 1: self.op = self.EXPRESSION self.expr = parse_kwargs(kwargs)[0] if len(kwargs) > 1: self.op = self.AND for token in parse_kwargs(kwargs): - self.children.append(self.__class__(_expr=token)) + self.children.append(self.new(expr=token)) + + @classmethod + def new( + cls, + expr: str | None = None, + *, + negated: bool = False, + op: str | None = None, + children: list["RQLQuery"] | set["RQLQuery"] | None = None, + ) -> Self: + """Create a new RQLQuery object from a expression or from a set of op and children.""" + if isinstance(children, set): + children = list(children) + query = cls() + query.op = op or cls.EXPRESSION + query.children = children or [] + query.negated = negated + query.expr = expr + return query def __len__(self) -> int: if self.op == self.EXPRESSION: @@ -221,10 +235,10 @@ def __or__(self, other: object) -> Self: return self._join(other, self.OR) def __invert__(self) -> Self: - inverted_query = self.__class__( - _op=self.AND, - _expr=self.expr, - _negated=True, + inverted_query = self.new( + op=self.AND, + expr=self.expr, + negated=True, ) inverted_query._append(self) # noqa: SLF001 return inverted_query @@ -449,10 +463,10 @@ def _to_string(self, query: "RQLQuery") -> str: return f"{query.op}({str_tokens})" def _copy(self, other: "RQLQuery") -> Self: - return self.__class__( - _op=other.op, - _children=other.children.copy(), - _expr=other.expr, + return self.new( + op=other.op, + children=other.children.copy(), + expr=other.expr, ) def _join(self, other: "RQLQuery", op: str) -> Self: @@ -463,7 +477,7 @@ def _join(self, other: "RQLQuery", op: str) -> Self: if not self: return self._copy(other) - query = self.__class__(_op=op) + query = self.new(op=op) query._append(self) # noqa: SLF001 query._append(other) # noqa: SLF001 return query diff --git a/tests/rql/query_builder/test_create_rql.py b/tests/rql/query_builder/test_create_rql.py index feb55e52..e9a72bb8 100644 --- a/tests/rql/query_builder/test_create_rql.py +++ b/tests/rql/query_builder/test_create_rql.py @@ -3,6 +3,7 @@ def test_create(): query = RQLQuery() + assert query.op == RQLQuery.EXPRESSION assert query.children == [] assert query.negated is False @@ -10,13 +11,16 @@ def test_create(): def test_create_with_field(): query = RQLQuery("field") + query.eq("value") + assert query.op == RQLQuery.EXPRESSION assert str(query) == "eq(field,value)" def test_create_single_kwarg(): query = RQLQuery(id="ID") + assert query.op == RQLQuery.EXPRESSION assert str(query) == "eq(id,ID)" assert query.children == [] @@ -25,6 +29,7 @@ def test_create_single_kwarg(): def test_create_multiple_kwargs(): # noqa: WPS218 query = RQLQuery(id="ID", status__in=("a", "b"), ok=True) + assert query.op == RQLQuery.AND assert str(query) == "and(eq(id,ID),in(status,(a,b)),eq(ok,true))" assert len(query.children) == 3 @@ -37,3 +42,38 @@ def test_create_multiple_kwargs(): # noqa: WPS218 assert query.children[2].op == RQLQuery.EXPRESSION assert query.children[2].children == [] assert str(query.children[2]) == "eq(ok,true)" + + +def test_new_empty(): + query = RQLQuery.new() + + assert query.op == RQLQuery.EXPRESSION + assert query.children == [] + assert query.negated is False + + +def test_new_with_parameters(): + project_rql = RQLQuery.new("project=rql") + status_not_done = RQLQuery.new("status=done", negated=True) + + query = RQLQuery.new( + op=RQLQuery.AND, + children=[project_rql, status_not_done], + ) + + assert str(query) == "and(project=rql,not(status=done))" + + +def test_new_with_set(): + project_rql = RQLQuery.new("project=rql") + status_not_done = RQLQuery.new("status=done", negated=True) + + query = RQLQuery.new( + op=RQLQuery.AND, + children={project_rql, status_not_done}, + ) + + assert isinstance(query.children, list) + assert len(query.children) == 2 + assert project_rql in query.children + assert status_not_done in query.children