Skip to content

Commit

Permalink
Rename selection_mode to str_selection_mode.
Browse files Browse the repository at this point in the history
  • Loading branch information
riga committed Feb 17, 2023
1 parent 4d5008b commit 934e065
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 57 deletions.
10 changes: 5 additions & 5 deletions order/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
@unique_tree(parents=-1, deep_children=True, deep_parents=True)
class Category(UniqueObject, CopyMixin, AuxDataMixin, TagMixin, SelectionMixin, LabelMixin):
""" __init__(name, id="+", channel=None, categories=None, label=None, label_short=None, \
selection=None, selection_mode=None, tags=None, aux=None)
selection=None, str_selection_mode=None, tags=None, aux=None)
Class that describes an analysis category. This is not to be confused with an analysis
:py:class:`Channel`. While the definition of a channel can be understood as being fixed by e.g.
the final state of an event, a category describes an arbitrary sub phase-space. Therefore, a
Expand All @@ -30,7 +30,7 @@ class Category(UniqueObject, CopyMixin, AuxDataMixin, TagMixin, SelectionMixin,
are initialized with *categories*.
*label* and *label_short* are forwarded to the :py:class:`~order.mixins.LabelMixin`, *selection*
and *selection_mode* to the :py:class:`~order.mixins.SelectionMixin`, *tags* to the
and *str_selection_mode* to the :py:class:`~order.mixins.SelectionMixin`, *tags* to the
:py:class:`~order.mixins.TagMixin`, *aux* to the :py:class:`~order.mixins.AuxDataMixin`, and
*name* and *id* (defaulting to an auto id) to the :py:class:`~order.unique.UniqueObject`
constructor.
Expand All @@ -54,7 +54,7 @@ class Category(UniqueObject, CopyMixin, AuxDataMixin, TagMixin, SelectionMixin,
import order as od
# toggle the default selection mode to Root-style selection string concatenation
od.Category.default_selection_mode = "root"
od.Category.default_str_selection_mode = "root"
cat = od.Category(
name="4j",
Expand Down Expand Up @@ -172,15 +172,15 @@ def __init__(
label=None,
label_short=None,
selection=None,
selection_mode=None,
str_selection_mode=None,
tags=None,
aux=None,
):
UniqueObject.__init__(self, name, id)
CopyMixin.__init__(self)
AuxDataMixin.__init__(self, aux=aux)
TagMixin.__init__(self, tags=tags)
SelectionMixin.__init__(self, selection=selection, selection_mode=selection_mode)
SelectionMixin.__init__(self, selection=selection, str_selection_mode=str_selection_mode)
LabelMixin.__init__(self, label=label, label_short=label_short)

# register empty attributes
Expand Down
83 changes: 44 additions & 39 deletions order/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -670,12 +670,13 @@ def data_source(self):

class SelectionMixin(object):
"""
Mixin-class that adds attibutes and methods to describe a selection rule using ROOT- or
numexpr-style expression syntax, or a bare callable.
Mixin-class that adds attibutes and methods to describe a selection rule, either as strings,
bare callables, or sequences of the two. When rules are defined as strings, ROOT- or
numexpr-style expression syntax are supported with convenient logical concatenation.
**Arguments**
*selection* and *selection_mode* initialize the same-named attributes.
*selection* and *str_selection_mode* initialize the same-named attributes.
**Example**
Expand All @@ -686,8 +687,8 @@ class SelectionMixin(object):
class MyClass(od.SelectionMixin):
pass
# ROOT-style expressions
c = MyClass(selection="branchA > 0", selection_mode=MyClass.MODE_ROOT)
# ROOT-style string expressions
c = MyClass(selection="branchA > 0", str_selection_mode=MyClass.MODE_ROOT)
c.selection
# -> "branchA > 0"
Expand All @@ -700,8 +701,8 @@ class MyClass(od.SelectionMixin):
c.selection
# -> "((myBranchA > 0) && (myBranchB < 100)) * (myWeight)"
# numexpr-style expressions
c = MyClass(selection="branchA > 0", selection_mode=MyClass.MODE_NUMEXPR)
# numexpr-style string expressions
c = MyClass(selection="branchA > 0", str_selection_mode=MyClass.MODE_NUMEXPR)
c.add_selection("myBranchB < 100")
c.selection
Expand All @@ -713,7 +714,7 @@ def my_selection(*args, **kwargs):
c.selection = my_selection
c.selection
# -> <function my_selection()>
c.selection_mode
c.str_selection_mode
# -> None
c.add_selection("myBranchB < 100")
# -> TypeError
Expand All @@ -730,20 +731,20 @@ def my_selection(*args, **kwargs):
Flag denoting the numexpr-style selection mode (``"numexpr"``).
.. py:classattribute:: default_selection_mode
.. py:classattribute:: default_str_selection_mode
type: string
The default *selection_mode* when none is given in the instance constructor. It is initially
set to *MODE_NUMEXPR* if :py:attr:`order.util.ROOT_DEFAULT` is *false*, or to *MODE_ROOT*
otherwise.
The default *str_selection_mode* when none is given in the instance constructor. It is
initially set to *MODE_NUMEXPR* if :py:attr:`order.util.ROOT_DEFAULT` is *false*, or to
*MODE_ROOT* otherwise.
.. py:attribute:: selection
type: string, callable
type: string, callable, list
The selection string or a callable. When a string, :py:attr:`selection_mode` decides how the
string is treated.
The selection string, callable or a sequence of them. When a string,
:py:attr:`str_selection_mode` decides how the string is treated.
.. py:attribute:: selection_mode
.. py:attribute:: str_selection_mode
type: string, None
The selection mode. Should either be *MODE_ROOT* or *MODE_NUMEXPR*. Only considered when
Expand All @@ -753,23 +754,23 @@ def my_selection(*args, **kwargs):
MODE_ROOT = "root"
MODE_NUMEXPR = "numexpr"

