diff options
author | A_D <A_D@snoonet.org> | 2018-12-26 20:46:31 +0200 |
---|---|---|
committer | TingPing <tingping@tingping.se> | 2019-01-02 18:50:10 -0500 |
commit | 7abeb10cf1f82fbad4d167f9e6f6918e1f47650b (patch) | |
tree | fc2a7469951acbd89dbb9e32fb51aeb78e9e0c5e /plugins/python/python.py | |
parent | a5a727122b66c9003b44fcdc199ad56dbe15a131 (diff) |
python: plugin cleanup and refactor
Diffstat (limited to 'plugins/python/python.py')
-rw-r--r-- | plugins/python/python.py | 99 |
1 files changed, 65 insertions, 34 deletions
diff --git a/plugins/python/python.py b/plugins/python/python.py index 1afb36c4..30694802 100644 --- a/plugins/python/python.py +++ b/plugins/python/python.py @@ -1,30 +1,30 @@ from __future__ import print_function +import importlib import os import pydoc -import sys -from contextlib import contextmanager -import importlib import signal -import site +import sys import traceback import weakref +from contextlib import contextmanager + +from _hexchat_embedded import ffi, lib if sys.version_info < (3, 0): from io import BytesIO as HelpEater else: from io import StringIO as HelpEater -from _hexchat_embedded import ffi, lib - if not hasattr(sys, 'argv'): sys.argv = ['<hexchat>'] VERSION = b'2.0' # Sync with hexchat.__version__ PLUGIN_NAME = ffi.new('char[]', b'Python') -PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' - % (sys.version_info[0], sys.version_info[1])) +PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' % (sys.version_info[0], sys.version_info[1])) PLUGIN_VERSION = ffi.new('char[]', VERSION) + +# TODO: Constants should be screaming snake case hexchat = None local_interp = None hexchat_stdout = None @@ -40,10 +40,11 @@ def redirected_stdout(): sys.stderr = hexchat_stdout -if os.environ.get('HEXCHAT_LOG_PYTHON'): +if os.getenv('HEXCHAT_LOG_PYTHON'): def log(*args): with redirected_stdout(): print(*args) + else: def log(*args): pass @@ -56,7 +57,7 @@ class Stdout: def write(self, string): string = string.encode() idx = string.rfind(b'\n') - if idx is not -1: + if idx != -1: self.buffer += string[:idx] lib.hexchat_print(lib.ph, bytes(self.buffer)) self.buffer = bytearray(string[idx + 1:]) @@ -91,13 +92,15 @@ class Hook: lib.hexchat_unhook(lib.ph, self.hexchat_hook) -if sys.version_info[0] is 2: +if sys.version_info[0] == 2: def compile_file(data, filename): return compile(data, filename, 'exec', dont_inherit=True) + def compile_line(string): try: return compile(string, '<string>', 'eval', dont_inherit=True) + except SyntaxError: # For some reason `print` is invalid for eval # This will hide any return value though @@ -106,6 +109,7 @@ else: def compile_file(data, filename): return compile(data, filename, 'exec', optimize=2, dont_inherit=True) + def compile_line(string): # newline appended to solve unexpected EOF issues return compile(string + '\n', '<string>', 'single', optimize=2, dont_inherit=True) @@ -135,8 +139,9 @@ class Plugin: ud = h.userdata self.hooks.remove(h) return ud - else: - log('Hook not found') + + log('Hook not found') + return None def loadfile(self, filename): try: @@ -148,21 +153,22 @@ class Plugin: try: self.name = self.globals['__module_name__'] + except KeyError: lib.hexchat_print(lib.ph, b'Failed to load module: __module_name__ must be set') + return False self.version = self.globals.get('__module_version__', '') self.description = self.globals.get('__module_description__', '') - self.ph = lib.hexchat_plugingui_add(lib.ph, filename.encode(), - self.name.encode(), - self.description.encode(), - self.version.encode(), - ffi.NULL) + self.ph = lib.hexchat_plugingui_add(lib.ph, filename.encode(), self.name.encode(), + self.description.encode(), self.version.encode(), ffi.NULL) + except Exception as e: lib.hexchat_print(lib.ph, 'Failed to load module: {}'.format(e).encode()) traceback.print_exc() return False + return True def __del__(self): @@ -171,17 +177,20 @@ class Plugin: if hook.is_unload is True: try: hook.callback(hook.userdata) + except Exception as e: log('Failed to run hook:', e) traceback.print_exc() + del self.hooks if self.ph is not None: lib.hexchat_plugingui_remove(lib.ph, self.ph) -if sys.version_info[0] is 2: +if sys.version_info[0] == 2: def __decode(string): return string + else: def __decode(string): return string.decode() @@ -192,6 +201,7 @@ def wordlist_len(words): for i in range(31, 1, -1): if ffi.string(words[i]): return i + return 0 @@ -205,24 +215,26 @@ def create_wordlist(words): # This makes no sense to do... def create_wordeollist(words): words = reversed(words) - last = None accum = None ret = [] for word in words: if accum is None: accum = word + elif word: last = accum accum = ' '.join((word, last)) + ret.insert(0, accum) + return ret def to_cb_ret(value): if value is None: return 0 - else: - return int(value) + + return int(value) @ffi.def_extern() @@ -274,13 +286,14 @@ def _on_timer_hook(userdata): hook = ffi.from_handle(userdata) if hook.callback(hook.userdata) is True: return 1 - else: - hook.is_unload = True # Don't unhook - for h in hook.plugin.hooks: - if h == hook: - hook.plugin.hooks.remove(h) - break - return 0 + + hook.is_unload = True # Don't unhook + for h in hook.plugin.hooks: + if h == hook: + hook.plugin.hooks.remove(h) + break + + return 0 @ffi.def_extern(error=3) @@ -291,6 +304,7 @@ def _on_say_command(word, word_eol, userdata): lib.hexchat_print(lib.ph, b'>>> ' + python) exec_in_interp(__decode(python)) return 1 + return 0 @@ -298,33 +312,36 @@ def load_filename(filename): filename = os.path.expanduser(filename) if not os.path.isabs(filename): configdir = __decode(ffi.string(lib.hexchat_get_info(lib.ph, b'configdir'))) + filename = os.path.join(configdir, 'addons', filename) + if filename and not any(plugin.filename == filename for plugin in plugins): plugin = Plugin() if plugin.loadfile(filename): plugins.add(plugin) return True + return False def unload_name(name): if name: for plugin in plugins: - if name in (plugin.name, plugin.filename, - os.path.basename(plugin.filename)): + if name in (plugin.name, plugin.filename, os.path.basename(plugin.filename)): plugins.remove(plugin) return True + return False def reload_name(name): if name: for plugin in plugins: - if name in (plugin.name, plugin.filename, - os.path.basename(plugin.filename)): + if name in (plugin.name, plugin.filename, os.path.basename(plugin.filename)): filename = plugin.filename plugins.remove(plugin) return load_filename(filename) + return False @@ -346,6 +363,7 @@ def autoload(): log('Autoloading', f) # TODO: Set cwd load_filename(os.path.join(addondir, f)) + except FileNotFoundError as e: log('Autoload failed', e) @@ -376,7 +394,6 @@ def list_plugins(): for row in tbl: lib.hexchat_print(lib.ph, b' '.join(item.ljust(column_sizes[i]) for i, item in enumerate(row))) - lib.hexchat_print(lib.ph, b'') @@ -396,6 +413,7 @@ def exec_in_interp(python): ret = eval(code, local_interp.globals, local_interp.locals) if ret is not None: lib.hexchat_print(lib.ph, '{}'.format(ret).encode()) + except Exception as e: traceback.print_exc(file=hexchat_stdout) @@ -406,6 +424,7 @@ def _on_load_command(word, word_eol, userdata): if filename.endswith(b'.py'): load_filename(__decode(filename)) return 3 + return 0 @@ -415,6 +434,7 @@ def _on_unload_command(word, word_eol, userdata): if filename.endswith(b'.py'): unload_name(__decode(filename)) return 3 + return 0 @@ -424,6 +444,7 @@ def _on_reload_command(word, word_eol, userdata): if filename.endswith(b'.py'): reload_name(__decode(filename)) return 3 + return 0 @@ -434,23 +455,30 @@ def _on_py_command(word, word_eol, userdata): if subcmd == 'exec': python = __decode(ffi.string(word_eol[3])) exec_in_interp(python) + elif subcmd == 'load': filename = __decode(ffi.string(word[3])) load_filename(filename) + elif subcmd == 'unload': name = __decode(ffi.string(word[3])) if not unload_name(name): lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name') + elif subcmd == 'reload': name = __decode(ffi.string(word[3])) if not reload_name(name): lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name') + elif subcmd == 'console': lib.hexchat_command(lib.ph, b'QUERY >>python<<') + elif subcmd == 'list': list_plugins() + elif subcmd == 'about': lib.hexchat_print(lib.ph, b'HexChat Python interface version ' + VERSION) + else: lib.hexchat_command(lib.ph, b'HELP PY') @@ -473,8 +501,10 @@ def _on_plugin_init(plugin_name, plugin_desc, plugin_version, arg, libdir): modpath = os.path.join(libdir, '..', 'python') sys.path.append(os.path.abspath(modpath)) hexchat = importlib.import_module('hexchat') + except (UnicodeDecodeError, ImportError) as e: lib.hexchat_print(lib.ph, b'Failed to import module: ' + repr(e).encode()) + return 0 hexchat_stdout = Stdout() @@ -517,6 +547,7 @@ def _on_plugin_deinit(): for mod in ('_hexchat', 'hexchat', 'xchat', '_hexchat_embedded'): try: del sys.modules[mod] + except KeyError: pass |