1.2.6. Advanced functions¶
1.2.6.1. Backward path¶
We remember you that:
- each item is enveloped into a BagNode.
- each item can be included in other Bags.
This means that a Bag knows its children but not its father (infact a Bag may have more than one father). We could set some stricter hypotesis about a Bag’s structure, making it more similar to a tree-leaf model: this would happen if a Bag had a back reference to its Bag father.
This feature is implemented by the
setBackRef()
method. If we call it on a Bag instance, that Bag becomes the root of a tree structure in which each leaf (BagNode) knows its father. This means that we can traverse a Bag backward using theparent
property of Bag’s nodes:>>> family = Bag() >>> family['grandpa'] = Bag() >>> family['grandpa'].setBackRef() >>> family['grandpa.father.son.nephew']=Bag() >>> nephew = family['grandpa.father.son.nephew'] >>> son = family['grandpa.father.son'] >>> father = family['grandpa.father'] >>> son.parent == father True >>> nephew.parent.parent == father True >>> nephew.parent == son TrueA Bag with back reference can be traversed with special back-paths that use a new syntax. The
../
symbol in a path is equivalent to theparent
property: when the backreference is set, it is possible to get from the Bag its own BagNode:>>> nephew['../../'] == father True
1.2.6.2. Trigger¶
Bag provides a trigger system.
This means that a Bag may be notified when its data changes. Bag triggers are based on the concept of subscription, that is a link between an event (update, insert, delete) with its eventhandler callback functions. The subscribe method defines new subscriptions for update, insert and delete events.
Triggers may be defined either on Bags or BagNodes; to do so, you have to use the
Bag.subscribe()
method and theBagNode.subscribe()
:Bag.subscribe(update=callback1, insert=callback2, delete=callback3, any=callback4) BagNode.subscribe(updval=callback1, updattr=callback2)Where
- “update”, “insert”, “delete” and “any” are the parameters for the Bag’s subscribe method that allow to trigger their relative callback.
- “updval” and “updattr” are the parameters for the BagNode’s subscribe method that allow to trigger their relative callback.
1.2.6.3. Trigger on a Bag: the subscribe method¶
Subscribing an event on a Bag means that every time that the event is triggered, it is propagated along the Bag hierarchy and is triggered by its eventhandler. A subscription can be seen as an event-function couple, so you can define many eventhandlers for the same event.
Let’s consider a Bag like the one shown below:
>>> family = Bag() >>> family['Walt'] = Bag() >>> walt = family['Walt'] >>> walt['children'] = Bag() >>> walt['children.Mickey.weight'] = 32 >>> walt['children.Mickey.height'] = 53 >>> walt['children.Donald.height'] = 51Now we want that the root Bag called “family” is able to handle any data changes that happens within the Bag itself. So we define as an example three eventhandler functions:
def onUpdate(node=None, pathlist=None, oldvalue=None, evt=None, **kwargs): if evt=='upd_value': print """My node at path: %s\n has been updated. Value changed from %s to %s \n""" %('.'.join(pathlist), oldvalue, node.getValue()) if evt=='upd_attrs': print 'My node at path: %s\n has been updated. attributes changed\n' def onDelete(node=None, pathlist=None, ind=None, **kwargs): print 'My node %s at path: %s\n has been deleted from position %i.\n' %(node.getLabel(), '.'.join(pathlist), ind) def onInsert(node=None, pathlist=None, **kwargs): print 'A new node has been inserted at path: %s \n' %('.'.join(pathlist))An eventhandler function receives the following parameters
Parameter Type Description node BagNode
The node inserted/deleted/updated pathlist list
Include the Bag subscribed’s path linked to the node where the event was catched oldvalue any
For value updates only, it is the previous node’s value ind int
The ordinal position of the node inserted/deleted evt string
Event type: insert, delete, upd_value, upd_attrs To allow the “family” Bag to trigger on an insert, on an update and on a delete events, we have to add the
Bag.subscribe()
method to the “family” Bag:>>> family.subscribe(update=onUpdate, insert=onInsert, delete=onDelete) >>> walt['children.Mickey.weight']=36 My node at path: Walt.children.Mickey.weight has been updated. Value changed from 32 to 36>>> walt['children.Donald.weight']=31 A new node has been inserted at path: Walt.children.Donald>>> walt.delItem('children.Mickey.height') My node height at path: walt.children.Mickey has been deleted from position 2.We can add on a Bag many subscriptions for the same event; for example we’ll add a generic trigger that handles any event:
def onBagEvent(node=None, evt=None, pathlist=None, **kwargs): print '%s on node %s at path %s'%(evt, node.getLabel(),('.'.join(pathlist) or 'nullpath'))>>> family.subscribe(any=onBagEvent)Using the “any” parameter is equivalent to set the same callback function for insert, update and delete events. The new subscripstion doesn’t overwrite the existing one, so update events are triggered by both functions.
>>> walt['children.Mickey.weight']=37 My node at path: Walt.children.Mickey.weight has been updated. Value changed from 32 to 37 update on node height at path Walt.children.Mickey.weightSince an event is propagated along the Bag’s hierarchy, it can be triggered by any Bag on the path. In this case there’s an insert trigger subscribed by the Bag children
def onNewChild(node=None, ind=None, **kwargs): print 'Greetings for %s, your son number %i \n' %(node.getLabel(), ind+1)>>> walt['children'].subscribe(insert=onNewChild) >>> walt['children.Goofy']=Bag() Greetings for Goofy, your son number 3 A new node has been inserted at path: Walt.children ins on node children at path WaltAll the trigger functions are executed at different levels, as the event is catched.
1.2.6.4. Unsubscribe a Bag¶
It is possible to unsubscribe a bag from a previously subscribed trigger with the
Bag.unsubscribe()
method.Let’s unsubscribe some of the triggers of our example:
>>> Walt['children'].unsubscribe(insert=onNewChild) >>> family.unsubscribe(insert=onInsert)we have unsubscribed all the events for the insertion.
1.2.6.5. Trigger on a BagNode¶
Sometimes triggering updates of a generic node is not enought: infact a node may need a specific event handling. Trigger on bags assumes that each node is similar to others, that’s why we provide a more accurate way to manage update triggers. A BagNode may define its own triggers, by the method subscribe. Since by node’s update, we mean either value change or attributes change, subscribe method allows two kinds of trigger: upd_value and upd_attrs:
def onValueChange(node, info=None, evt=None): if evt == 'upd_value': print 'My value is changed from %s to %s \n' %(info, node.getValue()) if evt == 'upd_attrs': print 'My attributes: %s is/are changed \n' %(', '.join(info))A trigger function that handles node’s update receives the following parameters:
Parameter Type Description node BagNode
The node that has been updated info list
orany
Old value or list of modified attributes oldvalue any
For value updates only, it is the previous node’s value ind int
The ordinal position of the node inserted/deleted evt string
Event type: upd_value, upd_attrs >>> Walt.getNode('children.Mickey.weight').subscribe(upd_value=onValueChange) >>> Walt['children.Mickey.weight']=55 My value is changed from 36 to 55 My node at path: Walt.children.Mickey.weight has been updated. Value changed from 36 to 55There are a BagNode trigger and a Bag trigger [1] both launched by the update event. The BagNode trigger is launched because the value of the subscribed node is updated, while the Bag trigger is launched because the Bag is subscribed to another update trigger.
1.2.6.6. Validators¶
The basic idea for a Bag validator is to make a control of the data inserted as a node’s value. The validation function for a Bag node can be defined with two different syntaxes:
- through some node attributes.
- using some validator methods.
1.2.6.7. Setting a validator through a node attribute¶
To set a validator through a node attribute you have to use the string
validate_
followed by a validation type:>>> myform.setItem('list.user.name','',validate_case='capitalize')When you overwrite the value at the path ‘list.user.name’ the validator will trigger:
>>> myform['list.user.name'] = 'john smith' >>> print myform['list.user.name'] John smithAs you can see, the validator have capitalized the first word, that is “john”.
1.2.6.8. Values’ list for the validate_
parameter¶
Actually you can set these values:
- validate_case: the parameter string can be ‘upper’, ‘lower’, ‘capitalize’.
- validate_inList: the parameter string is a list of the values accepted eg: ‘value1,value2,value3’.
- validate_length: the parameter string is the min and the max number of char accepted for the value: eg ‘2,4’.
- validate_hostaddr: no parameters.
1.2.6.9. Setting a validator using Bag’s methods¶
To set a validator through the
addValidator()
method you have to give a path, a validator and a parameterString, where:
- path: node’s path.
- validator: validation’s type.
- parameterString: a string which contains the validation parameters.
>>> myform = Bag() >>> myform.addValidator('list.user.name','case','capitalize') >>> myform['list.user.name'] = 'ABCD efgh Ij kLM' >>> print myform 0 - (Bag) list: 0 - (Bag) user: 0 - (str) name: Abcd efgh ij klmThe
removeValidator()
method allow to remove a validator (parameters: path and validator)
Footnotes:
[1] | The Bag trigger is made by the onUpdate function that has been previously defined in the Trigger on a Bag: the subscribe method section. |