Initializing a connection

Using dependency injection

Dependency injection is the most straightforward way to initialize P4OO, giving clear and direct control to the caller.

Establish a connection using P4Python

    p4Handle = P4.P4(port="p4-server:1666", user="perforce_user").connect()

Create P4OO.py objects using that connection

When constructing any P4OO.py object, simply pass in your p4Handle object as p4PythonObj

    changeCounter = P4OOCounter(id="change", p4PythonObj=p4Handle)
  OR
    lastSubmittedChange = P4OOChangeSet(p4PythonObj=p4Handle).query(status="submitted", max=1)[0]

Using the environment or p4 defaults

Just like 'p4' and P4Python, P4OO.py can be used without any special initialization. P4OO.py will provide its own P4Python connection and work with P4Python's defaults or environment. No special syntax, just construct P4OO.py objects without passing in the 'p4PythonObj' argument.

    changeCounter = P4OOCounter(id="change")

WARNING: When using defaults, watch out for P4 environment changes between calls. You may get inconsistent results across different P4OO.py objects as they get initialized with different connections.

Connection persistence

As new P4OO.py objects are returned from P4OO.py operations, they are seeded with the connection attributes of the object creating them. Objects created from different connections can be used simultaneously without conflict.

Working with multiple connections simultaneously

In an offline backup situation, we might want to examine our offline replica and compare its content to our production server. With P4OO.py it is very easy to accomplish this.
    offlineP4Port = "rsh:%s -r %s -J off -i" % (p4d_bin, p4Root,)

    offlineP4Handle = P4.P4(port=offlineP4Port, user=p4SysUser).connect()
    masterP4Handle = P4.P4(port=masterP4Port, user=p4SysUser).connect()

    offlineJournalCounter = P4OOCounter(id="journal", p4PythonObj=offlineP4Handle)
    masterJournalCounter = P4OOCounter(id="journal", p4PythonObj=masterP4Handle)

Working with P4OO.py Objects

Objects as arguments

In P4OO.py, P4OO.py objects can be passed around as arguments to operations on other objects. Where a client name or user name is required, the string name can be provided. A P4OO.py object identified by that id may also be provided, it is not necessary to serialize objects within P4OO.py.

Example

Given these variables
    userName = "dave"
    p4UserObj = P4OOCounter(id=userName, p4PythonObj=p4Handle)
The following will produce the same results:
    userClients = P4OOClientSet(p4PythonObj=p4Handle).query(user=userName)
    userClients = P4OOClientSet(p4PythonObj=p4Handle).query(user=p4UserObj)

Sets of objects

Set Operations

addObjects

delObjects

listObjectIDs

Method to help convert a set of objects into a list of the names of the objects.

Lazy Initialization and Caching for Performance

Wherever possible, P4OO.py uses lazy initialization. Specs are not read until attributes are requested from the spec. Additionally, spec attributes are cached once read. Changes to specs made within P4OO.py will clear the cache as appropriate, but changes outside of P4OO.py may cause inconsistency. No accommodation is made for thread safety, it's fair to assume P4OO.py is not at all thread safe.

Parsing, parsing, parsing

P4OO.py avoids parsing as much as possible, but in some cases it simply cannot be avoided. P4Python is a handy interface into Perforce, but it sometimes resembles commandline output a little bit too much. When creating or deleting a spec it will return a string describing what it did or did not accomplish for instance, and P4OO.py has to parse that output. P4OO.py does leverage P4Python's ability to accept list parameters in command execution, and so it does not need nor attempt to construct properly escaped commandlines. P4OO.py parses so you don't have to.

The Nitty Gritty details

Counters

Reading

Setting

Spec Objects

Reading attributes from an existing spec

Setting attributes

Creating a new spec

Object and Attribute Reference

ObjectAttributeDetails
P4OOBranch branchID attribute
description
owner
options
view
updatepresented as a datetime
accesspresented as a datetime
P4OOChange /
P4OOChangelist
changeID attribute
client
description
user
status
type
jobs
files
datepresented as a datetime
P4OOClient /
P4OOWorkspace
clientID attribute
description
owner
host
root
altroots
options
submitoptions
lineend
view
updatepresented as a datetime
accesspresented as a datetime
P4OODepot depotID attribute
description
owner
type
address
suffix
map
datepresented as a datetime
P4OOGroup groupID attribute
owners
users
maxresults
maxscanrows
maxlocktime
timeout
subgroups
P4OOJob jobID attribute
description
user
status
datepresented as a datetime
P4OOLabel labelID attribute
description
owner
options
revision
view
updatepresented as a datetime
accesspresented as a datetime
P4OOUser userID attribute
email
fullname
jobview
password
reviews
updatepresented as a datetime
accesspresented as a datetime

Querying Perforce

You know the id of the object you're interested in

