bec-widgets 0.49.0__py3-none-any.whl → 0.50.0__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.
- bec_widgets/cli/client.py +69 -4
- bec_widgets/cli/client_utils.py +18 -11
- bec_widgets/cli/rpc_register.py +76 -0
- bec_widgets/cli/server.py +8 -16
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +5 -0
- bec_widgets/utils/bec_connector.py +21 -1
- bec_widgets/widgets/__init__.py +0 -1
- bec_widgets/widgets/figure/figure.py +23 -22
- bec_widgets/widgets/plots/image.py +14 -24
- bec_widgets/widgets/plots/motor_map.py +12 -19
- bec_widgets/widgets/plots/plot_base.py +1 -0
- bec_widgets/widgets/plots/waveform.py +18 -14
- {bec_widgets-0.49.0.dist-info → bec_widgets-0.50.0.dist-info}/METADATA +4 -4
- {bec_widgets-0.49.0.dist-info → bec_widgets-0.50.0.dist-info}/RECORD +23 -20
- tests/end-2-end/__init__.py +0 -0
- tests/end-2-end/conftest.py +25 -0
- tests/end-2-end/test_bec_figure_rpc_e2e.py +165 -0
- tests/end-2-end/test_rpc_register_e2e.py +50 -0
- tests/unit_tests/conftest.py +7 -0
- tests/unit_tests/test_rpc_register.py +52 -0
- bec_widgets/widgets/editor/__init__.py +0 -1
- bec_widgets/widgets/editor/editor.py +0 -407
- tests/unit_tests/test_editor.py +0 -170
- {bec_widgets-0.49.0.dist-info → bec_widgets-0.50.0.dist-info}/LICENSE +0 -0
- {bec_widgets-0.49.0.dist-info → bec_widgets-0.50.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.49.0.dist-info → bec_widgets-0.50.0.dist-info}/top_level.txt +0 -0
@@ -1,407 +0,0 @@
|
|
1
|
-
import subprocess
|
2
|
-
|
3
|
-
import qdarktheme
|
4
|
-
from jedi import Script
|
5
|
-
from jedi.api import Completion
|
6
|
-
from qtconsole.manager import QtKernelManager
|
7
|
-
from qtconsole.rich_jupyter_widget import RichJupyterWidget
|
8
|
-
|
9
|
-
# pylint: disable=no-name-in-module
|
10
|
-
from qtpy.Qsci import QsciAPIs, QsciLexerPython, QsciScintilla
|
11
|
-
from qtpy.QtCore import Qt, QThread, Signal
|
12
|
-
from qtpy.QtGui import QColor, QFont
|
13
|
-
from qtpy.QtWidgets import QApplication, QFileDialog, QSplitter, QTextEdit, QVBoxLayout, QWidget
|
14
|
-
|
15
|
-
from bec_widgets.widgets.toolbar import ModularToolBar
|
16
|
-
|
17
|
-
|
18
|
-
class AutoCompleter(QThread):
|
19
|
-
"""Initializes the AutoCompleter thread for handling autocompletion and signature help.
|
20
|
-
|
21
|
-
Args:
|
22
|
-
file_path (str): The path to the file for which autocompletion is required.
|
23
|
-
api (QsciAPIs): The QScintilla API instance used for managing autocompletions.
|
24
|
-
enable_docstring (bool, optional): Flag to determine if docstrings should be included in the signatures.
|
25
|
-
"""
|
26
|
-
|
27
|
-
def __init__(self, file_path: str, api: QsciAPIs, enable_docstring: bool = False):
|
28
|
-
super().__init__(None)
|
29
|
-
self.file_path = file_path
|
30
|
-
self.script: Script = None
|
31
|
-
self.api: QsciAPIs = api
|
32
|
-
self.completions: list[Completion] = None
|
33
|
-
self.line = 0
|
34
|
-
self.index = 0
|
35
|
-
self.text = ""
|
36
|
-
|
37
|
-
# TODO so far disabled, quite buggy, docstring extraction has to be generalised
|
38
|
-
self.enable_docstring = enable_docstring
|
39
|
-
|
40
|
-
def update_script(self, text: str):
|
41
|
-
"""Updates the script for Jedi completion based on the current editor text.
|
42
|
-
|
43
|
-
Args:
|
44
|
-
text (str): The current text of the editor.
|
45
|
-
"""
|
46
|
-
if self.script is None or self.script.path != text:
|
47
|
-
self.script = Script(text, path=self.file_path)
|
48
|
-
|
49
|
-
def run(self):
|
50
|
-
"""Runs the thread for generating autocompletions. Overrides QThread.run."""
|
51
|
-
self.update_script(self.text)
|
52
|
-
try:
|
53
|
-
self.completions = self.script.complete(self.line, self.index)
|
54
|
-
self.load_autocomplete(self.completions)
|
55
|
-
except Exception as err:
|
56
|
-
print(err)
|
57
|
-
self.finished.emit()
|
58
|
-
|
59
|
-
def get_function_signature(self, line: int, index: int, text: str) -> str:
|
60
|
-
"""Fetches the function signature for a given position in the text.
|
61
|
-
|
62
|
-
Args:
|
63
|
-
line (int): The line number in the editor.
|
64
|
-
index (int): The index (column number) in the line.
|
65
|
-
text (str): The current text of the editor.
|
66
|
-
|
67
|
-
Returns:
|
68
|
-
str: A string containing the function signature or an empty string if not available.
|
69
|
-
"""
|
70
|
-
self.update_script(text)
|
71
|
-
try:
|
72
|
-
signatures = self.script.get_signatures(line, index)
|
73
|
-
if signatures and self.enable_docstring is True:
|
74
|
-
full_docstring = signatures[0].docstring(raw=True)
|
75
|
-
compact_docstring = self.get_compact_docstring(full_docstring)
|
76
|
-
return compact_docstring
|
77
|
-
if signatures and self.enable_docstring is False:
|
78
|
-
return signatures[0].to_string()
|
79
|
-
except Exception as err:
|
80
|
-
print(f"Signature Error:{err}")
|
81
|
-
return ""
|
82
|
-
|
83
|
-
def load_autocomplete(self, completions: list):
|
84
|
-
"""Loads the autocomplete suggestions into the QScintilla API.
|
85
|
-
|
86
|
-
Args:
|
87
|
-
completions (list[Completion]): A list of Completion objects to be added to the API.
|
88
|
-
"""
|
89
|
-
self.api.clear()
|
90
|
-
for i in completions:
|
91
|
-
self.api.add(i.name)
|
92
|
-
self.api.prepare()
|
93
|
-
|
94
|
-
def get_completions(self, line: int, index: int, text: str):
|
95
|
-
"""Starts the autocompletion process for a given position in the text.
|
96
|
-
|
97
|
-
Args:
|
98
|
-
line (int): The line number in the editor.
|
99
|
-
index (int): The index (column number) in the line.
|
100
|
-
text (str): The current text of the editor.
|
101
|
-
"""
|
102
|
-
self.line = line
|
103
|
-
self.index = index
|
104
|
-
self.text = text
|
105
|
-
self.start()
|
106
|
-
|
107
|
-
def get_compact_docstring(self, full_docstring):
|
108
|
-
"""Generates a compact version of a function's docstring.
|
109
|
-
|
110
|
-
Args:
|
111
|
-
full_docstring (str): The full docstring of a function.
|
112
|
-
|
113
|
-
Returns:
|
114
|
-
str: A compact version of the docstring.
|
115
|
-
"""
|
116
|
-
lines = full_docstring.split("\n")
|
117
|
-
# TODO make it also for different docstring styles, now it is only for numpy style
|
118
|
-
cutoff_indices = [
|
119
|
-
i
|
120
|
-
for i, line in enumerate(lines)
|
121
|
-
if line.strip().lower() in ["parameters", "returns", "examples", "see also", "warnings"]
|
122
|
-
]
|
123
|
-
|
124
|
-
if cutoff_indices:
|
125
|
-
lines = lines[: cutoff_indices[0]]
|
126
|
-
|
127
|
-
compact_docstring = "\n".join(lines).strip()
|
128
|
-
return compact_docstring
|
129
|
-
|
130
|
-
|
131
|
-
class ScriptRunnerThread(QThread):
|
132
|
-
"""Initializes the thread for running a Python script.
|
133
|
-
|
134
|
-
Args:
|
135
|
-
script (str): The script to be executed.
|
136
|
-
"""
|
137
|
-
|
138
|
-
outputSignal = Signal(str)
|
139
|
-
|
140
|
-
def __init__(self, script):
|
141
|
-
super().__init__()
|
142
|
-
self.script = script
|
143
|
-
|
144
|
-
def run(self):
|
145
|
-
"""Executes the script in a subprocess and emits output through a signal. Overrides QThread.run."""
|
146
|
-
process = subprocess.Popen(
|
147
|
-
["python", "-u", "-c", self.script],
|
148
|
-
stdout=subprocess.PIPE,
|
149
|
-
stderr=subprocess.PIPE,
|
150
|
-
bufsize=1,
|
151
|
-
universal_newlines=True,
|
152
|
-
text=True,
|
153
|
-
)
|
154
|
-
|
155
|
-
while True:
|
156
|
-
output = process.stdout.readline()
|
157
|
-
if output == "" and process.poll() is not None:
|
158
|
-
break
|
159
|
-
if output:
|
160
|
-
self.outputSignal.emit(output)
|
161
|
-
error = process.communicate()[1]
|
162
|
-
if error:
|
163
|
-
self.outputSignal.emit(error)
|
164
|
-
|
165
|
-
|
166
|
-
class BECEditor(QWidget):
|
167
|
-
"""Initializes the BEC Editor widget.
|
168
|
-
|
169
|
-
Args:
|
170
|
-
toolbar_enabled (bool, optional): Determines if the toolbar should be enabled. Defaults to True.
|
171
|
-
"""
|
172
|
-
|
173
|
-
def __init__(
|
174
|
-
self, toolbar_enabled=True, jupyter_terminal_enabled=False, docstring_tooltip=False
|
175
|
-
):
|
176
|
-
super().__init__()
|
177
|
-
|
178
|
-
self.script_runner_thread = None
|
179
|
-
self.file_path = None
|
180
|
-
self.docstring_tooltip = docstring_tooltip
|
181
|
-
self.jupyter_terminal_enabled = jupyter_terminal_enabled
|
182
|
-
# TODO just temporary solution, could be extended to other languages
|
183
|
-
self.is_python_file = True
|
184
|
-
|
185
|
-
# Initialize the editor and terminal
|
186
|
-
self.editor = QsciScintilla()
|
187
|
-
if self.jupyter_terminal_enabled:
|
188
|
-
self.terminal = self.make_jupyter_widget_with_kernel()
|
189
|
-
else:
|
190
|
-
self.terminal = QTextEdit()
|
191
|
-
self.terminal.setReadOnly(True)
|
192
|
-
|
193
|
-
# Layout
|
194
|
-
self.layout = QVBoxLayout()
|
195
|
-
|
196
|
-
# Initialize and add the toolbar if enabled
|
197
|
-
if toolbar_enabled:
|
198
|
-
self.toolbar = ModularToolBar(self)
|
199
|
-
self.layout.addWidget(self.toolbar)
|
200
|
-
|
201
|
-
# Initialize the splitter
|
202
|
-
self.splitter = QSplitter(Qt.Orientation.Vertical, self)
|
203
|
-
self.splitter.addWidget(self.editor)
|
204
|
-
self.splitter.addWidget(self.terminal)
|
205
|
-
self.splitter.setSizes([400, 200])
|
206
|
-
|
207
|
-
# Add Splitter to layout
|
208
|
-
self.layout.addWidget(self.splitter)
|
209
|
-
self.setLayout(self.layout)
|
210
|
-
|
211
|
-
self.setup_editor()
|
212
|
-
|
213
|
-
def setup_editor(self):
|
214
|
-
"""Sets up the editor with necessary configurations like lexer, auto indentation, and line numbers."""
|
215
|
-
# Set the lexer for Python
|
216
|
-
self.lexer = QsciLexerPython()
|
217
|
-
self.editor.setLexer(self.lexer)
|
218
|
-
|
219
|
-
# Enable auto indentation and competition within the editor
|
220
|
-
self.editor.setAutoIndent(True)
|
221
|
-
self.editor.setIndentationsUseTabs(False)
|
222
|
-
self.editor.setIndentationWidth(4)
|
223
|
-
self.editor.setAutoCompletionSource(QsciScintilla.AutoCompletionSource.AcsAll)
|
224
|
-
self.editor.setAutoCompletionThreshold(1)
|
225
|
-
|
226
|
-
# Autocomplete for python file
|
227
|
-
# Connect cursor position change signal for autocompletion
|
228
|
-
self.editor.cursorPositionChanged.connect(self.on_cursor_position_changed)
|
229
|
-
|
230
|
-
# if self.is_python_file: #TODO can be changed depending on supported languages
|
231
|
-
self.__api = QsciAPIs(self.lexer)
|
232
|
-
self.auto_completer = AutoCompleter(
|
233
|
-
self.editor.text(), self.__api, enable_docstring=self.docstring_tooltip
|
234
|
-
)
|
235
|
-
self.auto_completer.finished.connect(self.loaded_autocomplete)
|
236
|
-
|
237
|
-
# Enable line numbers in the margin
|
238
|
-
self.editor.setMarginType(0, QsciScintilla.MarginType.NumberMargin)
|
239
|
-
self.editor.setMarginWidth(0, "0000") # Adjust the width as needed
|
240
|
-
|
241
|
-
# Additional UI elements like menu for load/save can be added here
|
242
|
-
self.set_editor_style()
|
243
|
-
|
244
|
-
@staticmethod
|
245
|
-
def make_jupyter_widget_with_kernel() -> object:
|
246
|
-
"""Start a kernel, connect to it, and create a RichJupyterWidget to use it"""
|
247
|
-
kernel_manager = QtKernelManager(kernel_name="python3")
|
248
|
-
kernel_manager.start_kernel()
|
249
|
-
|
250
|
-
kernel_client = kernel_manager.client()
|
251
|
-
kernel_client.start_channels()
|
252
|
-
|
253
|
-
jupyter_widget = RichJupyterWidget()
|
254
|
-
jupyter_widget.set_default_style("linux")
|
255
|
-
jupyter_widget.kernel_manager = kernel_manager
|
256
|
-
jupyter_widget.kernel_client = kernel_client
|
257
|
-
return jupyter_widget
|
258
|
-
|
259
|
-
def show_call_tip(self, position):
|
260
|
-
"""Shows a call tip at the given position in the editor.
|
261
|
-
|
262
|
-
Args:
|
263
|
-
position (int): The position in the editor where the call tip should be shown.
|
264
|
-
"""
|
265
|
-
line, index = self.editor.lineIndexFromPosition(position)
|
266
|
-
signature = self.auto_completer.get_function_signature(line + 1, index, self.editor.text())
|
267
|
-
if signature:
|
268
|
-
self.editor.showUserList(1, [signature])
|
269
|
-
|
270
|
-
def on_cursor_position_changed(self, line, index):
|
271
|
-
"""Handles the event of cursor position change in the editor.
|
272
|
-
|
273
|
-
Args:
|
274
|
-
line (int): The current line number where the cursor is.
|
275
|
-
index (int): The current column index where the cursor is.
|
276
|
-
"""
|
277
|
-
# if self.is_python_file: #TODO can be changed depending on supported languages
|
278
|
-
# Get completions
|
279
|
-
self.auto_completer.get_completions(line + 1, index, self.editor.text())
|
280
|
-
self.editor.autoCompleteFromAPIs()
|
281
|
-
|
282
|
-
# Show call tip - signature
|
283
|
-
position = self.editor.positionFromLineIndex(line, index)
|
284
|
-
self.show_call_tip(position)
|
285
|
-
|
286
|
-
def loaded_autocomplete(self):
|
287
|
-
"""Placeholder method for actions after autocompletion data is loaded."""
|
288
|
-
|
289
|
-
def set_editor_style(self):
|
290
|
-
"""Sets the style and color scheme for the editor."""
|
291
|
-
# Dracula Theme Colors
|
292
|
-
background_color = QColor("#282a36")
|
293
|
-
text_color = QColor("#f8f8f2")
|
294
|
-
keyword_color = QColor("#8be9fd")
|
295
|
-
string_color = QColor("#f1fa8c")
|
296
|
-
comment_color = QColor("#6272a4")
|
297
|
-
class_function_color = QColor("#50fa7b")
|
298
|
-
|
299
|
-
# Set Font
|
300
|
-
font = QFont()
|
301
|
-
font.setFamily("Consolas")
|
302
|
-
font.setPointSize(10)
|
303
|
-
self.editor.setFont(font)
|
304
|
-
self.editor.setMarginsFont(font)
|
305
|
-
|
306
|
-
# Set Editor Colors
|
307
|
-
self.editor.setMarginsBackgroundColor(background_color)
|
308
|
-
self.editor.setMarginsForegroundColor(text_color)
|
309
|
-
self.editor.setCaretForegroundColor(text_color)
|
310
|
-
self.editor.setCaretLineBackgroundColor(QColor("#44475a"))
|
311
|
-
self.editor.setPaper(background_color) # Set the background color for the entire paper
|
312
|
-
self.editor.setColor(text_color)
|
313
|
-
|
314
|
-
# Set editor
|
315
|
-
# Syntax Highlighting Colors
|
316
|
-
lexer = self.editor.lexer()
|
317
|
-
if lexer:
|
318
|
-
lexer.setDefaultPaper(background_color) # Set the background color for the text area
|
319
|
-
lexer.setDefaultColor(text_color)
|
320
|
-
lexer.setColor(keyword_color, QsciLexerPython.Keyword)
|
321
|
-
lexer.setColor(string_color, QsciLexerPython.DoubleQuotedString)
|
322
|
-
lexer.setColor(string_color, QsciLexerPython.SingleQuotedString)
|
323
|
-
lexer.setColor(comment_color, QsciLexerPython.Comment)
|
324
|
-
lexer.setColor(class_function_color, QsciLexerPython.ClassName)
|
325
|
-
lexer.setColor(class_function_color, QsciLexerPython.FunctionMethodName)
|
326
|
-
|
327
|
-
# Set the style for all text to have a transparent background
|
328
|
-
# TODO find better way how to do it!
|
329
|
-
for style in range(
|
330
|
-
128
|
331
|
-
): # QsciScintilla supports 128 styles by default, this set all to transparent background
|
332
|
-
self.lexer.setPaper(background_color, style)
|
333
|
-
|
334
|
-
def run_script(self):
|
335
|
-
"""Runs the current script in the editor."""
|
336
|
-
if self.jupyter_terminal_enabled:
|
337
|
-
script = self.editor.text()
|
338
|
-
self.terminal.execute(script)
|
339
|
-
|
340
|
-
else:
|
341
|
-
script = self.editor.text()
|
342
|
-
self.script_runner_thread = ScriptRunnerThread(script)
|
343
|
-
self.script_runner_thread.outputSignal.connect(self.update_terminal)
|
344
|
-
self.script_runner_thread.start()
|
345
|
-
|
346
|
-
def update_terminal(self, text):
|
347
|
-
"""Updates the terminal with new text.
|
348
|
-
|
349
|
-
Args:
|
350
|
-
text (str): The text to be appended to the terminal.
|
351
|
-
"""
|
352
|
-
self.terminal.append(text)
|
353
|
-
|
354
|
-
def enable_docstring_tooltip(self):
|
355
|
-
"""Enables the docstring tooltip."""
|
356
|
-
self.docstring_tooltip = True
|
357
|
-
self.auto_completer.enable_docstring = True
|
358
|
-
|
359
|
-
def open_file(self):
|
360
|
-
"""Opens a file dialog for selecting and opening a Python file in the editor."""
|
361
|
-
options = QFileDialog.Options()
|
362
|
-
options |= QFileDialog.DontUseNativeDialog
|
363
|
-
file_path, _ = QFileDialog.getOpenFileName(
|
364
|
-
self, "Open file", "", "Python files (*.py);;All Files (*)", options=options
|
365
|
-
)
|
366
|
-
|
367
|
-
if not file_path:
|
368
|
-
return
|
369
|
-
try:
|
370
|
-
with open(file_path, "r") as file:
|
371
|
-
text = file.read()
|
372
|
-
self.editor.setText(text)
|
373
|
-
except FileNotFoundError:
|
374
|
-
print(f"The file {file_path} was not found.")
|
375
|
-
except Exception as e:
|
376
|
-
print(f"An error occurred while opening the file {file_path}: {e}")
|
377
|
-
|
378
|
-
def save_file(self):
|
379
|
-
"""Opens a save file dialog for saving the current script in the editor."""
|
380
|
-
options = QFileDialog.Options()
|
381
|
-
options |= QFileDialog.DontUseNativeDialog
|
382
|
-
file_path, _ = QFileDialog.getSaveFileName(
|
383
|
-
self, "Save file", "", "Python files (*.py);;All Files (*)", options=options
|
384
|
-
)
|
385
|
-
|
386
|
-
if not file_path:
|
387
|
-
return
|
388
|
-
try:
|
389
|
-
if not file_path.endswith(".py"):
|
390
|
-
file_path += ".py"
|
391
|
-
|
392
|
-
with open(file_path, "w") as file:
|
393
|
-
text = self.editor.text()
|
394
|
-
file.write(text)
|
395
|
-
print(f"File saved to {file_path}")
|
396
|
-
except Exception as e:
|
397
|
-
print(f"An error occurred while saving the file to {file_path}: {e}")
|
398
|
-
|
399
|
-
|
400
|
-
if __name__ == "__main__": # pragma: no cover
|
401
|
-
app = QApplication([])
|
402
|
-
qdarktheme.setup_theme("auto")
|
403
|
-
|
404
|
-
mainWin = BECEditor(jupyter_terminal_enabled=True)
|
405
|
-
|
406
|
-
mainWin.show()
|
407
|
-
app.exec()
|
tests/unit_tests/test_editor.py
DELETED
@@ -1,170 +0,0 @@
|
|
1
|
-
# pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring
|
2
|
-
|
3
|
-
import os
|
4
|
-
import tempfile
|
5
|
-
from unittest.mock import MagicMock, mock_open, patch
|
6
|
-
|
7
|
-
import pytest
|
8
|
-
from qtpy.Qsci import QsciScintilla
|
9
|
-
from qtpy.QtWidgets import QTextEdit
|
10
|
-
|
11
|
-
from bec_widgets.widgets.editor.editor import AutoCompleter, BECEditor
|
12
|
-
|
13
|
-
|
14
|
-
@pytest.fixture(scope="function")
|
15
|
-
def editor(qtbot, docstring_tooltip=False):
|
16
|
-
"""Helper function to set up the BECEditor widget."""
|
17
|
-
widget = BECEditor(toolbar_enabled=True, docstring_tooltip=docstring_tooltip)
|
18
|
-
qtbot.addWidget(widget)
|
19
|
-
qtbot.waitExposed(widget)
|
20
|
-
yield widget
|
21
|
-
|
22
|
-
|
23
|
-
def find_action_by_text(toolbar, text):
|
24
|
-
"""Helper function to find an action in the toolbar by its text."""
|
25
|
-
for action in toolbar.actions():
|
26
|
-
if action.text() == text:
|
27
|
-
return action
|
28
|
-
return None
|
29
|
-
|
30
|
-
|
31
|
-
def test_bec_editor_initialization(editor):
|
32
|
-
"""Test if the BECEditor widget is initialized correctly."""
|
33
|
-
assert isinstance(editor.editor, QsciScintilla)
|
34
|
-
assert isinstance(editor.terminal, QTextEdit)
|
35
|
-
assert isinstance(editor.auto_completer, AutoCompleter)
|
36
|
-
|
37
|
-
|
38
|
-
@patch("bec_widgets.widgets.editor.editor.Script") # Mock the Script class from jedi
|
39
|
-
def test_autocompleter_suggestions(mock_script, editor, qtbot):
|
40
|
-
"""Test if the autocompleter provides correct suggestions based on input."""
|
41
|
-
# Set up mock return values for the Script.complete method
|
42
|
-
mock_completion = MagicMock()
|
43
|
-
mock_completion.name = "mocked_method"
|
44
|
-
mock_script.return_value.complete.return_value = [mock_completion]
|
45
|
-
|
46
|
-
# Simulate user input in the editor
|
47
|
-
test_code = "print("
|
48
|
-
editor.editor.setText(test_code)
|
49
|
-
line, index = editor.editor.getCursorPosition()
|
50
|
-
|
51
|
-
# Trigger autocomplete
|
52
|
-
editor.auto_completer.get_completions(line, index, test_code)
|
53
|
-
|
54
|
-
# Use qtbot to wait for the completion thread
|
55
|
-
qtbot.waitUntil(lambda: editor.auto_completer.completions is not None, timeout=1000)
|
56
|
-
|
57
|
-
# Check if the expected completion is in the autocompleter's suggestions
|
58
|
-
suggested_methods = [completion.name for completion in editor.auto_completer.completions]
|
59
|
-
assert "mocked_method" in suggested_methods
|
60
|
-
|
61
|
-
|
62
|
-
@patch("bec_widgets.widgets.editor.editor.Script") # Mock the Script class from jedi
|
63
|
-
@pytest.mark.parametrize(
|
64
|
-
"docstring_enabled, expected_signature",
|
65
|
-
[(True, "Mocked signature with docstring"), (False, "Mocked signature")],
|
66
|
-
)
|
67
|
-
def test_autocompleter_signature(mock_script, editor, docstring_enabled, expected_signature):
|
68
|
-
"""Test if the autocompleter provides correct function signature based on docstring setting."""
|
69
|
-
# Set docstring mode based on parameter
|
70
|
-
editor.docstring_tooltip = docstring_enabled
|
71
|
-
editor.auto_completer.enable_docstring = docstring_enabled
|
72
|
-
|
73
|
-
# Set up mock return values for the Script.get_signatures method
|
74
|
-
mock_signature = MagicMock()
|
75
|
-
if docstring_enabled:
|
76
|
-
mock_signature.docstring.return_value = expected_signature
|
77
|
-
else:
|
78
|
-
mock_signature.to_string.return_value = expected_signature
|
79
|
-
mock_script.return_value.get_signatures.return_value = [mock_signature]
|
80
|
-
|
81
|
-
# Simulate user input that would trigger a signature request
|
82
|
-
test_code = "print("
|
83
|
-
editor.editor.setText(test_code)
|
84
|
-
line, index = editor.editor.getCursorPosition()
|
85
|
-
|
86
|
-
# Trigger signature request
|
87
|
-
signature = editor.auto_completer.get_function_signature(line, index, test_code)
|
88
|
-
|
89
|
-
# Check if the expected signature is returned
|
90
|
-
assert signature == expected_signature
|
91
|
-
|
92
|
-
|
93
|
-
def test_open_file(editor):
|
94
|
-
"""Test open_file method of BECEditor."""
|
95
|
-
# Create a temporary file with some content
|
96
|
-
with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as temp_file:
|
97
|
-
temp_file.write(b"test file content")
|
98
|
-
|
99
|
-
# Mock user selecting the file in the dialog
|
100
|
-
with patch("qtpy.QtWidgets.QFileDialog.getOpenFileName", return_value=(temp_file.name, "")):
|
101
|
-
with patch("builtins.open", new_callable=mock_open, read_data="test file content"):
|
102
|
-
editor.open_file()
|
103
|
-
|
104
|
-
# Verify if the editor's text is set to the file content
|
105
|
-
assert editor.editor.text() == "test file content"
|
106
|
-
|
107
|
-
# Clean up by removing the temporary file
|
108
|
-
os.remove(temp_file.name)
|
109
|
-
|
110
|
-
|
111
|
-
def test_save_file(editor):
|
112
|
-
"""Test save_file method of BECEditor."""
|
113
|
-
# Set some text in the editor
|
114
|
-
editor.editor.setText("test save content")
|
115
|
-
|
116
|
-
# Mock user selecting the file in the dialog
|
117
|
-
with patch(
|
118
|
-
"qtpy.QtWidgets.QFileDialog.getSaveFileName", return_value=("/path/to/save/file.py", "")
|
119
|
-
):
|
120
|
-
with patch("builtins.open", new_callable=mock_open) as mock_file:
|
121
|
-
editor.save_file()
|
122
|
-
|
123
|
-
# Verify if the file was opened correctly for writing
|
124
|
-
mock_file.assert_called_with("/path/to/save/file.py", "w")
|
125
|
-
|
126
|
-
# Verify if the editor's text was written to the file
|
127
|
-
mock_file().write.assert_called_with("test save content")
|
128
|
-
|
129
|
-
|
130
|
-
def test_open_file_through_toolbar(editor):
|
131
|
-
"""Test the open_file method through the ModularToolBar."""
|
132
|
-
# Create a temporary file
|
133
|
-
with tempfile.NamedTemporaryFile(delete=False, suffix=".py") as temp_file:
|
134
|
-
temp_file.write(b"test file content")
|
135
|
-
|
136
|
-
# Find the open file action in the toolbar
|
137
|
-
open_action = find_action_by_text(editor.toolbar, "Open File")
|
138
|
-
assert open_action is not None, "Open File action should be found"
|
139
|
-
|
140
|
-
# Mock the file dialog and built-in open function
|
141
|
-
with patch("qtpy.QtWidgets.QFileDialog.getOpenFileName", return_value=(temp_file.name, "")):
|
142
|
-
with patch("builtins.open", new_callable=mock_open, read_data="test file content"):
|
143
|
-
open_action.trigger()
|
144
|
-
# Verify if the editor's text is set to the file content
|
145
|
-
assert editor.editor.text() == "test file content"
|
146
|
-
|
147
|
-
# Clean up
|
148
|
-
os.remove(temp_file.name)
|
149
|
-
|
150
|
-
|
151
|
-
def test_save_file_through_toolbar(editor):
|
152
|
-
"""Test the save_file method through the ModularToolBar."""
|
153
|
-
# Set some text in the editor
|
154
|
-
editor.editor.setText("test save content")
|
155
|
-
|
156
|
-
# Find the save file action in the toolbar
|
157
|
-
save_action = find_action_by_text(editor.toolbar, "Save File")
|
158
|
-
assert save_action is not None, "Save File action should be found"
|
159
|
-
|
160
|
-
# Mock the file dialog and built-in open function
|
161
|
-
with patch(
|
162
|
-
"qtpy.QtWidgets.QFileDialog.getSaveFileName", return_value=("/path/to/save/file.py", "")
|
163
|
-
):
|
164
|
-
with patch("builtins.open", new_callable=mock_open) as mock_file:
|
165
|
-
save_action.trigger()
|
166
|
-
# Verify if the file was opened correctly for writing
|
167
|
-
mock_file.assert_called_with("/path/to/save/file.py", "w")
|
168
|
-
|
169
|
-
# Verify if the editor's text was written to the file
|
170
|
-
mock_file().write.assert_called_with("test save content")
|
File without changes
|
File without changes
|
File without changes
|