| 1 |
"""Object-oriented interfaces for Perforce entities.""" |
|---|
| 2 |
|
|---|
| 3 |
__all__ = ['CommandError', |
|---|
| 4 |
'Client', 'User', 'Branch', 'Label', 'Change', 'Job'] |
|---|
| 5 |
|
|---|
| 6 |
import re |
|---|
| 7 |
import perforce.api |
|---|
| 8 |
|
|---|
| 9 |
class CommandError(perforce.api.PerforceError): |
|---|
| 10 |
"""Exception raised when a Perforce command fails.""" |
|---|
| 11 |
|
|---|
| 12 |
def __init__(self, command, errors): |
|---|
| 13 |
self.command = command |
|---|
| 14 |
self.errors = errors |
|---|
| 15 |
|
|---|
| 16 |
def __str__(self): |
|---|
| 17 |
return "\n".join((x.format() for x in self.errors)) |
|---|
| 18 |
|
|---|
| 19 |
class FormObject(object): |
|---|
| 20 |
"""A Perforce form-based object. |
|---|
| 21 |
|
|---|
| 22 |
Provides dictionary-style index access to form fields and to 'Options' |
|---|
| 23 |
field values. |
|---|
| 24 |
|
|---|
| 25 |
For example:: |
|---|
| 26 |
| >>> c = FormObject(clientForm) |
|---|
| 27 |
| >>> print c['Options'] |
|---|
| 28 |
| noallwrite noclobber nocompress unlocked nomodtime normdir |
|---|
| 29 |
| >>> print c['compress'] |
|---|
| 30 |
| False |
|---|
| 31 |
| >>> c['compress'] = True |
|---|
| 32 |
| >>> print c['Options'] |
|---|
| 33 |
| noallwrite noclobber compress unlocked nomodtime normdir |
|---|
| 34 |
| >>> print c['compress'] |
|---|
| 35 |
| True |
|---|
| 36 |
""" |
|---|
| 37 |
|
|---|
| 38 |
__slots__ = ['_form'] |
|---|
| 39 |
|
|---|
| 40 |
def __init__(self, form): |
|---|
| 41 |
self._form = form |
|---|
| 42 |
|
|---|
| 43 |
def __getitem__(self, key): |
|---|
| 44 |
if key in self._form: |
|---|
| 45 |
return self._form[key] |
|---|
| 46 |
elif 'Options' in self._form: |
|---|
| 47 |
field = self._form.Options |
|---|
| 48 |
if field.isSingle() and field.values is not None: |
|---|
| 49 |
fieldValues = field.values |
|---|
| 50 |
if not isinstance(fieldValues, list): |
|---|
| 51 |
fieldValues = [fieldValues] |
|---|
| 52 |
vals = self._form['Options'].split() |
|---|
| 53 |
for opts in fieldValues: |
|---|
| 54 |
if key in opts: |
|---|
| 55 |
return key in vals |
|---|
| 56 |
raise KeyError("No such field or option '%s'" % key) |
|---|
| 57 |
|
|---|
| 58 |
def __setitem__(self, key, value): |
|---|
| 59 |
if key in self._form: |
|---|
| 60 |
self._form[key] = value |
|---|
| 61 |
return |
|---|
| 62 |
elif 'Options' in self._form: |
|---|
| 63 |
field = self._form.Options |
|---|
| 64 |
if field.isSingle() and field.values is not None: |
|---|
| 65 |
fieldValues = field.values |
|---|
| 66 |
if not isinstance(fieldValues, list): |
|---|
| 67 |
fieldValues = [fieldValues] |
|---|
| 68 |
vals = self._form['Options'].split() |
|---|
| 69 |
for opts in fieldValues: |
|---|
| 70 |
if key in opts: |
|---|
| 71 |
others = [opt for opt in opts if opt != key] |
|---|
| 72 |
if value: |
|---|
| 73 |
# Set to true by replacing one of the other values |
|---|
| 74 |
# with this value or adding it to the list. |
|---|
| 75 |
for other in others: |
|---|
| 76 |
if other in vals: |
|---|
| 77 |
vals[vals.index(other)] = key |
|---|
| 78 |
break |
|---|
| 79 |
else: |
|---|
| 80 |
if key not in vals: |
|---|
| 81 |
vals.append(key) |
|---|
| 82 |
else: |
|---|
| 83 |
# Set to false by replacing this one with the other |
|---|
| 84 |
# value or appending the other value to the list. |
|---|
| 85 |
if len(others) != 1: |
|---|
| 86 |
raise KeyError( |
|---|
| 87 |
("Ambiguous operation setting option '%s'"+ |
|---|
| 88 |
"to False") % key) |
|---|
| 89 |
|
|---|
| 90 |
if key in vals: |
|---|
| 91 |
vals[vals.index(key)] = others[0] |
|---|
| 92 |
elif others[0] not in vals: |
|---|
| 93 |
vals.append(others[0]) |
|---|
| 94 |
self._form['Options'] = ' '.join(vals) |
|---|
| 95 |
return |
|---|
| 96 |
raise KeyError("No such field or option '%s'" % key) |
|---|
| 97 |
|
|---|
| 98 |
def __delitem__(self, key): |
|---|
| 99 |
del self._form[key] |
|---|
| 100 |
|
|---|
| 101 |
class Client(FormObject): |
|---|
| 102 |
"""A Perforce client/workspace object.""" |
|---|
| 103 |
|
|---|
| 104 |
__slots__ = ['__connection', '__name'] |
|---|
| 105 |
|
|---|
| 106 |
def __init__(self, connection, clientName): |
|---|
| 107 |
"""Initialises the Client based on the form retrieved on the |
|---|
| 108 |
connection. |
|---|
| 109 |
|
|---|
| 110 |
@param connection: The connection to the Perforce server to use to |
|---|
| 111 |
query the client details. |
|---|
| 112 |
@type connection: L{perforce.connection.Connection} |
|---|
| 113 |
|
|---|
| 114 |
@param clientName: The name of the Perforce client to query. |
|---|
| 115 |
@type clientName: C{str} or C{unicode} |
|---|
| 116 |
|
|---|
| 117 |
@raise CommandError: If there was a Perforce error querying the client. |
|---|
| 118 |
""" |
|---|
| 119 |
results = connection.run('client', '-o', clientName) |
|---|
| 120 |
if results.errors or not results.forms: |
|---|
| 121 |
raise CommandError('p4 client -o %s' % clientName, |
|---|
| 122 |
results.messages) |
|---|
| 123 |
|
|---|
| 124 |
form = results.forms[0] |
|---|
| 125 |
|
|---|
| 126 |
FormObject.__init__(self, form) |
|---|
| 127 |
self.__connection = connection |
|---|
| 128 |
self.__name = clientName |
|---|
| 129 |
|
|---|
| 130 |
def save(self): |
|---|
| 131 |
"""Save any changes made to the client on the Perforce server. |
|---|
| 132 |
|
|---|
| 133 |
@raise CommandError: If there was a Perforce error saving the client. |
|---|
| 134 |
""" |
|---|
| 135 |
results = self.__connection.run('client', '-i', input=self._form) |
|---|
| 136 |
if results.errors: |
|---|
| 137 |
raise CommandError('p4 client -i', results.errors) |
|---|
| 138 |
|
|---|
| 139 |
def sync(self, *fileRevisions, **options): |
|---|
| 140 |
"""Sync the client workspace to the specified file revisions. |
|---|
| 141 |
|
|---|
| 142 |
If no file revisions are specified, then sync the entire workspace |
|---|
| 143 |
to the latest revision. |
|---|
| 144 |
|
|---|
| 145 |
@param fileRevisions: The file revisions to sync in this workspace. |
|---|
| 146 |
If no fileRevisions were specified then all files in the workspace are |
|---|
| 147 |
synced to the #head revision. |
|---|
| 148 |
@type fileRevisions: C{tuple} of C{str} or C{unicode} |
|---|
| 149 |
|
|---|
| 150 |
@keyword force: Pass as C{True} to force all named file revisions to |
|---|
| 151 |
be retransmitted rather than just those that are out of date. |
|---|
| 152 |
@type force: C{boolean} |
|---|
| 153 |
|
|---|
| 154 |
@return: The results of performing the sync operation |
|---|
| 155 |
@rtype: L{perforce.results.Results} |
|---|
| 156 |
|
|---|
| 157 |
@raise CommandError: If there was a Perforce error syncing the client. |
|---|
| 158 |
""" |
|---|
| 159 |
force = False |
|---|
| 160 |
if 'force' in options: |
|---|
| 161 |
force = options['force'] |
|---|
| 162 |
|
|---|
| 163 |
overrides = {'client' : self.__name} |
|---|
| 164 |
if force: |
|---|
| 165 |
results = self.__connection.run('sync', '-f', |
|---|
| 166 |
*fileRevisions, |
|---|
| 167 |
**overrides) |
|---|
| 168 |
else: |
|---|
| 169 |
results = self.__connection.run('sync', |
|---|
| 170 |
*fileRevisions, |
|---|
| 171 |
**overrides) |
|---|
| 172 |
return results |
|---|
| 173 |
|
|---|
| 174 |
def delete(self, force=False): |
|---|
| 175 |
"""Delete this client workspace from the Perforce repository. |
|---|
| 176 |
|
|---|
| 177 |
The client cannot normally be deleted when the client is locked. |
|---|
| 178 |
Passing force=True will allow the client owner or a Perforce user with |
|---|
| 179 |
admin privileges to forcibly delete a locked client. |
|---|
| 180 |
|
|---|
| 181 |
@note: This operation will not delete the files from the client's |
|---|
| 182 |
directory. |
|---|
| 183 |
|
|---|
| 184 |
@raise CommandError: If there was a Perforce error deleting the client. |
|---|
| 185 |
""" |
|---|
| 186 |
if force: |
|---|
| 187 |
results = self.__connection.run('client', '-d', '-f', self.__name) |
|---|
| 188 |
else: |
|---|
| 189 |
results = self.__connection.run('client', '-d', self.__name) |
|---|
| 190 |
|
|---|
| 191 |
if results.errors: |
|---|
| 192 |
if force: |
|---|
| 193 |
raise CommandError('p4 client -d -f %s' % self.__name, |
|---|
| 194 |
results.errors) |
|---|
| 195 |
else: |
|---|
| 196 |
raise CommandError('p4 client -d %s' % self.__name, |
|---|
| 197 |
results.errors) |
|---|
| 198 |
|
|---|
| 199 |
class User(FormObject): |
|---|
| 200 |
"""A Perforce user object.""" |
|---|
| 201 |
|
|---|
| 202 |
__slots__ = ['__connection', '__name'] |
|---|
| 203 |
|
|---|
| 204 |
def __init__(self, connection, userName): |
|---|
| 205 |
"""Initialises the User based on the form retrieved on the connection. |
|---|
| 206 |
|
|---|
| 207 |
@param connection: The connection to the Perforce server to use. |
|---|
| 208 |
@type connection: L{perforce.connection.Connection} |
|---|
| 209 |
|
|---|
| 210 |
@param userName: The name of the user to query. |
|---|
| 211 |
@type userName: C{str} or C{unicode} |
|---|
| 212 |
|
|---|
| 213 |
@raise CommandError: If there was a Perforce error querying the user. |
|---|
| 214 |
""" |
|---|
| 215 |
results = connection.run('user', '-o', userName) |
|---|
| 216 |
if results.errors or not results.forms: |
|---|
| 217 |
raise CommandError('p4 user -o %s' % userName, |
|---|
| 218 |
results.messages) |
|---|
| 219 |
form = results.forms[0] |
|---|
| 220 |
FormObject.__init__(self, form) |
|---|
| 221 |
self.__connection = connection |
|---|
| 222 |
self.__name = userName |
|---|
| 223 |
|
|---|
| 224 |
def save(self, force=False): |
|---|
| 225 |
"""Save any changes made to the user to the Perforce server. |
|---|
| 226 |
|
|---|
| 227 |
By default a user can only update their own user specification. |
|---|
| 228 |
Specifying the C{force} parameter as C{True} allows users with |
|---|
| 229 |
C{'super'} access to modify other user's specifications and to |
|---|
| 230 |
modify the C{'Update'} field. |
|---|
| 231 |
|
|---|
| 232 |
@param force: Flag indicating whether the update is forced. |
|---|
| 233 |
@type force: C{boolean} |
|---|
| 234 |
|
|---|
| 235 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 236 |
""" |
|---|
| 237 |
if force: |
|---|
| 238 |
results = self.__connection.run('user', '-i', '-f', |
|---|
| 239 |
input=self._form) |
|---|
| 240 |
else: |
|---|
| 241 |
results = self.__connection.run('user', '-i', |
|---|
| 242 |
input=self._form) |
|---|
| 243 |
|
|---|
| 244 |
if results.errors: |
|---|
| 245 |
if force: |
|---|
| 246 |
raise CommandError('p4 user -i -f', results.errors) |
|---|
| 247 |
else: |
|---|
| 248 |
raise CommandError('p4 user -i', results.errors) |
|---|
| 249 |
|
|---|
| 250 |
def delete(self, force=False): |
|---|
| 251 |
"""Delete this user from the Perforce server. |
|---|
| 252 |
|
|---|
| 253 |
By default a user can only delete their own user specification. |
|---|
| 254 |
Specifying the C{force} parameter as C{True} allows users with |
|---|
| 255 |
C{'super'} access to delete other users. |
|---|
| 256 |
|
|---|
| 257 |
@param force: Flag indicating whether to force deletion of the user. |
|---|
| 258 |
@type force: C{boolean} |
|---|
| 259 |
|
|---|
| 260 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 261 |
""" |
|---|
| 262 |
if force: |
|---|
| 263 |
results = self.__connection.run('user', '-d', '-f', self.__name) |
|---|
| 264 |
else: |
|---|
| 265 |
results = self.__connection.run('user', '-d', self.__name) |
|---|
| 266 |
|
|---|
| 267 |
if results.errors: |
|---|
| 268 |
if force: |
|---|
| 269 |
raise CommandError('p4 user -d -f %s' % self.__name, |
|---|
| 270 |
results.errors) |
|---|
| 271 |
else: |
|---|
| 272 |
raise CommandError('p4 user -d %s' % self.__name, |
|---|
| 273 |
results.errors) |
|---|
| 274 |
|
|---|
| 275 |
def setPassword(self, newPassword, oldPassword=None): |
|---|
| 276 |
"""Set a new password for this user. |
|---|
| 277 |
|
|---|
| 278 |
By default a user can only change their own password, and then only |
|---|
| 279 |
if they correctly provide their old password. However, users with |
|---|
| 280 |
'super' access can set new passwords for other users without providing |
|---|
| 281 |
the old password. |
|---|
| 282 |
|
|---|
| 283 |
@param newPassword: The new password to set. |
|---|
| 284 |
Pass None or '' for the new password to set it to blank. |
|---|
| 285 |
@type newPassword: C{str}, C{unicode} or C{None} |
|---|
| 286 |
|
|---|
| 287 |
@param oldPassword: The old password. Leave as C{None} to set someone |
|---|
| 288 |
else's password without the old password (provided you have 'super' |
|---|
| 289 |
access). |
|---|
| 290 |
@type oldPassword: C{str}, C{unicode} or C{None} |
|---|
| 291 |
|
|---|
| 292 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 293 |
""" |
|---|
| 294 |
|
|---|
| 295 |
if newPassword is None: |
|---|
| 296 |
newPassword = '' |
|---|
| 297 |
|
|---|
| 298 |
if oldPassword is None or self.__name != self.__connection.user: |
|---|
| 299 |
# Setting a password without the old password or setting another |
|---|
| 300 |
# user's password needs 'super' access. |
|---|
| 301 |
results = self.__connection.run('passwd', self.__name, |
|---|
| 302 |
input=[newPassword, |
|---|
| 303 |
newPassword]) |
|---|
| 304 |
if results.errors: |
|---|
| 305 |
raise CommandError('p4 passwd %s' % self.__name, |
|---|
| 306 |
results.errors) |
|---|
| 307 |
else: |
|---|
| 308 |
# Setting the current user's password |
|---|
| 309 |
if oldPassword == '': |
|---|
| 310 |
results = self.__connection.run('passwd', |
|---|
| 311 |
input=[newPassword, |
|---|
| 312 |
newPassword]) |
|---|
| 313 |
else: |
|---|
| 314 |
results = self.__connection.run('passwd', |
|---|
| 315 |
input=[oldPassword, |
|---|
| 316 |
newPassword, |
|---|
| 317 |
newPassword]) |
|---|
| 318 |
|
|---|
| 319 |
if results.errors: |
|---|
| 320 |
raise CommandError('p4 passwd', |
|---|
| 321 |
results.errors) |
|---|
| 322 |
|
|---|
| 323 |
class Branch(FormObject): |
|---|
| 324 |
"""A Perforce branch object.""" |
|---|
| 325 |
|
|---|
| 326 |
__slots__ = ["__connection", "__name"] |
|---|
| 327 |
|
|---|
| 328 |
def __init__(self, connection, branchName): |
|---|
| 329 |
"""Intialise the Branch based on the form retrieved on the connection. |
|---|
| 330 |
|
|---|
| 331 |
@param connection: The connection to the Perforce server to use. |
|---|
| 332 |
Must be already connected. |
|---|
| 333 |
@type connection: L{perforce.connection.Connection} |
|---|
| 334 |
|
|---|
| 335 |
@param branchName: The name of the branch to query. |
|---|
| 336 |
@type branchName: C{str} or C{unicode} |
|---|
| 337 |
|
|---|
| 338 |
@raise CommandError: If there was a Perforc error querying the branch. |
|---|
| 339 |
""" |
|---|
| 340 |
results = connection.run('branch', '-o', branchName) |
|---|
| 341 |
if results.errors: |
|---|
| 342 |
raise CommandError('p4 branch -o %s' % branchName, |
|---|
| 343 |
results.errors) |
|---|
| 344 |
form = results.forms[0] |
|---|
| 345 |
FormObject.__init__(self, form) |
|---|
| 346 |
self.__connection = connection |
|---|
| 347 |
self.__name = branchName |
|---|
| 348 |
|
|---|
| 349 |
def save(self, force=False): |
|---|
| 350 |
"""Save any changes made to the branch to the Perforce server. |
|---|
| 351 |
|
|---|
| 352 |
By default only the owner of a locked branch can modify the branch. |
|---|
| 353 |
Passing C{force} as C{True} allows users with C{'admin'} access to |
|---|
| 354 |
modify locked branches owned by another user. |
|---|
| 355 |
|
|---|
| 356 |
@param force: Flag indicating whether to force saving of updates. |
|---|
| 357 |
@type force: C{boolean} |
|---|
| 358 |
|
|---|
| 359 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 360 |
""" |
|---|
| 361 |
form = self._form |
|---|
| 362 |
if force: |
|---|
| 363 |
results = self.__connection.run('branch', '-i', '-f', |
|---|
| 364 |
input=form) |
|---|
| 365 |
if results.errors: |
|---|
| 366 |
raise CommandError('p4 branch -i -f', results.errors) |
|---|
| 367 |
else: |
|---|
| 368 |
results = self.__connection.run('branch', '-i', |
|---|
| 369 |
input=form) |
|---|
| 370 |
if results.errors: |
|---|
| 371 |
raise CommandError('p4 branch -i', results.errors) |
|---|
| 372 |
|
|---|
| 373 |
def delete(self, force=False): |
|---|
| 374 |
"""Delete this branch from the Perforce server. |
|---|
| 375 |
|
|---|
| 376 |
By default, a branch cannot be deleted if it is locked. |
|---|
| 377 |
Specifying the 'force' parameter as True allows users with 'admin' |
|---|
| 378 |
access to delete locked branches. |
|---|
| 379 |
|
|---|
| 380 |
@param force: Flag indicating whether to force deletion of the branch. |
|---|
| 381 |
@type force: C{boolean} |
|---|
| 382 |
|
|---|
| 383 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 384 |
""" |
|---|
| 385 |
if force: |
|---|
| 386 |
results = self.__connection.run('branch', '-d', '-f', self.__name) |
|---|
| 387 |
if results.errors: |
|---|
| 388 |
raise CommandError('p4 branch -d -f %s' % self.__name, |
|---|
| 389 |
results.errors) |
|---|
| 390 |
else: |
|---|
| 391 |
results = self.__connection.run('branch', '-d', self.__name) |
|---|
| 392 |
if results.errors: |
|---|
| 393 |
raise CommandError('p4 branch -d %s' % self.__name, |
|---|
| 394 |
results.errors) |
|---|
| 395 |
|
|---|
| 396 |
class Label(FormObject): |
|---|
| 397 |
"""The label class wraps the Perforce label concept.""" |
|---|
| 398 |
|
|---|
| 399 |
__slots__ = ['__connection', '__name'] |
|---|
| 400 |
|
|---|
| 401 |
def __init__(self, connection, labelName): |
|---|
| 402 |
"""Intialise the Label based on the form retrieved on the connection. |
|---|
| 403 |
|
|---|
| 404 |
@param connection: The connection to the Perforce server to use. |
|---|
| 405 |
Must be already connected. |
|---|
| 406 |
@type connection: L{perforce.connection.Connection} |
|---|
| 407 |
|
|---|
| 408 |
@param labelName: The name of the label to query. |
|---|
| 409 |
@type labelName: C{str} or C{unicode} |
|---|
| 410 |
|
|---|
| 411 |
@raise CommandError: If there was a Perforce error querying the label. |
|---|
| 412 |
""" |
|---|
| 413 |
results = connection.run('label', '-o', labelName) |
|---|
| 414 |
if results.errors: |
|---|
| 415 |
raise CommandError('p4 label -o %s' % labelName, |
|---|
| 416 |
results.errors) |
|---|
| 417 |
form = results.forms[0] |
|---|
| 418 |
FormObject.__init__(self, form) |
|---|
| 419 |
self.__connection = connection |
|---|
| 420 |
self.__name = labelName |
|---|
| 421 |
|
|---|
| 422 |
def save(self, force=False): |
|---|
| 423 |
"""Save any changes made to the label on the Perforce server. |
|---|
| 424 |
|
|---|
| 425 |
By default a locked label can only be updated by its owner. Passing |
|---|
| 426 |
C{force} as C{True} will allow users with C{'admin'} access to force |
|---|
| 427 |
saving changes to the labe. |
|---|
| 428 |
|
|---|
| 429 |
@param force: Flag indicating whether to force saving of updates. |
|---|
| 430 |
@type force: C{boolean} |
|---|
| 431 |
|
|---|
| 432 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 433 |
""" |
|---|
| 434 |
if force: |
|---|
| 435 |
results = self.__connection.run('label', '-i', '-f', |
|---|
| 436 |
input=self._form) |
|---|
| 437 |
else: |
|---|
| 438 |
results = self.__connection.run('label', '-i', |
|---|
| 439 |
input=self._form) |
|---|
| 440 |
|
|---|
| 441 |
if results.errors: |
|---|
| 442 |
if force: |
|---|
| 443 |
raise CommandError('p4 label -i -f', results.errors) |
|---|
| 444 |
else: |
|---|
| 445 |
raise CommandError('p4 label -i', results.errors) |
|---|
| 446 |
|
|---|
| 447 |
def delete(self, force=False): |
|---|
| 448 |
"""Delete this label from the Perforce server. |
|---|
| 449 |
|
|---|
| 450 |
By default a locked label can't be deleted. Users with 'admin' access |
|---|
| 451 |
can force deletion of a locked label by specifying C{force} as C{True}. |
|---|
| 452 |
|
|---|
| 453 |
@param force: Flag indicating whether to force deletion of a locked |
|---|
| 454 |
label. |
|---|
| 455 |
@type force: C{boolean} |
|---|
| 456 |
|
|---|
| 457 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 458 |
""" |
|---|
| 459 |
if force: |
|---|
| 460 |
results = self.__connection.run('label', '-d', '-f', self.__name) |
|---|
| 461 |
else: |
|---|
| 462 |
results = self.__connection.run('label', '-d', self.__name) |
|---|
| 463 |
|
|---|
| 464 |
if results.errors: |
|---|
| 465 |
if force: |
|---|
| 466 |
raise CommandError('p4 user -d -f %s' % self.__name, |
|---|
| 467 |
results.errors) |
|---|
| 468 |
else: |
|---|
| 469 |
raise CommandError('p4 user -d %s' % self.__name, |
|---|
| 470 |
results.errors) |
|---|
| 471 |
|
|---|
| 472 |
class Change(FormObject): |
|---|
| 473 |
"""The change class wraps the Perforce changelist concept.""" |
|---|
| 474 |
|
|---|
| 475 |
__slots__ = ['__connection', '__name', '__client'] |
|---|
| 476 |
|
|---|
| 477 |
__changeCreatedMessageRE = re.compile( |
|---|
| 478 |
'^Change (?P<change>\d+) created') |
|---|
| 479 |
__changeRenamedMessageRE = re.compile( |
|---|
| 480 |
'^Change (?P<old>\d+) renamed change (?P<new>\d+)') |
|---|
| 481 |
|
|---|
| 482 |
def __init__(self, connection, change=None, client=None): |
|---|
| 483 |
"""Initialise the Change based on the form retrieved on the connection. |
|---|
| 484 |
|
|---|
| 485 |
@param connection: The connection to the Perforce server to use. |
|---|
| 486 |
Must already be connected. |
|---|
| 487 |
@type connection: L{perforce.connection.Connection} |
|---|
| 488 |
|
|---|
| 489 |
@param change: The number of the changelist to retrieve. |
|---|
| 490 |
If not specified then open the default changelist on the connection's |
|---|
| 491 |
current client. Saving the default changelist will create a new named |
|---|
| 492 |
changelist. |
|---|
| 493 |
@type change: C{int}, C{str}, C{unicode} or C{None} |
|---|
| 494 |
|
|---|
| 495 |
@param client: The name of the Perforce client to use for retrieving |
|---|
| 496 |
default changelists and submitting files. The client used to submit |
|---|
| 497 |
the changelist must be the same as the client the changelist was |
|---|
| 498 |
created on. |
|---|
| 499 |
@type client: C{str} or C{unicode} |
|---|
| 500 |
|
|---|
| 501 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 502 |
""" |
|---|
| 503 |
FormObject.__init__(self, None) |
|---|
| 504 |
|
|---|
| 505 |
self.__connection = connection |
|---|
| 506 |
self.__client = client |
|---|
| 507 |
|
|---|
| 508 |
if change is None: |
|---|
| 509 |
self.__name = None |
|---|
| 510 |
elif isinstance(change, int): |
|---|
| 511 |
self.__name = str(change) |
|---|
| 512 |
elif isinstance(change, basestring): |
|---|
| 513 |
self.__name = change |
|---|
| 514 |
else: |
|---|
| 515 |
raise TypeError("Invalid type for 'change' parameter.") |
|---|
| 516 |
|
|---|
| 517 |
self.refresh() |
|---|
| 518 |
|
|---|
| 519 |
def save(self, force=False): |
|---|
| 520 |
"""Save any changes made to the changelist on the Perforce server. |
|---|
| 521 |
|
|---|
| 522 |
@param force: Flag indicating whether or not to force update of other |
|---|
| 523 |
users' pending changelists or 'Update' and 'Description' fields of |
|---|
| 524 |
submitted changelists. The user must have 'admin' privileges to |
|---|
| 525 |
force modification of changelists. |
|---|
| 526 |
@type force: C{boolean} |
|---|
| 527 |
|
|---|
| 528 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 529 |
""" |
|---|
| 530 |
|
|---|
| 531 |
client = self.__client or self.__connection.client |
|---|
| 532 |
if force: |
|---|
| 533 |
results = self.__connection.run('change', '-s', '-f', '-i', |
|---|
| 534 |
intput=self._form, |
|---|
| 535 |
client=client) |
|---|
| 536 |
if results.errors: |
|---|
| 537 |
raise CommandError('p4 change -s -f -i', |
|---|
| 538 |
results.errors) |
|---|
| 539 |
else: |
|---|
| 540 |
results = self.__connection.run('change', '-s', '-i', |
|---|
| 541 |
input=self._form, |
|---|
| 542 |
client=client) |
|---|
| 543 |
if results.errors: |
|---|
| 544 |
raise CommandError('p4 change -s -i', |
|---|
| 545 |
results.errors) |
|---|
| 546 |
|
|---|
| 547 |
if self.__name is None: |
|---|
| 548 |
# We just saved a new change form, query the new change number. |
|---|
| 549 |
message = results.infos[0].format() |
|---|
| 550 |
match = Change.__changeCreatedMessageRE.match(message) |
|---|
| 551 |
if not match: |
|---|
| 552 |
raise RuntimeError( |
|---|
| 553 |
"Perforce server returned an unrecognised response " + |
|---|
| 554 |
"creating a new changelist: %s" % message) |
|---|
| 555 |
|
|---|
| 556 |
self.__name = match.group('change') |
|---|
| 557 |
|
|---|
| 558 |
def delete(self, force=False): |
|---|
| 559 |
"""Delete a pending changelist on the Perforce server. |
|---|
| 560 |
|
|---|
| 561 |
@note: The default changelist cannot be deleted. |
|---|
| 562 |
|
|---|
| 563 |
@param force: Force deletion of another user's changelist or a |
|---|
| 564 |
submitted changelist (once all files on the changelist have been |
|---|
| 565 |
obliterated). The user must have 'admin' privileges to force |
|---|
| 566 |
deletion of changelists. |
|---|
| 567 |
@type force: C{boolean} |
|---|
| 568 |
|
|---|
| 569 |
@raise CommandError: If the operation failed due to Perforce errors. |
|---|
| 570 |
""" |
|---|
| 571 |
|
|---|
| 572 |
client = self.__client or self.__connection.client |
|---|
| 573 |
if force: |
|---|
| 574 |
results = self.__connection.run('change', '-d', '-f', self.__name, |
|---|
| 575 |
client=client) |
|---|
| 576 |
if results.errors: |
|---|
| 577 |
raise CommandError('p4 change -d -f %s' % self.__name, |
|---|
| 578 |
results.errors) |
|---|
| 579 |
else: |
|---|
| 580 |
results = self.__connection.run('change', '-d', self.__name, |
|---|
| 581 |
client=client) |
|---|
| 582 |
if results.errors: |
|---|
| 583 |
raise CommandError('p4 change -d %s' % < |
|---|