default_selection_mode = MODE_ROOT if ROOT_DEFAULT else MODE_NUMEXPR
default_str_selection_mode = MODE_ROOT if ROOT_DEFAULT else MODE_NUMEXPR

copy_specs = []

def __init__(self, selection=None, selection_mode=None):
def __init__(self, selection=None, str_selection_mode=None):
super(SelectionMixin, self).__init__()

# instance members
self._selection = "1"
self._selection_mode = None
self._str_selection_mode = None

# fallback to default selection mode
if selection_mode is None:
selection_mode = self.default_selection_mode
if str_selection_mode is None:
str_selection_mode = self.default_str_selection_mode

# set initial values
self.selection_mode = selection_mode
self.str_selection_mode = str_selection_mode
if selection is not None:
self.selection = selection

Expand All @@ -780,15 +781,19 @@ def selection(self):

@selection.setter
def selection(self, selection):
if callable(selection):
if selection is None:
raise TypeError("invalid selection: {}".format(selection))

# just store the valud when not a string
if not isinstance(selection, six.string_types):
self._selection = selection
self._selection_mode = None
self._str_selection_mode = None
return

# get the selection mode
if self.selection_mode == self.MODE_ROOT:
# interpret as string, get the selection mode
if self.str_selection_mode == self.MODE_ROOT:
join = join_root_selection
elif self.selection_mode == self.MODE_NUMEXPR:
elif self.str_selection_mode == self.MODE_NUMEXPR:
join = join_numexpr_selection
else:
raise Exception("when selection is a string, selection mode must be set")
Expand All @@ -799,19 +804,19 @@ def selection(self, selection):
raise TypeError("invalid selection type: {}".format(selection))

@typed
def selection_mode(self, selection_mode):
# selection mode parser
if selection_mode is None:
return selection_mode
def str_selection_mode(self, str_selection_mode):
# str_selection_mode parser
if str_selection_mode is None:
return str_selection_mode

if not isinstance(selection_mode, six.string_types):
raise TypeError("invalid selection_mode type: {}".format(selection_mode))
if not isinstance(str_selection_mode, six.string_types):
raise TypeError("invalid str_selection_mode type: {}".format(str_selection_mode))

selection_mode = str(selection_mode)
if selection_mode not in (self.MODE_ROOT, self.MODE_NUMEXPR):
raise ValueError("unknown selection_mode: {}".format(selection_mode))
str_selection_mode = str(str_selection_mode)
if str_selection_mode not in (self.MODE_ROOT, self.MODE_NUMEXPR):
raise ValueError("unknown str_selection_mode: {}".format(str_selection_mode))

return selection_mode
return str_selection_mode

