Sublime Text 3.1 Build 3170 ViewEventListener Методы суперкласса - PullRequest
0 голосов
/ 08 мая 2018

Я обновил до Sublime Text build 3170 сегодня из последней стабильной сборки, которая, я думаю, была 3143. Я использовал sublime_plugin.EventListener , чтобы сделать некоторые задачи, связанные с файлами .scpt и т. д. Для этого я создал класс с основными функциями для просмотра двоичных или закодированных файлов. Чтобы использовать этот класс, я бы затем создал его подкласс и подкласс sublime_plugin.EventListener , чтобы sublime вызывал соответствующие методы (on_modified, on_load и т. Д.).
Тем не менее, я надеялся изменить значение на sublime_plugin.ViewEventListener после обновления (которое расширило API для ViewEventListener), потому что это позволило бы мне выполнить больше задач в моем плагине.
Проблема в том, что мои методы суперкласса не вызывались возвышенными. Ниже приведен код, который, надеюсь, объяснит мою проблему. Заранее спасибо.

#this does work
class test:
  def on_modified(self, view):
    print("mod")

class test2(test, sublime_plugin.EventListener):
  pass

Он будет вызывать on_modified при внесении изменений в представление и каждый раз печатать "mod".

#this does not work
class test:
  def on_modified(self):
    print("mod")

class test2(test, sublime_plugin.ViewEventListener):
  pass

С другой стороны, это не сработает. «мод» никогда не печатается. Я искал Google и попытался отладки. Я могу подтвердить, что создается экземпляр ViewEventListener и что если on_modified находится в test2, то выводится «mod».
Любые предложения будут ценны. Еще раз спасибо.

1 Ответ

0 голосов
/ 08 мая 2018

Это небольшая интересная проблема, потому что на первый взгляд кажется, что это должно работать просто отлично. Однако, если вы проследите достаточно вокруг внутренних компонентов системы плагинов Sublime, причина этого станет более очевидной.

Для целей этого ответа я использую плагин, который соответствует нерабочему примеру в вашем вопросе, который я назвал test_plugin.py:

import sublime
import sublime_plugin

class test:
    def on_modified(self):
        print("mod")

class test2(test, sublime_plugin.ViewEventListener):
    pass

Для начала, способ, которым EventListener и ViewEventListener используются Sublime, имеет различные механизмы внутри, когда дело касается плагинов.

События

EventListener в равной степени применяются ко всем представлениям повсюду, поэтому, когда Sublime загружает плагин и находит класс EventListener, он сразу создает его экземпляр и затем проверяет, какие события поддерживает этот экземпляр. Соответствующий код для этого находится в sublime_plugin.py в методе reload_plugin() вокруг строки 142:

if issubclass(t, EventListener):
    obj = t()
    for p in all_callbacks.items():
        if p[0] in dir(obj):
            p[1].append(obj)

all_callbacks - это словарь, в котором ключами являются имена событий, а значениями являются массивы; таким образом, проверяется, содержит ли dir() экземпляр слушателя события событие, и если да, то оно добавляется в список классов, поддерживающих это событие.

С другой стороны, ViewEventListener применяется только к определенным представлениям, основанным на методах класса is_applicable и applies_to_primary_view_only. Это означает, что вместо этого нужно class вместо instance , чтобы при создании новых представлений он мог создавать экземпляр, специфичный для этого представления.

Соответствующий код чуть ниже приведенного выше, в строке 156 файла sublime_plugin.py:

if issubclass(t, ViewEventListener):
    view_event_listener_classes.append(t)
    module_view_event_listener_classes.append(t)

Теперь давайте посмотрим, что происходит, когда нужно вызвать событие. В нашем примере мы смотрим на событие on_modified, которое обрабатывается модульной функцией on_modified в sublime_plugin.py в строке 566 (но все события работают аналогично):

def on_modified(view_id):
    v = sublime.View(view_id)
    for callback in all_callbacks['on_modified']:
        run_callback('on_modified', callback, lambda: callback.on_modified(v))
    run_view_listener_callback(v, 'on_modified')

Первые части для обычного EventListener; он находит все найденные ранее экземпляры объектов, которые имеют обработчик on_modified, а затем непосредственно вызывает on_modified() к ним (оболочка run_callback() отвечает за синхронизацию выполнения профилирования, которое вы можете увидеть в Tools > Developer > Profile Plugins ).

Обработка для ViewEventListener происходит в run_view_listener_callback(), что составляет около строки 480 в sublime_plugin.py:

def run_view_listener_callback(view, name):
    for vel in event_listeners_for_view(view):
        if name in vel.__class__.__dict__:
            run_callback(name, vel, lambda: vel.__class__.__dict__[name](vel))

Это та часть, где все начинает складываться для вас, если вы определили классы, как в своем примере, потому что он специально запрашивает атрибут __dict__, содержит ли оно событие для вызова, и вызывает его только в том случае, если он делает.

Обратите внимание на следующее из консоли Sublime, основанной на примере плагина выше:

>>> from User.test_plugin import test, test2
>>> "on_modified" in test.__dict__
True
>>> "on_modified" in test2.__dict__
False

В частности, test2 напрямую не содержит метод on_modified, поэтому его нет в __dict__. В обычной программе на Python, если вы попытаетесь вызвать on_modified, он заметит, что он не находится в __dict__ объекта, а затем начнет поиск в иерархии, найдя его в test, и все будет просто.

В случае EventListener это то, что происходит; функция dir() знает, что один из суперклассов содержит on_modified и возвращает его, и вызов для его вызова идет вверх по цепочке, и все работает так, как вы ожидаете.

Поскольку вызов run_view_listener_callback не пытается напрямую вызвать метод, он не находит его в прямом поиске __dict__ и поэтому ничего не делает, потому что считает, что класс не обрабатывает это событие даже хотя это так.

Таким образом, результатом всей этой стены текста является то, что для ViewEventListener события должны существовать непосредственно в классе, который является подклассом ViewEventListener, или это не работает.

В вашем примере, один из способов сделать это - реструктурировать код и определить эти методы непосредственно внутри test2 вместо test.

Другим способом было бы «прокси» их, определив метод в вашем подклассе, но вместо этого применив тело к версии суперкласса. Это помещает соответствующий метод в __dict__ класса, но все же позволяет реализации появляться в другом классе.

class test2(test, sublime_plugin.ViewEventListener):
    def on_modified(self):
        super().on_modified()

Я не гуру Python, но этот не кажется мне чрезмерно Pythonic (по крайней мере, для приведенного примера), и, вероятно, есть другие способы достичь того же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...