Review Board

beta

Update perforce SCMTool to use p4python 08.1 API.

Updated 5 months, 2 weeks ago

David Trowbridge Reviewers
trunk reviewboard
None Review Board SVN
As of the 08.1 API release, the p4python wrapper is now an officially supported
product from Perforce.  This is awesome, but they changed things up a bit
(entirely for the better -- parsing changesets is quite a bit simpler now).
This change updates our perforce tool to use the new API.
This code has been running on reviewboard.eng since they upgraded our main
perforce server a couple months ago.

Diff revision 2 (Latest)

1 2
1 2

  1. /trunk/reviewboard/manage.py: 2 changes [ 1 2 ]
  2. /trunk/reviewboard/scmtools/perforce.py: 9 changes [ 1 2 3 4 5 6 7 8 9 ]
/trunk/reviewboard/manage.py
Revision 1408 New Change
82
        imp.find_module('pysvn')
82
        imp.find_module('pysvn')
83
    except ImportError:
83
    except ImportError:
84
        dependency_warning('pysvn not found.  SVN integration will not work.')
84
        dependency_warning('pysvn not found.  SVN integration will not work.')
85
85
86
    try:
86
    try:
87
        imp.find_module('p4')
87
        imp.find_module('P4')
88
        subprocess.call(['p4', '-h'],
88
        subprocess.call(['p4', '-h'],
89
                        stdin=subprocess.PIPE, stdout=subprocess.PIPE)
89
                        stdin=subprocess.PIPE, stdout=subprocess.PIPE)
90
    except ImportError:
90
    except ImportError:
91
        dependency_warning('p4python not found.  Perforce integration will not work.')
91
        dependency_warning('p4python (>=08.1) not found.  Perforce integration will not work.')
92
    except OSError:
92
    except OSError:
93
        dependency_error('p4 command not found.  Perforce integration will not work.')
93
        dependency_error('p4 command not found.  Perforce integration will not work.')
94
94
95
    try:
95
    try:
96
        imp.find_module('mercurial')
96
        imp.find_module('mercurial')
/trunk/reviewboard/scmtools/perforce.py
Revision 1408 New Change
1
import re
1
import re
2
import subprocess
2
import subprocess
3
3
4
try:
4
try:
5
    from p4 import P4Error
5
    from P4 import P4Error
6
except ImportError:
6
except ImportError:
7
    pass
7
    pass
8
8
9
from reviewboard.diffviewer.parser import DiffParser
9
from reviewboard.diffviewer.parser import DiffParser
10
from reviewboard.scmtools.core import SCMTool, ChangeSet, HEAD, PRE_CREATION
10
from reviewboard.scmtools.core import SCMTool, ChangeSet, HEAD, PRE_CREATION
11
11
12
class PerforceTool(SCMTool):
12
class PerforceTool(SCMTool):
13
    def __init__(self, repository):
13
    def __init__(self, repository):
14
        SCMTool.__init__(self, repository)
14
        SCMTool.__init__(self, repository)
15
15
16
        import p4
16
        import P4
17
        self.p4 = p4.P4()
17
        self.p4 = P4.P4()
18
        self.p4.port = str(repository.mirror_path or repository.path)
18
        self.p4.port = str(repository.mirror_path or repository.path)
19
        self.p4.user = str(repository.username)
19
        self.p4.user = str(repository.username)
20
        self.p4.password = str(repository.password)
20
        self.p4.password = str(repository.password)
21
        self.p4.exception_level = 1
21
22
22
        # We defer actually connecting until just before we do some operation
23
        # We defer actually connecting until just before we do some operation
23
        # that requires an active connection to the perforce depot.  This
24
        # that requires an active connection to the perforce depot.  This
24
        # connection is then left open as long as possible.
25
        # connection is then left open as long as possible.
25
26
33
            # no internet connection, we'll get a P4Error from disconnect().
34
            # no internet connection, we'll get a P4Error from disconnect().
34
            # This is totally safe to ignore.
35
            # This is totally safe to ignore.
35
            pass
36
            pass
36
37
37
    def _connect(self):
38
    def _connect(self):
38
        if not self.p4.connected:
39
        if not self.p4.connected():
39
            self.p4.connect()
40
            self.p4.connect()
40
41
41
    def _disconnect(self):
42
    def _disconnect(self):
42
        try:
43
        try:
43
            if self.p4.connected:
44
            if self.p4.connected():
44
                self.p4.disconnect()
45
                self.p4.disconnect()
45
        except AttributeError:
46
        except AttributeError:
46
            pass
47
            pass
47
48
48
    def get_pending_changesets(self, userid):
49
    def get_pending_changesets(self, userid):
53
54
54
    def get_changeset(self, changesetid):
55
    def get_changeset(self, changesetid):
55
        self._connect()
56
        self._connect()
56
        changeset = self.p4.run_describe('-s', str(changesetid))
57
        changeset = self.p4.run_describe('-s', str(changesetid))
57
        self._disconnect()
58
        self._disconnect()
58
        return self.parse_change_desc(changeset, changesetid)
59
59
60
        if changeset:
61
            return self.parse_change_desc(changeset[0], changesetid)
62
        return None
63
60
    def get_diffs_use_absolute_paths(self):
64
    def get_diffs_use_absolute_paths(self):
61
        return True
65
        return True
62
66
63
    def get_file(self, path, revision=HEAD):
67
    def get_file(self, path, revision=HEAD):
64
        if revision == PRE_CREATION:
68
        if revision == PRE_CREATION:
136
        #
140
        #
137
        # We parse the username out of the first line to check that one user
141
        # We parse the username out of the first line to check that one user
138
        # isn't attempting to "claim" another's changelist.  We then split
142
        # isn't attempting to "claim" another's changelist.  We then split
139
        # everything around the 'Affected files ...' line, and process the
143
        # everything around the 'Affected files ...' line, and process the
140
        # results.
144
        # results.
141
        changeset.username = changedesc[0].split(' ')[3].split('@')[0]
145
        changeset.username = changedesc['user']
142
146
        changeset.description = changedesc['desc']
143
        description = '\n'.join(changedesc[1:])
147
        changeset.files = changedesc['depotFile']
144
        file_header = re.search('Affected files ...', description)
145
146
        desc = None
147
        for line in description[:file_header.start()].split('\n'):
148
            if line.startswith('\t'):
149
                line = line[1:]
150
            if desc:
151
                desc += '\n' + line.rstrip()
152
            else:
153
                desc = line.rstrip()
154
        changeset.description = desc.rstrip()
155
        changeset.files = filter(lambda x: len(x),
156
            [x.strip().split('#', 1)[0] for x in
157
                description[file_header.end():].split('\n')])
158
148
159
        split = changeset.description.find('\n\n')
149
        split = changeset.description.find('\n\n')
160
        if split >= 0 and split < 100:
150
        if split >= 0 and split < 100:
161
            changeset.summary = \
151
            changeset.summary = \
162
                changeset.description.split('\n\n', 1)[0].replace('\n', ' ')
152
                changeset.description.split('\n\n', 1)[0].replace('\n', ' ')
  1. /trunk/reviewboard/manage.py: 2 changes [ 1 2 ]
  2. /trunk/reviewboard/scmtools/perforce.py: 9 changes [ 1 2 3 4 5 6 7 8 9 ]