Editor: added code to move a breakpoint to a line actually creating some byte code (Python only). eric7

Tue, 24 Aug 2021 18:04:32 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Tue, 24 Aug 2021 18:04:32 +0200
branch
eric7
changeset 8539
24daea9ad41b
parent 8538
01b7559d3f4e
child 8540
ddeace12034a

Editor: added code to move a breakpoint to a line actually creating some byte code (Python only).

docs/changelog file | annotate | diff | comparison | revisions
eric7/Preferences/ConfigurationPages/DebuggerGeneralPage.py file | annotate | diff | comparison | revisions
eric7/Preferences/ConfigurationPages/DebuggerGeneralPage.ui file | annotate | diff | comparison | revisions
eric7/Preferences/__init__.py file | annotate | diff | comparison | revisions
eric7/QScintilla/Editor.py file | annotate | diff | comparison | revisions
eric7/UI/PythonDisViewer.py file | annotate | diff | comparison | revisions
--- a/docs/changelog	Tue Aug 24 17:20:58 2021 +0200
+++ b/docs/changelog	Tue Aug 24 18:04:32 2021 +0200
@@ -10,6 +10,8 @@
 - Editor
   -- added the capability to suppress syntax highlighting by associating
      the file type 'Text'
+  -- added code to move a breakpoint to a line actually creating some byte code
+     (Python only)
 - Plugin Uninstall Dialog
   -- added capability to uninstall several plugins with one invocation of the
      dialog
--- a/eric7/Preferences/ConfigurationPages/DebuggerGeneralPage.py	Tue Aug 24 17:20:58 2021 +0200
+++ b/eric7/Preferences/ConfigurationPages/DebuggerGeneralPage.py	Tue Aug 24 18:04:32 2021 +0200
@@ -142,6 +142,8 @@
             Preferences.getDebugger("MultiProcessEnabled"))
         self.debugThreeStateBreakPoint.setChecked(
             Preferences.getDebugger("ThreeStateBreakPoints"))
+        self.intelligentBreakPointCheckBox.setChecked(
+            Preferences.getDebugger("IntelligentBreakpoints"))
         self.recentFilesSpinBox.setValue(
             Preferences.getDebugger("RecentNumber"))
         self.exceptionBreakCheckBox.setChecked(
@@ -237,6 +239,9 @@
             "ThreeStateBreakPoints",
             self.debugThreeStateBreakPoint.isChecked())
         Preferences.setDebugger(
+            "IntelligentBreakpoints",
+            self.intelligentBreakPointCheckBox.isChecked())
+        Preferences.setDebugger(
             "RecentNumber",
             self.recentFilesSpinBox.value())
         Preferences.setDebugger(
--- a/eric7/Preferences/ConfigurationPages/DebuggerGeneralPage.ui	Tue Aug 24 17:20:58 2021 +0200
+++ b/eric7/Preferences/ConfigurationPages/DebuggerGeneralPage.ui	Tue Aug 24 18:04:32 2021 +0200
@@ -505,8 +505,8 @@
      <property name="title">
       <string>Breakpoints</string>
      </property>
-     <layout class="QVBoxLayout" name="verticalLayout_2">
-      <item>
+     <layout class="QGridLayout" name="gridLayout_4">
+      <item row="0" column="0">
        <widget class="QCheckBox" name="debugThreeStateBreakPoint">
         <property name="toolTip">
          <string>Select to change the breakpoint toggle order from Off-&gt;On-&gt;Off to Off-&gt;On (permanent)-&gt;On (temporary)-&gt;Off</string>
@@ -516,7 +516,17 @@
         </property>
        </widget>
       </item>
-      <item>
+      <item row="0" column="1">
+       <widget class="QCheckBox" name="intelligentBreakPointCheckBox">
+        <property name="toolTip">
+         <string>Select to move a breakpoint to a line generating executable code</string>
+        </property>
+        <property name="text">
+         <string>Intelligent breakpoint</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0" colspan="2">
        <layout class="QHBoxLayout" name="horizontalLayout_2">
         <item>
          <widget class="QLabel" name="label_4">
@@ -814,6 +824,7 @@
   <tabstop>automaticResetCheckBox</tabstop>
   <tabstop>multiprocessCheckBox</tabstop>
   <tabstop>debugThreeStateBreakPoint</tabstop>
+  <tabstop>intelligentBreakPointCheckBox</tabstop>
   <tabstop>recentFilesSpinBox</tabstop>
   <tabstop>exceptionBreakCheckBox</tabstop>
   <tabstop>exceptionShellCheckBox</tabstop>
--- a/eric7/Preferences/__init__.py	Tue Aug 24 17:20:58 2021 +0200
+++ b/eric7/Preferences/__init__.py	Tue Aug 24 18:04:32 2021 +0200
@@ -76,6 +76,7 @@
         # max. number of file names to be remembered for the add breakpoint
         # dialog
         "BreakAlways": False,
+        "IntelligentBreakpoints": True,
         "ShowExceptionInShell": True,
         "Python3VirtualEnv": "",
         "RubyInterpreter": "",
@@ -1722,19 +1723,23 @@
     Module function to retrieve the debugger settings.
     
     @param key the key of the value to get
+    @type str
     @param prefClass preferences class used as the storage area
+    @type Prefs
     @return the requested debugger setting
+    @rtype Any
     """
-    if key in ["RemoteDbgEnabled", "PassiveDbgEnabled",
+    if key in ("RemoteDbgEnabled", "PassiveDbgEnabled",
                "AutomaticReset", "DebugEnvironmentReplace",
                "PythonRedirect", "PythonNoEncoding",
                "Python3Redirect", "Python3NoEncoding",
                "RubyRedirect",
                "ConsoleDbgEnabled", "PathTranslation",
                "Autosave", "ThreeStateBreakPoints",
-               "BreakAlways", "AutoViewSourceCode",
-               "ShowExceptionInShell", "MultiProcessEnabled",
-               ]:
+               "BreakAlways", "IntelligentBreakpoints",
+               "AutoViewSourceCode", "ShowExceptionInShell",
+               "MultiProcessEnabled",
+               ):
         return toBool(prefClass.settings.value(
             "Debugger/" + key, prefClass.debuggerDefaults[key]))
     elif key in ["PassiveDbgPort", "MaxVariableSize", "RecentNumber"]:
@@ -1782,8 +1787,11 @@
     Module function to store the debugger settings.
     
     @param key the key of the setting to be set
+    @type str
     @param value the value to be set
+    @type Any
     @param prefClass preferences class used as the storage area
+    @type Prefs
     """
     prefClass.settings.setValue("Debugger/" + key, value)
 
--- a/eric7/QScintilla/Editor.py	Tue Aug 24 17:20:58 2021 +0200
+++ b/eric7/QScintilla/Editor.py	Tue Aug 24 18:04:32 2021 +0200
@@ -7,6 +7,7 @@
 Module implementing the editor component of the eric IDE.
 """
 
+import bisect
 import collections
 import contextlib
 import difflib
@@ -44,6 +45,8 @@
 
 import UI.PixmapCache
 
+from UI import PythonDisViewer
+
 EditorAutoCompletionListID = 1
 TemplateCompletionListID = 2
 ReferencesListID = 3
@@ -2415,6 +2418,23 @@
         @param temporary flag indicating a temporary breakpoint (boolean)
         """
         if self.fileName and self.isPyFile():
+            linestarts = PythonDisViewer.linestarts(self.text())
+            if line not in linestarts:
+                if Preferences.getDebugger("IntelligentBreakpoints"):
+                    # change line to the next one starting an instruction block
+                    index = bisect.bisect(linestarts, line)
+                    with contextlib.suppress(IndexError):
+                        line = linestarts[index]
+                        self.__toggleBreakpoint(line, temporary=temporary)
+                else:
+                    EricMessageBox.warning(
+                        self,
+                        self.tr("Add Breakpoint"),
+                        self.tr("No Python byte code will be created for the"
+                                " selected line. No break point will be set!")
+                    )
+                return
+            
             self.breakpointModel.addBreakPoint(
                 self.fileName, line, ('', temporary, True, 0))
             self.breakpointToggled.emit(self)
--- a/eric7/UI/PythonDisViewer.py	Tue Aug 24 17:20:58 2021 +0200
+++ b/eric7/UI/PythonDisViewer.py	Tue Aug 24 18:04:32 2021 +0200
@@ -477,7 +477,7 @@
         
         with EricOverrideCursor():
             try:
-                codeObject = self.__tryCompile(source, filename)
+                codeObject = tryCompile(source, filename)
             except Exception as exc:
                 codeObject = None
                 self.__createErrorItem(str(exc))
@@ -652,24 +652,6 @@
                                    expand=True)
             self.__editor.setHighlight(startLine - 1, 0, endLine, -1)
     
