python / python/mypy

[plugins/help] Triggering node visits?

Ouverte
#19,481 7 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

question
Langage dominant
Python
Étoiles
20.6k
Forks
3.3k
Métriques de merge des PR
Métriques de PR en attente

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.finaland 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:

  1. Is my overall approach sound/in the spirit of mypy plugins API?
  2. How can I trigger a visit? I thought about writing a custom semantic analyzer but I don't know how to "trigger" it?
  3. 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.
  4. Should I carry out both replace_protected_decorator_with_final and replace_protected_assignment_with_Final simultaneously with a unique semantic analyzer?

Thanks a lot in advance!!!
Élie

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par les implémentations des hooks du plugin dans l’issue et lisez mypy/semanal.py, en particulier SemanticAnalyzer.visit_decorator(). Déterminez si l’API actuelle du plugin prend en charge la visite et la modification des nœuds de classe ciblés, et si un analyseur sémantique personnalisé est approprié ; le travail est terminé lorsqu’une approche prise en charge a été établie ou qu’il a été documenté que le parcours demandé n’est pas disponible.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
tooling
Type d'issue
Fonctionnalité
Difficulté
5/5
Temps estimé
Plus d'une semaine
Activité
À l'abandon
Clarté
À clarifier
Accessibilité débutants
25/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.