pyqt-code-editor 1.0.4__tar.gz → 1.0.6__tar.gz
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.
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/PKG-INFO +1 -1
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/__init__.py +1 -1
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/sigmund/sigmund_analyst_widget.py +1 -1
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/base.py +7 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/manager.py +91 -13
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/COPYING +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyproject.toml +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/_settings.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/app.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/code_editors.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/languages/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/languages/generic.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/languages/javascript.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/languages/markdown.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/languages/python.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/languages/text.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/editor_panel.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/find_in_files.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/jupyter_console/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/jupyter_console/jupyter_console.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/jupyter_console/jupyter_console_tab.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/jupyter_console/kernel_spec_manager.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/project_explorer.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/settings_panel.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/sigmund/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/sigmund/confirm_run_code_dialog.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/sigmund/editor_workspace.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/sigmund/sigmund.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/sigmund/sigmund_analyst_chat_widget.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/workspace_explorer.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/environment_manager.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/auto_indent.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/auto_pair.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/check.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/comment.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/complete.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/execute.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/file_link.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/highlight_matching_brackets.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/line_number.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/merge_undo_actions.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/python_auto_indent.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/search_replace.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/shortcuts.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/smart_backspace_delete.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/symbols.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/theme.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/widgets/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/zoom.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/sigmund-analyst.png +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/signal_router.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/syntax_highlighters/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/syntax_highlighters/languages/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/syntax_highlighters/languages/generic.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/syntax_highlighters/languages/python.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/syntax_highlighters/syntax_highlighter.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/themes.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/utils/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/utils/languages/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/utils/languages/python/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/utils/languages/python/_auto_indent.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/utils/languages/python/_extract_cells.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/utils/languages/python/_mask_str_in_code.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/watchdog.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/calltip_widget.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/completion_popup.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/dock.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/quick_open_dialog.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/quick_open_file_dialog.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/quick_symbol_dialog.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/search_replace_frame.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/tab_splitter.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/tabbed_editor.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/languages/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/languages/generic.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/languages/python.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/process.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/__init__.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/codestral.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/jedi.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/ruff.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/sigmund.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/symbol.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/tree_sitter.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/settings.py +0 -0
- {pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/readme.md +0 -0
|
@@ -174,7 +174,7 @@ class SigmundAnalystWidget(SigmundWidget):
|
|
|
174
174
|
time.sleep(.5)
|
|
175
175
|
self._execution_result = None
|
|
176
176
|
self._execution_loop = QEventLoop()
|
|
177
|
-
QTimer.singleShot(
|
|
177
|
+
QTimer.singleShot(5 * 60000, self._execution_loop.quit) # 5min timeout
|
|
178
178
|
self._jupyter_console.execute_code(code)
|
|
179
179
|
self._execution_loop.exec_()
|
|
180
180
|
self._execution_loop = None
|
|
@@ -146,6 +146,13 @@ class Base:
|
|
|
146
146
|
# This function periodically prunes worker processes if they are unused.
|
|
147
147
|
for pid in to_remove:
|
|
148
148
|
self._active_requests.pop(pid, None)
|
|
149
|
+
# Non-blockingly finish cleaning up any workers that were
|
|
150
|
+
# previously asked to stop, and possibly ask further idle
|
|
151
|
+
# workers to stop (throttled internally). Neither call blocks
|
|
152
|
+
# the GUI thread, even on platforms (e.g. Windows) where
|
|
153
|
+
# joining a worker process can take a noticeable amount of
|
|
154
|
+
# time.
|
|
155
|
+
manager.reap_stopping_workers()
|
|
149
156
|
manager.stop_unused_workers()
|
|
150
157
|
|
|
151
158
|
def set_modified(self, modified):
|
|
@@ -5,8 +5,11 @@ from .process import main_worker_process_function
|
|
|
5
5
|
from .. import watchdog, settings
|
|
6
6
|
logger = logging.getLogger(__name__)
|
|
7
7
|
STOP_UNUSED_INTERVAL = 10
|
|
8
|
+
MAX_CONCURRENT_STARTING = 2
|
|
8
9
|
_last_stop_unused_time = 0
|
|
9
10
|
_workers = {} # pid -> {"process", "request_queue", "result_queue", "is_free"}
|
|
11
|
+
_stopping = {} # pid -> same shape as _workers, but already asked to quit
|
|
12
|
+
_starting = set() # pids of workers created but not yet proven alive-and-useful
|
|
10
13
|
suspended = False
|
|
11
14
|
|
|
12
15
|
|
|
@@ -35,6 +38,9 @@ def _cleanup_dead_workers():
|
|
|
35
38
|
w["process"].join(timeout=1)
|
|
36
39
|
except Exception as e:
|
|
37
40
|
logger.warning(f"Error joining dead worker {pid}: {e}")
|
|
41
|
+
# If this worker died before ever completing its first request, it
|
|
42
|
+
# was still occupying a "starting" slot; free that slot up.
|
|
43
|
+
_starting.discard(pid)
|
|
38
44
|
|
|
39
45
|
|
|
40
46
|
def send_worker_request(**data) -> (Queue, int):
|
|
@@ -46,7 +52,17 @@ def send_worker_request(**data) -> (Queue, int):
|
|
|
46
52
|
call mark_worker_as_free(pid) to release this worker for future use.
|
|
47
53
|
|
|
48
54
|
If workers are suspended, return (None, None).
|
|
49
|
-
|
|
55
|
+
|
|
56
|
+
We also decline to create a new worker (returning (None, None)) if
|
|
57
|
+
MAX_CONCURRENT_STARTING workers are already "starting" -- i.e. have
|
|
58
|
+
been created but haven't yet completed a first successful round-trip
|
|
59
|
+
(see _starting / mark_worker_as_free()). This caps how many freshly
|
|
60
|
+
spawned, still-booting worker processes can pile up at once. A single
|
|
61
|
+
worker creation still pays the normal (and, on Windows, sometimes
|
|
62
|
+
substantial) process-creation cost on the GUI thread, but we avoid the
|
|
63
|
+
much worse case of several such creations happening back-to-back and
|
|
64
|
+
competing for CPU/disk/antivirus scanning at the same time.
|
|
65
|
+
"""
|
|
50
66
|
if suspended:
|
|
51
67
|
return None, None
|
|
52
68
|
# 1. Look for an existing free worker
|
|
@@ -57,7 +73,12 @@ def send_worker_request(**data) -> (Queue, int):
|
|
|
57
73
|
w["request_queue"].put(data)
|
|
58
74
|
return w["result_queue"], pid
|
|
59
75
|
|
|
60
|
-
# 2. If no free worker was found, create a new one
|
|
76
|
+
# 2. If no free worker was found, create a new one -- unless too many
|
|
77
|
+
# workers are already in the process of starting up.
|
|
78
|
+
if len(_starting) >= MAX_CONCURRENT_STARTING:
|
|
79
|
+
logger.info("declining requst, because too many worker(s) already starting.")
|
|
80
|
+
return None, None
|
|
81
|
+
|
|
61
82
|
request_queue = Queue()
|
|
62
83
|
result_queue = Queue()
|
|
63
84
|
p = Process(target=main_worker_process_function,
|
|
@@ -72,6 +93,7 @@ def send_worker_request(**data) -> (Queue, int):
|
|
|
72
93
|
"result_queue": result_queue,
|
|
73
94
|
"is_free": False,
|
|
74
95
|
}
|
|
96
|
+
_starting.add(pid)
|
|
75
97
|
|
|
76
98
|
logger.info(f"Creating new worker {pid} for request {data['action']}")
|
|
77
99
|
settings_action = {'action': 'set_settings',
|
|
@@ -85,12 +107,18 @@ def mark_worker_as_free(pid: int):
|
|
|
85
107
|
"""
|
|
86
108
|
Mark a previously-used worker process (identified by pid)
|
|
87
109
|
as free for reuse. If the worker has died, clean it up instead.
|
|
110
|
+
|
|
111
|
+
This is also the point at which a worker is considered to have proven
|
|
112
|
+
itself alive-and-useful (it has completed a request), so we release
|
|
113
|
+
its "starting" slot here if it still held one -- whether it succeeded
|
|
114
|
+
or died in the process.
|
|
88
115
|
"""
|
|
89
116
|
w = _workers.get(pid)
|
|
90
117
|
if w is None:
|
|
91
118
|
return
|
|
92
119
|
if w["process"].is_alive():
|
|
93
120
|
w["is_free"] = True
|
|
121
|
+
_starting.discard(pid)
|
|
94
122
|
logger.info(f"Marking worker {pid} as free")
|
|
95
123
|
else:
|
|
96
124
|
# The worker died while handling a request; clean up its resources
|
|
@@ -98,16 +126,51 @@ def mark_worker_as_free(pid: int):
|
|
|
98
126
|
_workers.pop(pid)
|
|
99
127
|
_close_worker_queues(w)
|
|
100
128
|
w["process"].join(timeout=1)
|
|
129
|
+
_starting.discard(pid)
|
|
101
130
|
|
|
102
131
|
def check_worker_alive(pid: int) -> bool:
|
|
103
132
|
w = _workers.get(pid)
|
|
104
133
|
return w and w["process"].is_alive()
|
|
105
134
|
|
|
106
|
-
def
|
|
135
|
+
def reap_stopping_workers():
|
|
136
|
+
"""
|
|
137
|
+
Finish cleaning up worker processes that were previously asked to quit
|
|
138
|
+
(via stop_unused_workers()), but only once they have actually
|
|
139
|
+
terminated. This is non-blocking: we merely check `is_alive()` for each
|
|
140
|
+
stopping worker, and only `join()` (with a zero timeout, so it returns
|
|
141
|
+
essentially instantly) processes that have already exited. Workers that
|
|
142
|
+
haven't exited yet are left in place and will be picked up on a later
|
|
143
|
+
call.
|
|
144
|
+
|
|
145
|
+
This should be called periodically (e.g. from the same timer that calls
|
|
146
|
+
stop_unused_workers()) so that workers asked to stop are eventually
|
|
147
|
+
cleaned up and their queues closed.
|
|
148
|
+
"""
|
|
149
|
+
done_pids = [pid for pid, w in _stopping.items() if not w["process"].is_alive()]
|
|
150
|
+
for pid in done_pids:
|
|
151
|
+
w = _stopping.pop(pid)
|
|
152
|
+
logger.info(f"Reaping stopped worker {pid}.")
|
|
153
|
+
try:
|
|
154
|
+
w["process"].join(timeout=0)
|
|
155
|
+
except Exception as e:
|
|
156
|
+
logger.warning(f"Error joining stopped worker {pid}: {e}")
|
|
157
|
+
_close_worker_queues(w)
|
|
158
|
+
|
|
159
|
+
def stop_unused_workers(max_free: int = 3, force: bool = False):
|
|
107
160
|
"""
|
|
108
|
-
|
|
109
|
-
left. This keeps us from accumulating too
|
|
110
|
-
prunes any workers that have died
|
|
161
|
+
Request that free worker processes stop until there is at most
|
|
162
|
+
'max_free' free processes left. This keeps us from accumulating too
|
|
163
|
+
many idle worker processes. Also prunes any workers that have died
|
|
164
|
+
unexpectedly.
|
|
165
|
+
|
|
166
|
+
Important: this function never blocks waiting for a worker to actually
|
|
167
|
+
exit. Workers that we decide to stop are sent a "quit" message and
|
|
168
|
+
moved into an internal `_stopping` bookkeeping dict; they are only
|
|
169
|
+
joined (and their queues closed) once they have actually terminated,
|
|
170
|
+
which is handled by reap_stopping_workers(). This matters because
|
|
171
|
+
joining a process that hasn't exited yet can block for a noticeable
|
|
172
|
+
amount of time, especially on Windows, and this function is typically
|
|
173
|
+
called from a periodic timer on the main/GUI thread.
|
|
111
174
|
"""
|
|
112
175
|
global _last_stop_unused_time
|
|
113
176
|
if not force and time.time() - _last_stop_unused_time < STOP_UNUSED_INTERVAL:
|
|
@@ -125,21 +188,24 @@ def stop_unused_workers(max_free: int = 1, force: bool = False):
|
|
|
125
188
|
logger.info("No free workers to stop.")
|
|
126
189
|
return
|
|
127
190
|
|
|
128
|
-
#
|
|
191
|
+
# Ask some free workers to stop until we have max_free left. We don't
|
|
192
|
+
# wait for them to exit here; see reap_stopping_workers().
|
|
129
193
|
while to_stop > 0 and free_pids:
|
|
130
194
|
pid = free_pids.pop()
|
|
131
195
|
w = _workers.pop(pid)
|
|
132
|
-
logger.info(f"
|
|
196
|
+
logger.info(f"Asking free worker {pid} to stop because we have too many.")
|
|
133
197
|
w["request_queue"].put({"action": "quit"})
|
|
134
|
-
|
|
135
|
-
_close_worker_queues(w)
|
|
198
|
+
_stopping[pid] = w
|
|
136
199
|
to_stop -= 1
|
|
137
200
|
|
|
138
|
-
logger.info("Finished
|
|
201
|
+
logger.info("Finished requesting unused workers to stop.")
|
|
139
202
|
|
|
140
203
|
def stop_all_workers():
|
|
141
204
|
"""
|
|
142
|
-
Cleanly shut down all worker processes.
|
|
205
|
+
Cleanly shut down all worker processes. Unlike stop_unused_workers(),
|
|
206
|
+
this function blocks until every worker has actually exited, since it
|
|
207
|
+
is only used when we need a hard guarantee that no worker processes
|
|
208
|
+
remain (e.g. on application shutdown or when suspending).
|
|
143
209
|
"""
|
|
144
210
|
logger.info(f"Stopping {len(_workers)} worker processes...")
|
|
145
211
|
for pid, w in list(_workers.items()):
|
|
@@ -149,6 +215,18 @@ def stop_all_workers():
|
|
|
149
215
|
w["process"].join()
|
|
150
216
|
_close_worker_queues(w)
|
|
151
217
|
del _workers[pid]
|
|
218
|
+
# Also finish off any workers that were previously asked to stop (via
|
|
219
|
+
# stop_unused_workers()) but haven't been reaped yet.
|
|
220
|
+
for pid, w in list(_stopping.items()):
|
|
221
|
+
logger.info(f"Joining previously-stopping worker {pid}.")
|
|
222
|
+
try:
|
|
223
|
+
w["process"].join()
|
|
224
|
+
except Exception as e:
|
|
225
|
+
logger.warning(f"Error joining stopping worker {pid}: {e}")
|
|
226
|
+
_close_worker_queues(w)
|
|
227
|
+
del _stopping[pid]
|
|
228
|
+
# Nothing is left running, so no worker can still be "starting".
|
|
229
|
+
_starting.clear()
|
|
152
230
|
logger.info("All workers stopped.")
|
|
153
231
|
|
|
154
232
|
|
|
@@ -177,4 +255,4 @@ def resume():
|
|
|
177
255
|
logger.info("Resuming worker processes...")
|
|
178
256
|
|
|
179
257
|
|
|
180
|
-
settings.setting_changed.connect(update_setting)
|
|
258
|
+
settings.setting_changed.connect(update_setting)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/code_editors.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/languages/generic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/languages/python.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/code_editors/languages/text.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/editor_panel.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/find_in_files.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/project_explorer.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/settings_panel.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/sigmund/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/sigmund/sigmund.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/components/workspace_explorer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/merge_undo_actions.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/python_auto_indent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/smart_backspace_delete.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/mixins/widgets/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/syntax_highlighters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/utils/languages/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/calltip_widget.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/completion_popup.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/quick_open_dialog.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/quick_open_file_dialog.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/quick_symbol_dialog.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/widgets/search_replace_frame.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/languages/__init__.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/languages/generic.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/languages/python.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/__init__.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/codestral.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/sigmund.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/symbol.py
RENAMED
|
File without changes
|
{pyqt_code_editor-1.0.4 → pyqt_code_editor-1.0.6}/pyqt_code_editor/worker/providers/tree_sitter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|