Querying all objects for specific attributes

Perforce querying is provided by the _P4OOSet.query() method. It is inherited by all _P4OOSet subclasses directly, and _P4OOSpec subclasses can access it through a helper method in _P4OOBase. The easiest way to use it is to create an empty set object to call query against. For instance, to find the last submitted change, the query would look like this:
    emptySet = P4OOChangeSet(p4PythonObj=p4Handle)
    submittedChangeSet = emptySet.query(status="submitted", max=1)
    lastSubmittedChangeObj = submittedChangeSet[0]
or more directly:
    lastSubmittedChangeObj = P4OOChangeSet(p4PythonObj=p4Handle).query(status="submitted", max=1)[0]
The supported attributes for query are the same that the 'p4 changes' command supports, and are documented in each module as well as a comprehensive table below.

Notes on Querying

Owner and User attributes

"Owner" and "User" attributes are not used entirely consistently within Perforce. For instance, 'p4 branch -o ' will return a "Owner" attribute, but 'p4 branches' takes a '-u ' argument. With 'p4 change', the attribute is named "User" instead of "Owner". P4OO.py attempts to mitigate this partially by allowing 'user' to be queried as 'owner' for P4OOBranchSet, P4OOChangeSet, P4OOClientSet, and P4OOLabelSet. P4OO.py does NOT allow the returned object's attribute to be fetched by anything other than its native name however.

Querying attribute Reference

ObjectAttributeTypeMultiplicityNotes
P4OOBranchSet user[ string, P4OOUser ]1 
owner  *interchangeable with user*
P4OOChangeSet user[ string, P4OOUser ]1 
owner  *interchangeable with user*
client[ string, P4OOClient ]1 
status[ string ]1 
max[ integer ]1 
longoutput<none>0 
files[ string, P4OOFile, P4OOFileSet ]n 
P4OOClientSet user[ string, P4OOUser ]1 
owner  *interchangeable with user*
namefilter[ string ]1 
max[ integer ]1 
P4OODepotSet    *no queryable attributes*
P4OOGroupSet user[ string, P4OOUser ]1 
P4OOJobSet jobview[ string ]1 
files[ string, P4OOFile, P4OOFileSet ]n 
P4OOLabelSet user[ string, P4OOUser ]1 
owner  *interchangeable with user*
namefilter[ string ]1 
max[ integer ]1 
files[ string, P4OOFile, P4OOFileSet ]n 
P4OOUserSet max[ integer ]1 
allusers<none>0 
longoutput<none>0 
users[ string, P4OOUser, P4OOUserSet ]n 

Custom Extensions for common Perforce functions

P4OO.py is not merely a replacement for executing `p4` or P4Python calls and parsing output. Since first class objects are provided for each modeled table, each object can be extended to provide new methods, generalizing common behaviors. For example, deleting a user completely from Perforce can be a long series of commands for an admin. Pending changes and shelved files have to be cleaned up, clients need to be removed, and so on. The procedure is also easily automated since there is a standard algorithm. With P4OO.py, it's simply a method call on a P4OOUser object that implements the algorithm, leveraging similar cleanup methods on P4OOChange and P4OOClient objects as necessary. By providing these method extensions, P4OO.py becomes a far richer API for working with Perforce.

Changes

Find the list of changes in a client from change# to change#

Labels

Find the latest change# on a given label

    p4 changes -m 1 @<label>
 OR
    p4LabelObj.getLastChange()

Find the list of changes between two labels

Fetch the diffs from changes between two labels

Users

Find opened files for a user

Across all client workspaces

    p4UserObj.listOpenedFiles()

In a specific client workspace

    p4UserObj.listOpenedFiles(client=p4ClientObj)
 OR
    p4ClientObj.getOpenedFiles(user=p4UserObj)

Find all clients belonging to a user

    p4UserObj.listClients()
  OR
    P4OOClientSet().query(user=p4UserObj)

Find all changes owned by a user

    p4UserObj.listChanges()
  OR
    P4OOChangeSet().query(user=p4UserObj)

Delete user, doing all necessary steps in the process

Deleting a Perforce user is not always as simple as removing the user spec. If the user has opened files, shelved files, existing client workspaces, or pending changes, those need to be addressed before the user can be removed. The algorithm works as follows (simplified for illustration):
    for client in `p4 clients -u $user`
        p4 -c $client revert -k //$client/...
	
    for change in `p4 changes -u $user -s pending`
        client = `p4 -ztag change -o 131 |grep -i client`
        p4 -c $client shelve -d -f -c $change
        p4 change -d -f $change
	
    for client in `p4 clients -u $user`
        p4 client -d -f $client
	
    p4 user -d -f $user
The equivalent as implemented in P4OO.py:
    p4UserObj.deleteWithVengeance()