setta 0.0.3.dev8__py3-none-any.whl → 0.0.3.dev9__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -15,7 +15,7 @@
15
15
 
16
16
 
17
17
  <title>setta.dev</title>
18
- <script type="module" crossorigin src="/static/assets/index-8204c872.js"></script>
18
+ <script type="module" crossorigin src="/static/assets/index-d7ede6b5.js"></script>
19
19
  <link rel="stylesheet" href="/static/assets/index-af271c9f.css">
20
20
  </head>
21
21
  <body>
setta/tasks/tasks.py CHANGED
@@ -1,5 +1,7 @@
1
1
  import asyncio
2
+ import copy
2
3
  import logging
4
+ import time
3
5
  from typing import Dict
4
6
 
5
7
  from setta.database.utils import create_new_id
@@ -63,12 +65,12 @@ class Tasks:
63
65
  tasks = []
64
66
  results = []
65
67
 
66
- for sp_info in self.in_memory_subprocesses.values():
67
- for fn_name, dependencies in sp_info["dependencies"].items():
68
+ for sp_key, sp_info in self.in_memory_subprocesses.items():
69
+ for fn_name, fnInfo in sp_info["fnInfo"].items():
68
70
  if (
69
71
  call_all
70
- or None in dependencies
71
- or any(k in dependencies for k in message.content.keys())
72
+ or None in fnInfo["dependencies"]
73
+ or any(k in fnInfo["dependencies"] for k in message.content.keys())
72
74
  ):
73
75
  # Send message to subprocess
