liveConsole 1.7.6__py3-none-any.whl → 1.7.8__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 liveConsole might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: liveConsole
3
- Version: 1.7.6
3
+ Version: 1.7.8
4
4
  Summary: An IDLE-like debugger to allow for real-time command injection for debugging and testing python code
5
5
  Author-email: Tzur Soffer <tzur.soffer@gmail.com>
6
6
  License: MIT
@@ -1,19 +1,19 @@
1
1
  liveConsole/__init__.py,sha256=GyV_Y3iiiS12JoEiqE55uZPQ8ZDYfOSKvxh5rcfaD1U,20
2
- liveconsole-1.7.6.dist-info/licenses/LICENSE,sha256=7dZ0zL72aGaFE0C9DxacOpnaSkC5jajhG6iL7lqhWmU,1064
2
+ liveconsole-1.7.8.dist-info/licenses/LICENSE,sha256=7dZ0zL72aGaFE0C9DxacOpnaSkC5jajhG6iL7lqhWmU,1064
3
3
  pysole/__init__.py,sha256=0Jq2s5WxFVveZW-pGwThGZLt_mVSdGcsT9lpJNEZ6ds,124
4
4
  pysole/__main__.py,sha256=QvVFH8J2yzgQaF9MosQ6ajCW67uiRbYliePsTEUCIAg,82
5
5
  pysole/commandHistory.py,sha256=xJtWbJ_vgJo2QGgaZJsApTOi_Hm8Yz0V9_zqQUCItj8,1084
6
6
  pysole/helpTab.py,sha256=o0uSY-8sw7FuuBrt0FQwcgK6ljNVx3IgRnW7heZ6GvY,2454
7
7
  pysole/liveConsole.py,sha256=lzS3dphAQ1i8pQC4E2FY-FyMMSKi-dAN0xr6XUgrNmo,168
8
8
  pysole/mainConsole.py,sha256=t_ijaSm2rTxM5CrLqjxT5Oq_OFwxk7JsPOAXxo9dbdE,15357
9
- pysole/pysole.py,sha256=gQgjzmnXesUCWk2OnOdAGRcYZlw86QH4OeeVQhD_6Zs,12970
9
+ pysole/pysole.py,sha256=QxcLjy2SyxXPkzv54SY2BBC1zJ8SqrDtRzhHQqVDw6I,13775
10
10
  pysole/settings.json,sha256=cmOtIhRDWHMwmQMESuykWuzJd_jG6iT2tJ-uhSps_3U,722
11
11
  pysole/styledTextbox.py,sha256=qWnwGAKjl7R2HYPXQOr8GrGYIr4JC8PzmeioOCH7hGo,2236
12
- pysole/suggestionManager.py,sha256=EUFeCQoZnLS8EjPPNuZpzjY0BR07m2KP5TloyaMDVGY,6457
12
+ pysole/suggestionManager.py,sha256=CGR1wsRIU4le3Bq_6CPAytM1CzTQAV_jUl5KZbg9LPU,6544
13
13
  pysole/themes.json,sha256=2KvEfxm-eDsfVKIdBhWk-Qd93wYQub3YwkxbS6CGKH0,2525
14
- pysole/utils.py,sha256=cBjS3KxDCuVr4Wa8up0eH493cLky9_CFImO-kaiWakQ,339
15
- liveconsole-1.7.6.dist-info/METADATA,sha256=HvzEQCn9tzYeDfsdF9Oa6YIdY2x2D6U17TOSzsIB8eE,10407
16
- liveconsole-1.7.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
- liveconsole-1.7.6.dist-info/entry_points.txt,sha256=qtvuJHcex4QqM97x_UawFWJYnfhQRl0jhqLcWRpnAGo,84
18
- liveconsole-1.7.6.dist-info/top_level.txt,sha256=YGhC2H7bvcDnMwEtvLXF6qQNIZT2Saayyk1KOJyndN8,19
19
- liveconsole-1.7.6.dist-info/RECORD,,
14
+ pysole/utils.py,sha256=cKsSPWeYRxPhkiM8Xrb-aMW6w0SU5Xuu9WCgsu6_xtA,1364
15
+ liveconsole-1.7.8.dist-info/METADATA,sha256=ra5xvWuvJAaTs-0JAgcxo_rclzPm0He5Ij1p6lShW5c,10407
16
+ liveconsole-1.7.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ liveconsole-1.7.8.dist-info/entry_points.txt,sha256=qtvuJHcex4QqM97x_UawFWJYnfhQRl0jhqLcWRpnAGo,84
18
+ liveconsole-1.7.8.dist-info/top_level.txt,sha256=YGhC2H7bvcDnMwEtvLXF6qQNIZT2Saayyk1KOJyndN8,19
19
+ liveconsole-1.7.8.dist-info/RECORD,,
pysole/pysole.py CHANGED
@@ -8,7 +8,7 @@ import sys
8
8
  import io
9
9
  import json
10
10
 
11
- from .utils import settingsPath, themesPath, stdPrint
11
+ from .utils import settingsPath, themesPath, stdPrint, normalizeWhitespace, findUnindentedLine
12
12
  from .helpTab import HelpTab
13
13
  from .mainConsole import InteractiveConsoleText
14
14
 
@@ -73,6 +73,7 @@ class InteractiveConsole(ctk.CTk):
73
73
  if userLocals is None:
