easycoder 260108.1__py2.py3-none-any.whl → 260118.3__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/ec_handler.py CHANGED
@@ -76,4 +76,4 @@ class Handler:
76
76
 
77
77
  # Get the value of an unknown item (domain-specific)
78
78
  def getUnknownValue(self, value):
79
- return None # Unable to get value
79
+ return value
easycoder/ec_mqtt.py CHANGED
@@ -1,7 +1,9 @@
1
- from easycoder import Handler, ECObject, ECValue, ECDictionary, FatalError, RuntimeError
1
+ from easycoder import Handler, ECObject, ECValue, RuntimeError
2
2
  import paho.mqtt.client as mqtt
3
3
  import time
4
-
4
+ import threading
5
+ import json
6
+
5
7
  #############################################################################
6
8
  # MQTT client class
7
9
  class MQTTClient():
@@ -14,7 +16,14 @@ class MQTTClient():
14
16
  self.broker = broker
15
17
  self.port = port
16
18
  self.topics = topics
19
+ self.onConnectPC = None
17
20
  self.onMessagePC = None
21
+ self.timeout = False
22
+ self.messages = {}
23
+ self.chunked_messages = {} # Store incoming chunked messages {topic: {part_num: data}}
24
+ self.confirmation_lock = threading.Lock()
25
+ self.chunk_size = 1024 # Default chunk size
26
+ self.last_send_time = None # Time taken for last message transmission (seconds)
18
27
  self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=self.clientID) # type: ignore
19
28
 
20
29
  # Setup callbacks
@@ -25,28 +34,132 @@ class MQTTClient():
25
34
  print(f"Client {self.clientID} connected")
26
35
  for item in self.topics:
27
36
  topic = self.program.getObject(self.program.getVariable(item))
28
- self.client.subscribe(topic.getName(), qos=topic.getQOS())
29
- print(f"Subscribed to topic: {topic.getName()} with QoS {topic.getQOS()}")
37
+ self.client.subscribe(topic.getName(), qos=topic.getQoS())
38
+ print(f"Subscribed to topic: {topic.getName()} with QoS {topic.getQoS()}")
39
+
40
+ if self.onConnectPC is not None:
41
+ self.program.run(self.onConnectPC)
42
+ self.program.flushCB()
30
43
 
31
44
  def on_message(self, client, userdata, msg):
32
- # print(f"Message received on topic {msg.topic}: {msg.payload.decode()}")
33
- if self.onMessagePC is not None:
34
- self.message = msg
35
- self.program.run(self.onMessagePC)
36
- self.program.flushCB()
45
+ payload = msg.payload.decode('utf-8')
46
+ topic = msg.topic
47
+
48
+ # Check if this is a chunked message (format: "!part!<n> <total><data>")
49
+ if payload.startswith('!part!'):
50
+ # Extract: "!part!<n> <total><data>"
51
+ header_end = payload.find(' ', 6) # Find space after part number
52
+ if header_end > 6:
53
+ try:
54
+ part_num = int(payload[6:header_end]) # Extract part number
55
+ # Find next space to get total_chunks
56
+ total_end = payload.find(' ', header_end + 1)
57
+ if total_end > header_end:
58
+ total_chunks = int(payload[header_end + 1:total_end])
59
+ data = payload[total_end + 1:] # Rest is data
60
+
61
+ # Initialize chunked message storage if this is part 0
62
+ if part_num == 0:
63
+ self.chunked_messages[topic] = {}
64
+
65
+ # Store this chunk with its part number
66
+ if topic in self.chunked_messages:
67
+ self.chunked_messages[topic][part_num] = data
68
+ print(f"Received chunk {part_num}/{total_chunks - 1} on topic {topic}")
69
+ except (ValueError, IndexError):
70
+ pass
71
+ return
72
+
73
+ elif payload.startswith('!last!'):
74
+ # Final chunk: "!last!<total><data>"
75
+ try:
76
+ # Find where the total ends and data begins (first space after !last!)
77
+ space_pos = payload.find(' ', 6)
78
+ if space_pos > 6:
79
+ total_chunks = int(payload[6:space_pos])
80
+ data = payload[space_pos + 1:] # Rest is data
81
+
82
+ # Initialize topic storage if not present (single chunk case)
83
+ if topic not in self.chunked_messages:
84
+ self.chunked_messages[topic] = {}
85
+
86
+ # Store the last chunk
87
+ self.chunked_messages[topic][total_chunks - 1] = data
88
+
89
+ # Verify all chunks are present
90
+ expected_parts = set(range(total_chunks))
91
+ received_parts = set(self.chunked_messages[topic].keys())
92
+
93
+ if expected_parts == received_parts:
94
+ # All chunks received - assemble complete message
95
+ message_parts = [self.chunked_messages[topic][i] for i in sorted(self.chunked_messages[topic].keys())]
96
+ complete_message = ''.join(message_parts)
97
+ del self.chunked_messages[topic]
98
+
99
+ # Confirmation is now handled at the EasyCoder level
100
+ # print(f"All chunks received for topic {topic} ({len(complete_message)} bytes total).")
101
+ self.message = {"topic": topic, "payload": complete_message}
102
+
103
+ if self.onMessagePC is not None:
104
+ # print(f'Run from PC {self.onMessagePC}')
105
+ self.program.run(self.onMessagePC)
106
+ self.program.flushCB()
107
+ else:
108
+ missing = expected_parts - received_parts
109
+ print(f"Warning: Missing chunks {missing} for topic {topic}")
110
+ except (ValueError, IndexError):
111
+ pass
112
+ return
37
113
 
