-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Description
Describe the bug
Some valid scripts do not pass type checking due to problems with the generated mypy type stubs.
To Reproduce
Run the example scripts below through mypy with --strict
.
Expected behavior
I expected the example scripts to pass type checking.
Environment:
- OS: Ubuntu 22.04.5 LTS
- Java Version: openjdk 21.0.7 2025-04-15
- Ghidra Version: 11.4
- Ghidra Origin: GitHub Releases
- Python: 3.8.20
- mypy: 1.14.1
- ghidra-stubs: 11.4
- ghidra-stubs Origin: ghidra_11.4_PUBLIC/docs/ghidra_stubs/ghidra_stubs-11.4-py3-none-any.whl
Additional context
I did see the open issue #8018 but it did not seem related enough to warrant commenting this there.
Examples
1. No Typed Parameters and Missing None
Return Type
type checking this script
import ghidra.features.bsim.query.protocol as ghprotocol
insertreq = ghprotocol.InsertRequest()
results in
test.py:3: error: Call to untyped function "InsertRequest" in typed context [no-untyped-call]
even though a type stub for InsertRequest
exists
class InsertRequest(BSimQuery[ResponseInsert]):
def __init__(self):
...
this error is because the __init__
method has no typed parameters and no return type, so mypy counts it as untyped. Changing this to
class InsertRequest(BSimQuery[ResponseInsert]):
def __init__(self) -> None:
...
fixes the problem, as described in https://stackoverflow.com/a/62615728 and https://stackoverflow.com/a/46779290. It looks like generating return types for void
functions is skipped in the typestub code, which seems intentional so maybe this is due to a version difference with mypy.
2. Overloading Inherited Methods
If I'm understanding this mypy issue automatic inheritance of overloaded signatures is not supported and signatures from the superclass need to be repeated in the subclass alongside the added overload, and marked with @typing.overload
. The Ghidra code for generating the type stubs only includes the added overloads in classes and does not repeat the ones from the superclass, so mypy cannot see them which causes the issue. This script
import __main__
import ghidra.app.cmd.function as ghfunction
import ghidra.program.model.symbol as ghsymbol
import ghidra.program.model.address as ghaddress
cmd = ghfunction.ApplyFunctionDataTypesCmd(
list(), ghaddress.AddressSet(), ghsymbol.SourceType.USER_DEFINED, False, False
)
cmd.applyTo(__main__.currentProgram)
gives this error
test.py:9: error: Missing positional argument "monitor" in call to "applyTo" of "BackgroundCommand" [call-arg]
even though these type stubs exist
class ApplyFunctionDataTypesCmd(ghidra.framework.cmd.BackgroundCommand[ghidra.program.model.listing.Program]):
...
class BackgroundCommand(Command[T], typing.Generic[T]):
def applyTo(self, obj: T, monitor: ghidra.util.task.TaskMonitor) -> bool:
...
class Command(java.lang.Object, typing.Generic[T]):
def applyTo(self, obj: T) -> bool:
...
mypy can't see the applyTo
from Command
that only takes one argument on the ApplyFunctionDataTypesCmd
class because BackgroundCommand
defining a new applyTo
replaces the original one. But changing the definition of BackgroundCommand
to
class BackgroundCommand(Command[T], typing.Generic[T]):
@typing.overload
def applyTo(self, obj: T) -> bool:
...
@typing.overload
def applyTo(self, obj: T, monitor: ghidra.util.task.TaskMonitor) -> bool:
...
makes mypy happy by repeating the signature of applyTo
inherited from Command
.
3. Special Cases
Finally, there are a few functions that are special cases and not 100% in line with what is accepted at runtime or don't play nice with Jython conversions for one reason or another.
The default <E extends Exception> void withTransaction(String description, ExceptionalCallback<E> callback) throws E
method on ghidra.framework.model.DomainObject
gives a mypy error when passing a Python function to the callback parameter, but doing that works at runtime. I'm not sure that mypy has a way of specifying that one type coerces to another, so maybe the way to fix this is by generating more overloads or using unions like you do for some other types of parameters. Or, maybe adding the Java type stubs would fix this case.
The defaultValue
parameter of the public AddressValue defineAddress(String name, Address defaultValue, Program program)
method on ghidra.features.base.values.GhidraValuesMap
says "defaultValue - an option default value" in the docs but it is not marked optional in the type stubs. There are too many functions like this to list here, but I do pass None
to quite a few of these non-optional optional parameters without issue at runtime. I think this one is going to be hard to fix automatically with the stub generation code since the optioanl-ness of parameters isn't encoded in the Java types, since any class type parameter could hypothetically be passed null
. And I supposed the same thing could be said about the return types of functions that may return null.