from __future__ import absolute_import
from binascii import hexlify, unhexlify
-import errno, os, re, struct, sys, time, zlib
+import os, re, struct, time, zlib
+import socket
from bup import git, ssh, vfs
-from bup.compat import environ, range, reraise
+from bup.compat import environ, pending_raise, range, reraise
from bup.helpers import (Conn, atomically_replaced_file, chunkyreader, debug1,
debug2, linereader, lines_until_sentinel,
- mkdirp, progress, qprogress, DemuxConn)
+ mkdirp, nullcontext_if_not, progress, qprogress, DemuxConn)
from bup.io import path_msg
-from bup.vint import read_bvec, read_vuint, write_bvec
+from bup.vint import write_bvec
bwlimit = None
class Client:
def __init__(self, remote, create=False):
+ self.closed = False
self._busy = self.conn = None
self.sock = self.p = self.pout = self.pin = None
is_reverse = environ.get(b'BUP_SERVER_REVERSE')
reraise(ClientError('connect: %s' % e))
elif self.protocol == b'bup':
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.sock.connect((self.host, atoi(self.port) or 1982))
+ self.sock.connect((self.host,
+ 1982 if self.port is None else int(self.port)))
self.sockw = self.sock.makefile('wb')
self.conn = DemuxConn(self.sock.fileno(), self.sockw)
self._available_commands = self._get_available_commands()
self.check_ok()
self.sync_indexes()
- def __del__(self):
- try:
- self.close()
- except IOError as e:
- if e.errno == errno.EPIPE:
- pass
- else:
- raise
-
def close(self):
+ self.closed = True
if self.conn and not self._busy:
self.conn.write(b'quit\n')
if self.pin:
self.conn = None
self.sock = self.p = self.pin = self.pout = None
+ def __del__(self):
+ assert self.closed
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ with pending_raise(value, rethrow=False):
+ self.close()
+
def check_ok(self):
if self.p:
rv = self.p.poll()
return self.conn.check_ok()
except Exception as e:
reraise(ClientError(e))
+ # reraise doesn't return
+ return None
def check_busy(self):
if self._busy:
raise ClientError('already busy with command %r' % self._busy)
-
+
def ensure_busy(self):
if not self._busy:
raise ClientError('expected to be busy, but not busy?!')
-
+
def _not_busy(self):
self._busy = None
raise not_ok
self._not_busy()
- def rev_list(self, refs, count=None, parse=None, format=None):
+ def rev_list(self, refs, parse=None, format=None):
"""See git.rev_list for the general semantics, but note that with the
current interface, the parse function must be able to handle
(consume) any blank lines produced by the format because the
"""
self._require_command(b'rev-list')
- assert (count is None) or (isinstance(count, Integral))
if format:
assert b'\n' not in format
assert parse
self._busy = b'rev-list'
conn = self.conn
conn.write(b'rev-list\n')
- if count is not None:
- conn.write(b'%d' % count)
conn.write(b'\n')
if format:
conn.write(format)
return result
+# FIXME: disentangle this (stop inheriting) from PackWriter
class PackWriter_Remote(git.PackWriter):
+
def __init__(self, conn, objcache_maker, suggest_packs,
onopen, onclose,
ensure_busy,
compression_level=compression_level,
max_pack_size=max_pack_size,
max_pack_objects=max_pack_objects)
+ self.remote_closed = False
self.file = conn
self.filename = b'remote socket'
self.suggest_packs = suggest_packs
self._bwcount = 0
self._bwtime = time.time()
+ # __enter__ and __exit__ are inherited
+
def _open(self):
if not self._packopen:
self.onopen()
self._packopen = True
def _end(self, run_midx=True):
+ # Called by other PackWriter methods like breakpoint().
+ # Must not close the connection (self.file)
assert(run_midx) # We don't support this via remote yet
- if self._packopen and self.file:
+ self.objcache, objcache = None, self.objcache
+ with nullcontext_if_not(objcache):
+ if not (self._packopen and self.file):
+ return None
self.file.write(b'\0\0\0\0')
self._packopen = False
self.onclose() # Unbusy
- self.objcache = None
+ if objcache is not None:
+ objcache.close()
return self.suggest_packs() # Returns last idx received
def close(self):
+ # Called by inherited __exit__
+ self.remote_closed = True
id = self._end()
self.file = None
return id
+ def __del__(self):
+ assert self.remote_closed
+ super(PackWriter_Remote, self).__del__()
+
def abort(self):
raise ClientError("don't know how to abort remote pack writing")