holado 0.2.3__py3-none-any.whl → 0.2.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of holado might be problematic. Click here for more details.

Files changed (30) hide show
  1. holado/__init__.py +61 -23
  2. holado/common/context/service_manager.py +10 -2
  3. holado/common/context/session_context.py +43 -12
  4. holado/common/handlers/object.py +12 -4
  5. holado/holado_config.py +1 -0
  6. {holado-0.2.3.dist-info → holado-0.2.5.dist-info}/METADATA +1 -1
  7. {holado-0.2.3.dist-info → holado-0.2.5.dist-info}/RECORD +30 -30
  8. holado_ais/ais/ais_messages.py +140 -139
  9. holado_core/common/tools/path_manager.py +8 -4
  10. holado_grpc/api/rpc/grpc_client.py +122 -118
  11. holado_helper/docker/logging.conf +3 -1
  12. holado_helper/docker/run_holado_test_nonreg_in_docker.sh +28 -28
  13. holado_helper/docker/run_terminal_in_docker-with_docker_control.sh +27 -27
  14. holado_helper/docker/run_terminal_in_docker.sh +26 -26
  15. holado_helper/script/initialize_script.py +5 -5
  16. holado_logging/__init__.py +7 -9
  17. holado_logging/common/logging/holado_logger.py +2 -2
  18. holado_logging/common/logging/log_config.py +152 -127
  19. holado_logging/common/logging/log_manager.py +20 -19
  20. holado_multitask/multitasking/multitask_manager.py +1 -1
  21. holado_multitask/multithreading/thread.py +8 -4
  22. holado_protobuf/__init__.py +1 -1
  23. holado_protobuf/ipc/protobuf/protobuf_messages.py +821 -818
  24. holado_rabbitmq/tools/rabbitmq/rabbitmq_client.py +1 -2
  25. holado_test/behave/independant_runner.py +3 -5
  26. holado_test/scenario/step_tools.py +2 -0
  27. test_holado/environment.py +1 -1
  28. test_holado/logging.conf +3 -1
  29. {holado-0.2.3.dist-info → holado-0.2.5.dist-info}/WHEEL +0 -0
  30. {holado-0.2.3.dist-info → holado-0.2.5.dist-info}/licenses/LICENSE +0 -0
@@ -21,7 +21,6 @@ from holado_core.common.tools.string_tools import StrTools
21
21
  from holado_binary.ipc.binary import Binary
22
22
  from holado_core.common.tools.tools import Tools
23
23
  from holado_python.standard_library.typing import Typing
24
- from holado_ais.ais.patch_pyais import HATagBlock, HA_ais_to_nmea_0183
25
24
  import copy
26
25
 
27
26
  logger = logging.getLogger(__name__)
@@ -29,6 +28,7 @@ logger = logging.getLogger(__name__)
29
28
  try:
30
29
  import pyais
31
30
  from bitarray import bitarray
31
+ from holado_ais.ais.patch_pyais import HATagBlock, HA_ais_to_nmea_0183
32
32
  with_pyais = True
33
33
 
34
34
  # Patch pyais with some fixes
@@ -36,7 +36,6 @@ try:
36
36
  except Exception as exc:
37
37
  if Tools.do_log(logger, logging.DEBUG):
38
38
  logger.debug(f"AIS is not available. Initialization failed on error: {exc}")
39
- logger.warning(f"AIS is not available. Initialization failed on error: {exc}")
40
39
  with_pyais = False
41
40
 
42
41
 
@@ -47,157 +46,159 @@ class AISMessages(object):
47
46
  def is_available(cls):
48
47
  return with_pyais
49
48
 
50
- def __init__(self):
51
- self.__tag_block_group_id = 0
52
-
53
- def default_message_data(self, msg_type:int):
54
- """Return default data values for required data by pyais but not functionnaly required.
55
- It is useful when generating custom messages, for example for tests.
56
- """
57
- return {}
58
-
59
- def new_message_as_bitarray_bytes(self, msg_type:int, data:Union[dict,TableWithHeader]):
60
- msg = self.new_message(msg_type, data)
61
- res = self.convert_message_to_bytes(msg)
62
- return res
63
-
64
- def new_message(self, msg_type:int, data:Union[dict,TableWithHeader]):
65
- if isinstance(data, TableWithHeader):
66
- data = self.__convert_table_to_dict(data)
67
- if not isinstance(data, dict):
68
- raise TechnicalException(f"Unexpected data type '{Typing.get_object_class_fullname(data)}'")
49
+ if with_pyais:
69
50
 
