diff --git a/DEBUG_template.ini b/DEBUG_template.ini index e04d78b0a..41d067966 100644 --- a/DEBUG_template.ini +++ b/DEBUG_template.ini @@ -3,11 +3,16 @@ ; prefer_pip_inkex = False [DEBUG] -;;; select one active debugger, default: none -; debugger = vscode -; debugger = pycharm -; debugger = pydev -; debugger = file +;;; select one active debug_type, default: none +; debug_type = vscode +; debug_type = pycharm +; debug_type = pydev + +;;; enable debugger, see cmd line arg -d, default: False +; debug_enable = True + +;;; debug log output to file even if debugger is not enabled, default: False +; debug_to_file = True ;;; disable debugger when calling from inkscape, default: False ; disable_from_inkscape = True @@ -28,12 +33,14 @@ ; bash_file_base = debug_inkstitch [PROFILE] -;;; select one active profiler, default: none -; profiler = cprofile -; profiler = profile -; profiler = pyinstrument +;;; select one active profiler_type, default: none +; profiler_type = cprofile +; profiler_type = profile +; profiler_type = pyinstrument + +;;; enable profiler, see cmd line arg -p, default: False +; profile_enable = True ;;; base name for profile output files, default: debug_profile ; profile_file_base = debug_profile - diff --git a/inkstitch.py b/inkstitch.py index 514d42cd5..c00fcc042 100644 --- a/inkstitch.py +++ b/inkstitch.py @@ -41,16 +41,36 @@ else: debug_active = bool((gettrace := getattr(sys, 'gettrace')) and gettrace()) # check if debugger is active on startup debug_type = 'none' -profile_type = 'none' +profiler_type = 'none' if not running_as_frozen: # debugging/profiling only in development mode # specify debugger type - # - if script was already started from debugger then don't read debug type from ini file + # - if script was already started from debugger then don't read debug type from ini file or cmd line if not debug_active: - debug_type = ini.get("DEBUG","debugger", fallback="none") # debugger type vscode, pycharm, pydevd, file + # enable/disable debugger + if os.environ.get('INKSTITCH_DEBUG_ENABLE', '').lower() in ['true', '1', 'yes', 'y']: + debug_enable = True + else: + debug_enable = ini.getboolean("DEBUG","debug_enable", fallback=False) # enable debugger on startup from ini + + debug_type = ini.get("DEBUG","debug_type", fallback="none") # debugger type vscode, pycharm, pydevd + if not debug_enable: + debug_type = 'none' + + debug_to_file = ini.getboolean("DEBUG","debug_to_file", fallback=False) # write debug output to file + if debug_to_file and debug_type == 'none': + debug_type = 'file' + + # enbale/disable profiling + if os.environ.get('INKSTITCH_PROFILE_ENABLE', '').lower() in ['true', '1', 'yes', 'y']: + profile_enable = True + else: + profile_enable = ini.getboolean("PROFILE","profile_enable", fallback=False) # read from ini # specify profiler type - profile_type = ini.get("PROFILE","profiler", fallback="none") # profiler type cprofile, profile, pyinstrument + profiler_type = ini.get("PROFILE","profiler_type", fallback="none") # profiler type cprofile, profile, pyinstrument + if not profile_enable: + profiler_type = 'none' if running_from_inkscape: # process creation of the Bash script - should be done before sys.path is modified, see below in prefere_pip_inkex @@ -96,7 +116,7 @@ if running_as_frozen or not debug_active: warnings.filterwarnings('ignore') # TODO - check if this is still for shapely needed, apparently, shapely uses only exceptions instead of io. -# all logs were removed from version 2.0.0, if we ensure that shapely is always >= 2.0.0 +# all logs were removed from version 2.0.0, ensure that shapely is always >= 2.0.0 # ---- plan to remove this in future ---- # set logger for shapely - for old versions of shapely @@ -127,11 +147,11 @@ extension = extension_class() # create instance of extension class - call __ini # extension run(), we differentiate between debug and normal mode # - in debug or profile mode we debug or profile extension.run() method # - in normal mode we run extension.run() in try/except block to catch all exceptions and hide GTK spam -if debug_active or profile_type != "none": # if debug or profile mode - if profile_type == 'none': # only debugging +if debug_active or profiler_type != "none": # if debug or profile mode + if profiler_type == 'none': # only debugging extension.run(args=remaining_args) else: # do profiling - debug_utils.profile(profile_type, SCRIPTDIR, ini, extension, remaining_args) + debug_utils.profile(profiler_type, SCRIPTDIR, ini, extension, remaining_args) else: # if not debug nor profile mode save_stderr() # hide GTK spam @@ -154,7 +174,7 @@ else: # if not debug nor profile mode finally: restore_stderr() - if shapely_errors.tell(): - errormsg(shapely_errors.getvalue()) + # if shapely_errors.tell(): + # errormsg(shapely_errors.getvalue()) sys.exit(0) diff --git a/lib/debug_utils.py b/lib/debug_utils.py index 298a7b806..ab2a6ca9e 100644 --- a/lib/debug_utils.py +++ b/lib/debug_utils.py @@ -13,7 +13,6 @@ import configparser # to read DEBUG.ini # - later sys.path may be modified that influences importing inkex (see prefere_pip_inkex) - def write_offline_debug_script(debug_script_dir : Path, ini : configparser.ConfigParser): ''' prepare Bash script for offline debugging from console @@ -42,30 +41,40 @@ def write_offline_debug_script(debug_script_dir : Path, ini : configparser.Confi bash_file = debug_script_dir / bash_name with open(bash_file, 'w') as f: # "w" text mode, automatic conversion of \n to os.linesep - f.write(f"#!/usr/bin/env bash\n\n") - f.write(f"# python version: {sys.version}\n") # python version + f.write(f'#!/usr/bin/env bash\n') + + # cmd line arguments for debugging and profiling + f.write(bash_parser()) # parse cmd line arguments: -d -p + + f.write(f'# python version: {sys.version}\n') # python version myargs = " ".join(sys.argv[1:]) f.write(f'# script: {sys.argv[0]} arguments: {myargs}\n') # script name and arguments + # environment PATH + f.write(f'# PATH:\n') + f.write(f'# {os.environ["PATH"]}\n') + # for p in os.environ.get("PATH", '').split(os.pathsep): # PATH to list + # f.write(f'# {p}\n') + # python module path - f.write(f"# python sys.path:\n") + f.write(f'# python sys.path:\n') for p in sys.path: - f.write(f"# {p}\n") + f.write(f'# {p}\n') # see static void set_extensions_env() in inkscape/src/inkscape-main.cpp - f.write(f"# PYTHONPATH:\n") + f.write(f'# PYTHONPATH:\n') for p in os.environ.get('PYTHONPATH', '').split(os.pathsep): # PYTHONPATH to list - f.write(f"# {p}\n") + f.write(f'# {p}\n') - f.write(f"# copy {svg_file} to {bash_svg}\n") + f.write(f'# copy {svg_file} to {bash_svg}\n') shutil.copy(svg_file, debug_script_dir / bash_svg) # copy file to bash_svg myargs = myargs.replace(str(svg_file), str(bash_svg)) # replace file name with bash_svg # see void Extension::set_environment() in inkscape/src/extension/extension.cpp - notexported = ["SELF_CALL"] # if an extension calls inkscape itself - exported = ["INKEX_GETTEXT_DOMAIN", "INKEX_GETTEXT_DIRECTORY", - "INKSCAPE_PROFILE_DIR", "DOCUMENT_PATH", "PYTHONPATH"] + notexported = ['SELF_CALL'] # if an extension calls inkscape itself + exported = ['INKEX_GETTEXT_DOMAIN', 'INKEX_GETTEXT_DIRECTORY', + 'INKSCAPE_PROFILE_DIR', 'DOCUMENT_PATH', 'PYTHONPATH'] for k in notexported: if k in os.environ: f.write(f'# export {k}="{os.environ[k]}"\n') @@ -77,10 +86,48 @@ def write_offline_debug_script(debug_script_dir : Path, ini : configparser.Confi f.write(f'export INKSTITCH_OFFLINE_SCRIPT="True"\n') f.write('# call inkstitch\n') - f.write(f"python3 inkstitch.py {myargs}\n") + f.write(f'python3 inkstitch.py {myargs}\n') bash_file.chmod(0o0755) # make file executable, hopefully ignored on Windows +def bash_parser(): + return ''' +set -e # exit on error + +# parse cmd line arguments: +# -d enable debugging +# -p enable profiling +# ":..." - silent error reporting +while getopts ":dp" opt; do + case $opt in + d) + arg_d="true" + ;; + p) + arg_p="true" + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + exit 1 + ;; + esac +done + +# -v: check if variable is set +if [[ -v arg_d ]]; then + export INKSTITCH_DEBUG_ENABLE="True" +fi +if [[ -v arg_p ]]; then + export INKSTITCH_PROFILE_ENABLE="True" +fi + +''' + + def reorder_sys_path(): ''' change sys.path to prefer pip installed inkex over inkscape bundled inkex