def update_thread(manager, connection, stop): while True: # Read the size and position of the update message data = b'' while True: try: data += connection.partial_receive(1) size = Decoder.decode_message_size(data) break except IndexError: pass except: # pylint: disable=bare-except # TODO: is there a better way to catch exceptions when the # thread is forcibly stopped (e.g. by CTRL+c)? return if stop.is_set(): connection.close() return # Read and decode the update message data = connection.receive(size) update = Decoder.decode_message(data, KRPC.StreamUpdate) # Add the data to the cache manager.update(update.results)
def decode_default_value(value, typ): value = base64.b64decode(value) value = array.array('B', value).tostring() # Note: following is a workaround for decoding EnumerationType, # as set_values has not been called if not isinstance(typ, EnumerationType): return Decoder.decode(value, typ) return Decoder.decode(value, Types().sint32_type)
def decode_default_value(self, value, typ): value = base64.b64decode(value) # Note: following is a workaround for decoding EnumerationType, # as set_values has not been called value = array.array('B', value).tostring() if not isinstance(typ, EnumerationType): return Decoder.decode(value, typ) return Decoder.decode(value, self.types.sint32_type)
def receive_message(self, typ): """ Receive a protobuf message and decode it """ # Read the size and position of the response message data = b'' while True: try: data += self.partial_receive(1) size = Decoder.decode_message_size(data) break except IndexError: pass # Read and decode the response message data = self.receive(size) return Decoder.decode_message(data, typ)
def _invoke(self, service, procedure, args, param_names, param_types, return_type): """ Execute an RPC """ # Build the request call = self._build_call(service, procedure, args, param_names, param_types, return_type) request = KRPC.Request() request.calls.extend([call]) # Send the request with self._rpc_connection_lock: self._rpc_connection.send_message(request) response = self._rpc_connection.receive_message(KRPC.Response) # Check for an error response if response.HasField('error'): raise self._build_error(response.error) # Check for an error in the procedure results if response.results[0].HasField('error'): raise self._build_error(response.results[0].error) # Decode the response and return the (optional) result result = None if return_type is not None: result = Decoder.decode(response.results[0].value, return_type) if isinstance(result, KRPC.Event): result = Event(self, result) return result
def _run_test_decode_value(self, typ, cases): for decoded, encoded in cases: value = Decoder.decode(unhexlify(encoded), self.types.as_type(typ)) if typ in ('float','double'): self.assertEqual(str(decoded)[0:8], str(value)[0:8]) else: self.assertEqual(decoded, value)
def _run_test_decode_value(self, typ, cases): for decoded, encoded in cases: value = Decoder.decode(unhexlify(encoded), self.types.as_type(typ)) if typ in ('float', 'double'): self.assertEqual(str(decoded)[0:8], str(value)[0:8]) else: self.assertEqual(decoded, value)
def _receive_response(self): """ Receive data from the server and decode it into a KRPC.Response object """ # Read the size and position of the response message data = b'' while True: try: data += self._rpc_connection.partial_receive(1) size, _ = Decoder.decode_size_and_position(data) break except IndexError: pass # Read and decode the response message data = self._rpc_connection.receive(size) return Decoder.decode(data, self._response_type)
def _run_test_decode_value(self, typ, cases): for decoded, encoded in cases: value = Decoder.decode(unhexlify(encoded), typ) if typ.python_type == float: self.assertEqual(str(decoded)[0:8], str(value)[0:8]) else: self.assertEqual(decoded, value)
def _invoke(self, service, procedure, args=[], kwargs={}, param_names=[], param_types=[], return_type=None): """ Execute an RPC """ # Build the request request = self._build_request(service, procedure, args, kwargs, param_names, param_types, return_type) # Send the request with self._rpc_connection_lock: self._send_request(request) response = self._receive_response() # Check for an error response if response.has_error: raise RPCError(response.error) # Decode the response and return the (optional) result result = None if return_type is not None: result = Decoder.decode(response.return_value, return_type) return result
def _receive_response(self): """ Receive data from the server and decode it into a KRPC.Response object """ # Read the size and position of the response message data = b"" while True: try: data += self._rpc_connection.partial_receive(1) size, position = Decoder.decode_size_and_position(data) break except IndexError: pass # Read and decode the response message data = self._rpc_connection.receive(size) return Decoder.decode(data, self._response_type)
def test_decode_message_delimited(self): typ = krpc.schema.KRPC.Request message = '1c' + '0a0b536572766963654e616d65120d50726f6365647572654e616d65' request = Decoder.decode_delimited(unhexlify(message), self.types.as_type('KRPC.Request')) self.assertEqual('ServiceName', request.service) self.assertEqual('ProcedureName', request.procedure)
def __init__(self, name, position, type, attributes, documentation, default_argument=None): self.name = name self.type = types.get_parameter_type(position, type, attributes) self.has_default_argument = default_argument is not None if default_argument is not None: default_argument = Decoder.decode(str(bytearray(default_argument)), self.type) self.default_argument = default_argument self.documentation = documentation
def update_thread(connection, stop): stream_message_type = Types().as_type('KRPC.StreamMessage') response_type = Types().as_type('KRPC.Response') while True: # Read the size and position of the response message data = b'' while True: try: data += connection.partial_receive(1) size,position = Decoder.decode_size_and_position(data) break except IndexError: pass except: #TODO: is there a better way to catch exceptions when the thread is forcibly stopped (e.g. by CTRL+c)? return if stop.is_set(): connection.close() return # Read and decode the response message data = connection.receive(size) response = Decoder.decode(data, stream_message_type) # Add the data to the cache with _stream_cache_lock: for response in response.responses: id = response.id if id not in _stream_cache: continue # Check for an error response if response.response.has_error: _stream_cache[id].value = RPCError(response.response.error) continue # Decode the return value and store it in the cache typ = _stream_cache[id].return_type value = Decoder.decode(response.response.return_value, typ) _stream_cache[id].update(value)
def update_thread(connection, stop, cache, cache_lock): stream_message_type = Types().as_type('KRPC.StreamMessage') while True: # Read the size and position of the response message data = b'' while True: try: data += connection.partial_receive(1) size, _ = Decoder.decode_size_and_position(data) break except IndexError: pass except: # pylint: disable=bare-except # TODO: is there a better way to catch exceptions when the # thread is forcibly stopped (e.g. by CTRL+c)? return if stop.is_set(): connection.close() return # Read and decode the response message data = connection.receive(size) response = Decoder.decode(data, stream_message_type) # Add the data to the cache with cache_lock: for response in response.responses: if response.id not in cache: continue # Check for an error response if response.response.has_error: cache[response.id].value = RPCError( response.response.error) continue # Decode the return value and store it in the cache typ = cache[response.id].return_type value = Decoder.decode(response.response.return_value, typ) cache[response.id].update(value)
def update_thread(connection): stream_message_type = Types().as_type('KRPC.StreamMessage') response_type = Types().as_type('KRPC.Response') while True: # Read the size and position of the response message data = b'' while True: try: data += connection.partial_receive(1) size,position = Decoder.decode_size_and_position(data) break except IndexError: pass except socket.error: return # Read and decode the response message try: data = connection.receive(size) except socket.error: return response = Decoder.decode(data, stream_message_type) # Add the data to the cache with _stream_cache_lock: for response in response.responses: id = response.id if id not in _stream_cache: continue # Check for an error response if response.response.HasField('error'): _stream_cache[id].value = RPCError(response.response.error) continue # Decode the return value and store it in the cache typ = _stream_cache[id].return_type value = Decoder.decode(response.response.return_value, typ) _stream_cache[id].update(value)
def _parse_procedure(cls, procedure): param_names = [_to_snake_case(param.name) for param in procedure.parameters] param_types = [cls._client._types.get_parameter_type(i, param.type, procedure.attributes) for i,param in enumerate(procedure.parameters)] param_required = [not param.HasField('default_argument') for param in procedure.parameters] param_default = [] for param,typ in zip(procedure.parameters, param_types): if param.HasField('default_argument'): param_default.append(Decoder.decode(param.default_argument, typ)) else: param_default.append(None) return_type = None if procedure.HasField('return_type'): return_type = cls._client._types.get_return_type(procedure.return_type, procedure.attributes) return param_names, param_types, param_required, param_default, return_type
def _parse_procedure(cls, procedure): param_names = [_to_snake_case(param.name) for param in procedure.parameters] param_types = [cls._client._types.get_parameter_type(i, param.type, procedure.attributes) for i,param in enumerate(procedure.parameters)] param_required = [not param.has_default_argument for param in procedure.parameters] param_default = [] for param,typ in zip(procedure.parameters, param_types): if param.has_default_argument: param_default.append(Decoder.decode(param.default_argument, typ)) else: param_default.append(None) return_type = None if procedure.has_return_type: return_type = cls._client._types.get_return_type(procedure.return_type, procedure.attributes) return param_names, param_types, param_required, param_default, return_type
def __init__(self, name, type, documentation, default_value=None): super(Parameter, self).__init__() self.name = name self.type = as_type(self.types, type) self.has_default_value = default_value is not None if default_value is not None: # Note: following is a workaround for decoding EnumerationType, # as set_values has not been called if not isinstance(self.type, EnumerationType): typ = self.type else: typ = self.types.sint32_type default_value = Decoder.decode( str(bytearray(base64.b64decode(default_value))), typ) self.default_value = default_value self.documentation = documentation
def __init__( self, name, position, type, attributes, documentation, default_value=None ): # pylint: disable=redefined-builtin super(Parameter, self).__init__() self.name = name self.type = self.types.get_parameter_type(position, type, attributes) self.has_default_value = default_value is not None if default_value is not None: # Note: following is a workaround for decoding EnumType, as set_values has not been called if not isinstance(self.type, EnumType): typ = self.type else: typ = self.types.as_type("int32") default_value = Decoder.decode(str(bytearray(base64.b64decode(default_value))), typ) self.default_value = default_value self.documentation = documentation
def update(self, results): with self._update_lock: for result in results: if result.id not in self._streams: continue # Check for an error response if result.result.HasField('error'): self._update_stream( result.id, self._conn._build_error(result.result.error)) continue # Decode the return value and store it in the cache typ = self._streams[result.id].return_type value = Decoder.decode(result.result.value, typ) self._update_stream(result.id, value)
def _parse_procedure(cls, procedure): param_names = [snake_case(param.name) for param in procedure.parameters] param_types = [cls._client._types.as_type(param.type) for param in procedure.parameters] param_required = [not param.default_value for param in procedure.parameters] param_default = [] for param, typ in zip(procedure.parameters, param_types): if param.default_value: param_default.append(Decoder.decode(param.default_value, typ)) else: param_default.append(None) return_type = None if not Types.is_none_type(procedure.return_type): return_type = cls._client._types.as_type(procedure.return_type) return param_names, param_types, param_required, \ param_default, return_type
def _invoke(self, service, procedure, args=[], kwargs={}, param_names=[], param_types=[], return_type=None): """ Execute an RPC """ # Build the request request = self._build_request(service, procedure, args, kwargs, param_names, param_types, return_type) # Send the request with self._rpc_connection_lock: self._send_request(request) response = self._receive_response() # Check for an error response if response.HasField("error"): raise RPCError(response.error) # Decode the response and return the (optional) result result = None if return_type is not None: result = Decoder.decode(response.return_value, return_type) return result
def _parse_procedure(cls, procedure): param_names = [ snake_case(param.name) for param in procedure.parameters ] param_types = [ cls._client._types.as_type(param.type) for param in procedure.parameters ] param_required = [ not param.default_value for param in procedure.parameters ] param_default = [] for param, typ in zip(procedure.parameters, param_types): if param.default_value: param_default.append(Decoder.decode(param.default_value, typ)) else: param_default.append(None) return_type = None if not Types.is_none_type(procedure.return_type): return_type = cls._client._types.as_type(procedure.return_type) return param_names, param_types, param_required, \ param_default, return_type
def __init__( self, name, position, type, # pylint: disable=redefined-builtin attributes, documentation, default_value=None): super(Parameter, self).__init__() self.name = name self.type = self.types.get_parameter_type(position, type, attributes) self.has_default_value = default_value is not None if default_value is not None: # Note: following is a workaround for decoding EnumType, # as set_values has not been called if not isinstance(self.type, EnumType): typ = self.type else: typ = self.types.as_type('int32') default_value = Decoder.decode( str(bytearray(base64.b64decode(default_value))), typ) self.default_value = default_value self.documentation = documentation
def test_decode_message(self): message = '0a0b536572766963654e616d65120d50726f6365647572654e616d65' request = Decoder.decode(unhexlify(message), self.types.as_type('KRPC.Request')) self.assertEqual('ServiceName', request.service) self.assertEqual('ProcedureName', request.procedure)
def test_decode_value(self): value = Decoder.decode(unhexlify('ac02'), self.types.as_type('int32')) self.assertEqual(int(300), value)
def test_decode_message(self): message = '0a0b536572766963654e616d65120d50726f6365647572654e616d65' call = Decoder.decode( unhexlify(message), self.types.procedure_call_type) self.assertEqual('ServiceName', call.service) self.assertEqual('ProcedureName', call.procedure)
def test_message_size(self): message = '1c' size = Decoder.decode_message_size(unhexlify(message)) self.assertEqual(28, size)
def test_decode_size_and_position(self): message = '1c' size,position = Decoder.decode_size_and_position(unhexlify(message)) self.assertEqual(28, size) self.assertEqual(1, position)
def test_decode_unicode_string(self): value = Decoder.decode(unhexlify('03e284a2'), self.types.as_type('string')) self.assertEqual(b'\xe2\x84\xa2'.decode('utf-8'), value)
def test_decode_value_delimited(self): value = Decoder.decode_delimited(unhexlify('02'+'ac02'), self.types.as_type('int32')) self.assertEqual(300, value)
def test_guid(self): self.assertEqual( '6f271b39-00dd-4de4-9732-f0d3a68838df', Decoder.guid(unhexlify('391b276fdd00e44d9732f0d3a68838df')))
def test_decode_class(self): typ = self.types.as_type('Class(ServiceName.ClassName)') value = Decoder.decode(unhexlify('ac02'), typ) self.assertTrue(isinstance(value, typ.python_type)) self.assertEqual(300, value._object_id)
def test_decode_value_delimited(self): value = Decoder.decode_delimited(unhexlify('02' + 'ac02'), self.types.as_type('int32')) self.assertEqual(300, value)
def test_guid(self): self.assertEqual('6f271b39-00dd-4de4-9732-f0d3a68838df', Decoder.guid(unhexlify('391b276fdd00e44d9732f0d3a68838df')))
def test_decode_value(self): value = Decoder.decode(unhexlify('ac02'), self.types.uint32_type) self.assertEqual(int(300), value)
def test_decode_message_delimited(self): typ = krpc.schema.KRPC.Request message = '1c'+'0a0b536572766963654e616d65120d50726f6365647572654e616d65' request = Decoder.decode_delimited(unhexlify(message), self.types.as_type('KRPC.Request')) self.assertEqual('ServiceName', request.service) self.assertEqual('ProcedureName', request.procedure)
def test_decode_message(self): message = '0a0b536572766963654e616d65120d50726f6365647572654e616d65' call = Decoder.decode(unhexlify(message), self.types.procedure_call_type) self.assertEqual('ServiceName', call.service) self.assertEqual('ProcedureName', call.procedure)
def test_decode_unicode_string(self): value = Decoder.decode(unhexlify('03e284a2'), self.types.string_type) self.assertEqual(b'\xe2\x84\xa2'.decode('utf-8'), value)
def test_decode_class(self): typ = self.types.class_type('ServiceName', 'ClassName') value = Decoder.decode(unhexlify('ac02'), typ) self.assertTrue(isinstance(value, typ.python_type)) self.assertEqual(300, value._object_id)
def test_decode_class_none(self): typ = self.types.class_type('ServiceName', 'ClassName') value = Decoder.decode(unhexlify('00'), typ) self.assertIsNone(value)
def test_decode_class_none(self): typ = self.types.as_type('Class(ServiceName.ClassName)') value = Decoder.decode(unhexlify('00'), typ) self.assertIsNone(value)
def test_decode_size_and_position(self): message = '1c' size, position = Decoder.decode_size_and_position(unhexlify(message)) self.assertEqual(28, size) self.assertEqual(1, position)