easycoder 251104.1__py2.py3-none-any.whl → 260108.1__py2.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.
Potentially problematic release.
This version of easycoder might be problematic. Click here for more details.
- easycoder/__init__.py +6 -3
- easycoder/debugger/__init__.py +5 -0
- easycoder/debugger/ec_dbg_value_display copy.py +195 -0
- easycoder/debugger/ec_dbg_value_display.py +24 -0
- easycoder/debugger/ec_dbg_watch_list copy.py +219 -0
- easycoder/debugger/ec_dbg_watchlist.py +293 -0
- easycoder/debugger/ec_debug.py +1025 -0
- easycoder/ec_border.py +15 -11
- easycoder/ec_classes.py +494 -7
- easycoder/ec_compiler.py +82 -44
- easycoder/ec_condition.py +1 -1
- easycoder/ec_core.py +1043 -1089
- easycoder/ec_gclasses.py +236 -0
- easycoder/ec_graphics.py +1683 -0
- easycoder/ec_handler.py +18 -13
- easycoder/ec_keyboard.py +50 -50
- easycoder/ec_mqtt.py +249 -0
- easycoder/ec_program.py +313 -152
- easycoder/ec_psutil.py +48 -0
- easycoder/ec_timestamp.py +2 -1
- easycoder/ec_value.py +65 -47
- easycoder/icons/exit.png +0 -0
- easycoder/icons/run.png +0 -0
- easycoder/icons/step.png +0 -0
- easycoder/icons/stop.png +0 -0
- easycoder/pre/README.md +3 -0
- easycoder/pre/__init__.py +17 -0
- easycoder/pre/debugger/__init__.py +5 -0
- easycoder/pre/debugger/ec_dbg_value_display copy.py +195 -0
- easycoder/pre/debugger/ec_dbg_value_display.py +24 -0
- easycoder/pre/debugger/ec_dbg_watch_list copy.py +219 -0
- easycoder/pre/debugger/ec_dbg_watchlist.py +293 -0
- easycoder/pre/debugger/ec_debug.py +1014 -0
- easycoder/pre/ec_border.py +67 -0
- easycoder/pre/ec_classes.py +470 -0
- easycoder/pre/ec_compiler.py +291 -0
- easycoder/pre/ec_condition.py +27 -0
- easycoder/pre/ec_core.py +2772 -0
- easycoder/pre/ec_gclasses.py +230 -0
- easycoder/{ec_pyside.py → pre/ec_graphics.py} +631 -494
- easycoder/pre/ec_handler.py +79 -0
- easycoder/pre/ec_keyboard.py +439 -0
- easycoder/pre/ec_program.py +557 -0
- easycoder/pre/ec_psutil.py +48 -0
- easycoder/pre/ec_timestamp.py +11 -0
- easycoder/pre/ec_value.py +124 -0
- easycoder/pre/icons/close.png +0 -0
- easycoder/pre/icons/exit.png +0 -0
- easycoder/pre/icons/run.png +0 -0
- easycoder/pre/icons/step.png +0 -0
- easycoder/pre/icons/stop.png +0 -0
- easycoder/pre/icons/tick.png +0 -0
- {easycoder-251104.1.dist-info → easycoder-260108.1.dist-info}/METADATA +11 -1
- easycoder-260108.1.dist-info/RECORD +59 -0
- easycoder-251104.1.dist-info/RECORD +0 -19
- /easycoder/{close.png → icons/close.png} +0 -0
- /easycoder/{tick.png → icons/tick.png} +0 -0
- {easycoder-251104.1.dist-info → easycoder-260108.1.dist-info}/WHEEL +0 -0
- {easycoder-251104.1.dist-info → easycoder-260108.1.dist-info}/entry_points.txt +0 -0
- {easycoder-251104.1.dist-info → easycoder-260108.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from PySide6.QtWidgets import QWidget
|
|
3
|
+
from PySide6.QtGui import QPixmap, QPainter
|
|
4
|
+
from PySide6.QtCore import Qt, Signal, QRect
|
|
5
|
+
|
|
6
|
+
class Border(QWidget):
|
|
7
|
+
tickClicked = Signal()
|
|
8
|
+
closeClicked = Signal()
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super().__init__()
|
|
12
|
+
self._size = 40
|
|
13
|
+
self.setFixedHeight(self._size)
|
|
14
|
+
self._drag_active = False
|
|
15
|
+
self._drag_start_pos = None
|
|
16
|
+
self._tick: QPixmap = QPixmap()
|
|
17
|
+
self._close_icon: QPixmap = QPixmap()
|
|
18
|
+
|
|
19
|
+
def paintEvent(self, event):
|
|
20
|
+
painter = QPainter(self)
|
|
21
|
+
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
22
|
+
# Draw the tick icon
|
|
23
|
+
self._tick = QPixmap(f'{os.path.dirname(os.path.abspath(__file__))}/icons/tick.png').scaled(
|
|
24
|
+
self._size, self._size, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
|
|
25
|
+
x = 0
|
|
26
|
+
y = 0
|
|
27
|
+
painter.drawPixmap(x, y, self._tick)
|
|
28
|
+
# Draw the close icon
|
|
29
|
+
self._close_icon = QPixmap(f'{os.path.dirname(os.path.abspath(__file__))}/icons/close.png').scaled(
|
|
30
|
+
self._size, self._size, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
|
|
31
|
+
x = self.width() - self._close_icon.width()
|
|
32
|
+
y = 0
|
|
33
|
+
painter.drawPixmap(x, y, self._close_icon)
|
|
34
|
+
|
|
35
|
+
def mousePressEvent(self, event):
|
|
36
|
+
# Tick icon
|
|
37
|
+
x = 0
|
|
38
|
+
y = 0
|
|
39
|
+
tickRect = self._tick.rect().translated(x, y)
|
|
40
|
+
# Close icon
|
|
41
|
+
x = self.width() - self._close_icon.width()
|
|
42
|
+
y = 0
|
|
43
|
+
closeRect = self._close_icon.rect().translated(x, y)
|
|
44
|
+
if tickRect.contains(event.pos()):
|
|
45
|
+
self.tickClicked.emit()
|
|
46
|
+
if closeRect.contains(event.pos()):
|
|
47
|
+
self.closeClicked.emit()
|
|
48
|
+
elif QRect(0, 0, self.width(), self.height()).contains(event.pos()):
|
|
49
|
+
if hasattr(self.window().windowHandle(), 'startSystemMove'):
|
|
50
|
+
self.window().windowHandle().startSystemMove()
|
|
51
|
+
else:
|
|
52
|
+
self._drag_active = True
|
|
53
|
+
self._drag_start_pos = event.globalPosition().toPoint()
|
|
54
|
+
self._dialog_start_pos = self.window().pos()
|
|
55
|
+
else:
|
|
56
|
+
super().mousePressEvent(event)
|
|
57
|
+
|
|
58
|
+
def mouseMoveEvent(self, event):
|
|
59
|
+
if self._drag_active:
|
|
60
|
+
delta = event.globalPosition().toPoint() - self._drag_start_pos
|
|
61
|
+
self.window().move(self._dialog_start_pos + delta)
|
|
62
|
+
else:
|
|
63
|
+
super().mouseMoveEvent(event)
|
|
64
|
+
|
|
65
|
+
def mouseReleaseEvent(self, event):
|
|
66
|
+
self._drag_active = False
|
|
67
|
+
super().mouseReleaseEvent(event)
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import sys, paramiko, json
|
|
2
|
+
from typing import Optional, Any
|
|
3
|
+
|
|
4
|
+
class FatalError(BaseException):
|
|
5
|
+
def __init__(self, compiler, message):
|
|
6
|
+
compiler.showWarnings()
|
|
7
|
+
lino = compiler.tokens[compiler.index].lino
|
|
8
|
+
script = compiler.script.lines[lino].strip()
|
|
9
|
+
print(f'Compile error in {compiler.program.name} at line {lino + 1} ({script}):\n-> {message}')
|
|
10
|
+
sys.exit()
|
|
11
|
+
|
|
12
|
+
class NoValueError(FatalError):
|
|
13
|
+
def __init__(self, compiler, record):
|
|
14
|
+
super().__init__(compiler, f'Variable {record["name"]} does not hold a value')
|
|
15
|
+
|
|
16
|
+
class RuntimeAssertionError:
|
|
17
|
+
def __init__(self, program, msg=None):
|
|
18
|
+
code = program.code[program.pc]
|
|
19
|
+
lino = code['lino']
|
|
20
|
+
message = f'Assertion Error in {program.name} at line {lino + 1}'
|
|
21
|
+
if msg != None:
|
|
22
|
+
message += f': {msg}'
|
|
23
|
+
print(message)
|
|
24
|
+
sys.exit()
|
|
25
|
+
|
|
26
|
+
class RuntimeError(BaseException):
|
|
27
|
+
def __init__(self, program, message):
|
|
28
|
+
if program == None:
|
|
29
|
+
sys.exit(f'Runtime Error: {message}')
|
|
30
|
+
else:
|
|
31
|
+
code = program.code[program.pc]
|
|
32
|
+
lino = code['lino']
|
|
33
|
+
script = program.script.lines[lino].strip()
|
|
34
|
+
print(f'Runtime Error in {program.name} at line {lino + 1} ({script}):\n-> {message}')
|
|
35
|
+
sys.exit()
|
|
36
|
+
|
|
37
|
+
class NoValueRuntimeError(RuntimeError):
|
|
38
|
+
def __init__(self, program, record):
|
|
39
|
+
super().__init__(program, 'Variable {record["name"]} does not hold a value')
|
|
40
|
+
|
|
41
|
+
class RuntimeWarning:
|
|
42
|
+
def __init__(self, program, message):
|
|
43
|
+
if program == None:
|
|
44
|
+
print(f'Runtime Warning: {message}')
|
|
45
|
+
else:
|
|
46
|
+
code = program.code[program.pc]
|
|
47
|
+
lino = code['lino']
|
|
48
|
+
script = program.script.lines[lino].strip()
|
|
49
|
+
print(f'Runtime Warning in {program.name} at line {lino + 1} ({script}): {message}')
|
|
50
|
+
|
|
51
|
+
class Script:
|
|
52
|
+
def __init__(self, source):
|
|
53
|
+
self.lines = source.splitlines()
|
|
54
|
+
self.tokens = []
|
|
55
|
+
|
|
56
|
+
class Token:
|
|
57
|
+
def __init__(self, lino, token):
|
|
58
|
+
self.lino = lino
|
|
59
|
+
self.token = token
|
|
60
|
+
|
|
61
|
+
###############################################################################
|
|
62
|
+
# This is the set of generic EasyCoder objects (values and variables)
|
|
63
|
+
|
|
64
|
+
###############################################################################
|
|
65
|
+
# A multipurpose value object. Holds a single value, with domain and type information
|
|
66
|
+
class ECValue():
|
|
67
|
+
def __init__(self, domain: Optional[str] = 'core', type: Optional[str] = None,
|
|
68
|
+
content: Any = None, name: Optional[str] = None):
|
|
69
|
+
object.__setattr__(self, 'domain', domain)
|
|
70
|
+
object.__setattr__(self, 'type', type)
|
|
71
|
+
object.__setattr__(self, 'content', content)
|
|
72
|
+
object.__setattr__(self, 'name', name)
|
|
73
|
+
object.__setattr__(self, 'properties', {})
|
|
74
|
+
object.__setattr__(self, 'locked', False)
|
|
75
|
+
object.__setattr__(self, '_attrs', {}) # Store dynamic attributes
|
|
76
|
+
|
|
77
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
78
|
+
"""Allow setting any attribute dynamically."""
|
|
79
|
+
if name in ('domain', 'type', 'content', 'name', 'properties', 'locked', '_attrs'):
|
|
80
|
+
object.__setattr__(self, name, value)
|
|
81
|
+
else:
|
|
82
|
+
# Store dynamic attributes in _attrs dict
|
|
83
|
+
self._attrs[name] = value
|
|
84
|
+
|
|
85
|
+
def __getattr__(self, name: str) -> Any:
|
|
86
|
+
"""Retrieve dynamic attributes or return None if not found."""
|
|
87
|
+
if name == '_attrs':
|
|
88
|
+
return object.__getattribute__(self, '_attrs')
|
|
89
|
+
return self._attrs.get(name)
|
|
90
|
+
|
|
91
|
+
def setDomain(self, domain):
|
|
92
|
+
self.domain = domain
|
|
93
|
+
|
|
94
|
+
def getDomain(self):
|
|
95
|
+
return self.domain
|
|
96
|
+
|
|
97
|
+
def setType(self, type):
|
|
98
|
+
self.type = type
|
|
99
|
+
|
|
100
|
+
def getType(self):
|
|
101
|
+
return self.type
|
|
102
|
+
|
|
103
|
+
def setContent(self, content):
|
|
104
|
+
self.content = content
|
|
105
|
+
|
|
106
|
+
def getContent(self):
|
|
107
|
+
return self.content
|
|
108
|
+
|
|
109
|
+
def setValue(self, type=None, content=None):
|
|
110
|
+
self.type = type
|
|
111
|
+
self.content = content
|
|
112
|
+
|
|
113
|
+
def setProperty(self, key, value):
|
|
114
|
+
self.properties[key] = value
|
|
115
|
+
|
|
116
|
+
def getProperty(self, key):
|
|
117
|
+
return self.properties.get(key, None)
|
|
118
|
+
|
|
119
|
+
def setName(self, name):
|
|
120
|
+
self.name = name
|
|
121
|
+
|
|
122
|
+
def getName(self):
|
|
123
|
+
return self.name
|
|
124
|
+
|
|
125
|
+
def lock(self):
|
|
126
|
+
self.locked = True
|
|
127
|
+
|
|
128
|
+
def isLocked(self):
|
|
129
|
+
return self.locked
|
|
130
|
+
|
|
131
|
+
###############################################################################
|
|
132
|
+
# The base class for all EasyCoder variable types
|
|
133
|
+
class ECObject():
|
|
134
|
+
def __init__(self):
|
|
135
|
+
self.locked: bool = False
|
|
136
|
+
self.elements: int = 0
|
|
137
|
+
self.index: Optional[int] = None
|
|
138
|
+
self.values: Optional[list] = None
|
|
139
|
+
self.name: Optional[str] = None
|
|
140
|
+
self.properties = {}
|
|
141
|
+
|
|
142
|
+
# Set the index for the variable
|
|
143
|
+
def setIndex(self, index: int) -> None:
|
|
144
|
+
self.index = index
|
|
145
|
+
|
|
146
|
+
# Get the index for the variable
|
|
147
|
+
def getIndex(self):
|
|
148
|
+
return self.index
|
|
149
|
+
|
|
150
|
+
# Lock the variable
|
|
151
|
+
def setLocked(self):
|
|
152
|
+
self.locked = True
|
|
153
|
+
|
|
154
|
+
# Check if the variable is locked
|
|
155
|
+
def isLocked(self):
|
|
156
|
+
return self.locked
|
|
157
|
+
|
|
158
|
+
# Set the value at the current index
|
|
159
|
+
def setValue(self, value):
|
|
160
|
+
if self.values is None:
|
|
161
|
+
self.index = 0
|
|
162
|
+
self.elements = 1
|
|
163
|
+
self.values = [None]
|
|
164
|
+
if isinstance(value, ECValue): value.setName(self.name)
|
|
165
|
+
self.values[self.index] = value # type: ignore
|
|
166
|
+
|
|
167
|
+
# Get the value at the current index
|
|
168
|
+
def getValue(self):
|
|
169
|
+
if self.values is None: return None
|
|
170
|
+
return self.values[self.index] # type: ignore
|
|
171
|
+
|
|
172
|
+
# Get all the values
|
|
173
|
+
def getValues(self):
|
|
174
|
+
return self.values
|
|
175
|
+
|
|
176
|
+
# Set the number of elements in the variable
|
|
177
|
+
def setElements(self, elements):
|
|
178
|
+
if self.elements == 0:
|
|
179
|
+
self.values = [None] * elements
|
|
180
|
+
self.elements = elements
|
|
181
|
+
self.index = 0
|
|
182
|
+
if elements == self.elements:
|
|
183
|
+
pass
|
|
184
|
+
elif elements > self.elements:
|
|
185
|
+
self.values.extend([None] * (elements - self.elements)) # pyright: ignore[reportOptionalMemberAccess]
|
|
186
|
+
else:
|
|
187
|
+
del self.values[elements:] # pyright: ignore[reportOptionalSubscript]
|
|
188
|
+
self.index = 0
|
|
189
|
+
self.elements = elements
|
|
190
|
+
|
|
191
|
+
# Get the number of elements in the variable
|
|
192
|
+
def getElements(self):
|
|
193
|
+
return self.elements
|
|
194
|
+
|
|
195
|
+
# Check if the object has a runtime value. Default is False
|
|
196
|
+
def hasRuntimeValue(self):
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
# Check if the object is mutable. Default is False
|
|
200
|
+
def isMutable(self):
|
|
201
|
+
return False
|
|
202
|
+
|
|
203
|
+
# Check if the object is clearable
|
|
204
|
+
def isClearable(self):
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
# Get the content of the value at the current index
|
|
208
|
+
def getContent(self):
|
|
209
|
+
if not self.hasRuntimeValue(): return None
|
|
210
|
+
v = self.getValue()
|
|
211
|
+
if v is None: return None
|
|
212
|
+
return v.getContent()
|
|
213
|
+
|
|
214
|
+
# Get the type of the value at the current index
|
|
215
|
+
def getType(self):
|
|
216
|
+
if not self.hasRuntimeValue(): return None
|
|
217
|
+
v = self.getValue()
|
|
218
|
+
if v is None: return None
|
|
219
|
+
return v.getType()
|
|
220
|
+
|
|
221
|
+
# Check if the object is empty. Default is True
|
|
222
|
+
def isEmpty(self):
|
|
223
|
+
return True
|
|
224
|
+
|
|
225
|
+
# Set the name of the object
|
|
226
|
+
def setName(self, name):
|
|
227
|
+
self.name = name
|
|
228
|
+
|
|
229
|
+
# Get the name of the object
|
|
230
|
+
def getName(self):
|
|
231
|
+
return self.name
|
|
232
|
+
|
|
233
|
+
# Set a specific property on the object
|
|
234
|
+
def setProperty(self, name, value):
|
|
235
|
+
self.properties[name] = value
|
|
236
|
+
|
|
237
|
+
# Check if the object has a specific property
|
|
238
|
+
def hasProperty(self, name):
|
|
239
|
+
return name in self.properties
|
|
240
|
+
|
|
241
|
+
# Get a specific property
|
|
242
|
+
def getProperty(self, name):
|
|
243
|
+
return self.properties[name]
|
|
244
|
+
|
|
245
|
+
###############################################################################
|
|
246
|
+
# A generic variable object that can hold a mutable value
|
|
247
|
+
class ECValueHolder(ECObject):
|
|
248
|
+
def __init__(self):
|
|
249
|
+
super().__init__()
|
|
250
|
+
|
|
251
|
+
# Set the content of the value at the current index
|
|
252
|
+
def setContent(self, content):
|
|
253
|
+
if self.values is None:
|
|
254
|
+
self.index = 0
|
|
255
|
+
self.elements = 1
|
|
256
|
+
self.values = [None]
|
|
257
|
+
self.values[self.index] = content # type: ignore
|
|
258
|
+
|
|
259
|
+
# Set the value to a given ECValue
|
|
260
|
+
def setValue(self, value):
|
|
261
|
+
if self.values is None:
|
|
262
|
+
self.index = 0
|
|
263
|
+
self.elements = 1
|
|
264
|
+
self.values = [None]
|
|
265
|
+
if self.index >= self.elements: raise RuntimeError(None, 'Index out of range') # type: ignore
|
|
266
|
+
self.values[self.index] = value # type: ignore
|
|
267
|
+
|
|
268
|
+
# Report if the object is clearable
|
|
269
|
+
def isClearable(self):
|
|
270
|
+
return True
|
|
271
|
+
|
|
272
|
+
# This object has a runtime value
|
|
273
|
+
def hasRuntimeValue(self):
|
|
274
|
+
return True
|
|
275
|
+
|
|
276
|
+
# This object is mutable.
|
|
277
|
+
def isMutable(self):
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
# Reset the object to empty state
|
|
281
|
+
def reset(self):
|
|
282
|
+
self.setValue(ECValue(content=None))
|
|
283
|
+
|
|
284
|
+
###############################################################################
|
|
285
|
+
# A string or int variable
|
|
286
|
+
class ECVariable(ECValueHolder):
|
|
287
|
+
def __init__(self):
|
|
288
|
+
super().__init__()
|
|
289
|
+
|
|
290
|
+
# Reset the object to an empty string
|
|
291
|
+
def reset(self):
|
|
292
|
+
self.setValue(ECValue(type=str, content=''))
|
|
293
|
+
|
|
294
|
+
# Set the value to a given ECValue
|
|
295
|
+
def setValue(self, value):
|
|
296
|
+
if value.getType() not in (str, int, float, bool):
|
|
297
|
+
raise RuntimeError(None, 'ECVariable can only hold str, int, float, or bool values') # type: ignore
|
|
298
|
+
super().setValue(value)
|
|
299
|
+
|
|
300
|
+
###############################################################################
|
|
301
|
+
# A dictionary variable
|
|
302
|
+
class ECDictionary(ECValueHolder):
|
|
303
|
+
def __init__(self):
|
|
304
|
+
super().__init__()
|
|
305
|
+
self.reset()
|
|
306
|
+
|
|
307
|
+
# Reset the object to empty state
|
|
308
|
+
def reset(self):
|
|
309
|
+
self.setValue(ECValue(content={}))
|
|
310
|
+
|
|
311
|
+
# Set the value to an ECValue
|
|
312
|
+
def setValue(self, value):
|
|
313
|
+
varType = value.getType()
|
|
314
|
+
if varType in (str, 'dict'):
|
|
315
|
+
content = value.getContent()
|
|
316
|
+
if varType == str:
|
|
317
|
+
try:
|
|
318
|
+
if content in ('', None): content = {}
|
|
319
|
+
else: content = json.loads(content)
|
|
320
|
+
except:
|
|
321
|
+
raise RuntimeError(None, 'ECDictionary string value is not valid JSON') # type: ignore
|
|
322
|
+
elif varType == None:
|
|
323
|
+
content = {}
|
|
324
|
+
else:
|
|
325
|
+
raise RuntimeError(None, 'ECDictionary can only hold dict values or None') # type: ignore
|
|
326
|
+
super().setValue(content)
|
|
327
|
+
|
|
328
|
+
def getValue(self):
|
|
329
|
+
return super().getValue()
|
|
330
|
+
|
|
331
|
+
# Set an entry in the dictionary
|
|
332
|
+
def setEntry(self, key, value):
|
|
333
|
+
content = self.getValue()
|
|
334
|
+
if content is None:
|
|
335
|
+
return
|
|
336
|
+
if isinstance(value, str):
|
|
337
|
+
try:
|
|
338
|
+
value = json.loads(value)
|
|
339
|
+
except Exception:
|
|
340
|
+
pass
|
|
341
|
+
content[key] = value # type: ignore
|
|
342
|
+
|
|
343
|
+
# Test if an entry exists in the dictionary
|
|
344
|
+
def hasEntry(self, key):
|
|
345
|
+
content = self.getValue()
|
|
346
|
+
if content is None:
|
|
347
|
+
return False
|
|
348
|
+
return key in content
|
|
349
|
+
|
|
350
|
+
# Get an entry from the dictionary
|
|
351
|
+
def getEntry(self, key):
|
|
352
|
+
content = self.getValue()
|
|
353
|
+
if content is None:
|
|
354
|
+
return None
|
|
355
|
+
return content.get(key, None)
|
|
356
|
+
|
|
357
|
+
# Delete an entry from the dictionary
|
|
358
|
+
def deleteEntry(self, key):
|
|
359
|
+
content = self.getValue()
|
|
360
|
+
if content is None:
|
|
361
|
+
return
|
|
362
|
+
if key in content:
|
|
363
|
+
del content[key]
|
|
364
|
+
|
|
365
|
+
# Get the keys of the dictionary
|
|
366
|
+
def keys(self):
|
|
367
|
+
content = self.getValue()
|
|
368
|
+
if content is None:
|
|
369
|
+
return []
|
|
370
|
+
return list(content.keys())
|
|
371
|
+
|
|
372
|
+
# Check if the dictionary is empty
|
|
373
|
+
def isEmpty(self):
|
|
374
|
+
return len(self.keys()) == 0
|
|
375
|
+
|
|
376
|
+
###############################################################################
|
|
377
|
+
# A list variable
|
|
378
|
+
class ECList(ECValueHolder):
|
|
379
|
+
def __init__(self):
|
|
380
|
+
super().__init__()
|
|
381
|
+
self.reset()
|
|
382
|
+
|
|
383
|
+
# Reset the object to empty state
|
|
384
|
+
def reset(self):
|
|
385
|
+
self.setValue(ECValue(content=[]))
|
|
386
|
+
|
|
387
|
+
# Set the value to a given ECValue
|
|
388
|
+
def setValue(self, value):
|
|
389
|
+
if value.getType() not in ('list', None):
|
|
390
|
+
raise RuntimeError(None, 'ECList can only hold list values or None') # type: ignore
|
|
391
|
+
super().setValue(value)
|
|
392
|
+
|
|
393
|
+
# Add an item to the list
|
|
394
|
+
def addItem(self, item):
|
|
395
|
+
content = self.getContent()
|
|
396
|
+
if content is None:
|
|
397
|
+
return
|
|
398
|
+
if isinstance(item, str):
|
|
399
|
+
try:
|
|
400
|
+
item = json.loads(item)
|
|
401
|
+
except Exception:
|
|
402
|
+
pass
|
|
403
|
+
content.append(item) # type: ignore
|
|
404
|
+
|
|
405
|
+
# Return the number of items in the list
|
|
406
|
+
def getItemCount(self):
|
|
407
|
+
content = self.getContent()
|
|
408
|
+
if content is None:
|
|
409
|
+
return 0
|
|
410
|
+
return len(content)
|
|
411
|
+
|
|
412
|
+
# Get an item from the list
|
|
413
|
+
def getItem(self, index):
|
|
414
|
+
content = self.getContent()
|
|
415
|
+
if content is None:
|
|
416
|
+
return None
|
|
417
|
+
return content[index]
|
|
418
|
+
|
|
419
|
+
# Check if the list is empty
|
|
420
|
+
def isEmpty(self):
|
|
421
|
+
return self.getItemCount() == 0
|
|
422
|
+
|
|
423
|
+
###############################################################################
|
|
424
|
+
# A file variable
|
|
425
|
+
class ECFile(ECObject):
|
|
426
|
+
def __init__(self):
|
|
427
|
+
super().__init__()
|
|
428
|
+
|
|
429
|
+
###############################################################################
|
|
430
|
+
# An SSH variable
|
|
431
|
+
class ECSSH(ECObject):
|
|
432
|
+
def __init__(self):
|
|
433
|
+
super().__init__()
|
|
434
|
+
|
|
435
|
+
# Set up the SSH connection
|
|
436
|
+
def setup(self, host=None, user=None, password=None):
|
|
437
|
+
ssh = paramiko.SSHClient()
|
|
438
|
+
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
439
|
+
try:
|
|
440
|
+
ssh.connect(host, username=user, password=password, timeout=10) # type: ignore
|
|
441
|
+
self.setValue(ssh)
|
|
442
|
+
self.sftp = ssh.open_sftp()
|
|
443
|
+
return True
|
|
444
|
+
except:
|
|
445
|
+
return False
|
|
446
|
+
|
|
447
|
+
# Get the SFTP client
|
|
448
|
+
def getSFTP(self):
|
|
449
|
+
return self.sftp
|
|
450
|
+
|
|
451
|
+
###############################################################################
|
|
452
|
+
# A stack variable
|
|
453
|
+
class ECStack(ECObject):
|
|
454
|
+
|
|
455
|
+
def __init__(self):
|
|
456
|
+
super().__init__()
|
|
457
|
+
self.values: Optional[list[list[Any]]] = None # List of stacks, each holding any type
|
|
458
|
+
|
|
459
|
+
def push(self, item: Any) -> None:
|
|
460
|
+
if self.values is None:
|
|
461
|
+
self.index = 0
|
|
462
|
+
self.elements = 1
|
|
463
|
+
self.values = [[]]
|
|
464
|
+
assert self.index is not None # Type narrowing: index is always set when values exists
|
|
465
|
+
self.values[self.index].append(item)
|
|
466
|
+
|
|
467
|
+
def pop(self) -> Any:
|
|
468
|
+
if self.values is None or self.index is None or self.values[self.index] is None or len(self.values[self.index]) == 0:
|
|
469
|
+
return None
|
|
470
|
+
return self.values[self.index].pop()
|