learning-loop-node 0.13.4__py3-none-any.whl → 0.13.6__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 learning-loop-node might be problematic. Click here for more details.

@@ -6,41 +6,38 @@ import numpy as np
6
6
 
7
7
  from ..data_classes import ImageMetadata, ModelInformation
8
8
  from ..globals import GLOBALS
9
+ from .exceptions import NodeNeedsRestartError
9
10
 
10
11
 
11
12
  class DetectorLogic():
12
13
 
13
14
  def __init__(self, model_format: str) -> None:
14
15
  self.model_format: str = model_format
15
- self._model_info: Optional[ModelInformation] = None
16
+ self.model_info: Optional[ModelInformation] = None
16
17
 
17
- async def soft_reload(self):
18
- self._model_info = None
19
-
20
- @property
21
- def model_info(self) -> ModelInformation:
22
- if self._model_info is None:
23
- raise Exception('Model not loaded')
24
- return self._model_info
18
+ self._remaining_init_attempts: int = 2
25
19
 
26
- @property
27
- def is_initialized(self) -> bool:
28
- return self._model_info is not None
20
+ async def soft_reload(self):
21
+ self.model_info = None
29
22
 
30
- def load_model(self):
23
+ def load_model_info_and_init_model(self):
31
24
  logging.info('Loading model from %s', GLOBALS.data_folder)
32
- model_info = ModelInformation.load_from_disk(f'{GLOBALS.data_folder}/model')
33
- if model_info is None:
25
+ self.model_info = ModelInformation.load_from_disk(f'{GLOBALS.data_folder}/model')
26
+ if self.model_info is None:
34
27
  logging.error('No model found')
35
- self._model_info = None
36
- raise Exception('No model found')
28
+ self.model_info = None
29
+ return
30
+
37
31
  try:
38
- self._model_info = model_info
39
32
  self.init()
40
- logging.info('Successfully loaded model %s', self._model_info)
33
+ logging.info('Successfully loaded model %s', self.model_info)
34
+ self._remaining_init_attempts = 2
41
35
  except Exception:
42
- self._model_info = None
43
- logging.error('Could not init model %s', model_info)
36
+ self._remaining_init_attempts -= 1
37
+ self.model_info = None
38
+ logging.error('Could not init model %s. Retries left: %s', self.model_info, self._remaining_init_attempts)
39
+ if self._remaining_init_attempts == 0:
40
+ raise NodeNeedsRestartError('Could not init model') from None
44
41
  raise
45
42
 
46
43
  @abstractmethod
@@ -1,8 +1,10 @@
1
1
  import asyncio
2
2
  import contextlib
3
+ import math
3
4
  import os
4
5
  import shutil
5
6
  import subprocess
7
+ import sys
6
8
  from dataclasses import asdict
7
9
  from datetime import datetime
8
10
  from threading import Thread
@@ -14,8 +16,16 @@ from dacite import from_dict
14
16
  from fastapi.encoders import jsonable_encoder
15
17
  from socketio import AsyncClient
16
18
 
17
- from ..data_classes import (AboutResponse, Category, Context, DetectionStatus, ImageMetadata, ModelInformation,
18
- ModelVersionResponse, Shape)
19
+ from ..data_classes import (
20
+ AboutResponse,
21
+ Category,
22
+ Context,
23
+ DetectionStatus,
24
+ ImageMetadata,
25
+ ModelInformation,
26
+ ModelVersionResponse,
27
+ Shape,
28
+ )
19
29
  from ..data_classes.socket_response import SocketResponse
20
30
  from ..data_exchanger import DataExchanger, DownloadError
21
31
  from ..enums import OperationMode, VersionMode
@@ -23,6 +33,7 @@ from ..globals import GLOBALS
23
33
  from ..helpers import environment_reader
24
34
  from ..node import Node
25
35
  from .detector_logic import DetectorLogic
36
+ from .exceptions import NodeNeedsRestartError
26
37
  from .inbox_filter.relevance_filter import RelevanceFilter
27
38
  from .outbox import Outbox
28
39
  from .rest import about as rest_about
@@ -80,13 +91,13 @@ class DetectorNode(Node):
80
91
  return AboutResponse(
81
92
  operation_mode=self.operation_mode.value,
82
93
  state=self.status.state,
83
- model_info=self.detector_logic._model_info, # pylint: disable=protected-access
94
+ model_info=self.detector_logic.model_info, # pylint: disable=protected-access
84
95
  target_model=self.target_model.version if self.target_model else None,
85
96
  version_control=self.version_control.value
86
97
  )
87
98
 
88
99
  def get_model_version_response(self) -> ModelVersionResponse:
