Customizing

P4 is meant to be customized.

  • You can change variables found in p4.var.Var

  • You can semi-permanently add your own code into files that p4 reads on startup. Doing that you can extend or over-ride the behaviour of p4.

    • You want p4 Alignments to do something else? - Add it!

    • You don’t like the way that p4 Tree objects are written? - Change it!

Setting var variables

You can change the default behaviour by setting or un-setting some variables in var (which is an instance of the p4.var.Var class, but you don’t really need to know that). For example, when p4 reads files it is by default not verbose, not describing what it is doing as it goes. This is good everyday behaviour, but not very good for debugging when something goes wrong. You can make it verbose by setting:

var.verboseRead=True

Generally you will get your phylogenetic stuff into p4 from files, using the read() function. The read() function also reads strings, which is occasionally handy. If you hand the read() function a string, it will first ask if there is a file by that name, and if so it will attempt to read it. If there is no file, it will try it as a string. The problem is that if you mis-spell a file name, you are liable to get a bunch of confusing error messages as it attempts to read the file name as some sort of phylogenetic data. So the default behaviour when it can’t find a file is to give a little warning, and you can turn that warning off by setting:

var.warnReadNoFile=False

There are several other things in the p4.var.Var class that you might want to know about. Take a look.

Note

You can make these settings to var, either

  • temporarily

    • in interactive p4

    • at the top of individual scripts

  • or in startup scripts

    • possibly in your ~/.p4 directory

Add your own code

You can of course add your own code in your p4 script to extend p4 - perhaps to add your own function, or to extend a p4 Class with an additional method or two. You might put that part of the code in a separate file in your working directory and ask your script to load that module. But if you find your new code generally useful you might want to semi-permanently add it to your p4, so that it is read in for any script in any directory, without you needing to explicitly ask it to do so. To do that, you can put your code in a directory known to p4, and p4 will read it on startup. One such directory is ~/.p4. So if you put python files in there, p4 will find them and read them on startup. Other directories can be specified by the environment variable P4_STARTUP_DIRS. That variable is a colon-separated list of directory names.

So for example when p4 starts up, by default it prints out a splash screen. Whether that is done or not is controlled by var.doSplash. When you get tired of seeing the splash screen, you can turn it off by setting var.doSplash=False. But wait! - By the time you have the opportunity to set that variable, its already too late– the splash screen has already displayed itself. The solution is to put var.doSplash=False in a file ending in .py in a .p4 directory in your home directory, or in another of the P4_STARTUP_DIRS. Any file ending in .py in one of those directories is read on startup. Its a good place to put var.warnReadNoFile=False, for example.

Since it is Python, you can add methods to existing classes. For example, if you have this in your script:

def greet(self):
    print "Howdy!"
Alignment.greet = greet
del(greet)

and you had an Alignment instance a, you could then do:

p4> a.greet()
Howdy!
p4>

You can over-ride existing methods that way also. If you don’t like the way p4 works, change it to suit yourself.

A good place to put these new methods and functions is in files in the ~/.p4 directory, or in one of your specified P4_STARTUP_DIRS. You can easily turn off files, but allow them to remain there for later, by changing the file name so that it no longer ends in .py, or just put them in a sub-directory.

One special file that is read in after the others, but only if you are interactive, is the ~/.p4/interactive file, and note that while it is a Python file, it does not end in .py. I like to instantiate a few blank objects in there, for quick access to the doc strings via completion (see below). Here is my current interactive file, for what it’s worth:

#print('Here is the interactive startup file.')

# Make some blank objects.
if 1:
    a0 = Alignment()
    d0 = Data([])
    t0 = Tree()
    n0 = Node()
    #tt0 = Trees()

# Give single Alignments, Trees, or SequenceLists names.
if pyFileCount == 0:
    if len(var.alignments) == 1:
        print("There is one alignment in var.alignments-- I am naming it 'a'")
        a = var.alignments[0]
    if len(var.trees) == 1:
        print("There is one tree in var.trees-- I am naming it 't'")
        t = var.trees[0]
    if len(var.sequenceLists) == 1:
        print("There is one sequenceList in var.sequenceLists-- I am naming it 'sl'")
        sl = var.sequenceLists[0]