-    def __tryCompile(self, source, name):
-        """
-        Private method to attempt to compile the given source, first as an
-        expression and then as a statement if the first approach fails.
-        
-        @param source source code string to be compiled
-        @type str
-        @param name name of the file containing the source
-        @type str
-        @return compiled code
-        @rtype code object
-        """
-        try:
-            c = compile(source, name, 'eval')
-        except SyntaxError:
-            c = compile(source, name, 'exec')
-        return c
-    
     def __disassembleObject(self, co, parentItem, parentName="", lasti=-1):
         """
         Private method to disassemble the given code object recursively.
@@ -888,3 +870,51 @@
         """
         ericApp().getObject("UserInterface").showPreferences(
             "pythonPage")
+
+
+def tryCompile(source, name):
+    """
+    Function to attempt to compile the given source, first as an
+    expression and then as a statement if the first approach fails.
+    
+    @param source source code string to be compiled
+    @type str
+    @param name name of the file containing the source
+    @type str
+    @return compiled code
+    @rtype code object
+    """
+    try:
+        c = compile(source, name, 'eval')
+    except SyntaxError:
+        c = compile(source, name, 'exec')
+    return c
+
+
+def linestarts(co, filename="", getall=True):
+    """
+    Function to get the line starts for the given code object
+    
+    @param co reference to the compiled code object or the source code
+    @type code object or str
+    @param filename name of the source file (optional)
+    @type str
+    @param getall flag indicating to get all line starts recursively
+    @type bool
+    @return list of lines starting some byte code instruction block
+    @rtype list of int
+    """
+    if isinstance(co, str):
+        # try to compile the given source code first
+        try:
+            fn = filename if filename else "<dis>"
+            co = tryCompile(co, fn)
+        except SyntaxError:
+            return []
+    
+    starts = [inst[1] for inst in dis.findlinestarts(co)]
+    if getall:
+        for x in co.co_consts:
+            if hasattr(x, 'co_code'):
+                starts.extend(linestarts(x))
+    return sorted(starts)

eric ide

mercurial