easycoder 251104.2__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.

Files changed (61) hide show
  1. easycoder/__init__.py +6 -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 +493 -11
  10. easycoder/ec_compiler.py +81 -44
  11. easycoder/ec_condition.py +1 -1
  12. easycoder/ec_core.py +1043 -1089
  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_mqtt.py +249 -0
  18. easycoder/ec_program.py +308 -156
  19. easycoder/ec_psutil.py +48 -0
  20. easycoder/ec_timestamp.py +2 -1
  21. easycoder/ec_value.py +65 -47
  22. easycoder/icons/exit.png +0 -0
  23. easycoder/icons/run.png +0 -0
  24. easycoder/icons/step.png +0 -0
  25. easycoder/icons/stop.png +0 -0
  26. easycoder/pre/README.md +3 -0
  27. easycoder/pre/__init__.py +17 -0
  28. easycoder/pre/debugger/__init__.py +5 -0
  29. easycoder/pre/debugger/ec_dbg_value_display copy.py +195 -0
  30. easycoder/pre/debugger/ec_dbg_value_display.py +24 -0
  31. easycoder/pre/debugger/ec_dbg_watch_list copy.py +219 -0
  32. easycoder/pre/debugger/ec_dbg_watchlist.py +293 -0
  33. easycoder/pre/debugger/ec_debug.py +1014 -0
  34. easycoder/pre/ec_border.py +67 -0
  35. easycoder/pre/ec_classes.py +470 -0
  36. easycoder/pre/ec_compiler.py +291 -0
  37. easycoder/pre/ec_condition.py +27 -0
  38. easycoder/pre/ec_core.py +2772 -0
  39. easycoder/pre/ec_gclasses.py +230 -0
  40. easycoder/{ec_pyside.py → pre/ec_graphics.py} +631 -496
  41. easycoder/pre/ec_handler.py +79 -0
  42. easycoder/pre/ec_keyboard.py +439 -0
  43. easycoder/pre/ec_program.py +557 -0
  44. easycoder/pre/ec_psutil.py +48 -0
  45. easycoder/pre/ec_timestamp.py +11 -0
  46. easycoder/pre/ec_value.py +124 -0
  47. easycoder/pre/icons/close.png +0 -0
  48. easycoder/pre/icons/exit.png +0 -0
  49. easycoder/pre/icons/run.png +0 -0
  50. easycoder/pre/icons/step.png +0 -0
  51. easycoder/pre/icons/stop.png +0 -0
  52. easycoder/pre/icons/tick.png +0 -0
  53. {easycoder-251104.2.dist-info → easycoder-260108.1.dist-info}/METADATA +11 -1
  54. easycoder-260108.1.dist-info/RECORD +59 -0
  55. easycoder/ec_debug.py +0 -464
  56. easycoder-251104.2.dist-info/RECORD +0 -20
  57. /easycoder/{close.png → icons/close.png} +0 -0
  58. /easycoder/{tick.png → icons/tick.png} +0 -0
  59. {easycoder-251104.2.dist-info → easycoder-260108.1.dist-info}/WHEEL +0 -0
  60. {easycoder-251104.2.dist-info → easycoder-260108.1.dist-info}/entry_points.txt +0 -0
  61. {easycoder-251104.2.dist-info → easycoder-260108.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,557 @@
1
+ import time, sys, json
2
+ from copy import deepcopy
3
+ from collections import deque
4
+ from .ec_classes import (
5
+ Script,
6
+ Token,
7
+ FatalError,
8
+ RuntimeError,
9
+ NoValueRuntimeError,
10
+ ECObject,
11
+ ECValue
12
+ )
13
+ from .ec_compiler import Compiler
14
+ from .ec_core import Core
15
+ import importlib
16
+ from importlib.metadata import version
17
+
18
+ # Flush the queue
19
+ def flush():
20
+ global queue
21
+ while len(queue):
22
+ item = queue.popleft()
23
+ item.program.flush(item.pc)
24
+
25
+ class Program:
26
+
27
+ def __init__(self, arg):
28
+ global queue
29
+ print(f'EasyCoder pre-Python 3.10, version {version("easycoder")}')
30
+ if len(arg) == 0:
31
+ print('No script supplied')
32
+ exit()
33
+ if arg in ['-v', '--version']: return
34
+ if arg[0:6] == 'debug ':
35
+ print('Debug mode requested')
36
+ self.scriptName = arg[6:]
37
+ self.debugging = True
38
+ else:
39
+ self.scriptName = arg
40
+ self.debugging = False
41
+
42
+ f = open(self.scriptName, 'r')
43
+ source = f.read()
44
+ f.close()
45
+ queue = deque()
46
+ self.domains = []
47
+ self.domainIndex = {}
48
+ self.name = '<anon>'
49
+ self.code = []
50
+ self.pc = 0
51
+ self.symbols = {}
52
+ self.onError = 0
53
+ self.debugStep = False
54
+ self.debugSkip = False
55
+ self.stack = []
56
+ self.script = Script(source)
57
+ self.compiler = Compiler(self)
58
+ self.object = ECObject()
59
+ self.value = self.compiler.value
60
+ self.condition = self.compiler.condition
61
+ self.graphics = None
62
+ self.psutil = None
63
+ self.useClass(Core)
64
+ self.ticker = 0
65
+ self.graphicsRunning = False
66
+ self.debugger = None
67
+ self.running = False
68
+ self.parent = None
69
+ self.message = None
70
+ self.onMessagePC = 0
71
+ self.breakpoint = False
72
+
73
+ # This is called at 10msec intervals by the GUI code
74
+ def flushCB(self):
75
+ self.ticker += 1
76
+ # if self.ticker % 1000 == 0: print(f'GUI Tick {self.ticker}')
77
+ flush()
78
+
79
+ def start(self, parent=None, module = None, exports=[]):
80
+ self.parent = parent
81
+ self.exports = exports
82
+ if self.debugging: self.useGraphics()
83
+ if module != None:
84
+ module['child'] = self
85
+ startCompile = time.time()
86
+ self.tokenise(self.script)
87
+ if self.compiler.compileFromStart():
88
+ finishCompile = time.time()
89
+ s = len(self.script.lines)
90
+ t = len(self.script.tokens)
91
+ print(f'Compiled {self.name}: {s} lines ({t} tokens) in ' +
92
+ f'{round((finishCompile - startCompile) * 1000)} ms')
93
+ for name in self.symbols.keys():
94
+ record = self.code[self.symbols[name]]
95
+ if name[-1] != ':' and not 'used' in record:
96
+ print(f'Variable "{name}" not used')
97
+ else:
98
+ print(f'Run {self.name}')
99
+ self.run(0)
100
+ else:
101
+ self.compiler.showWarnings()
102
+
103
+ # If this is the main script and there's no graphics, run a main loop
104
+ if parent == None:
105
+ while not self.graphicsRunning:
106
+ if self.running == True:
107
+ flush()
108
+ time.sleep(0.01)
109
+ else:
110
+ break
111
+
112
+ # Use the graphics module
113
+ def useGraphics(self):
114
+ if self.graphics == None:
115
+ print('Loading graphics module')
116
+ from .ec_graphics import Graphics
117
+ self.graphics = Graphics
118
+ self.useClass(Graphics)
119
+ return True
120
+
121
+ # Use the psutil module
122
+ def usePSUtil(self):
123
+ if self.psutil == None:
124
+ print('Loading psutil module')
125
+ from .ec_psutil import PSUtil
126
+ self.psutil = PSUtil
127
+ self.useClass(PSUtil)
128
+ return True
129
+
130
+ # Indicate that graphics are running
131
+ def startGraphics(self):
132
+ self.graphicsRunning = True
133
+
134
+ # Import a plugin
135
+ def importPlugin(self, source):
136
+ args=source.split(':')
137
+ if len(args)<2:
138
+ RuntimeError(None, f'Invalid plugin spec "{source}"')
139
+ idx=args[0].rfind('/')
140
+ if idx<0:
141
+ sys.path.append('.')
142
+ module=args[0]
143
+ else:
144
+ sys.path.append(args[0][0:idx])
145
+ module=args[0][idx+1:len(args[0])]
146
+ module = module.replace('/','.').replace('.py','')
147
+ module = importlib.import_module(module)
148
+ plugin = getattr(module, args[1])
149
+ self.useClass(plugin)
150
+
151
+ # Use a specified class
152
+ def useClass(self, clazz):
153
+ handler = clazz(self.compiler)
154
+ self.domains.append(handler)
155
+ self.domainIndex[handler.getName()] = handler
156
+
157
+ # This is the runtime callback for event handlers
158
+ def callback(self, item, record, goto):
159
+ object = self.getObject(record)
160
+ values = object.getValues() # type: ignore
161
+ for i, v in enumerate(values):
162
+ if isinstance(v, ECValue): v = v.getContent()
163
+ if v == item:
164
+ object.setIndex(i) # type: ignore
165
+ self.run(goto)
166
+ return
167
+
168
+ # Ensure the program is running
169
+ def ensureRunning(self):
170
+ if not self.running:
171
+ raise RuntimeError(self, 'Improper use of runtime function')
172
+
173
+ # Ensure the program is not running
174
+ def ensureNotRunning(self):
175
+ if self.running:
176
+ raise RuntimeError(self, 'Improper use of non-runtime function')
177
+
178
+ # Get the domain list
179
+ def getDomains(self):
180
+ return self.domains
181
+
182
+ def isSymbol(self, name):
183
+ return name in self.symbols
184
+
185
+ # Get the symbol record for a given name
186
+ def getVariable(self, name):
187
+ self.ensureRunning()
188
+ if isinstance(name, dict): name = name['name']
189
+ try:
190
+ target = self.code[self.symbols[name]]
191
+ if 'import' in target:
192
+ target = target['import']
193
+ return target
194
+ except:
195
+ RuntimeError(self, f'Unknown symbol \'{name}\'')
196
+
197
+ # Get the object represented by a symbol record
198
+ def getObject(self, record):
199
+ if isinstance(record, dict) and 'object' in record:
200
+ return record['object']
201
+ return record
202
+
203
+ # Check if an object is an instance of a given class
204
+ # This can either be variable record (a dict) or an instance of ECObject
205
+ def isObjectType(self, object, classes):
206
+ if isinstance(object, dict) and 'object' in object and isinstance(object['object'], ECObject):
207
+ object = object['object']
208
+ return isinstance(object, classes)
209
+
210
+ # Check if the object is an instance of one of a set of classes. Compile and runtime
211
+ def checkObjectType(self, object, classes):
212
+ if isinstance(object, dict): return
213
+ if not isinstance(object, classes):
214
+ if isinstance(classes, tuple):
215
+ class_names = ", ".join([c.__name__ for c in classes])
216
+ message = f"Variable {object.name} should be one of {class_names}"
217
+ else:
218
+ class_names = classes.__name__
219
+ message = f"Variable {object.name} should be {class_names}"
220
+ if self.running:
221
+ raise RuntimeError(self, message)
222
+ else:
223
+ raise FatalError(self.compiler, message)
224
+
225
+ # Get the inner (non-EC) object from a name, record or object
226
+ def getInnerObject(self, object):
227
+ if isinstance(object, dict): object = object['object']
228
+ elif isinstance(object, str):
229
+ record = self.getVariable(object) # type: ignore
230
+ object = self.getObject(record) # type: ignore
231
+ value = object.getValue() # type: ignore
232
+ if isinstance(value, ECValue) and value.getType() == 'object':
233
+ return value.getContent()
234
+ else: return value
235
+
236
+ def constant(self, content, numeric):
237
+ result = {}
238
+ result['type'] = int if numeric else str
239
+ result['content'] = content
240
+ return result
241
+
242
+ # Test if an item is a string or a number
243
+ def getItemType(self, value):
244
+ return int if isinstance(value, int) else str
245
+
246
+ # Get the value of an item that may be an ECValue or a raw value. Return as an ECValue
247
+ def getValueOf(self, item):
248
+ value = ECValue()
249
+ if isinstance(item, ECValue):
250
+ if item.getType() == 'object':
251
+ return item.getContent()
252
+ else: value = item
253
+ else:
254
+ varType = type(item).__name__
255
+ if varType in [int, str, 'bool', float, 'list', 'dict']:
256
+ if varType == int: value.setValue(type=int, content=item)
257
+ elif varType == str: value.setValue(type=str, content=item)
258
+ elif varType == 'bool': value.setValue(type=bool, content=item)
259
+ elif varType == float: value.setValue(type=str, content=str(item))
260
+ elif varType == 'list': value.setValue(type='list', content=item)
261
+ elif varType == 'dict': value.setValue(type='dict', content=item)
262
+ else: value.setValue(None)
263
+ return value
264
+
265
+ # Runtime function to evaluate an ECObject or ECValue. Returns another ECValue
266
+ # This function may be called recursively by value handlers.
267
+ def evaluate(self, item):
268
+ self.ensureRunning()
269
+ if isinstance(item, ECObject):
270
+ value = item.getValue()
271
+ if value == None:
272
+ raise RuntimeError(self, f'Symbol {item.getName()} not initialized')
273
+ else: value = item
274
+ try:
275
+ valType = value.getType() # type: ignore
276
+ except:
277
+ RuntimeError(self, 'Value does not hold a valid ECValue')
278
+ result = ECValue(type=valType)
279
+
280
+ if valType in (str, int, bool, 'list', 'dict', None):
281
+ # Simple value - just return the content
282
+ result.setContent(value.getContent()) # type: ignore
283
+
284
+ elif valType == 'object':
285
+ # Object other than ECVariable
286
+ record = self.getVariable(value.getName())
287
+ object = self.getObject(record) # type: ignore
288
+ result = object.getContent() # type: ignore
289
+
290
+ elif valType == 'symbol': # type: ignore
291
+ # If it's a symbol, get its value
292
+ record = self.getVariable(value.getContent()) # type: ignore
293
+ if not 'object' in record: return None # type: ignore
294
+ variable = self.getObject(record) # type: ignore
295
+ result = variable.getValue() # type: ignore
296
+ if isinstance(result, ECValue): return self.evaluate(result)
297
+ if isinstance(result, ECObject): return result.getValue()
298
+ if isinstance(result, dict) or isinstance(result, list):
299
+ return result
300
+ # See if one of the domains can handle this value
301
+ value = result
302
+ result = None
303
+ for domain in self.domains:
304
+ result = domain.getUnknownValue(value)
305
+ if result != None: break
306
+
307
+ elif valType == 'cat':
308
+ # Handle concatenation
309
+ content = ''
310
+ for part in value.getContent(): # pyright: ignore[reportOptionalMemberAccess]
311
+ val = self.evaluate(part) # pyright: ignore[reportAttributeAccessIssue]
312
+ if val != None:
313
+ if isinstance(val, ECValue): val = str(val.getContent())
314
+ if val == None: val = ''
315
+ else: content += str(val)
316
+ result.setValue(type=str, content=content)
317
+
318
+ else:
319
+ # Call the given domain to handle a value
320
+ domainName = value.getDomain() # type: ignore
321
+ if domainName == None: domainName = 'core'
322
+ domain = self.domainIndex[domainName]
323
+ handler = domain.valueHandler(value.getType()) # type: ignore
324
+ if handler: result = handler(value)
325
+
326
+ return result
327
+
328
+ # Get the runtime value of a value object (as a string or integer)
329
+ def textify(self, value):
330
+ self.ensureRunning()
331
+ if value is None:
332
+ return None
333
+
334
+ if isinstance(value, dict):
335
+ value = value['object']
336
+ if isinstance(value, ECObject):
337
+ value = value.getValue()
338
+ if isinstance(value, ECValue): # type: ignore
339
+ v = self.evaluate(value) # type: ignore
340
+ else:
341
+ v = value
342
+ if v is None: return None
343
+ if isinstance(v, ECValue):
344
+ if v.getType() == 'object':
345
+ return value.getContent() # type: ignore
346
+ return v.getContent()
347
+ if isinstance(v, (dict, list)):
348
+ return json.dumps(v)
349
+ return v
350
+
351
+ # Get the content of a symbol
352
+ def getSymbolContent(self, record):
353
+ if len(record['value']) == 0:
354
+ return None
355
+ try: return record['value'][record['index']]
356
+ except: raise RuntimeError(self, f'Cannot get content of symbol "{record["name"]}"')
357
+
358
+ # Get the value of a symbol as an ECValue
359
+ def getSymbolValue(self, record):
360
+ self.ensureRunning()
361
+ object = self.getObject(record)
362
+ self.checkObjectType(object, ECObject)
363
+ value = object.getValue() # type: ignore
364
+ if value is None:
365
+ raise NoValueRuntimeError(self, f'Symbol "{record["name"]}" has no value')
366
+ # copy = deepcopy(value)
367
+ copy = ECValue(domain=value.getDomain(),type=value.getType(),content=deepcopy(value.getContent()))
368
+ return copy
369
+
370
+ # Set the value of a symbol to either an ECValue or a raw value
371
+ def putSymbolValue(self, record, value):
372
+ variable = self.getObject(record)
373
+ if variable.isLocked(): # type: ignore
374
+ name = record['name']
375
+ raise RuntimeError(self, f'Symbol "{name}" is locked')
376
+ variable.setValue(self.getValueOf(value)) # type: ignore
377
+
378
+ def encode(self, value):
379
+ return value
380
+
381
+ def decode(self, value):
382
+ return value
383
+
384
+ # Tokenise the script
385
+ def tokenise(self, script):
386
+ token = ''
387
+ literal = False
388
+ for lino in range(0, len(script.lines)):
389
+ line = script.lines[lino]
390
+ length = len(line)
391
+ if length == 0:
392
+ continue
393
+ # Look for the first non-space
394
+ n = 0
395
+ while n < length and line[n].isspace():
396
+ n += 1
397
+ # The whole line may be empty
398
+ if n == length:
399
+ if literal:
400
+ token += '\n'
401
+ continue
402
+ # If in an unfinished literal, the first char must be a backtick to continue adding to it
403
+ if literal:
404
+ if line[n] != '`':
405
+ # Close the current token
406
+ if len(token) > 0:
407
+ script.tokens.append(Token(lino, token))
408
+ token = ''
409
+ literal = False
410
+ n += 1
411
+ for n in range(n, length):
412
+ c = line[n]
413
+ # Test if we are in a literal
414
+ if not literal:
415
+ if c.isspace():
416
+ if len(token) > 0:
417
+ script.tokens.append(Token(lino, token))
418
+ token = ''
419
+ continue
420
+ elif c == '!':
421
+ break
422
+ # Test for the start or end of a literal
423
+ if c == '`':
424
+ if literal:
425
+ token += c
426
+ literal = False
427
+ else:
428
+ token += c
429
+ literal = True
430
+ m = n
431
+ continue
432
+ else:
433
+ token += c
434
+ if len(token) > 0:
435
+ if literal:
436
+ token += '\n'
437
+ else:
438
+ script.tokens.append(Token(lino, token))
439
+ token = ''
440
+ return
441
+
442
+ def releaseParent(self):
443
+ if self.parent and self.parent.waiting and self.parent.program.running: # type: ignore[union-attr]
444
+ self.parent.waiting = False # type: ignore[union-attr]
445
+ self.parent.program.run(self.parent.pc) # type: ignore[union-attr]
446
+
447
+ # Flush the queue
448
+ def flush(self, pc):
449
+ global queue
450
+ self.pc = pc
451
+ while self.running:
452
+ command = self.code[self.pc]
453
+
454
+ # Check if debugger wants to halt before executing this command
455
+ if self.debugger != None:
456
+ # pc==1 is the first real command (pc==0 is the debug loader)
457
+ is_first = (self.pc == 1)
458
+ if self.debugger.checkIfHalt(is_first):
459
+ # Debugger says halt - break out and wait for user
460
+ break
461
+
462
+ domainName = command['domain']
463
+ if domainName == None:
464
+ self.pc += 1
465
+ else:
466
+ keyword = command['keyword']
467
+ if self.debugStep and not self.debugSkip and 'debug' in command:
468
+ lino = command['lino'] + 1
469
+ line = self.script.lines[command['lino']].strip()
470
+ print(f'{self.name}: Line {lino}: {domainName}:{keyword}: {line}')
471
+ domain = self.domainIndex[domainName]
472
+ handler = domain.runHandler(keyword)
473
+ if handler:
474
+ command = self.code[self.pc]
475
+ command['program'] = self
476
+ try:
477
+ if self.breakpoint:
478
+ pass # Place a breakpoint here for a debugger to catch
479
+ self.pc = handler(command)
480
+ except Exception as e:
481
+ raise RuntimeError(self, f'Error during execution of {domainName}:{keyword}: {str(e)}')
482
+ # Deal with 'exit'
483
+ if self.pc == -1:
484
+ queue = deque()
485
+ if self.parent != None:
486
+ self.releaseParent()
487
+ self.running = False
488
+ break
489
+ elif self.pc == None or self.pc == 0 or self.pc >= len(self.code):
490
+ break
491
+
492
+ # Run the script at a given PC value
493
+ def run(self, pc):
494
+ global queue
495
+ item = ECValue()
496
+ item.program = self # type: ignore
497
+ item.pc = pc # type: ignore
498
+ queue.append(item)
499
+ self.running = True
500
+
501
+ def kill(self):
502
+ self.running = False
503
+ if self.parent != None: self.parent.program.kill()
504
+
505
+ def nonNumericValueError(self):
506
+ FatalError(self.compiler, 'Non-numeric value')
507
+
508
+ def compare(self, value1, value2):
509
+ if value1 == None or value2 == None:
510
+ RuntimeError(self, 'Cannot compare a value with None')
511
+ v1 = self.textify(value1)
512
+ v2 = self.textify(value2)
513
+ if v1 == None or v2 == None:
514
+ raise RuntimeError(self, 'Both items must have a value for comparison')
515
+ if type(v1) == str and type(v2) == str:
516
+ # String comparison
517
+ if v1 < v2: return -1
518
+ if v1 > v2: return 1
519
+ return 0
520
+
521
+ if type(v1) is str:
522
+ try:
523
+ v1 = int(v1)
524
+ except:
525
+ print(f'{v1} is not an integer')
526
+ return None
527
+ if type(v2) is str:
528
+ try:
529
+ v2 = int(v2)
530
+ except:
531
+ print(f'{v2} is not an integer')
532
+ return None
533
+ if v1 < v2: # type: ignore[operator]
534
+ return -1
535
+ if v1 > v2: # type: ignore[operator]
536
+ return 1
537
+ return 0
538
+
539
+ # Set up a message handler
540
+ def onMessage(self, pc):
541
+ self.onMessagePC = pc
542
+
543
+ # Handle a message from our parent program
544
+ def handleMessage(self, message):
545
+ self.message = message
546
+ self.run(self.onMessagePC)
547
+
548
+ # This is the program launcher
549
+ def Main():
550
+ if (len(sys.argv) > 1):
551
+ Program(' '.join(sys.argv[1:])).start()
552
+ else:
553
+ Program('-v')
554
+
555
+ if __name__ == '__main__':
556
+ Main()
557
+
@@ -0,0 +1,48 @@
1
+ from easycoder import Handler, ECValue
2
+ import os
3
+ from psutil import Process
4
+
5
+ class PSUtil(Handler):
6
+
7
+ def __init__(self, compiler):
8
+ Handler.__init__(self, compiler)
9
+
10
+ def getName(self):
11
+ return 'psutil'
12
+
13
+ #############################################################################
14
+ # Keyword handlers
15
+
16
+ #############################################################################
17
+ # Compile a value in this domain
18
+ def compileValue(self):
19
+ value = ECValue(domain=self.getName())
20
+ if self.tokenIs('the'):
21
+ self.nextToken()
22
+ token = self.getToken()
23
+ if token in ['mem', 'memory']:
24
+ value.setType('memory')
25
+ return value
26
+ return None
27
+
28
+ #############################################################################
29
+ # Modify a value or leave it unchanged.
30
+ def modifyValue(self, value):
31
+ return value
32
+
33
+ #############################################################################
34
+ # Value handlers
35
+
36
+ def v_memory(self, v):
37
+ process: Process = Process(os.getpid())
38
+ megabytes: float = process.memory_info().rss / (1024 * 1024)
39
+ return ECValue(domain=self.getName(), type=float, content=megabytes)
40
+
41
+ #############################################################################
42
+ # Compile a condition
43
+ def compileCondition(self):
44
+ condition = {}
45
+ return condition
46
+
47
+ #############################################################################
48
+ # Condition handlers
@@ -0,0 +1,11 @@
1
+ import pytz, time
2
+ from datetime import datetime
3
+
4
+ def getTimestamp(t):
5
+ tz = pytz.timezone('GB') # Localize this!
6
+ dt = datetime.fromtimestamp(t)
7
+ # print(f'{dt} + {tz.dst(dt).seconds}')
8
+ dst = tz.dst(dt)
9
+ return int(t) + (dst.seconds if dst else 0)
10
+
11
+ # Usage: print(getTimestamp(time.time()))