comparison hgext/patchbomb.py @ 43578:cbcd72844df1

py3: use native strings when forming email headers in patchbomb Per previous changesets, encoded header's values are native str. We complete the change in patchbomb extension to have literal header values native str as well. Then we can also change headers' keys to be str. In _msgid(), we still need to use encoding.strfromlocal() because usage of os.environ is not allowed by check-code. This finally removes the "if pycompat.ispy3:" TODO.
author Denis Laxalde <denis@laxalde.org>
date Sat, 09 Nov 2019 16:16:31 +0100
parents 14b96072797d
children af3e341dbf03
comparison
equal deleted inserted replaced
43577:599e25add437 43578:cbcd72844df1
319 subj = desc[0].strip().rstrip(b'. ') 319 subj = desc[0].strip().rstrip(b'. ')
320 if not numbered: 320 if not numbered:
321 subj = b' '.join([prefix, opts.get(b'subject') or subj]) 321 subj = b' '.join([prefix, opts.get(b'subject') or subj])
322 else: 322 else:
323 subj = b' '.join([prefix, subj]) 323 subj = b' '.join([prefix, subj])
324 msg[b'Subject'] = mail.headencode(ui, subj, _charsets, opts.get(b'test')) 324 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get(b'test'))
325 msg[b'X-Mercurial-Node'] = node 325 msg['X-Mercurial-Node'] = pycompat.sysstr(node)
326 msg[b'X-Mercurial-Series-Index'] = b'%i' % idx 326 msg['X-Mercurial-Series-Index'] = '%i' % idx
327 msg[b'X-Mercurial-Series-Total'] = b'%i' % total 327 msg['X-Mercurial-Series-Total'] = '%i' % total
328 return msg, subj, ds 328 return msg, subj, ds
329 329
330 330
331 def _getpatches(repo, revs, **opts): 331 def _getpatches(repo, revs, **opts):
332 """return a list of patches for a list of revisions 332 """return a list of patches for a list of revisions
419 'attachment', 419 'attachment',
420 filename=encoding.strfromlocal(bundlename), 420 filename=encoding.strfromlocal(bundlename),
421 ) 421 )
422 emailencoders.encode_base64(datapart) 422 emailencoders.encode_base64(datapart)
423 msg.attach(datapart) 423 msg.attach(datapart)
424 msg[b'Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) 424 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
425 return [(msg, subj, None)] 425 return [(msg, subj, None)]
426 426
427 427
428 def _makeintro(repo, sender, revs, patches, **opts): 428 def _makeintro(repo, sender, revs, patches, **opts):
429 """make an introduction email, asking the user for content if needed 429 """make an introduction email, asking the user for content if needed
452 else: 452 else:
453 diffstat = None 453 diffstat = None
454 454
455 body = _getdescription(repo, body, sender, **opts) 455 body = _getdescription(repo, body, sender, **opts)
456 msg = mail.mimeencode(ui, body, _charsets, opts.get('test')) 456 msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
457 msg[b'Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) 457 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
458 return (msg, subj, diffstat) 458 return (msg, subj, diffstat)
459 459
460 460
461 def _getpatchmsgs(repo, sender, revs, patchnames=None, **opts): 461 def _getpatchmsgs(repo, sender, revs, patchnames=None, **opts):
462 """return a list of emails from a list of patches 462 """return a list of emails from a list of patches
520 ui.status(_(b"no changes found\n")) 520 ui.status(_(b"no changes found\n"))
521 return revs 521 return revs
522 522
523 523
524 def _msgid(node, timestamp): 524 def _msgid(node, timestamp):
525 hostname = encoding.strtolocal(socket.getfqdn()) 525 try:
526 hostname = encoding.environ.get(b'HGHOSTNAME', hostname) 526 hostname = encoding.strfromlocal(encoding.environ[b'HGHOSTNAME'])
527 return b'<%s.%d@%s>' % (node, timestamp, hostname) 527 except KeyError:
528 hostname = socket.getfqdn()
529 return '<%s.%d@%s>' % (node, timestamp, hostname)
528 530
529 531
530 emailopts = [ 532 emailopts = [
531 (b'', b'body', None, _(b'send patches as inline message text (default)')), 533 (b'', b'body', None, _(b'send patches as inline message text (default)')),
532 (b'a', b'attach', None, _(b'send patches as attachments')), 534 (b'a', b'attach', None, _(b'send patches as attachments')),
910 ui.write(b'\n') 912 ui.write(b'\n')
911 913
912 parent = opts.get(b'in_reply_to') or None 914 parent = opts.get(b'in_reply_to') or None
913 # angle brackets may be omitted, they're not semantically part of the msg-id 915 # angle brackets may be omitted, they're not semantically part of the msg-id
914 if parent is not None: 916 if parent is not None:
915 if not parent.startswith(b'<'): 917 parent = encoding.strfromlocal(parent)
916 parent = b'<' + parent 918 if not parent.startswith('<'):
917 if not parent.endswith(b'>'): 919 parent = '<' + parent
918 parent += b'>' 920 if not parent.endswith('>'):
921 parent += '>'
919 922
920 sender_addr = eutil.parseaddr(encoding.strfromlocal(sender))[1] 923 sender_addr = eutil.parseaddr(encoding.strfromlocal(sender))[1]
921 sender = mail.addressencode(ui, sender, _charsets, opts.get(b'test')) 924 sender = mail.addressencode(ui, sender, _charsets, opts.get(b'test'))
922 sendmail = None 925 sendmail = None
923 firstpatch = None 926 firstpatch = None
924 progress = ui.makeprogress( 927 progress = ui.makeprogress(
925 _(b'sending'), unit=_(b'emails'), total=len(msgs) 928 _(b'sending'), unit=_(b'emails'), total=len(msgs)
926 ) 929 )
927 for i, (m, subj, ds) in enumerate(msgs): 930 for i, (m, subj, ds) in enumerate(msgs):
928 try: 931 try:
929 m[b'Message-Id'] = genmsgid(m[b'X-Mercurial-Node']) 932 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
930 if not firstpatch: 933 if not firstpatch:
931 firstpatch = m[b'Message-Id'] 934 firstpatch = m['Message-Id']
932 m[b'X-Mercurial-Series-Id'] = firstpatch 935 m['X-Mercurial-Series-Id'] = firstpatch
933 except TypeError: 936 except TypeError:
934 m[b'Message-Id'] = genmsgid(b'patchbomb') 937 m['Message-Id'] = genmsgid('patchbomb')
935 if parent: 938 if parent:
936 m[b'In-Reply-To'] = parent 939 m['In-Reply-To'] = parent
937 m[b'References'] = parent 940 m['References'] = parent
938 if not parent or b'X-Mercurial-Node' not in m: 941 if not parent or 'X-Mercurial-Node' not in m:
939 parent = m[b'Message-Id'] 942 parent = m['Message-Id']
940 943
941 m[b'User-Agent'] = b'Mercurial-patchbomb/%s' % util.version() 944 m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version().decode()
942 m[b'Date'] = eutil.formatdate(start_time[0], localtime=True) 945 m['Date'] = eutil.formatdate(start_time[0], localtime=True)
943 946
944 start_time = (start_time[0] + 1, start_time[1]) 947 start_time = (start_time[0] + 1, start_time[1])
945 m[b'From'] = sender 948 m['From'] = sender
946 m[b'To'] = ', '.join(to) 949 m['To'] = ', '.join(to)
947 if cc: 950 if cc:
948 m[b'Cc'] = ', '.join(cc) 951 m['Cc'] = ', '.join(cc)
949 if bcc: 952 if bcc:
950 m[b'Bcc'] = ', '.join(bcc) 953 m['Bcc'] = ', '.join(bcc)
951 if replyto: 954 if replyto:
952 m[b'Reply-To'] = ', '.join(replyto) 955 m['Reply-To'] = ', '.join(replyto)
953 # Fix up all headers to be native strings.
954 # TODO(durin42): this should probably be cleaned up above in the future.
955 if pycompat.ispy3:
956 for hdr, val in list(m.items()):
957 change = False
958 if isinstance(hdr, bytes):
959 del m[hdr]
960 hdr = pycompat.strurl(hdr)
961 change = True
962 if isinstance(val, bytes):
963 # header value should be ASCII since it's encoded by
964 # mail.headencode(), but -n/--test disables it and raw
965 # value of platform encoding is stored.
966 val = encoding.strfromlocal(val)
967 if not change:
968 # prevent duplicate headers
969 del m[hdr]
970 change = True
971 if change:
972 m[hdr] = val
973 if opts.get(b'test'): 956 if opts.get(b'test'):
974 ui.status(_(b'displaying '), subj, b' ...\n') 957 ui.status(_(b'displaying '), subj, b' ...\n')
975 ui.pager(b'email') 958 ui.pager(b'email')
976 generator = mail.Generator(ui, mangle_from_=False) 959 generator = mail.Generator(ui, mangle_from_=False)
977 try: 960 try:
985 sendmail = mail.connect(ui, mbox=mbox) 968 sendmail = mail.connect(ui, mbox=mbox)
986 ui.status(_(b'sending '), subj, b' ...\n') 969 ui.status(_(b'sending '), subj, b' ...\n')
987 progress.update(i, item=subj) 970 progress.update(i, item=subj)
988 if not mbox: 971 if not mbox:
989 # Exim does not remove the Bcc field 972 # Exim does not remove the Bcc field
990 del m[b'Bcc'] 973 del m['Bcc']
991 fp = stringio() 974 fp = stringio()
992 generator = mail.Generator(fp, mangle_from_=False) 975 generator = mail.Generator(fp, mangle_from_=False)
993 generator.flatten(m, 0) 976 generator.flatten(m, 0)
994 alldests = to + bcc + cc 977 alldests = to + bcc + cc
995 sendmail(sender_addr, alldests, fp.getvalue()) 978 sendmail(sender_addr, alldests, fp.getvalue())