38
114
  def getMessageTopic(self):
39
- return self.message.topic
115
+ return self.message.topic # type: ignore
40
116
 
41
- def getMessagePayload(self):
42
- return self.message.payload.decode('utf-8')
117
+ def getReceivedMessage(self):
118
+ return self.message
43
119
 
44
120
  def onMessage(self, pc):
45
121
  self.onMessagePC = pc
122
+
123
+ def sendMessage(self, topic, message, qos, chunk_size=0):
124
+ """Send a message, optionally chunked if chunk_size > 0
125
+
126
+ Stores transmission time in self.last_send_time (seconds)
127
+ """
128
+ send_start = time.time()
129
+
130
+ # Send message in chunks using selected strategy
131
+ message_len = len(message)
132
+ num_chunks = (message_len + chunk_size - 1) // chunk_size
133
+
134
+ # print(f'Sending message ({message_len} bytes) in {num_chunks} chunks of size {chunk_size} to topic {topic} with QoS {qos}')
135
+
136
+ self._send_rapid_fire(topic, message, qos, chunk_size, num_chunks)
137
+
138
+ self.last_send_time = time.time() - send_start
139
+ print(f'Message transmission complete in {self.last_send_time:.3f} seconds')
46
140
 
47
- def sendMessage(self, topic, message, qos):
48
- self.client.publish(topic, message, qos=qos)
141
+ def _send_rapid_fire(self, topic, message, qos, chunk_size, num_chunks):
142
+ """Send all chunks as rapidly as possible, wait for single final confirmation"""
143
+ # print(f"Sending all {num_chunks} chunks as fast as possible...")
144
+
145
+ # Send all chunks rapidly
146
+ for i in range(num_chunks):
147
+ start = i * chunk_size
148
+ end = min(start + chunk_size, len(message))
149
+ chunk_data = message[start:end]
150
+
151
+ # Prepare chunk with header: "!part!<n> <total><data>" or "!last!<total><data>"
152
+ if i == num_chunks - 1:
153
+ chunk_msg = f"!last!{num_chunks} {chunk_data}"
154
+ else:
155
+ chunk_msg = f"!part!{i} {num_chunks} {chunk_data}"
156
+
157
+ # Send without waiting
158
+ self.client.publish(topic, chunk_msg, qos=qos)
159
+ # print(f"Sent chunk {i}/{num_chunks - 1} to topic {topic} with QoS {qos}: {chunk_msg}")
160
+ # No waiting here; confirmations are handled in EasyCoder using sender identity
49
161
 