def add_selection(self, selection, **kwargs):
"""
Expand All @@ -826,9 +831,9 @@ def add_selection(self, selection, **kwargs):
"selection {}".format(self.selection),
)

if self.selection_mode == self.MODE_ROOT:
if self.str_selection_mode == self.MODE_ROOT:
join = join_root_selection
elif self.selection_mode == self.MODE_NUMEXPR:
elif self.str_selection_mode == self.MODE_NUMEXPR:
join = join_numexpr_selection
else:
raise Exception("when selection is a string, selection mode must be set")
Expand Down
14 changes: 12 additions & 2 deletions order/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ def flatten(struct, depth=-1):
return [struct]


def try_float(obj):
"""
Tries to cast *obj* to float and returns it. If the conversion fails, *None* is returned.
"""
try:
return float(obj)
except:
return None


def to_root_latex(s):
"""
Converts latex expressions in a string *s* to ROOT-compatible latex.
Expand All @@ -193,8 +203,8 @@ def _parse_selection(*selection):
if s == 1:
continue
elif isinstance(s, six.string_types):
# special case: skip empty strings
if not s.strip():
# special case: skip empty strings and ones
if not s.strip() or try_float(s) == 1:
continue
else:
raise Exception("invalid selection string: {}".format(s))
Expand Down
6 changes: 3 additions & 3 deletions order/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Variable(UniqueObject, CopyMixin, AuxDataMixin, TagMixin, SelectionMixin):
*y_title_short*, *unit*, *unit_format* and *null_value*. See the attribute listing below for
further information.
*selection* and *selection_mode* are passed to the :py:class:`~order.mixins.SelectionMixin`,
*selection* and *str_selection_mode* are passed to the :py:class:`~order.mixins.SelectionMixin`,
*tags* to the :py:class:`~order.mixins.TagMixin`, *aux* to the
:py:class:`~order.mixins.AuxDataMixin`, and *name* and *id* (defaulting to an automatically
increasing id) to the :py:class:`~order.unique.UniqueObject` constructor.
Expand Down Expand Up @@ -258,7 +258,7 @@ def __init__(
unit_format="{title} / {unit}",
null_value=None,
selection=None,
selection_mode=None,
str_selection_mode=None,
tags=None,
aux=None,
# backwards compatibility
Expand All @@ -271,7 +271,7 @@ def __init__(
CopyMixin.__init__(self)
AuxDataMixin.__init__(self, aux=aux)
TagMixin.__init__(self, tags=tags)
SelectionMixin.__init__(self, selection=selection, selection_mode=selection_mode)
SelectionMixin.__init__(self, selection=selection, str_selection_mode=str_selection_mode)

# instance members
self._expression = None
Expand Down
18 changes: 10 additions & 8 deletions tests/test_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ class C(DataSourceMixin):
class SelectionMixinTest(unittest.TestCase):

def test_constructor_root(self):
s = SelectionMixin("myBranchC > 0", selection_mode=SelectionMixin.MODE_ROOT)
s = SelectionMixin("myBranchC > 0", str_selection_mode=SelectionMixin.MODE_ROOT)
self.assertEqual(s.selection, "myBranchC > 0")

s.add_selection("myBranchD < 100", bracket=True)
Expand All @@ -239,7 +239,7 @@ def test_constructor_root(self):
)

def test_constructor_numexpr(self):
s = SelectionMixin("myBranchC > 0", selection_mode=SelectionMixin.MODE_NUMEXPR)
s = SelectionMixin("myBranchC > 0", str_selection_mode=SelectionMixin.MODE_NUMEXPR)
self.assertEqual(s.selection, "myBranchC > 0")

s.add_selection("myBranchD < 100", bracket=True)
Expand All @@ -255,15 +255,15 @@ def test_constructor_numexpr(self):
)

def test_constructor_callable(self):
s = SelectionMixin("myBranchC > 0", selection_mode=SelectionMixin.MODE_NUMEXPR)
s = SelectionMixin("myBranchC > 0", str_selection_mode=SelectionMixin.MODE_NUMEXPR)
self.assertEqual(s.selection, "myBranchC > 0")

s.selection = lambda: None
self.assertTrue(callable(s.selection))
self.assertIsNone(s.selection_mode)
self.assertIsNone(s.str_selection_mode)

def test_selections(self):
s = SelectionMixin(selection_mode=SelectionMixin.MODE_ROOT)
s = SelectionMixin(str_selection_mode=SelectionMixin.MODE_ROOT)

s.selection = "myBranchC > 0"
self.assertEqual(s.selection, "myBranchC > 0")
Expand All @@ -275,11 +275,13 @@ def test_selections(self):
s.add_selection("myBranchD > 0", op="||", bracket=True)
self.assertEqual(s.selection, "((myBranchC > 0) || (myBranchD > 0))")

s.selection = ["myBranchC > 0", "myBranchE > 0"]
s.selection = "1"
s.add_selection(["myBranchC > 0", "myBranchE > 0"])
self.assertEqual(s.selection, "(myBranchC > 0) && (myBranchE > 0)")

s.selection_mode = SelectionMixin.MODE_NUMEXPR
s.selection = ["myBranchC > 0", "myBranchE > 0"]
s.selection = "1"
s.str_selection_mode = SelectionMixin.MODE_NUMEXPR
s.add_selection(["myBranchC > 0", "myBranchE > 0"])
self.assertEqual(s.selection, "(myBranchC > 0) & (myBranchE > 0)")


Expand Down

0 comments on commit 934e065

Please sign in to comment.