74
76
  sp_info["subprocess"].parent_conn.send(
@@ -78,6 +80,8 @@ class Tasks:
78
80
  # Create task for receiving response
79
81
  task = asyncio.create_task(
80
82
  self._handle_subprocess_response(
83
+ sp_key,
84
+ fn_name,
81
85
  message.id,
82
86
  sp_info["subprocess"].parent_conn.recv,
83
87
  websocket_manager,
@@ -100,30 +104,40 @@ class Tasks:
100
104
  return {"content": content, "messageType": C.WS_IN_MEMORY_FN_RETURN}
101
105
 
102
106
  async def _handle_subprocess_response(
103
- self, msg_id, recv_fn, websocket_manager, results
107
+ self, subprocess_key, fn_name, msg_id, recv_fn, websocket_manager, results
104
108
  ):
105
109
  # Run the receive function in a thread
110
+ start_time = time.perf_counter()
106
111
  result = await self.task_runner.run(recv_fn, [], RunType.THREAD)
112
+ elapsed_time = time.perf_counter() - start_time
107
113
  if result["status"] == "success":
108
- if websocket_manager is not None and result["content"]:
109
- await websocket_manager.send_message_to_requester(
110
- msg_id, result["content"], result["messageType"]
114
+ self.update_average_subprocess_fn_time(
115
+ subprocess_key, fn_name, elapsed_time
116
+ )
117
+ if websocket_manager is not None:
118
+ if result["content"]:
119
+ await websocket_manager.send_message_to_requester(
120
+ msg_id, result["content"], result["messageType"]
121
+ )
122
+ await self.maybe_send_latest_run_time_info(
123
+ subprocess_key, fn_name, msg_id, websocket_manager
111
124
  )
112
- else:
113
- results.append(result)
125
+ else:
126
+ results.append(result)
114
127
 
115
128
  async def add_custom_fns(self, code_graph, to_cache):
116
129
  for c in code_graph:
117
130
  subprocess_key = c["subprocess_key"]
118
131
  module_name = c["module_name"]
119
132
  sp = self.in_memory_subprocesses.get(subprocess_key, {}).get("subprocess")
120
- if not sp:
121
- logger.debug(f"Creating new subprocess for {module_name}")
122
- sp = SettaInMemoryFnSubprocess(self.stop_event, self.websockets)
123
- self.in_memory_subprocesses[subprocess_key] = {
124
- "subprocess": sp,
125
- "dependencies": {},
126
- }
133
+ if sp:
134
+ sp.close()
135
+ logger.debug(f"Creating new subprocess for {module_name}")
136
+ sp = SettaInMemoryFnSubprocess(self.stop_event, self.websockets)
137
+ self.in_memory_subprocesses[subprocess_key] = {
138
+ "subprocess": sp,
139
+ "fnInfo": {},
140
+ }
127
141
 
128
142
  sp.parent_conn.send(
129
143
  {
@@ -134,17 +148,22 @@ class Tasks:
134
148
  }
135
149
  )
136
150
  result = await self.task_runner.run(sp.parent_conn.recv, [], RunType.THREAD)
137
- sp_info = self.in_memory_subprocesses[subprocess_key]
151
+ fnInfo = self.in_memory_subprocesses[subprocess_key]["fnInfo"]
138
152
 
139
153
  if result["status"] == "success":
140
- sp_info["dependencies"].update(result["content"])
154
+ for k, v in result["content"].items():
155
+ if k not in fnInfo:
156
+ fnInfo[k] = {
157
+ "dependencies": set(),
158
+ "averageRunTime": None,
159
+ "callCount": 0,
160
+ "lastStatsUpdate": time.time(),
161
+ }
162
+ fnInfo[k]["dependencies"].update(v)
141
163
  else:
142
164
  # TODO: store error message and display on frontend?
143
165
  pass
144
166
 
145
- all_dependencies = set()
146
- for d in sp_info["dependencies"].values():
147
- all_dependencies.update(d)
148
167
  initial_result = await self.call_in_memory_subprocess_fn(
149
168
  TaskMessage(id=create_new_id(), content={}), call_all=True
150
169
  )
@@ -153,9 +172,41 @@ class Tasks:
153
172
  f"self.in_memory_subprocesses keys: {self.in_memory_subprocesses.keys()}"
154
173
  )
155
174
 
156
- return all_dependencies, initial_result["content"]
175
+ return initial_result["content"]
157
176
 
158
177
  def close(self):
159
178
  self.stop_event.set()
160
179
  for v in self.in_memory_subprocesses.values():
161
180
  v["subprocess"].close()
181
+
182
+ def update_average_subprocess_fn_time(self, subprocess_key, fn_name, new_time):
183
+ fnInfo = self.in_memory_subprocesses[subprocess_key]["fnInfo"][fn_name]
184
+ current_avg = fnInfo["averageRunTime"]
185
+ new_avg = (
186
+ new_time
187
+ if current_avg is None
188
+ else ((0.9) * current_avg) + (0.1 * new_time)
189
+ )
190
+ fnInfo["averageRunTime"] = new_avg
191
+ fnInfo["callCount"] += 1
192
+ fnInfo["lastStatsUpdate"] = time.time()
193
+
194
+ async def maybe_send_latest_run_time_info(
195
+ self, subprocess_key, fn_name, msg_id, websocket_manager
196
+ ):
197
+ fnInfo = self.in_memory_subprocesses[subprocess_key]["fnInfo"][fn_name]
198
+ if fnInfo["callCount"] % 10 == 0 or (
199
+ fnInfo["lastStatsUpdate"] and (time.time() - fnInfo["lastStatsUpdate"]) > 10
200
+ ):
201
+ newInfo = self.getInMemorySubprocessInfo()
202
+ await websocket_manager.send_message_to_requester(
203
+ msg_id, newInfo, C.WS_IN_MEMORY_FN_AVG_RUN_TIME
204
+ )
205
+
206
+ def getInMemorySubprocessInfo(self):
207
+ output = {}
208
+ for sp_key, sp_info in self.in_memory_subprocesses.items():
209
+ output[sp_key] = {"fnInfo": copy.deepcopy(sp_info["fnInfo"])}
210
+ for fnInfo in output[sp_key]["fnInfo"].values():
211
+ fnInfo["dependencies"] = list(fnInfo["dependencies"])
212
+ return output
setta/tasks/utils.py CHANGED
@@ -53,7 +53,8 @@ class SettaInMemoryFnSubprocess:
53
53
  self.process.daemon = True # Ensure process dies with parent
54
54
  self.process.start()
55
55
 
56
- self.stop_event = stop_event
56
+ self.stop_event = asyncio.Event()
57
+ self.tasks_stop_event = stop_event
57
58
  self.websockets = websockets
58
59
  self.stdout_queue = queue.Queue()
59
60
  self.stdout_processor_task = None
@@ -99,15 +100,15 @@ class SettaInMemoryFnSubprocess:
99
100
  # Import and store module
100
101
  module = import_code_from_string(code, module_name)
101
102
  added_fn_names = add_fns_from_module(fns_dict, module, module_name)
102
- task_metadata = {}
103
+ dependencies = {}
103
104
  for k in added_fn_names:
104
105
  cache[k] = msg["to_cache"]
105
- task_metadata[k] = get_task_metadata(fns_dict[k], cache[k])
106
+ dependencies[k] = get_task_metadata(fns_dict[k], cache[k])
106
107
 
107
108
  self.child_conn.send(
108
109
  {
109
110
  "status": "success",
110
- "content": task_metadata,
111
+ "content": dependencies,
111
112
  }
112
113
  )
113
114
 
@@ -138,22 +139,34 @@ class SettaInMemoryFnSubprocess:
138
139
 
139
140
  def close(self):
140
141
  try:
142
+ logger.debug("Initiating shutdown sequence")
141
143
  self.parent_conn.send({"type": "shutdown"})
142
- self.process.join(timeout=1.0)
143
- except:
144
- pass
144
+ self.process.join(timeout=2) # Add timeout to process join
145
145
 
146
- if self.process.is_alive():
147
- self.process.terminate()
148
- self.process.join()
146
+ if self.process.is_alive():
147
+ logger.debug("Process still alive after timeout, forcing termination")
148
+ self.process.terminate()
149
+ self.process.join(timeout=1)
150
+ except Exception as e:
151
+ logger.debug(f"Error during process shutdown: {e}")
149
152
 
150
- # Close both sets of connections
151
- self.parent_conn.close()
152
- self.child_conn.close()
153
- self.stdout_parent_conn.close()
154
- self.stdout_child_conn.close()
153
+ # Set stop event before closing pipes
154
+ self.stop_event.set()
155
+
156
+ # Close all connections
157
+ for conn in [
158
+ self.parent_conn,
159
+ self.child_conn,
160
+ self.stdout_parent_conn,
161
+ self.stdout_child_conn,
162
+ ]:
163
+ conn.close()
164
+
165
+ self.stdout_thread.join(timeout=2) # Add timeout to thread join
166
+
167
+ if self.stdout_thread.is_alive():
168
+ logger.debug("Stdout thread failed to terminate within timeout")
155
169
 
156
- self.stdout_thread.join()
157
170
  if self.stdout_processor_task:
158
171
  self.stdout_processor_task.cancel()
159
172
 
@@ -183,9 +196,9 @@ class SettaInMemoryFnSubprocess:
183
196
  self.stdout_processor_task = None
184
197
 
185
198
  async def process_stdout_queue(self):
186
- while not self.stop_event.is_set():
199
+ while not self.should_stop():
187
200
  try:
188
- if self.stop_event.is_set():
201
+ if self.should_stop():
189
202
  break
190
203
  if len(self.websockets) > 0:
191
204
  stdout_data = self.stdout_queue.get_nowait()
@@ -198,19 +211,28 @@ class SettaInMemoryFnSubprocess:
198
211
  except asyncio.CancelledError:
199
212
  break
200
213
  except Exception as e:
201
- if self.stop_event.is_set():
214
+ if self.should_stop():
202
215
  break
203
216
  logger.debug(f"Error processing stdout: {e}")
204
217
 
205
218
  def stdout_listener(self):
206
- while not self.stop_event.is_set():
207
- try:
208
- stdout_data = self.stdout_parent_conn.recv()
209
- self.stdout_queue.put(stdout_data) # simple put, no async needed
210
- except Exception as e:
211
- if self.stop_event.is_set():
219
+ while not self.should_stop():
220
+ if self.stdout_parent_conn.poll(0.1): # Check for data with timeout
221
+ try:
222
+ stdout_data = self.stdout_parent_conn.recv()
223
+ self.stdout_queue.put(stdout_data)
224
+ except EOFError: # Pipe was closed
225
+ break
226
+ except Exception as e:
227
+ logger.debug(f"Error in stdout listener: {e}")
228
+ if self.should_stop():
229
+ break
230
+ else: # No data available within timeout
231
+ if self.should_stop():
212
232
  break
213
- logger.debug(f"Error in stdout listener: {e}")
233
+
234
+ def should_stop(self):
235
+ return self.stop_event.is_set() or self.tasks_stop_event.is_set()
214
236
 
215
237
 
216
238
  def add_fns_from_module(fns_dict, module, module_name=None):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: setta
3
- Version: 0.0.3.dev8
3
+ Version: 0.0.3.dev9
4
4
  Home-page: https://setta.dev
5
5
  Project-URL: GitHub, https://github.com/settadev/setta
6
6
  License-File: LICENSE
@@ -1,4 +1,4 @@
1
- setta/__init__.py,sha256=VRMrMIrGmnKm4x2-C0jl9DIpY57NuqIv9j7dygH9Wjo,27
1
+ setta/__init__.py,sha256=mRKlQs_lYaraYvOzYlGQDMNDPQsRu2O-zNqzBFCdPLA,27
2
2
  setta/server.py,sha256=P78BMFvcCokVhjnVlNIKcmpCSvmzSqt-33bx4bhOe6Y,4626
3
3
  setta/start.py,sha256=jEeSiocLeBitvg48fa6k1CpLnPI63JHkQ8O-WVM6ch8,3078
4
4
  setta/cli/__init__.py,sha256=UxZG_VOMuF6lEBT3teUgTS9ulsK3wt3Gu3BbAQiAmt8,47
@@ -87,7 +87,7 @@ setta/routers/artifact.py,sha256=9wHdreg5DsLshhET-6gEDw2Apw_-r8bRF1x3-_dD9mU,266
87
87
  setta/routers/code_info.py,sha256=rDBLkr5VQOlktap3hWA73ls0VrBi5y4mc_SfWzw9ad0,857
88
88
  setta/routers/dependencies.py,sha256=lQDZFcXw-jUXK1q8yglUg3jY9dKoE-xvVcWckKqmOr4,1126
89
89
  setta/routers/in_memory_fn_stdout_websocket.py,sha256=T2BpLzh6PwYQP0qIkFS4r_VfEKBlwl4gkwIaq6r6Phs,604
90
- setta/routers/interactive.py,sha256=juUtPmCyqHSnIb5k6iOYUtfmukp1Z6x25wISlANgUic,4385
90
+ setta/routers/interactive.py,sha256=V68ooA_C2rSqyXeARHifvr_17iCfLrWPERLSrrb_PIU,4316
91
91
  setta/routers/lsp.py,sha256=DAZqdiRKDWJ9ikjwQetV4_8s9U-EDC91ToJA3u57qnU,385
92
92
  setta/routers/projects.py,sha256=p3zPD3jobYOxBGJSSIYS1Aqu1w-PrJCcEN6dPJ0DT6E,5255
93
93
  setta/routers/reference_renaming.py,sha256=Ec1hz2Nz_hYqk8GyGmUcWKvXo-lVEDbIIK2YbX-wi00,3598
@@ -97,7 +97,7 @@ setta/routers/terminals.py,sha256=91I3tVUPJtLyCD_-E_qBQ_k8uuNUrcXl5P7sCTQuDQE,24
97
97
  setta/routers/websocket.py,sha256=6fSROv9C5PobPXppUWwNLDDO0p8VADYaf2KcgIuTQp4,1121
98
98
  setta/static/constants/BaseUITypes.json,sha256=S08qzaNwjclRKuC9U3J0_PpmDTw5aQFwKQcUb0SiiR4,3873
99
99
  setta/static/constants/Settings.json,sha256=-cpSDnjJ9s86z_hfC69xq7A4kxqphKXqssMUUhTN4BQ,3444
100
- setta/static/constants/constants.json,sha256=mijCGv64ZQ6uTvsIhZ22DBiJjBmsEfn8krBef78aQyk,5093
100
+ setta/static/constants/constants.json,sha256=ICVe1HiGoUlSyiuNptfLX7rimHJk_7SfN38MWJEL62c,5151
101
101
  setta/static/constants/db_init.sql,sha256=eSYQrcEEqNPcshOHJEOpSGGkoN_FJwMfZqJFrbnX_8U,8416
102
102
  setta/static/constants/defaultValues.json,sha256=7S8wabxk7CIM23H0FcXdLaIhhldsFUfN8lrzWAIU5pQ,2948
103
103
  setta/static/constants/settingsProject.json,sha256=yfSIQpu2zUcFnUp2JimAkZ0ispvMv8lMPUUTx4AWSi4,10006
@@ -108,7 +108,7 @@ setta/static/frontend/browserconfig.xml,sha256=w0iw1t89kA7-965LTfyLYrFzewTQnUWE_
108
108
  setta/static/frontend/favicon-16x16.png,sha256=q67Crpy8s3wryu7Y3kffPeysN99Lt4XeFygXhPKize8,740
109
109
  setta/static/frontend/favicon-32x32.png,sha256=4NKXYticYdMrRHmVveHjxqnBU1HWgBT5JyJG8lx3BNE,1027
110
110
  setta/static/frontend/favicon.ico,sha256=02qhEBLsvsgBTZX6dcZElMyivlvrR7Yr6wB8ItEZFsc,15086
111
- setta/static/frontend/index.html,sha256=6pKKvUKghX6IJ3Osmo1icsm9NVQaqwUXVSKZRfh2noo,1296
111
+ setta/static/frontend/index.html,sha256=C5t7lfzDhTvNFweX0ISsHGNyj8TkHAz8uv-VwByWtlY,1296
112
112
  setta/static/frontend/manifest.json,sha256=ULPYw5A68_eNhxuUVXqxT045yhkurKPSz6hjyGcnmhQ,492
113
113
  setta/static/frontend/mstile-144x144.png,sha256=wQqckmRWre2NCCevevI3rv4j0tcduVMkpYr2tPj73cs,2692
114
114
  setta/static/frontend/mstile-150x150.png,sha256=FUwy6PipTofnhmJB5CdXWYgwy-2inq_sIOdOwdDklcY,2674
@@ -184,8 +184,8 @@ setta/static/frontend/assets/cormorant-garamond-latin-700-italic-0bc53e12.woff2,
184
184
  setta/static/frontend/assets/cormorant-garamond-latin-ext-700-italic-525738e0.woff2,sha256=Ulc44CPXdUiI5dY86W76HLk7801Fm9_QywCV-8GRtFU,17240
185
185
  setta/static/frontend/assets/cormorant-garamond-vietnamese-700-italic-99563037.woff2,sha256=mVYwN54qI0RLXqyrDGPUNpBHWSJDKjgUyBRWa2FJ_ao,5248
186
186
  setta/static/frontend/assets/erase-5e0448ea.svg,sha256=XgRI6pChr392LJ-pGbwqqkN8OWcJMEtRGX-Gv-90qjw,872
187
- setta/static/frontend/assets/index-8204c872.js,sha256=3vVnv6_MeYW0qxkfUH5ecJwXxeb4lJevbvhWT60vXxQ,2847453
188
187
  setta/static/frontend/assets/index-af271c9f.css,sha256=ryccn_uul30_aMdIhgzMlq6GcE0D6x8fyfneCdgaY9o,232608
188
+ setta/static/frontend/assets/index-d7ede6b5.js,sha256=giu4kQ9aQjEAt6f0_j7KZXnM0XNGtAXSIdOXAO3q_40,2848353
189
189
  setta/static/frontend/assets/inter-all-400-normal-054f12d0.woff,sha256=BU8S0GmcIMyYte4ESEdQJO-WvL2Rb-38m1n0ujdbYxI,128624
190
190
  setta/static/frontend/assets/inter-all-600-normal-c03769e5.woff,sha256=wDdp5VNyQL_IbxcPThD2qI-ETg_QKj7AmCwMCjqDfLE,139072
191
191
  setta/static/frontend/assets/inter-all-800-normal-15dc6e4b.woff,sha256=FdxuS9xuVumB5nbVcCXyt3IWqzS4Z75qiRz_0FV5al0,139120
@@ -232,8 +232,8 @@ setta/static/seed/.DS_Store,sha256=ENxJvDQd7Te_U8gExcXtHE-mAeBUYOHELRfDWgN1NmA,6
232
232
  setta/static/seed/examples/.DS_Store,sha256=1lFlJ5EFymdzGAUAaI30vcaaLHt3F1LwpG7xILf9jsM,6148
233
233
  setta/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
234
234
  setta/tasks/task_runner.py,sha256=gMXpfZWFMQbix2MfrHVCKB7BxQCjO8JH2P8cxUmt1ms,849
235
- setta/tasks/tasks.py,sha256=6VLxadVQecHRw3yGJ-AGWe8ARgzyCbwr5fGga_9px1E,6007
236
- setta/tasks/utils.py,sha256=SFUn2reG9DjinBrdJ84W8CPGqt0XhEQhz5kIS-pNai4,8587
235
+ setta/tasks/tasks.py,sha256=0VYL2P9bOV5b82OHEuIIIhixBit9qcoK817x5JZdpQE,8126
236
+ setta/tasks/utils.py,sha256=9XfSoqYsUf9rBWorZTEqGXfU6J6-nDztIkkgRo1ndhQ,9509
237
237
  setta/tasks/fns/__init__.py,sha256=JhGzzQGaT9BWtF3pOmguh6pzIF9kdG3jdDNLyYZ2w7g,461
238
238
  setta/tasks/fns/codeAreaAutocomplete.py,sha256=gJ5JbjkWDyTothr-UF-YlOxrbVzj2iyOVK7XD3lfhSQ,6416
239
239
  setta/tasks/fns/codeAreaFindTemplateVars.py,sha256=vD9rY8VNPavv6VKa1bnxRPPRDNvFQy6mPIZRl-_3GnY,3708
@@ -254,9 +254,9 @@ setta/utils/generate_new_filename.py,sha256=KBLX6paDmTvXR-027TpqQkfijIXc7mCfhen-
254
254
  setta/utils/section_contents.py,sha256=V2HQPik6DfSXw4j7IalbP5AZ3OEGCbtL5ub3xL-Q_Qo,4141
255
255
  setta/utils/utils.py,sha256=KjzcvgM3Ab3IcE8vaWYtgBpwzPLKg0LmblnHLoYZJHM,9164
256
256
  setta/utils/websocket_manager.py,sha256=S2lEGZsc2OhW0yKLW9VEcH7wi7ppzTfDMQa_hxf3ovc,3772
257
- setta-0.0.3.dev8.dist-info/LICENSE,sha256=us9fuCq9wmiZVzayjKxNZ2iJYF6dROe0Qp57ToCO7XU,11361
258
- setta-0.0.3.dev8.dist-info/METADATA,sha256=HHh9zIUqN15JpKkBkfLsfC3YFSp1WYPrt1uB3Z1Wo1o,829
259
- setta-0.0.3.dev8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
260
- setta-0.0.3.dev8.dist-info/entry_points.txt,sha256=P0qCESy9fWF2q1EQ9JufGldCSnPHplDPn8J6Bgk5hB0,42
261
- setta-0.0.3.dev8.dist-info/top_level.txt,sha256=8G4lmRzVOnJ11_DescPVHE6MQZH-o06A0nGsDDV2ngY,6
262
- setta-0.0.3.dev8.dist-info/RECORD,,
257
+ setta-0.0.3.dev9.dist-info/LICENSE,sha256=us9fuCq9wmiZVzayjKxNZ2iJYF6dROe0Qp57ToCO7XU,11361
258
+ setta-0.0.3.dev9.dist-info/METADATA,sha256=OXk3_WwJkRIycDIw9YOQFDWJpYQlWXv5TQLznCHNd_g,829
259
+ setta-0.0.3.dev9.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
260
+ setta-0.0.3.dev9.dist-info/entry_points.txt,sha256=P0qCESy9fWF2q1EQ9JufGldCSnPHplDPn8J6Bgk5hB0,42
261
+ setta-0.0.3.dev9.dist-info/top_level.txt,sha256=8G4lmRzVOnJ11_DescPVHE6MQZH-o06A0nGsDDV2ngY,6
262
+ setta-0.0.3.dev9.dist-info/RECORD,,