From d6b7bb8c74914d1db0f3ab2b38009077a8e5b466 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Fri, 2 Jan 2026 13:29:02 -0500 Subject: [PATCH] GP-6283: Fixing PyGhidra API inadvertently squashing some exceptions --- Ghidra/Features/PyGhidra/src/main/py/README.md | 11 +++++++++-- .../PyGhidra/src/main/py/src/pyghidra/__init__.py | 3 ++- .../PyGhidra/src/main/py/src/pyghidra/api.py | 15 ++++++++++----- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Ghidra/Features/PyGhidra/src/main/py/README.md b/Ghidra/Features/PyGhidra/src/main/py/README.md index 591a4198b8..ed42b65a94 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/README.md +++ b/Ghidra/Features/PyGhidra/src/main/py/README.md @@ -120,7 +120,7 @@ def consume_program( object was provided, the same consumer object is returned. Otherwise, a new consumer object is created and returned. :raises FileNotFoundError: If the path does not exist in the project. - :raises TypeError: If the path in the project exists but is not a Program. + :raises pyghidra.ProgramTypeError: If the path in the project exists but is not a Program. """ ``` @@ -139,7 +139,7 @@ def program_context( :param path: The project path of the program (should start with "/"). :return: The Ghidra program. :raises FileNotFoundError: If the path does not exist in the project. - :raises TypeError: If the path in the project exists but is not a Program. + :raises pyghidra.ProgramTypeError: If the path in the project exists but is not a Program. """ ``` @@ -277,6 +277,12 @@ def walk_programs( """ ``` +### pyghidra.ProgramTypeError +```python +class ProgramTypeError(TypeError): + """Custom exception for when a Program was expected but not received.""" +``` + ## Example The following example, while not very useful, showcases much of the API: ```python @@ -564,6 +570,7 @@ import pdb_ # imports Ghidra's pdb __3.0.2__ * Fixed an issue that prevented [`pyghidra.analysis_properties()`](#pyghidraanalysis_properties) from having access to all of the analysis properties. +* Fixed issues related to the PyGhidra API inadvertently squashing exceptions __3.0.1__ * Fixed `AttributeError: module 'pyghidra' has no attribute 'program_conext'` when performing a diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__init__.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__init__.py index e9cd49d2d4..bcc7a0a26e 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__init__.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/__init__.py @@ -59,6 +59,7 @@ from pyghidra.api import start, started from pyghidra.api import open_project, open_filesystem, consume_program, program_context, analyze from pyghidra.api import ghidra_script, transaction, analysis_properties, program_info from pyghidra.api import program_loader, task_monitor, walk_project, walk_programs +from pyghidra.api import ProgramTypeError from pyghidra.launcher import DeferredPyGhidraLauncher, GuiPyGhidraLauncher, HeadlessPyGhidraLauncher from pyghidra.script import get_current_interpreter from pyghidra.version import ApplicationInfo, ExtensionDetails @@ -70,5 +71,5 @@ __all__ = [ "HeadlessPyGhidraLauncher", "start", "started", "open_project", "open_filesystem", "consume_program", "program_context", "analyze", "ghidra_script", "transaction", "analysis_properties", "program_info", "program_loader", "task_monitor", "walk_project", - "walk_programs" + "walk_programs", "ProgramTypeError" ] diff --git a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/api.py b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/api.py index a29b5048d8..7fe59fe968 100644 --- a/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/api.py +++ b/Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/api.py @@ -121,7 +121,7 @@ def consume_program( object was provided, the same consumer object is returned. Otherwise, a new consumer object is created and returned. :raises FileNotFoundError: If the path does not exist in the project. - :raises TypeError: If the path in the project exists but is not a Program. + :raises pyghidra.ProgramTypeError: If the path in the project exists but is not a Program. """ from ghidra.program.model.listing import Program from java.lang import Object # type:ignore @UnresolvedImport @@ -135,7 +135,7 @@ def consume_program( program_cls = Program.class_ if not program_cls.isAssignableFrom(dobj.getClass()): dobj.release(consumer) - raise TypeError(f'"{path}" exists but is not a Program') + raise ProgramTypeError(f'"{path}" exists but is not a Program') return dobj, consumer @contextlib.contextmanager @@ -151,7 +151,7 @@ def program_context( :param path: The project path of the program (should start with "/"). :return: The Ghidra program. :raises FileNotFoundError: If the path does not exist in the project. - :raises TypeError: If the path in the project exists but is not a Program. + :raises pyghidra.ProgramTypeError: If the path in the project exists but is not a Program. """ program, consumer = consume_program(project, path) try: @@ -270,6 +270,7 @@ def transaction( yield transaction_id except: success = False + raise finally: program.endTransaction(transaction_id, success) @@ -361,7 +362,11 @@ def walk_programs( with program_context(project, file.getPathname()) as program: if program_filter(file, program): callback(file, program) - except TypeError: - pass # skip over non-programs + except ProgramTypeError: + pass # skip over non-programs walk_project(project, process, start=start) + +class ProgramTypeError(TypeError): + """Custom exception for when a Program was expected but not received.""" + pass