diff options
Diffstat (limited to 'lib/debug.py')
| -rw-r--r-- | lib/debug.py | 92 |
1 files changed, 55 insertions, 37 deletions
diff --git a/lib/debug.py b/lib/debug.py index a256bc0a..d3c19429 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -3,21 +3,25 @@ # Copyright (c) 2010 Authors # Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. -import atexit import os -import socket import sys -import time -from contextlib import contextmanager -from datetime import datetime +import atexit # to save svg file on exit +import socket # to check if debugger is running +import time # to measure time of code block, use time.monotonic() instead of time.time() +from datetime import datetime + +from contextlib import contextmanager # to measure time of with block +import configparser # to read DEBUG.ini +from pathlib import Path # to work with paths as objects import inkex -from lxml import etree +from lxml import etree # to create svg file from .svg import line_strings_to_path from .svg.tags import INKSCAPE_GROUPMODE, INKSCAPE_LABEL - +# decorator to check if debugging is enabled +# - if debug is not enabled then decorated function is not called def check_enabled(func): def decorated(self, *args, **kwargs): if self.enabled: @@ -26,13 +30,17 @@ def check_enabled(func): return decorated +# unwrapping = provision for functions as arguments +# - if argument is callable then it is called and return value is used as argument +# otherwise argument is returned as is def _unwrap(arg): if callable(arg): return arg() else: return arg - +# decorator to unwrap arguments if they are callable +# eg: if argument is lambda function then it is called and return value is used as argument def unwrap_arguments(func): def decorated(self, *args, **kwargs): unwrapped_args = [_unwrap(arg) for arg in args] @@ -69,21 +77,26 @@ class Debug(object): self.current_layer = None self.group_stack = [] + def enable(self, debug_type, debug_dir : Path, ini : configparser.ConfigParser): + # initilize file names and other parameters from DEBUG.ini file + self.debug_dir = debug_dir # directory where debug files are stored + self.debug_log_file = ini.get("DEBUG","debug_log_file", fallback="debug.log") + self.debug_svg_file = ini.get("DEBUG","debug_svg_file", fallback="debug.svg") + self.wait_attach = ini.getboolean("DEBUG","wait_attach", fallback=True) # currently only for vscode - def enable(self, debug_type, debug_file, wait_attach): if debug_type == 'none': return + self.debugger = debug_type - self.wait_attach = wait_attach self.enabled = True - self.init_log(debug_file) + self.init_log() self.init_debugger() self.init_svg() - def init_log(self, debug_file): - self.log_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), debug_file) + def init_log(self): + self.log_file = self.debug_dir / self.debug_log_file # delete old content - with open(self.log_file, "w"): + with self.log_file.open("w"): pass self.log("Debug logging enabled.") @@ -93,9 +106,14 @@ class Debug(object): # 1. Install LiClipse (liclipse.com) -- no need to install Eclipse first # 2. Start debug server as described here: http://www.pydev.org/manual_adv_remote_debugger.html # * follow the "Note:" to enable the debug server menu item - # 3. Create a file named "DEBUG" next to inkstitch.py in your git clone. + # 3. Copy and edit a file named "DEBUG.ini" from "DEBUG_template.ini" next to inkstitch.py in your git clone. # 4. Run any extension and PyDev will start debugging. + # debugger = vscode - 'debugpy' for vscode editor + # debugger = pycharm - 'pydevd-pycharm' for pycharm editor + # debugger = pydev - 'pydevd' for eclipse editor + # debugger = file - no debugger, only debug.log, debug.svg are used + ### # To debug with PyCharm: @@ -119,7 +137,7 @@ class Debug(object): # configuration. Set "IDE host name:" to "localhost" and "Port:" to 5678. # You can leave the default settings for all other choices. # - # 3. Touch a file named "DEBUG" at the top of your git repo, as above. + # 3. Touch a file named "DEBUG.ini" at the top of your git repo, as above. # # 4. Create a symbolic link in the Inkscape extensions directory to the # top-level directory of your git repo. On a mac, for example: @@ -132,16 +150,11 @@ class Debug(object): # extensions directory, or you'll see duplicate entries in the Ink/Stitch # extensions menu in Inkscape. # - # 5. In the execution env for Inkscape, set the environment variable - # PYCHARM_REMOTE_DEBUG to any value, and launch Inkscape. If you're starting - # Inkscape from the PyCharm Terminal pane, you can do: - # export PYCHARM_REMOTE_DEBUG=true;inkscape - # - # 6. In Pycharm, either click on the green "bug" icon if visible in the upper + # 5. In Pycharm, either click on the green "bug" icon if visible in the upper # right or press Ctrl-D to start debugging.The PyCharm debugger pane will # display the message "Waiting for process connection..." # - # 7. Do some action in Inkscape which invokes Ink/Stitch extension code, and the + # 6. Do some action in Inkscape which invokes Ink/Stitch extension code, and the # debugger will be triggered. If you've left "Suspend after connect" checked # in the Run configuration, PyCharm will pause in the "self.log("Enabled # PyDev debugger.)" statement, below. Uncheck the box to have it continue @@ -155,7 +168,7 @@ class Debug(object): # # 1. Install the Python extension for VS Code # pip install debugpy - # 2. create .vscode/launch.json containing somewhere: + # 2. create .vscode/launch.json containing: # "configurations": [ ... # { # "name": "Python: Attach", @@ -167,10 +180,11 @@ class Debug(object): # } # } # ] - # 3. Touch a file named "DEBUG" at the top of your git repo, as above. - # containing "vscode" or "vscode-script" see parse_file() in debug_mode.py for details + # 3. Touch a file named "DEBUG.ini" at the top of your git repo, as above. # 4. Start the debug server in VS Code by clicking on the debug icon in the left pane - # select "Python: Attach" from the dropdown menu and click on the green arrow + # select "Python: Attach" from the dropdown menu and click on the green arrow. + # The debug server will start and connect to already running python processes, + # but immediately exit if no python processes are running. # # Notes: # to see flask server url routes: @@ -184,11 +198,13 @@ class Debug(object): import pydevd_pycharm elif self.debugger == 'pydev': import pydevd + elif self.debugger == 'file': + pass else: raise ValueError(f"unknown debugger: '{self.debugger}'") except ImportError: - self.log("importing pydevd failed (debugger disabled)") + self.log(f"importing debugger failed (debugger disabled) for {self.debugger}") # pydevd likes to shout about errors to stderr whether I want it to or not with open(os.devnull, 'w') as devnull: @@ -207,6 +223,8 @@ class Debug(object): stderrToServer=True) elif self.debugger == 'pydev': pydevd.settrace() + elif self.debugger == 'file': + pass else: raise ValueError(f"unknown debugger: '{self.debugger}'") @@ -224,7 +242,7 @@ class Debug(object): def save_svg(self): tree = etree.ElementTree(self.svg) - debug_svg = os.path.join(os.path.dirname(os.path.dirname(__file__)), "debug.svg") + debug_svg = self.debug_dir / self.debug_svg_file tree.write(debug_svg) @check_enabled @@ -267,20 +285,21 @@ class Debug(object): timestamp = now.isoformat() self.last_log_time = now - with open(self.log_file, "a") as logfile: + with self.log_file.open("a") as logfile: print(timestamp, message % args, file=logfile) logfile.flush() + # decorator to measure time of function def time(self, func): def decorated(*args, **kwargs): if self.enabled: self.raw_log("entering %s()", func.__name__) - start = time.time() + start = time.monotonic() result = func(*args, **kwargs) if self.enabled: - end = time.time() + end = time.monotonic() self.raw_log("leaving %s(), duration = %s", func.__name__, round(end - start, 6)) return result @@ -348,20 +367,19 @@ class Debug(object): INKSCAPE_LABEL: name })) + # decorator to measure time of with block @contextmanager def time_this(self, label="code block"): if self.enabled: - start = time.time() + start = time.monotonic() self.raw_log("begin %s", label) yield if self.enabled: - self.raw_log("completed %s, duration = %s", label, time.time() - start) + self.raw_log("completed %s, duration = %s", label, time.monotonic() - start) +# global debug object debug = Debug() - -def enable(debug_type, debug_file, wait_attach): - debug.enable(debug_type, debug_file, wait_attach) |
