easycoder 251104.2__py2.py3-none-any.whl → 260110.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.

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