| 
					
				 | 
			
			
				@@ -38,8 +38,7 @@ logger = create_logger(__name__) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 RPC_PROTOCOL_VERSION = 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 BORG_VERSION = parse_version(__version__) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-MSGID, MSG, ARGS, RESULT = 'i', 'm', 'a', 'r'  # pack 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-MSGIDB, MSGB, ARGSB, RESULTB = b'i', b'm', b'a', b'r'  # unpack 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+MSGID, MSG, ARGS, RESULT = 'i', 'm', 'a', 'r' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 MAX_INFLIGHT = 100 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -139,10 +138,6 @@ compatMap = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-def decode_keys(d): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return {k.decode(): d[k] for k in d} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class RepositoryServer:  # pragma: no cover 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     rpc_methods = ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         '__len__', 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -217,14 +212,13 @@ class RepositoryServer:  # pragma: no cover 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 for unpacked in unpacker: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     if isinstance(unpacked, dict): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         dictFormat = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        msgid = unpacked[MSGIDB] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        method = unpacked[MSGB].decode() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        args = decode_keys(unpacked[ARGSB]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        msgid = unpacked[MSGID] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        method = unpacked[MSG] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        args = unpacked[ARGS] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     elif isinstance(unpacked, tuple) and len(unpacked) == 4: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         dictFormat = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         # The first field 'type' was always 1 and has always been ignored 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         _, msgid, method, args = unpacked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        method = method.decode() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         args = self.positional_to_named(method, args) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         if self.repository is not None: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -308,7 +302,7 @@ class RepositoryServer:  # pragma: no cover 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         # clients since 1.1.0b3 use a dict as client_data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         # clients since 1.1.0b6 support json log format from server 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if isinstance(client_data, dict): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self.client_version = client_data[b'client_version'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.client_version = client_data['client_version'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             level = logging.getLevelName(logging.getLogger('').level) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             setup_logging(is_serve=True, json=True, level=level) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             logger.debug('Initialized logging system for JSON-based protocol') 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -370,7 +364,6 @@ class RepositoryServer:  # pragma: no cover 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return self.repository.id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def inject_exception(self, kind): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        kind = kind.decode() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         s1 = 'test string' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         s2 = 'test string2' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if kind == 'DoesNotExist': 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -484,35 +477,35 @@ class RemoteRepository: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     class RPCError(Exception): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         def __init__(self, unpacked): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            # for borg < 1.1: unpacked only has b'exception_class' as key 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            # for borg 1.1+: unpacked has keys: b'exception_args', b'exception_full', b'exception_short', b'sysinfo' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            # for borg < 1.1: unpacked only has 'exception_class' as key 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            # for borg 1.1+: unpacked has keys: 'exception_args', 'exception_full', 'exception_short', 'sysinfo' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self.unpacked = unpacked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         def get_message(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if b'exception_short' in self.unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return b'\n'.join(self.unpacked[b'exception_short']).decode() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if 'exception_short' in self.unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return '\n'.join(self.unpacked['exception_short']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return self.exception_class 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         def traceback(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return self.unpacked.get(b'exception_trace', True) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return self.unpacked.get('exception_trace', True) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         def exception_class(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return self.unpacked[b'exception_class'].decode() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return self.unpacked['exception_class'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         def exception_full(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if b'exception_full' in self.unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return b'\n'.join(self.unpacked[b'exception_full']).decode() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if 'exception_full' in self.unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return '\n'.join(self.unpacked['exception_full']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return self.get_message() + '\nRemote Exception (see remote log for the traceback)' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         def sysinfo(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if b'sysinfo' in self.unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return self.unpacked[b'sysinfo'].decode() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if 'sysinfo' in self.unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return self.unpacked['sysinfo'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return '' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -577,9 +570,9 @@ class RemoteRepository: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 raise ConnectionClosedWithHint('Is borg working on the server?') from None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if version == RPC_PROTOCOL_VERSION: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 self.dictFormat = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            elif isinstance(version, dict) and b'server_version' in version: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif isinstance(version, dict) and 'server_version' in version: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 self.dictFormat = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                self.server_version = version[b'server_version'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                self.server_version = version['server_version'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 raise Exception('Server insisted on using unsupported protocol version %s' % version) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -734,9 +727,9 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return msgid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         def handle_error(unpacked): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            error = unpacked[b'exception_class'].decode() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            old_server = b'exception_args' not in unpacked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            args = unpacked.get(b'exception_args') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            error = unpacked['exception_class'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            old_server = 'exception_args' not in unpacked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            args = unpacked.get('exception_args') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if error == 'DoesNotExist': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 raise Repository.DoesNotExist(self.location.processed) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -748,29 +741,29 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if old_server: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     raise IntegrityError('(not available)') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    raise IntegrityError(args[0].decode()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    raise IntegrityError(args[0]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             elif error == 'AtticRepository': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if old_server: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     raise Repository.AtticRepository('(not available)') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    raise Repository.AtticRepository(args[0].decode()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    raise Repository.AtticRepository(args[0]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             elif error == 'PathNotAllowed': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if old_server: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     raise PathNotAllowed('(unknown)') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    raise PathNotAllowed(args[0].decode()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    raise PathNotAllowed(args[0]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             elif error == 'ParentPathDoesNotExist': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                raise Repository.ParentPathDoesNotExist(args[0].decode()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                raise Repository.ParentPathDoesNotExist(args[0]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             elif error == 'ObjectNotFound': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if old_server: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     raise Repository.ObjectNotFound('(not available)', self.location.processed) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    raise Repository.ObjectNotFound(args[0].decode(), self.location.processed) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    raise Repository.ObjectNotFound(args[0], self.location.processed) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             elif error == 'InvalidRPCMethod': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if old_server: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     raise InvalidRPCMethod('(not available)') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    raise InvalidRPCMethod(args[0].decode()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    raise InvalidRPCMethod(args[0]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 raise self.RPCError(unpacked) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -789,10 +782,10 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     unpacked = self.responses.pop(waiting_for[0]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     waiting_for.pop(0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if b'exception_class' in unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if 'exception_class' in unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         handle_error(unpacked) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        yield unpacked[RESULTB] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        yield unpacked[RESULT] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         if not waiting_for and not calls: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 except KeyError: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -809,10 +802,10 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        if b'exception_class' in unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if 'exception_class' in unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             handle_error(unpacked) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            yield unpacked[RESULTB] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            yield unpacked[RESULT] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if self.to_send or ((calls or self.preload_ids) and len(waiting_for) < MAX_INFLIGHT): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 w_fds = [self.stdin_fd] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             else: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -829,26 +822,26 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     self.unpacker.feed(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     for unpacked in self.unpacker: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         if isinstance(unpacked, dict): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            msgid = unpacked[MSGIDB] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            msgid = unpacked[MSGID] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         elif isinstance(unpacked, tuple) and len(unpacked) == 4: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             # The first field 'type' was always 1 and has always been ignored 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             _, msgid, error, res = unpacked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             if error: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 # ignore res, because it is only a fixed string anyway. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                unpacked = {MSGIDB: msgid, b'exception_class': error} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                unpacked = {MSGID: msgid, 'exception_class': error} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                unpacked = {MSGIDB: msgid, RESULTB: res} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                unpacked = {MSGID: msgid, RESULT: res} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             raise UnexpectedRPCDataFormatFromServer(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         if msgid in self.ignore_responses: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             self.ignore_responses.remove(msgid) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             # async methods never return values, but may raise exceptions. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            if b'exception_class' in unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            if 'exception_class' in unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 self.async_responses[msgid] = unpacked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 # we currently do not have async result values except "None", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                 # so we do not add them into async_responses. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                if unpacked[RESULTB] is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                if unpacked[RESULT] is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                     self.async_responses[msgid] = unpacked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             self.responses[msgid] = unpacked 
			 |