Rappels du plug-in Sublime Text "Inline" dans le générateur

Aperçu

Le processus d'utilisation des différentes interfaces d'entrée de Sublime Text a tendance à être un enfer de rappel, alors je l'ai bien écrit en utilisant un générateur! C'est une histoire. J'utilise le plug-in que j'écris maintenant.

Contexte

L'API Sublime Text 3 peut faire ce que vous voulez la plupart du temps via un éditeur de texte. Il est possible d'accepter diverses entrées telles que l'affichage d'un panneau rapide avec des éléments de menu personnalisés et l'affichage d'un panneau de saisie de texte, mais comme vous pouvez le voir, celui de l'éditeur tout en acceptant les entrées de divers utilisateurs Les autres opérations ne sont pas bloquées, non? C'est, bien sûr, parce que les fonctions qui exploitent ces interfaces de réception d'entrée sont exécutées dans un nouveau thread, mais si vous voulez interférer avec le traitement du thread principal à partir de là, après l'entrée de la fonction de rappel que vous avez passée à l'avance. Appeler avec.

Par exemple, dans le cas de l'affichage du panneau rapide, cela ressemble à ce qui suit:

from sublime_plugin import WindowCommand


class ShowMenu(WindowCommand):

    def run(self):
        menu = ["A", "B", "C"]
        def cb(index):
            print(menu[index])
        self.window.show_quick_panel(menu, cb)

Peut afficher un menu contenant les éléments "A", "B", "C", et la fonction cb () reçoit l'index du menu sélectionné. Eh bien, ce n'est pas un gros problème car le contenu du callback est petit, mais si le nombre de lignes du callback et le nombre de variables à passer au callback augmentent, cela redeviendra gênant. Encore plus gênant est lorsque vous devez appeler des commandes qui attendent une entrée utilisateur successivement, et lorsque vous essayez d'appeler la prochaine commande d'attente d'entrée utilisateur avec un rappel approprié dans le rappel ... Par exemple:

class Auth(WindowCommand):

    def run(self):
        self.window.show_input_panel("username", "", cb1)
        #Lorsque le panneau de saisie s'affiche, la ligne de ce côté continue sans attendre l'entrée.
        #Traitement d'écriture en utilisant l'entrée dans le rappel


def _cb1(username):
    # show_input_panel()S'il est laissé tel quel, la chaîne de caractères d'entrée sera affichée et c'est vraiment ça, mais pour le moment je m'en fiche
    self.window.show_input_panel(
        "password", "", functools.partial(_cb2, username))


def _cb2(username, password):
    _auth(username, password)


def _auth(username, password):
    #Processus d'authentification à l'aide du nom d'utilisateur et du mot de passe

Eh bien, je peux l'écrire, mais ce n'est pas facile à lire. Il est fastidieux et peu intuitif de devoir se séparer en tant que fonction juste pour l'exécuter en tant que rappel, même si l'unité à découper en tant que fonction n'est pas appropriée. S'il y a beaucoup de paramètres à passer, cela sera gênant et la maintenabilité sera réduite.

Écrivons plus élégamment

Ne pouvez-vous pas écrire plus élégamment? ……Peut écrire. Avec Python.

En principe, ce serait bien si l'exécution de la fonction pouvait être suspendue lorsqu'il s'agissait d'attendre une entrée de l'utilisateur. ――Oui, c'est là que l'instruction yield entre en jeu. En appelant l'instruction yield, vous pouvez enregistrer l'état de fonctionnement jusqu'à ce que la fonction next () ou la fonctionsend ()soit appelée pour le générateur. S'il y a une entrée de l'utilisateur, elle peut être utilisée dans l'expression du générateur en utilisant la fonction send (). Sinon, appelez simplement next (). Alors, tout d'abord, je vais préparer un décorateur comme celui-ci.


def chain_callbacks(
    f: Callable[..., Generator[Callable[Callable[...]], Any, Any]
) -> Callable[..., None]:
    @wraps(f)
    def wrapper(*args, **kwargs):
        chain = f(*args, **kwargs)
        try:
            next_f = next(chain)
        except StopIteration:
            return

        def cb(*args, **kwargs):
            nonlocal next_f
            try:
                if len(args) + len(kwargs) != 0:
                    next_f = chain.send(*args, **kwargs)
                else:
                    next_f = next(chain)
                next_f(cb)
            except StopIteration:
                return
        next_f(cb)
    return wrapper

C'est un peu délicat, mais vous pouvez l'utiliser pour "intégrer" vos rappels. Dans l'instruction yield, passez" une fonction qui prend une fonction de rappel à laquelle vous voulez que l'instruction yield et les instructions suivantes soient exécutées lors de l'appel". Si une valeur est passée à cette fonction de rappel, elle peut être reçue comme valeur de retour de yield.

from functools import partial

class Auth(WindowCommand):

    @chain_callback
    def run(self):
        # `functools.partial()`Avec`on_done`Ne prenez qu'un seul argument
        #Sous la forme d'une fonction, le rendement
        username = yield partial(
            self.window.show_input_panel, "username", "",
            on_change=None, on_cancel=None)
        password = yield partial(
            self.window.show_input_panel, "password", "",
            on_change=None, on_cancel=None)
        #De là, le processus d'authentification à l'aide du nom d'utilisateur et du mot de passe

Ce côté est rafraîchissant!

Cette utilisation de générateurs est également utilisée dans les frameworks Web comme «Twisted» et «Tornado».

Recommended Posts

Rappels du plug-in Sublime Text "Inline" dans le générateur
Créer un environnement de construction python3 avec Sublime Text3
Pythonbrew avec Sublime Text
Configurer un environnement de développement Python avec Sublime Text 2
Créez un plug-in qui met toujours en évidence le texte arbitraire dans Sublime Text 2
GOTO en Python avec Sublime Text 3
Activer Python raw_input avec Sublime Text 3
Créer un gros fichier texte avec shellscript
J'ai fait un générateur Hanko avec GAN
Créer une matrice avec PythonGUI (zone de texte)
J'ai créé un package comme le dictionnaire pop-up Weblio anglais-japonais avec Sublime Text3
Créez un plugin qui vous permet de rechercher les onglets Sublime Text 3 en Python
Parler en japonais avec OpenJtalk (lire un fichier texte)
Parler en japonais avec gTTS (lire un fichier texte)
Créez un simple générateur d'images par points avec Flask
Plugin pour ajouter des symboles variables (Sublime Text) Description
J'ai essayé de l'activer / de le désactiver en définissant "Créer un plug-in qui met en évidence les espaces pleine largeur avec Sublime Text 2".
Comment créer un sous-menu avec le plug-in [Blender]
[Blender] Complétez l'API Python de Blender avec un éditeur de texte
Liens pour faire ce que vous voulez avec Sublime Text
Utilisez des programmes Python avec le plugin de sortie exec_filter de fluentd
Utilisez python installé par Pyenv avec Sublime REPL de Sublime Text 3
Utilisez des programmes Python avec le plugin de sortie exec de fluentd