pyqt-code-editor 1.0.6__tar.gz → 1.1.0__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.
Files changed (91) hide show
  1. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/PKG-INFO +1 -1
  2. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/__init__.py +1 -1
  3. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/manager.py +80 -24
  4. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/COPYING +0 -0
  5. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyproject.toml +0 -0
  6. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/_settings.py +0 -0
  7. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/app.py +0 -0
  8. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/code_editors/__init__.py +0 -0
  9. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/code_editors/code_editors.py +0 -0
  10. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/code_editors/languages/__init__.py +0 -0
  11. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/code_editors/languages/generic.py +0 -0
  12. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/code_editors/languages/javascript.py +0 -0
  13. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/code_editors/languages/markdown.py +0 -0
  14. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/code_editors/languages/python.py +0 -0
  15. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/code_editors/languages/text.py +0 -0
  16. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/__init__.py +0 -0
  17. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/editor_panel.py +0 -0
  18. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/find_in_files.py +0 -0
  19. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/jupyter_console/__init__.py +0 -0
  20. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/jupyter_console/jupyter_console.py +0 -0
  21. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/jupyter_console/jupyter_console_tab.py +0 -0
  22. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/jupyter_console/kernel_spec_manager.py +0 -0
  23. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/project_explorer.py +0 -0
  24. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/settings_panel.py +0 -0
  25. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/sigmund/__init__.py +0 -0
  26. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/sigmund/confirm_run_code_dialog.py +0 -0
  27. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/sigmund/editor_workspace.py +0 -0
  28. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/sigmund/sigmund.py +0 -0
  29. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/sigmund/sigmund_analyst_chat_widget.py +0 -0
  30. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/sigmund/sigmund_analyst_widget.py +0 -0
  31. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/components/workspace_explorer.py +0 -0
  32. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/environment_manager.py +0 -0
  33. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/__init__.py +0 -0
  34. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/auto_indent.py +0 -0
  35. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/auto_pair.py +0 -0
  36. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/base.py +0 -0
  37. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/check.py +0 -0
  38. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/comment.py +0 -0
  39. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/complete.py +0 -0
  40. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/execute.py +0 -0
  41. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/file_link.py +0 -0
  42. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/highlight_matching_brackets.py +0 -0
  43. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/line_number.py +0 -0
  44. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/merge_undo_actions.py +0 -0
  45. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/python_auto_indent.py +0 -0
  46. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/search_replace.py +0 -0
  47. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/shortcuts.py +0 -0
  48. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/smart_backspace_delete.py +0 -0
  49. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/symbols.py +0 -0
  50. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/theme.py +0 -0
  51. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/widgets/__init__.py +0 -0
  52. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/mixins/zoom.py +0 -0
  53. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/sigmund-analyst.png +0 -0
  54. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/signal_router.py +0 -0
  55. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/syntax_highlighters/__init__.py +0 -0
  56. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/syntax_highlighters/languages/__init__.py +0 -0
  57. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/syntax_highlighters/languages/generic.py +0 -0
  58. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/syntax_highlighters/languages/python.py +0 -0
  59. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/syntax_highlighters/syntax_highlighter.py +0 -0
  60. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/themes.py +0 -0
  61. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/utils/__init__.py +0 -0
  62. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/utils/languages/__init__.py +0 -0
  63. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/utils/languages/python/__init__.py +0 -0
  64. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/utils/languages/python/_auto_indent.py +0 -0
  65. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/utils/languages/python/_extract_cells.py +0 -0
  66. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/utils/languages/python/_mask_str_in_code.py +0 -0
  67. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/watchdog.py +0 -0
  68. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/__init__.py +0 -0
  69. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/calltip_widget.py +0 -0
  70. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/completion_popup.py +0 -0
  71. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/dock.py +0 -0
  72. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/quick_open_dialog.py +0 -0
  73. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/quick_open_file_dialog.py +0 -0
  74. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/quick_symbol_dialog.py +0 -0
  75. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/search_replace_frame.py +0 -0
  76. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/tab_splitter.py +0 -0
  77. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/widgets/tabbed_editor.py +0 -0
  78. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/__init__.py +0 -0
  79. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/languages/__init__.py +0 -0
  80. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/languages/generic.py +0 -0
  81. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/languages/python.py +0 -0
  82. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/process.py +0 -0
  83. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/providers/__init__.py +0 -0
  84. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/providers/codestral.py +0 -0
  85. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/providers/jedi.py +0 -0
  86. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/providers/ruff.py +0 -0
  87. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/providers/sigmund.py +0 -0
  88. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/providers/symbol.py +0 -0
  89. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/providers/tree_sitter.py +0 -0
  90. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/pyqt_code_editor/worker/settings.py +0 -0
  91. {pyqt_code_editor-1.0.6 → pyqt_code_editor-1.1.0}/readme.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyqt_code_editor
