Merge remote-tracking branch

'origin/GP-6664_ryanmkurtz_pyghidra-exceptions'
This commit is contained in:
Ryan Kurtz
2026-06-09 14:21:28 -04:00
4 changed files with 37 additions and 18 deletions

View File

@@ -570,6 +570,7 @@ import pdb_ # imports Ghidra's pdb
__3.2.0__
* PyGhidra's `help()` override can now show `public/private/protected` on Ghidra Java fields and
methods (available in Ghidra 12.2 and later).
* When an uncaught `JException` occurs, PyGhidra will now output the full Java stack trace.
__3.1.0__
* PyGhidra will now, by default, restore `sys.modules` to its prior state after a PyGhidra script is

View File

@@ -20,6 +20,7 @@ import logging
import re
import sys
import threading
import traceback
import types
from code import InteractiveConsole
from typing import Generator
@@ -35,10 +36,10 @@ from java.lang import String # type:ignore @UnresolvedImport
from java.lang import Thread as JThread # type:ignore @UnresolvedImport
from java.util import Collections # type:ignore @UnresolvedImport
from java.util.function import Consumer # type:ignore @UnresolvedImport
from jpype import JClass, JImplements, JOverride
from jpype import JClass, JImplements, JOverride, JException
from pyghidra.internal.plugin.completions import PythonCodeCompleter
from pyghidra.script import PyGhidraScript
from pyghidra.script import PyGhidraScript, print_stacktrace
logger = logging.getLogger(__name__)
@@ -296,8 +297,7 @@ class PyConsole(InteractiveConsole):
super().showsyntaxerror(filename=filename, **kwargs)
def showtraceback(self) -> None:
with self.redirect_writer():
super().showtraceback()
print_stacktrace(self._script, "<console>")
@contextlib.contextmanager
def _run_context(self) -> Generator[None, None, None]:

View File

@@ -32,7 +32,7 @@ from pathlib import Path
from typing import Generator, List, NoReturn, Tuple, Union
import jpype
from jpype import imports, _jpype
from jpype import imports, _jpype, JException
from packaging.version import Version
from pyghidra.javac import java_compile
@@ -176,6 +176,12 @@ def _lastrun() -> Path:
return None
def _pyghidra_excepthook(exc_type, exc_value, tb):
sys.__excepthook__(exc_type, exc_value, tb)
if isinstance(exc_type, JException):
# Remove the first line of the Java stack trace...it's already output
sys.stderr.write(exc_value.stacktrace().partition("\n")[2])
class PyGhidraLauncher:
"""
Base pyghidra launcher
@@ -221,6 +227,10 @@ class PyGhidraLauncher:
self.vm_args = self._jvm_args()
self.args = []
self.app_info = ApplicationInfo.from_file(ghidra_dir / "application.properties")
# Install our custom excepthook (only if no one else already has)
if sys.excepthook == sys.__excepthook__:
sys.excepthook = _pyghidra_excepthook
def _setup_dev_classpath(self, utility_dir: Path):
"""

View File

@@ -22,7 +22,7 @@ import traceback
from collections.abc import ItemsView, KeysView
from importlib.machinery import ModuleSpec, SourceFileLoader
from pathlib import Path
from jpype import JClass, JImplementationFor
from jpype import JClass, JImplementationFor, JException
from typing import List
@@ -255,18 +255,7 @@ class PyGhidraScript(dict):
spec.loader.exec_module(m)
# pylint: disable=bare-except
except:
# filter the traceback so that it stops at the script
exc_type, exc_value, exc_tb = sys.exc_info()
i = 0
tb = traceback.extract_tb(exc_tb)
for fs in tb:
if fs.filename == script_path:
break
i += 1
ss = traceback.StackSummary.from_list(tb[i:])
e = traceback.TracebackException(exc_type, exc_value, exc_tb)
e.stack = ss
self._script.printerr(''.join(e.format()))
print_stacktrace(self._script, script_path)
finally:
sys.argv = orig_argv
@@ -320,3 +309,22 @@ def get_current_interpreter():
except ImportError:
return None
def print_stacktrace(script, script_path):
exc_type, exc_value, exc_tb = sys.exc_info()
i = 0
tb = traceback.extract_tb(exc_tb)
for fs in tb:
if fs.filename == script_path:
break
i += 1
ss = traceback.StackSummary.from_list(tb[i:])
te = traceback.TracebackException(exc_type, exc_value, exc_tb)
te.stack = ss
output = ''.join(te.format())
if isinstance(exc_type, JException):
# Remove the first line of the Java stack trace...it's already output
output += exc_value.stacktrace().partition('\n')[2]
script.printerr(output)