comparison hgext/bugzilla.py @ 45942:89a2afe31e82

formating: upgrade to black 20.8b1 This required a couple of small tweaks to un-confuse black, but now it works. Big formatting changes come from: * Dramatically improved collection-splitting logic upstream * Black having a strong (correct IMO) opinion that """ is better than ''' Differential Revision: https://phab.mercurial-scm.org/D9430
author Augie Fackler <raf@durin42.com>
date Fri, 27 Nov 2020 17:03:29 -0500
parents 3194cc8c8de0
children d6afa9c149c3
comparison
equal deleted inserted replaced
45941:346af7687c6f 45942:89a2afe31e82
323 323
324 configtable = {} 324 configtable = {}
325 configitem = registrar.configitem(configtable) 325 configitem = registrar.configitem(configtable)
326 326
327 configitem( 327 configitem(
328 b'bugzilla', b'apikey', default=b'', 328 b'bugzilla',
329 ) 329 b'apikey',
330 configitem( 330 default=b'',
331 b'bugzilla', b'bzdir', default=b'/var/www/html/bugzilla', 331 )
332 ) 332 configitem(
333 configitem( 333 b'bugzilla',
334 b'bugzilla', b'bzemail', default=None, 334 b'bzdir',
335 ) 335 default=b'/var/www/html/bugzilla',
336 configitem( 336 )
337 b'bugzilla', b'bzurl', default=b'http://localhost/bugzilla/', 337 configitem(
338 ) 338 b'bugzilla',
339 configitem( 339 b'bzemail',
340 b'bugzilla', b'bzuser', default=None, 340 default=None,
341 ) 341 )
342 configitem( 342 configitem(
343 b'bugzilla', b'db', default=b'bugs', 343 b'bugzilla',
344 b'bzurl',
345 default=b'http://localhost/bugzilla/',
346 )
347 configitem(
348 b'bugzilla',
349 b'bzuser',
350 default=None,
351 )
352 configitem(
353 b'bugzilla',
354 b'db',
355 default=b'bugs',
344 ) 356 )
345 configitem( 357 configitem(
346 b'bugzilla', 358 b'bugzilla',
347 b'fixregexp', 359 b'fixregexp',
348 default=( 360 default=(
351 br'(?P<ids>(?:#?\d+\s*(?:,?\s*(?:and)?)?\s*)+)' 363 br'(?P<ids>(?:#?\d+\s*(?:,?\s*(?:and)?)?\s*)+)'
352 br'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?' 364 br'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?'
353 ), 365 ),
354 ) 366 )
355 configitem( 367 configitem(
356 b'bugzilla', b'fixresolution', default=b'FIXED', 368 b'bugzilla',
357 ) 369 b'fixresolution',
358 configitem( 370 default=b'FIXED',
359 b'bugzilla', b'fixstatus', default=b'RESOLVED', 371 )
360 ) 372 configitem(
361 configitem( 373 b'bugzilla',
362 b'bugzilla', b'host', default=b'localhost', 374 b'fixstatus',
363 ) 375 default=b'RESOLVED',
364 configitem( 376 )
365 b'bugzilla', b'notify', default=configitem.dynamicdefault, 377 configitem(
366 ) 378 b'bugzilla',
367 configitem( 379 b'host',
368 b'bugzilla', b'password', default=None, 380 default=b'localhost',
381 )
382 configitem(
383 b'bugzilla',
384 b'notify',
385 default=configitem.dynamicdefault,
386 )
387 configitem(
388 b'bugzilla',
389 b'password',
390 default=None,
369 ) 391 )
370 configitem( 392 configitem(
371 b'bugzilla', 393 b'bugzilla',
372 b'regexp', 394 b'regexp',
373 default=( 395 default=(
375 br'(?P<ids>(?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)' 397 br'(?P<ids>(?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)'
376 br'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?' 398 br'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?'
377 ), 399 ),
378 ) 400 )
379 configitem( 401 configitem(
380 b'bugzilla', b'strip', default=0, 402 b'bugzilla',
381 ) 403 b'strip',
382 configitem( 404 default=0,
383 b'bugzilla', b'style', default=None, 405 )
384 ) 406 configitem(
385 configitem( 407 b'bugzilla',
386 b'bugzilla', b'template', default=None, 408 b'style',
387 ) 409 default=None,
388 configitem( 410 )
389 b'bugzilla', b'timeout', default=5, 411 configitem(
390 ) 412 b'bugzilla',
391 configitem( 413 b'template',
392 b'bugzilla', b'user', default=b'bugs', 414 default=None,
393 ) 415 )
394 configitem( 416 configitem(
395 b'bugzilla', b'usermap', default=None, 417 b'bugzilla',
396 ) 418 b'timeout',
397 configitem( 419 default=5,
398 b'bugzilla', b'version', default=None, 420 )
421 configitem(
422 b'bugzilla',
423 b'user',
424 default=b'bugs',
425 )
426 configitem(
427 b'bugzilla',
428 b'usermap',
429 default=None,
430 )
431 configitem(
432 b'bugzilla',
433 b'version',
434 default=None,
399 ) 435 )
400 436
401 437
402 class bzaccess(object): 438 class bzaccess(object):
403 '''Base class for access to Bugzilla.''' 439 '''Base class for access to Bugzilla.'''
428 464
429 def filter_cset_known_bug_ids(self, node, bugs): 465 def filter_cset_known_bug_ids(self, node, bugs):
430 '''remove bug IDs where node occurs in comment text from bugs.''' 466 '''remove bug IDs where node occurs in comment text from bugs.'''
431 467
432 def updatebug(self, bugid, newstate, text, committer): 468 def updatebug(self, bugid, newstate, text, committer):
433 '''update the specified bug. Add comment text and set new states. 469 """update the specified bug. Add comment text and set new states.
434 470
435 If possible add the comment as being from the committer of 471 If possible add the comment as being from the committer of
436 the changeset. Otherwise use the default Bugzilla user. 472 the changeset. Otherwise use the default Bugzilla user.
437 ''' 473 """
438 474
439 def notify(self, bugs, committer): 475 def notify(self, bugs, committer):
440 '''Force sending of Bugzilla notification emails. 476 """Force sending of Bugzilla notification emails.
441 477
442 Only required if the access method does not trigger notification 478 Only required if the access method does not trigger notification
443 emails automatically. 479 emails automatically.
444 ''' 480 """
445 481
446 482
447 # Bugzilla via direct access to MySQL database. 483 # Bugzilla via direct access to MySQL database.
448 class bzmysql(bzaccess): 484 class bzmysql(bzaccess):
449 '''Support for direct MySQL access to Bugzilla. 485 """Support for direct MySQL access to Bugzilla.
450 486
451 The earliest Bugzilla version this is tested with is version 2.16. 487 The earliest Bugzilla version this is tested with is version 2.16.
452 488
453 If your Bugzilla is version 3.4 or above, you are strongly 489 If your Bugzilla is version 3.4 or above, you are strongly
454 recommended to use the XMLRPC access method instead. 490 recommended to use the XMLRPC access method instead.
455 ''' 491 """
456 492
457 @staticmethod 493 @staticmethod
458 def sql_buglist(ids): 494 def sql_buglist(ids):
459 '''return SQL-friendly list of bug ids''' 495 '''return SQL-friendly list of bug ids'''
460 return b'(' + b','.join(map(str, ids)) + b')' 496 return b'(' + b','.join(map(str, ids)) + b')'
579 userid = int(all[0][0]) 615 userid = int(all[0][0])
580 self.user_ids[user] = userid 616 self.user_ids[user] = userid
581 return userid 617 return userid
582 618
583 def get_bugzilla_user(self, committer): 619 def get_bugzilla_user(self, committer):
584 '''See if committer is a registered bugzilla user. Return 620 """See if committer is a registered bugzilla user. Return
585 bugzilla username and userid if so. If not, return default 621 bugzilla username and userid if so. If not, return default
586 bugzilla username and userid.''' 622 bugzilla username and userid."""
587 user = self.map_committer(committer) 623 user = self.map_committer(committer)
588 try: 624 try:
589 userid = self.get_user_id(user) 625 userid = self.get_user_id(user)
590 except KeyError: 626 except KeyError:
591 try: 627 try:
602 % (user, defaultuser) 638 % (user, defaultuser)
603 ) 639 )
604 return (user, userid) 640 return (user, userid)
605 641
606 def updatebug(self, bugid, newstate, text, committer): 642 def updatebug(self, bugid, newstate, text, committer):
607 '''update bug state with comment text. 643 """update bug state with comment text.
608 644
609 Try adding comment as committer of changeset, otherwise as 645 Try adding comment as committer of changeset, otherwise as
610 default bugzilla user.''' 646 default bugzilla user."""
611 if len(newstate) > 0: 647 if len(newstate) > 0:
612 self.ui.warn(_(b"Bugzilla/MySQL cannot update bug state\n")) 648 self.ui.warn(_(b"Bugzilla/MySQL cannot update bug state\n"))
613 649
614 (user, userid) = self.get_bugzilla_user(committer) 650 (user, userid) = self.get_bugzilla_user(committer)
615 now = time.strftime('%Y-%m-%d %H:%M:%S') 651 now = time.strftime('%Y-%m-%d %H:%M:%S')
867 if fieldname == b"id": 903 if fieldname == b"id":
868 fieldname = b"bug_id" 904 fieldname = b"bug_id"
869 return b"@%s = %s" % (fieldname, pycompat.bytestr(value)) 905 return b"@%s = %s" % (fieldname, pycompat.bytestr(value))
870 906
871 def send_bug_modify_email(self, bugid, commands, comment, committer): 907 def send_bug_modify_email(self, bugid, commands, comment, committer):
872 '''send modification message to Bugzilla bug via email. 908 """send modification message to Bugzilla bug via email.
873 909
874 The message format is documented in the Bugzilla email_in.pl 910 The message format is documented in the Bugzilla email_in.pl
875 specification. commands is a list of command lines, comment is the 911 specification. commands is a list of command lines, comment is the
876 comment text. 912 comment text.
877 913
878 To stop users from crafting commit comments with 914 To stop users from crafting commit comments with
879 Bugzilla commands, specify the bug ID via the message body, rather 915 Bugzilla commands, specify the bug ID via the message body, rather
880 than the subject line, and leave a blank line after it. 916 than the subject line, and leave a blank line after it.
881 ''' 917 """
882 user = self.map_committer(committer) 918 user = self.map_committer(committer)
883 matches = self.bzproxy.User.get( 919 matches = self.bzproxy.User.get(
884 {b'match': [user], b'token': self.bztoken} 920 {b'match': [user], b'token': self.bztoken}
885 ) 921 )
886 if not matches[b'users']: 922 if not matches[b'users']:
1014 % (bugid, sn) 1050 % (bugid, sn)
1015 ) 1051 )
1016 del bugs[bugid] 1052 del bugs[bugid]
1017 1053
1018 def updatebug(self, bugid, newstate, text, committer): 1054 def updatebug(self, bugid, newstate, text, committer):
1019 '''update the specified bug. Add comment text and set new states. 1055 """update the specified bug. Add comment text and set new states.
1020 1056
1021 If possible add the comment as being from the committer of 1057 If possible add the comment as being from the committer of
1022 the changeset. Otherwise use the default Bugzilla user. 1058 the changeset. Otherwise use the default Bugzilla user.
1023 ''' 1059 """
1024 bugmod = {} 1060 bugmod = {}
1025 if b'hours' in newstate: 1061 if b'hours' in newstate:
1026 bugmod[b'work_time'] = newstate[b'hours'] 1062 bugmod[b'work_time'] = newstate[b'hours']
1027 if b'fix' in newstate: 1063 if b'fix' in newstate:
1028 bugmod[b'status'] = self.fixstatus 1064 bugmod[b'status'] = self.fixstatus
1048 }, 1084 },
1049 ) 1085 )
1050 self.ui.debug(b'added comment to bug %s\n' % bugid) 1086 self.ui.debug(b'added comment to bug %s\n' % bugid)
1051 1087
1052 def notify(self, bugs, committer): 1088 def notify(self, bugs, committer):
1053 '''Force sending of Bugzilla notification emails. 1089 """Force sending of Bugzilla notification emails.
1054 1090
1055 Only required if the access method does not trigger notification 1091 Only required if the access method does not trigger notification
1056 emails automatically. 1092 emails automatically.
1057 ''' 1093 """
1058 pass 1094 pass
1059 1095
1060 1096
1061 class bugzilla(object): 1097 class bugzilla(object):
1062 # supported versions of bugzilla. different versions have 1098 # supported versions of bugzilla. different versions have
1090 self.ui.config(b'bugzilla', b'fixregexp'), re.IGNORECASE 1126 self.ui.config(b'bugzilla', b'fixregexp'), re.IGNORECASE
1091 ) 1127 )
1092 self.split_re = re.compile(br'\D+') 1128 self.split_re = re.compile(br'\D+')
1093 1129
1094 def find_bugs(self, ctx): 1130 def find_bugs(self, ctx):
1095 '''return bugs dictionary created from commit comment. 1131 """return bugs dictionary created from commit comment.
1096 1132
1097 Extract bug info from changeset comments. Filter out any that are 1133 Extract bug info from changeset comments. Filter out any that are
1098 not known to Bugzilla, and any that already have a reference to 1134 not known to Bugzilla, and any that already have a reference to
1099 the given changeset in their comments. 1135 the given changeset in their comments.
1100 ''' 1136 """
1101 start = 0 1137 start = 0
1102 bugs = {} 1138 bugs = {}
1103 bugmatch = self.bug_re.search(ctx.description(), start) 1139 bugmatch = self.bug_re.search(ctx.description(), start)
1104 fixmatch = self.fix_re.search(ctx.description(), start) 1140 fixmatch = self.fix_re.search(ctx.description(), start)
1105 while True: 1141 while True:
1150 1186
1151 def update(self, bugid, newstate, ctx): 1187 def update(self, bugid, newstate, ctx):
1152 '''update bugzilla bug with reference to changeset.''' 1188 '''update bugzilla bug with reference to changeset.'''
1153 1189
1154 def webroot(root): 1190 def webroot(root):
1155 '''strip leading prefix of repo root and turn into 1191 """strip leading prefix of repo root and turn into
1156 url-safe path.''' 1192 url-safe path."""
1157 count = int(self.ui.config(b'bugzilla', b'strip')) 1193 count = int(self.ui.config(b'bugzilla', b'strip'))
1158 root = util.pconvert(root) 1194 root = util.pconvert(root)
1159 while count > 0: 1195 while count > 0:
1160 c = root.find(b'/') 1196 c = root.find(b'/')
1161 if c == -1: 1197 if c == -1:
1193 '''ensure Bugzilla users are notified of bug change.''' 1229 '''ensure Bugzilla users are notified of bug change.'''
1194 self.bzdriver.notify(bugs, committer) 1230 self.bzdriver.notify(bugs, committer)
1195 1231
1196 1232
1197 def hook(ui, repo, hooktype, node=None, **kwargs): 1233 def hook(ui, repo, hooktype, node=None, **kwargs):
1198 '''add comment to bugzilla for each changeset that refers to a 1234 """add comment to bugzilla for each changeset that refers to a
1199 bugzilla bug id. only add a comment once per bug, so same change 1235 bugzilla bug id. only add a comment once per bug, so same change
1200 seen multiple times does not fill bug with duplicate data.''' 1236 seen multiple times does not fill bug with duplicate data."""
1201 if node is None: 1237 if node is None:
1202 raise error.Abort( 1238 raise error.Abort(
1203 _(b'hook type %s does not pass a changeset id') % hooktype 1239 _(b'hook type %s does not pass a changeset id') % hooktype
1204 ) 1240 )
1205 try: 1241 try: