There are a number of Perforce APIs out there... P4Ruby, P4Python, P4Perl, and P4COM are some of the most popular. My goal was to make P4.Net as similar as possible to these. In general, if you're familiar with the Ruby/Python/Perl APIs, you'll be able to transition to P4.Net fairly quickly. The biggest conceptual difference is that those APIs are used in dynamic languages, and P4.Net targets static languages. As a result, P4.Net uses custom types where the other APIs use built-in types, and P4.Net will use separate methods/properties to distinguish actions that output different types.
Let's just dive into a sample, and do a side-by-side comparison. In this case, I'll compare P4.Net using IronPython to P4Python using standard Python. Note, I don't mean to suggest that P4.Net/IronPython is superior to P4Python/Standard Python, I just want to show apples-to-apples comparisons, without interference from individual language features.
# Standard Python and P4Python | # IronPython and P4.Net | ||
import clr | |||
1 | from System import Array, String | ||
clr.AddReferenceToFile('P4API.dll') | |||
import p4 as P4API | import P4API | ||
p4 = P4API.P4() | p4 = P4API.P4Connection() | ||
2 | p4.parse_forms() | ||
p4.connect() | p4.Connect() | ||
lname = 'P4NetTestingSample' | lname = 'P4NetTestingSample' | ||
# build my label | # build my label | ||
labelForm = p4.fetch_label(lname) | labelForm = p4.Fetch_Form('label', lname) | ||
labelForm['Description'] = 'Created for P4.Net sample' | labelForm['Description'] = 'Created for P4.Net sample' | ||
3 | view = ['//guest/shawn_hladky/...'] | view = Array[String](['//guest/shawn_hladky/...']) | |
labelForm['View'] = view | labelForm.ArrayFields['View'] = view | ||
res = p4.save_label(labelForm) | res = p4.Save_Form(labelForm) | ||
4 | if len(p4.errors) > 0: | if res.HasErrors(): | |
for e in p4.errors: print e | for e in res.Errors: print e | ||
# My list of changes. This is totally arbitrary | # My list of changes. This is totally arbitrary | ||
changes=['5774', '5680', '5636', '5444'] | changes=['5774', '5680', '5636', '5444'] | ||
sorted_changes = changes.sort() | sorted_changes = changes.sort() | ||
# dictionary: keyed by file, value = revision | # dictionary: keyed by file, value = revision | ||
filerevs = {} | filerevs = {} | ||
# spin the description on each file | # spin the description on each file | ||
5 | for chg in p4.run_describe('-s', *changes): | for chg in p4.Run('describe', '-s', *changes): | |
6 | depotFiles = chg['depotFile'] | depotFiles = chg.ArrayFields['depotFile'] | |
revisions = chg['rev'] | revisions = chg.ArrayFields['rev'] | ||
for i in range(0, len(depotFiles)): | for i in range(0, len(depotFiles)): | ||
key,value = depotFiles[i], revisions[i] | key,value = depotFiles[i], revisions[i] | ||
filerevs[key]=value | filerevs[key]=value | ||
# convert the dictionary to a list | # convert the dictionary to a list | ||
flist = [] | flist = [] | ||
for k,v in filerevs.items(): | for k,v in filerevs.items(): | ||
flist.append("%s#%s" % (k,v)) | flist.append("%s#%s" % (k,v)) | ||
# now I want to run in non-parsed mode | |||
p4.disconnect() | |||
p4 = P4API.P4() | |||
7 | p4.connect() | ||
out = p4.run('labelsync', '-l', lname, *flist) | out = p4.RunUnParsed('labelsync', '-l', lname, *flist) | ||
for s in out: print s | for s in out: print s | ||
# delete the label to keep the public depot clean :-) | # delete the label to keep the public depot clean :-) | ||
p4.run('label', '-d', lname) | p4.RunUnParsed('label', '-d', lname) | ||
p4.disconnect() | p4.Disconnect() |
This is some overhead required for IronPython. It will load the CLR, and P4.Net.
This chunk of code is establishing a connection to the Perforce server. There are a couple things unique to P4.Net worth noting here. Classes in P4.Net are more explicitly named. Here the connection class is named P4Connection. While we're talking about naming, P4.Net uses traditional .Net conventions, and all method names use camel case.
Also, note that P4.Net does not need the parse_forms() method. P4.Net is designed to be more statically typed than P4Python/P4Ruby/P4Perl. You use different methods to retrieve tagged vs. untagged output. Therefore, P4.Net auto-manages the connection, and will dynamically switch modes in the native C++ API when needed.
There's several things going on in this chunk, so let's break it down line-by-line:
P4Python: labelForm = p4.fetch_label(lname) P4.Net : labelForm = p4.Fetch_Form('label', lname)
Since P4.Net isn't meant for dynamic languages, there are no shortcut methods available.
P4Python: labelForm['Description'] = 'Created for P4.Net sample' P4.Net : labelForm['Description'] = 'Created for P4.Net sample'
Identical syntax!
P4Python: view = ['//guest/shawn_hladky/...'] P4.Net : view = Array[String](['//guest/shawn_hladky/...'])
IronPython doesn't explicitly convert Python lists to .Net Arrays.
P4Python: labelForm['View'] = view P4.Net : labelForm.ArrayFields['View'] = view
In P4.Net, you use the ArrayFields property when getting/setting fields with array values.
P4Python: res = p4.save_label(labelForm) P4.Net : res = p4.Save_Form(labelForm)
There's no shortcut methode available in P4.Net; however, sine the form is a rich object, it already knows the command, and doesn't need to be supplied with the Save_Form method.
This is a subtle, but significant departure from the other APIs. In P4.Net, results are rich objects, not just simple lists. All warnings, errors, and messages are associated with the results object. In other APIs, these are part of the connection object, and are reset with each run.
More of the same... no short-cut methods, and use the ArrayFields property. Only in this instance we're dealing with a P4Recordset instead of a form.
In P4.Net, you use different methods to return tagged and untagged output. This means P4.Net auto-manages the connection for you, so no need to explicitly reset as other APIs require.