thestage 0.5.46__py3-none-any.whl → 0.5.471__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.
Files changed (49) hide show
  1. thestage/.env +5 -0
  2. thestage/__init__.py +2 -1
  3. thestage/cli_command.py +56 -0
  4. thestage/cli_command_helper.py +51 -0
  5. thestage/config/config_storage.py +5 -0
  6. thestage/controllers/base_controller.py +19 -15
  7. thestage/controllers/config_controller.py +30 -38
  8. thestage/controllers/container_controller.py +43 -79
  9. thestage/controllers/instance_controller.py +23 -40
  10. thestage/controllers/project_controller.py +95 -184
  11. thestage/controllers/utils_controller.py +12 -8
  12. thestage/debug_tests.py +12 -0
  13. thestage/entities/container.py +0 -5
  14. thestage/entities/rented_instance.py +0 -5
  15. thestage/entities/self_hosted_instance.py +0 -2
  16. thestage/helpers/error_handler.py +14 -14
  17. thestage/helpers/exception_hook.py +14 -0
  18. thestage/helpers/logger/app_logger.py +3 -4
  19. thestage/main.py +25 -13
  20. thestage/services/abstract_service.py +0 -40
  21. thestage/services/app_config_service.py +7 -6
  22. thestage/services/clients/.DS_Store +0 -0
  23. thestage/services/clients/thestage_api/api_client.py +54 -68
  24. thestage/services/clients/thestage_api/core/api_client_core.py +92 -9
  25. thestage/services/clients/thestage_api/dtos/container_response.py +1 -1
  26. thestage/services/clients/thestage_api/dtos/inference_simulator_model_response.py +1 -1
  27. thestage/services/clients/thestage_api/dtos/inference_simulator_response.py +1 -1
  28. thestage/services/clients/thestage_api/dtos/instance_rented_response.py +1 -1
  29. thestage/services/clients/thestage_api/dtos/selfhosted_instance_response.py +1 -1
  30. thestage/services/clients/thestage_api/dtos/task_controller/task_status_localized_map_response.py +1 -1
  31. thestage/services/clients/thestage_api/dtos/validate_token_response.py +11 -0
  32. thestage/services/config_provider/config_provider.py +75 -47
  33. thestage/services/connect/connect_service.py +11 -22
  34. thestage/services/container/container_service.py +6 -23
  35. thestage/services/core_files/config_entity.py +7 -1
  36. thestage/services/filesystem_service.py +2 -2
  37. thestage/services/instance/instance_service.py +12 -26
  38. thestage/services/logging/logging_service.py +17 -45
  39. thestage/services/project/project_service.py +33 -72
  40. thestage/services/remote_server_service.py +3 -4
  41. thestage/services/service_factory.py +21 -27
  42. thestage/services/validation_service.py +14 -9
  43. {thestage-0.5.46.dist-info → thestage-0.5.471.dist-info}/METADATA +3 -4
  44. {thestage-0.5.46.dist-info → thestage-0.5.471.dist-info}/RECORD +47 -41
  45. {thestage-0.5.46.dist-info → thestage-0.5.471.dist-info}/WHEEL +1 -1
  46. thestage/exceptions/http_error_exception.py +0 -12
  47. thestage/services/clients/thestage_api/core/api_client_abstract.py +0 -91
  48. {thestage-0.5.46.dist-info → thestage-0.5.471.dist-info}/LICENSE.txt +0 -0
  49. {thestage-0.5.46.dist-info → thestage-0.5.471.dist-info}/entry_points.txt +0 -0
@@ -5,7 +5,6 @@ import typer
5
5
 
6
6
  from thestage.entities.rented_instance import RentedInstanceEntity
7
7
  from thestage.entities.self_hosted_instance import SelfHostedInstanceEntity
8
- from thestage.services.core_files.config_entity import ConfigEntity
9
8
  from thestage.i18n.translation import __
10
9
  from thestage.services.clients.thestage_api.dtos.enums.selfhosted_status import SelfhostedBusinessStatus
11
10
  from thestage.services.clients.thestage_api.dtos.enums.instance_rented_status import InstanceRentedBusinessStatus
@@ -24,6 +23,7 @@ from thestage.services.remote_server_service import RemoteServerService
24
23
  class InstanceService(AbstractService):
25
24
 
26
25
  __thestage_api_client: TheStageApiClient = None
26
+ __config_provider: ConfigProvider = None
27
27
 
28
28
  def __init__(
29
29
  self,
@@ -31,29 +31,23 @@ class InstanceService(AbstractService):
31
31
  config_provider: ConfigProvider,
32
32
  remote_server_service: RemoteServerService,
33
33
  ):