89
- current_version = self.detector_logic._model_info.version if self.detector_logic._model_info is not None else 'None' # pylint: disable=protected-access
100
+ current_version = self.detector_logic.model_info.version if self.detector_logic.model_info is not None else 'None' # pylint: disable=protected-access
90
101
  target_version = self.target_model.version if self.target_model is not None else 'None'
91
102
  loop_version = self.loop_deployment_target.version if self.loop_deployment_target is not None else 'None'
92
103
 
@@ -170,13 +181,13 @@ class DetectorNode(Node):
170
181
 
171
182
  # simulate startup
172
183
  await self.detector_logic.soft_reload()
173
- self.detector_logic.load_model()
184
+ self.detector_logic.load_model_info_and_init_model()
174
185
  self.operation_mode = OperationMode.Idle
175
186
 
176
187
  async def on_startup(self) -> None:
177
188
  try:
178
189
  self.outbox.ensure_continuous_upload()
179
- self.detector_logic.load_model()
190
+ self.detector_logic.load_model_info_and_init_model()
180
191
  except Exception:
181
192
  self.log.exception("error during 'startup'")
182
193
  self.operation_mode = OperationMode.Idle
@@ -235,7 +246,7 @@ class DetectorNode(Node):
235
246
 
236
247
  @self.sio.event
237
248
  async def info(sid) -> Dict:
238
- if self.detector_logic.is_initialized:
249
+ if self.detector_logic.model_info is not None:
239
250
  return asdict(self.detector_logic.model_info)
240
251
  return {"status": "No model loaded"}
241
252
 
@@ -272,7 +283,7 @@ class DetectorNode(Node):
272
283
  '''upload an image with detections'''
273
284
 
274
285
  detection_data = data.get('detections', {})
275
- if detection_data and self.detector_logic.is_initialized:
286
+ if detection_data and self.detector_logic.model_info is not None:
276
287
  try:
277
288
  image_metadata = from_dict(data_class=ImageMetadata, data=detection_data)
278
289
  except Exception as e:
@@ -321,9 +332,12 @@ class DetectorNode(Node):
321
332
  self.log.debug('not checking for updates; no target model selected')
322
333
  return
323
334
 
324
- current_version = self.detector_logic._model_info.version if self.detector_logic._model_info is not None else None # pylint: disable=protected-access
335
+ if self.detector_logic.model_info is not None:
336
+ current_version = self.detector_logic.model_info.version
337
+ else:
338
+ current_version = None
325
339
 
326
- if not self.detector_logic.is_initialized or self.target_model.version != current_version:
340
+ if current_version != self.target_model.version:
327
341
  self.log.info('Current model "%s" needs to be updated to %s',
328
342
  current_version or "-", self.target_model.version)
329
343
 
@@ -349,7 +363,10 @@ class DetectorNode(Node):
349
363
  self.log.info('Updated symlink for model to %s', os.readlink(model_symlink))
350
364
 
351
365
  try:
352
- self.detector_logic.load_model()
366
+ self.detector_logic.load_model_info_and_init_model()
367
+ except NodeNeedsRestartError:
368
+ self.log.error('Node needs restart')
369
+ sys.exit(0)
353
370
  except Exception:
354
371
  self.log.exception('Could not load model, will retry download on next check')
355
372
  shutil.rmtree(target_model_folder, ignore_errors=True)
@@ -383,9 +400,9 @@ class DetectorNode(Node):
383
400
  self.log.info('Status sync failed: not connected')
384
401
  raise Exception('Status sync failed: not connected')
385
402
 
386
- try:
403
+ if self.detector_logic.model_info is not None:
387
404
  current_model = self.detector_logic.model_info.version
388
- except Exception:
405
+ else:
389
406
  current_model = None
390
407
 
391
408
  target_model_version = self.target_model.version if self.target_model else None
@@ -0,0 +1,7 @@
1
+
2
+
3
+ class NodeNeedsRestartError(Exception):
4
+ '''
5
+ NodeNeedsRestartError is raised when the node needs to be restarted.
6
+ This is e.g. the case when the GPU is not available anymore.
7
+ '''
@@ -74,6 +74,7 @@ class Node(FastAPI):
74
74
  self.repeat_loop_lock = asyncio.Lock()
75
75
 
76
76
  self.previous_state: Optional[str] = None
77
+ self.repeat_loop_cycle_sec = 5
77
78
 
78
79
  def log_status_on_change(self, current_state_str: str, full_status: Any):
79
80
  if self.previous_state != current_state_str:
@@ -146,7 +147,7 @@ class Node(FastAPI):
146
147
  except Exception:
147
148
  self.log.exception('error in repeat loop')
148
149
 
149
- await asyncio.sleep(5)
150
+ await asyncio.sleep(self.repeat_loop_cycle_sec)
150
151
 
