Communicating to static analysis tools that a type is dynamically generated and informing them of how

Hello,
I’m writing a package to generate classes via a mixin/transformer model, (to simplify it, think Pydantic but geared more toward behavior rather than data storage), and I’m running up against the limitations of static tooling and my own incompetence.

I want a reliable way to essentially tell SA “this class has a method named foo with such and such signature, now” without manually outlining a protocol or abstract base class, as that would serve as a stumbling block in terms of efficiency.

I found, for lack of a better term, a godforsaken hack that works at runtime by generating actual type classes from parameters, but it’s kind of pointless without integration into static analysis tools.

import types
from collections.abc import Callable
from typing import Any, Self


class FakeType(type):
	_instancecheck: Callable[[Self, Any], bool]


	def __instancecheck__(cls, instance: Any) -> bool:
		if cls._instancecheck is None:
			raise TypeError(f"{cls.__name__}.__instancecheck__ is None")
		return cls._instancecheck(cls, instance)


class HasAttributeMeta(type):

	def __getitem__[T](cls, item: tuple[str, type[T]]):
		name, type_ = item
		type_name = f"HasAttribute_{name}"
		def exec_body(ns: dict[str, Any]) -> None:
			ns["_instancecheck"] = cls.has_attribute_inst_check(name, type_)
		new_type = types.new_class(
			type_name,
			(),
			{"metaclass": FakeType},
			exec_body=exec_body
		)
		return new_type

	@classmethod
	def has_attribute_inst_check(cls, name: str, type_: type) -> Callable[[Self, Any], bool]:
		def _instancecheck(_cls: Self, instance: Any) -> bool:
			if not hasattr(instance, name):
				return False
			return isinstance(getattr(instance, name), type_)
		_instancecheck.__name__ = "__instancecheck__"
		return _instancecheck
	
	
class HasAttribute(metaclass=HasAttributeMeta):
	...

I am fully aware that what I’m doing is potentially suboptimal, but I would really like to know how I can make this work, or if I’m missing something.

This is not currently possible; your best bet is to generate .pyi files in a pre-runtime buildstep.

Or write a plugin for those type checkers that support that.

1 Like

Perhaps typing.dataclass_transform might help: Dataclasses — typing documentation

1 Like

I was afraid of this, I’ve had to use that before in order to accept strings or integers as ‘type arguments’ without having warnings.