##// END OF EJS Templates
keepalive: use absolute_import
Gregory Szorc -
r27507:a16489f9 default
parent child Browse files
Show More
@@ -1,751 +1,753 b''
1 # This library is free software; you can redistribute it and/or
1 # This library is free software; you can redistribute it and/or
2 # modify it under the terms of the GNU Lesser General Public
2 # modify it under the terms of the GNU Lesser General Public
3 # License as published by the Free Software Foundation; either
3 # License as published by the Free Software Foundation; either
4 # version 2.1 of the License, or (at your option) any later version.
4 # version 2.1 of the License, or (at your option) any later version.
5 #
5 #
6 # This library is distributed in the hope that it will be useful,
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
9 # Lesser General Public License for more details.
10 #
10 #
11 # You should have received a copy of the GNU Lesser General Public
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, see
12 # License along with this library; if not, see
13 # <http://www.gnu.org/licenses/>.
13 # <http://www.gnu.org/licenses/>.
14
14
15 # This file is part of urlgrabber, a high-level cross-protocol url-grabber
15 # This file is part of urlgrabber, a high-level cross-protocol url-grabber
16 # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
16 # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
17
17
18 # Modified by Benoit Boissinot:
18 # Modified by Benoit Boissinot:
19 # - fix for digest auth (inspired from urllib2.py @ Python v2.4)
19 # - fix for digest auth (inspired from urllib2.py @ Python v2.4)
20 # Modified by Dirkjan Ochtman:
20 # Modified by Dirkjan Ochtman:
21 # - import md5 function from a local util module
21 # - import md5 function from a local util module
22 # Modified by Augie Fackler:
22 # Modified by Augie Fackler:
23 # - add safesend method and use it to prevent broken pipe errors
23 # - add safesend method and use it to prevent broken pipe errors
24 # on large POST requests
24 # on large POST requests
25
25
26 """An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.
26 """An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.
27
27
28 >>> import urllib2
28 >>> import urllib2
29 >>> from keepalive import HTTPHandler
29 >>> from keepalive import HTTPHandler
30 >>> keepalive_handler = HTTPHandler()
30 >>> keepalive_handler = HTTPHandler()
31 >>> opener = urllib2.build_opener(keepalive_handler)
31 >>> opener = urllib2.build_opener(keepalive_handler)
32 >>> urllib2.install_opener(opener)
32 >>> urllib2.install_opener(opener)
33 >>>
33 >>>
34 >>> fo = urllib2.urlopen('http://www.python.org')
34 >>> fo = urllib2.urlopen('http://www.python.org')
35
35
36 If a connection to a given host is requested, and all of the existing
36 If a connection to a given host is requested, and all of the existing
37 connections are still in use, another connection will be opened. If
37 connections are still in use, another connection will be opened. If
38 the handler tries to use an existing connection but it fails in some
38 the handler tries to use an existing connection but it fails in some
39 way, it will be closed and removed from the pool.
39 way, it will be closed and removed from the pool.
40
40
41 To remove the handler, simply re-run build_opener with no arguments, and
41 To remove the handler, simply re-run build_opener with no arguments, and
42 install that opener.
42 install that opener.
43
43
44 You can explicitly close connections by using the close_connection()
44 You can explicitly close connections by using the close_connection()
45 method of the returned file-like object (described below) or you can
45 method of the returned file-like object (described below) or you can
46 use the handler methods:
46 use the handler methods:
47
47
48 close_connection(host)
48 close_connection(host)
49 close_all()
49 close_all()
50 open_connections()
50 open_connections()
51
51
52 NOTE: using the close_connection and close_all methods of the handler
52 NOTE: using the close_connection and close_all methods of the handler
53 should be done with care when using multiple threads.
53 should be done with care when using multiple threads.
54 * there is nothing that prevents another thread from creating new
54 * there is nothing that prevents another thread from creating new
55 connections immediately after connections are closed
55 connections immediately after connections are closed
56 * no checks are done to prevent in-use connections from being closed
56 * no checks are done to prevent in-use connections from being closed
57
57
58 >>> keepalive_handler.close_all()
58 >>> keepalive_handler.close_all()
59
59
60 EXTRA ATTRIBUTES AND METHODS
60 EXTRA ATTRIBUTES AND METHODS
61
61
62 Upon a status of 200, the object returned has a few additional
62 Upon a status of 200, the object returned has a few additional
63 attributes and methods, which should not be used if you want to
63 attributes and methods, which should not be used if you want to
64 remain consistent with the normal urllib2-returned objects:
64 remain consistent with the normal urllib2-returned objects:
65
65
66 close_connection() - close the connection to the host
66 close_connection() - close the connection to the host
67 readlines() - you know, readlines()
67 readlines() - you know, readlines()
68 status - the return status (i.e. 404)
68 status - the return status (i.e. 404)
69 reason - english translation of status (i.e. 'File not found')
69 reason - english translation of status (i.e. 'File not found')
70
70
71 If you want the best of both worlds, use this inside an
71 If you want the best of both worlds, use this inside an
72 AttributeError-catching try:
72 AttributeError-catching try:
73
73
74 >>> try: status = fo.status
74 >>> try: status = fo.status
75 >>> except AttributeError: status = None
75 >>> except AttributeError: status = None
76
76
77 Unfortunately, these are ONLY there if status == 200, so it's not
77 Unfortunately, these are ONLY there if status == 200, so it's not
78 easy to distinguish between non-200 responses. The reason is that
78 easy to distinguish between non-200 responses. The reason is that
79 urllib2 tries to do clever things with error codes 301, 302, 401,
79 urllib2 tries to do clever things with error codes 301, 302, 401,
80 and 407, and it wraps the object upon return.
80 and 407, and it wraps the object upon return.
81
81
82 For python versions earlier than 2.4, you can avoid this fancy error
82 For python versions earlier than 2.4, you can avoid this fancy error
83 handling by setting the module-level global HANDLE_ERRORS to zero.
83 handling by setting the module-level global HANDLE_ERRORS to zero.
84 You see, prior to 2.4, it's the HTTP Handler's job to determine what
84 You see, prior to 2.4, it's the HTTP Handler's job to determine what
85 to handle specially, and what to just pass up. HANDLE_ERRORS == 0
85 to handle specially, and what to just pass up. HANDLE_ERRORS == 0
86 means "pass everything up". In python 2.4, however, this job no
86 means "pass everything up". In python 2.4, however, this job no
87 longer belongs to the HTTP Handler and is now done by a NEW handler,
87 longer belongs to the HTTP Handler and is now done by a NEW handler,
88 HTTPErrorProcessor. Here's the bottom line:
88 HTTPErrorProcessor. Here's the bottom line:
89
89
90 python version < 2.4
90 python version < 2.4
91 HANDLE_ERRORS == 1 (default) pass up 200, treat the rest as
91 HANDLE_ERRORS == 1 (default) pass up 200, treat the rest as
92 errors
92 errors
93 HANDLE_ERRORS == 0 pass everything up, error processing is
93 HANDLE_ERRORS == 0 pass everything up, error processing is
94 left to the calling code
94 left to the calling code
95 python version >= 2.4
95 python version >= 2.4
96 HANDLE_ERRORS == 1 pass up 200, treat the rest as errors
96 HANDLE_ERRORS == 1 pass up 200, treat the rest as errors
97 HANDLE_ERRORS == 0 (default) pass everything up, let the
97 HANDLE_ERRORS == 0 (default) pass everything up, let the
98 other handlers (specifically,
98 other handlers (specifically,
99 HTTPErrorProcessor) decide what to do
99 HTTPErrorProcessor) decide what to do
100
100
101 In practice, setting the variable either way makes little difference
101 In practice, setting the variable either way makes little difference
102 in python 2.4, so for the most consistent behavior across versions,
102 in python 2.4, so for the most consistent behavior across versions,
103 you probably just want to use the defaults, which will give you
103 you probably just want to use the defaults, which will give you
104 exceptions on errors.
104 exceptions on errors.
105
105
106 """
106 """
107
107
108 # $Id: keepalive.py,v 1.14 2006/04/04 21:00:32 mstenner Exp $
108 # $Id: keepalive.py,v 1.14 2006/04/04 21:00:32 mstenner Exp $
109
109
110 from __future__ import absolute_import
111
110 import errno
112 import errno
111 import httplib
113 import httplib
112 import socket
114 import socket
115 import sys
113 import thread
116 import thread
114 import urllib2
117 import urllib2
115
118
116 DEBUG = None
119 DEBUG = None
117
120
118 import sys
119 if sys.version_info < (2, 4):
121 if sys.version_info < (2, 4):
120 HANDLE_ERRORS = 1
122 HANDLE_ERRORS = 1
121 else: HANDLE_ERRORS = 0
123 else: HANDLE_ERRORS = 0
122
124
123 class ConnectionManager(object):
125 class ConnectionManager(object):
124 """
126 """
125 The connection manager must be able to:
127 The connection manager must be able to:
126 * keep track of all existing
128 * keep track of all existing
127 """
129 """
128 def __init__(self):
130 def __init__(self):
129 self._lock = thread.allocate_lock()
131 self._lock = thread.allocate_lock()
130 self._hostmap = {} # map hosts to a list of connections
132 self._hostmap = {} # map hosts to a list of connections
131 self._connmap = {} # map connections to host
133 self._connmap = {} # map connections to host
132 self._readymap = {} # map connection to ready state
134 self._readymap = {} # map connection to ready state
133
135
134 def add(self, host, connection, ready):
136 def add(self, host, connection, ready):
135 self._lock.acquire()
137 self._lock.acquire()
136 try:
138 try:
137 if host not in self._hostmap:
139 if host not in self._hostmap:
138 self._hostmap[host] = []
140 self._hostmap[host] = []
139 self._hostmap[host].append(connection)
141 self._hostmap[host].append(connection)
140 self._connmap[connection] = host
142 self._connmap[connection] = host
141 self._readymap[connection] = ready
143 self._readymap[connection] = ready
142 finally:
144 finally:
143 self._lock.release()
145 self._lock.release()
144
146
145 def remove(self, connection):
147 def remove(self, connection):
146 self._lock.acquire()
148 self._lock.acquire()
147 try:
149 try:
148 try:
150 try:
149 host = self._connmap[connection]
151 host = self._connmap[connection]
150 except KeyError:
152 except KeyError:
151 pass
153 pass
152 else:
154 else:
153 del self._connmap[connection]
155 del self._connmap[connection]
154 del self._readymap[connection]
156 del self._readymap[connection]
155 self._hostmap[host].remove(connection)
157 self._hostmap[host].remove(connection)
156 if not self._hostmap[host]: del self._hostmap[host]
158 if not self._hostmap[host]: del self._hostmap[host]
157 finally:
159 finally:
158 self._lock.release()
160 self._lock.release()
159
161
160 def set_ready(self, connection, ready):
162 def set_ready(self, connection, ready):
161 try:
163 try:
162 self._readymap[connection] = ready
164 self._readymap[connection] = ready
163 except KeyError:
165 except KeyError:
164 pass
166 pass
165
167
166 def get_ready_conn(self, host):
168 def get_ready_conn(self, host):
167 conn = None
169 conn = None
168 self._lock.acquire()
170 self._lock.acquire()
169 try:
171 try:
170 if host in self._hostmap:
172 if host in self._hostmap:
171 for c in self._hostmap[host]:
173 for c in self._hostmap[host]:
172 if self._readymap[c]:
174 if self._readymap[c]:
173 self._readymap[c] = 0
175 self._readymap[c] = 0
174 conn = c
176 conn = c
175 break
177 break
176 finally:
178 finally:
177 self._lock.release()
179 self._lock.release()
178 return conn
180 return conn
179
181
180 def get_all(self, host=None):
182 def get_all(self, host=None):
181 if host:
183 if host:
182 return list(self._hostmap.get(host, []))
184 return list(self._hostmap.get(host, []))
183 else:
185 else:
184 return dict(self._hostmap)
186 return dict(self._hostmap)
185
187
186 class KeepAliveHandler(object):
188 class KeepAliveHandler(object):
187 def __init__(self):
189 def __init__(self):
188 self._cm = ConnectionManager()
190 self._cm = ConnectionManager()
189
191
190 #### Connection Management
192 #### Connection Management
191 def open_connections(self):
193 def open_connections(self):
192 """return a list of connected hosts and the number of connections
194 """return a list of connected hosts and the number of connections
193 to each. [('foo.com:80', 2), ('bar.org', 1)]"""
195 to each. [('foo.com:80', 2), ('bar.org', 1)]"""
194 return [(host, len(li)) for (host, li) in self._cm.get_all().items()]
196 return [(host, len(li)) for (host, li) in self._cm.get_all().items()]
195
197
196 def close_connection(self, host):
198 def close_connection(self, host):
197 """close connection(s) to <host>
199 """close connection(s) to <host>
198 host is the host:port spec, as in 'www.cnn.com:8080' as passed in.
200 host is the host:port spec, as in 'www.cnn.com:8080' as passed in.
199 no error occurs if there is no connection to that host."""
201 no error occurs if there is no connection to that host."""
200 for h in self._cm.get_all(host):
202 for h in self._cm.get_all(host):
201 self._cm.remove(h)
203 self._cm.remove(h)
202 h.close()
204 h.close()
203
205
204 def close_all(self):
206 def close_all(self):
205 """close all open connections"""
207 """close all open connections"""
206 for host, conns in self._cm.get_all().iteritems():
208 for host, conns in self._cm.get_all().iteritems():
207 for h in conns:
209 for h in conns:
208 self._cm.remove(h)
210 self._cm.remove(h)
209 h.close()
211 h.close()
210
212
211 def _request_closed(self, request, host, connection):
213 def _request_closed(self, request, host, connection):
212 """tells us that this request is now closed and that the
214 """tells us that this request is now closed and that the
213 connection is ready for another request"""
215 connection is ready for another request"""
214 self._cm.set_ready(connection, 1)
216 self._cm.set_ready(connection, 1)
215
217
216 def _remove_connection(self, host, connection, close=0):
218 def _remove_connection(self, host, connection, close=0):
217 if close:
219 if close:
218 connection.close()
220 connection.close()
219 self._cm.remove(connection)
221 self._cm.remove(connection)
220
222
221 #### Transaction Execution
223 #### Transaction Execution
222 def http_open(self, req):
224 def http_open(self, req):
223 return self.do_open(HTTPConnection, req)
225 return self.do_open(HTTPConnection, req)
224
226
225 def do_open(self, http_class, req):
227 def do_open(self, http_class, req):
226 host = req.get_host()
228 host = req.get_host()
227 if not host:
229 if not host:
228 raise urllib2.URLError('no host given')
230 raise urllib2.URLError('no host given')
229
231
230 try:
232 try:
231 h = self._cm.get_ready_conn(host)
233 h = self._cm.get_ready_conn(host)
232 while h:
234 while h:
233 r = self._reuse_connection(h, req, host)
235 r = self._reuse_connection(h, req, host)
234
236
235 # if this response is non-None, then it worked and we're
237 # if this response is non-None, then it worked and we're
236 # done. Break out, skipping the else block.
238 # done. Break out, skipping the else block.
237 if r:
239 if r:
238 break
240 break
239
241
240 # connection is bad - possibly closed by server
242 # connection is bad - possibly closed by server
241 # discard it and ask for the next free connection
243 # discard it and ask for the next free connection
242 h.close()
244 h.close()
243 self._cm.remove(h)
245 self._cm.remove(h)
244 h = self._cm.get_ready_conn(host)
246 h = self._cm.get_ready_conn(host)
245 else:
247 else:
246 # no (working) free connections were found. Create a new one.
248 # no (working) free connections were found. Create a new one.
247 h = http_class(host)
249 h = http_class(host)
248 if DEBUG:
250 if DEBUG:
249 DEBUG.info("creating new connection to %s (%d)",
251 DEBUG.info("creating new connection to %s (%d)",
250 host, id(h))
252 host, id(h))
251 self._cm.add(host, h, 0)
253 self._cm.add(host, h, 0)
252 self._start_transaction(h, req)
254 self._start_transaction(h, req)
253 r = h.getresponse()
255 r = h.getresponse()
254 except (socket.error, httplib.HTTPException) as err:
256 except (socket.error, httplib.HTTPException) as err:
255 raise urllib2.URLError(err)
257 raise urllib2.URLError(err)
256
258
257 # if not a persistent connection, don't try to reuse it
259 # if not a persistent connection, don't try to reuse it
258 if r.will_close:
260 if r.will_close:
259 self._cm.remove(h)
261 self._cm.remove(h)
260
262
261 if DEBUG:
263 if DEBUG:
262 DEBUG.info("STATUS: %s, %s", r.status, r.reason)
264 DEBUG.info("STATUS: %s, %s", r.status, r.reason)
263 r._handler = self
265 r._handler = self
264 r._host = host
266 r._host = host
265 r._url = req.get_full_url()
267 r._url = req.get_full_url()
266 r._connection = h
268 r._connection = h
267 r.code = r.status
269 r.code = r.status
268 r.headers = r.msg
270 r.headers = r.msg
269 r.msg = r.reason
271 r.msg = r.reason
270
272
271 if r.status == 200 or not HANDLE_ERRORS:
273 if r.status == 200 or not HANDLE_ERRORS:
272 return r
274 return r
273 else:
275 else:
274 return self.parent.error('http', req, r,
276 return self.parent.error('http', req, r,
275 r.status, r.msg, r.headers)
277 r.status, r.msg, r.headers)
276
278
277 def _reuse_connection(self, h, req, host):
279 def _reuse_connection(self, h, req, host):
278 """start the transaction with a re-used connection
280 """start the transaction with a re-used connection
279 return a response object (r) upon success or None on failure.
281 return a response object (r) upon success or None on failure.
280 This DOES not close or remove bad connections in cases where
282 This DOES not close or remove bad connections in cases where
281 it returns. However, if an unexpected exception occurs, it
283 it returns. However, if an unexpected exception occurs, it
282 will close and remove the connection before re-raising.
284 will close and remove the connection before re-raising.
283 """
285 """
284 try:
286 try:
285 self._start_transaction(h, req)
287 self._start_transaction(h, req)
286 r = h.getresponse()
288 r = h.getresponse()
287 # note: just because we got something back doesn't mean it
289 # note: just because we got something back doesn't mean it
288 # worked. We'll check the version below, too.
290 # worked. We'll check the version below, too.
289 except (socket.error, httplib.HTTPException):
291 except (socket.error, httplib.HTTPException):
290 r = None
292 r = None
291 except: # re-raises
293 except: # re-raises
292 # adding this block just in case we've missed
294 # adding this block just in case we've missed
293 # something we will still raise the exception, but
295 # something we will still raise the exception, but
294 # lets try and close the connection and remove it
296 # lets try and close the connection and remove it
295 # first. We previously got into a nasty loop
297 # first. We previously got into a nasty loop
296 # where an exception was uncaught, and so the
298 # where an exception was uncaught, and so the
297 # connection stayed open. On the next try, the
299 # connection stayed open. On the next try, the
298 # same exception was raised, etc. The trade-off is
300 # same exception was raised, etc. The trade-off is
299 # that it's now possible this call will raise
301 # that it's now possible this call will raise
300 # a DIFFERENT exception
302 # a DIFFERENT exception
301 if DEBUG:
303 if DEBUG:
302 DEBUG.error("unexpected exception - closing "
304 DEBUG.error("unexpected exception - closing "
303 "connection to %s (%d)", host, id(h))
305 "connection to %s (%d)", host, id(h))
304 self._cm.remove(h)
306 self._cm.remove(h)
305 h.close()
307 h.close()
306 raise
308 raise
307
309
308 if r is None or r.version == 9:
310 if r is None or r.version == 9:
309 # httplib falls back to assuming HTTP 0.9 if it gets a
311 # httplib falls back to assuming HTTP 0.9 if it gets a
310 # bad header back. This is most likely to happen if
312 # bad header back. This is most likely to happen if
311 # the socket has been closed by the server since we
313 # the socket has been closed by the server since we
312 # last used the connection.
314 # last used the connection.
313 if DEBUG:
315 if DEBUG:
314 DEBUG.info("failed to re-use connection to %s (%d)",
316 DEBUG.info("failed to re-use connection to %s (%d)",
315 host, id(h))
317 host, id(h))
316 r = None
318 r = None
317 else:
319 else:
318 if DEBUG:
320 if DEBUG:
319 DEBUG.info("re-using connection to %s (%d)", host, id(h))
321 DEBUG.info("re-using connection to %s (%d)", host, id(h))
320
322
321 return r
323 return r
322
324
323 def _start_transaction(self, h, req):
325 def _start_transaction(self, h, req):
324 # What follows mostly reimplements HTTPConnection.request()
326 # What follows mostly reimplements HTTPConnection.request()
325 # except it adds self.parent.addheaders in the mix.
327 # except it adds self.parent.addheaders in the mix.
326 headers = req.headers.copy()
328 headers = req.headers.copy()
327 if sys.version_info >= (2, 4):
329 if sys.version_info >= (2, 4):
328 headers.update(req.unredirected_hdrs)
330 headers.update(req.unredirected_hdrs)
329 headers.update(self.parent.addheaders)
331 headers.update(self.parent.addheaders)
330 headers = dict((n.lower(), v) for n, v in headers.items())
332 headers = dict((n.lower(), v) for n, v in headers.items())
331 skipheaders = {}
333 skipheaders = {}
332 for n in ('host', 'accept-encoding'):
334 for n in ('host', 'accept-encoding'):
333 if n in headers:
335 if n in headers:
334 skipheaders['skip_' + n.replace('-', '_')] = 1
336 skipheaders['skip_' + n.replace('-', '_')] = 1
335 try:
337 try:
336 if req.has_data():
338 if req.has_data():
337 data = req.get_data()
339 data = req.get_data()
338 h.putrequest('POST', req.get_selector(), **skipheaders)
340 h.putrequest('POST', req.get_selector(), **skipheaders)
339 if 'content-type' not in headers:
341 if 'content-type' not in headers:
340 h.putheader('Content-type',
342 h.putheader('Content-type',
341 'application/x-www-form-urlencoded')
343 'application/x-www-form-urlencoded')
342 if 'content-length' not in headers:
344 if 'content-length' not in headers:
343 h.putheader('Content-length', '%d' % len(data))
345 h.putheader('Content-length', '%d' % len(data))
344 else:
346 else:
345 h.putrequest('GET', req.get_selector(), **skipheaders)
347 h.putrequest('GET', req.get_selector(), **skipheaders)
346 except (socket.error) as err:
348 except (socket.error) as err:
347 raise urllib2.URLError(err)
349 raise urllib2.URLError(err)
348 for k, v in headers.items():
350 for k, v in headers.items():
349 h.putheader(k, v)
351 h.putheader(k, v)
350 h.endheaders()
352 h.endheaders()
351 if req.has_data():
353 if req.has_data():
352 h.send(data)
354 h.send(data)
353
355
354 class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler):
356 class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler):
355 pass
357 pass
356
358
357 class HTTPResponse(httplib.HTTPResponse):
359 class HTTPResponse(httplib.HTTPResponse):
358 # we need to subclass HTTPResponse in order to
360 # we need to subclass HTTPResponse in order to
359 # 1) add readline() and readlines() methods
361 # 1) add readline() and readlines() methods
360 # 2) add close_connection() methods
362 # 2) add close_connection() methods
361 # 3) add info() and geturl() methods
363 # 3) add info() and geturl() methods
362
364
363 # in order to add readline(), read must be modified to deal with a
365 # in order to add readline(), read must be modified to deal with a
364 # buffer. example: readline must read a buffer and then spit back
366 # buffer. example: readline must read a buffer and then spit back
365 # one line at a time. The only real alternative is to read one
367 # one line at a time. The only real alternative is to read one
366 # BYTE at a time (ick). Once something has been read, it can't be
368 # BYTE at a time (ick). Once something has been read, it can't be
367 # put back (ok, maybe it can, but that's even uglier than this),
369 # put back (ok, maybe it can, but that's even uglier than this),
368 # so if you THEN do a normal read, you must first take stuff from
370 # so if you THEN do a normal read, you must first take stuff from
369 # the buffer.
371 # the buffer.
370
372
371 # the read method wraps the original to accommodate buffering,
373 # the read method wraps the original to accommodate buffering,
372 # although read() never adds to the buffer.
374 # although read() never adds to the buffer.
373 # Both readline and readlines have been stolen with almost no
375 # Both readline and readlines have been stolen with almost no
374 # modification from socket.py
376 # modification from socket.py
375
377
376
378
377 def __init__(self, sock, debuglevel=0, strict=0, method=None):
379 def __init__(self, sock, debuglevel=0, strict=0, method=None):
378 httplib.HTTPResponse.__init__(self, sock, debuglevel, method)
380 httplib.HTTPResponse.__init__(self, sock, debuglevel, method)
379 self.fileno = sock.fileno
381 self.fileno = sock.fileno
380 self.code = None
382 self.code = None
381 self._rbuf = ''
383 self._rbuf = ''
382 self._rbufsize = 8096
384 self._rbufsize = 8096
383 self._handler = None # inserted by the handler later
385 self._handler = None # inserted by the handler later
384 self._host = None # (same)
386 self._host = None # (same)
385 self._url = None # (same)
387 self._url = None # (same)
386 self._connection = None # (same)
388 self._connection = None # (same)
387
389
388 _raw_read = httplib.HTTPResponse.read
390 _raw_read = httplib.HTTPResponse.read
389
391
390 def close(self):
392 def close(self):
391 if self.fp:
393 if self.fp:
392 self.fp.close()
394 self.fp.close()
393 self.fp = None
395 self.fp = None
394 if self._handler:
396 if self._handler:
395 self._handler._request_closed(self, self._host,
397 self._handler._request_closed(self, self._host,
396 self._connection)
398 self._connection)
397
399
398 def close_connection(self):
400 def close_connection(self):
399 self._handler._remove_connection(self._host, self._connection, close=1)
401 self._handler._remove_connection(self._host, self._connection, close=1)
400 self.close()
402 self.close()
401
403
402 def info(self):
404 def info(self):
403 return self.headers
405 return self.headers
404
406
405 def geturl(self):
407 def geturl(self):
406 return self._url
408 return self._url
407
409
408 def read(self, amt=None):
410 def read(self, amt=None):
409 # the _rbuf test is only in this first if for speed. It's not
411 # the _rbuf test is only in this first if for speed. It's not
410 # logically necessary
412 # logically necessary
411 if self._rbuf and not amt is None:
413 if self._rbuf and not amt is None:
412 L = len(self._rbuf)
414 L = len(self._rbuf)
413 if amt > L:
415 if amt > L:
414 amt -= L
416 amt -= L
415 else:
417 else:
416 s = self._rbuf[:amt]
418 s = self._rbuf[:amt]
417 self._rbuf = self._rbuf[amt:]
419 self._rbuf = self._rbuf[amt:]
418 return s
420 return s
419
421
420 s = self._rbuf + self._raw_read(amt)
422 s = self._rbuf + self._raw_read(amt)
421 self._rbuf = ''
423 self._rbuf = ''
422 return s
424 return s
423
425
424 # stolen from Python SVN #68532 to fix issue1088
426 # stolen from Python SVN #68532 to fix issue1088
425 def _read_chunked(self, amt):
427 def _read_chunked(self, amt):
426 chunk_left = self.chunk_left
428 chunk_left = self.chunk_left
427 value = ''
429 value = ''
428
430
429 # XXX This accumulates chunks by repeated string concatenation,
431 # XXX This accumulates chunks by repeated string concatenation,
430 # which is not efficient as the number or size of chunks gets big.
432 # which is not efficient as the number or size of chunks gets big.
431 while True:
433 while True:
432 if chunk_left is None:
434 if chunk_left is None:
433 line = self.fp.readline()
435 line = self.fp.readline()
434 i = line.find(';')
436 i = line.find(';')
435 if i >= 0:
437 if i >= 0:
436 line = line[:i] # strip chunk-extensions
438 line = line[:i] # strip chunk-extensions
437 try:
439 try:
438 chunk_left = int(line, 16)
440 chunk_left = int(line, 16)
439 except ValueError:
441 except ValueError:
440 # close the connection as protocol synchronization is
442 # close the connection as protocol synchronization is
441 # probably lost
443 # probably lost
442 self.close()
444 self.close()
443 raise httplib.IncompleteRead(value)
445 raise httplib.IncompleteRead(value)
444 if chunk_left == 0:
446 if chunk_left == 0:
445 break
447 break
446 if amt is None:
448 if amt is None:
447 value += self._safe_read(chunk_left)
449 value += self._safe_read(chunk_left)
448 elif amt < chunk_left:
450 elif amt < chunk_left:
449 value += self._safe_read(amt)
451 value += self._safe_read(amt)
450 self.chunk_left = chunk_left - amt
452 self.chunk_left = chunk_left - amt
451 return value
453 return value
452 elif amt == chunk_left:
454 elif amt == chunk_left:
453 value += self._safe_read(amt)
455 value += self._safe_read(amt)
454 self._safe_read(2) # toss the CRLF at the end of the chunk
456 self._safe_read(2) # toss the CRLF at the end of the chunk
455 self.chunk_left = None
457 self.chunk_left = None
456 return value
458 return value
457 else:
459 else:
458 value += self._safe_read(chunk_left)
460 value += self._safe_read(chunk_left)
459 amt -= chunk_left
461 amt -= chunk_left
460
462
461 # we read the whole chunk, get another
463 # we read the whole chunk, get another
462 self._safe_read(2) # toss the CRLF at the end of the chunk
464 self._safe_read(2) # toss the CRLF at the end of the chunk
463 chunk_left = None
465 chunk_left = None
464
466
465 # read and discard trailer up to the CRLF terminator
467 # read and discard trailer up to the CRLF terminator
466 ### note: we shouldn't have any trailers!
468 ### note: we shouldn't have any trailers!
467 while True:
469 while True:
468 line = self.fp.readline()
470 line = self.fp.readline()
469 if not line:
471 if not line:
470 # a vanishingly small number of sites EOF without
472 # a vanishingly small number of sites EOF without
471 # sending the trailer
473 # sending the trailer
472 break
474 break
473 if line == '\r\n':
475 if line == '\r\n':
474 break
476 break
475
477
476 # we read everything; close the "file"
478 # we read everything; close the "file"
477 self.close()
479 self.close()
478
480
479 return value
481 return value
480
482
481 def readline(self, limit=-1):
483 def readline(self, limit=-1):
482 i = self._rbuf.find('\n')
484 i = self._rbuf.find('\n')
483 while i < 0 and not (0 < limit <= len(self._rbuf)):
485 while i < 0 and not (0 < limit <= len(self._rbuf)):
484 new = self._raw_read(self._rbufsize)
486 new = self._raw_read(self._rbufsize)
485 if not new:
487 if not new:
486 break
488 break
487 i = new.find('\n')
489 i = new.find('\n')
488 if i >= 0:
490 if i >= 0:
489 i = i + len(self._rbuf)
491 i = i + len(self._rbuf)
490 self._rbuf = self._rbuf + new
492 self._rbuf = self._rbuf + new
491 if i < 0:
493 if i < 0:
492 i = len(self._rbuf)
494 i = len(self._rbuf)
493 else:
495 else:
494 i = i + 1
496 i = i + 1
495 if 0 <= limit < len(self._rbuf):
497 if 0 <= limit < len(self._rbuf):
496 i = limit
498 i = limit
497 data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
499 data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
498 return data
500 return data
499
501
500 def readlines(self, sizehint=0):
502 def readlines(self, sizehint=0):
501 total = 0
503 total = 0
502 list = []
504 list = []
503 while True:
505 while True:
504 line = self.readline()
506 line = self.readline()
505 if not line:
507 if not line:
506 break
508 break
507 list.append(line)
509 list.append(line)
508 total += len(line)
510 total += len(line)
509 if sizehint and total >= sizehint:
511 if sizehint and total >= sizehint:
510 break
512 break
511 return list
513 return list
512
514
513 def safesend(self, str):
515 def safesend(self, str):
514 """Send `str' to the server.
516 """Send `str' to the server.
515
517
516 Shamelessly ripped off from httplib to patch a bad behavior.
518 Shamelessly ripped off from httplib to patch a bad behavior.
517 """
519 """
518 # _broken_pipe_resp is an attribute we set in this function
520 # _broken_pipe_resp is an attribute we set in this function
519 # if the socket is closed while we're sending data but
521 # if the socket is closed while we're sending data but
520 # the server sent us a response before hanging up.
522 # the server sent us a response before hanging up.
521 # In that case, we want to pretend to send the rest of the
523 # In that case, we want to pretend to send the rest of the
522 # outgoing data, and then let the user use getresponse()
524 # outgoing data, and then let the user use getresponse()
523 # (which we wrap) to get this last response before
525 # (which we wrap) to get this last response before
524 # opening a new socket.
526 # opening a new socket.
525 if getattr(self, '_broken_pipe_resp', None) is not None:
527 if getattr(self, '_broken_pipe_resp', None) is not None:
526 return
528 return
527
529
528 if self.sock is None:
530 if self.sock is None:
529 if self.auto_open:
531 if self.auto_open:
530 self.connect()
532 self.connect()
531 else:
533 else:
532 raise httplib.NotConnected
534 raise httplib.NotConnected
533
535
534 # send the data to the server. if we get a broken pipe, then close
536 # send the data to the server. if we get a broken pipe, then close
535 # the socket. we want to reconnect when somebody tries to send again.
537 # the socket. we want to reconnect when somebody tries to send again.
536 #
538 #
537 # NOTE: we DO propagate the error, though, because we cannot simply
539 # NOTE: we DO propagate the error, though, because we cannot simply
538 # ignore the error... the caller will know if they can retry.
540 # ignore the error... the caller will know if they can retry.
539 if self.debuglevel > 0:
541 if self.debuglevel > 0:
540 print "send:", repr(str)
542 print "send:", repr(str)
541 try:
543 try:
542 blocksize = 8192
544 blocksize = 8192
543 read = getattr(str, 'read', None)
545 read = getattr(str, 'read', None)
544 if read is not None:
546 if read is not None:
545 if self.debuglevel > 0:
547 if self.debuglevel > 0:
546 print "sending a read()able"
548 print "sending a read()able"
547 data = read(blocksize)
549 data = read(blocksize)
548 while data:
550 while data:
549 self.sock.sendall(data)
551 self.sock.sendall(data)
550 data = read(blocksize)
552 data = read(blocksize)
551 else:
553 else:
552 self.sock.sendall(str)
554 self.sock.sendall(str)
553 except socket.error as v:
555 except socket.error as v:
554 reraise = True
556 reraise = True
555 if v[0] == errno.EPIPE: # Broken pipe
557 if v[0] == errno.EPIPE: # Broken pipe
556 if self._HTTPConnection__state == httplib._CS_REQ_SENT:
558 if self._HTTPConnection__state == httplib._CS_REQ_SENT:
557 self._broken_pipe_resp = None
559 self._broken_pipe_resp = None
558 self._broken_pipe_resp = self.getresponse()
560 self._broken_pipe_resp = self.getresponse()
559 reraise = False
561 reraise = False
560 self.close()
562 self.close()
561 if reraise:
563 if reraise:
562 raise
564 raise
563
565
564 def wrapgetresponse(cls):
566 def wrapgetresponse(cls):
565 """Wraps getresponse in cls with a broken-pipe sane version.
567 """Wraps getresponse in cls with a broken-pipe sane version.
566 """
568 """
567 def safegetresponse(self):
569 def safegetresponse(self):
568 # In safesend() we might set the _broken_pipe_resp
570 # In safesend() we might set the _broken_pipe_resp
569 # attribute, in which case the socket has already
571 # attribute, in which case the socket has already
570 # been closed and we just need to give them the response
572 # been closed and we just need to give them the response
571 # back. Otherwise, we use the normal response path.
573 # back. Otherwise, we use the normal response path.
572 r = getattr(self, '_broken_pipe_resp', None)
574 r = getattr(self, '_broken_pipe_resp', None)
573 if r is not None:
575 if r is not None:
574 return r
576 return r
575 return cls.getresponse(self)
577 return cls.getresponse(self)
576 safegetresponse.__doc__ = cls.getresponse.__doc__
578 safegetresponse.__doc__ = cls.getresponse.__doc__
577 return safegetresponse
579 return safegetresponse
578
580
579 class HTTPConnection(httplib.HTTPConnection):
581 class HTTPConnection(httplib.HTTPConnection):
580 # use the modified response class
582 # use the modified response class
581 response_class = HTTPResponse
583 response_class = HTTPResponse
582 send = safesend
584 send = safesend
583 getresponse = wrapgetresponse(httplib.HTTPConnection)
585 getresponse = wrapgetresponse(httplib.HTTPConnection)
584
586
585
587
586 #########################################################################
588 #########################################################################
587 ##### TEST FUNCTIONS
589 ##### TEST FUNCTIONS
588 #########################################################################
590 #########################################################################
589
591
590 def error_handler(url):
592 def error_handler(url):
591 global HANDLE_ERRORS
593 global HANDLE_ERRORS
592 orig = HANDLE_ERRORS
594 orig = HANDLE_ERRORS
593 keepalive_handler = HTTPHandler()
595 keepalive_handler = HTTPHandler()
594 opener = urllib2.build_opener(keepalive_handler)
596 opener = urllib2.build_opener(keepalive_handler)
595 urllib2.install_opener(opener)
597 urllib2.install_opener(opener)
596 pos = {0: 'off', 1: 'on'}
598 pos = {0: 'off', 1: 'on'}
597 for i in (0, 1):
599 for i in (0, 1):
598 print " fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)
600 print " fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)
599 HANDLE_ERRORS = i
601 HANDLE_ERRORS = i
600 try:
602 try:
601 fo = urllib2.urlopen(url)
603 fo = urllib2.urlopen(url)
602 fo.read()
604 fo.read()
603 fo.close()
605 fo.close()
604 try:
606 try:
605 status, reason = fo.status, fo.reason
607 status, reason = fo.status, fo.reason
606 except AttributeError:
608 except AttributeError:
607 status, reason = None, None
609 status, reason = None, None
608 except IOError as e:
610 except IOError as e:
609 print " EXCEPTION: %s" % e
611 print " EXCEPTION: %s" % e
610 raise
612 raise
611 else:
613 else:
612 print " status = %s, reason = %s" % (status, reason)
614 print " status = %s, reason = %s" % (status, reason)
613 HANDLE_ERRORS = orig
615 HANDLE_ERRORS = orig
614 hosts = keepalive_handler.open_connections()
616 hosts = keepalive_handler.open_connections()
615 print "open connections:", hosts
617 print "open connections:", hosts
616 keepalive_handler.close_all()
618 keepalive_handler.close_all()
617
619
618 def continuity(url):
620 def continuity(url):
619 from util import md5
621 from . import util
622 md5 = util.md5
620 format = '%25s: %s'
623 format = '%25s: %s'
621
624
622 # first fetch the file with the normal http handler
625 # first fetch the file with the normal http handler
623 opener = urllib2.build_opener()
626 opener = urllib2.build_opener()
624 urllib2.install_opener(opener)
627 urllib2.install_opener(opener)
625 fo = urllib2.urlopen(url)
628 fo = urllib2.urlopen(url)
626 foo = fo.read()
629 foo = fo.read()
627 fo.close()
630 fo.close()
628 m = md5(foo)
631 m = md5(foo)
629 print format % ('normal urllib', m.hexdigest())
632 print format % ('normal urllib', m.hexdigest())
630
633
631 # now install the keepalive handler and try again
634 # now install the keepalive handler and try again
632 opener = urllib2.build_opener(HTTPHandler())
635 opener = urllib2.build_opener(HTTPHandler())
633 urllib2.install_opener(opener)
636 urllib2.install_opener(opener)
634
637
635 fo = urllib2.urlopen(url)
638 fo = urllib2.urlopen(url)
636 foo = fo.read()
639 foo = fo.read()
637 fo.close()
640 fo.close()
638 m = md5(foo)
641 m = md5(foo)
639 print format % ('keepalive read', m.hexdigest())
642 print format % ('keepalive read', m.hexdigest())
640
643
641 fo = urllib2.urlopen(url)
644 fo = urllib2.urlopen(url)
642 foo = ''
645 foo = ''
643 while True:
646 while True:
644 f = fo.readline()
647 f = fo.readline()
645 if f:
648 if f:
646 foo = foo + f
649 foo = foo + f
647 else: break
650 else: break
648 fo.close()
651 fo.close()
649 m = md5(foo)
652 m = md5(foo)
650 print format % ('keepalive readline', m.hexdigest())
653 print format % ('keepalive readline', m.hexdigest())
651
654
652 def comp(N, url):
655 def comp(N, url):
653 print ' making %i connections to:\n %s' % (N, url)
656 print ' making %i connections to:\n %s' % (N, url)
654
657
655 sys.stdout.write(' first using the normal urllib handlers')
658 sys.stdout.write(' first using the normal urllib handlers')
656 # first use normal opener
659 # first use normal opener
657 opener = urllib2.build_opener()
660 opener = urllib2.build_opener()
658 urllib2.install_opener(opener)
661 urllib2.install_opener(opener)
659 t1 = fetch(N, url)
662 t1 = fetch(N, url)
660 print ' TIME: %.3f s' % t1
663 print ' TIME: %.3f s' % t1
661
664
662 sys.stdout.write(' now using the keepalive handler ')
665 sys.stdout.write(' now using the keepalive handler ')
663 # now install the keepalive handler and try again
666 # now install the keepalive handler and try again
664 opener = urllib2.build_opener(HTTPHandler())
667 opener = urllib2.build_opener(HTTPHandler())
665 urllib2.install_opener(opener)
668 urllib2.install_opener(opener)
666 t2 = fetch(N, url)
669 t2 = fetch(N, url)
667 print ' TIME: %.3f s' % t2
670 print ' TIME: %.3f s' % t2
668 print ' improvement factor: %.2f' % (t1 / t2)
671 print ' improvement factor: %.2f' % (t1 / t2)
669
672
670 def fetch(N, url, delay=0):
673 def fetch(N, url, delay=0):
671 import time
674 import time
672 lens = []
675 lens = []
673 starttime = time.time()
676 starttime = time.time()
674 for i in range(N):
677 for i in range(N):
675 if delay and i > 0:
678 if delay and i > 0:
676 time.sleep(delay)
679 time.sleep(delay)
677 fo = urllib2.urlopen(url)
680 fo = urllib2.urlopen(url)
678 foo = fo.read()
681 foo = fo.read()
679 fo.close()
682 fo.close()
680 lens.append(len(foo))
683 lens.append(len(foo))
681 diff = time.time() - starttime
684 diff = time.time() - starttime
682
685
683 j = 0
686 j = 0
684 for i in lens[1:]:
687 for i in lens[1:]:
685 j = j + 1
688 j = j + 1
686 if not i == lens[0]:
689 if not i == lens[0]:
687 print "WARNING: inconsistent length on read %i: %i" % (j, i)
690 print "WARNING: inconsistent length on read %i: %i" % (j, i)
688
691
689 return diff
692 return diff
690
693
691 def test_timeout(url):
694 def test_timeout(url):
692 global DEBUG
695 global DEBUG
693 dbbackup = DEBUG
696 dbbackup = DEBUG
694 class FakeLogger(object):
697 class FakeLogger(object):
695 def debug(self, msg, *args):
698 def debug(self, msg, *args):
696 print msg % args
699 print msg % args
697 info = warning = error = debug
700 info = warning = error = debug
698 DEBUG = FakeLogger()
701 DEBUG = FakeLogger()
699 print " fetching the file to establish a connection"
702 print " fetching the file to establish a connection"
700 fo = urllib2.urlopen(url)
703 fo = urllib2.urlopen(url)
701 data1 = fo.read()
704 data1 = fo.read()
702 fo.close()
705 fo.close()
703
706
704 i = 20
707 i = 20
705 print " waiting %i seconds for the server to close the connection" % i
708 print " waiting %i seconds for the server to close the connection" % i
706 while i > 0:
709 while i > 0:
707 sys.stdout.write('\r %2i' % i)
710 sys.stdout.write('\r %2i' % i)
708 sys.stdout.flush()
711 sys.stdout.flush()
709 time.sleep(1)
712 time.sleep(1)
710 i -= 1
713 i -= 1
711 sys.stderr.write('\r')
714 sys.stderr.write('\r')
712
715
713 print " fetching the file a second time"
716 print " fetching the file a second time"
714 fo = urllib2.urlopen(url)
717 fo = urllib2.urlopen(url)
715 data2 = fo.read()
718 data2 = fo.read()
716 fo.close()
719 fo.close()
717
720
718 if data1 == data2:
721 if data1 == data2:
719 print ' data are identical'
722 print ' data are identical'
720 else:
723 else:
721 print ' ERROR: DATA DIFFER'
724 print ' ERROR: DATA DIFFER'
722
725
723 DEBUG = dbbackup
726 DEBUG = dbbackup
724
727
725
728
726 def test(url, N=10):
729 def test(url, N=10):
727 print "checking error handler (do this on a non-200)"
730 print "checking error handler (do this on a non-200)"
728 try: error_handler(url)
731 try: error_handler(url)
729 except IOError:
732 except IOError:
730 print "exiting - exception will prevent further tests"
733 print "exiting - exception will prevent further tests"
731 sys.exit()
734 sys.exit()
732 print
735 print
733 print "performing continuity test (making sure stuff isn't corrupted)"
736 print "performing continuity test (making sure stuff isn't corrupted)"
734 continuity(url)
737 continuity(url)
735 print
738 print
736 print "performing speed comparison"
739 print "performing speed comparison"
737 comp(N, url)
740 comp(N, url)
738 print
741 print
739 print "performing dropped-connection check"
742 print "performing dropped-connection check"
740 test_timeout(url)
743 test_timeout(url)
741
744
742 if __name__ == '__main__':
745 if __name__ == '__main__':
743 import time
746 import time
744 import sys
745 try:
747 try:
746 N = int(sys.argv[1])
748 N = int(sys.argv[1])
747 url = sys.argv[2]
749 url = sys.argv[2]
748 except (IndexError, ValueError):
750 except (IndexError, ValueError):
749 print "%s <integer> <url>" % sys.argv[0]
751 print "%s <integer> <url>" % sys.argv[0]
750 else:
752 else:
751 test(url, N)
753 test(url, N)
@@ -1,193 +1,192 b''
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/dispatch.py requires print_function
103 mercurial/dispatch.py requires print_function
104 mercurial/exchange.py not using absolute_import
104 mercurial/exchange.py not using absolute_import
105 mercurial/httpclient/__init__.py not using absolute_import
105 mercurial/httpclient/__init__.py not using absolute_import
106 mercurial/httpclient/_readers.py not using absolute_import
106 mercurial/httpclient/_readers.py not using absolute_import
107 mercurial/httpclient/socketutil.py not using absolute_import
107 mercurial/httpclient/socketutil.py not using absolute_import
108 mercurial/httpconnection.py not using absolute_import
108 mercurial/httpconnection.py not using absolute_import
109 mercurial/keepalive.py not using absolute_import
110 mercurial/keepalive.py requires print_function
109 mercurial/keepalive.py requires print_function
111 mercurial/localrepo.py not using absolute_import
110 mercurial/localrepo.py not using absolute_import
112 mercurial/lsprof.py requires print_function
111 mercurial/lsprof.py requires print_function
113 mercurial/lsprofcalltree.py requires print_function
112 mercurial/lsprofcalltree.py requires print_function
114 mercurial/mail.py requires print_function
113 mercurial/mail.py requires print_function
115 setup.py not using absolute_import
114 setup.py not using absolute_import
116 tests/filterpyflakes.py requires print_function
115 tests/filterpyflakes.py requires print_function
117 tests/generate-working-copy-states.py requires print_function
116 tests/generate-working-copy-states.py requires print_function
118 tests/get-with-headers.py requires print_function
117 tests/get-with-headers.py requires print_function
119 tests/heredoctest.py requires print_function
118 tests/heredoctest.py requires print_function
120 tests/hypothesishelpers.py not using absolute_import
119 tests/hypothesishelpers.py not using absolute_import
121 tests/hypothesishelpers.py requires print_function
120 tests/hypothesishelpers.py requires print_function
122 tests/killdaemons.py not using absolute_import
121 tests/killdaemons.py not using absolute_import
123 tests/md5sum.py not using absolute_import
122 tests/md5sum.py not using absolute_import
124 tests/mockblackbox.py not using absolute_import
123 tests/mockblackbox.py not using absolute_import
125 tests/printenv.py not using absolute_import
124 tests/printenv.py not using absolute_import
126 tests/readlink.py not using absolute_import
125 tests/readlink.py not using absolute_import
127 tests/readlink.py requires print_function
126 tests/readlink.py requires print_function
128 tests/revlog-formatv0.py not using absolute_import
127 tests/revlog-formatv0.py not using absolute_import
129 tests/run-tests.py not using absolute_import
128 tests/run-tests.py not using absolute_import
130 tests/seq.py not using absolute_import
129 tests/seq.py not using absolute_import
131 tests/seq.py requires print_function
130 tests/seq.py requires print_function
132 tests/silenttestrunner.py not using absolute_import
131 tests/silenttestrunner.py not using absolute_import
133 tests/silenttestrunner.py requires print_function
132 tests/silenttestrunner.py requires print_function
134 tests/sitecustomize.py not using absolute_import
133 tests/sitecustomize.py not using absolute_import
135 tests/svn-safe-append.py not using absolute_import
134 tests/svn-safe-append.py not using absolute_import
136 tests/svnxml.py not using absolute_import
135 tests/svnxml.py not using absolute_import
137 tests/test-ancestor.py requires print_function
136 tests/test-ancestor.py requires print_function
138 tests/test-atomictempfile.py not using absolute_import
137 tests/test-atomictempfile.py not using absolute_import
139 tests/test-batching.py not using absolute_import
138 tests/test-batching.py not using absolute_import
140 tests/test-batching.py requires print_function
139 tests/test-batching.py requires print_function
141 tests/test-bdiff.py not using absolute_import
140 tests/test-bdiff.py not using absolute_import
142 tests/test-bdiff.py requires print_function
141 tests/test-bdiff.py requires print_function
143 tests/test-context.py not using absolute_import
142 tests/test-context.py not using absolute_import
144 tests/test-context.py requires print_function
143 tests/test-context.py requires print_function
145 tests/test-demandimport.py not using absolute_import
144 tests/test-demandimport.py not using absolute_import
146 tests/test-demandimport.py requires print_function
145 tests/test-demandimport.py requires print_function
147 tests/test-dispatch.py not using absolute_import
146 tests/test-dispatch.py not using absolute_import
148 tests/test-dispatch.py requires print_function
147 tests/test-dispatch.py requires print_function
149 tests/test-doctest.py not using absolute_import
148 tests/test-doctest.py not using absolute_import
150 tests/test-duplicateoptions.py not using absolute_import
149 tests/test-duplicateoptions.py not using absolute_import
151 tests/test-duplicateoptions.py requires print_function
150 tests/test-duplicateoptions.py requires print_function
152 tests/test-filecache.py not using absolute_import
151 tests/test-filecache.py not using absolute_import
153 tests/test-filecache.py requires print_function
152 tests/test-filecache.py requires print_function
154 tests/test-filelog.py not using absolute_import
153 tests/test-filelog.py not using absolute_import
155 tests/test-filelog.py requires print_function
154 tests/test-filelog.py requires print_function
156 tests/test-hg-parseurl.py not using absolute_import
155 tests/test-hg-parseurl.py not using absolute_import
157 tests/test-hg-parseurl.py requires print_function
156 tests/test-hg-parseurl.py requires print_function
158 tests/test-hgweb-auth.py not using absolute_import
157 tests/test-hgweb-auth.py not using absolute_import
159 tests/test-hgweb-auth.py requires print_function
158 tests/test-hgweb-auth.py requires print_function
160 tests/test-hgwebdir-paths.py not using absolute_import
159 tests/test-hgwebdir-paths.py not using absolute_import
161 tests/test-hybridencode.py not using absolute_import
160 tests/test-hybridencode.py not using absolute_import
162 tests/test-hybridencode.py requires print_function
161 tests/test-hybridencode.py requires print_function
163 tests/test-lrucachedict.py not using absolute_import
162 tests/test-lrucachedict.py not using absolute_import
164 tests/test-lrucachedict.py requires print_function
163 tests/test-lrucachedict.py requires print_function
165 tests/test-manifest.py not using absolute_import
164 tests/test-manifest.py not using absolute_import
166 tests/test-minirst.py not using absolute_import
165 tests/test-minirst.py not using absolute_import
167 tests/test-minirst.py requires print_function
166 tests/test-minirst.py requires print_function
168 tests/test-parseindex2.py not using absolute_import
167 tests/test-parseindex2.py not using absolute_import
169 tests/test-parseindex2.py requires print_function
168 tests/test-parseindex2.py requires print_function
170 tests/test-pathencode.py not using absolute_import
169 tests/test-pathencode.py not using absolute_import
171 tests/test-pathencode.py requires print_function
170 tests/test-pathencode.py requires print_function
172 tests/test-propertycache.py not using absolute_import
171 tests/test-propertycache.py not using absolute_import
173 tests/test-propertycache.py requires print_function
172 tests/test-propertycache.py requires print_function
174 tests/test-revlog-ancestry.py not using absolute_import
173 tests/test-revlog-ancestry.py not using absolute_import
175 tests/test-revlog-ancestry.py requires print_function
174 tests/test-revlog-ancestry.py requires print_function
176 tests/test-run-tests.py not using absolute_import
175 tests/test-run-tests.py not using absolute_import
177 tests/test-simplemerge.py not using absolute_import
176 tests/test-simplemerge.py not using absolute_import
178 tests/test-status-inprocess.py not using absolute_import
177 tests/test-status-inprocess.py not using absolute_import
179 tests/test-status-inprocess.py requires print_function
178 tests/test-status-inprocess.py requires print_function
180 tests/test-symlink-os-yes-fs-no.py not using absolute_import
179 tests/test-symlink-os-yes-fs-no.py not using absolute_import
181 tests/test-trusted.py not using absolute_import
180 tests/test-trusted.py not using absolute_import
182 tests/test-trusted.py requires print_function
181 tests/test-trusted.py requires print_function
183 tests/test-ui-color.py not using absolute_import
182 tests/test-ui-color.py not using absolute_import
184 tests/test-ui-color.py requires print_function
183 tests/test-ui-color.py requires print_function
185 tests/test-ui-config.py not using absolute_import
184 tests/test-ui-config.py not using absolute_import
186 tests/test-ui-config.py requires print_function
185 tests/test-ui-config.py requires print_function
187 tests/test-ui-verbosity.py not using absolute_import
186 tests/test-ui-verbosity.py not using absolute_import
188 tests/test-ui-verbosity.py requires print_function
187 tests/test-ui-verbosity.py requires print_function
189 tests/test-url.py not using absolute_import
188 tests/test-url.py not using absolute_import
190 tests/test-url.py requires print_function
189 tests/test-url.py requires print_function
191 tests/test-walkrepo.py requires print_function
190 tests/test-walkrepo.py requires print_function
192 tests/test-wireproto.py requires print_function
191 tests/test-wireproto.py requires print_function
193 tests/tinyproxy.py requires print_function
192 tests/tinyproxy.py requires print_function
General Comments 0
You need to be logged in to leave comments. Login now