##// END OF EJS Templates
mail: use print function...
Gregory Szorc -
r27619:10bed65e default
parent child Browse files
Show More
@@ -1,334 +1,334
1 # mail.py - mail sending bits for mercurial
1 # mail.py - mail sending bits for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import, print_function
9
9
10 import email
10 import email
11 import os
11 import os
12 import quopri
12 import quopri
13 import smtplib
13 import smtplib
14 import socket
14 import socket
15 import sys
15 import sys
16 import time
16 import time
17
17
18 from .i18n import _
18 from .i18n import _
19 from . import (
19 from . import (
20 encoding,
20 encoding,
21 error,
21 error,
22 sslutil,
22 sslutil,
23 util,
23 util,
24 )
24 )
25
25
26 _oldheaderinit = email.Header.Header.__init__
26 _oldheaderinit = email.Header.Header.__init__
27 def _unifiedheaderinit(self, *args, **kw):
27 def _unifiedheaderinit(self, *args, **kw):
28 """
28 """
29 Python 2.7 introduces a backwards incompatible change
29 Python 2.7 introduces a backwards incompatible change
30 (Python issue1974, r70772) in email.Generator.Generator code:
30 (Python issue1974, r70772) in email.Generator.Generator code:
31 pre-2.7 code passed "continuation_ws='\t'" to the Header
31 pre-2.7 code passed "continuation_ws='\t'" to the Header
32 constructor, and 2.7 removed this parameter.
32 constructor, and 2.7 removed this parameter.
33
33
34 Default argument is continuation_ws=' ', which means that the
34 Default argument is continuation_ws=' ', which means that the
35 behavior is different in <2.7 and 2.7
35 behavior is different in <2.7 and 2.7
36
36
37 We consider the 2.7 behavior to be preferable, but need
37 We consider the 2.7 behavior to be preferable, but need
38 to have an unified behavior for versions 2.4 to 2.7
38 to have an unified behavior for versions 2.4 to 2.7
39 """
39 """
40 # override continuation_ws
40 # override continuation_ws
41 kw['continuation_ws'] = ' '
41 kw['continuation_ws'] = ' '
42 _oldheaderinit(self, *args, **kw)
42 _oldheaderinit(self, *args, **kw)
43
43
44 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
44 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
45
45
46 class STARTTLS(smtplib.SMTP):
46 class STARTTLS(smtplib.SMTP):
47 '''Derived class to verify the peer certificate for STARTTLS.
47 '''Derived class to verify the peer certificate for STARTTLS.
48
48
49 This class allows to pass any keyword arguments to SSL socket creation.
49 This class allows to pass any keyword arguments to SSL socket creation.
50 '''
50 '''
51 def __init__(self, sslkwargs, **kwargs):
51 def __init__(self, sslkwargs, **kwargs):
52 smtplib.SMTP.__init__(self, **kwargs)
52 smtplib.SMTP.__init__(self, **kwargs)
53 self._sslkwargs = sslkwargs
53 self._sslkwargs = sslkwargs
54
54
55 def starttls(self, keyfile=None, certfile=None):
55 def starttls(self, keyfile=None, certfile=None):
56 if not self.has_extn("starttls"):
56 if not self.has_extn("starttls"):
57 msg = "STARTTLS extension not supported by server"
57 msg = "STARTTLS extension not supported by server"
58 raise smtplib.SMTPException(msg)
58 raise smtplib.SMTPException(msg)
59 (resp, reply) = self.docmd("STARTTLS")
59 (resp, reply) = self.docmd("STARTTLS")
60 if resp == 220:
60 if resp == 220:
61 self.sock = sslutil.wrapsocket(self.sock, keyfile, certfile,
61 self.sock = sslutil.wrapsocket(self.sock, keyfile, certfile,
62 **self._sslkwargs)
62 **self._sslkwargs)
63 self.file = smtplib.SSLFakeFile(self.sock)
63 self.file = smtplib.SSLFakeFile(self.sock)
64 self.helo_resp = None
64 self.helo_resp = None
65 self.ehlo_resp = None
65 self.ehlo_resp = None
66 self.esmtp_features = {}
66 self.esmtp_features = {}
67 self.does_esmtp = 0
67 self.does_esmtp = 0
68 return (resp, reply)
68 return (resp, reply)
69
69
70 class SMTPS(smtplib.SMTP):
70 class SMTPS(smtplib.SMTP):
71 '''Derived class to verify the peer certificate for SMTPS.
71 '''Derived class to verify the peer certificate for SMTPS.
72
72
73 This class allows to pass any keyword arguments to SSL socket creation.
73 This class allows to pass any keyword arguments to SSL socket creation.
74 '''
74 '''
75 def __init__(self, sslkwargs, keyfile=None, certfile=None, **kwargs):
75 def __init__(self, sslkwargs, keyfile=None, certfile=None, **kwargs):
76 self.keyfile = keyfile
76 self.keyfile = keyfile
77 self.certfile = certfile
77 self.certfile = certfile
78 smtplib.SMTP.__init__(self, **kwargs)
78 smtplib.SMTP.__init__(self, **kwargs)
79 self.default_port = smtplib.SMTP_SSL_PORT
79 self.default_port = smtplib.SMTP_SSL_PORT
80 self._sslkwargs = sslkwargs
80 self._sslkwargs = sslkwargs
81
81
82 def _get_socket(self, host, port, timeout):
82 def _get_socket(self, host, port, timeout):
83 if self.debuglevel > 0:
83 if self.debuglevel > 0:
84 print >> sys.stderr, 'connect:', (host, port)
84 print('connect:', (host, port), file=sys.stderr)
85 new_socket = socket.create_connection((host, port), timeout)
85 new_socket = socket.create_connection((host, port), timeout)
86 new_socket = sslutil.wrapsocket(new_socket,
86 new_socket = sslutil.wrapsocket(new_socket,
87 self.keyfile, self.certfile,
87 self.keyfile, self.certfile,
88 **self._sslkwargs)
88 **self._sslkwargs)
89 self.file = smtplib.SSLFakeFile(new_socket)
89 self.file = smtplib.SSLFakeFile(new_socket)
90 return new_socket
90 return new_socket
91
91
92 def _smtp(ui):
92 def _smtp(ui):
93 '''build an smtp connection and return a function to send mail'''
93 '''build an smtp connection and return a function to send mail'''
94 local_hostname = ui.config('smtp', 'local_hostname')
94 local_hostname = ui.config('smtp', 'local_hostname')
95 tls = ui.config('smtp', 'tls', 'none')
95 tls = ui.config('smtp', 'tls', 'none')
96 # backward compatible: when tls = true, we use starttls.
96 # backward compatible: when tls = true, we use starttls.
97 starttls = tls == 'starttls' or util.parsebool(tls)
97 starttls = tls == 'starttls' or util.parsebool(tls)
98 smtps = tls == 'smtps'
98 smtps = tls == 'smtps'
99 if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
99 if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
100 raise error.Abort(_("can't use TLS: Python SSL support not installed"))
100 raise error.Abort(_("can't use TLS: Python SSL support not installed"))
101 mailhost = ui.config('smtp', 'host')
101 mailhost = ui.config('smtp', 'host')
102 if not mailhost:
102 if not mailhost:
103 raise error.Abort(_('smtp.host not configured - cannot send mail'))
103 raise error.Abort(_('smtp.host not configured - cannot send mail'))
104 verifycert = ui.config('smtp', 'verifycert', 'strict')
104 verifycert = ui.config('smtp', 'verifycert', 'strict')
105 if verifycert not in ['strict', 'loose']:
105 if verifycert not in ['strict', 'loose']:
106 if util.parsebool(verifycert) is not False:
106 if util.parsebool(verifycert) is not False:
107 raise error.Abort(_('invalid smtp.verifycert configuration: %s')
107 raise error.Abort(_('invalid smtp.verifycert configuration: %s')
108 % (verifycert))
108 % (verifycert))
109 verifycert = False
109 verifycert = False
110 if (starttls or smtps) and verifycert:
110 if (starttls or smtps) and verifycert:
111 sslkwargs = sslutil.sslkwargs(ui, mailhost)
111 sslkwargs = sslutil.sslkwargs(ui, mailhost)
112 else:
112 else:
113 # 'ui' is required by sslutil.wrapsocket() and set by sslkwargs()
113 # 'ui' is required by sslutil.wrapsocket() and set by sslkwargs()
114 sslkwargs = {'ui': ui}
114 sslkwargs = {'ui': ui}
115 if smtps:
115 if smtps:
116 ui.note(_('(using smtps)\n'))
116 ui.note(_('(using smtps)\n'))
117 s = SMTPS(sslkwargs, local_hostname=local_hostname)
117 s = SMTPS(sslkwargs, local_hostname=local_hostname)
118 elif starttls:
118 elif starttls:
119 s = STARTTLS(sslkwargs, local_hostname=local_hostname)
119 s = STARTTLS(sslkwargs, local_hostname=local_hostname)
120 else:
120 else:
121 s = smtplib.SMTP(local_hostname=local_hostname)
121 s = smtplib.SMTP(local_hostname=local_hostname)
122 if smtps:
122 if smtps:
123 defaultport = 465
123 defaultport = 465
124 else:
124 else:
125 defaultport = 25
125 defaultport = 25
126 mailport = util.getport(ui.config('smtp', 'port', defaultport))
126 mailport = util.getport(ui.config('smtp', 'port', defaultport))
127 ui.note(_('sending mail: smtp host %s, port %d\n') %
127 ui.note(_('sending mail: smtp host %s, port %d\n') %
128 (mailhost, mailport))
128 (mailhost, mailport))
129 s.connect(host=mailhost, port=mailport)
129 s.connect(host=mailhost, port=mailport)
130 if starttls:
130 if starttls:
131 ui.note(_('(using starttls)\n'))
131 ui.note(_('(using starttls)\n'))
132 s.ehlo()
132 s.ehlo()
133 s.starttls()
133 s.starttls()
134 s.ehlo()
134 s.ehlo()
135 if (starttls or smtps) and verifycert:
135 if (starttls or smtps) and verifycert:
136 ui.note(_('(verifying remote certificate)\n'))
136 ui.note(_('(verifying remote certificate)\n'))
137 sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict')
137 sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict')
138 username = ui.config('smtp', 'username')
138 username = ui.config('smtp', 'username')
139 password = ui.config('smtp', 'password')
139 password = ui.config('smtp', 'password')
140 if username and not password:
140 if username and not password:
141 password = ui.getpass()
141 password = ui.getpass()
142 if username and password:
142 if username and password:
143 ui.note(_('(authenticating to mail server as %s)\n') %
143 ui.note(_('(authenticating to mail server as %s)\n') %
144 (username))
144 (username))
145 try:
145 try:
146 s.login(username, password)
146 s.login(username, password)
147 except smtplib.SMTPException as inst:
147 except smtplib.SMTPException as inst:
148 raise error.Abort(inst)
148 raise error.Abort(inst)
149
149
150 def send(sender, recipients, msg):
150 def send(sender, recipients, msg):
151 try:
151 try:
152 return s.sendmail(sender, recipients, msg)
152 return s.sendmail(sender, recipients, msg)
153 except smtplib.SMTPRecipientsRefused as inst:
153 except smtplib.SMTPRecipientsRefused as inst:
154 recipients = [r[1] for r in inst.recipients.values()]
154 recipients = [r[1] for r in inst.recipients.values()]
155 raise error.Abort('\n' + '\n'.join(recipients))
155 raise error.Abort('\n' + '\n'.join(recipients))
156 except smtplib.SMTPException as inst:
156 except smtplib.SMTPException as inst:
157 raise error.Abort(inst)
157 raise error.Abort(inst)
158
158
159 return send
159 return send
160
160
161 def _sendmail(ui, sender, recipients, msg):
161 def _sendmail(ui, sender, recipients, msg):
162 '''send mail using sendmail.'''
162 '''send mail using sendmail.'''
163 program = ui.config('email', 'method', 'smtp')
163 program = ui.config('email', 'method', 'smtp')
164 cmdline = '%s -f %s %s' % (program, util.email(sender),
164 cmdline = '%s -f %s %s' % (program, util.email(sender),
165 ' '.join(map(util.email, recipients)))
165 ' '.join(map(util.email, recipients)))
166 ui.note(_('sending mail: %s\n') % cmdline)
166 ui.note(_('sending mail: %s\n') % cmdline)
167 fp = util.popen(cmdline, 'w')
167 fp = util.popen(cmdline, 'w')
168 fp.write(msg)
168 fp.write(msg)
169 ret = fp.close()
169 ret = fp.close()
170 if ret:
170 if ret:
171 raise error.Abort('%s %s' % (
171 raise error.Abort('%s %s' % (
172 os.path.basename(program.split(None, 1)[0]),
172 os.path.basename(program.split(None, 1)[0]),
173 util.explainexit(ret)[0]))
173 util.explainexit(ret)[0]))
174
174
175 def _mbox(mbox, sender, recipients, msg):
175 def _mbox(mbox, sender, recipients, msg):
176 '''write mails to mbox'''
176 '''write mails to mbox'''
177 fp = open(mbox, 'ab+')
177 fp = open(mbox, 'ab+')
178 # Should be time.asctime(), but Windows prints 2-characters day
178 # Should be time.asctime(), but Windows prints 2-characters day
179 # of month instead of one. Make them print the same thing.
179 # of month instead of one. Make them print the same thing.
180 date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
180 date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
181 fp.write('From %s %s\n' % (sender, date))
181 fp.write('From %s %s\n' % (sender, date))
182 fp.write(msg)
182 fp.write(msg)
183 fp.write('\n\n')
183 fp.write('\n\n')
184 fp.close()
184 fp.close()
185
185
186 def connect(ui, mbox=None):
186 def connect(ui, mbox=None):
187 '''make a mail connection. return a function to send mail.
187 '''make a mail connection. return a function to send mail.
188 call as sendmail(sender, list-of-recipients, msg).'''
188 call as sendmail(sender, list-of-recipients, msg).'''
189 if mbox:
189 if mbox:
190 open(mbox, 'wb').close()
190 open(mbox, 'wb').close()
191 return lambda s, r, m: _mbox(mbox, s, r, m)
191 return lambda s, r, m: _mbox(mbox, s, r, m)
192 if ui.config('email', 'method', 'smtp') == 'smtp':
192 if ui.config('email', 'method', 'smtp') == 'smtp':
193 return _smtp(ui)
193 return _smtp(ui)
194 return lambda s, r, m: _sendmail(ui, s, r, m)
194 return lambda s, r, m: _sendmail(ui, s, r, m)
195
195
196 def sendmail(ui, sender, recipients, msg, mbox=None):
196 def sendmail(ui, sender, recipients, msg, mbox=None):
197 send = connect(ui, mbox=mbox)
197 send = connect(ui, mbox=mbox)
198 return send(sender, recipients, msg)
198 return send(sender, recipients, msg)
199
199
200 def validateconfig(ui):
200 def validateconfig(ui):
201 '''determine if we have enough config data to try sending email.'''
201 '''determine if we have enough config data to try sending email.'''
202 method = ui.config('email', 'method', 'smtp')
202 method = ui.config('email', 'method', 'smtp')
203 if method == 'smtp':
203 if method == 'smtp':
204 if not ui.config('smtp', 'host'):
204 if not ui.config('smtp', 'host'):
205 raise error.Abort(_('smtp specified as email transport, '
205 raise error.Abort(_('smtp specified as email transport, '
206 'but no smtp host configured'))
206 'but no smtp host configured'))
207 else:
207 else:
208 if not util.findexe(method):
208 if not util.findexe(method):
209 raise error.Abort(_('%r specified as email transport, '
209 raise error.Abort(_('%r specified as email transport, '
210 'but not in PATH') % method)
210 'but not in PATH') % method)
211
211
212 def mimetextpatch(s, subtype='plain', display=False):
212 def mimetextpatch(s, subtype='plain', display=False):
213 '''Return MIME message suitable for a patch.
213 '''Return MIME message suitable for a patch.
214 Charset will be detected as utf-8 or (possibly fake) us-ascii.
214 Charset will be detected as utf-8 or (possibly fake) us-ascii.
215 Transfer encodings will be used if necessary.'''
215 Transfer encodings will be used if necessary.'''
216
216
217 cs = 'us-ascii'
217 cs = 'us-ascii'
218 if not display:
218 if not display:
219 try:
219 try:
220 s.decode('us-ascii')
220 s.decode('us-ascii')
221 except UnicodeDecodeError:
221 except UnicodeDecodeError:
222 try:
222 try:
223 s.decode('utf-8')
223 s.decode('utf-8')
224 cs = 'utf-8'
224 cs = 'utf-8'
225 except UnicodeDecodeError:
225 except UnicodeDecodeError:
226 # We'll go with us-ascii as a fallback.
226 # We'll go with us-ascii as a fallback.
227 pass
227 pass
228
228
229 return mimetextqp(s, subtype, cs)
229 return mimetextqp(s, subtype, cs)
230
230
231 def mimetextqp(body, subtype, charset):
231 def mimetextqp(body, subtype, charset):
232 '''Return MIME message.
232 '''Return MIME message.
233 Quoted-printable transfer encoding will be used if necessary.
233 Quoted-printable transfer encoding will be used if necessary.
234 '''
234 '''
235 enc = None
235 enc = None
236 for line in body.splitlines():
236 for line in body.splitlines():
237 if len(line) > 950:
237 if len(line) > 950:
238 body = quopri.encodestring(body)
238 body = quopri.encodestring(body)
239 enc = "quoted-printable"
239 enc = "quoted-printable"
240 break
240 break
241
241
242 msg = email.MIMEText.MIMEText(body, subtype, charset)
242 msg = email.MIMEText.MIMEText(body, subtype, charset)
243 if enc:
243 if enc:
244 del msg['Content-Transfer-Encoding']
244 del msg['Content-Transfer-Encoding']
245 msg['Content-Transfer-Encoding'] = enc
245 msg['Content-Transfer-Encoding'] = enc
246 return msg
246 return msg
247
247
248 def _charsets(ui):
248 def _charsets(ui):
249 '''Obtains charsets to send mail parts not containing patches.'''
249 '''Obtains charsets to send mail parts not containing patches.'''
250 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
250 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
251 fallbacks = [encoding.fallbackencoding.lower(),
251 fallbacks = [encoding.fallbackencoding.lower(),
252 encoding.encoding.lower(), 'utf-8']
252 encoding.encoding.lower(), 'utf-8']
253 for cs in fallbacks: # find unique charsets while keeping order
253 for cs in fallbacks: # find unique charsets while keeping order
254 if cs not in charsets:
254 if cs not in charsets:
255 charsets.append(cs)
255 charsets.append(cs)
256 return [cs for cs in charsets if not cs.endswith('ascii')]
256 return [cs for cs in charsets if not cs.endswith('ascii')]
257
257
258 def _encode(ui, s, charsets):
258 def _encode(ui, s, charsets):
259 '''Returns (converted) string, charset tuple.
259 '''Returns (converted) string, charset tuple.
260 Finds out best charset by cycling through sendcharsets in descending
260 Finds out best charset by cycling through sendcharsets in descending
261 order. Tries both encoding and fallbackencoding for input. Only as
261 order. Tries both encoding and fallbackencoding for input. Only as
262 last resort send as is in fake ascii.
262 last resort send as is in fake ascii.
263 Caveat: Do not use for mail parts containing patches!'''
263 Caveat: Do not use for mail parts containing patches!'''
264 try:
264 try:
265 s.decode('ascii')
265 s.decode('ascii')
266 except UnicodeDecodeError:
266 except UnicodeDecodeError:
267 sendcharsets = charsets or _charsets(ui)
267 sendcharsets = charsets or _charsets(ui)
268 for ics in (encoding.encoding, encoding.fallbackencoding):
268 for ics in (encoding.encoding, encoding.fallbackencoding):
269 try:
269 try:
270 u = s.decode(ics)
270 u = s.decode(ics)
271 except UnicodeDecodeError:
271 except UnicodeDecodeError:
272 continue
272 continue
273 for ocs in sendcharsets:
273 for ocs in sendcharsets:
274 try:
274 try:
275 return u.encode(ocs), ocs
275 return u.encode(ocs), ocs
276 except UnicodeEncodeError:
276 except UnicodeEncodeError:
277 pass
277 pass
278 except LookupError:
278 except LookupError:
279 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
279 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
280 # if ascii, or all conversion attempts fail, send (broken) ascii
280 # if ascii, or all conversion attempts fail, send (broken) ascii
281 return s, 'us-ascii'
281 return s, 'us-ascii'
282
282
283 def headencode(ui, s, charsets=None, display=False):
283 def headencode(ui, s, charsets=None, display=False):
284 '''Returns RFC-2047 compliant header from given string.'''
284 '''Returns RFC-2047 compliant header from given string.'''
285 if not display:
285 if not display:
286 # split into words?
286 # split into words?
287 s, cs = _encode(ui, s, charsets)
287 s, cs = _encode(ui, s, charsets)
288 return str(email.Header.Header(s, cs))
288 return str(email.Header.Header(s, cs))
289 return s
289 return s
290
290
291 def _addressencode(ui, name, addr, charsets=None):
291 def _addressencode(ui, name, addr, charsets=None):
292 name = headencode(ui, name, charsets)
292 name = headencode(ui, name, charsets)
293 try:
293 try:
294 acc, dom = addr.split('@')
294 acc, dom = addr.split('@')
295 acc = acc.encode('ascii')
295 acc = acc.encode('ascii')
296 dom = dom.decode(encoding.encoding).encode('idna')
296 dom = dom.decode(encoding.encoding).encode('idna')
297 addr = '%s@%s' % (acc, dom)
297 addr = '%s@%s' % (acc, dom)
298 except UnicodeDecodeError:
298 except UnicodeDecodeError:
299 raise error.Abort(_('invalid email address: %s') % addr)
299 raise error.Abort(_('invalid email address: %s') % addr)
300 except ValueError:
300 except ValueError:
301 try:
301 try:
302 # too strict?
302 # too strict?
303 addr = addr.encode('ascii')
303 addr = addr.encode('ascii')
304 except UnicodeDecodeError:
304 except UnicodeDecodeError:
305 raise error.Abort(_('invalid local address: %s') % addr)
305 raise error.Abort(_('invalid local address: %s') % addr)
306 return email.Utils.formataddr((name, addr))
306 return email.Utils.formataddr((name, addr))
307
307
308 def addressencode(ui, address, charsets=None, display=False):
308 def addressencode(ui, address, charsets=None, display=False):
309 '''Turns address into RFC-2047 compliant header.'''
309 '''Turns address into RFC-2047 compliant header.'''
310 if display or not address:
310 if display or not address:
311 return address or ''
311 return address or ''
312 name, addr = email.Utils.parseaddr(address)
312 name, addr = email.Utils.parseaddr(address)
313 return _addressencode(ui, name, addr, charsets)
313 return _addressencode(ui, name, addr, charsets)
314
314
315 def addrlistencode(ui, addrs, charsets=None, display=False):
315 def addrlistencode(ui, addrs, charsets=None, display=False):
316 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
316 '''Turns a list of addresses into a list of RFC-2047 compliant headers.
317 A single element of input list may contain multiple addresses, but output
317 A single element of input list may contain multiple addresses, but output
318 always has one address per item'''
318 always has one address per item'''
319 if display:
319 if display:
320 return [a.strip() for a in addrs if a.strip()]
320 return [a.strip() for a in addrs if a.strip()]
321
321
322 result = []
322 result = []
323 for name, addr in email.Utils.getaddresses(addrs):
323 for name, addr in email.Utils.getaddresses(addrs):
324 if name or addr:
324 if name or addr:
325 result.append(_addressencode(ui, name, addr, charsets))
325 result.append(_addressencode(ui, name, addr, charsets))
326 return result
326 return result
327
327
328 def mimeencode(ui, s, charsets=None, display=False):
328 def mimeencode(ui, s, charsets=None, display=False):
329 '''creates mime text object, encodes it if needed, and sets
329 '''creates mime text object, encodes it if needed, and sets
330 charset and transfer-encoding accordingly.'''
330 charset and transfer-encoding accordingly.'''
331 cs = 'us-ascii'
331 cs = 'us-ascii'
332 if not display:
332 if not display:
333 s, cs = _encode(ui, s, charsets)
333 s, cs = _encode(ui, s, charsets)
334 return mimetextqp(s, 'plain', cs)
334 return mimetextqp(s, 'plain', cs)
@@ -1,182 +1,181
1 #require test-repo
1 #require test-repo
2
2
3 $ cd "$TESTDIR"/..
3 $ cd "$TESTDIR"/..
4
4
5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
6 contrib/casesmash.py not using absolute_import
6 contrib/casesmash.py not using absolute_import
7 contrib/check-code.py not using absolute_import
7 contrib/check-code.py not using absolute_import
8 contrib/check-code.py requires print_function
8 contrib/check-code.py requires print_function
9 contrib/check-config.py not using absolute_import
9 contrib/check-config.py not using absolute_import
10 contrib/check-config.py requires print_function
10 contrib/check-config.py requires print_function
11 contrib/debugcmdserver.py not using absolute_import
11 contrib/debugcmdserver.py not using absolute_import
12 contrib/debugcmdserver.py requires print_function
12 contrib/debugcmdserver.py requires print_function
13 contrib/debugshell.py not using absolute_import
13 contrib/debugshell.py not using absolute_import
14 contrib/fixpax.py not using absolute_import
14 contrib/fixpax.py not using absolute_import
15 contrib/fixpax.py requires print_function
15 contrib/fixpax.py requires print_function
16 contrib/hgclient.py not using absolute_import
16 contrib/hgclient.py not using absolute_import
17 contrib/hgclient.py requires print_function
17 contrib/hgclient.py requires print_function
18 contrib/hgfixes/fix_bytes.py not using absolute_import
18 contrib/hgfixes/fix_bytes.py not using absolute_import
19 contrib/hgfixes/fix_bytesmod.py not using absolute_import
19 contrib/hgfixes/fix_bytesmod.py not using absolute_import
20 contrib/hgfixes/fix_leftover_imports.py not using absolute_import
20 contrib/hgfixes/fix_leftover_imports.py not using absolute_import
21 contrib/import-checker.py not using absolute_import
21 contrib/import-checker.py not using absolute_import
22 contrib/import-checker.py requires print_function
22 contrib/import-checker.py requires print_function
23 contrib/memory.py not using absolute_import
23 contrib/memory.py not using absolute_import
24 contrib/perf.py not using absolute_import
24 contrib/perf.py not using absolute_import
25 contrib/python-hook-examples.py not using absolute_import
25 contrib/python-hook-examples.py not using absolute_import
26 contrib/revsetbenchmarks.py not using absolute_import
26 contrib/revsetbenchmarks.py not using absolute_import
27 contrib/revsetbenchmarks.py requires print_function
27 contrib/revsetbenchmarks.py requires print_function
28 contrib/showstack.py not using absolute_import
28 contrib/showstack.py not using absolute_import
29 contrib/synthrepo.py not using absolute_import
29 contrib/synthrepo.py not using absolute_import
30 contrib/win32/hgwebdir_wsgi.py not using absolute_import
30 contrib/win32/hgwebdir_wsgi.py not using absolute_import
31 doc/check-seclevel.py not using absolute_import
31 doc/check-seclevel.py not using absolute_import
32 doc/gendoc.py not using absolute_import
32 doc/gendoc.py not using absolute_import
33 doc/hgmanpage.py not using absolute_import
33 doc/hgmanpage.py not using absolute_import
34 hgext/__init__.py not using absolute_import
34 hgext/__init__.py not using absolute_import
35 hgext/acl.py not using absolute_import
35 hgext/acl.py not using absolute_import
36 hgext/blackbox.py not using absolute_import
36 hgext/blackbox.py not using absolute_import
37 hgext/bugzilla.py not using absolute_import
37 hgext/bugzilla.py not using absolute_import
38 hgext/censor.py not using absolute_import
38 hgext/censor.py not using absolute_import
39 hgext/children.py not using absolute_import
39 hgext/children.py not using absolute_import
40 hgext/churn.py not using absolute_import
40 hgext/churn.py not using absolute_import
41 hgext/clonebundles.py not using absolute_import
41 hgext/clonebundles.py not using absolute_import
42 hgext/color.py not using absolute_import
42 hgext/color.py not using absolute_import
43 hgext/convert/__init__.py not using absolute_import
43 hgext/convert/__init__.py not using absolute_import
44 hgext/convert/bzr.py not using absolute_import
44 hgext/convert/bzr.py not using absolute_import
45 hgext/convert/common.py not using absolute_import
45 hgext/convert/common.py not using absolute_import
46 hgext/convert/convcmd.py not using absolute_import
46 hgext/convert/convcmd.py not using absolute_import
47 hgext/convert/cvs.py not using absolute_import
47 hgext/convert/cvs.py not using absolute_import
48 hgext/convert/cvsps.py not using absolute_import
48 hgext/convert/cvsps.py not using absolute_import
49 hgext/convert/darcs.py not using absolute_import
49 hgext/convert/darcs.py not using absolute_import
50 hgext/convert/filemap.py not using absolute_import
50 hgext/convert/filemap.py not using absolute_import
51 hgext/convert/git.py not using absolute_import
51 hgext/convert/git.py not using absolute_import
52 hgext/convert/gnuarch.py not using absolute_import
52 hgext/convert/gnuarch.py not using absolute_import
53 hgext/convert/hg.py not using absolute_import
53 hgext/convert/hg.py not using absolute_import
54 hgext/convert/monotone.py not using absolute_import
54 hgext/convert/monotone.py not using absolute_import
55 hgext/convert/p4.py not using absolute_import
55 hgext/convert/p4.py not using absolute_import
56 hgext/convert/subversion.py not using absolute_import
56 hgext/convert/subversion.py not using absolute_import
57 hgext/convert/transport.py not using absolute_import
57 hgext/convert/transport.py not using absolute_import
58 hgext/eol.py not using absolute_import
58 hgext/eol.py not using absolute_import
59 hgext/extdiff.py not using absolute_import
59 hgext/extdiff.py not using absolute_import
60 hgext/factotum.py not using absolute_import
60 hgext/factotum.py not using absolute_import
61 hgext/fetch.py not using absolute_import
61 hgext/fetch.py not using absolute_import
62 hgext/gpg.py not using absolute_import
62 hgext/gpg.py not using absolute_import
63 hgext/graphlog.py not using absolute_import
63 hgext/graphlog.py not using absolute_import
64 hgext/hgcia.py not using absolute_import
64 hgext/hgcia.py not using absolute_import
65 hgext/hgk.py not using absolute_import
65 hgext/hgk.py not using absolute_import
66 hgext/highlight/__init__.py not using absolute_import
66 hgext/highlight/__init__.py not using absolute_import
67 hgext/highlight/highlight.py not using absolute_import
67 hgext/highlight/highlight.py not using absolute_import
68 hgext/histedit.py not using absolute_import
68 hgext/histedit.py not using absolute_import
69 hgext/keyword.py not using absolute_import
69 hgext/keyword.py not using absolute_import
70 hgext/largefiles/__init__.py not using absolute_import
70 hgext/largefiles/__init__.py not using absolute_import
71 hgext/largefiles/basestore.py not using absolute_import
71 hgext/largefiles/basestore.py not using absolute_import
72 hgext/largefiles/lfcommands.py not using absolute_import
72 hgext/largefiles/lfcommands.py not using absolute_import
73 hgext/largefiles/lfutil.py not using absolute_import
73 hgext/largefiles/lfutil.py not using absolute_import
74 hgext/largefiles/localstore.py not using absolute_import
74 hgext/largefiles/localstore.py not using absolute_import
75 hgext/largefiles/overrides.py not using absolute_import
75 hgext/largefiles/overrides.py not using absolute_import
76 hgext/largefiles/proto.py not using absolute_import
76 hgext/largefiles/proto.py not using absolute_import
77 hgext/largefiles/remotestore.py not using absolute_import
77 hgext/largefiles/remotestore.py not using absolute_import
78 hgext/largefiles/reposetup.py not using absolute_import
78 hgext/largefiles/reposetup.py not using absolute_import
79 hgext/largefiles/uisetup.py not using absolute_import
79 hgext/largefiles/uisetup.py not using absolute_import
80 hgext/largefiles/wirestore.py not using absolute_import
80 hgext/largefiles/wirestore.py not using absolute_import
81 hgext/mq.py not using absolute_import
81 hgext/mq.py not using absolute_import
82 hgext/notify.py not using absolute_import
82 hgext/notify.py not using absolute_import
83 hgext/pager.py not using absolute_import
83 hgext/pager.py not using absolute_import
84 hgext/patchbomb.py not using absolute_import
84 hgext/patchbomb.py not using absolute_import
85 hgext/purge.py not using absolute_import
85 hgext/purge.py not using absolute_import
86 hgext/rebase.py not using absolute_import
86 hgext/rebase.py not using absolute_import
87 hgext/record.py not using absolute_import
87 hgext/record.py not using absolute_import
88 hgext/relink.py not using absolute_import
88 hgext/relink.py not using absolute_import
89 hgext/schemes.py not using absolute_import
89 hgext/schemes.py not using absolute_import
90 hgext/share.py not using absolute_import
90 hgext/share.py not using absolute_import
91 hgext/shelve.py not using absolute_import
91 hgext/shelve.py not using absolute_import
92 hgext/strip.py not using absolute_import
92 hgext/strip.py not using absolute_import
93 hgext/transplant.py not using absolute_import
93 hgext/transplant.py not using absolute_import
94 hgext/win32mbcs.py not using absolute_import
94 hgext/win32mbcs.py not using absolute_import
95 hgext/win32text.py not using absolute_import
95 hgext/win32text.py not using absolute_import
96 hgext/zeroconf/Zeroconf.py not using absolute_import
96 hgext/zeroconf/Zeroconf.py not using absolute_import
97 hgext/zeroconf/Zeroconf.py requires print_function
97 hgext/zeroconf/Zeroconf.py requires print_function
98 hgext/zeroconf/__init__.py not using absolute_import
98 hgext/zeroconf/__init__.py not using absolute_import
99 i18n/check-translation.py not using absolute_import
99 i18n/check-translation.py not using absolute_import
100 i18n/polib.py not using absolute_import
100 i18n/polib.py not using absolute_import
101 mercurial/cmdutil.py not using absolute_import
101 mercurial/cmdutil.py not using absolute_import
102 mercurial/commands.py not using absolute_import
102 mercurial/commands.py not using absolute_import
103 mercurial/mail.py requires print_function
104 setup.py not using absolute_import
103 setup.py not using absolute_import
105 tests/filterpyflakes.py requires print_function
104 tests/filterpyflakes.py requires print_function
106 tests/generate-working-copy-states.py requires print_function
105 tests/generate-working-copy-states.py requires print_function
107 tests/get-with-headers.py requires print_function
106 tests/get-with-headers.py requires print_function
108 tests/heredoctest.py requires print_function
107 tests/heredoctest.py requires print_function
109 tests/hypothesishelpers.py not using absolute_import
108 tests/hypothesishelpers.py not using absolute_import
110 tests/hypothesishelpers.py requires print_function
109 tests/hypothesishelpers.py requires print_function
111 tests/killdaemons.py not using absolute_import
110 tests/killdaemons.py not using absolute_import
112 tests/md5sum.py not using absolute_import
111 tests/md5sum.py not using absolute_import
113 tests/mockblackbox.py not using absolute_import
112 tests/mockblackbox.py not using absolute_import
114 tests/printenv.py not using absolute_import
113 tests/printenv.py not using absolute_import
115 tests/readlink.py not using absolute_import
114 tests/readlink.py not using absolute_import
116 tests/readlink.py requires print_function
115 tests/readlink.py requires print_function
117 tests/revlog-formatv0.py not using absolute_import
116 tests/revlog-formatv0.py not using absolute_import
118 tests/run-tests.py not using absolute_import
117 tests/run-tests.py not using absolute_import
119 tests/seq.py not using absolute_import
118 tests/seq.py not using absolute_import
120 tests/seq.py requires print_function
119 tests/seq.py requires print_function
121 tests/silenttestrunner.py not using absolute_import
120 tests/silenttestrunner.py not using absolute_import
122 tests/silenttestrunner.py requires print_function
121 tests/silenttestrunner.py requires print_function
123 tests/sitecustomize.py not using absolute_import
122 tests/sitecustomize.py not using absolute_import
124 tests/svn-safe-append.py not using absolute_import
123 tests/svn-safe-append.py not using absolute_import
125 tests/svnxml.py not using absolute_import
124 tests/svnxml.py not using absolute_import
126 tests/test-ancestor.py requires print_function
125 tests/test-ancestor.py requires print_function
127 tests/test-atomictempfile.py not using absolute_import
126 tests/test-atomictempfile.py not using absolute_import
128 tests/test-batching.py not using absolute_import
127 tests/test-batching.py not using absolute_import
129 tests/test-batching.py requires print_function
128 tests/test-batching.py requires print_function
130 tests/test-bdiff.py not using absolute_import
129 tests/test-bdiff.py not using absolute_import
131 tests/test-bdiff.py requires print_function
130 tests/test-bdiff.py requires print_function
132 tests/test-context.py not using absolute_import
131 tests/test-context.py not using absolute_import
133 tests/test-context.py requires print_function
132 tests/test-context.py requires print_function
134 tests/test-demandimport.py not using absolute_import
133 tests/test-demandimport.py not using absolute_import
135 tests/test-demandimport.py requires print_function
134 tests/test-demandimport.py requires print_function
136 tests/test-dispatch.py not using absolute_import
135 tests/test-dispatch.py not using absolute_import
137 tests/test-dispatch.py requires print_function
136 tests/test-dispatch.py requires print_function
138 tests/test-doctest.py not using absolute_import
137 tests/test-doctest.py not using absolute_import
139 tests/test-duplicateoptions.py not using absolute_import
138 tests/test-duplicateoptions.py not using absolute_import
140 tests/test-duplicateoptions.py requires print_function
139 tests/test-duplicateoptions.py requires print_function
141 tests/test-filecache.py not using absolute_import
140 tests/test-filecache.py not using absolute_import
142 tests/test-filecache.py requires print_function
141 tests/test-filecache.py requires print_function
143 tests/test-filelog.py not using absolute_import
142 tests/test-filelog.py not using absolute_import
144 tests/test-filelog.py requires print_function
143 tests/test-filelog.py requires print_function
145 tests/test-hg-parseurl.py not using absolute_import
144 tests/test-hg-parseurl.py not using absolute_import
146 tests/test-hg-parseurl.py requires print_function
145 tests/test-hg-parseurl.py requires print_function
147 tests/test-hgweb-auth.py not using absolute_import
146 tests/test-hgweb-auth.py not using absolute_import
148 tests/test-hgweb-auth.py requires print_function
147 tests/test-hgweb-auth.py requires print_function
149 tests/test-hgwebdir-paths.py not using absolute_import
148 tests/test-hgwebdir-paths.py not using absolute_import
150 tests/test-hybridencode.py not using absolute_import
149 tests/test-hybridencode.py not using absolute_import
151 tests/test-hybridencode.py requires print_function
150 tests/test-hybridencode.py requires print_function
152 tests/test-lrucachedict.py not using absolute_import
151 tests/test-lrucachedict.py not using absolute_import
153 tests/test-lrucachedict.py requires print_function
152 tests/test-lrucachedict.py requires print_function
154 tests/test-manifest.py not using absolute_import
153 tests/test-manifest.py not using absolute_import
155 tests/test-minirst.py not using absolute_import
154 tests/test-minirst.py not using absolute_import
156 tests/test-minirst.py requires print_function
155 tests/test-minirst.py requires print_function
157 tests/test-parseindex2.py not using absolute_import
156 tests/test-parseindex2.py not using absolute_import
158 tests/test-parseindex2.py requires print_function
157 tests/test-parseindex2.py requires print_function
159 tests/test-pathencode.py not using absolute_import
158 tests/test-pathencode.py not using absolute_import
160 tests/test-pathencode.py requires print_function
159 tests/test-pathencode.py requires print_function
161 tests/test-propertycache.py not using absolute_import
160 tests/test-propertycache.py not using absolute_import
162 tests/test-propertycache.py requires print_function
161 tests/test-propertycache.py requires print_function
163 tests/test-revlog-ancestry.py not using absolute_import
162 tests/test-revlog-ancestry.py not using absolute_import
164 tests/test-revlog-ancestry.py requires print_function
163 tests/test-revlog-ancestry.py requires print_function
165 tests/test-run-tests.py not using absolute_import
164 tests/test-run-tests.py not using absolute_import
166 tests/test-simplemerge.py not using absolute_import
165 tests/test-simplemerge.py not using absolute_import
167 tests/test-status-inprocess.py not using absolute_import
166 tests/test-status-inprocess.py not using absolute_import
168 tests/test-status-inprocess.py requires print_function
167 tests/test-status-inprocess.py requires print_function
169 tests/test-symlink-os-yes-fs-no.py not using absolute_import
168 tests/test-symlink-os-yes-fs-no.py not using absolute_import
170 tests/test-trusted.py not using absolute_import
169 tests/test-trusted.py not using absolute_import
171 tests/test-trusted.py requires print_function
170 tests/test-trusted.py requires print_function
172 tests/test-ui-color.py not using absolute_import
171 tests/test-ui-color.py not using absolute_import
173 tests/test-ui-color.py requires print_function
172 tests/test-ui-color.py requires print_function
174 tests/test-ui-config.py not using absolute_import
173 tests/test-ui-config.py not using absolute_import
175 tests/test-ui-config.py requires print_function
174 tests/test-ui-config.py requires print_function
176 tests/test-ui-verbosity.py not using absolute_import
175 tests/test-ui-verbosity.py not using absolute_import
177 tests/test-ui-verbosity.py requires print_function
176 tests/test-ui-verbosity.py requires print_function
178 tests/test-url.py not using absolute_import
177 tests/test-url.py not using absolute_import
179 tests/test-url.py requires print_function
178 tests/test-url.py requires print_function
180 tests/test-walkrepo.py requires print_function
179 tests/test-walkrepo.py requires print_function
181 tests/test-wireproto.py requires print_function
180 tests/test-wireproto.py requires print_function
182 tests/tinyproxy.py requires print_function
181 tests/tinyproxy.py requires print_function
General Comments 0
You need to be logged in to leave comments. Login now