34
- super(InstanceService, self).__init__(
35
- config_provider=config_provider
36
- )
37
34
  self.__thestage_api_client = thestage_api_client
38
35
  self.__remote_server_service = remote_server_service
36
+ self.__config_provider = config_provider
39
37
 
40
38
  def get_rented_instance(
41
39
  self,
42
- config: ConfigEntity,
43
40
  instance_slug: str,
44
41
  ) -> Optional[InstanceRentedDto]:
45
42
  return self.__thestage_api_client.get_rented_instance(
46
- token=config.main.thestage_auth_token,
47
43
  instance_slug=instance_slug,
48
44
  )
49
45
 
50
46
  def get_self_hosted_instance(
51
47
  self,
52
- config: ConfigEntity,
53
48
  instance_slug: str,
54
49
  ) -> Optional[SelfHostedInstanceDto]:
55
50
  return self.__thestage_api_client.get_selfhosted_instance(
56
- token=config.main.thestage_auth_token,
57
51
  instance_slug=instance_slug,
58
52
  )
59
53
 
@@ -123,10 +117,9 @@ class InstanceService(AbstractService):
123
117
  def connect_to_rented_instance(
124
118
  self,
125
119
  instance_rented_slug: str,
126
- config: ConfigEntity,
127
120
  input_ssh_key_path: Optional[str]
128
121
  ):
129
- instance = self.get_rented_instance(config=config, instance_slug=instance_rented_slug)
122
+ instance = self.get_rented_instance(instance_slug=instance_rented_slug)
130
123
 
131
124
  if instance:
132
125
  self.check_instance_status_to_connect(
@@ -135,7 +128,7 @@ class InstanceService(AbstractService):
135
128
 
136
129
  ssh_path_from_config: Optional[str] = None
137
130
  if not input_ssh_key_path:
138
- ssh_path_from_config = self._config_provider.get_valid_private_key_path_by_ip_address(instance.ip_address)
131
+ ssh_path_from_config = self.__config_provider.get_valid_private_key_path_by_ip_address(instance.ip_address)
139
132
  if ssh_path_from_config:
140
133
  typer.echo(f"Using configured ssh key for this instance: {ssh_path_from_config}")
141
134
 
@@ -150,7 +143,7 @@ class InstanceService(AbstractService):
150
143
 
151
144
  # cannot really detect how ssh connection was ended. capturing stderr using subprocess feels bad/unreliable.
152
145
  if input_ssh_key_path:
153
- self._config_provider.update_remote_server_config_entry(ip_address=instance.ip_address, ssh_key_path=Path(input_ssh_key_path))
146
+ self.__config_provider.update_remote_server_config_entry(ip_address=instance.ip_address, ssh_key_path=Path(input_ssh_key_path))
154
147
  else:
155
148
  typer.echo(__("Server instance not found: %instance_item%", {'instance_item': instance_rented_slug}))
156
149
 
@@ -160,14 +153,13 @@ class InstanceService(AbstractService):
160
153
  self,
161
154
  selfhosted_instance_slug: str,
162
155
  username: str,
163
- config: ConfigEntity,
164
156
  input_ssh_key_path: Optional[str],
165
157
  ):
166
158
  if not username:
167
159
  username = 'root'
168
160
  typer.echo(__("No remote server username provided, using 'root' as username"))
169
161
 
170
- instance = self.get_self_hosted_instance(config=config, instance_slug=selfhosted_instance_slug)
162
+ instance = self.get_self_hosted_instance(instance_slug=selfhosted_instance_slug)
171
163
 
172
164
  if instance:
173
165
  self.check_selfhosted_status_to_connect(
@@ -176,7 +168,7 @@ class InstanceService(AbstractService):
176
168
 
177
169
  ssh_path_from_config: Optional[str] = None
178
170
  if not input_ssh_key_path:
179
- ssh_path_from_config = self._config_provider.get_valid_private_key_path_by_ip_address(instance.ip_address)
171
+ ssh_path_from_config = self.__config_provider.get_valid_private_key_path_by_ip_address(instance.ip_address)
180
172
  if ssh_path_from_config:
181
173
  typer.echo(f"Using configured ssh key for this instance: {ssh_path_from_config}")
182
174
 
@@ -190,7 +182,7 @@ class InstanceService(AbstractService):
190
182
  )
191
183
 
192
184
  if input_ssh_key_path:
193
- self._config_provider.update_remote_server_config_entry(ip_address=instance.ip_address, ssh_key_path=Path(input_ssh_key_path))
185
+ self.__config_provider.update_remote_server_config_entry(ip_address=instance.ip_address, ssh_key_path=Path(input_ssh_key_path))
194
186
  else:
