Reputation: 640
I am writing a fuzzer for a strongly-typed assembly-like language and I am relying on the Python typing system to parametrize the typing of the underlying generated language. I then inspect the Python types at runtime in order to dynamically generate the correct parametrization of the target fuzzed language.
I have a case where I parametrize a Generic[T, Optional[S]]
and as the Optional
hints, the S will sometimes be None
. Let's take the following minimal example of class structure:
class Signed:
...
class Unsigned:
...
class OpTypeInt:
...
class OpTypeFloat:
...
class LogicalOperator(Generic[T, S]):
...
class BinaryLogicalOperatorFuzzMixin:
...
class BinaryLogicalOperator(LogicalOperator[T, Optional[S]]):
...
# "less than" OpCode parametrized for floats
class OpFLessThan(
BinaryLogicalOperatorFuzzMixin, BinaryLogicalOperator[OpTypeFloat, None]
):
...
# "less than" OpCode parametrized for unsigned integers
class OpULessThan(
BinaryLogicalOperatorFuzzMixin, BinaryLogicalOperator[OpTypeInt, Unsigned]
):
...
# "less than" OpCode parametrized for signed integers
class OpSLessThan(
BinaryLogicalOperatorFuzzMixin, BinaryLogicalOperator[OpTypeInt, Signed]
):
...
Now during the runtime of the program, I will inspect these types to dictate the behaviour of the fuzzer. The inspection code looks like this:
base_type, type_constraint = get_args(OpSLessThan.__orig_bases__[1])
# OpSLessThan is a BinaryLogicalOperator[OpTypeInt, Signed]
print(base_type, type_constraint)
# <class '__main__.OpTypeInt'> <class '__main__.Signed'>
print(
isinstance(type_constraint, Signed),
type(type_constraint) == type(Signed),
type(type_constraint) == Signed,
type_constraint == type(Signed),
)
# False True False False
base_type, type_constraint = get_args(OpILessThan.__orig_bases__[1])
# OpILessThan is a BinaryLogicalOperator[OpTypeInt, None]
print(base_type, type_constraint)
# <class '__main__.OpTypeInt'> <class 'NoneType'>
print(
isinstance(type_constraint, NoneType),
type(type_constraint) == type(None),
type(type_constraint) == None,
type_constraint == type(None),
type_constraint is None,
)
# False False False True False huh?
Why is type(type_constraint) == type(Signed)
a valid way of checking that the class has Signed
as a base but isn't a valid way of checking for None
? Why does None
require to check for type_constraint == type(None)
?
Upvotes: 0
Views: 111
Reputation: 3390
Use issubclass
:
base_type, type_constraint = get_args(OpFLessThan.__orig_bases__[1])
# OpILessThan is a BinaryLogicalOperator[OpTypeInt, None]
print(base_type, type_constraint)
# <class '__main__.OpTypeInt'> <class 'NoneType'>
print(issubclass(type_constraint, NoneType))
# True
type(a_class) == type(Signed)
is not a valid way of checking that the class has Signed
as a base.
It evaluates to True
because type(<a class>)
is type
, hence type == type
produces True
.
>>> type(Signed)
<class 'type'>
This holds for every class in Python
, including the type
class itself:
>>> type(type)
<class 'type'>
type(None) != type(a_class)
because None
is an object, not a class, hence type(None) == NoneType
.
Upvotes: 1