74
74
  userLocals = callerFrame.f_locals
75
75
 
76
+ self.callerFrame = callerFrame
76
77
  self.userGlobals = userGlobals
77
78
  self.userLocals = userLocals
78
79
 
@@ -87,20 +88,30 @@ class InteractiveConsole(ctk.CTk):
87
88
  self.runRemainingCode = runRemainingCode
88
89
  self.printStartupCode = printStartupCode
89
90
  self.removeWaterMark = removeWaterMark
90
- self.startupCode = ()
91
+ self.startupCode = []
91
92
  if runRemainingCode:
92
- code_obj = callerFrame.f_code
93
- filename = code_obj.co_filename
94
-
95
- # Read the rest of the file after the call to probe()
96
- with open(filename, "r", encoding="utf-8") as f:
97
- lines = f.readlines()
98
-
99
- startLineIndex = callerFrame.f_lineno
100
- startLine = lines[startLineIndex - 1]
101
- leadingWhitespaceLen = len(startLine) - len(startLine.lstrip())
102
- self.startupCode = lines[startLineIndex:]
103
- self.startupCode = [line.rstrip()[leadingWhitespaceLen:] for line in self.startupCode]
93
+ self.startupCode = self._getStartupCode()
94
+
95
+ def _getStartupCode(self):
96
+ code_obj = self.callerFrame.f_code
97
+ callStartLineIndex = inspect.getframeinfo(self.callerFrame).positions.lineno #< start of the probe call
98
+ callEndLineIndex = inspect.getframeinfo(self.callerFrame).positions.end_lineno #< end of the probe call
99
+ filename = code_obj.co_filename
100
+
101
+ # Read the rest of the file after the call to probe()
102
+ with open(filename, "r", encoding="utf-8") as f:
103
+ lines = f.readlines()
104
+
105
+ startLine = lines[callStartLineIndex-1]
106
+ for line in lines[callStartLineIndex:callEndLineIndex]:
107
+ startLine += line.strip()
108
+
109
+ startupCode = normalizeWhitespace(lines[callEndLineIndex:]) #< ensure the code is not indented too much (egg if in __name__ == "__main__")
110
+ firstUnindentedLine = findUnindentedLine(startupCode)
111
+ while firstUnindentedLine != 0 and firstUnindentedLine != None: #< handle if probe is inside a loop/if/etc by simply unindenting the call (while is for nested calls)
112
+ startupCode[:firstUnindentedLine] = normalizeWhitespace(startupCode[:firstUnindentedLine])
113
+ firstUnindentedLine = findUnindentedLine(startupCode)
114
+ return(startupCode)
104
115
 
105
116
  def _createMenu(self):
106
117
  """Create a menu bar using CTkOptionMenu."""
@@ -202,7 +213,7 @@ class InteractiveConsole(ctk.CTk):
202
213
  def saveSettings():
203
214
  try:
204
215
  newSettings = json.loads(textbox.get("0.0", "end-1c"))
205
- with open("settings.json", "w") as f:
216
+ with open(settingsPath, "w") as f:
206
217
  json.dump(newSettings, f, indent=4)
207
218
  messagebox.showinfo("Success", "Settings saved!")
208
219
  editor.destroy()
@@ -107,8 +107,11 @@ class CodeSuggestionManager:
107
107
  self.suggestionListbox.selection_set(0)
108
108
 
109
109
  # Position window near cursor
110
- self._positionSuggestionWindow()
111
- self.suggestionWindow.deiconify()
110
+ try: #< some weird errors idk
111
+ self._positionSuggestionWindow()
112
+ self.suggestionWindow.deiconify()
113
+ except:
114
+ pass
112
115
 
113
116
  def _createSuggestionWindow(self):
114
117
  """Create the suggestion popup window."""
pysole/utils.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import sys
3
+ import re
3
4
 
4
5
  settingsPath = os.path.join(os.path.dirname(__file__), "settings.json")
5
6
  themesPath = os.path.join(os.path.dirname(__file__), "themes.json")
@@ -10,4 +11,35 @@ def stdPrint(text):
10
11
  sys.__stdout__.write(f"{text}\n")
11
12
  sys.__stdout__.flush()
12
13
  except:
13
- pass
14
+ pass
15
+
16
+ def normalizeWhitespace(lines):
17
+ """
18
+ Normalize leading whitespace across a list of code lines.
19
+ Removes common leading indentation and trims excess on over-indented lines.
20
+ """
21
+
22
+ if type(lines) == str:
23
+ lines = lines.split('\n')
24
+
25
+ # Remove empty lines and preserve original line endings
26
+ strippedLines = [line.rstrip('\n') for line in lines if line.strip()]
27
+ if not strippedLines:
28
+ return []
29
+
30
+ # Find minimum indentation across non-empty lines
31
+ indentLevels = [
32
+ len(re.match(r'^[ \t]*', line).group())
33
+ for line in strippedLines
34
+ ]
35
+ minIndent = min(indentLevels)
36
+
37
+
38
+ normalized = [line[minIndent:] if len(line) >= minIndent else line for line in lines] #< Normalize by removing minIndent from each line
39
+ return(normalized)
40
+
41
+ def findUnindentedLine(lines):
42
+ for i, line in enumerate(lines):
43
+ if re.match(r'^\S', line): # Line starts with non-whitespace
44
+ return(i)
45
+ return(None)