ExtensionDetails.from_file split each line on every '=', so it raised
ValueError on any properties file whose value contains an '=', and on
blank or comment lines.
PyGhidra writes this file itself (_install_plugin does
ext.write_text(str(details))) and reads it back in _uninstall_old_plugin,
so a plugin whose description contains an '=' produced a file PyGhidra
could not parse. The read is wrapped in a bare except that only logs, so
the outdated extension is silently left installed instead.
str.partition keeps the first '=' as the separator and lets lines without
one be skipped, matching what ApplicationInfo.from_file already does.
is run, or the interactive interpreter is reset.
Can be disabled by uncommenting
"#VMARGS=-Dpyghidra.sys.modules.restore.disable=true" in
launch.properties if its found to cause problems.
pyghidra_launcher.py can now use an existing pyghidra
installation in an externally managed environment. Upgrading is disabled
in this scenario.
Fixed an issue with getting the package version from an arbitrary
environment
Verifying the type annotations used by PyGhidra with Mypy static type
checker leads to the following error:
core.py:171: error: Argument 1 to "contextmanager" has incompatible
type "Callable[[str | Path, str | Path, str, Any, str, str, str |
JClass, str, Any], AbstractContextManager[Any, bool | None]]";
expected "Callable[[str | Path, str | Path, str, Any, str, str, str
| JClass, str, Any], Iterator[Never]]" [arg-type]
Indeed, in Ghidra/Features/PyGhidra/src/main/py/src/pyghidra/core.py,
function open_program was declared to return a
ContextManager["FlatProgramAPI"]. While this function indeed returns
such a type, the implementation uses decorator @contextlib.contextmanager
which expects the wrapped function to return an generator (with yield).
Use Generator["FlatProgramAPI", None, None] to fix this.
While at it, fix other locations where the type annotation of the
function wrapped with contextmanager was incorrect.