# p4.py -- Simple Python interface to Perforce, using p4 -G # Gareth Rees, Ravenbrook Limited, 2000-09-25. # $Id: //info.ravenbrook.com/project/p4dti/branch/2000-12-07/document-history/code/replicator/p4.py#4 $ # # Copyright 2000 Ravenbrook Limited. This document is provided "as is", # without any express or implied warranty. In no event will the authors # be held liable for any damages arising from the use of this document. # You may make and distribute copies and derivative works of this # document provided that (1) you do not charge a fee for this document or # for its distribution, and (2) you retain as they appear all copyright # and licence notices and document history entries, and (3) you append # descriptions of your modifications to the document history. import marshal import os import tempfile error = 'Perforce error' class p4: config = { 'p4-client': None, 'p4-client-executable': 'p4', 'p4-password': None, 'p4-port': None, 'p4-user': None, } def __init__(self, config = {}): # Merge the supplied config with the default config (the former takes # precedence). for k in config.keys(): self.config[k] = config[k] # p4.run(arguments, input, verbose). Run the p4 client with the # given arguments. The arguments should be a Perforce command and # its arguments, like "jobs -o //foo/...". Options should # generally include -i and/or -o to avoid forms being put up # interactively. If input is supplied, then it should be a list # of dictionaries. If verbose is true, then the input, command # and results will be printed. These dictionaries are sent one by # one to the process. The results are read into a list and # returned. Since the output of the process is read to EOF and # the input is closed, there should be no process left hanging # about. def run(self, arguments, input = None, verbose = 0): temp_filename = None # Quote the Perforce command if it contains spaces. See job000049. p4_exe = self.config['p4-client-executable'] if ' ' in p4_exe: command_words = ['"%s"' % p4_exe] else: command_words = [p4_exe] command_words.append('-G') if self.config['p4-port']: command_words.append('-p %s' % self.config['p4-port']) if self.config['p4-user']: command_words.append('-u %s' % self.config['p4-user']) if self.config['p4-password']: command_words.append('-P %s' % self.config['p4-password']) if self.config['p4-client']: command_words.append('-c %s' % self.config['p4-client']) command_words.append(arguments) if input: tempfile.template = 'p4dti_data' temp_filename = tempfile.mktemp() temp_file = open(temp_filename, 'wb') marshal.dump(input[0], temp_file) temp_file.close() command_words.extend(['<', temp_filename]) if verbose: print "p4 input:", str(input) command = reduce((lambda x,y: x + ' ' + y), command_words) if verbose: print "p4 command:", command assert (os.name == 'nt' or os.name == 'posix'), \ ("p4.run does not support os.name='%s'." % os.name) if os.name == 'nt' : mode = 'rb' elif os.name == 'posix' : mode = 'r' stream = os.popen(command, mode) results = [] try: while 1: results.append(marshal.load(stream)) except EOFError: if (temp_filename): os.remove(temp_filename) if verbose: print "p4 results:", str(results) # Note: this isn't a reliable way to spot an error in a Perforce # command. See job000003. if (len(results) == 1 and results[0].has_key('code') and results[0]['code'] == 'error'): raise error, results[0]['data'] else: return results # B. Document History # # 2000-12-07 GDR Provided defaults for all configuration parameters so that you # can make a p4 object passing no parameters to get the default Perforce # behaviour.