Mercurial > public > mercurial-scm > hg
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()) |