#import sys
#sys.ps1 = '>>> '

Using interactive helpers

If you are using p4 interactively you can set it up so that you get

  • completion

  • previous commands

  • signatures of functions and methods

  • doc strings

This week, there are three ways to do it —

  • p3rlcompleter (comes with p4, based on rlcompleter that comes with Python)

  • bpython

  • ipython

You can turn it on by setting p4.var.Var.interactiveHelper, which is by default None, for example:

var.interactiveHelper = 'p3rlcompleter'

In p3rlcompleter,

  • you get signatures (argspecs) inserted in place

  • in the attribute listing, you get an indication of whether the attribute is a function or method (with a ()) or a list, a numpy array, or a plain variable

However, ipython and bpython look a lot nicer! With colour!

Using p3rlcompleter

Completion is a memory aid and can save you a lot of keystrokes. To use this, you need to have the readline library linked to your Python. I have modified the wonderful rlcompleter module that comes with Python so that it is slightly more helpful. Completion will remind you of methods and functions, and their arguments, classes, variables, and documentation.

If you type <tab>, a partially typed name is completed up to the point of ambiguity. If it is unambiguous, the whole thing is completed. If what you typed is ambiguous, then the function or method is competed only up to the point of ambiguity. At a point of ambiguity, typing <tab><tab> tells you your options.

For example, if you type:

func.chi<tab>

then, since this is unambiguous (there is only one function in the func module that starts with chi) p4 will complete it, resulting in

func.chiSquaredProb()

If you just type:

func.<tab>

then nothing happens, because it is ambiguous at that point. Typing a second <tab> tells you the available possibilities. Then you can type more of the name to resolve the ambiguity and finish it up with a <tab>.

You can also get completion starting from nothing. If you just type

<tab><tab>

then you get all the top level names that p4 knows about.

All this works for method names also. For example, type the name of the class or object, and the dot. If you then type <tab><tab> all the available methods and instance variable names appropriate to that object are given.

If you type a function or method name up to the argument list, but not including the opening parenthesis, and then a dot, and then <tab><tab>, then the documentation for the function or method is given, if it exists. For example, do this to get the documentation for the read function:

read.<tab><tab>

To get the documentation for the Alignment method translate():

Alignment.translate.<tab><tab>

Generally, functions and methods tell you that they are functions and methods by printing out a pair of parens after the name (eg foobar()). You can get the argspec (the stuff that goes inside the parens) by completing the name followed by a single open paren. So, for example, using an Alignment instance a, to get the argspec for the method translate(), you might say:

a.tr<tab>

which gets completed to:

a.translate()

You can back over the unparen and get the argspec (signature) by:

a.translate(<tab>

which then tells you:

a.translate(transl_table=1)

In this example, I asked for completion using an Alignment instance a, but it works using the Alignment class as well. Note that in methods, the first argument is self; I skip that in argspecs via completion, but retain it in the documentation output via completion.

Tip

You can delete the entire line backwards with control-u. Delete everything to the end of the line with control-k

Interfacing with your editor

In my world, I use a terminal to run p4 and emacs to edit my scripts and code. When you use emacs to run your Python code and you get a traceback, you can use that traceback to easily go to those locations in the source. That is brilliant functionality, but I have never got the hang of running Python in emacs — so I use a terminal. Using the terminal is simpler, but I still want that traceback functionality.

Using the terminal, when I get a traceback from an exception I want to go to where the error is, or perhaps one of the previous places in the traceback stack, and I want to go quickly and easily and start editing. For that I use an exceptionhook that invokes the editor and goes to the right line in the right file. It is under the control of p4.var.Var.excepthookEditor. It is by default None, but you can turn it on by:

var.excepthookEditor = 'emacsclient -n'

It appears to work for vim, as well, although I have not tested it well.