151
152
  async def _ensure_sio_connection(self):
152
153
  if self.socket_connection_broken or self._sio_client is None or not self.sio_client.connected:
@@ -129,10 +129,6 @@ class MockDetectorLogic(DetectorLogic): # pylint: disable=abstract-method
129
129
  model_name="mock",
130
130
  )])
131
131
 
132
- @property
133
- def is_initialized(self):
134
- return True
135
-
136
132
  def evaluate_with_all_info(self, image: np.ndarray, tags: List[str], source: Optional[str] = None, creation_date: Optional[str] = None):
137
133
  return self.image_metadata
138
134
 
@@ -51,9 +51,6 @@ def test_rest_detect(test_detector_node: DetectorNode, grouping_key: str):
51
51
  headers = {grouping_key: '0:0:0:0', 'tags': 'some_tag'}
52
52
 
53
53
  assert isinstance(test_detector_node.detector_logic, TestingDetectorLogic)
54
- # test_detector_node.detector_logic.mock_is_initialized = True
55
- # print(test_detector_node.detector_logic.mock_is_initialized)
56
- # print(test_detector_node.detector_logic.is_initialized)
57
54
  response = requests.post(f'http://localhost:{GLOBALS.detector_port}/detect',
58
55
  files=image, headers=headers, timeout=30)
59
56
  assert response.status_code == 200
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: learning-loop-node
3
- Version: 0.13.4
3
+ Version: 0.13.6
4
4
  Summary: Python Library for Nodes which connect to the Zauberzeug Learning Loop
5
5
  Home-page: https://github.com/zauberzeug/learning_loop_node
6
6
  License: MIT
@@ -11,8 +11,9 @@ learning_loop_node/data_classes/socket_response.py,sha256=tIdt-oYf6ULoJIDYQCecNM
11
11
  learning_loop_node/data_classes/training.py,sha256=FFPsr2AA7ynYz39MLZaFJ0sF_9Axll5HHbAA8nnirp0,5726
12
12
  learning_loop_node/data_exchanger.py,sha256=IG5ki3f3IsVuXbyw6q_gUIakgv-GMT6e9nhOhzjKgW4,9055
13
13
  learning_loop_node/detector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- learning_loop_node/detector/detector_logic.py,sha256=FhGbu0mdF0tW0Gg8cr8xM6ZmZzEigGf0IcAWBjoxFrs,2191
15
- learning_loop_node/detector/detector_node.py,sha256=2DpolliA36HIOvFqNeZaBOX2vwKL58i1rEJidPcFymM,25151
14
+ learning_loop_node/detector/detector_logic.py,sha256=s1EFLrk_SFvLJOsIj9b0lp-Oq0DgfVWxT6Q34Vmi_JE,2243
15
+ learning_loop_node/detector/detector_node.py,sha256=6MUdR_WzzcubwUAiaUo7TDF2xs-VkEvB_y1sNsrONYI,25461
16
+ learning_loop_node/detector/exceptions.py,sha256=C6KbNPlSbtfgDrZx2Hbhm7Suk9jVoR3fMRCO0CkrMsQ,196
16
17
  learning_loop_node/detector/inbox_filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
18
  learning_loop_node/detector/inbox_filter/cam_observation_history.py,sha256=1PHgXRrhSQ34HSFw7mdX8ndRxHf_i1aP5nXXnrZxhAY,3312
18
19
  learning_loop_node/detector/inbox_filter/relevance_filter.py,sha256=NPEmrAtuGjIWCtHS0B3zDmnYWkhVFCLbd_7RUp08_AM,1372
@@ -38,7 +39,7 @@ learning_loop_node/helpers/gdrive_downloader.py,sha256=zeYJciTAJVRpu_eFjwgYLCpIa
38
39
  learning_loop_node/helpers/log_conf.py,sha256=hqVAa_9NnYEU6N0dcOKmph82p7MpgKqeF_eomTLYzWY,961
39
40
  learning_loop_node/helpers/misc.py,sha256=J29iBmsEUAraKKDN1m1NKiHQ3QrP5ub5HBU6cllSP2g,7384
40
41
  learning_loop_node/loop_communication.py,sha256=kc7GrkUS14Ka5OICaaOd_LZ61D-6O19GcyDEwckTxvM,7286
41
- learning_loop_node/node.py,sha256=rBdZiBzp7VqQkIt9GJ8b4nK2Y54lUJFzK-gc7rb14cA,10966
42
+ learning_loop_node/node.py,sha256=IRV81q1G3-A6_BLNqB3NBT7T_dN5OXegBoM9JHMJuLM,11030
42
43
  learning_loop_node/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
44
  learning_loop_node/rest.py,sha256=omwlRHLnyG-kgCBVnZDk5_SAPobL9g7slWeX21wsPGw,1551
