1212... Comparison.less('timestamp', datetime.fromtimestamp(1757422128))
1313... ))
1414>>> print(f)
15- ((( 'source'=='test source');( 'metricType'=in=('number', 'number[]'))),( 'timestamp'<'2025-09-09T14:48:48') )
15+ (('source'=='test source'; 'metricType'=in=('number', 'number[]')), 'timestamp'<'2025-09-09T14:48:48')
1616"""
1717from __future__ import annotations
1818from abc import ABC
1919from datetime import datetime
2020from typing import Any , Iterable , List
2121
2222
23- FieldName = str # TODO: validate field names?
23+ FieldName = str
2424"""https://obelisk.pages.ilabt.imec.be/obelisk-core/query.html#available-data-point-fields
2525Field names are not validated at this time, due to the inherent complexity.
2626"""
2727
2828
2929class Constraint (ABC ):
30+ """
31+ Constraints are simply groups of :class:`Comparison`,
32+ such as :class:`And`, or :class:`Or`.
33+
34+ These constraints always enclose their contents in parentheses,
35+ to avoid confusing precendence situations in serialised format.
36+ """
3037 pass
3138
3239
3340class Comparison ():
41+ """
42+ Comparisons are the basic items of a :class:`Filter`.
43+ They consist of a field name, operator, and possibly a value on the right.
44+
45+ It is strongly suggested you create comparisons by using the staticmethods
46+ on this class, rather than trying to construct them yourselves.
47+
48+ When serializing to RSQL format,
49+ each argument is single quoted as to accept any otherwise reserved characters,
50+ and serialised using :func:`str`.
51+ """
3452 left : FieldName
3553 right : Any
3654 op : str
@@ -42,10 +60,10 @@ def __init__(self, left: FieldName, right: Any, op: str):
4260
4361 def __str__ (self ) -> str :
4462 right = self ._sstr (self .right )
45- if not right .startswith ('(' ):
63+ if not right .startswith ('(' ) and len ( right ) > 0 :
4664 right = f"'{ right } '"
4765
48- return f"( '{ self .left } '{ self .op } { right } ) "
66+ return f"'{ self .left } '{ self .op } { right } "
4967
5068 @staticmethod
5169 def _sstr (item : Any ):
@@ -124,6 +142,18 @@ def __str__(self) -> str:
124142
125143
126144class Filter ():
145+ """
146+ Filter is an easier way to programatically create filters for the Obelisk CORE platform.
147+
148+ We still recommend you familiarise yourself with the CORE filter documentation,
149+ as not everything is typechecked.
150+ Specifically, the left hand side of any comparison is left unchecked.
151+ Checking this would be borderline impossible with the optional arguments to some fields,
152+ and the dot notation for labels.
153+
154+ As this field is not checked, we also do not check whether the type of left operand
155+ and right operand make sense in the provided comparison.
156+ """
127157 content : Item | None = None
128158
129159 def __init__ (self , content : Constraint | None = None ):
@@ -133,13 +163,29 @@ def __str__(self) -> str:
133163 return str (self .content )
134164
135165 def add_and (self , * other : Item ) -> Filter :
166+ """
167+ Encloses the current filter contents, if any,
168+ in an :class:`And`, along with any other provided Items.
169+
170+ >>> str(Filter(Comparison.not_null('labels.username'))
171+ ... .add_and(Comparison.equal('labels.username', 'stpletin'), Comparison.less_equal('timestamp', '2025-01-12T00:00:00Z')))
172+ "('labels.username'=notnull=;'labels.username'=='stpletin';'timestamp'<='2025-01-12T00:00:00Z')"
173+ """
136174 if self .content is None :
137175 self .content = And (* other )
138176 else :
139177 self .content = And (self .content , * other )
140178 return self
141179
142180 def add_or (self , * other : Item ) -> Filter :
181+ """
182+ Encloses the current filter contents, if any,
183+ in an :class:`Or`, along with any other provided Items.
184+
185+ >>> str(Filter(Comparison.not_null('labels.username'))
186+ ... .add_or(Comparison.equal('labels.username', 'stpletin'), Comparison.less_equal('timestamp', '2025-01-12T00:00:00Z')))
187+ "('labels.username'=notnull=,'labels.username'=='stpletin','timestamp'<='2025-01-12T00:00:00Z')"
188+ """
143189 if self .content is None :
144190 self .content = Or (* other )
145191 else :
0 commit comments