From e32e4ed49b0b065b4ff5e9429c0f194b948a9e74 Mon Sep 17 00:00:00 2001 From: Vincenzo Eduardo Padulano Date: Fri, 28 Jun 2019 16:59:16 +0200 Subject: [PATCH] Add Info operations family and start with GetColumnType The "Other Operations" family of functions in the ROOT RDataFrame class has been added to PyRDF in the `Operation` class. This group of operations is called for the moment "Info", since they mostly provide information about the dataframe, such as the names and types of the defined columns. To start the implementation of one of those, this PR supports `GetColumnType` calls on PyRDF nodes. A very basic algorithm to support this feature has been implemented in the Proxy module. When a new `Info` operation is created, since it is not actually a modification of the data, it is not wrapped in a `Proxy`. Instead `_trigger_info_operation` function is called, that triggers the creation of PyROOT objects for all the nodes defined until this moment. In this way, `GetColumnType` can be correctly called on the PyROOT object linked to the PyRDF node. If the logic is accepted, new tests will be added to check this functionality. Following is a sample script to test the new operation: ``` import PyRDF rdf = PyRDF.RDataFrame(100) mydef = rdf.Define("x","rdfentry_ * 2") mycoltype = mydef.GetColumnType("x") print(mycoltype) # ULong64_t ``` --- PyRDF/Operation.py | 22 ++++++++++++++++++++-- PyRDF/Proxy.py | 32 +++++++++++++++++++++++++++++++- PyRDF/backend/Backend.py | 10 +++++++++- PyRDF/backend/Local.py | 8 ++++++++ 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/PyRDF/Operation.py b/PyRDF/Operation.py index 69068e8..357d80c 100644 --- a/PyRDF/Operation.py +++ b/PyRDF/Operation.py @@ -32,7 +32,7 @@ class Operation(object): print(PyRDF.current_backend.supported_operations) """ - Types = Enum("Types", "ACTION TRANSFORMATION INSTANT_ACTION") + Types = Enum("Types", "ACTION TRANSFORMATION INSTANT_ACTION INFO") def __init__(self, name, *args, **kwargs): """ @@ -80,7 +80,15 @@ def _classify_operation(self, name): 'Take': ops.ACTION, 'Graph': ops.ACTION, 'Snapshot': ops.INSTANT_ACTION, - 'Foreach': ops.INSTANT_ACTION + 'Foreach': ops.INSTANT_ACTION, + 'Alias': ops.INFO, + 'GetColumnNames': ops.INFO, + 'GetDefinedColumnNames': ops.INFO, + 'GetColumnType': ops.INFO, + 'GetColumnTypeNamesList': ops.INFO, + 'GetFilterNames': ops.INFO, + 'Display': ops.INFO, + 'SaveGraph': ops.INFO } op_type = operations_dict.get(name) @@ -107,3 +115,13 @@ def is_transformation(self): False otherwise. """ return self.op_type == Operation.Types.TRANSFORMATION + + def is_info(self): + """ + Checks if the current operation is an info. + + Returns: + bool: True if the current operation is an info, + False otherwise. + """ + return self.op_type == Operation.Types.INFO diff --git a/PyRDF/Proxy.py b/PyRDF/Proxy.py index a6aa7fb..12b6fcd 100644 --- a/PyRDF/Proxy.py +++ b/PyRDF/Proxy.py @@ -3,6 +3,7 @@ from abc import ABCMeta, abstractmethod from PyRDF.Operation import Operation from PyRDF.Node import Node +import ROOT # Abstract class declaration # This ensures compatibility between Python 2 and 3 versions, since in # Python 2 there is no ABC class @@ -89,6 +90,28 @@ def _call_action_result(self, *args, **kwargs): return getattr(self.GetValue(), self._cur_attr)(*args, **kwargs) +def _trigger_info_operation(node, op, *args, **kwargs): + """ + Function to retrieve the PyROOT node objects defined by the user + up to the point of calling an Info operation. This function triggers the + creation of PyROOT objects in order to call the ROOT function issued by the + user. + """ + generator = CallableGenerator(node.get_head()) + + mapper = generator.get_callable() # Get the callable + + # Create a PyROOT RDataFrame + pyroot_rdf = ROOT.ROOT.RDataFrame(*generator.head_node.args) + + # Execute the mapper function + mapper(pyroot_rdf) + + # Call the function on the PyROOT object with the arguments provided by + # the user. + return getattr(node.pyroot_node, op.name)(*args, **kwargs) + + class TransformationProxy(Proxy): """ A proxy object to an non-action node. It implements acces to attributes @@ -133,8 +156,15 @@ def _create_new_op(self, *args, **kwargs): # Add the new node as a child of the current node self.proxied_node.children.append(newNode) - # Return the appropriate proxy object for the node + # Behave accordingly to the type of the operation if op.is_action(): return ActionProxy(newNode) + elif op.is_info(): + return _trigger_info_operation( + self.proxied_node, + op, + *args, + **kwargs + ) else: return TransformationProxy(newNode) diff --git a/PyRDF/backend/Backend.py b/PyRDF/backend/Backend.py index 69ff786..90cb8e2 100644 --- a/PyRDF/backend/Backend.py +++ b/PyRDF/backend/Backend.py @@ -40,7 +40,15 @@ class Backend(ABC): 'Foreach', 'Reduce', 'Aggregate', - 'Graph' + 'Graph', + 'Alias', + 'GetColumnNames', + 'GetDefinedColumnNames', + 'GetColumnType', + 'GetColumnTypeNamesList', + 'GetFilterNames', + 'Display', + 'SaveGraph' ] initialization = staticmethod(lambda: None) diff --git a/PyRDF/backend/Local.py b/PyRDF/backend/Local.py index 04d0b58..e57258d 100644 --- a/PyRDF/backend/Local.py +++ b/PyRDF/backend/Local.py @@ -28,6 +28,14 @@ def __init__(self, config={}): 'Foreach', 'Reduce', 'Aggregate', + 'Alias', + 'GetColumnNames', + 'GetDefinedColumnNames', + # 'GetColumnType', + 'GetColumnTypeNamesList', + 'GetFilterNames', + 'Display', + 'SaveGraph' ] if ROOT.ROOT.IsImplicitMTEnabled(): # Add `Range` operation only if multi-threading