44
45
  learning_loop_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -47,14 +48,14 @@ learning_loop_node/tests/annotator/conftest.py,sha256=G4ZvdZUdvPp9bYCzg3eEVkGCeX
47
48
  learning_loop_node/tests/annotator/pytest.ini,sha256=8QdjmawLy1zAzXrJ88or1kpFDhJw0W5UOnDfGGs_igU,262
48
49
  learning_loop_node/tests/annotator/test_annotator_node.py,sha256=UWRXRSBc1e795ftkp7xrEXbyR4LYvFDDHRpZGqC3vr8,1974
49
50
  learning_loop_node/tests/detector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
- learning_loop_node/tests/detector/conftest.py,sha256=4zNW8dnwj3CDKCkFNVCPbHgFTYtDvdaqnUM4s_I-cq4,5328
51
+ learning_loop_node/tests/detector/conftest.py,sha256=gut-RaacarhWJNCvGEz7O7kj3cS7vJ4SvAxCmR87PIw,5263
51
52
  learning_loop_node/tests/detector/inbox_filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
53
  learning_loop_node/tests/detector/inbox_filter/test_observation.py,sha256=k4WYdvnuV7d_r7zI4M2aA8WuBjm0aycQ0vj1rGE2q4w,1370
53
54
  learning_loop_node/tests/detector/inbox_filter/test_relevance_group.py,sha256=r-wABFQVsTNTjv7vYGr8wbHfOWy43F_B14ZDWHfiZ-A,7613
54
55
  learning_loop_node/tests/detector/inbox_filter/test_unexpected_observations_count.py,sha256=3KKwf-J9oJRMIuuVju2vT9IM9vWhKvswPiXJI8KxmcU,1661
55
56
  learning_loop_node/tests/detector/pytest.ini,sha256=8QdjmawLy1zAzXrJ88or1kpFDhJw0W5UOnDfGGs_igU,262
56
57
  learning_loop_node/tests/detector/test.jpg,sha256=msA-vHPmvPiro_D102Qmn1fn4vNfooqYYEXPxZUmYpk,161390
57
- learning_loop_node/tests/detector/test_client_communication.py,sha256=RUsdmZRQE1YO587JPHB2c2-bTE_tmThQodWuhjzBPEk,9180
58
+ learning_loop_node/tests/detector/test_client_communication.py,sha256=PUjnWnY-9RCZe-gqrtWf3o0ylCNH3WuzHoL7v3eAjAQ,8984
58
59
  learning_loop_node/tests/detector/test_detector_node.py,sha256=0ZMV6coAvdq-nH8CwY9_LR2tUcH9VLcAB1CWuwHQMpo,3023
59
60
  learning_loop_node/tests/detector/test_outbox.py,sha256=IfCz4iBmYA4bm3TK4q2NmWyzQCwZWhUbBrKQNHGxZM4,3007
60
61
  learning_loop_node/tests/detector/test_relevance_filter.py,sha256=ZKcCstFWCDxJzKdVlAe8E6sZzv5NiH8mADhaZjokHoU,2052
@@ -96,6 +97,6 @@ learning_loop_node/trainer/test_executor.py,sha256=6BVGDN_6f5GEMMEvDLSG1yzMybSvg
96
97
  learning_loop_node/trainer/trainer_logic.py,sha256=eK-01qZzi10UjLMCQX8vy5eW2FoghPj3rzzDC-s3Si4,8792
97
98
  learning_loop_node/trainer/trainer_logic_generic.py,sha256=RQqon8JIVzxaNh0KdEe6tMxebsY0DgZllEohHR-AgqU,26846
98
99
  learning_loop_node/trainer/trainer_node.py,sha256=Dl4ZQAjjXQggibeBjvhXAoFClw1ZX2Kkt3v_fjrJnCI,4508
99
- learning_loop_node-0.13.4.dist-info/METADATA,sha256=PCQ2UaAA26NGRvbsCnjf2uWla3Qf-WBjfCs1o5gsgJs,12761
100
- learning_loop_node-0.13.4.dist-info/WHEEL,sha256=WGfLGfLX43Ei_YORXSnT54hxFygu34kMpcQdmgmEwCQ,88
101
- learning_loop_node-0.13.4.dist-info/RECORD,,
100
+ learning_loop_node-0.13.6.dist-info/METADATA,sha256=z7dmbFXZUgUXgs-y0N-w-KwBDo8H2oVngoc1ja2Giok,12761
101
+ learning_loop_node-0.13.6.dist-info/WHEEL,sha256=WGfLGfLX43Ei_YORXSnT54hxFygu34kMpcQdmgmEwCQ,88
102
+ learning_loop_node-0.13.6.dist-info/RECORD,,