3
- Version: 1.0.6
3
+ Version: 1.1.0
4
4
  Summary: Fully featured code-editor widgets for PyQt
5
5
  Keywords: code editor,pyqt,ide
6
6
  Author-email: Sebastiaan Mathôt <s.mathot@cogsci.nl>
@@ -2,4 +2,4 @@
2
2
 
3
3
  from ._settings import settings
4
4
 
5
- __version__ = '1.0.6'
5
+ __version__ = '1.1.0'
@@ -43,6 +43,42 @@ def _cleanup_dead_workers():
43
43
  _starting.discard(pid)
44
44
 
45
45
 
46
+ def _start_new_worker(is_free: bool = False) -> int:
47
+ """
48
+ Create and start a single new worker process, send it the current
49
+ settings, and register it in _workers and _starting.
50
+
51
+ `is_free` controls whether the worker is immediately marked as
52
+ available for reuse (used by start_worker_pool(), which starts idle
53
+ workers ahead of time) or not (used by send_worker_request(), which
54
+ immediately queues a real request on it).
55
+
56
+ Returns the new worker's pid. Does not check MAX_CONCURRENT_STARTING;
57
+ callers are responsible for that.
58
+ """
59
+ request_queue = Queue()
60
+ result_queue = Queue()
61
+ p = Process(target=main_worker_process_function,
62
+ args=(request_queue, result_queue))
63
+ p.start()
64
+ pid = p.pid
65
+ watchdog.register_subprocess(pid)
66
+
67
+ _workers[pid] = {
68
+ "process": p,
69
+ "request_queue": request_queue,
70
+ "result_queue": result_queue,
71
+ "is_free": is_free,
72
+ }
73
+ _starting.add(pid)
74
+
75
+ logger.info(f"Created new worker {pid} (is_free={is_free})")
76
+ settings_action = {'action': 'set_settings',
77
+ 'settings': {name: value for name, value in settings}}
78
+ request_queue.put(settings_action)
79
+ return pid
80
+
81
+
46
82
  def send_worker_request(**data) -> (Queue, int):
47
83
  """
48
84
  Send a request to a worker process. If a free worker is available,
@@ -62,10 +98,15 @@ def send_worker_request(**data) -> (Queue, int):
62
98
  substantial) process-creation cost on the GUI thread, but we avoid the
63
99
  much worse case of several such creations happening back-to-back and
64
100
  competing for CPU/disk/antivirus scanning at the same time.
101
+
102
+ Note that start_worker_pool() shares the same _starting bookkeeping,
103
+ so a pool worker that is still booting also counts against this cap.
65
104
  """
66
105
  if suspended:
67
106
  return None, None
68
- # 1. Look for an existing free worker
107
+ # 1. Look for an existing free worker. This also picks up workers that
108
+ # were started ahead of time by start_worker_pool(), whether or not
109
+ # they have finished booting yet.
69
110
  for pid, w in list(_workers.items()):