162
+ # Start the MQTT client loop
50
163
  def run(self):
51
164
  self.client.connect(self.broker, int(self.port), 60)
52
165
  self.client.loop_start()
@@ -57,19 +170,31 @@ class ECTopic(ECObject):
57
170
  def __init__(self):
58
171
  super().__init__()
59
172
 
60
- def create(self, name, qos=1):
61
- super().__init__()
62
- self.name = name
63
- self.qos = qos
64
-
65
173
  def getName(self):
66
- return self.name
67
-
68
- def getQOS(self):
69
- return self.qos
174
+ v = self.getValue()
175
+ if v is None:
176
+ return ""
177
+ if v is None:
178
+ return ""
179
+ return v['name']
180
+
181
+ def getQoS(self):
182
+ v = self.getValue()
183
+ if v is None:
184
+ return 0
185
+ if v is None:
186
+ return 0
187
+ return int(v['qos'])
188
+
189
+ def textify(self):
190
+ v = self.getValue()
191
+ if v is None:
192
+ return ""
193
+ return f'{{"name": "{v["name"]}", "qos": {v["qos"]}}}'
70
194
 
71
195
  ###############################################################################
72
- # The MQTT compiler and rutime handlers
196
+ ###############################################################################
197
+ # The MQTT compiler and runtime handlers
73
198
  class MQTT(Handler):
74
199
 
75
200
  def __init__(self, compiler):
@@ -89,7 +214,8 @@ class MQTT(Handler):
89
214
  self.checkObjectType(record, ECTopic)
90
215
  command['topic'] = record['name']
91
216
  self.skip('name')
92
- command['name'] = self.nextValue()
217
+ name =self.nextValue()
218
+ command['name'] = name
93
219
  self.skip('qos')
94
220
  command['qos'] = self.nextValue()
95
221
  self.add(command)
@@ -99,7 +225,10 @@ class MQTT(Handler):
99
225
  def r_init(self, command):
100
226
  record = self.getVariable(command['topic'])
101
227
  topic = ECTopic()
102
- topic.create(self.textify(command['name']), qos=int(self.textify(command['qos'])))
228
+ value = {}
229
+ value['name'] = self.textify(command['name'])
230
+ value['qos'] = int(self.textify(command['qos']))
231
+ topic.setValue(value)
103
232
  record['object'] = topic
104
233
  return self.nextPC()
105
234
 
@@ -149,7 +278,9 @@ class MQTT(Handler):
149
278
  token = self.peek()
150
279
  if token == 'mqtt':
151
280
  self.nextToken()
152
- if self.nextIs('message'):
281
+ event = self.nextToken()
282
+ if event in ['connect', 'message']:
283
+ command['event'] = event
153
284
  self.nextToken()
154
285
  command['goto'] = 0
155
286
  self.add(command)
@@ -174,23 +305,33 @@ class MQTT(Handler):
174
305
  return False
175
306
 
176
307
  def r_on(self, command):
177
- self.program.mqttClient.onMessage(self.nextPC()+1)
308
+ event = command['event']
309
+ if event == 'connect':
310
+ self.program.mqttClient.onConnectPC = self.nextPC()+1
311
+ elif event == 'message':
312
+ self.program.mqttClient.onMessagePC = self.nextPC()+1
178
313
  return command['goto']
179
314
 
180
315
  # send {message} to {topic}
181
316
  def k_send(self, command):
182
317
  if self.nextIs('mqtt'):
183
318
  command['message'] = self.nextValue()
184
- self.skip('from')
185
- if self.nextIsSymbol():
186
- record = self.getSymbolRecord()
187
- self.checkObjectType(record, MQTTClient)
188
- command['from'] = record['name']
189
319
  self.skip('to')
190
320
  if self.nextIsSymbol():
191
321
  record = self.getSymbolRecord()
192
322
  self.checkObjectType(record, MQTTClient)
193
323
  command['to'] = record['name']
