Sublime Text 3.1 Build 3170 ViewEventListener Super Class 方法
Sublime Text 3.1 Build 3170 ViewEventListener Super Class methods
我更新到 Sublime Text build 3170 today from the last stable build which I think was 3143. I had been using sublime_plugin.EventListener to do some tasks related to .scpt files, etc. To do that I had created a class with basic functionality for viewing binary or encoded files. To use that class I then would subclass it and subclass sublime_plugin.EventListener 这样 sublime 就会调用适当的方法(on_modified、on_load 等)。
但是,我希望在更新后更改为 sublime_plugin.ViewEventListener(扩展了 ViewEventListener 的 api),因为这将使我能够在我的插件中完成更多的事情。
问题是我的 super class 方法没有被 sublime 调用。下面是一些有望解释我的问题的代码。提前致谢。
#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
另一方面,这是行不通的。 "mod" 从不打印。我搜索了 google 并尝试调试。我可以确认正在创建一个 ViewEventListener 实例,并且如果 on_modified 在 test2 中,则打印 "mod"。
任何建议将不胜感激。再次感谢。
这是一个有趣的小问题,因为从表面上看,它应该可以正常工作。但是,如果您在 Sublime 插件系统的内部进行足够多的跟踪,其原因就会变得更加明显。
为了这个答案的目的,我使用了一个与你问题中的非工作示例相匹配的插件,我将其命名为 test_plugin.py
:
import sublime
import sublime_plugin
class test:
def on_modified(self):
print("mod")
class test2(test, sublime_plugin.ViewEventListener):
pass
首先,Sublime 使用 EventListener
和 ViewEventListener
的方式在插件方面有不同的内部机制。
EventListener
事件同样适用于任何地方的所有视图,因此当 Sublime 加载插件并找到 EventListener
class 时,它会立即创建它的实例然后检查查看实例支持的事件。相关代码在 reload_plugin()
方法中的 sublime_plugin.py
中第 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()
是否包含事件,如果是,则将其添加到支持该事件的 classes 列表中。
另一方面,ViewEventListener
仅适用于基于 is_applicable
和 applies_to_primary_view_only
class 方法的某些视图。这意味着它必须存储 class 而不是 实例 ,以便在创建新视图时它可以创建一个该视图特定的实例。
相关代码就在上面的下面,sublime_plugin.py
文件的第 156 行:
if issubclass(t, ViewEventListener):
view_event_listener_classes.append(t)
module_view_event_listener_classes.append(t)
现在让我们看看当需要引发事件时会发生什么。在我们的示例中,我们正在查看 on_modified
事件,它由 sublime_plugin.py
中的 on_modified
模块函数处理,位于第 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()
中,它在 sublime_plugin.py
中的第 480 行附近:
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))
如果您已经按照示例中的方式定义了 classes,那么这就是事情开始变得糟糕的部分,因为它专门询问 __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__
中,然后开始搜索层次结构,在 [=47= 中找到它] 一切都是肉汁。
在 EventListener
实例的情况下,会发生这种情况; dir()
函数知道其中一个 superclasses 包含 on_modified
和 returns 它,并且调用它的调用在链上进行,一切都按您预期的那样进行.
由于对 run_view_listener_callback
的调用不是直接尝试调用该方法,因此它不会在直接 __dict__
查找中找到它,因此什么也不做,因为它认为 class 不处理该事件,即使它处理了。
因此,所有这些文字墙的结果是,对于 ViewEventListener
,事件必须直接存在于 class 中,即子 classing ViewEventListener
否则它不起作用。
在您的示例中,一种方法是重构您的代码并直接在 test2
中而不是在 test
中定义这些方法。
另一种方法是 "proxy" 它们通过在您的 subclass 中定义方法来实现,但是让正文服从超级 class 版本。这将适当的方法放在 class 的 __dict__
中,但仍然让实现出现在另一个 class.
中
class test2(test, sublime_plugin.ViewEventListener):
def on_modified(self):
super().on_modified()
我不是 Python 大师,但这个大师对我来说似乎并不过分 Pythonic(至少对于列出的示例而言)并且可能还有其他方法可以实现同样的事情。
我更新到 Sublime Text build 3170 today from the last stable build which I think was 3143. I had been using sublime_plugin.EventListener to do some tasks related to .scpt files, etc. To do that I had created a class with basic functionality for viewing binary or encoded files. To use that class I then would subclass it and subclass sublime_plugin.EventListener 这样 sublime 就会调用适当的方法(on_modified、on_load 等)。
但是,我希望在更新后更改为 sublime_plugin.ViewEventListener(扩展了 ViewEventListener 的 api),因为这将使我能够在我的插件中完成更多的事情。
问题是我的 super class 方法没有被 sublime 调用。下面是一些有望解释我的问题的代码。提前致谢。
#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
另一方面,这是行不通的。 "mod" 从不打印。我搜索了 google 并尝试调试。我可以确认正在创建一个 ViewEventListener 实例,并且如果 on_modified 在 test2 中,则打印 "mod"。
任何建议将不胜感激。再次感谢。
这是一个有趣的小问题,因为从表面上看,它应该可以正常工作。但是,如果您在 Sublime 插件系统的内部进行足够多的跟踪,其原因就会变得更加明显。
为了这个答案的目的,我使用了一个与你问题中的非工作示例相匹配的插件,我将其命名为 test_plugin.py
:
import sublime
import sublime_plugin
class test:
def on_modified(self):
print("mod")
class test2(test, sublime_plugin.ViewEventListener):
pass
首先,Sublime 使用 EventListener
和 ViewEventListener
的方式在插件方面有不同的内部机制。
EventListener
事件同样适用于任何地方的所有视图,因此当 Sublime 加载插件并找到 EventListener
class 时,它会立即创建它的实例然后检查查看实例支持的事件。相关代码在 reload_plugin()
方法中的 sublime_plugin.py
中第 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()
是否包含事件,如果是,则将其添加到支持该事件的 classes 列表中。
另一方面,ViewEventListener
仅适用于基于 is_applicable
和 applies_to_primary_view_only
class 方法的某些视图。这意味着它必须存储 class 而不是 实例 ,以便在创建新视图时它可以创建一个该视图特定的实例。
相关代码就在上面的下面,sublime_plugin.py
文件的第 156 行:
if issubclass(t, ViewEventListener):
view_event_listener_classes.append(t)
module_view_event_listener_classes.append(t)
现在让我们看看当需要引发事件时会发生什么。在我们的示例中,我们正在查看 on_modified
事件,它由 sublime_plugin.py
中的 on_modified
模块函数处理,位于第 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()
中,它在 sublime_plugin.py
中的第 480 行附近:
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))
如果您已经按照示例中的方式定义了 classes,那么这就是事情开始变得糟糕的部分,因为它专门询问 __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__
中,然后开始搜索层次结构,在 [=47= 中找到它] 一切都是肉汁。
在 EventListener
实例的情况下,会发生这种情况; dir()
函数知道其中一个 superclasses 包含 on_modified
和 returns 它,并且调用它的调用在链上进行,一切都按您预期的那样进行.
由于对 run_view_listener_callback
的调用不是直接尝试调用该方法,因此它不会在直接 __dict__
查找中找到它,因此什么也不做,因为它认为 class 不处理该事件,即使它处理了。
因此,所有这些文字墙的结果是,对于 ViewEventListener
,事件必须直接存在于 class 中,即子 classing ViewEventListener
否则它不起作用。
在您的示例中,一种方法是重构您的代码并直接在 test2
中而不是在 test
中定义这些方法。
另一种方法是 "proxy" 它们通过在您的 subclass 中定义方法来实现,但是让正文服从超级 class 版本。这将适当的方法放在 class 的 __dict__
中,但仍然让实现出现在另一个 class.
class test2(test, sublime_plugin.ViewEventListener):
def on_modified(self):
super().on_modified()
我不是 Python 大师,但这个大师对我来说似乎并不过分 Pythonic(至少对于列出的示例而言)并且可能还有其他方法可以实现同样的事情。