November 5, 2015

Keyword Arguments in P4Python

Integration

Since 2011.1, the P4.__init__ constructor and the P4.run method have both supported keyword arguments, simplifying and shortening some of the typical call patterns we have seen in a number of scripts.

Keyword arguments are optional arguments of the form kwarg=value.

Python methods can announce that they accept keyword arguments in two ways:

Explicitly stating the keyword argument with a default value in the argument list:

def function(cmd, state='awesome'):
print(cmd + ' is ' + state)

Or collecting an arbitrary number of keyword arguments in a dictionary:

def function(cmd, **kargs):
      state = 'awesome'
      if 'state' in kargs:
            state = kargs['state']
      print(cmd + ' is ' + state)

The method signature in P4Python follows the second pattern, allowing an arbitrary number of standard and keyword arguments:

class P4:
      def __init__(self, *args, **kargs):
            // ...
      def __run__(self, *args, **kargs):
            // ...

Constructor Keywords

For the constructor, you can pass in keywords such as user, port, or client. This is particularly useful if you want to parameterize a script, since you can pass in all connection parameters in one single dictionary. One example is a trigger.

Look at P4Triggers.py in the workshop for a framework that makes use of this idea. The constructor of P4Trigger effectively looks like this:

class P4Trigger:
      def __init__(self, **kargs):
            self.p4 = P4.P4(**kargs)

The constructor passes the keyargs straight to the constructor of the P4 connection object. A typical subclass of P4Trigger (such as the CaseCheckTrigger) expects its arguments from the command line in the form,

CheckCaseTrigger.py change [karg=value]*

This allows me to call the trigger from the Helix server using the trigger table in this form:

checkcase change-submit //... “CheckCaseTrigger.py %change% port=%port%

Fully parameterized, and no need for any hardcoding of port or any other parameter I want to pass in.

Context parameters in the run method

The other method, run, accepts keyword arguments to temporarily change the behavior of the command to be executed. Typical candidates are tagged mode, exception_level, or even the user or client a command is run under.

For example, I can avoid the potential exception “[Warning]: '... - file(s) up-to-date.'” when syncing a workspace by lowering the exception_level to 1:

p4.run_sync(exception_level = 1)

This is shorter than setting the exception level explicitly before running the command and then potentially forgetting to reset it to its original value. If you need to change to a different context for a range of commands, you can use the with statement instead.

Subclassing P4

When subclassing P4, make sure to pass in the keyword arguments into the constructor and especially the P4.run method, if you decide to wrap that method. Internally, P4 uses keyword arguments to pass state between some overridden run methods such as P4.run_print and P4.run_filelog. This is used for the new logging capability built into P4Python -- I will explain that in a future post.

Therefore,

class MyClass(P4.P4):
      def __init__(self, *args, **kargs):
            P4.P4.__init__(self, *args, **kargs)
      def run(self, *args, **kargs):
            P4.P4.run(self, *args, **kargs)

If you miss the keyword arguments, you will get an exception when you run P4.run_filelog or P4.run_print: “TypeError: run() got an unexpected keyword argument 'resultLogging'”

Feel free to try out these keyword arguments in your own scripts or look at the examples in P4PythonLib in the workshop to see how much more compact your scripts can be. As usual, contact me @p4sven if you have any questions or feedback.

Happy hacking!