-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Hello there 👋
First of all, I post here instead of on Gitter because I think my question is a bit long and it would pollute the thread while being hard to respond too. Also, Thanks for all the documentation in source code, it helps a lot already!
I'm trying to write a plugin for my paramclasses library and I would love some help! For a starting point, I'll focus on my protected
function/decorator, which essentially provides runtime equivalent for both typing.final
and typing.Final
during paramclasses' definition:
class Foo(ParamClass):
x = protected(0) # Unmodifiable in instances and subclasses
@protected
def bar(self) -> None:
"""Works on methods."""
@protected
@property
def baz(self) -> int:
"""Also works on properties."""
return 0
I started understanding a bit more how mypy plugins work, and I currently have the following:
METAPARAMCLASS_FULLNAME = "paramclasses.paramclasses._MetaParamClass" # Paramclasses have this as metaclass
class CustomPlugin(Plugin):
"""Help mypy undestand ``@protected`` and ``protected()``."""
def get_metaclass_hook(self, fullname) -> Callable[[ClassDefContext], None] | None:
"""Update paramclass definition."""
return paramclass_finder_hook
def get_base_class_hook(self, fullname) -> Callable[[ClassDefContext], None] | None:
"""Update paramclass definition."""
return paramclass_finder_hook
def paramclass_finder_hook(ctx: ClassDefContext):
"""Identify paramclasses and trigger class def modification."""
mcs = ctx.cls.info.metaclass_type
if mcs is not None and mcs.type.fullname == METAPARAMCLASS_FULLNAME:
modify_paramclass_def(ctx.cls)
def modify_paramclass_def(cls: ClassDef) -> None:
"""Handle ``@property`` and ``protected()``."""
replace_protected_decorator_with_final(cls)
replace_protected_assignment_with_Final(cls)
def replace_protected_decorator_with_final(cls: ClassDef) -> None:
"""Visit decorator nodes, move @protected, add to original_decorators, mark node as final."""
# Help pls!
As you can see, I think I'm pretty close to a first POC, I just need to know how to visit cls
in replace_protected_decorator_with_final
and modify targetted nodes. For that, I'll take inspiration from mypy.semanal.SemanticAnalyzer.visit_decorator() I think?
But I have so many questions:
- Is my overall approach sound/in the spirit of mypy plugins API?
- How can I trigger a visit? I thought about writing a custom semantic analyzer but I don't know how to "trigger" it?
- If I write one, should my
SemanticAnalyzer
inherit from(NodeVisitor[None], SemanticAnalyzerInterface, SemanticAnalyzerPluginInterface)
just like the one from mypy/semanal.py? I don't really understand what's useful and when. - Should I carry out both
replace_protected_decorator_with_final
andreplace_protected_assignment_with_Final
simultaneously with a unique semantic analyzer?
Thanks a lot in advance!!!
Élie