324
+ token = self.peek()
325
+ if token == 'with':
326
+ self.nextToken()
327
+ while True:
328
+ token = self.nextToken()
329
+ if token == 'qos':
330
+ command['qos'] = self.nextValue()
331
+ if self.peek() == 'and':
332
+ self.nextToken()
333
+ else:
334
+ break
194
335
  self.add(command)
195
336
  return True
196
337
  return False
@@ -200,7 +341,38 @@ class MQTT(Handler):
200
341
  raise RuntimeError(self.program, 'No MQTT client defined')
201
342
  topic = self.getObject(self.getVariable(command['to']))
202
343
  message = self.textify(command['message'])
203
- self.program.mqttClient.sendMessage(topic.getName(), message, topic.getQOS())
344
+
345
+ # Validate that outgoing message is valid JSON with required 'sender' and 'action' fields
346
+ try:
347
+ payload_dict = json.loads(message)
348
+ if not isinstance(payload_dict, dict):
349
+ raise RuntimeError(self.program, f'MQTT message must be a JSON object, got {type(payload_dict).__name__}')
350
+
351
+ # sender can be a string (topic name) or a dict with 'name' and 'qos' keys
352
+ if 'sender' not in payload_dict:
353
+ raise RuntimeError(self.program, 'MQTT message must contain "sender" field')
354
+ sender_val = payload_dict.get('sender')
355
+ if isinstance(sender_val, str):
356
+ pass # sender is a string (topic name), valid
357
+ elif isinstance(sender_val, dict):
358
+ if 'name' not in sender_val or not isinstance(sender_val.get('name'), str):
359
+ raise RuntimeError(self.program, 'MQTT message "sender" dict must contain "name" field as string')
360
+ else:
361
+ raise RuntimeError(self.program, f'MQTT message "sender" must be a string or dict, got {type(sender_val).__name__}')
362
+
363
+ # action must be a string
364
+ if 'action' not in payload_dict or not isinstance(payload_dict.get('action'), str):
365
+ raise RuntimeError(self.program, 'MQTT message must contain "action" field as string')
366
+ except json.JSONDecodeError as e:
367
+ raise RuntimeError(self.program, f'MQTT message must be valid JSON: {e}')
368
+
369
+ if 'qos' in command:
370
+ qos = int(self.textify(command['qos']))
371
+ else:
372
+ qos = topic.qos if hasattr(topic, 'qos') else 1
373
+ self.program.mqttClient.sendMessage(topic.getName(), message, qos, chunk_size=1024)
374
+ if self.program.mqttClient.timeout:
375
+ return 0
204
376
  return self.nextPC()
205
377
 
206
378
  # Declare a topic variable
@@ -214,15 +386,22 @@ class MQTT(Handler):
214
386
  #############################################################################
215
387
  # Compile a value in this domain
216
388
  def compileValue(self):
217
- token = self.nextToken()
218
- if token == 'mqtt':
219
- value = ECValue(domain=self.getName())
389
+ token = self.getToken()
390
+ if token == 'the':
220
391
  token = self.nextToken()
221
- if token in ['topic', 'message']:
222
- value.setType(token)
223
- return value
392
+ if self.isSymbol():
393
+ record = self.getSymbolRecord()
394
+ object = self.getObject(record)
395
+ if isinstance(object, ECTopic):
396
+ return ECValue(domain=self.getName(), type='topic', content=record['name'])
397
+ else: return None
224
398
  else:
225
- return self.getValue()
399
+ if token == 'mqtt':
400
+ token = self.nextToken()
401
+ if token == 'message':
402
+ return ECValue(domain=self.getName(), type='mqtt', content=token)
403
+ # else:
404
+ # return self.getValue()
226
405
  return None
227
406
 
228
407
  #############################################################################
@@ -234,10 +413,17 @@ class MQTT(Handler):
234
413
  # Value handlers
235
414
 
236
415
  def v_message(self, v):
237
- return self.program.mqttClient.getMessagePayload()
416
+ return self.program.mqttClient.message
417
+
418
+ def v_mqtt(self, v):
419
+ content = v.getContent()
420
+ if content == 'message':
421
+ return self.program.mqttClient.message
422
+ return None
238
423
 
239
424
  def v_topic(self, v):
240
- return self.program.mqttClient.getMessageTopic()
425
+ topic = self.getObject(self.getVariable(self.textify(v.getContent())))
426
+ return f'{{"name": "{topic.getName()}", "qos": {topic.getQoS()}}}'
241
427
 
242
428
  #############################################################################
243
429
  # Compile a condition
easycoder/ec_program.py CHANGED
@@ -1,6 +1,7 @@
1
- import time, sys, json
1
+ import time, sys, json, traceback
2
2
  from copy import deepcopy
3
3
  from collections import deque
4
+
4
5
  from .ec_classes import (
5
6
  Script,
6
7
  Token,
@@ -60,6 +61,7 @@ class Program:
60
61
  self.value = self.compiler.value
61
62
  self.condition = self.compiler.condition
62
63
  self.graphics = None
64
+ self.mqtt = None
63
65
  self.psutil = None
64
66
  self.useClass(Core)
65
67
  self.ticker = 0
@@ -93,7 +95,7 @@ class Program:
93
95
  f'{round((finishCompile - startCompile) * 1000)} ms')
94
96
  for name in self.symbols.keys():
95
97
  record = self.code[self.symbols[name]]
96
- if name[-1] != ':' and not 'used' in record:
98
+ if name[-1] != ':' and not record['used']:
97
99
  print(f'Variable "{name}" not used')
98
100
  else:
99
101
  print(f'Run {self.name}')
@@ -119,12 +121,12 @@ class Program:
119
121
  self.useClass(Graphics)
120
122
  return True
121
123
 
122
- # Use the mqtt module
124
+ # Use the MQTT module
123
125
  def useMQTT(self):
124
- if self.psutil == None:
125
- print('Loading mqtt module')
126
+ if self.mqtt == None:
127
+ print('Loading MQTT module')
126
128
  from .ec_mqtt import MQTT
127
- self.psutil = MQTT
129
+ self.mqtt = MQTT
128
130
  self.useClass(MQTT)
129
131
  return True
130
132
 
@@ -252,7 +254,7 @@ class Program:
252
254
  # Test if an item is a string or a number
253
255
  def getItemType(self, value):
254
256
  return int if isinstance(value, int) else str
255
-
257
+
256
258
  # Get the value of an item that may be an ECValue or a raw value. Return as an ECValue
257
259
  def getValueOf(self, item):
258
260
  value = ECValue()
@@ -270,6 +272,15 @@ class Program:
270
272
  elif varType == 'dict': value.setValue(type=dict, content=item)
271
273
  else: value.setValue(type=None, content=None)
272
274
  return value
275
+
276
+ # Get the value of an item from its domain handler
277
+ def textifyInDomain(self, value):
278
+ domainName = value.getDomain() # type: ignore
279
+ if domainName == None: domainName = 'core'
280
+ domain = self.domainIndex[domainName]
281
+ handler = domain.valueHandler(value.getType()) # type: ignore
282
+ result = handler(value) if handler else None
283
+ return result
273
284
 
274
285
  # Runtime function to evaluate an ECObject or ECValue. Returns another ECValue
275
286
  # This function may be called recursively by value handlers.
@@ -298,7 +309,7 @@ class Program:
298
309
 
299
310
  elif valType == 'symbol': # type: ignore
300
311
  # If it's a symbol, get its value
301
- record = self.getVariable(value.getContent()) # type: ignore
312
+ record = self.getVariable(value.getName()) # type: ignore
302
313
  if not 'object' in record: return None # type: ignore
303
314
  variable = self.getObject(record) # type: ignore
304
315
  result = variable.getValue() # type: ignore
@@ -325,12 +336,7 @@ class Program:
325
336
  result.setValue(type=str, content=content)
326
337
 
327
338
  else:
328
- # Call the given domain to handle a value
329
- domainName = value.getDomain() # type: ignore
330
- if domainName == None: domainName = 'core'
331
- domain = self.domainIndex[domainName]
332
- handler = domain.valueHandler(value.getType()) # type: ignore
333
- if handler: result = handler(value)
339
+ result = self.textifyInDomain(value)
334
340
 
335
341
  return result
336
342
 
@@ -343,7 +349,7 @@ class Program:
343
349
  if isinstance(value, dict):
344
350
  value = value['object']
345
351
  if isinstance(value, ECObject):
346
- value = value.getValue()
352
+ value = value.textify() # type: ignore
347
353
  if isinstance(value, ECValue): # type: ignore
348
354
  v = self.evaluate(value) # type: ignore
349
355
  else:
@@ -353,6 +359,8 @@ class Program:
353
359
  if v.getType() == 'object':
354
360
  return value.getContent() # type: ignore
355
361
  return v.getContent()
362
+ elif isinstance(v, ECObject):
363
+ return v.textify() # type: ignore
356
364
  if isinstance(v, (dict, list)):
357
365
  return json.dumps(v)
358
366
  return v
@@ -372,7 +380,6 @@ class Program:
372
380
  value = object.getValue() # type: ignore
373
381
  if value is None:
374
382
  raise NoValueRuntimeError(self, f'Symbol "{record["name"]}" has no value')
375
- # copy = deepcopy(value)
376
383
  copy = ECValue(domain=value.getDomain(),type=value.getType(),content=deepcopy(value.getContent()))
377
384
  return copy
378
385
 
@@ -487,7 +494,8 @@ class Program:
487
494
  pass # Place a breakpoint here for a debugger to catch
488
495
  self.pc = handler(command)
489
496
  except Exception as e:
490
- raise RuntimeError(self, f'Error during execution of {domainName}:{keyword}: {str(e)}')
497
+ tb = traceback.format_exc()
498
+ raise RuntimeError(self, f'Error during execution of {domainName}:{keyword}: {str(e)}\n\nTraceback:\n{tb}')
491
499
  # Deal with 'exit'
492
500
  if self.pc == -1:
493
501
  queue = deque()
easycoder/ec_value.py CHANGED
@@ -22,11 +22,11 @@ class Value:
22
22
 
23
23
  value = ECValue()
24
24
 
25
- if token == 'true':
25
+ if token.lower() == 'true':
26
26
  value.setValue(bool, True)
27
27
  return value
28
28
 
29
- if token == 'false':
29
+ if token.lower() == 'false':
30
30
  value.setValue(bool, False)
31
31
  return value
32
32
 
@@ -96,8 +96,9 @@ class Value:
96
96
  self.compiler.warning(f'ec_value.compileValue: Cannot get the value of "{token}"')
97
97
  return None
98
98
  if item.getType() == 'symbol':
99
- object = self.compiler.getSymbolRecord(item.getContent())['object']
99
+ object = self.compiler.getSymbolRecord(item.getName())['object']
100
100
  if not object.hasRuntimeValue(): return None
101
+ item.setContent(object.name)
101
102
 
102
103
  if self.peek() == 'cat':
103
104
  self.nextToken() # consume 'cat'
easycoder/pre/ec_core.py CHANGED
@@ -69,7 +69,7 @@ class Core(Handler):
69
69
  if not isinstance(self.getObject(record), ECVariable): return False
70
70
  # If 'giving' comes next, this variable is the second value
71
71
  if self.peek() == 'giving':
72
- v2 = ECValue(type='symbol', content=record['name'])
72
+ v2 = ECValue(type='symbol', name=record['name'])
73
73
  command['value2'] = v2
74
74
  self.nextToken()
75
75
  # Now get the target variable
@@ -1655,8 +1655,7 @@ class Core(Handler):
1655
1655
  self.checkObjectType(record, ECObject)
1656
1656
  # If 'giving' comes next, this variable is the second value
1657
1657
  if self.peek() == 'giving':
1658
- v2 = ECValue(type='symbol')
1659
- v2.setContent(record['name'])
1658
+ v2 = ECValue(type='symbol', name=record['name'])
1660
1659
  command['value2'] = v2
1661
1660
  self.nextToken()
1662
1661
  # Now get the target variable
@@ -1912,7 +1911,7 @@ class Core(Handler):
1912
1911
  value = ECValue()
1913
1912
  token = self.getToken()
1914
1913
  if self.isSymbol():
1915
- value.setValue(type='symbol', content=token)
1914
+ value.setValue(type='symbol', name=token)
1916
1915
  return value
1917
1916
 
1918
1917
  value.setType(token)
@@ -1946,13 +1945,13 @@ class Core(Handler):
1946
1945
  value.format = None
1947
1946
  return value
1948
1947
 
1949
- if token == 'element':
1948
+ if token == 'item':
1950
1949
  value.index = self.nextValue()
1951
1950
  if self.nextToken() == 'of':
1952
1951
  if self.nextIsSymbol():
1953
1952
  record = self.getSymbolRecord()
1954
1953
  self.checkObjectType(record['object'], ECList)
1955
- value.target = ECValue(type='symbol', content=record['name'])
1954
+ value.target = ECValue(type='symbol', name=record['name'])
1956
1955
  return value
1957
1956
  return None
1958
1957
 
@@ -2214,10 +2213,10 @@ class Core(Handler):
2214
2213
  value = v
2215
2214
  return value
2216
2215
 
2217
- def v_element(self, v):
2216
+ def v_item(self, v):
2218
2217
  index = self.textify(v.index)
2219
2218
  targetName = v.target
2220
- target = self.getVariable(targetName.getContent())
2219
+ target = self.getVariable(targetName.getName())
2221
2220
  variable = self.getObject(target)
2222
2221
  self.checkObjectType(variable, ECList)
2223
2222
  content = variable.getContent()
@@ -2656,7 +2655,7 @@ class Core(Handler):
2656
2655
 
2657
2656
  def c_empty(self, condition):
2658
2657
  if condition.value1.getType() == 'symbol':
2659
- record = self.getVariable(condition.value1.content)
2658
+ record = self.getVariable(condition.value1.name)
2660
2659
  variable = self.getObject(record)
2661
2660
  if isinstance(variable, (ECList, ECDictionary)):
2662
2661
  comparison = variable.isEmpty()
@@ -289,7 +289,7 @@ class Program:
289
289
 
290
290
  elif valType == 'symbol': # type: ignore
291
291
  # If it's a symbol, get its value
292
- record = self.getVariable(value.getContent()) # type: ignore
292
+ record = self.getVariable(value.getName()) # type: ignore
293
293
  if not 'object' in record: return None # type: ignore
294
294
  variable = self.getObject(record) # type: ignore
295
295
  result = variable.getValue() # type: ignore
easycoder/pre/ec_value.py CHANGED
@@ -96,7 +96,7 @@ class Value:
96
96
  self.compiler.warning(f'ec_value.compileValue: Cannot get the value of "{token}"')
97
97
  return None
98
98
  if item.getType() == 'symbol':
99
- object = self.compiler.getSymbolRecord(item.getContent())['object']
99
+ object = self.compiler.getSymbolRecord(item.getName())['object']
100
100
  if not object.hasRuntimeValue(): return None
101
101
 
102
102
  if self.peek() == 'cat':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easycoder
3
- Version: 260108.1
3
+ Version: 260118.3
4
4
  Summary: Rapid scripting in English
5
5
  Keywords: compiler,scripting,prototyping,programming,coding,python,low code,hypertalk,computer language,learn to code
6
6
  Author-email: Graham Trott <gtanyware@gmail.com>
@@ -12,6 +12,7 @@ Requires-Dist: requests
12
12
  Requires-Dist: psutil
13
13
  Requires-Dist: paramiko
14
14
  Requires-Dist: pyside6
15
+ Requires-Dist: paho-mqtt
15
16
  Project-URL: Home, https://github.com/easycoder/easycoder-py
16
17
 
17
18
  # Introduction