Это небольшая интересная проблема, потому что на первый взгляд кажется, что это должно работать просто отлично. Однако, если вы проследите достаточно вокруг внутренних компонентов системы плагинов 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 (по крайней мере, для приведенного примера), и, вероятно, есть другие способы достичь того же.