Mercurial > hg
changeset 14378:1dab7afff11f
merge crew with main
author | Martin Geisler <mg@aragost.com> |
---|---|
date | Thu, 19 May 2011 16:57:14 +0200 |
parents | f90d5641c78b (current diff) a75e0f4ba0ab (diff) |
children | 10546bb7d201 |
files | |
diffstat | 12 files changed, 142 insertions(+), 65 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/bash_completion Wed May 18 23:20:26 2011 -0700 +++ b/contrib/bash_completion Thu May 19 16:57:14 2011 +0200 @@ -62,7 +62,7 @@ _hg_commands() { local commands - commands="$(_hg_cmd debugcomplete "$cur")" || commands="" + commands="$(HGPLAINEXCEPT=alias _hg_cmd debugcomplete "$cur")" || commands="" COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$commands' -- "$cur")) }
--- a/mercurial/commands.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/commands.py Thu May 19 16:57:14 2011 +0200 @@ -1865,7 +1865,9 @@ ts = 0 heads = set() for rev in xrange(numrevs): - dbase = r.base(rev) + dbase = r.deltaparent(rev) + if dbase == -1: + dbase = rev cbase = r.chainbase(rev) p1, p2 = r.parentrevs(rev) rs = r.rawsize(rev)
--- a/mercurial/httpclient/__init__.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/httpclient/__init__.py Thu May 19 16:57:14 2011 +0200 @@ -112,6 +112,10 @@ def complete(self): """Returns true if this response is completely loaded. + + Note that if this is a connection where complete means the + socket is closed, this will nearly always return False, even + in cases where all the data has actually been loaded. """ if self._chunked: return self._chunked_done @@ -174,10 +178,7 @@ return True logger.debug('response read %d data during _select', len(data)) if not data: - if not self.headers: - self._load_response(self._end_headers) - self._content_len = 0 - elif self._content_len == _LEN_CLOSE_IS_END: + if self.headers and self._content_len == _LEN_CLOSE_IS_END: self._content_len = len(self._body) return False else: @@ -561,17 +562,46 @@ continue if not data: logger.info('socket appears closed in read') - outgoing_headers = body = None - break + self.sock = None + self._current_response = None + # This if/elif ladder is a bit subtle, + # comments in each branch should help. + if response is not None and ( + response.complete() or + response._content_len == _LEN_CLOSE_IS_END): + # Server responded completely and then + # closed the socket. We should just shut + # things down and let the caller get their + # response. + logger.info('Got an early response, ' + 'aborting remaining request.') + break + elif was_first and response is None: + # Most likely a keepalive that got killed + # on the server's end. Commonly happens + # after getting a really large response + # from the server. + logger.info( + 'Connection appeared closed in read on first' + ' request loop iteration, will retry.') + reconnect('read') + continue + else: + # We didn't just send the first data hunk, + # and either have a partial response or no + # response at all. There's really nothing + # meaningful we can do here. + raise HTTPStateError( + 'Connection appears closed after ' + 'some request data was written, but the ' + 'response was missing or incomplete!') + logger.debug('read %d bytes in request()', len(data)) if response is None: response = self.response_class(r[0], self.timeout) response._load_response(data) - if (response._content_len == _LEN_CLOSE_IS_END - and len(data) == 0): - response._content_len = len(response._body) - if response.complete(): - w = [] - response.will_close = True + # Jump to the next select() call so we load more + # data if the server is still sending us content. + continue except socket.error, e: if e[0] != errno.EPIPE and not was_first: raise @@ -662,4 +692,7 @@ class HTTPProxyConnectFailedException(httplib.HTTPException): """Connecting to the HTTP proxy failed.""" + +class HTTPStateError(httplib.HTTPException): + """Invalid internal state encountered.""" # no-check-code
--- a/mercurial/httpclient/tests/simple_http_test.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/httpclient/tests/simple_http_test.py Thu May 19 16:57:14 2011 +0200 @@ -26,6 +26,7 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import socket import unittest import http @@ -39,7 +40,7 @@ def _run_simple_test(self, host, server_data, expected_req, expected_data): con = http.HTTPConnection(host) con._connect() - con.sock.data.extend(server_data) + con.sock.data = server_data con.request('GET', '/') self.assertStringEqual(expected_req, con.sock.sent) @@ -353,4 +354,33 @@ self.assertEqual(('1.2.3.4', 80), con.sock.sa) self.assertEqual(expected_req, con.sock.sent) + + def test_conn_keep_alive_but_server_close_anyway(self): + sockets = [] + def closingsocket(*args, **kwargs): + s = util.MockSocket(*args, **kwargs) + sockets.append(s) + s.data = ['HTTP/1.1 200 OK\r\n', + 'Server: BogusServer 1.0\r\n', + 'Connection: Keep-Alive\r\n', + 'Content-Length: 16', + '\r\n\r\n', + 'You can do that.'] + s.close_on_empty = True + return s + + socket.socket = closingsocket + con = http.HTTPConnection('1.2.3.4:80') + con._connect() + con.request('GET', '/') + r1 = con.getresponse() + r1.read() + self.assertFalse(con.sock.closed) + self.assert_(con.sock.remote_closed) + con.request('GET', '/') + self.assertEqual(2, len(sockets)) + + def test_no_response_raises_response_not_ready(self): + con = http.HTTPConnection('foo') + self.assertRaises(http.httplib.ResponseNotReady, con.getresponse) # no-check-code
--- a/mercurial/httpclient/tests/test_chunked_transfer.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/httpclient/tests/test_chunked_transfer.py Thu May 19 16:57:14 2011 +0200 @@ -53,7 +53,7 @@ con = http.HTTPConnection('1.2.3.4:80') con._connect() sock = con.sock - sock.read_wait_sentinel = 'end-of-body' + sock.read_wait_sentinel = '0\r\n\r\n' sock.data = ['HTTP/1.1 200 OK\r\n', 'Server: BogusServer 1.0\r\n', 'Content-Length: 6',
--- a/mercurial/httpclient/tests/test_proxy_support.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/httpclient/tests/test_proxy_support.py Thu May 19 16:57:14 2011 +0200 @@ -43,7 +43,7 @@ """ def s(*args, **kwargs): sock = util.MockSocket(*args, **kwargs) - sock.data = data[:] + sock.early_data = data[:] return sock return s @@ -97,24 +97,27 @@ '\r\n' '1234567890']) con._connect() - con.sock.data.extend(['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 10\r\n', - '\r\n' - '1234567890' - ]) + con.sock.data = ['HTTP/1.1 200 OK\r\n', + 'Server: BogusServer 1.0\r\n', + 'Content-Length: 10\r\n', + '\r\n' + '1234567890' + ] + connect_sent = con.sock.sent + con.sock.sent = '' con.request('GET', '/') - expected_req = ('CONNECT 1.2.3.4:443 HTTP/1.0\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n' - '\r\n' - 'GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') + expected_connect = ('CONNECT 1.2.3.4:443 HTTP/1.0\r\n' + 'Host: 1.2.3.4\r\n' + 'accept-encoding: identity\r\n' + '\r\n') + expected_request = ('GET / HTTP/1.1\r\n' + 'Host: 1.2.3.4\r\n' + 'accept-encoding: identity\r\n\r\n') self.assertEqual(('127.0.0.42', 4242), con.sock.sa) - self.assertStringEqual(expected_req, con.sock.sent) + self.assertStringEqual(expected_connect, connect_sent) + self.assertStringEqual(expected_request, con.sock.sent) resp = con.getresponse() self.assertEqual(resp.status, 200) self.assertEqual('1234567890', resp.read())
--- a/mercurial/httpclient/tests/test_ssl.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/httpclient/tests/test_ssl.py Thu May 19 16:57:14 2011 +0200 @@ -41,15 +41,15 @@ con._connect() # extend the list instead of assign because of how # MockSSLSocket works. - con.sock.data.extend(['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'MultiHeader: Value\r\n' - 'MultiHeader: Other Value\r\n' - 'MultiHeader: One More!\r\n' - 'Content-Length: 10\r\n', - '\r\n' - '1234567890' - ]) + con.sock.data = ['HTTP/1.1 200 OK\r\n', + 'Server: BogusServer 1.0\r\n', + 'MultiHeader: Value\r\n' + 'MultiHeader: Other Value\r\n' + 'MultiHeader: One More!\r\n' + 'Content-Length: 10\r\n', + '\r\n' + '1234567890' + ] con.request('GET', '/') expected_req = ('GET / HTTP/1.1\r\n' @@ -68,17 +68,15 @@ def testSslRereadInEarlyResponse(self): con = http.HTTPConnection('1.2.3.4:443') con._connect() - # extend the list instead of assign because of how - # MockSSLSocket works. - con.sock.early_data.extend(['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'MultiHeader: Value\r\n' - 'MultiHeader: Other Value\r\n' - 'MultiHeader: One More!\r\n' - 'Content-Length: 10\r\n', - '\r\n' - '1234567890' - ]) + con.sock.early_data = ['HTTP/1.1 200 OK\r\n', + 'Server: BogusServer 1.0\r\n', + 'MultiHeader: Value\r\n' + 'MultiHeader: Other Value\r\n' + 'MultiHeader: One More!\r\n' + 'Content-Length: 10\r\n', + '\r\n' + '1234567890' + ] expected_req = self.doPost(con, False) self.assertEqual(None, con.sock, @@ -92,3 +90,4 @@ resp.headers.getheaders('multiheader')) self.assertEqual(['BogusServer 1.0'], resp.headers.getheaders('server')) +# no-check-code
--- a/mercurial/httpclient/tests/util.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/httpclient/tests/util.py Thu May 19 16:57:14 2011 +0200 @@ -88,7 +88,7 @@ def ready_for_read(self): return ((self.early_data and http._END_HEADERS in self.sent) or (self.read_wait_sentinel in self.sent and self.data) - or self.closed) + or self.closed or self.remote_closed) def send(self, data): # this is a horrible mock, but nothing needs us to raise the @@ -117,6 +117,11 @@ def __getattr__(self, key): return getattr(self._sock, key) + def __setattr__(self, key, value): + if key not in ('_sock', '_fail_recv'): + return setattr(self._sock, key, value) + return object.__setattr__(self, key, value) + def recv(self, amt=-1): try: if self._fail_recv:
--- a/mercurial/httpconnection.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/httpconnection.py Thu May 19 16:57:14 2011 +0200 @@ -107,6 +107,7 @@ _configuredlogging = False +LOGFMT = '%(levelname)s:%(name)s:%(lineno)d:%(message)s' # Subclass BOTH of these because otherwise urllib2 "helpfully" # reinserts them since it notices we don't include any subclasses of # them. @@ -122,7 +123,9 @@ _configuredlogging = True logger = logging.getLogger('mercurial.httpclient') logger.setLevel(getattr(logging, loglevel.upper())) - logger.addHandler(logging.StreamHandler()) + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter(LOGFMT)) + logger.addHandler(handler) def close_all(self): """Close and remove all connection objects being kept for reuse."""
--- a/mercurial/revlog.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/revlog.py Thu May 19 16:57:14 2011 +0200 @@ -320,8 +320,6 @@ return self.start(rev) + self.length(rev) def length(self, rev): return self.index[rev][1] - def base(self, rev): - return self.index[rev][3] def chainbase(self, rev): index = self.index base = index[rev][3]
--- a/mercurial/ui.py Wed May 18 23:20:26 2011 -0700 +++ b/mercurial/ui.py Thu May 19 16:57:14 2011 +0200 @@ -82,10 +82,12 @@ 'traceback', 'verbose'): if k in cfg['ui']: del cfg['ui'][k] + for k, v in cfg.items('defaults'): + del cfg['defaults'][k] + # Don't remove aliases from the configuration if in the exceptionlist + if self.plain('alias'): for k, v in cfg.items('alias'): del cfg['alias'][k] - for k, v in cfg.items('defaults'): - del cfg['defaults'][k] if trusted: self._tcfg.update(cfg) @@ -330,7 +332,7 @@ for name, value in self.configitems(section, untrusted): yield section, name, value - def plain(self): + def plain(self, feature=None): '''is plain mode active? Plain mode means that all configuration variables which affect @@ -341,14 +343,16 @@ The only way to trigger plain mode is by setting either the `HGPLAIN' or `HGPLAINEXCEPT' environment variables. - The return value can either be False, True, or a list of - features that plain mode should not apply to (e.g., i18n, - progress, etc). + The return value can either be + - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT + - True otherwise ''' if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ: return False exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',') - return exceptions or True + if feature and exceptions: + return feature not in exceptions + return True def username(self): """Return default username to be used in commits.
--- a/tests/test-hgrc.t Wed May 18 23:20:26 2011 -0700 +++ b/tests/test-hgrc.t Thu May 19 16:57:14 2011 +0200 @@ -144,7 +144,7 @@ $ echo "plain=./plain.py" >> $HGRCPATH $ HGPLAINEXCEPT=; export HGPLAINEXCEPT $ hg showconfig --config ui.traceback=True --debug - plain: [''] + plain: True read config from: $TESTTMP/hgrc $TESTTMP/hgrc:15: extensions.plain=./plain.py none: ui.traceback=True @@ -153,7 +153,7 @@ none: ui.quiet=False $ unset HGPLAIN $ hg showconfig --config ui.traceback=True --debug - plain: [''] + plain: True read config from: $TESTTMP/hgrc $TESTTMP/hgrc:15: extensions.plain=./plain.py none: ui.traceback=True @@ -162,7 +162,7 @@ none: ui.quiet=False $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT $ hg showconfig --config ui.traceback=True --debug - plain: ['i18n'] + plain: True read config from: $TESTTMP/hgrc $TESTTMP/hgrc:15: extensions.plain=./plain.py none: ui.traceback=True