195
187
  typer.echo(__("Server instance not found: %instance_item%", {'instance_item': selfhosted_instance_slug}))
196
188
 
@@ -198,13 +190,11 @@ class InstanceService(AbstractService):
198
190
  @error_handler()
199
191
  def get_rented_list(
200
192
  self,
201
- config: ConfigEntity,
202
193
  statuses: List[str],
203
194
  row: int = 5,
204
195
  page: int = 1,
205
196
  ) -> PaginatedEntityList[InstanceRentedDto]:
206
197
  data = self.__thestage_api_client.get_rented_instance_list(
207
- token=config.main.thestage_auth_token,
208
198
  statuses=statuses,
209
199
  page=page,
210
200
  limit=row,
@@ -215,13 +205,11 @@ class InstanceService(AbstractService):
215
205
  @error_handler()
216
206
  def get_self_hosted_list(
217
207
  self,
218
- config: ConfigEntity,
219
208
  statuses: List[str],
220
209
  row: int = 5,
221
210
  page: int = 1,
222
211
  ) -> PaginatedEntityList[SelfHostedInstanceDto]:
223
212
  data = self.__thestage_api_client.get_selfhosted_instance_list(
224
- token=config.main.thestage_auth_token,
225
213
  statuses=statuses,
226
214
  page=page,
227
215
  limit=row,
@@ -230,8 +218,8 @@ class InstanceService(AbstractService):
230
218
 
231
219
 
232
220
  @error_handler()
233
- def print_self_hosted_instance_list(self, config: ConfigEntity, statuses, row, page):
234
- selfhosted_instance_status_map = self.__thestage_api_client.get_selfhosted_business_status_map(config.main.thestage_auth_token)
221
+ def print_self_hosted_instance_list(self, statuses, row, page):
222
+ selfhosted_instance_status_map = self.__thestage_api_client.get_selfhosted_business_status_map()
235
223
 
236
224
  if not statuses:
237
225
  statuses = ({key: selfhosted_instance_status_map[key] for key in [
@@ -265,7 +253,6 @@ class InstanceService(AbstractService):
265
253
  'statuses': backend_statuses,
266
254
  },
267
255
  mapper=SelfHostedMapper(),
268
- config=config,
269
256
  headers=list(map(lambda x: x.alias, SelfHostedInstanceEntity.model_fields.values())),
270
257
  row=row,
271
258
  page=page,
@@ -274,8 +261,8 @@ class InstanceService(AbstractService):
274
261
 
275
262
 
276
263
  @error_handler()
277
- def print_rented_instance_list(self, config: ConfigEntity, statuses, row, page):
278
- instance_rented_status_map = self.__thestage_api_client.get_rented_business_status_map(config.main.thestage_auth_token)
264
+ def print_rented_instance_list(self, statuses, row, page):
265
+ instance_rented_status_map = self.__thestage_api_client.get_rented_business_status_map()
279
266
 
280
267
  if not statuses:
281
268
  statuses = ({key: instance_rented_status_map[key] for key in [
@@ -310,7 +297,6 @@ class InstanceService(AbstractService):
310
297
  'statuses': backend_statuses,
311
298
  },
312
299
  mapper=InstanceMapper(),
313
- config=config,
314
300
  headers=list(map(lambda x: x.alias, RentedInstanceEntity.model_fields.values())),
315
301
  row=row,
316
302
  page=page,
@@ -7,11 +7,9 @@ from typing import Optional, Dict
7
7
  import aioconsole
8
8
  import typer
9
9
  from httpx import ReadTimeout, ConnectError, ConnectTimeout
10
- from requests.exceptions import ChunkedEncodingError
11
10
 
12
11
  from thestage.helpers.logger.app_logger import app_logger
13
12
  from thestage.services.clients.thestage_api.dtos.enums.container_status import DockerContainerStatus
14
- from thestage.services.core_files.config_entity import ConfigEntity
15
13
  from thestage.services.clients.thestage_api.dtos.enums.inference_simulator_status import InferenceSimulatorStatus
16
14
  from thestage.services.clients.thestage_api.dtos.enums.task_status import TaskStatus
17
15
  from thestage.services.clients.thestage_api.dtos.inference_controller.get_inference_simulator_response import \
@@ -25,7 +23,6 @@ from thestage.services.abstract_service import AbstractService
25
23
  from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
26
24
  from thestage.helpers.error_handler import error_handler
27
25
  from thestage.services.clients.thestage_api.api_client import TheStageApiClient
28
- from thestage.services.config_provider.config_provider import ConfigProvider
29
26
  from rich import print
30
27
 
31
28
  from thestage.services.logging.exception.log_polling_exception import LogPollingException
@@ -35,24 +32,19 @@ from thestage.services.logging.logging_constants import LOG_MESSAGE_CODE_TASK_FI
35
32
  is_logs_streaming = False
36
33
 
37
34
 
38
- class LoggingService(AbstractService):
35
+ class LoggingService:
39
36
  __thestage_api_client: TheStageApiClient = None
40
37
 
41
38
  def __init__(
42
39
  self,
43
40
  thestage_api_client: TheStageApiClient,
44
- config_provider: ConfigProvider,
45
41
  ):
46
- super(LoggingService, self).__init__(
47
- config_provider=config_provider
48
- )
49
42
  self.__thestage_api_client = thestage_api_client
50
43
 
51
44
 
52
45
  @error_handler()
53
- def print_last_task_logs(self, config: ConfigEntity, task_id: int, logs_number: Optional[int]):
46
+ def print_last_task_logs(self, task_id: int, logs_number: Optional[int]):
54
47
  logs = self.__thestage_api_client.query_user_logs(
55
- token=config.main.thestage_auth_token,
56
48
  task_id=task_id,
57
49
  limit=logs_number
58
50
  )
@@ -61,9 +53,8 @@ class LoggingService(AbstractService):
61
53
 
62
54
 
63
55
  @error_handler()
64
- def print_last_inference_simulator_logs(self, config: ConfigEntity, inference_simulator_id: int, logs_number: Optional[int]):
56
+ def print_last_inference_simulator_logs(self, inference_simulator_id: int, logs_number: Optional[int]):
65
57
  logs = self.__thestage_api_client.query_user_logs(
66
- token=config.main.thestage_auth_token,
67
58
  inference_simulator_id=inference_simulator_id,
68
59
  limit=logs_number
69
60
  )
@@ -72,9 +63,8 @@ class LoggingService(AbstractService):
72
63
 
73
64
 
74
65
  @error_handler()
75
- def print_last_container_logs(self, config: ConfigEntity, container_uid: str, logs_number: Optional[int]):
66
+ def print_last_container_logs(self, container_uid: str, logs_number: Optional[int]):
76
67
  container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
77
- token=config.main.thestage_auth_token,
78
68
  container_slug=container_uid,
79
69
  )
80
70
 
@@ -83,7 +73,6 @@ class LoggingService(AbstractService):
83
73
  raise typer.Exit(1)
84
74
 
85
75
  logs = self.__thestage_api_client.query_user_logs(
86
- token=config.main.thestage_auth_token,
87
76
  container_id=container.id,
88
77
  limit=logs_number
89
78
  )
@@ -92,25 +81,17 @@ class LoggingService(AbstractService):
92
81
 
93
82
 
94
83
  @error_handler()
95
- def stream_task_logs_with_controls(self, config: ConfigEntity, task_id: int):
84
+ def stream_task_logs_with_controls(self, task_id: int):
96
85
  asyncio.run(
97
- self.__stream_task_logs_with_controls_async(
98
- config=config,
99
- task_id=task_id
100
- )
86
+ self.__stream_task_logs_with_controls_async(task_id=task_id)
101
87
  )
102
88
 
103
89
 
104
90
  @error_handler()
105
- async def __stream_task_logs_with_controls_async(self, config: ConfigEntity, task_id: int):
106
- task_view_response: Optional[TaskViewResponse] = self.__thestage_api_client.get_task(
107
- token=config.main.thestage_auth_token,
108
- task_id=task_id,
109
- )
91
+ async def __stream_task_logs_with_controls_async(self, task_id: int):
92
+ task_view_response: Optional[TaskViewResponse] = self.__thestage_api_client.get_task(task_id=task_id,)
110
93
 
111
- task_status_map: Dict[str, str] = self.__thestage_api_client.get_task_localized_status_map(
112
- token=config.main.thestage_auth_token,
113
- )
94
+ task_status_map: Dict[str, str] = self.__thestage_api_client.get_task_localized_status_map()
114
95
 
115
96
  task = task_view_response.task
116
97
 
@@ -135,7 +116,7 @@ class LoggingService(AbstractService):
135
116
 
136
117
  typer.echo(__("CTRL+C to cancel the task. CTRL+D to disconnect from log stream."))
137
118
 
138
- print_logs_task = asyncio.create_task(self.print_realtime_logs(token=config.main.thestage_auth_token, task_id=task.id))
119
+ print_logs_task = asyncio.create_task(self.print_realtime_logs(task_id=task.id))
139
120
  input_task = asyncio.create_task(self.read_log_stream_input())
140
121
 
141
122
  def sigint_handler():
@@ -152,29 +133,24 @@ class LoggingService(AbstractService):
152
133
  if not input_task.result(): # result is only expected if ctrl+D triggered EOFError
153
134
  typer.echo(f"\rTask {task_id} will be canceled")
154
135
  self.__thestage_api_client.cancel_task(
155
- token=config.main.thestage_auth_token,
156
136
  task_id=task.id,
157
137
  )
158
138
 
159
139
  @error_handler()
160
- def stream_inference_simulator_logs_with_controls(self, config: ConfigEntity, slug: str):
140
+ def stream_inference_simulator_logs_with_controls(self, slug: str):
161
141
  asyncio.run(
162
142
  self.__stream_inference_simulator_logs_with_controls_async(
163
- config=config,
164
143
  slug=slug
165
144
  )
166
145
  )
167
146
 
168
147
  @error_handler()
169
- async def __stream_inference_simulator_logs_with_controls_async(self, config: ConfigEntity, slug: str):
148
+ async def __stream_inference_simulator_logs_with_controls_async(self, slug: str):
170
149
  get_inference_simulator_response: Optional[GetInferenceSimulatorResponse] = self.__thestage_api_client.get_inference_simulator(
171
- token=config.main.thestage_auth_token,
172
150
  slug=slug,
173
151
  )
174
152
 
175
- inference_simulator_status_map: Dict[str, str] = self.__thestage_api_client.get_inference_simulator_business_status_map(
176
- token=config.main.thestage_auth_token,
177
- )
153
+ inference_simulator_status_map: Dict[str, str] = self.__thestage_api_client.get_inference_simulator_business_status_map()
178
154
 
179
155
  inference_simulator = get_inference_simulator_response.inferenceSimulator
180
156
 
@@ -201,7 +177,7 @@ class LoggingService(AbstractService):
201
177
  typer.echo(__("CTRL+D to disconnect from log stream."))
202
178
 
203
179
  print_task_or_inference_simulator_logs = asyncio.create_task(
204
- self.print_realtime_logs(token=config.main.thestage_auth_token, inference_simulator_id=inference_simulator.id)
180
+ self.print_realtime_logs(inference_simulator_id=inference_simulator.id)
205
181
  )
206
182
  input_task = asyncio.create_task(self.read_log_stream_input())
207
183
 
@@ -214,19 +190,17 @@ class LoggingService(AbstractService):
214
190
 
215
191
 
216
192
  @error_handler()
217
- def stream_container_logs_with_controls(self, config: ConfigEntity, container_uid: str):
193
+ def stream_container_logs_with_controls(self, container_uid: str):
218
194
  asyncio.run(
219
195
  self.__stream_container_logs_with_controls_async(
220
- config=config,
221
196
  container_uid=container_uid
222
197
  )
223
198
  )
224
199
 
225
200
 
226
201
  @error_handler()
227
- async def __stream_container_logs_with_controls_async(self, config: ConfigEntity, container_uid: str):
202
+ async def __stream_container_logs_with_controls_async(self, container_uid: str):
228
203
  container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
229
- token=config.main.thestage_auth_token,
230
204
  container_slug=container_uid,
231
205
  )
232
206
 
@@ -240,7 +214,7 @@ class LoggingService(AbstractService):
240
214
  typer.echo(f"Log stream for Docker container started")
241
215
  typer.echo("CTRL+D to disconnect from log stream.")
242
216
 
243
- print_logs_task = asyncio.create_task(self.print_realtime_logs(token=config.main.thestage_auth_token, docker_container_id=container.id))
217
+ print_logs_task = asyncio.create_task(self.print_realtime_logs(docker_container_id=container.id))
244
218
  input_task = asyncio.create_task(self.read_log_stream_input())
245
219
 
246
220
  def sigint_handler():
@@ -269,7 +243,6 @@ class LoggingService(AbstractService):
269
243
 
270
244
  async def print_realtime_logs(
271
245
  self,
272
- token: str,
273
246
  task_id: Optional[int] = None,
274
247
  inference_simulator_id: Optional[int] = None,
275
248
  docker_container_id: Optional[int] = None,
@@ -296,7 +269,6 @@ class LoggingService(AbstractService):
296
269
  # print_nonblocking(f'TDIFF {stream_to_logs_diff.total_seconds()}', writer)
297
270
  try:
298
271
  logs_response = await self.__thestage_api_client.poll_logs_httpx(
299
- token=token,
300
272
  task_id=task_id,
301
273
  inference_simulator_id=inference_simulator_id,
302
274
  docker_container_id=docker_container_id,