70
111
  if w["is_free"] and w["process"].is_alive():
71
112
  logger.info(f"Reusing free worker {pid} (of {len(_workers)}) for request {list(data.keys())}")
@@ -76,32 +117,42 @@ def send_worker_request(**data) -> (Queue, int):
76
117
  # 2. If no free worker was found, create a new one -- unless too many
77
118
  # workers are already in the process of starting up.
78
119
  if len(_starting) >= MAX_CONCURRENT_STARTING:
79
- logger.info("declining requst, because too many worker(s) already starting.")
120
+ logger.info(
121
+ f"Declining to start a new worker for request "
122
+ f"{list(data.keys())}; {len(_starting)} worker(s) already "
123
+ "starting."
124
+ )
80
125
  return None, None
81
126
 
82
- request_queue = Queue()
83
- result_queue = Queue()
84
- p = Process(target=main_worker_process_function,
85
- args=(request_queue, result_queue))
86
- p.start()
87
- pid = p.pid
88
- watchdog.register_subprocess(pid)
127
+ pid = _start_new_worker(is_free=False)
128
+ w = _workers[pid]
129
+ logger.info(f"Sending request {data['action']} to newly created worker {pid}")
130
+ w["request_queue"].put(data)
131
+ return w["result_queue"], pid
89
132
 
90
- _workers[pid] = {
91
- "process": p,
92
- "request_queue": request_queue,
93
- "result_queue": result_queue,
94
- "is_free": False,
95
- }
96
- _starting.add(pid)
97
-
98
- logger.info(f"Creating new worker {pid} for request {data['action']}")
99
- settings_action = {'action': 'set_settings',
100
- 'settings': {name: value for name, value in settings}}
101
- request_queue.put(settings_action)
102
- # 3. Send the request, return the new worker's result queue and pid.
103
- request_queue.put(data)
104
- return result_queue, pid
133
+ def start_worker_pool():
134
+ """
135
+ Proactively start up to MAX_CONCURRENT_STARTING idle worker processes,
136
+ so that a free (or at least already-booting) worker is more likely to
137
+ be available by the time the user actually triggers a request -- e.g.
138
+ right when they start typing.
139
+
140
+ Workers started here are tracked in _starting exactly like workers
141
+ created on-demand by send_worker_request(), and are marked as free
142
+ immediately, so send_worker_request() will pick them up as soon as
143
+ they exist -- whether or not they've finished booting yet.
144
+
145
+ Intended to be called at strategic moments (e.g. app startup, after
146
+ resume(), or when a new editor is opened), not from a tight loop: each
147
+ call may create up to MAX_CONCURRENT_STARTING new processes on top of
148
+ whatever is already starting.
149
+ """
150
+ if suspended:
151
+ logger.info("Not starting worker pool because workers are suspended.")
152
+ return
153
+ for _ in range(MAX_CONCURRENT_STARTING):
154
+ _start_new_worker(is_free=True)
155
+ logger.info(f"Started worker pool of {MAX_CONCURRENT_STARTING} worker(s).")
105
156
 
106
157
  def mark_worker_as_free(pid: int):
107
158
  """
@@ -171,6 +222,11 @@ def stop_unused_workers(max_free: int = 3, force: bool = False):
171
222
  joining a process that hasn't exited yet can block for a noticeable
172
223
  amount of time, especially on Windows, and this function is typically
173
224
  called from a periodic timer on the main/GUI thread.
225
+
226
+ Note: if max_free is set lower than MAX_CONCURRENT_STARTING, workers
227
+ just started by start_worker_pool() may be asked to stop again before
228
+ they ever get used. The default of 3 leaves headroom above
229
+ MAX_CONCURRENT_STARTING (2).
174
230
  """
175
231
  global _last_stop_unused_time
176
232
  if not force and time.time() - _last_stop_unused_time < STOP_UNUSED_INTERVAL: