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
easycoder/ec_debug.py DELETED
@@ -1,464 +0,0 @@
1
- import sys, os, json, html
2
- from PySide6.QtWidgets import (
3
- QMainWindow,
4
- QWidget,
5
- QFrame,
6
- QHBoxLayout,
7
- QVBoxLayout,
8
- QLabel,
9
- QSplitter,
10
- QFileDialog,
11
- QMessageBox,
12
- QScrollArea,
13
- QSizePolicy,
14
- QToolBar
15
- )
16
- from PySide6.QtGui import QAction, QKeySequence, QTextCursor
17
- from PySide6.QtCore import Qt, QTimer, QProcess
18
- from typing import Any
19
-
20
- class Object():
21
- """Dynamic object that allows arbitrary attribute assignment"""
22
- def __setattr__(self, name: str, value: Any) -> None:
23
- self.__dict__[name] = value
24
-
25
- def __getattr__(self, name: str) -> Any:
26
- return self.__dict__.get(name)
27
-
28
- class Debugger(QMainWindow):
29
-
30
- ###########################################################################
31
- # The left-hand column of the main window
32
- class MainLeftColumn(QWidget):
33
- def __init__(self, parent=None):
34
- super().__init__(parent)
35
- layout = QVBoxLayout(self)
36
- layout.addWidget(QLabel("Left column"))
37
- layout.addStretch()
38
-
39
- ###########################################################################
40
- # The right-hand column of the main window
41
- class MainRightColumn(QWidget):
42
- scroll: QScrollArea
43
- layout: QHBoxLayout # type: ignore[assignment]
44
- blob: QLabel
45
-
46
- def __init__(self, parent=None):
47
- super().__init__(parent)
48
-
49
- # Create a scroll area - its content widget holds the lines
50
- self.scroll = QScrollArea(self)
51
- self.scroll.setWidgetResizable(True)
52
-
53
- # Ensure this widget and the scroll area expand to fill available space
54
- self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
55
- self.scroll.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
56
-
57
- self.content = QWidget()
58
- # let the content expand horizontally but have flexible height
59
- self.content.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
60
-
61
- self.inner_layout = QVBoxLayout(self.content)
62
- # spacing and small top/bottom margins to separate lines
63
- self.inner_layout.setSpacing(0)
64
- self.inner_layout.setContentsMargins(0, 0, 0, 0)
65
-
66
- self.scroll.setWidget(self.content)
67
-
68
- # outer layout for this widget contains only the scroll area
69
- main_layout = QVBoxLayout(self)
70
- main_layout.setContentsMargins(0, 0, 0, 0)
71
- main_layout.addWidget(self.scroll)
72
- # ensure the scroll area gets the stretch so it fills the parent
73
- main_layout.setStretch(0, 1)
74
-
75
- #######################################################################
76
- # Add a line to the right-hand column
77
- def addLine(self, spec):
78
- class Label(QLabel):
79
- def __init__(self, text, fixed_width=None, align=Qt.AlignmentFlag.AlignLeft, on_click=spec.onClick):
80
- super().__init__()
81
- self.setText(text)
82
- # remove QLabel's internal margins/padding to reduce top/bottom space
83
- self.setMargin(0)
84
- self.setContentsMargins(0, 0, 0, 0)
85
- self.setStyleSheet("padding:0px; margin:0px; font-family: mono")
86
- fm = self.fontMetrics()
87
- # set a compact fixed height based on font metrics
88
- self.setFixedHeight(fm.height())
89
- # optional fixed width (used for the lino column)
90
- if fixed_width is not None:
91
- self.setFixedWidth(fixed_width)
92
- # align horizontally (keep vertically centered)
93
- self.setAlignment(align | Qt.AlignmentFlag.AlignVCenter)
94
- # optional click callback
95
- self._on_click = on_click
96
-
97
- def mousePressEvent(self, event):
98
- if self._on_click:
99
- try:
100
- self._on_click()
101
- except Exception:
102
- pass
103
- super().mousePressEvent(event)
104
-
105
- spec.label = self
106
- panel = QWidget()
107
- # ensure the panel itself has no margins
108
- try:
109
- panel.setContentsMargins(0, 0, 0, 0)
110
- except Exception:
111
- pass
112
- # tidy layout: remove spacing/margins so lines sit flush
113
- layout = QHBoxLayout(panel)
114
- layout.setSpacing(0)
115
- layout.setContentsMargins(0, 0, 0, 0)
116
- self.layout: QHBoxLayout = layout # type: ignore
117
- # make panel take minimal vertical space
118
- panel.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
119
- # compute width to fit a 4-digit line number using this widget's font
120
- fm_main = self.fontMetrics()
121
- width_4 = fm_main.horizontalAdvance('0000') + 8
122
-
123
- # create the red blob (always present). We'll toggle its opacity
124
- # by changing the stylesheet (rgba alpha 255/0). Do NOT store it
125
- # on the MainRightColumn instance — keep it per-line.
126
- blob = QLabel()
127
- blob_size = 10
128
- blob.setFixedSize(blob_size, blob_size)
129
-
130
- def set_blob_visible(widget, visible):
131
- alpha = 255 if visible else 0
132
- widget.setStyleSheet(f"background-color: rgba(255,0,0,{alpha}); border-radius: {blob_size//2}px; margin:0px; padding:0px;")
133
- widget._blob_visible = visible
134
- # force repaint
135
- widget.update()
136
-
137
- # attach methods to this blob so callers can toggle it via spec.label
138
- blob.showBlob = lambda: set_blob_visible(blob, True) # type: ignore[attr-defined]
139
- blob.hideBlob = lambda: set_blob_visible(blob, False) # type: ignore[attr-defined]
140
-
141
- # initialize according to spec flag
142
- if spec.bp:
143
- blob.showBlob() # type: ignore[attr-defined]
144
- else:
145
- blob.hideBlob() # type: ignore[attr-defined]
146
-
147
- # expose the blob to the outside via spec['label'] so onClick can call showBlob/hideBlob
148
- spec.label = blob
149
-
150
- # create the line-number label; clicking it reports back to the caller
151
- lino_label = Label(str(spec.lino+1), fixed_width=width_4, align=Qt.AlignmentFlag.AlignRight,
152
- on_click=lambda: spec.onClick(spec.lino))
153
- lino_label.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
154
- # create the text label for the line itself
155
- text_label = Label(spec.line, fixed_width=None, align=Qt.AlignmentFlag.AlignLeft)
156
- text_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
157
- layout.addWidget(lino_label)
158
- layout.addSpacing(10)
159
- layout.addWidget(blob, 0, Qt.AlignmentFlag.AlignVCenter)
160
- layout.addSpacing(3)
161
- layout.addWidget(text_label)
162
- self.inner_layout.addWidget(panel)
163
- return panel
164
-
165
- def showBlob(self):
166
- self.blob.setStyleSheet("background-color: red; border-radius: 5px; margin:0px; padding:0px;")
167
-
168
- def hideBlob(self):
169
- self.blob.setStyleSheet("background-color: none; border-radius: 5px; margin:0px; padding:0px;")
170
-
171
- def addStretch(self):
172
- self.layout.addStretch()
173
-
174
- ###########################################################################
175
- # Main debugger class initializer
176
- def __init__(self, program, width=800, height=600, ratio=0.2):
177
- super().__init__()
178
- self.program = program
179
- self.setWindowTitle("EasyCoder Debugger")
180
- self.setMinimumSize(width, height)
181
- self.stopped = True
182
-
183
- # try to load saved geometry from ~/.ecdebug.conf
184
- cfg_path = os.path.join(os.path.expanduser("~"), ".ecdebug.conf")
185
- initial_width = width
186
- # default console height (pixels) if not stored in cfg
187
- console_height = 150
188
- try:
189
- if os.path.exists(cfg_path):
190
- with open(cfg_path, "r", encoding="utf-8") as f:
191
- cfg = json.load(f)
192
- x = int(cfg.get("x", 0))
193
- y = int(cfg.get("y", 0))
194
- w = int(cfg.get("width", width))
195
- h = int(cfg.get("height", height))
196
- ratio =float(cfg.get("ratio", ratio))
197
- # load console height if present
198
- console_height = int(cfg.get("console_height", console_height))
199
- # Apply loaded geometry
200
- self.setGeometry(x, y, w, h)
201
- initial_width = w
202
- except Exception:
203
- # ignore errors and continue with defaults
204
- initial_width = width
205
-
206
- # process handle for running scripts
207
- self._proc = None
208
- # in-process Program instance and writer
209
- self._program = None
210
- self._writer = None
211
- self._orig_stdout = None
212
- self._orig_stderr = None
213
- self._flush_timer = None
214
-
215
- # Keep a ratio so proportions are preserved when window is resized
216
- self.ratio = ratio
217
-
218
- # Central horizontal splitter (left/right)
219
- self.hsplitter = QSplitter(Qt.Orientation.Horizontal, self)
220
- self.hsplitter.setHandleWidth(8)
221
- self.hsplitter.splitterMoved.connect(self.on_splitter_moved)
222
-
223
- # Left pane
224
- left = QFrame()
225
- left.setFrameShape(QFrame.Shape.StyledPanel)
226
- left_layout = QVBoxLayout(left)
227
- left_layout.setContentsMargins(8, 8, 8, 8)
228
- self.leftColumn = self.MainLeftColumn()
229
- left_layout.addWidget(self.leftColumn)
230
- left_layout.addStretch()
231
-
232
- # Right pane
233
- right = QFrame()
234
- right.setFrameShape(QFrame.Shape.StyledPanel)
235
- right_layout = QVBoxLayout(right)
236
- right_layout.setContentsMargins(8, 8, 8, 8)
237
- self.rightColumn = self.MainRightColumn()
238
- # Give the rightColumn a stretch factor so its scroll area fills the vertical space
239
- right_layout.addWidget(self.rightColumn, 1)
240
-
241
- # Add panes to horizontal splitter
242
- self.hsplitter.addWidget(left)
243
- self.hsplitter.addWidget(right)
244
-
245
- # Initial sizes (proportional) for horizontal splitter
246
- total = initial_width
247
- self.hsplitter.setSizes([int(self.ratio * total), int((1 - self.ratio) * total)])
248
-
249
- # Create a vertical splitter so we can add a resizable console panel at the bottom
250
- self.vsplitter = QSplitter(Qt.Orientation.Vertical, self)
251
- self.vsplitter.setHandleWidth(6)
252
- # top: the existing horizontal splitter
253
- self.vsplitter.addWidget(self.hsplitter)
254
-
255
- # bottom: console panel
256
- console_frame = QFrame()
257
- console_frame.setFrameShape(QFrame.Shape.StyledPanel)
258
- console_layout = QVBoxLayout(console_frame)
259
- console_layout.setContentsMargins(4, 4, 4, 4)
260
- # simple read-only text console for script output and messages
261
- from PySide6.QtWidgets import QTextEdit
262
- self.console = QTextEdit()
263
- self.console.setReadOnly(True)
264
- console_layout.addWidget(self.console)
265
- self.vsplitter.addWidget(console_frame)
266
-
267
- # Set initial vertical sizes: prefer saved console_height if available
268
- try:
269
- total_h = int(h) if 'h' in locals() else max(300, self.height())
270
- ch = max(50, min(total_h - 50, console_height))
271
- self.vsplitter.setSizes([int(total_h - ch), int(ch)])
272
- except Exception:
273
- pass
274
-
275
- # Use the vertical splitter as the central widget
276
- self.setCentralWidget(self.vsplitter)
277
- self.parse(program.script.lines)
278
- self.show()
279
-
280
- def on_splitter_moved(self, pos, index):
281
- # Update stored ratio when user drags the splitter
282
- left_width = self.hsplitter.widget(0).width()
283
- total = max(1, sum(w.width() for w in (self.hsplitter.widget(0), self.hsplitter.widget(1))))
284
- self.ratio = left_width / total
285
-
286
- def resizeEvent(self, event):
287
- # Preserve the proportional widths when the window is resized
288
- total_width = max(1, self.width())
289
- left_w = max(0, int(self.ratio * total_width))
290
- right_w = max(0, total_width - left_w)
291
- self.hsplitter.setSizes([left_w, right_w])
292
- super().resizeEvent(event)
293
-
294
- ###########################################################################
295
- # Parse a script into the right-hand column
296
- def parse(self, script):
297
- self.scriptLines = []
298
- # Clear existing lines from the right column layout
299
- layout = self.rightColumn.inner_layout
300
- while layout.count():
301
- item = layout.takeAt(0)
302
- widget = item.widget()
303
- if widget:
304
- widget.deleteLater()
305
-
306
- # Parse and add new lines
307
- lino = 0
308
- for line in script:
309
- if len(line) > 0:
310
- line = line.replace("\t", " ")
311
- line = self.coloriseLine(line, lino)
312
- else:
313
- # still need to call coloriseLine to keep token list in sync
314
- self.coloriseLine(line, lino)
315
- lineSpec = Object()
316
- lineSpec.lino = lino
317
- lineSpec.line = line
318
- lineSpec.bp = False
319
- lineSpec.onClick = self.onClickLino
320
- lino += 1
321
- self.scriptLines.append(lineSpec)
322
- lineSpec.panel = self.rightColumn.addLine(lineSpec)
323
- self.rightColumn.addStretch()
324
-
325
- ###########################################################################
326
- # Colorise a line of script for HTML display
327
- def coloriseLine(self, line, lino=None):
328
- output = ''
329
-
330
- # Preserve leading spaces (render as   except the first)
331
- if len(line) > 0 and line[0] == ' ':
332
- output += '<span>'
333
- n = 0
334
- while n < len(line) and line[n] == ' ': n += 1
335
- output += '&nbsp;' * (n - 1)
336
- output += '</span>'
337
-
338
- # Find the first unquoted ! (not inside backticks)
339
- comment_start = None
340
- in_backtick = False
341
- for idx, c in enumerate(line):
342
- if c == '`':
343
- in_backtick = not in_backtick
344
- elif c == '!' and not in_backtick:
345
- comment_start = idx
346
- break
347
-
348
- if comment_start is not None:
349
- code_part = line[:comment_start]
350
- comment_part = line[comment_start:]
351
- else:
352
- code_part = line
353
- comment_part = None
354
-
355
- # Tokenize code_part as before (respecting backticks)
356
- tokens = []
357
- i = 0
358
- L = len(code_part)
359
- while i < L:
360
- if code_part[i].isspace():
361
- i += 1
362
- continue
363
- if code_part[i] == '`':
364
- j = code_part.find('`', i + 1)
365
- if j == -1:
366
- tokens.append(code_part[i:])
367
- break
368
- else:
369
- tokens.append(code_part[i:j+1])
370
- i = j + 1
371
- else:
372
- j = i
373
- while j < L and not code_part[j].isspace():
374
- j += 1
375
- tokens.append(code_part[i:j])
376
- i = j
377
-
378
- # Colour code tokens and generate a list of elements
379
- for token in tokens:
380
- if token == '':
381
- continue
382
- elif token[0].isupper():
383
- esc = html.escape(token)
384
- element = f'&nbsp;<span style="color: purple; font-weight: bold;">{esc}</span>'
385
- elif token[0].isdigit():
386
- esc = html.escape(token)
387
- element = f'&nbsp;<span style="color: green;">{esc}</span>'
388
- elif token[0] == '`':
389
- esc = html.escape(token)
390
- element = f'&nbsp;<span style="color: peru;">{esc}</span>'
391
- else:
392
- esc = html.escape(token)
393
- element = f'&nbsp;<span>{esc}</span>'
394
- output += element
395
- # Colour comment if present
396
- if comment_part is not None:
397
- esc = html.escape(comment_part)
398
- output += f'<span style="color: green;">&nbsp;{esc}</span>'
399
-
400
- return output
401
-
402
- ###########################################################################
403
- # Here when the user clicks a line number
404
- def onClickLino(self, lino):
405
- lineSpec = self.scriptLines[lino]
406
- lineSpec.bp = not lineSpec.bp
407
- if lineSpec.bp: lineSpec.label.showBlob()
408
- else: lineSpec.label.hideBlob()
409
- # Set a breakpoint on this command
410
- command = self.program.code[self.program.pc]
411
- command['bp'] = True
412
- self.program.code[self.program.pc] = command
413
-
414
- ###########################################################################
415
- # Scroll to a given line number
416
- def scrollTo(self, lino):
417
- # Ensure the line number is valid
418
- if lino < 0 or lino >= len(self.scriptLines):
419
- return
420
-
421
- # Get the panel widget for this line
422
- lineSpec = self.scriptLines[lino]
423
- panel = lineSpec.panel
424
-
425
- if not panel:
426
- return
427
-
428
- # Get the scroll area from the right column
429
- scroll_area = self.rightColumn.scroll
430
-
431
- # Get the vertical position of the panel relative to the content widget
432
- panel_y = panel.y()
433
- panel_height = panel.height()
434
-
435
- # Get the viewport height (visible area)
436
- viewport_height = scroll_area.viewport().height()
437
-
438
- # Calculate the target scroll position to center the panel
439
- # We want the panel's center to align with the viewport's center
440
- target_scroll = panel_y + (panel_height // 2) - (viewport_height // 2)
441
-
442
- # Clamp to valid scroll range
443
- scrollbar = scroll_area.verticalScrollBar()
444
- target_scroll = max(scrollbar.minimum(), min(target_scroll, scrollbar.maximum()))
445
-
446
- # Smoothly scroll to the target position
447
- scrollbar.setValue(target_scroll)
448
-
449
- # Bring the window to the front
450
- self.raise_()
451
- self.activateWindow()
452
-
453
- ###########################################################################
454
- # Here when each instruction is about to run
455
- def step(self):
456
- if self.stopped:
457
- lino=self.program.code[self.program.pc]['lino']
458
- print(lino)
459
- self.scrollTo(lino)
460
- return False
461
- else:
462
- if self.program.code[self.program.pc]['bp']:
463
- pass
464
- return True
@@ -1,20 +0,0 @@
1
- easycoder/__init__.py,sha256=MRZ5qqYf7XUeRfrculBoOmXvlayyAdVqU7wI5n47Bqs,339
2
- easycoder/close.png,sha256=3B9ueRNtEu9E4QNmZhdyC4VL6uqKvGmdfeFxIV9aO_Y,9847
3
- easycoder/ec_border.py,sha256=KpOy0Jq8jI_6DYGo4jaFvoBP_jTIoAYWrmuHhl-FXA4,2355
4
- easycoder/ec_classes.py,sha256=EWpB3Wta_jvKZ8SNIWua_ElIbw1FzKMyM3_IiXBn-lg,1995
5
- easycoder/ec_compiler.py,sha256=Q6a9nMmZogJzHu8OB4VeMzmBBarVEl3-RkqH2gW4LiU,6599
6
- easycoder/ec_condition.py,sha256=uamZrlW3Ej3R4bPDuduGB2f00M80Z1D0qV8muDx4Qfw,784
7
- easycoder/ec_core.py,sha256=F8MQ1dJfzQoYxTrCyJ7aUrmyxjQEW4dmWd9uq71rR0w,100876
8
- easycoder/ec_debug.py,sha256=JrF8GDzymB2YCOuULtlot4jO1yiVEbt_hQqjZPDcPEI,18785
9
- easycoder/ec_handler.py,sha256=doGCMXBCQxvSaD3omKMlXoR_LvQODxV7dZyoWafecyg,2336
10
- easycoder/ec_keyboard.py,sha256=H8DhPT8-IvAIGgRxCs4qSaF_AQLaS6lxxtDteejbktM,19010
11
- easycoder/ec_program.py,sha256=biRWYKUs7ywFEgTte0oerTyPxgZlP4odfv_xJAN7ao8,10696
12
- easycoder/ec_pyside.py,sha256=WDzJn64fPKMJCwTBXjngCRkJyS4fA72FmwfUoqul-Hw,58400
13
- easycoder/ec_timestamp.py,sha256=myQnnF-mT31_1dpQKv2VEAu4BCcbypvMdzq7_DUi1xc,277
14
- easycoder/ec_value.py,sha256=zgDJTJhIg3yOvmnnKIfccIizmIhGbtvL_ghLTL1T5fg,2516
15
- easycoder/tick.png,sha256=OedASXJJTYvnza4J6Kv5m5lz6DrBfy667zX_WGgtbmM,9127
16
- easycoder-251104.2.dist-info/entry_points.txt,sha256=JXAZbenl0TnsIft2FcGJbJ-4qoztVu2FuT8PFmWFexM,44
17
- easycoder-251104.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
18
- easycoder-251104.2.dist-info/WHEEL,sha256=Dyt6SBfaasWElUrURkknVFAZDHSTwxg3PaTza7RSbkY,100
19
- easycoder-251104.2.dist-info/METADATA,sha256=SFh8m9keUUTbvuC0q4R3n7lCuBA_whA4Q2Pt8s1EMWc,6897
20
- easycoder-251104.2.dist-info/RECORD,,
File without changes
File without changes