70
- message_type = self.__get_message_type(msg_type)
71
- res = message_type.create(**data)
72
- return res
73
-
74
- def decode(self, *args, encoded_msg=None, parts_separator=b'\n'):
75
- if encoded_msg is not None:
76
- if isinstance(encoded_msg, str):
77
- encoded_bytes = encoded_msg.encode("utf-8")
78
- elif isinstance(encoded_msg, bytes):
79
- encoded_bytes = encoded_msg
80
- else:
81
- raise TechnicalException(f"Parameter 'encoded_msg' must be of type str or bytes")
82
- args = encoded_bytes.split(parts_separator)
83
- return pyais.decode(*args, error_if_checksum_invalid=True)
84
-
85
- def decode_message(self, msg_type:int, bit_array:Union[str,bitarray]):
86
- if isinstance(bit_array, str):
87
- if StrTools.is_hex(bit_array) and not StrTools.is_bitarray(bit_array):
88
- bit_array = Binary.convert_hex_str_to_bin_str(bit_array)
89
- bit_array = bitarray(bit_array)
90
- if not isinstance(bit_array, bitarray):
91
- raise TechnicalException(f"Bad parameter 'bit_array' (accepted types: bitarray, str)")
92
-
93
- message_type = self.__get_message_type(msg_type)
94
- return message_type.from_bitarray(bit_array)
95
-
96
- def encode_raw_payload(self, payload_hex: str, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
97
- from pyais.util import encode_ascii_6
51
+ def __init__(self):
52
+ self.__tag_block_group_id = 0
98
53
 
99
- payload_bitarray = bitarray()
100
- payload_bitarray.frombytes(bytes.fromhex(payload_hex))
101
- payload_ascii6, fill_bits = encode_ascii_6(payload_bitarray)
54
+ def default_message_data(self, msg_type:int):
55
+ """Return default data values for required data by pyais but not functionnaly required.
56
+ It is useful when generating custom messages, for example for tests.
57
+ """
58
+ return {}
102
59
 
103
- seq_id = kwargs.pop('seq_id') if 'seq_id' in kwargs else self.__tag_block_group_id % 10
104
- radio_channel = kwargs['radio_channel'] if 'radio_channel' in kwargs else 'A'
105
- group_id = kwargs.pop('group_id') if 'group_id' in kwargs else None
60
+ def new_message_as_bitarray_bytes(self, msg_type:int, data:Union[dict,TableWithHeader]):
61
+ msg = self.new_message(msg_type, data)
62
+ res = self.convert_message_to_bytes(msg)
63
+ return res
106
64
 
107
- sentences = HA_ais_to_nmea_0183(payload_ascii6, talker_id, radio_channel, fill_bits, seq_id)
108
- return self.__encode_add_tag_block(sentences, tag_block, with_tag_block=with_tag_block, group_id=group_id)
65
+ def new_message(self, msg_type:int, data:Union[dict,TableWithHeader]):
66
+ if isinstance(data, TableWithHeader):
67
+ data = self.__convert_table_to_dict(data)
68
+ if not isinstance(data, dict):
69
+ raise TechnicalException(f"Unexpected data type '{Typing.get_object_class_fullname(data)}'")
70
+
71
+ message_type = self.__get_message_type(msg_type)
72
+ res = message_type.create(**data)
73
+ return res
74
+
75
+ def decode(self, *args, encoded_msg=None, parts_separator=b'\n'):
76
+ if encoded_msg is not None:
77
+ if isinstance(encoded_msg, str):
78
+ encoded_bytes = encoded_msg.encode("utf-8")
79
+ elif isinstance(encoded_msg, bytes):
80
+ encoded_bytes = encoded_msg
81
+ else:
82
+ raise TechnicalException(f"Parameter 'encoded_msg' must be of type str or bytes")
83
+ args = encoded_bytes.split(parts_separator)
84
+ return pyais.decode(*args, error_if_checksum_invalid=True)
109
85
 
110
- def encode(self, data, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
111
- if isinstance(data, pyais.messages.Payload):
112
- return self.encode_msg(data, tag_block=tag_block, with_tag_block=with_tag_block, talker_id=talker_id, **kwargs)
113
- elif isinstance(data, dict):
114
- return self.encode_data(data, tag_block=tag_block, with_tag_block=with_tag_block, talker_id=talker_id, **kwargs)
115
- elif isinstance(data, TableWithHeader):
116
- return self.encode_table(data, tag_block=tag_block, with_tag_block=with_tag_block, talker_id=talker_id, **kwargs)
117
- else:
118
- raise TechnicalException(f"Unexpected data type '{Typing.get_object_class_fullname(data)}'")
119
-
120
- def encode_msg(self, msg, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
121
- if not isinstance(msg, pyais.messages.Payload):
122
- raise TechnicalException(f"Parameter 'msg' is not an AIS message (obtained type: {Typing.get_object_class_fullname(msg)})")
86
+ def decode_message(self, msg_type:int, bit_array:Union[str,bitarray]):
87
+ if isinstance(bit_array, str):
88
+ if StrTools.is_hex(bit_array) and not StrTools.is_bitarray(bit_array):
89
+ bit_array = Binary.convert_hex_str_to_bin_str(bit_array)
90
+ bit_array = bitarray(bit_array)
91
+ if not isinstance(bit_array, bitarray):
92
+ raise TechnicalException(f"Bad parameter 'bit_array' (accepted types: bitarray, str)")
93
+
94
+ message_type = self.__get_message_type(msg_type)
95
+ return message_type.from_bitarray(bit_array)
123
96
 
124
- seq_id = kwargs.pop('seq_id') if 'seq_id' in kwargs else self.__tag_block_group_id % 10
125
- group_id = kwargs.pop('group_id') if 'group_id' in kwargs else None
97
+ def encode_raw_payload(self, payload_hex: str, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
98
+ from pyais.util import encode_ascii_6
99
+
100
+ payload_bitarray = bitarray()
101
+ payload_bitarray.frombytes(bytes.fromhex(payload_hex))
102
+ payload_ascii6, fill_bits = encode_ascii_6(payload_bitarray)
103
+
104
+ seq_id = kwargs.pop('seq_id') if 'seq_id' in kwargs else self.__tag_block_group_id % 10
105
+ radio_channel = kwargs['radio_channel'] if 'radio_channel' in kwargs else 'A'
106
+ group_id = kwargs.pop('group_id') if 'group_id' in kwargs else None
107
+
108
+ sentences = HA_ais_to_nmea_0183(payload_ascii6, talker_id, radio_channel, fill_bits, seq_id)
109
+ return self.__encode_add_tag_block(sentences, tag_block, with_tag_block=with_tag_block, group_id=group_id)
110
+
111
+ def encode(self, data, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
112
+ if isinstance(data, pyais.messages.Payload):
113
+ return self.encode_msg(data, tag_block=tag_block, with_tag_block=with_tag_block, talker_id=talker_id, **kwargs)
114
+ elif isinstance(data, dict):
115
+ return self.encode_data(data, tag_block=tag_block, with_tag_block=with_tag_block, talker_id=talker_id, **kwargs)
116
+ elif isinstance(data, TableWithHeader):
117
+ return self.encode_table(data, tag_block=tag_block, with_tag_block=with_tag_block, talker_id=talker_id, **kwargs)
118
+ else:
119
+ raise TechnicalException(f"Unexpected data type '{Typing.get_object_class_fullname(data)}'")
126
120
 
127
- sentences = pyais.encode_msg(msg, seq_id=seq_id, talker_id=talker_id, **kwargs)
128
- return self.__encode_add_tag_block(sentences, tag_block, with_tag_block=with_tag_block, group_id=group_id)
129
-
130
- def encode_data(self, data, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
131
- if not isinstance(data, dict):
132
- raise TechnicalException(f"Parameter 'data' is not a dict (obtained type: {Typing.get_object_class_fullname(data)})")
121
+ def encode_msg(self, msg, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
122
+ if not isinstance(msg, pyais.messages.Payload):
123
+ raise TechnicalException(f"Parameter 'msg' is not an AIS message (obtained type: {Typing.get_object_class_fullname(msg)})")
124
+
125
+ seq_id = kwargs.pop('seq_id') if 'seq_id' in kwargs else self.__tag_block_group_id % 10
126
+ group_id = kwargs.pop('group_id') if 'group_id' in kwargs else None
127
+
128
+ sentences = pyais.encode_msg(msg, seq_id=seq_id, talker_id=talker_id, **kwargs)
129
+ return self.__encode_add_tag_block(sentences, tag_block, with_tag_block=with_tag_block, group_id=group_id)
133
130
 
134
- seq_id = kwargs.pop('seq_id') if 'seq_id' in kwargs else self.__tag_block_group_id % 10
135
- group_id = kwargs.pop('group_id') if 'group_id' in kwargs else None
131
+ def encode_data(self, data, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
132
+ if not isinstance(data, dict):
133
+ raise TechnicalException(f"Parameter 'data' is not a dict (obtained type: {Typing.get_object_class_fullname(data)})")
134
+
135
+ seq_id = kwargs.pop('seq_id') if 'seq_id' in kwargs else self.__tag_block_group_id % 10
136
+ group_id = kwargs.pop('group_id') if 'group_id' in kwargs else None
137
+
138
+ sentences = pyais.encode_dict(data, seq_id=seq_id, talker_id=talker_id, **kwargs)
139
+ return self.__encode_add_tag_block(sentences, tag_block, with_tag_block=with_tag_block, group_id=group_id)
136
140
 
137
- sentences = pyais.encode_dict(data, seq_id=seq_id, talker_id=talker_id, **kwargs)
138
- return self.__encode_add_tag_block(sentences, tag_block, with_tag_block=with_tag_block, group_id=group_id)
139
-
140
- def encode_table(self, table, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
141
- data = self.__convert_table_to_dict(table)
142
- return self.encode_data(data, tag_block=tag_block, with_tag_block=with_tag_block, talker_id=talker_id, **kwargs)
143
-
144
- def __encode_add_tag_block(self, encoded_sentences, tag_block: HATagBlock = None, with_tag_block=True, group_id=None):
145
- from pyais.messages import TagBlockGroup
141
+ def encode_table(self, table, tag_block: HATagBlock = None, with_tag_block=True, talker_id: str = "AIVDM", **kwargs):
142
+ data = self.__convert_table_to_dict(table)
143
+ return self.encode_data(data, tag_block=tag_block, with_tag_block=with_tag_block, talker_id=talker_id, **kwargs)
146
144
 
147
- if not with_tag_block:
148
- return encoded_sentences
145
+ def __encode_add_tag_block(self, encoded_sentences, tag_block: HATagBlock = None, with_tag_block=True, group_id=None):
146
+ from pyais.messages import TagBlockGroup
147
+
148
+ if not with_tag_block:
149
+ return encoded_sentences
150
+
151
+ res = []
152
+ nb_sentences = len(encoded_sentences)
153
+ if nb_sentences > 1 and group_id is None:
154
+ group_id = self.__tag_block_group_id
155
+ # Increment group_id for next message needing a group_id
156
+ self.__new_tag_block_group_id()
157
+ elif nb_sentences == 1:
158
+ # No group tag block is needed
159
+ group_id = None
160
+
161
+ for index, sentence in enumerate(encoded_sentences):
162
+ sentence_tag_block = copy.deepcopy(tag_block) if index == 0 else None
163
+ if group_id is not None:
164
+ if sentence_tag_block is None:
165
+ sentence_tag_block = HATagBlock()
166
+ sentence_tag_block._group = TagBlockGroup(msg_id=index+1, total=nb_sentences, group_id=group_id)
167
+
168
+ if sentence_tag_block is not None:
169
+ res.append('\\' + sentence_tag_block.to_raw().decode() + '\\' + sentence)
170
+ else:
171
+ res.append(sentence)
172
+
173
+ return res
149
174
 
150
- res = []
151
- nb_sentences = len(encoded_sentences)
152
- if nb_sentences > 1 and group_id is None:
153
- group_id = self.__tag_block_group_id
154
- # Increment group_id for next message needing a group_id
155
- self.__new_tag_block_group_id()
156
- elif nb_sentences == 1:
157
- # No group tag block is needed
158
- group_id = None
175
+ def __new_tag_block_group_id(self):
176
+ self.__tag_block_group_id += 1
177
+ if self.__tag_block_group_id > 9999:
178
+ self.__tag_block_group_id = 0
179
+ return self.__tag_block_group_id
159
180
 
160
- for index, sentence in enumerate(encoded_sentences):
161
- sentence_tag_block = copy.deepcopy(tag_block) if index == 0 else None
162
- if group_id is not None:
163
- if sentence_tag_block is None:
164
- sentence_tag_block = HATagBlock()
165
- sentence_tag_block._group = TagBlockGroup(msg_id=index+1, total=nb_sentences, group_id=group_id)
166
-
167
- if sentence_tag_block is not None:
168
- res.append('\\' + sentence_tag_block.to_raw().decode() + '\\' + sentence)
169
- else:
170
- res.append(sentence)
181
+ def convert_message_to_binary_str(self, msg):
182
+ return msg.to_bitarray().to01()
171
183
 
172
- return res
173
-
174
- def __new_tag_block_group_id(self):
175
- self.__tag_block_group_id += 1
176
- if self.__tag_block_group_id > 9999:
177
- self.__tag_block_group_id = 0
178
- return self.__tag_block_group_id
179
-
180
- def convert_message_to_binary_str(self, msg):
181
- return msg.to_bitarray().to01()
182
-
183
- def convert_message_to_bytes(self, msg):
184
- return msg.to_bitarray().tobytes()
185
-
186
- def __get_message_type(self, msg_type:int):
187
- import importlib
188
- module = importlib.import_module('pyais.messages')
189
- res = getattr(module, f"MessageType{msg_type}")
190
- return res
184
+ def convert_message_to_bytes(self, msg):
185
+ return msg.to_bitarray().tobytes()
191
186
 
192
- def __convert_table_to_dict(self, table):
193
- if not ValueTableManager.is_table_with_header(table):
194
- raise TechnicalException(f"Parameter 'table' is not a table with header (obtained type: {Typing.get_object_class_fullname(table)})")
187
+ def __get_message_type(self, msg_type:int):
188
+ import importlib
189
+ module = importlib.import_module('pyais.messages')
190
+ res = getattr(module, f"MessageType{msg_type}")
191
+ return res
192
+
193
+ def __convert_table_to_dict(self, table):
194
+ if not ValueTableManager.is_table_with_header(table):
195
+ raise TechnicalException(f"Parameter 'table' is not a table with header (obtained type: {Typing.get_object_class_fullname(table)})")
196
+
197
+ if ValueTableManager.verify_table_is_name_value_table(table, raise_exception=False):
198
+ res = ValueTableConverter.convert_name_value_table_2_dict(table)
199
+ else:
200
+ res = ValueTableConverter.convert_table_with_header_to_dict(table)
201
+ return res
195
202
 
196
- if ValueTableManager.verify_table_is_name_value_table(table, raise_exception=False):
197
- res = ValueTableConverter.convert_name_value_table_2_dict(table)
198
- else:
199
- res = ValueTableConverter.convert_table_with_header_to_dict(table)
200
- return res
201
-
202
203
 
203
204
 
@@ -122,10 +122,14 @@ class PathManager(object):
122
122
  else:
123
123
  os.replace(src, dst)
124
124
 
125
+ def get_user_home_path(self):
126
+ import pathlib
127
+ return str(pathlib.Path.home())
128
+
125
129
  def get_local_resources_path(self, name=None):
126
- base_path = os.getenv('HOLADO_TEST_LOCAL_RESOURCES_BASEDIR')
130
+ base_path = os.getenv('HOLADO_LOCAL_RESOURCES_BASEDIR')
127
131
  if base_path is None:
128
- raise Exception("Missing environment variable HOLADO_TEST_LOCAL_RESOURCES_BASEDIR with HolAdo local resources base path")
132
+ base_path = os.path.join(self.get_user_home_path(), '.holado', 'resources')
129
133
 
130
134
  if name is not None:
131
135
  return os.path.join(base_path, name)
@@ -133,9 +137,9 @@ class PathManager(object):
133
137
  return base_path
134
138
 
135
139
  def get_reports_path(self, name=None):
136
- base_path = os.getenv('HOLADO_TEST_OUTPUT_BASEDIR')
140
+ base_path = os.getenv('HOLADO_OUTPUT_BASEDIR')
137
141
  if base_path is None:
138
- raise Exception("Missing environment variable HOLADO_TEST_OUTPUT_BASEDIR with HolAdo output base path")
142
+ base_path = os.path.join(self.get_user_home_path(), '.holado', 'output')
139
143
 
140
144
  if name is not None:
141
145
  return os.path.join(base_path, "reports", name)
@@ -47,135 +47,139 @@ class GRpcClient(object):
47
47
  def is_available(cls):
48
48
  return with_grpc_requests
49
49
 
50
- def __init__(self, name, endpoint, **kwargs):
51
- self.__name = name
52
- self.__endpoint = endpoint
53
- self.__kwargs = kwargs
54
- self.__client = grpc_requests.client.get_by_endpoint(endpoint, **kwargs)
55
-
56
- self.__func_grpc_services = None
57
- self.__func_protobuf_converter = None
58
- self.__func_protobuf_messages = None
59
-
60
- def initialize(self, func_grpc_services, func_protobuf_converter, func_protobuf_messages):
61
- self.__func_grpc_services = func_grpc_services
62
- self.__func_protobuf_converter = func_protobuf_converter
63
- self.__func_protobuf_messages = func_protobuf_messages
50
+ if with_grpc_requests:
51
+ def __init__(self, name, endpoint, **kwargs):
52
+ self.__name = name
53
+ self.__endpoint = endpoint
54
+ self.__kwargs = kwargs
55
+ self.__client = grpc_requests.client.get_by_endpoint(endpoint, **kwargs)
56
+
57
+ self.__func_grpc_services = None
58
+ self.__func_protobuf_converter = None
59
+ self.__func_protobuf_messages = None
64
60
 
65
- @property
66
- def __grpc_services(self):
67
- return self.__func_grpc_services()
68
-
69
- @property
70
- def __protobuf_converter(self):
71
- return self.__func_protobuf_converter()
72
-
73
- @property
74
- def __protobuf_messages(self):
75
- return self.__func_protobuf_messages()
76
-
77
- @property
78
- def name(self):
79
- return self.__name
61
+ def initialize(self, func_grpc_services, func_protobuf_converter, func_protobuf_messages):
62
+ self.__func_grpc_services = func_grpc_services
63
+ self.__func_protobuf_converter = func_protobuf_converter
64
+ self.__func_protobuf_messages = func_protobuf_messages
65
+
66
+ @property
67
+ def __grpc_services(self):
68
+ return self.__func_grpc_services()
80
69
 
81
- @property
82
- def internal_client(self) -> grpc_requests.client.Client:
83
- return self.__client
70
+ @property
71
+ def __protobuf_converter(self):
72
+ return self.__func_protobuf_converter()
84
73
 
85
- def request(self, service, method, request, raw_output=False, **kwargs):
86
- """
87
- :param request: request data in json or proto format
88
- :param raw_output: if method should return a proto object or a json oject (default: False)
89
- :param kwargs: other arguments for underlying grpc_requests method, or further underlying grpc method (ex: 'timeout')
90
- :returns: if raw_output==True, returns a proto object, else returns a json object with proto data
91
- """
92
- logger.info(f"Requesting {service}.{method} with data [{request}] (raw_output: {raw_output} ; kwargs:{kwargs})")
74
+ @property
75
+ def __protobuf_messages(self):
76
+ return self.__func_protobuf_messages()
93
77
 
94
- # Set a default timeout
95
- raise_on_timeout = False
96
- if kwargs is None:
97
- kwargs = {}
98
- if 'timeout' not in kwargs:
99
- kwargs['timeout'] = Config.join_timeout_seconds
100
- raise_on_timeout = True
101
- timeout = kwargs['timeout'] if 'timeout' in kwargs else None
78
+ @property
79
+ def name(self):
80
+ return self.__name
102
81
 
103
- success = False
104
- last_exc = None
105
- for try_nb in range(1,4):
106
- try:
107
- # Ask always raw_output=True, so that we get a Protobuf instance, and then conversion is done if needed
108
- res_proto = self.internal_client.request(service, method, request, raw_output=True, **kwargs)
109
- except Exception as exc:
110
- last_exc = exc
111
- exc_str = str(exc)
112
- msg_list = [
113
- f"Request failed (try {try_nb}):",
114
- f" method: {service}.{method}",
115
- f" data:",
116
- Tools.indent_string(8, str(request)),
117
- f" raw_output: {raw_output}",
118
- f" kwargs: {kwargs}",
119
- f" error:",
120
- Tools.indent_string(8, exc_str) ]
121
- exc_msg = "\n".join(msg_list)
122
- if "status = StatusCode.UNAVAILABLE" in exc_str:
123
- logger.warning("Service temporarily unavailable:\n" + exc_msg)
124
- time.sleep(1)
125
- continue
126
- elif "status = StatusCode.DEADLINE_EXCEEDED" in exc_str:
127
- if raise_on_timeout:
128
- raise TimeoutTechnicalException(exc_msg)
129
- else:
130
- logger.warning(f"Timeout ({timeout} s) occured while requesting:\n" + exc_msg)
82
+ @property
83
+ def internal_client(self) -> grpc_requests.client.Client:
84
+ return self.__client
85
+
86
+ def request(self, service, method, request, raw_output=False, **kwargs):
87
+ """
88
+ :param request: request data in json or proto format
89
+ :param raw_output: if method should return a proto object or a json oject (default: False)
90
+ :param kwargs: other arguments for underlying grpc_requests method, or further underlying grpc method (ex: 'timeout')
91
+ :returns: if raw_output==True, returns a proto object, else returns a json object with proto data
92
+ """
93
+ logger.info(f"Requesting {service}.{method} with data [{request}] (raw_output: {raw_output} ; kwargs:{kwargs})")
94
+
95
+ # Set a default timeout
96
+ raise_on_timeout = False
97
+ if kwargs is None:
98
+ kwargs = {}
99
+ if 'timeout' not in kwargs:
100
+ kwargs['timeout'] = Config.join_timeout_seconds
101
+ raise_on_timeout = True
102
+ timeout = kwargs['timeout'] if 'timeout' in kwargs else None
103
+
104
+ success = False
105
+ last_exc = None
106
+ for try_nb in range(1,4):
107
+ try:
108
+ # Ask always raw_output=True, so that we get a Protobuf instance, and then conversion is done if needed
109
+ res_proto = self.internal_client.request(service, method, request, raw_output=True, **kwargs)
110
+ except Exception as exc:
111
+ last_exc = exc
112
+ exc_str = str(exc)
113
+ msg_list = [
114
+ f"Request failed (try {try_nb}):",
115
+ f" method: {service}.{method}",
116
+ f" data:",
117
+ Tools.indent_string(8, str(request)),
118
+ f" raw_output: {raw_output}",
119
+ f" kwargs: {kwargs}",
120
+ f" error:",
121
+ Tools.indent_string(8, exc_str) ]
122
+ exc_msg = "\n".join(msg_list)
123
+ if "status = StatusCode.UNAVAILABLE" in exc_str:
124
+ logger.warning("Service temporarily unavailable:\n" + exc_msg)
131
125
  time.sleep(1)
132
126
  continue
133
- elif "status = " in exc_str:
134
- raise FunctionalException(exc_msg) from exc
127
+ elif "status = StatusCode.DEADLINE_EXCEEDED" in exc_str:
128
+ if raise_on_timeout:
129
+ raise TimeoutTechnicalException(exc_msg)
130
+ else:
131
+ logger.warning(f"Timeout ({timeout} s) occured while requesting:\n" + exc_msg)
132
+ time.sleep(1)
133
+ continue
134
+ elif "status = " in exc_str:
135
+ raise FunctionalException(exc_msg) from exc
136
+ else:
137
+ raise TechnicalException(exc_msg) from exc
135
138
  else:
136
- raise TechnicalException(exc_msg) from exc
137
- else:
138
- success = True
139
- break
140
-
141
- if not success:
142
- if "status = " in exc_msg:
143
- raise FunctionalException(exc_msg) from last_exc
144
- else:
145
- raise TechnicalException(exc_msg) from last_exc
139
+ success = True
140
+ break
146
141
 
147
- # Manage result conversion if needed
148
- # Note: this step is done manually since grpc_requests has some limitations when raw_output=False:
149
- # - Field with default values are not set in json result
150
- # - Some field types are badly managed (ex: uint64 fields appear as string in json)
151
- if raw_output == True:
152
- if isinstance(res_proto, grpc._channel._MultiThreadedRendezvous):
153
- return list(res_proto)
142
+ if not success:
143
+ if "status = " in exc_msg:
144
+ raise FunctionalException(exc_msg) from last_exc
145
+ else:
146
+ raise TechnicalException(exc_msg) from last_exc
147
+
148
+ # Manage result conversion if needed
149
+ # Note: this step is done manually since grpc_requests has some limitations when raw_output=False:
150
+ # - Field with default values are not set in json result
151
+ # - Some field types are badly managed (ex: uint64 fields appear as string in json)
152
+ if raw_output == True:
153
+ if isinstance(res_proto, grpc._channel._MultiThreadedRendezvous):
154
+ return list(res_proto)
155
+ else:
156
+ return res_proto
154
157
  else:
155
- return res_proto
156
- else:
157
- if isinstance(res_proto, grpc._channel._MultiThreadedRendezvous):
158
- res = [self.__protobuf_converter.convert_protobuf_object_to_json_object(cur_res) for cur_res in res_proto]
158
+ if isinstance(res_proto, grpc._channel._MultiThreadedRendezvous):
159
+ res = [self.__protobuf_converter.convert_protobuf_object_to_json_object(cur_res) for cur_res in res_proto]
160
+ else:
161
+ res = self.__protobuf_converter.convert_protobuf_object_to_json_object(res_proto)
162
+ return res
163
+
164
+ def get_request_data_type_fullname(self, service, method):
165
+ method_descriptor = self.__grpc_services.get_method_descriptor(service, method)
166
+ return method_descriptor.input_type.full_name
167
+
168
+ def build_request_data(self, service, method, params_table=None, params_dict=None, as_proto=False):
169
+ if Tools.do_log(logger, logging.TRACE): # @UndefinedVariable
170
+ logger.trace(f"Building request data for service method '{service}.{method}'")
171
+ if as_proto is not None and as_proto:
172
+ method_descriptor = self.__grpc_services.get_method_descriptor(service, method)
173
+ res = self.__protobuf_messages.new_message(method_descriptor.input_type.full_name, fields_table=params_table, fields_dict=params_dict)
159
174
  else:
160
- res = self.__protobuf_converter.convert_protobuf_object_to_json_object(res_proto)
161
- return res
175
+ if params_table is not None:
176
+ res = ValueTableConverter.convert_name_value_table_2_json_object(params_table)
177
+ elif params_dict is not None:
178
+ res = params_dict
179
+ else:
180
+ res = {}
162
181
 
163
- def get_request_data_type_fullname(self, service, method):
164
- method_descriptor = self.__grpc_services.get_method_descriptor(service, method)
165
- return method_descriptor.input_type.full_name
182
+ return res
166
183
 
167
- def build_request_data(self, service, method, params_table=None, params_dict=None, as_proto=False):
168
- if Tools.do_log(logger, logging.TRACE): # @UndefinedVariable
169
- logger.trace(f"Building request data for service method '{service}.{method}'")
170
- if as_proto is not None and as_proto:
171
- method_descriptor = self.__grpc_services.get_method_descriptor(service, method)
172
- res = self.__protobuf_messages.new_message(method_descriptor.input_type.full_name, fields_table=params_table, fields_dict=params_dict)
173
- else:
174
- if params_table is not None:
175
- res = ValueTableConverter.convert_name_value_table_2_json_object(params_table)
176
- elif params_dict is not None:
177
- res = params_dict
178
- else:
179
- res = {}
180
184
 
181
- return res
185
+
@@ -1,7 +1,9 @@
1
- [logger_root]
1
+ [holado]
2
2
  #level=INFO
3
3
  level=DEBUG
4
4
  #level=TRACE
5
+ log_on_console=False
6
+ log_in_file=True
5
7
 
6
8
  [loggers_levels]
7
9
  holado=INFO