[arch-commits] Commit in hexchat/trunk (0001-python-backport.patch PKGBUILD)

Christian Hesse eworm at archlinux.org
Sun Dec 22 19:10:52 UTC 2019


    Date: Sunday, December 22, 2019 @ 19:10:51
  Author: eworm
Revision: 538888

upgpkg: hexchat 2.14.3-1

new upstream release

Modified:
  hexchat/trunk/PKGBUILD
Deleted:
  hexchat/trunk/0001-python-backport.patch

----------------------------+
 0001-python-backport.patch | 4995 -------------------------------------------
 PKGBUILD                   |    7 
 2 files changed, 2 insertions(+), 5000 deletions(-)

Deleted: 0001-python-backport.patch
===================================================================
--- 0001-python-backport.patch	2019-12-22 19:07:25 UTC (rev 538887)
+++ 0001-python-backport.patch	2019-12-22 19:10:51 UTC (rev 538888)
@@ -1,4995 +0,0 @@
-From 706f9bca82d463f6f1bd17d5dc609807e4a1e8a9 Mon Sep 17 00:00:00 2001
-From: Patrick Griffis <tingping at tingping.se>
-Date: Sat, 2 Sep 2017 17:52:25 -0400
-Subject: python: Rewrite with CFFI
----
- .travis.yml                            |    2 +-
- meson.build                            |    4 +
- plugins/python/_hexchat.py             |  364 +++
- plugins/python/generate_plugin.py      |   89 +
- plugins/python/hexchat.py              |    1 +
- plugins/python/meson.build             |   14 +-
- plugins/python/python.c                | 2834 ------------------------
- plugins/python/python.def              |    1 -
- plugins/python/python.py               |  497 +++++
- plugins/python/python2.vcxproj         |    8 +-
- plugins/python/python3.vcxproj         |   15 +-
- plugins/python/python3.vcxproj.filters |   19 +-
- plugins/python/xchat.py                |    1 +
- src/common/meson.build                 |    4 -
- win32/copy/copy.vcxproj                |    5 +
- win32/installer/hexchat.iss.tt         |    9 +-
- 16 files changed, 1017 insertions(+), 2850 deletions(-)
- create mode 100644 plugins/python/_hexchat.py
- create mode 100755 plugins/python/generate_plugin.py
- create mode 100644 plugins/python/hexchat.py
- delete mode 100644 plugins/python/python.c
- create mode 100644 plugins/python/python.py
- create mode 100644 plugins/python/xchat.py
-
-diff --git a/.travis.yml b/.travis.yml
-index df0c7e1f..9e226f0c 100644
---- a/.travis.yml
-+++ b/.travis.yml
-@@ -2,7 +2,7 @@ sudo: required
- services: docker
- before_install:
-     - docker pull ubuntu:16.04
--    - docker run --privileged --cidfile=/tmp/cid ubuntu:16.04 /bin/sh -c 'apt-get update && apt-get install -y meson/xenial-backports libcanberra-dev libdbus-glib-1-dev libglib2.0-dev libgtk2.0-dev libluajit-5.1-dev libnotify-dev libpci-dev libperl-dev libproxy-dev libssl-dev python3-dev mono-devel desktop-file-utils'
-+    - docker run --privileged --cidfile=/tmp/cid ubuntu:16.04 /bin/sh -c 'apt-get update && apt-get install -y meson/xenial-backports libcanberra-dev libdbus-glib-1-dev libglib2.0-dev libgtk2.0-dev libluajit-5.1-dev libnotify-dev libpci-dev libperl-dev libproxy-dev libssl-dev python3-dev python3-cffi mono-devel desktop-file-utils'
-     - docker commit `cat /tmp/cid` hexchat/ubuntu-ci
-     - rm -f /tmp/cid
- install:
-diff --git a/meson.build b/meson.build
-index 9d2ae05b..17e73795 100644
---- a/meson.build
-+++ b/meson.build
-@@ -49,6 +49,10 @@ config_h.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_34')
- config_h.set('HAVE_MEMRCHR', cc.has_function('memrchr'))
- config_h.set('HAVE_STRINGS_H', cc.has_header('strings.h'))
- 
-+config_h.set_quoted('HEXCHATLIBDIR',
-+  join_paths(get_option('prefix'), get_option('libdir'), 'hexchat/plugins')
-+)
-+
- if libssl_dep.found()
-   config_h.set('HAVE_X509_GET_SIGNATURE_NID',
-     cc.has_function('X509_get_signature_nid', dependencies: libssl_dep)
-diff --git a/plugins/python/_hexchat.py b/plugins/python/_hexchat.py
-new file mode 100644
-index 00000000..52b3ec14
---- /dev/null
-+++ b/plugins/python/_hexchat.py
-@@ -0,0 +1,364 @@
-+from contextlib import contextmanager
-+import inspect
-+import sys
-+from _hexchat_embedded import ffi, lib
-+
-+__all__ = [
-+    'EAT_ALL', 'EAT_HEXCHAT', 'EAT_NONE', 'EAT_PLUGIN', 'EAT_XCHAT',
-+    'PRI_HIGH', 'PRI_HIGHEST', 'PRI_LOW', 'PRI_LOWEST', 'PRI_NORM',
-+    '__doc__', '__version__', 'command', 'del_pluginpref', 'emit_print',
-+    'find_context', 'get_context', 'get_info',
-+    'get_list', 'get_lists', 'get_pluginpref', 'get_prefs', 'hook_command',
-+    'hook_print', 'hook_print_attrs', 'hook_server', 'hook_server_attrs',
-+    'hook_timer', 'hook_unload', 'list_pluginpref', 'nickcmp', 'prnt',
-+    'set_pluginpref', 'strip', 'unhook',
-+]
-+
-+__doc__ = 'HexChat Scripting Interface'
-+__version__ = (2, 0)
-+__license__ = 'GPL-2.0+'
-+
-+EAT_NONE = 0
-+EAT_HEXCHAT = 1
-+EAT_XCHAT = EAT_HEXCHAT
-+EAT_PLUGIN = 2
-+EAT_ALL = EAT_HEXCHAT | EAT_PLUGIN
-+
-+PRI_LOWEST = -128
-+PRI_LOW = -64
-+PRI_NORM = 0
-+PRI_HIGH = 64
-+PRI_HIGHEST = 127
-+
-+
-+# We need each module to be able to reference their parent plugin
-+# which is a bit tricky since they all share the exact same module.
-+# Simply navigating up to what module called it seems to actually
-+# be a fairly reliable and simple method of doing so if ugly.
-+def __get_current_plugin():
-+    frame = inspect.stack()[1][0]
-+    while '__plugin' not in frame.f_globals:
-+        frame = frame.f_back
-+        assert frame is not None
-+    return frame.f_globals['__plugin']
-+
-+
-+# Keeping API compat
-+if sys.version_info[0] is 2:
-+    def __decode(string):
-+        return string
-+else:
-+    def __decode(string):
-+        return string.decode()
-+
-+
-+# ------------ API ------------
-+def prnt(string):
-+    lib.hexchat_print(lib.ph, string.encode())
-+
-+
-+def emit_print(event_name, *args, **kwargs):
-+    time = kwargs.pop('time', 0)  # For py2 compat
-+    cargs = []
-+    for i in range(4):
-+        arg = args[i].encode() if len(args) > i else b''
-+        cstring = ffi.new('char[]', arg)
-+        cargs.append(cstring)
-+    if time is 0:
-+        return lib.hexchat_emit_print(lib.ph, event_name.encode(), *cargs)
-+    else:
-+        attrs = lib.hexchat_event_attrs_create(lib.ph)
-+        attrs.server_time_utc = time
-+        ret = lib.hexchat_emit_print(lib.ph, attrs, event_name.encode(), *cargs)
-+        lib.hexchat_event_attrs_free(lib.ph, attrs)
-+        return ret
-+
-+
-+def command(command):
-+    lib.hexchat_command(lib.ph, command.encode())
-+
-+
-+def nickcmp(string1, string2):
-+    return lib.hexchat_nickcmp(lib.ph, string1.encode(), string2.encode())
-+
-+
-+def strip(text, length=-1, flags=3):
-+    stripped = lib.hexchat_strip(lib.ph, text.encode(), length, flags)
-+    ret = __decode(ffi.string(stripped))
-+    lib.hexchat_free(lib.ph, stripped)
-+    return ret
-+
-+
-+def get_info(name):
-+    ret = lib.hexchat_get_info(lib.ph, name.encode())
-+    if ret == ffi.NULL:
-+        return None
-+    if name in ('gtkwin_ptr', 'win_ptr'):
-+        # Surely there is a less dumb way?
-+        ptr = repr(ret).rsplit(' ', 1)[1][:-1]
-+        return ptr
-+    return __decode(ffi.string(ret))
-+
-+
-+def get_prefs(name):
-+    string_out = ffi.new('char**')
-+    int_out = ffi.new('int*')
-+    type = lib.hexchat_get_prefs(lib.ph, name.encode(), string_out, int_out)
-+    if type is 0:
-+        return None
-+    elif type is 1:
-+        return __decode(ffi.string(string_out[0]))
-+    elif type is 2 or type is 3:  # XXX: 3 should be a bool, but keeps API
-+        return int_out[0]
-+    else:
-+        assert False
-+
-+
-+def __cstrarray_to_list(arr):
-+    i = 0
-+    ret = []
-+    while arr[i] != ffi.NULL:
-+        ret.append(ffi.string(arr[i]))
-+        i += 1
-+    return ret
-+
-+
-+__FIELD_CACHE = {}
-+
-+
-+def __get_fields(name):
-+    return __FIELD_CACHE.setdefault(name,
-+                                    __cstrarray_to_list(lib.hexchat_list_fields(lib.ph, name)))
-+
-+
-+__FIELD_PROPERTY_CACHE = {}
-+
-+
-+def __cached_decoded_str(string):
-+    return __FIELD_PROPERTY_CACHE.setdefault(string, __decode(string))
-+
-+
-+def get_lists():
-+    return [__cached_decoded_str(field) for field in __get_fields(b'lists')]
-+
-+
-+class ListItem:
-+    def __init__(self, name):
-+        self._listname = name
-+
-+    def __repr__(self):
-+        return '<{} list item at {}>'.format(self._listname, id(self))
-+
-+
-+def get_list(name):
-+    # XXX: This function is extremely inefficient and could be interators and
-+    # lazily loaded properties, but for API compat we stay slow
-+    orig_name = name
-+    name = name.encode()
-+
-+    if name not in __get_fields(b'lists'):
-+        raise KeyError('list not available')
-+
-+    list_ = lib.hexchat_list_get(lib.ph, name)
-+    if list_ == ffi.NULL:
-+        return None
-+
-+    ret = []
-+    fields = __get_fields(name)
-+
-+    def string_getter(field):
-+        string = lib.hexchat_list_str(lib.ph, list_, field)
-+        if string != ffi.NULL:
-+            return __decode(ffi.string(string))
-+        return ''
-+
-+    def ptr_getter(field):
-+        if field == b'context':
-+            ptr = lib.hexchat_list_str(lib.ph, list_, field)
-+            ctx = ffi.cast('hexchat_context*', ptr)
-+            return Context(ctx)
-+        return None
-+
-+    getters = {
-+        ord('s'): string_getter,
-+        ord('i'): lambda field: lib.hexchat_list_int(lib.ph, list_, field),
-+        ord('t'): lambda field: lib.hexchat_list_time(lib.ph, list_, field),
-+        ord('p'): ptr_getter,
-+    }
-+
-+    while lib.hexchat_list_next(lib.ph, list_) is 1:
-+        item = ListItem(orig_name)
-+        for field in fields:
-+            getter = getters.get(ord(field[0]))
-+            if getter is not None:
-+                field_name = field[1:]
-+                setattr(item, __cached_decoded_str(field_name), getter(field_name))
-+        ret.append(item)
-+
-+    lib.hexchat_list_free(lib.ph, list_)
-+    return ret
-+
-+
-+def hook_command(command, callback, userdata=None, priority=PRI_NORM, help=None):
-+    plugin = __get_current_plugin()
-+    hook = plugin.add_hook(callback, userdata)
-+    handle = lib.hexchat_hook_command(lib.ph, command.encode(), priority, lib._on_command_hook,
-+                                      help.encode() if help is not None else ffi.NULL,
-+                                      hook.handle)
-+    hook.hexchat_hook = handle
-+    return id(hook)
-+
-+
-+def hook_print(name, callback, userdata=None, priority=PRI_NORM):
-+    plugin = __get_current_plugin()
-+    hook = plugin.add_hook(callback, userdata)
-+    handle = lib.hexchat_hook_print(lib.ph, name.encode(), priority, lib._on_print_hook,
-+                                    hook.handle)
-+    hook.hexchat_hook = handle
-+    return id(hook)
-+
-+
-+def hook_print_attrs(name, callback, userdata=None, priority=PRI_NORM):
-+    plugin = __get_current_plugin()
-+    hook = plugin.add_hook(callback, userdata)
-+    handle = lib.hexchat_hook_print_attrs(lib.ph, name.encode(), priority, lib._on_print_attrs_hook,
-+                                          hook.handle)
-+    hook.hexchat_hook = handle
-+    return id(hook)
-+
-+
-+def hook_server(name, callback, userdata=None, priority=PRI_NORM):
-+    plugin = __get_current_plugin()
-+    hook = plugin.add_hook(callback, userdata)
-+    handle = lib.hexchat_hook_server(lib.ph, name.encode(), priority, lib._on_server_hook,
-+                                     hook.handle)
-+    hook.hexchat_hook = handle
-+    return id(hook)
-+
-+
-+def hook_server_attrs(name, callback, userdata=None, priority=PRI_NORM):
-+    plugin = __get_current_plugin()
-+    hook = plugin.add_hook(callback, userdata)
-+    handle = lib.hexchat_hook_server_attrs(lib.ph, name.encode(), priority, lib._on_server_attrs_hook,
-+                                           hook.handle)
-+    hook.hexchat_hook = handle
-+    return id(hook)
-+
-+
-+def hook_timer(timeout, callback, userdata=None):
-+    plugin = __get_current_plugin()
-+    hook = plugin.add_hook(callback, userdata)
-+    handle = lib.hexchat_hook_timer(lib.ph, timeout, lib._on_timer_hook, hook.handle)
-+    hook.hexchat_hook = handle
-+    return id(hook)
-+
-+
-+def hook_unload(callback, userdata=None):
-+    plugin = __get_current_plugin()
-+    hook = plugin.add_hook(callback, userdata, is_unload=True)
-+    return id(hook)
-+
-+
-+def unhook(handle):
-+    plugin = __get_current_plugin()
-+    return plugin.remove_hook(handle)
-+
-+
-+def set_pluginpref(name, value):
-+    if isinstance(value, str):
-+        return bool(lib.hexchat_pluginpref_set_str(lib.ph, name.encode(), value.encode()))
-+    elif isinstance(value, int):
-+        return bool(lib.hexchat_pluginpref_set_int(lib.ph, name.encode(), value))
-+    else:
-+        # XXX: This should probably raise but this keeps API
-+        return False
-+
-+
-+def get_pluginpref(name):
-+    name = name.encode()
-+    string_out = ffi.new('char[512]')
-+    if lib.hexchat_pluginpref_get_str(lib.ph, name, string_out) is not 1:
-+        return None
-+
-+    string = ffi.string(string_out)
-+    # This API stores everything as a string so we have to figure out what
-+    # its actual type was supposed to be.
-+    if len(string) > 12:  # Can't be a number
-+        return __decode(string)
-+
-+    number = lib.hexchat_pluginpref_get_int(lib.ph, name)
-+    if number == -1 and string != b'-1':
-+        return __decode(string)
-+
-+    return number
-+
-+
-+def del_pluginpref(name):
-+    return bool(lib.hexchat_pluginpref_delete(lib.ph, name.encode()))
-+
-+
-+def list_pluginpref():
-+    prefs_str = ffi.new('char[4096]')
-+    if lib.hexchat_pluginpref_list(lib.ph, prefs_str) is 1:
-+        return __decode(prefs_str).split(',')
-+    return []
-+
-+
-+class Context:
-+    def __init__(self, ctx):
-+        self._ctx = ctx
-+
-+    def __eq__(self, value):
-+        if not isinstance(value, Context):
-+            return False
-+        return self._ctx == value._ctx
-+
-+    @contextmanager
-+    def __change_context(self):
-+        old_ctx = lib.hexchat_get_context(lib.ph)
-+        if not self.set():
-+            # XXX: Behavior change, previously used wrong context
-+            lib.hexchat_print(lib.ph,
-+                              b'Context object refers to closed context, ignoring call')
-+            return
-+        yield
-+        lib.hexchat_set_context(lib.ph, old_ctx)
-+
-+    def set(self):
-+        # XXX: API addition, C plugin silently ignored failure
-+        return bool(lib.hexchat_set_context(lib.ph, self._ctx))
-+
-+    def prnt(self, string):
-+        with self.__change_context():
-+            prnt(string)
-+
-+    def emit_print(self, event_name, *args, **kwargs):
-+        time = kwargs.pop('time', 0)  # For py2 compat
-+        with self.__change_context():
-+            return emit_print(event_name, *args, time=time)
-+
-+    def command(self, string):
-+        with self.__change_context():
-+            command(string)
-+
-+    def get_info(self, name):
-+        with self.__change_context():
-+            return get_info(name)
-+
-+    def get_list(self, name):
-+        with self.__change_context():
-+            return get_list(name)
-+
-+
-+def get_context():
-+    ctx = lib.hexchat_get_context(lib.ph)
-+    return Context(ctx)
-+
-+
-+def find_context(server=None, channel=None):
-+    server = server.encode() if server is not None else ffi.NULL
-+    channel = channel.encode() if channel is not None else ffi.NULL
-+    ctx = lib.hexchat_find_context(lib.ph, server, channel)
-+    if ctx == ffi.NULL:
-+        return None
-+    return Context(ctx)
-diff --git a/plugins/python/generate_plugin.py b/plugins/python/generate_plugin.py
-new file mode 100755
-index 00000000..5c52b37b
---- /dev/null
-+++ b/plugins/python/generate_plugin.py
-@@ -0,0 +1,89 @@
-+#!/usr/bin/env python3
-+
-+import sys
-+import cffi
-+
-+builder = cffi.FFI()
-+
-+# hexchat-plugin.h
-+with open(sys.argv[1]) as f:
-+    output = []
-+    eat_until_endif = 0
-+    # This is very specific to hexchat-plugin.h, it is not a cpp
-+    for line in f:
-+        if line.startswith('#define'):
-+            continue
-+        elif line.endswith('HEXCHAT_PLUGIN_H\n'):
-+            continue
-+        elif 'time.h' in line:
-+            output.append('typedef int... time_t;')
-+        elif line.startswith('#if'):
-+            eat_until_endif += 1
-+        elif line.startswith('#endif'):
-+            eat_until_endif -= 1
-+        elif eat_until_endif and '_hexchat_context' not in line:
-+            continue
-+        else:
-+            output.append(line)
-+    builder.cdef(''.join(output))
-+
-+builder.embedding_api('''
-+extern "Python" int _on_py_command(char **, char **, void *);
-+extern "Python" int _on_load_command(char **, char **, void *);
-+extern "Python" int _on_unload_command(char **, char **, void *);
-+extern "Python" int _on_reload_command(char **, char **, void *);
-+extern "Python" int _on_say_command(char **, char **, void *);
-+
-+extern "Python" int _on_command_hook(char **, char **, void *);
-+extern "Python" int _on_print_hook(char **, void *);
-+extern "Python" int _on_print_attrs_hook(char **, hexchat_event_attrs *, void *);
-+extern "Python" int _on_server_hook(char **, char **, void *);
-+extern "Python" int _on_server_attrs_hook(char **, char **, hexchat_event_attrs *, void *);
-+extern "Python" int _on_timer_hook(void *);
-+
-+extern "Python" int _on_plugin_init(char **, char **, char **, char *, char *);
-+extern "Python" int _on_plugin_deinit(void);
-+
-+static hexchat_plugin *ph;
-+''')
-+
-+builder.set_source('_hexchat_embedded', '''
-+/* Python's header defines these.. */
-+#undef HAVE_MEMRCHR
-+#undef HAVE_STRINGS_H
-+
-+#include "config.h"
-+#include "hexchat-plugin.h"
-+
-+static hexchat_plugin *ph;
-+CFFI_DLLEXPORT int _on_plugin_init(char **, char **, char **, char *, char *);
-+CFFI_DLLEXPORT int _on_plugin_deinit(void);
-+
-+int hexchat_plugin_init(hexchat_plugin *plugin_handle,
-+                        char **name_out, char **description_out,
-+                        char **version_out, char *arg)
-+{
-+    if (ph != NULL)
-+    {
-+        puts ("Python plugin already loaded\\n");
-+        return 0; /* Prevent loading twice */
-+    }
-+
-+    ph = plugin_handle;
-+    return _on_plugin_init(name_out, description_out, version_out, arg, HEXCHATLIBDIR);
-+}
-+
-+int hexchat_plugin_deinit(void)
-+{
-+    int ret = _on_plugin_deinit();
-+    ph = NULL;
-+    return ret;
-+}
-+''')
-+
-+# python.py
-+with open(sys.argv[2]) as f:
-+    builder.embedding_init_code(f.read())
-+
-+# python.c
-+builder.emit_c_code(sys.argv[3])
-diff --git a/plugins/python/hexchat.py b/plugins/python/hexchat.py
-new file mode 100644
-index 00000000..6922490b
---- /dev/null
-+++ b/plugins/python/hexchat.py
-@@ -0,0 +1 @@
-+from _hexchat import *
-diff --git a/plugins/python/meson.build b/plugins/python/meson.build
-index e24f0c6f..2ad5128e 100644
---- a/plugins/python/meson.build
-+++ b/plugins/python/meson.build
-@@ -5,8 +5,18 @@ else
-   python_dep = dependency(python_opt, version: '>= 2.7')
- endif
- 
--shared_module('python', 'python.c',
--  dependencies: [libgio_dep, hexchat_plugin_dep, python_dep],
-+python3_source = custom_target('python-bindings',
-+  input: ['../../src/common/hexchat-plugin.h', 'python.py'],
-+  output: 'python.c',
-+  command: [find_program('generate_plugin.py'), '@INPUT@', '@OUTPUT@']
-+)
-+
-+install_data(['_hexchat.py', 'hexchat.py', 'xchat.py'],
-+  install_dir: join_paths(get_option('libdir'), 'hexchat/python')
-+)
-+
-+shared_module('python', python3_source,
-+  dependencies: [hexchat_plugin_dep, python_dep],
-   install: true,
-   install_dir: plugindir,
-   name_prefix: '',
-diff --git a/plugins/python/python.c b/plugins/python/python.c
-deleted file mode 100644
-index 4403474d..00000000
---- a/plugins/python/python.c
-+++ /dev/null
-@@ -1,2834 +0,0 @@
--/*
--* Copyright (c) 2002-2003  Gustavo Niemeyer <niemeyer at conectiva.com>
--*
--* XChat Python Plugin Interface
--*
--* Xchat Python Plugin Interface is free software; you can redistribute
--* it and/or modify it under the terms of the GNU General Public License
--* as published by the Free Software Foundation; either version 2 of the
--* License, or (at your option) any later version.
--*
--* pybot is distributed in the hope that it will be useful,
--* but WITHOUT ANY WARRANTY; without even the implied warranty of
--* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--* GNU General Public License for more details.
--*
--* You should have received a copy of the GNU General Public License
--* along with this file; if not, write to the Free Software
--* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
--*/
--
--/* Thread support
-- * ==============
-- *
-- * The python interpreter has a global interpreter lock. Any thread
-- * executing must acquire it before working with data accessible from
-- * python code. Here we must also care about xchat not being
-- * thread-safe. We do this by using an xchat lock, which protects
-- * xchat instructions from being executed out of time (when this
-- * plugin is not "active").
-- *
-- * When xchat calls python code:
-- *   - Change the current_plugin for the executing plugin;
-- *   - Release xchat lock
-- *   - Acquire the global interpreter lock
-- *   - Make the python call
-- *   - Release the global interpreter lock
-- *   - Acquire xchat lock
-- *
-- * When python code calls xchat:
-- *   - Release the global interpreter lock
-- *   - Acquire xchat lock
-- *   - Restore context, if necessary
-- *   - Make the xchat call
-- *   - Release xchat lock
-- *   - Acquire the global interpreter lock
-- *
-- * Inside a timer, so that individual threads have a chance to run:
-- *   - Release xchat lock
-- *   - Go ahead threads. Have a nice time!
-- *   - Acquire xchat lock
-- *
-- */
--
--#include "config.h"
--
--#include <glib.h>
--#include <glib/gstdio.h>
--#include <string.h>
--#include <stdlib.h>
--#include <sys/types.h>
--
--#ifdef WIN32
--#include <direct.h>
--#else
--#include <unistd.h>
--#include <dirent.h>
--#endif
--
--#include "hexchat-plugin.h"
--#undef _POSIX_C_SOURCE	/* Avoid warnings from /usr/include/features.h */
--#undef _XOPEN_SOURCE
--#undef HAVE_MEMRCHR /* Avoid redefinition in Python.h */
--#undef HAVE_STRINGS_H
--#include <Python.h>
--#include <structmember.h>
--#include <pythread.h>
--
--/* Macros to convert version macros into string literals.
-- * The indirect macro is a well-known preprocessor trick to force X to be evaluated before the # operator acts to make it a string literal.
-- * If STRINGIZE were to be directly defined as #X instead, VERSION would be "VERSION_MAJOR" instead of "1".
-- */
--#define STRINGIZE2(X) #X
--#define STRINGIZE(X) STRINGIZE2(X)
--
--/* Version number macros */
--#define VERSION_MAJOR 1
--#define VERSION_MINOR 0
--
--/* Version string macro e.g 1.0/3.3 */
--#define VERSION STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "/" \
--				STRINGIZE(PY_MAJOR_VERSION) "." STRINGIZE (PY_MINOR_VERSION)
--
--/* #define's for Python 2 */
--#if PY_MAJOR_VERSION == 2
--#undef PyLong_Check
--#define PyLong_Check PyInt_Check
--#define PyLong_AsLong PyInt_AsLong
--#define PyLong_FromLong PyInt_FromLong
--
--#undef PyUnicode_Check
--#undef PyUnicode_FromString
--#undef PyUnicode_FromFormat
--#define PyUnicode_Check PyString_Check
--#define PyUnicode_AsFormat PyString_AsFormat
--#define PyUnicode_FromFormat PyString_FromFormat
--#define PyUnicode_FromString PyString_FromString
--#define PyUnicode_AsUTF8 PyString_AsString
--
--#ifdef WIN32
--#undef WITH_THREAD
--#endif
--#endif
--
--/* #define for Python 3 */
--#if PY_MAJOR_VERSION == 3
--#define IS_PY3K
--#endif
--
--#define NONE 0
--#define ALLOW_THREADS 1
--#define RESTORE_CONTEXT 2
--
--#ifdef WITH_THREAD
--#define ACQUIRE_XCHAT_LOCK() PyThread_acquire_lock(xchat_lock, 1)
--#define RELEASE_XCHAT_LOCK() PyThread_release_lock(xchat_lock)
--#define BEGIN_XCHAT_CALLS(x) \
--	do { \
--		PyObject *calls_plugin = NULL; \
--		PyThreadState *calls_thread; \
--		if ((x) & RESTORE_CONTEXT) \
--			calls_plugin = Plugin_GetCurrent(); \
--		calls_thread = PyEval_SaveThread(); \
--		ACQUIRE_XCHAT_LOCK(); \
--		if (!((x) & ALLOW_THREADS)) { \
--			PyEval_RestoreThread(calls_thread); \
--			calls_thread = NULL; \
--		} \
--		if (calls_plugin) \
--			hexchat_set_context(ph, \
--				Plugin_GetContext(calls_plugin)); \
--		while (0)
--#define END_XCHAT_CALLS() \
--		RELEASE_XCHAT_LOCK(); \
--		if (calls_thread) \
--			PyEval_RestoreThread(calls_thread); \
--	} while(0)
--#else
--#define ACQUIRE_XCHAT_LOCK()
--#define RELEASE_XCHAT_LOCK()
--#define BEGIN_XCHAT_CALLS(x)
--#define END_XCHAT_CALLS()
--#endif
--
--#ifdef WITH_THREAD
--
--#define BEGIN_PLUGIN(plg) \
--	do { \
--	hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
--	RELEASE_XCHAT_LOCK(); \
--	Plugin_AcquireThread(plg); \
--	Plugin_SetContext(plg, begin_plugin_ctx); \
--	} while (0)
--#define END_PLUGIN(plg) \
--	do { \
--	Plugin_ReleaseThread(plg); \
--	ACQUIRE_XCHAT_LOCK(); \
--	} while (0)
--
--#else /* !WITH_THREAD (win32) */
--
--static PyThreadState *pTempThread;
--
--#define BEGIN_PLUGIN(plg) \
--	do { \
--	hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
--	RELEASE_XCHAT_LOCK(); \
--	PyEval_AcquireLock(); \
--	pTempThread = PyThreadState_Swap(((PluginObject *)(plg))->tstate); \
--	Plugin_SetContext(plg, begin_plugin_ctx); \
--	} while (0)
--#define END_PLUGIN(plg) \
--	do { \
--	((PluginObject *)(plg))->tstate = PyThreadState_Swap(pTempThread); \
--	PyEval_ReleaseLock(); \
--	ACQUIRE_XCHAT_LOCK(); \
--	} while (0)
--
--#endif /* !WITH_THREAD */
--
--#define Plugin_Swap(x) \
--	PyThreadState_Swap(((PluginObject *)(x))->tstate)
--#define Plugin_AcquireThread(x) \
--	PyEval_AcquireThread(((PluginObject *)(x))->tstate)
--#define Plugin_ReleaseThread(x) \
--	Util_ReleaseThread(((PluginObject *)(x))->tstate)
--#define Plugin_GetFilename(x) \
--	(((PluginObject *)(x))->filename)
--#define Plugin_GetName(x) \
--	(((PluginObject *)(x))->name)
--#define Plugin_GetVersion(x) \
--	(((PluginObject *)(x))->version)
--#define Plugin_GetDesc(x) \
--	(((PluginObject *)(x))->description)
--#define Plugin_GetHooks(x) \
--	(((PluginObject *)(x))->hooks)
--#define Plugin_GetContext(x) \
--	(((PluginObject *)(x))->context)
--#define Plugin_SetFilename(x, y) \
--	((PluginObject *)(x))->filename = (y);
--#define Plugin_SetName(x, y) \
--	((PluginObject *)(x))->name = (y);
--#define Plugin_SetVersion(x, y) \
--	((PluginObject *)(x))->version = (y);
--#define Plugin_SetDescription(x, y) \
--	((PluginObject *)(x))->description = (y);
--#define Plugin_SetHooks(x, y) \
--	((PluginObject *)(x))->hooks = (y);
--#define Plugin_SetContext(x, y) \
--	((PluginObject *)(x))->context = (y);
--#define Plugin_SetGui(x, y) \
--	((PluginObject *)(x))->gui = (y);
--
--#define HOOK_XCHAT  1
--#define HOOK_XCHAT_ATTR 2
--#define HOOK_UNLOAD 3
--
--/* ===================================================================== */
--/* Object definitions */
--
--typedef struct {
--	PyObject_HEAD
--	int softspace; /* We need it for print support. */
--} XChatOutObject;
--
--typedef struct {
--	PyObject_HEAD
--	hexchat_context *context;
--} ContextObject;
--
--typedef struct {
--	PyObject_HEAD
--	PyObject *time;
--} AttributeObject;
--
--typedef struct {
--	PyObject_HEAD
--	const char *listname;
--	PyObject *dict;
--} ListItemObject;
--
--typedef struct {
--	PyObject_HEAD
--	char *name;
--	char *version;
--	char *filename;
--	char *description;
--	GSList *hooks;
--	PyThreadState *tstate;
--	hexchat_context *context;
--	void *gui;
--} PluginObject;
--
--typedef struct {
--	int type;
--	PyObject *plugin;
--	PyObject *callback;
--	PyObject *userdata;
--	char *name;
--	void *data; /* A handle, when type == HOOK_XCHAT */
--} Hook;
--
--
--/* ===================================================================== */
--/* Function declarations */
--
--static PyObject *Util_BuildList(char *word[]);
--static PyObject *Util_BuildEOLList(char *word[]);
--static void Util_Autoload(void);
--static char *Util_Expand(char *filename);
--
--static int Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata);
--static int Callback_Command(char *word[], char *word_eol[], void *userdata);
--static int Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata);
--static int Callback_Print(char *word[], void *userdata);
--static int Callback_Timer(void *userdata);
--static int Callback_ThreadTimer(void *userdata);
--
--static PyObject *XChatOut_New(void);
--static PyObject *XChatOut_write(PyObject *self, PyObject *args);
--static void XChatOut_dealloc(PyObject *self);
--
--static PyObject *Attribute_New(hexchat_event_attrs *attrs);
--
--static void Context_dealloc(PyObject *self);
--static PyObject *Context_set(ContextObject *self, PyObject *args);
--static PyObject *Context_command(ContextObject *self, PyObject *args);
--static PyObject *Context_prnt(ContextObject *self, PyObject *args);
--static PyObject *Context_get_info(ContextObject *self, PyObject *args);
--static PyObject *Context_get_list(ContextObject *self, PyObject *args);
--static PyObject *Context_compare(ContextObject *a, ContextObject *b, int op);
--static PyObject *Context_FromContext(hexchat_context *context);
--static PyObject *Context_FromServerAndChannel(char *server, char *channel);
--
--static PyObject *Plugin_New(char *filename, PyObject *xcoobj);
--static PyObject *Plugin_GetCurrent(void);
--static PluginObject *Plugin_ByString(char *str);
--static Hook *Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
--			    PyObject *userdata, char *name, void *data);
--static Hook *Plugin_FindHook(PyObject *plugin, char *name);
--static void Plugin_RemoveHook(PyObject *plugin, Hook *hook);
--static void Plugin_RemoveAllHooks(PyObject *plugin);
--
--static PyObject *Module_hexchat_command(PyObject *self, PyObject *args);
--static PyObject *Module_xchat_prnt(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_get_context(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_find_context(PyObject *self, PyObject *args,
--					   PyObject *kwargs);
--static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_hook_command(PyObject *self, PyObject *args,
--					   PyObject *kwargs);
--static PyObject *Module_hexchat_hook_server(PyObject *self, PyObject *args,
--					  PyObject *kwargs);
--static PyObject *Module_hexchat_hook_print(PyObject *self, PyObject *args,
--					 PyObject *kwargs);
--static PyObject *Module_hexchat_hook_timer(PyObject *self, PyObject *args,
--					 PyObject *kwargs);
--static PyObject *Module_hexchat_unhook(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
--static PyObject *Module_xchat_get_list(PyObject *self, PyObject *args);
--static PyObject *Module_xchat_get_lists(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_nickcmp(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_strip(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_set(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_get(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_list(PyObject *self, PyObject *args);
--
--static void IInterp_Exec(char *command);
--static int IInterp_Cmd(char *word[], char *word_eol[], void *userdata);
--
--static void Command_PyList(void);
--static void Command_PyLoad(char *filename);
--static void Command_PyUnload(char *name);
--static void Command_PyReload(char *name);
--static void Command_PyAbout(void);
--static int Command_Py(char *word[], char *word_eol[], void *userdata);
--
--/* ===================================================================== */
--/* Static declarations and definitions */
--
--static PyTypeObject Plugin_Type;
--static PyTypeObject XChatOut_Type;
--static PyTypeObject Context_Type;
--static PyTypeObject ListItem_Type;
--static PyTypeObject Attribute_Type;
--
--static PyThreadState *main_tstate = NULL;
--static void *thread_timer = NULL;
--
--static hexchat_plugin *ph;
--static GSList *plugin_list = NULL;
--
--static PyObject *interp_plugin = NULL;
--static PyObject *xchatout = NULL;
--
--#ifdef WITH_THREAD
--static PyThread_type_lock xchat_lock = NULL;
--#endif
--
--static const char usage[] = "\
--Usage: /PY LOAD   <filename>\n\
--           UNLOAD <filename|name>\n\
--           RELOAD <filename|name>\n\
--           LIST\n\
--           EXEC <command>\n\
--           CONSOLE\n\
--           ABOUT\n\
--\n";
--
--static const char about[] = "HexChat Python interface version " VERSION "\n";
--
--/* ===================================================================== */
--/* Utility functions */
--
--static PyObject *
--Util_BuildList(char *word[])
--{
--	PyObject *list;
--	int listsize = 31;
--	int i;
--	/* Find the last valid array member; there may be intermediate NULLs that
--	 * would otherwise cause us to drop some members. */
--	while (listsize > 0 &&
--	       (word[listsize] == NULL || word[listsize][0] == 0))
--		listsize--;
--	list = PyList_New(listsize);
--	if (list == NULL) {
--		PyErr_Print();
--		return NULL;
--	}
--	for (i = 1; i <= listsize; i++) {
--		PyObject *o;
--		if (word[i] == NULL) {
--			Py_INCREF(Py_None);
--			o = Py_None;
--		} else {
--			/* This handles word[i][0] == 0 automatically. */
--			o = PyUnicode_FromString(word[i]);
--		}
--		PyList_SetItem(list, i - 1, o);
--	}
--	return list;
--}
--
--static PyObject *
--Util_BuildEOLList(char *word[])
--{
--	PyObject *list;
--	int listsize = 31;
--	int i;
--	char *accum = NULL;
--	char *last = NULL;
--
--	/* Find the last valid array member; there may be intermediate NULLs that
--	 * would otherwise cause us to drop some members. */
--	while (listsize > 0 &&
--	       (word[listsize] == NULL || word[listsize][0] == 0))
--		listsize--;
--	list = PyList_New(listsize);
--	if (list == NULL) {
--		PyErr_Print();
--		return NULL;
--	}
--	for (i = listsize; i > 0; i--) {
--		char *part = word[i];
--		PyObject *uni_part;
--		if (accum == NULL) {
--			accum = g_strdup (part);
--		} else if (part != NULL && part[0] != 0) {
--			last = accum;
--			accum = g_strjoin(" ", part, last, NULL);
--			g_free (last);
--			last = NULL;
--
--			if (accum == NULL) {
--				Py_DECREF(list);
--				hexchat_print(ph, "Not enough memory to alloc accum"
--				              "for python plugin callback");
--				return NULL;
--			}
--		}
--		uni_part = PyUnicode_FromString(accum);
--		PyList_SetItem(list, i - 1, uni_part);
--	}
--
--	g_free (last);
--	g_free (accum);
--
--	return list;
--}
--
--static void
--Util_Autoload_from (const char *dir_name)
--{
--	gchar *oldcwd;
--	const char *entry_name;
--	GDir *dir;
--
--	oldcwd = g_get_current_dir ();
--	if (oldcwd == NULL)
--		return;
--	if (g_chdir(dir_name) != 0)
--	{
--		g_free (oldcwd);
--		return;
--	}
--	dir = g_dir_open (".", 0, NULL);
--	if (dir == NULL)
--	{
--		g_free (oldcwd);
--		return;
--	}
--	while ((entry_name = g_dir_read_name (dir)))
--	{
--		if (g_str_has_suffix (entry_name, ".py"))
--			Command_PyLoad((char*)entry_name);
--	}
--	g_dir_close (dir);
--	g_chdir (oldcwd);
--}
--
--static void
--Util_Autoload()
--{
--	const char *xdir;
--	char *sub_dir;
--	/* we need local filesystem encoding for g_chdir, g_dir_open etc */
--
--	xdir = hexchat_get_info(ph, "configdir");
--
--	/* auto-load from subdirectory addons */
--	sub_dir = g_build_filename (xdir, "addons", NULL);
--	Util_Autoload_from(sub_dir);
--	g_free (sub_dir);
--}
--
--static char *
--Util_Expand(char *filename)
--{
--	char *expanded;
--
--	/* Check if this is an absolute path. */
--	if (g_path_is_absolute(filename)) {
--		if (g_file_test(filename, G_FILE_TEST_EXISTS))
--			return g_strdup(filename);
--		else
--			return NULL;
--	}
--
--	/* Check if it starts with ~/ and expand the home if positive. */
--	if (*filename == '~' && *(filename+1) == '/') {
--		expanded = g_build_filename(g_get_home_dir(),
--					    filename+2, NULL);
--		if (g_file_test(expanded, G_FILE_TEST_EXISTS))
--			return expanded;
--		else {
--			g_free(expanded);
--			return NULL;
--		}
--	}
--
--	/* Check if it's in the current directory. */
--	expanded = g_build_filename(g_get_current_dir(),
--				    filename, NULL);
--	if (g_file_test(expanded, G_FILE_TEST_EXISTS))
--		return expanded;
--	g_free(expanded);
--
--	/* Check if ~/.config/hexchat/addons/<filename> exists. */
--	expanded = g_build_filename(hexchat_get_info(ph, "configdir"),
--				    "addons", filename, NULL);
--	if (g_file_test(expanded, G_FILE_TEST_EXISTS))
--		return expanded;
--	g_free(expanded);
--
--	return NULL;
--}
--
--/* Similar to PyEval_ReleaseThread, but accepts NULL thread states. */
--static void
--Util_ReleaseThread(PyThreadState *tstate)
--{
--	PyThreadState *old_tstate;
--	if (tstate == NULL)
--		Py_FatalError("PyEval_ReleaseThread: NULL thread state");
--	old_tstate = PyThreadState_Swap(NULL);
--	if (old_tstate != tstate && old_tstate != NULL)
--		Py_FatalError("PyEval_ReleaseThread: wrong thread state");
--	PyEval_ReleaseLock();
--}
--
--/* ===================================================================== */
--/* Hookable functions. These are the entry points to python code, besides
-- * the load function, and the hooks for interactive interpreter. */
--
--static int
--Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata)
--{
--	Hook *hook = (Hook *) userdata;
--	PyObject *retobj;
--	PyObject *word_list, *word_eol_list;
--	PyObject *attributes;
--	int ret = HEXCHAT_EAT_NONE;
--	PyObject *plugin;
--
--	plugin = hook->plugin;
--	BEGIN_PLUGIN(plugin);
--
--	word_list = Util_BuildList(word);
--	if (word_list == NULL) {
--		END_PLUGIN(plugin);
--		return HEXCHAT_EAT_NONE;
--	}
--	word_eol_list = Util_BuildList(word_eol);
--	if (word_eol_list == NULL) {
--		Py_DECREF(word_list);
--		END_PLUGIN(plugin);
--		return HEXCHAT_EAT_NONE;
--	}
--
--	attributes = Attribute_New(attrs);
--
--	if (hook->type == HOOK_XCHAT_ATTR)
--		retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
--					       word_eol_list, hook->userdata, attributes);
--	else
--		retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
--					       word_eol_list, hook->userdata);
--	Py_DECREF(word_list);
--	Py_DECREF(word_eol_list);
--	Py_DECREF(attributes);
--
--	if (retobj == Py_None) {
--		ret = HEXCHAT_EAT_NONE;
--		Py_DECREF(retobj);
--	} else if (retobj) {
--		ret = PyLong_AsLong(retobj);
--		Py_DECREF(retobj);
--	} else {
--		PyErr_Print();
--	}
--
--	END_PLUGIN(plugin);
--
--	return ret;
--}
--
--static int
--Callback_Command(char *word[], char *word_eol[], void *userdata)
--{
--	Hook *hook = (Hook *) userdata;
--	PyObject *retobj;
--	PyObject *word_list, *word_eol_list;
--	int ret = HEXCHAT_EAT_NONE;
--	PyObject *plugin;
--
--	plugin = hook->plugin;
--	BEGIN_PLUGIN(plugin);
--
--	word_list = Util_BuildList(word);
--	if (word_list == NULL) {
--		END_PLUGIN(plugin);
--		return HEXCHAT_EAT_NONE;
--	}
--	word_eol_list = Util_BuildList(word_eol);
--	if (word_eol_list == NULL) {
--		Py_DECREF(word_list);
--		END_PLUGIN(plugin);
--		return HEXCHAT_EAT_NONE;
--	}
--
--	retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
--				       word_eol_list, hook->userdata);
--	Py_DECREF(word_list);
--	Py_DECREF(word_eol_list);
--
--	if (retobj == Py_None) {
--		ret = HEXCHAT_EAT_NONE;
--		Py_DECREF(retobj);
--	} else if (retobj) {
--		ret = PyLong_AsLong(retobj);
--		Py_DECREF(retobj);
--	} else {
--		PyErr_Print();
--	}
--
--	END_PLUGIN(plugin);
--
--	return ret;
--}
--
--static int
--Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata)
--{
--	Hook *hook = (Hook *) userdata;
--	PyObject *retobj;
--	PyObject *word_list;
--	PyObject *word_eol_list;
--	PyObject *attributes;
--	int ret = HEXCHAT_EAT_NONE;
--	PyObject *plugin;
--
--	plugin = hook->plugin;
--	BEGIN_PLUGIN(plugin);
--
--	word_list = Util_BuildList(word);
--	if (word_list == NULL) {
--		END_PLUGIN(plugin);
--		return HEXCHAT_EAT_NONE;
--	}
--	word_eol_list = Util_BuildEOLList(word);
--	if (word_eol_list == NULL) {
--		Py_DECREF(word_list);
--		END_PLUGIN(plugin);
--		return HEXCHAT_EAT_NONE;
--	}
--
--	attributes = Attribute_New(attrs);
--
--	retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
--					    word_eol_list, hook->userdata, attributes);
--
--	Py_DECREF(word_list);
--	Py_DECREF(word_eol_list);
--	Py_DECREF(attributes);
--
--	if (retobj == Py_None) {
--		ret = HEXCHAT_EAT_NONE;
--		Py_DECREF(retobj);
--	} else if (retobj) {
--		ret = PyLong_AsLong(retobj);
--		Py_DECREF(retobj);
--	} else {
--		PyErr_Print();
--	}
--
--	END_PLUGIN(plugin);
--
--	return ret;
--}
--
--static int
--Callback_Print(char *word[], void *userdata)
--{
--	Hook *hook = (Hook *) userdata;
--	PyObject *retobj;
--	PyObject *word_list;
--	PyObject *word_eol_list;
--	int ret = HEXCHAT_EAT_NONE;
--	PyObject *plugin;
--
--	plugin = hook->plugin;
--	BEGIN_PLUGIN(plugin);
--
--	word_list = Util_BuildList(word);
--	if (word_list == NULL) {
--		END_PLUGIN(plugin);
--		return HEXCHAT_EAT_NONE;
--	}
--	word_eol_list = Util_BuildEOLList(word);
--	if (word_eol_list == NULL) {
--		Py_DECREF(word_list);
--		END_PLUGIN(plugin);
--		return HEXCHAT_EAT_NONE;
--	}
--
--	retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
--					       word_eol_list, hook->userdata);
--
--	Py_DECREF(word_list);
--	Py_DECREF(word_eol_list);
--
--	if (retobj == Py_None) {
--		ret = HEXCHAT_EAT_NONE;
--		Py_DECREF(retobj);
--	} else if (retobj) {
--		ret = PyLong_AsLong(retobj);
--		Py_DECREF(retobj);
--	} else {
--		PyErr_Print();
--	}
--
--	END_PLUGIN(plugin);
--
--	return ret;
--}
--
--static int
--Callback_Timer(void *userdata)
--{
--	Hook *hook = (Hook *) userdata;
--	PyObject *retobj;
--	int ret = 0;
--	PyObject *plugin;
--
--	plugin = hook->plugin;
--
--	BEGIN_PLUGIN(hook->plugin);
--
--	retobj = PyObject_CallFunction(hook->callback, "(O)", hook->userdata);
--
--	if (retobj) {
--		ret = PyObject_IsTrue(retobj);
--		Py_DECREF(retobj);
--	} else {
--		PyErr_Print();
--	}
--
--	/* Returning 0 for this callback unhooks itself. */
--	if (ret == 0)
--		Plugin_RemoveHook(plugin, hook);
--
--	END_PLUGIN(plugin);
--
--	return ret;
--}
--
--#ifdef WITH_THREAD
--static int
--Callback_ThreadTimer(void *userdata)
--{
--	RELEASE_XCHAT_LOCK();
--#ifndef WIN32
--	usleep(1);
--#endif
--	ACQUIRE_XCHAT_LOCK();
--	return 1;
--}
--#endif
--
--/* ===================================================================== */
--/* XChatOut object */
--
--/* We keep this information global, so we can reset it when the
-- * deinit function is called. */
--/* XXX This should be somehow bound to the printing context. */
--static GString *xchatout_buffer = NULL;
--
--static PyObject *
--XChatOut_New()
--{
--	XChatOutObject *xcoobj;
--	xcoobj = PyObject_New(XChatOutObject, &XChatOut_Type);
--	if (xcoobj != NULL)
--		xcoobj->softspace = 0;
--	return (PyObject *) xcoobj;
--}
--
--static void
--XChatOut_dealloc(PyObject *self)
--{
--	Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--/* This is a little bit complex because we have to buffer data
-- * until a \n is received, since xchat breaks the line automatically.
-- * We also crop the last \n for this reason. */
--static PyObject *
--XChatOut_write(PyObject *self, PyObject *args)
--{
--	gboolean add_space;
--	char *data, *pos;
--
--	if (!PyArg_ParseTuple(args, "s:write", &data))
--		return NULL;
--	if (!data || !*data) {
--		Py_RETURN_NONE;
--	}
--	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
--	if (((XChatOutObject *)self)->softspace) {
--		add_space = TRUE;
--		((XChatOutObject *)self)->softspace = 0;
--	} else {
--		add_space = FALSE;
--	}
--
--	g_string_append (xchatout_buffer, data);
--
--	/* If not end of line add space to continue buffer later */
--	if (add_space && xchatout_buffer->str[xchatout_buffer->len - 1] != '\n')
--	{
--		g_string_append_c (xchatout_buffer, ' ');
--	}
--
--	/* If there is an end of line print up to that */
--	if ((pos = strrchr (xchatout_buffer->str, '\n')))
--	{
--		*pos = '\0';
--		hexchat_print (ph, xchatout_buffer->str);
--
--		/* Then remove it from buffer */
--		g_string_erase (xchatout_buffer, 0, pos - xchatout_buffer->str + 1);
--	}
--
--	END_XCHAT_CALLS();
--	Py_RETURN_NONE;
--}
--
--#define OFF(x) offsetof(XChatOutObject, x)
--
--static PyMemberDef XChatOut_members[] = {
--	{"softspace", T_INT, OFF(softspace), 0},
--	{0}
--};
--
--static PyMethodDef XChatOut_methods[] = {
--	{"write", XChatOut_write, METH_VARARGS},
--	{NULL, NULL}
--};
--
--static PyTypeObject XChatOut_Type = {
--	PyVarObject_HEAD_INIT(NULL, 0)
--	"hexchat.XChatOut",	/*tp_name*/
--	sizeof(XChatOutObject),	/*tp_basicsize*/
--	0,			/*tp_itemsize*/
--	XChatOut_dealloc,	/*tp_dealloc*/
--	0,			/*tp_print*/
--	0,			/*tp_getattr*/
--	0,			/*tp_setattr*/
--	0,			/*tp_compare*/
--	0,			/*tp_repr*/
--	0,			/*tp_as_number*/
--	0,			/*tp_as_sequence*/
--	0,			/*tp_as_mapping*/
--	0,			/*tp_hash*/
--        0,                      /*tp_call*/
--        0,                      /*tp_str*/
--        PyObject_GenericGetAttr,/*tp_getattro*/
--        PyObject_GenericSetAttr,/*tp_setattro*/
--        0,                      /*tp_as_buffer*/
--        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
--        0,                      /*tp_doc*/
--        0,                      /*tp_traverse*/
--        0,                      /*tp_clear*/
--        0,                      /*tp_richcompare*/
--        0,                      /*tp_weaklistoffset*/
--        0,                      /*tp_iter*/
--        0,                      /*tp_iternext*/
--        XChatOut_methods,       /*tp_methods*/
--        XChatOut_members,       /*tp_members*/
--        0,                      /*tp_getset*/
--        0,                      /*tp_base*/
--        0,                      /*tp_dict*/
--        0,                      /*tp_descr_get*/
--        0,                      /*tp_descr_set*/
--        0,                      /*tp_dictoffset*/
--        0,                      /*tp_init*/
--        PyType_GenericAlloc,    /*tp_alloc*/
--        PyType_GenericNew,      /*tp_new*/
--      	PyObject_Del,          /*tp_free*/
--        0,                      /*tp_is_gc*/
--};
--
--
--/* ===================================================================== */
--/* Attribute object */
--
--#undef OFF
--#define OFF(x) offsetof(AttributeObject, x)
--
--static PyMemberDef Attribute_members[] = {
--	{"time", T_OBJECT, OFF(time), 0},
--	{0}
--};
--
--static void
--Attribute_dealloc(PyObject *self)
--{
--	Py_DECREF(((AttributeObject*)self)->time);
--	Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyObject *
--Attribute_repr(PyObject *self)
--{
--	return PyUnicode_FromFormat("<Attribute object at %p>", self);
--}
--
--static PyTypeObject Attribute_Type = {
--	PyVarObject_HEAD_INIT(NULL, 0)
--	"hexchat.Attribute",	/*tp_name*/
--	sizeof(AttributeObject),	/*tp_basicsize*/
--	0,			/*tp_itemsize*/
--	Attribute_dealloc,	/*tp_dealloc*/
--	0,			/*tp_print*/
--	0,			/*tp_getattr*/
--	0,			/*tp_setattr*/
--	0,			/*tp_compare*/
--	Attribute_repr,		/*tp_repr*/
--	0,			/*tp_as_number*/
--	0,			/*tp_as_sequence*/
--	0,			/*tp_as_mapping*/
--	0,			/*tp_hash*/
--        0,                      /*tp_call*/
--        0,                      /*tp_str*/
--        PyObject_GenericGetAttr,/*tp_getattro*/
--        PyObject_GenericSetAttr,/*tp_setattro*/
--        0,                      /*tp_as_buffer*/
--        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
--        0,                      /*tp_doc*/
--        0,                      /*tp_traverse*/
--        0,                      /*tp_clear*/
--        0,                      /*tp_richcompare*/
--        0,                      /*tp_weaklistoffset*/
--        0,                      /*tp_iter*/
--        0,                      /*tp_iternext*/
--        0,                      /*tp_methods*/
--        Attribute_members,		/*tp_members*/
--        0,                      /*tp_getset*/
--        0,                      /*tp_base*/
--        0,                      /*tp_dict*/
--        0,                      /*tp_descr_get*/
--        0,                      /*tp_descr_set*/
--        0,						/*tp_dictoffset*/
--        0,                      /*tp_init*/
--        PyType_GenericAlloc,    /*tp_alloc*/
--        PyType_GenericNew,      /*tp_new*/
--      	PyObject_Del,          /*tp_free*/
--        0,                      /*tp_is_gc*/
--};
--
--static PyObject *
--Attribute_New(hexchat_event_attrs *attrs)
--{
--	AttributeObject *attr;
--	attr = PyObject_New(AttributeObject, &Attribute_Type);
--	if (attr != NULL) {
--		attr->time = PyLong_FromLong((long)attrs->server_time_utc);
--	}
--	return (PyObject *) attr;
--}
--
--
--/* ===================================================================== */
--/* Context object */
--
--static void
--Context_dealloc(PyObject *self)
--{
--	Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyObject *
--Context_set(ContextObject *self, PyObject *args)
--{
--	PyObject *plugin = Plugin_GetCurrent();
--	Plugin_SetContext(plugin, self->context);
--	Py_RETURN_NONE;
--}
--
--static PyObject *
--Context_command(ContextObject *self, PyObject *args)
--{
--	char *text;
--	if (!PyArg_ParseTuple(args, "s:command", &text))
--		return NULL;
--	BEGIN_XCHAT_CALLS(ALLOW_THREADS);
--	hexchat_set_context(ph, self->context);
--	hexchat_command(ph, text);
--	END_XCHAT_CALLS();
--	Py_RETURN_NONE;
--}
--
--static PyObject *
--Context_prnt(ContextObject *self, PyObject *args)
--{
--	char *text;
--	if (!PyArg_ParseTuple(args, "s:prnt", &text))
--		return NULL;
--	BEGIN_XCHAT_CALLS(ALLOW_THREADS);
--	hexchat_set_context(ph, self->context);
--	hexchat_print(ph, text);
--	END_XCHAT_CALLS();
--	Py_RETURN_NONE;
--}
--
--static PyObject *
--Context_emit_print(ContextObject *self, PyObject *args, PyObject *kwargs)
--{
--	char *argv[6];
--	char *name;
--	int res;
--	long time = 0;
--	hexchat_event_attrs *attrs;
--	char *kwlist[] = {"name", "arg1", "arg2", "arg3",
--					"arg4", "arg5", "arg6", 
--					"time", NULL};
--	memset(&argv, 0, sizeof(char*)*6);
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
--			      &argv[0], &argv[1], &argv[2],
--			      &argv[3], &argv[4], &argv[5],
--				  &time))
--		return NULL;
--	BEGIN_XCHAT_CALLS(ALLOW_THREADS);
--	hexchat_set_context(ph, self->context);
--	attrs = hexchat_event_attrs_create(ph);
--	attrs->server_time_utc = (time_t)time; 
--	
--	res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
--					 argv[3], argv[4], argv[5], NULL);
--
--	hexchat_event_attrs_free(ph, attrs);
--	END_XCHAT_CALLS();
--	return PyLong_FromLong(res);
--}
--
--static PyObject *
--Context_get_info(ContextObject *self, PyObject *args)
--{
--	const char *info;
--	char *name;
--	if (!PyArg_ParseTuple(args, "s:get_info", &name))
--		return NULL;
--	BEGIN_XCHAT_CALLS(NONE);
--	hexchat_set_context(ph, self->context);
--	info = hexchat_get_info(ph, name);
--	END_XCHAT_CALLS();
--	if (info == NULL) {
--		Py_RETURN_NONE;
--	}
--	return PyUnicode_FromString(info);
--}
--
--static PyObject *
--Context_get_list(ContextObject *self, PyObject *args)
--{
--	PyObject *plugin = Plugin_GetCurrent();
--	hexchat_context *saved_context = Plugin_GetContext(plugin);
--	PyObject *ret;
--	Plugin_SetContext(plugin, self->context);
--	ret = Module_xchat_get_list((PyObject*)self, args);
--	Plugin_SetContext(plugin, saved_context);
--	return ret;
--}
--
--/* needed to make context1 == context2 work */
--static PyObject *
--Context_compare(ContextObject *a, ContextObject *b, int op)
--{
--	PyObject *ret;
--	/* check for == */
--	if (op == Py_EQ)
--		ret = (a->context == b->context ? Py_True : Py_False);
--	/* check for != */
--	else if (op == Py_NE)
--		ret = (a->context != b->context ? Py_True : Py_False);
--	/* only makes sense as == and != */
--	else
--	{
--		PyErr_SetString(PyExc_TypeError, "contexts are either equal or not equal");
--		ret = Py_None;
--	}
--
--	Py_INCREF(ret);
--	return ret;
--}
--
--static PyMethodDef Context_methods[] = {
--	{"set", (PyCFunction) Context_set, METH_NOARGS},
--	{"command", (PyCFunction) Context_command, METH_VARARGS},
--	{"prnt", (PyCFunction) Context_prnt, METH_VARARGS},
--	{"emit_print", (PyCFunction) Context_emit_print, METH_VARARGS|METH_KEYWORDS},
--	{"get_info", (PyCFunction) Context_get_info, METH_VARARGS},
--	{"get_list", (PyCFunction) Context_get_list, METH_VARARGS},
--	{NULL, NULL}
--};
--
--static PyTypeObject Context_Type = {
--	PyVarObject_HEAD_INIT(NULL, 0)
--	"hexchat.Context",	/*tp_name*/
--	sizeof(ContextObject),	/*tp_basicsize*/
--	0,			/*tp_itemsize*/
--	Context_dealloc,        /*tp_dealloc*/
--	0,			/*tp_print*/
--	0,			/*tp_getattr*/
--	0,			/*tp_setattr*/
--	0,			/*tp_compare*/
--	0,			/*tp_repr*/
--	0,			/*tp_as_number*/
--	0,			/*tp_as_sequence*/
--	0,			/*tp_as_mapping*/
--	0,			/*tp_hash*/
--        0,                      /*tp_call*/
--        0,                      /*tp_str*/
--        PyObject_GenericGetAttr,/*tp_getattro*/
--        PyObject_GenericSetAttr,/*tp_setattro*/
--        0,                      /*tp_as_buffer*/
--        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
--        0,                      /*tp_doc*/
--        0,                      /*tp_traverse*/
--        0,                      /*tp_clear*/
--        (richcmpfunc)Context_compare,    /*tp_richcompare*/
--        0,                      /*tp_weaklistoffset*/
--        0,                      /*tp_iter*/
--        0,                      /*tp_iternext*/
--        Context_methods,        /*tp_methods*/
--        0,                      /*tp_members*/
--        0,                      /*tp_getset*/
--        0,                      /*tp_base*/
--        0,                      /*tp_dict*/
--        0,                      /*tp_descr_get*/
--        0,                      /*tp_descr_set*/
--        0,                      /*tp_dictoffset*/
--        0,                      /*tp_init*/
--        PyType_GenericAlloc,    /*tp_alloc*/
--        PyType_GenericNew,      /*tp_new*/
--      	PyObject_Del,          /*tp_free*/
--        0,                      /*tp_is_gc*/
--};
--
--static PyObject *
--Context_FromContext(hexchat_context *context)
--{
--	ContextObject *ctxobj = PyObject_New(ContextObject, &Context_Type);
--	if (ctxobj != NULL)
--		ctxobj->context = context;
--	return (PyObject *) ctxobj;
--}
--
--static PyObject *
--Context_FromServerAndChannel(char *server, char *channel)
--{
--	ContextObject *ctxobj;
--	hexchat_context *context;
--	BEGIN_XCHAT_CALLS(NONE);
--	context = hexchat_find_context(ph, server, channel);
--	END_XCHAT_CALLS();
--	if (context == NULL)
--		return NULL;
--	ctxobj = PyObject_New(ContextObject, &Context_Type);
--	if (ctxobj == NULL)
--		return NULL;
--	ctxobj->context = context;
--	return (PyObject *) ctxobj;
--}
--
--
--/* ===================================================================== */
--/* ListItem object */
--
--#undef OFF
--#define OFF(x) offsetof(ListItemObject, x)
--
--static PyMemberDef ListItem_members[] = {
--	{"__dict__", T_OBJECT, OFF(dict), 0},
--	{0}
--};
--
--static void
--ListItem_dealloc(PyObject *self)
--{
--	Py_DECREF(((ListItemObject*)self)->dict);
--	Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyObject *
--ListItem_repr(PyObject *self)
--{
--	return PyUnicode_FromFormat("<%s list item at %p>",
--			    	   ((ListItemObject*)self)->listname, self);
--}
--
--static PyTypeObject ListItem_Type = {
--	PyVarObject_HEAD_INIT(NULL, 0)
--	"hexchat.ListItem",	/*tp_name*/
--	sizeof(ListItemObject),	/*tp_basicsize*/
--	0,			/*tp_itemsize*/
--	ListItem_dealloc,	/*tp_dealloc*/
--	0,			/*tp_print*/
--	0,			/*tp_getattr*/
--	0,			/*tp_setattr*/
--	0,			/*tp_compare*/
--	ListItem_repr,		/*tp_repr*/
--	0,			/*tp_as_number*/
--	0,			/*tp_as_sequence*/
--	0,			/*tp_as_mapping*/
--	0,			/*tp_hash*/
--        0,                      /*tp_call*/
--        0,                      /*tp_str*/
--        PyObject_GenericGetAttr,/*tp_getattro*/
--        PyObject_GenericSetAttr,/*tp_setattro*/
--        0,                      /*tp_as_buffer*/
--        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
--        0,                      /*tp_doc*/
--        0,                      /*tp_traverse*/
--        0,                      /*tp_clear*/
--        0,                      /*tp_richcompare*/
--        0,                      /*tp_weaklistoffset*/
--        0,                      /*tp_iter*/
--        0,                      /*tp_iternext*/
--        0,                      /*tp_methods*/
--        ListItem_members,       /*tp_members*/
--        0,                      /*tp_getset*/
--        0,                      /*tp_base*/
--        0,                      /*tp_dict*/
--        0,                      /*tp_descr_get*/
--        0,                      /*tp_descr_set*/
--        OFF(dict),              /*tp_dictoffset*/
--        0,                      /*tp_init*/
--        PyType_GenericAlloc,    /*tp_alloc*/
--        PyType_GenericNew,      /*tp_new*/
--      	PyObject_Del,          /*tp_free*/
--        0,                      /*tp_is_gc*/
--};
--
--static PyObject *
--ListItem_New(const char *listname)
--{
--	ListItemObject *item;
--	item = PyObject_New(ListItemObject, &ListItem_Type);
--	if (item != NULL) {
--		/* listname parameter must be statically allocated. */
--		item->listname = listname;
--		item->dict = PyDict_New();
--		if (item->dict == NULL) {
--			Py_DECREF(item);
--			item = NULL;
--		}
--	}
--	return (PyObject *) item;
--}
--
--
--/* ===================================================================== */
--/* Plugin object */
--
--#define GET_MODULE_DATA(x, force) \
--	o = PyObject_GetAttrString(m, "__module_" #x "__"); \
--	if (o == NULL) { \
--		if (force) { \
--			hexchat_print(ph, "Module has no __module_" #x "__ " \
--					"defined"); \
--			goto error; \
--		} \
--		plugin->x = g_strdup(""); \
--	} else {\
--		if (!PyUnicode_Check(o)) { \
--			hexchat_print(ph, "Variable __module_" #x "__ " \
--					"must be a string"); \
--			goto error; \
--		} \
--		plugin->x = g_strdup(PyUnicode_AsUTF8(o)); \
--		if (plugin->x == NULL) { \
--			hexchat_print(ph, "Not enough memory to allocate " #x); \
--			goto error; \
--		} \
--	}
--
--static PyObject *
--Plugin_GetCurrent()
--{
--	PyObject *plugin;
--	plugin = PySys_GetObject("__plugin__");
--	if (plugin == NULL)
--		PyErr_SetString(PyExc_RuntimeError, "lost sys.__plugin__");
--	return plugin;
--}
--
--static hexchat_plugin *
--Plugin_GetHandle(PluginObject *plugin)
--{
--	/* This works but the issue is that the script must be ran to get
--	 * the name of it thus upon first use it will use the wrong handler
--	 * work around would be to run a fake script once to get name? */
--#if 0
--	/* return fake handle for pluginpref */
--	if (plugin->gui != NULL)
--		return plugin->gui;
--	else
--#endif
--		return ph;
--}
--
--static PluginObject *
--Plugin_ByString(char *str)
--{
--	GSList *list;
--	PluginObject *plugin;
--	char *basename;
--	list = plugin_list;
--	while (list != NULL) {
--		plugin = (PluginObject *) list->data;
--		basename = g_path_get_basename(plugin->filename);
--		if (basename == NULL)
--			break;
--		if (strcasecmp(plugin->name, str) == 0 ||
--		    strcasecmp(plugin->filename, str) == 0 ||
--		    strcasecmp(basename, str) == 0) {
--			g_free(basename);
--			return plugin;
--		}
--		g_free(basename);
--		list = list->next;
--	}
--	return NULL;
--}
--
--static Hook *
--Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
--	       PyObject *userdata, char *name, void *data)
--{
--	Hook *hook = g_new(Hook, 1);
--	hook->type = type;
--	hook->plugin = plugin;
--	Py_INCREF(callback);
--	hook->callback = callback;
--	Py_INCREF(userdata);
--	hook->userdata = userdata;
--	hook->name = g_strdup (name);
--	hook->data = NULL;
--	Plugin_SetHooks(plugin, g_slist_append(Plugin_GetHooks(plugin),
--					       hook));
--
--	return hook;
--}
--
--static Hook *
--Plugin_FindHook(PyObject *plugin, char *name)
--{
--	Hook *hook = NULL;
--	GSList *plugin_hooks = Plugin_GetHooks(plugin);
--	
--	while (plugin_hooks)
--	{
--		if (g_strcmp0 (((Hook *)plugin_hooks->data)->name, name) == 0)
--		{
--			hook = (Hook *)plugin_hooks->data;
--			break;
--		}
--		
--		plugin_hooks = g_slist_next(plugin_hooks);
--	}
--
--	return hook;
--}
--
--static void
--Plugin_RemoveHook(PyObject *plugin, Hook *hook)
--{
--	GSList *list;
--	/* Is this really a hook of the running plugin? */
--	list = g_slist_find(Plugin_GetHooks(plugin), hook);
--	if (list) {
--		/* Ok, unhook it. */
--		if (hook->type != HOOK_UNLOAD) {
--			/* This is an xchat hook. Unregister it. */
--			BEGIN_XCHAT_CALLS(NONE);
--			hexchat_unhook(ph, (hexchat_hook*)hook->data);
--			END_XCHAT_CALLS();
--		}
--		Plugin_SetHooks(plugin,
--				g_slist_remove(Plugin_GetHooks(plugin),
--					       hook));
--		Py_DECREF(hook->callback);
--		Py_DECREF(hook->userdata);
--		g_free(hook->name);
--		g_free(hook);
--	}
--}
--
--static void
--Plugin_RemoveAllHooks(PyObject *plugin)
--{
--	GSList *list = Plugin_GetHooks(plugin);
--	while (list) {
--		Hook *hook = (Hook *) list->data;
--		if (hook->type != HOOK_UNLOAD) {
--			/* This is an xchat hook. Unregister it. */
--			BEGIN_XCHAT_CALLS(NONE);
--			hexchat_unhook(ph, (hexchat_hook*)hook->data);
--			END_XCHAT_CALLS();
--		}
--		Py_DECREF(hook->callback);
--		Py_DECREF(hook->userdata);
--		g_free(hook->name);
--		g_free(hook);
--		list = list->next;
--	}
--	Plugin_SetHooks(plugin, NULL);
--}
--
--static void
--Plugin_Delete(PyObject *plugin)
--{
--	PyThreadState *tstate = ((PluginObject*)plugin)->tstate;
--	GSList *list = Plugin_GetHooks(plugin);
--	while (list) {
--		Hook *hook = (Hook *) list->data;
--		if (hook->type == HOOK_UNLOAD) {
--			PyObject *retobj;
--			retobj = PyObject_CallFunction(hook->callback, "(O)",
--						       hook->userdata);
--			if (retobj) {
--				Py_DECREF(retobj);
--			} else {
--				PyErr_Print();
--				PyErr_Clear();
--			}
--		}
--		list = list->next;
--	}
--	Plugin_RemoveAllHooks(plugin);
--	if (((PluginObject *)plugin)->gui != NULL)
--		hexchat_plugingui_remove(ph, ((PluginObject *)plugin)->gui);
--	Py_DECREF(plugin);
--	/*PyThreadState_Swap(tstate); needed? */
--	Py_EndInterpreter(tstate);
--}
--
--static PyObject *
--Plugin_New(char *filename, PyObject *xcoobj)
--{
--	PluginObject *plugin = NULL;
--	PyObject *m, *o;
--#ifdef IS_PY3K
--	wchar_t *argv[] = { L"<hexchat>", 0 };
--#else
--	char *argv[] = { "<hexchat>", 0 };
--#endif
--
--	if (filename) {
--		char *old_filename = filename;
--		filename = Util_Expand(filename);
--		if (filename == NULL) {
--			hexchat_printf(ph, "File not found: %s", old_filename);
--			return NULL;
--		}
--	}
--
--	/* Allocate plugin structure. */
--	plugin = PyObject_New(PluginObject, &Plugin_Type);
--	if (plugin == NULL) {
--		hexchat_print(ph, "Can't create plugin object");
--		goto error;
--	}
--
--	Plugin_SetName(plugin, NULL);
--	Plugin_SetVersion(plugin, NULL);
--	Plugin_SetFilename(plugin, NULL);
--	Plugin_SetDescription(plugin, NULL);
--	Plugin_SetHooks(plugin, NULL);
--	Plugin_SetContext(plugin, hexchat_get_context(ph));
--	Plugin_SetGui(plugin, NULL);
--
--	/* Start a new interpreter environment for this plugin. */
--	PyEval_AcquireThread(main_tstate);
--	plugin->tstate = Py_NewInterpreter();
--	if (plugin->tstate == NULL) {
--		hexchat_print(ph, "Can't create interpreter state");
--		goto error;
--	}
--
--	PySys_SetArgv(1, argv);
--	PySys_SetObject("__plugin__", (PyObject *) plugin);
--
--	/* Set stdout and stderr to xchatout. */
--	Py_INCREF(xcoobj);
--	PySys_SetObject("stdout", xcoobj);
--	Py_INCREF(xcoobj);
--	PySys_SetObject("stderr", xcoobj);
--
--	if (filename) {
--#ifdef WIN32
--		char *file;
--		if (!g_file_get_contents(filename, &file, NULL, NULL)) {
--			hexchat_printf(ph, "Can't open file %s: %s\n",
--				     filename, strerror(errno));
--			goto error;
--		}
--
--		if (PyRun_SimpleString(file) != 0) {
--			hexchat_printf(ph, "Error loading module %s\n",
--				     filename);
--			g_free (file);
--			goto error;
--		}
--
--		plugin->filename = filename;
--		filename = NULL;
--		g_free (file);
--#else
--		FILE *fp;
--		plugin->filename = filename;
--
--		/* It's now owned by the plugin. */
--		filename = NULL;
--
--		/* Open the plugin file. */
--		fp = fopen(plugin->filename, "r");
--		if (fp == NULL) {
--			hexchat_printf(ph, "Can't open file %s: %s\n",
--				     plugin->filename, strerror(errno));
--			goto error;
--		}
--
--		/* Run the plugin. */
--		if (PyRun_SimpleFile(fp, plugin->filename) != 0) {
--			hexchat_printf(ph, "Error loading module %s\n",
--				     plugin->filename);
--			fclose(fp);
--			goto error;
--		}
--		fclose(fp);
--#endif
--		m = PyDict_GetItemString(PyImport_GetModuleDict(),
--					 "__main__");
--		if (m == NULL) {
--			hexchat_print(ph, "Can't get __main__ module");
--			goto error;
--		}
--		GET_MODULE_DATA(name, 1);
--		GET_MODULE_DATA(version, 0);
--		GET_MODULE_DATA(description, 0);
--		plugin->gui = hexchat_plugingui_add(ph, plugin->filename,
--						  plugin->name,
--						  plugin->description,
--						  plugin->version, NULL);
--	}
--
--	PyEval_ReleaseThread(plugin->tstate);
--
--	return (PyObject *) plugin;
--
--error:
--	g_free(filename);
--
--	if (plugin) {
--		if (plugin->tstate)
--			Plugin_Delete((PyObject *)plugin);
--		else
--			Py_DECREF(plugin);
--	}
--	PyEval_ReleaseLock();
--
--	return NULL;
--}
--
--static void
--Plugin_dealloc(PluginObject *self)
--{
--	g_free(self->filename);
--	g_free(self->name);
--	g_free(self->version);
--	g_free(self->description);
--	Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyTypeObject Plugin_Type = {
--	PyVarObject_HEAD_INIT(NULL, 0)
--	"hexchat.Plugin",		/*tp_name*/
--	sizeof(PluginObject),	/*tp_basicsize*/
--	0,			/*tp_itemsize*/
--	(destructor)Plugin_dealloc, /*tp_dealloc*/
--	0,			/*tp_print*/
--	0,			/*tp_getattr*/
--	0,			/*tp_setattr*/
--	0,			/*tp_compare*/
--	0,			/*tp_repr*/
--	0,			/*tp_as_number*/
--	0,			/*tp_as_sequence*/
--	0,			/*tp_as_mapping*/
--	0,			/*tp_hash*/
--        0,                      /*tp_call*/
--        0,                      /*tp_str*/
--        PyObject_GenericGetAttr,/*tp_getattro*/
--        PyObject_GenericSetAttr,/*tp_setattro*/
--        0,                      /*tp_as_buffer*/
--        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
--        0,                      /*tp_doc*/
--        0,                      /*tp_traverse*/
--        0,                      /*tp_clear*/
--        0,                      /*tp_richcompare*/
--        0,                      /*tp_weaklistoffset*/
--        0,                      /*tp_iter*/
--        0,                      /*tp_iternext*/
--        0,                      /*tp_methods*/
--        0,                      /*tp_members*/
--        0,                      /*tp_getset*/
--        0,                      /*tp_base*/
--        0,                      /*tp_dict*/
--        0,                      /*tp_descr_get*/
--        0,                      /*tp_descr_set*/
--        0,                      /*tp_dictoffset*/
--        0,                      /*tp_init*/
--        PyType_GenericAlloc,    /*tp_alloc*/
--        PyType_GenericNew,      /*tp_new*/
--      	PyObject_Del,          /*tp_free*/
--        0,                      /*tp_is_gc*/
--};
--
--
--/* ===================================================================== */
--/* XChat module */
--
--static PyObject *
--Module_hexchat_command(PyObject *self, PyObject *args)
--{
--	char *text;
--	if (!PyArg_ParseTuple(args, "s:command", &text))
--		return NULL;
--	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
--	hexchat_command(ph, text);
--	END_XCHAT_CALLS();
--	Py_RETURN_NONE;
--}
--
--static PyObject *
--Module_xchat_prnt(PyObject *self, PyObject *args)
--{
--	char *text;
--	if (!PyArg_ParseTuple(args, "s:prnt", &text))
--		return NULL;
--	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
--	hexchat_print(ph, text);
--	END_XCHAT_CALLS();
--	Py_RETURN_NONE;
--}
--
--static PyObject *
--Module_hexchat_emit_print(PyObject *self, PyObject *args, PyObject *kwargs)
--{
--	char *argv[6];
--	char *name;
--	int res;
--	long time = 0;
--	hexchat_event_attrs *attrs;
--	char *kwlist[] = {"name", "arg1", "arg2", "arg3",
--					"arg4", "arg5", "arg6", 
--					"time", NULL};
--	memset(&argv, 0, sizeof(char*)*6);
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
--			      &argv[0], &argv[1], &argv[2],
--			      &argv[3], &argv[4], &argv[5],
--				  &time))
--		return NULL;
--	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
--	attrs = hexchat_event_attrs_create(ph);
--	attrs->server_time_utc = (time_t)time; 
--	
--	res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
--					 argv[3], argv[4], argv[5], NULL);
--
--	hexchat_event_attrs_free(ph, attrs);
--	END_XCHAT_CALLS();
--	return PyLong_FromLong(res);
--}
--
--static PyObject *
--Module_hexchat_get_info(PyObject *self, PyObject *args)
--{
--	const char *info;
--	char *name;
--	if (!PyArg_ParseTuple(args, "s:get_info", &name))
--		return NULL;
--	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
--	info = hexchat_get_info(ph, name);
--	END_XCHAT_CALLS();
--	if (info == NULL) {
--		Py_RETURN_NONE;
--	}
--	if (strcmp (name, "gtkwin_ptr") == 0 || strcmp (name, "win_ptr") == 0)
--		return PyUnicode_FromFormat("%p", info); /* format as pointer */
--	else
--		return PyUnicode_FromString(info);
--}
--
--static PyObject *
--Module_xchat_get_prefs(PyObject *self, PyObject *args)
--{
--	PyObject *res;
--	const char *info;
--	int integer;
--	char *name;
--	int type;
--	if (!PyArg_ParseTuple(args, "s:get_prefs", &name))
--		return NULL;
--	BEGIN_XCHAT_CALLS(NONE);
--	type = hexchat_get_prefs(ph, name, &info, &integer);
--	END_XCHAT_CALLS();
--	switch (type) {
--		case 0:
--			Py_INCREF(Py_None);
--			res = Py_None;
--			break;
--		case 1:
--			res = PyUnicode_FromString((char*)info);
--			break;
--		case 2:
--		case 3:
--			res = PyLong_FromLong(integer);
--			break;
--		default:
--			PyErr_Format(PyExc_RuntimeError,
--				     "unknown get_prefs type (%d), "
--				     "please report", type);
--			res = NULL;
--			break;
--	}
--	return res;
--}
--
--static PyObject *
--Module_hexchat_get_context(PyObject *self, PyObject *args)
--{
--	PyObject *plugin;
--	PyObject *ctxobj;
--	plugin = Plugin_GetCurrent();
--	if (plugin == NULL)
--		return NULL;
--	ctxobj = Context_FromContext(Plugin_GetContext(plugin));
--	if (ctxobj == NULL) {
--		Py_RETURN_NONE;
--	}
--	return ctxobj;
--}
--
--static PyObject *
--Module_hexchat_find_context(PyObject *self, PyObject *args, PyObject *kwargs)
--{
--	char *server = NULL;
--	char *channel = NULL;
--	PyObject *ctxobj;
--	char *kwlist[] = {"server", "channel", 0};
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|zz:find_context",
--					 kwlist, &server, &channel))
--		return NULL;
--	ctxobj = Context_FromServerAndChannel(server, channel);
--	if (ctxobj == NULL) {
--		Py_RETURN_NONE;
--	}
--	return ctxobj;
--}
--
--static PyObject *
--Module_hexchat_pluginpref_set(PyObject *self, PyObject *args)
--{
--	PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
--	hexchat_plugin *prefph = Plugin_GetHandle(plugin);
--	int result;
--	char *var;
--	PyObject *value;
--		
--	if (!PyArg_ParseTuple(args, "sO:set_pluginpref", &var, &value))
--		return NULL;
--	if (PyLong_Check(value)) {
--		int intvalue = PyLong_AsLong(value);
--		BEGIN_XCHAT_CALLS(NONE);
--		result = hexchat_pluginpref_set_int(prefph, var, intvalue);
--		END_XCHAT_CALLS();
--	}
--	else if (PyUnicode_Check(value)) {
--		char *charvalue = PyUnicode_AsUTF8(value);
--		BEGIN_XCHAT_CALLS(NONE);
--		result = hexchat_pluginpref_set_str(prefph, var, charvalue);
--		END_XCHAT_CALLS();
--	}
--	else
--		result = 0;
--	return PyBool_FromLong(result);
--}
--
--static PyObject *
--Module_hexchat_pluginpref_get(PyObject *self, PyObject *args)
--{
--	PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
--	hexchat_plugin *prefph = Plugin_GetHandle(plugin);
--	PyObject *ret;
--	char *var;
--	char retstr[512];
--	int retint;
--	int result;
--	if (!PyArg_ParseTuple(args, "s:get_pluginpref", &var))
--		return NULL;
--		
--	/* This will always return numbers as integers. */
--	BEGIN_XCHAT_CALLS(NONE);
--	result = hexchat_pluginpref_get_str(prefph, var, retstr);
--	END_XCHAT_CALLS();
--	if (result) {
--		if (strlen (retstr) <= 12) {
--			BEGIN_XCHAT_CALLS(NONE);
--			retint = hexchat_pluginpref_get_int(prefph, var);
--			END_XCHAT_CALLS();
--			if ((retint == -1) && (strcmp(retstr, "-1") != 0))
--				ret = PyUnicode_FromString(retstr);
--			else
--				ret = PyLong_FromLong(retint);
--		} else
--			ret = PyUnicode_FromString(retstr);
--	}
--	else
--	{
--		Py_INCREF(Py_None);
--		ret = Py_None;
--	}
--	return ret;
--}
--
--static PyObject *
--Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args)
--{
--	PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
--	hexchat_plugin *prefph = Plugin_GetHandle(plugin);
--	char *var;
--	int result;
--	if (!PyArg_ParseTuple(args, "s:del_pluginpref", &var))
--		return NULL;
--	BEGIN_XCHAT_CALLS(NONE);
--	result = hexchat_pluginpref_delete(prefph, var);
--	END_XCHAT_CALLS();
--	return PyBool_FromLong(result);
--}
--
--static PyObject *
--Module_hexchat_pluginpref_list(PyObject *self, PyObject *args)
--{
--	PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
--	hexchat_plugin *prefph = Plugin_GetHandle(plugin);
--	char list[4096];
--	char* token;
--	int result;
--	PyObject *pylist;
--	pylist = PyList_New(0);
--	BEGIN_XCHAT_CALLS(NONE);
--	result = hexchat_pluginpref_list(prefph, list);
--	END_XCHAT_CALLS();
--	if (result) {
--		token = strtok(list, ",");
--		while (token != NULL) {
--			PyList_Append(pylist, PyUnicode_FromString(token));
--			token = strtok (NULL, ",");
--		}
--	}
--	return pylist;
--}
--
--static PyObject *
--Module_hexchat_hook_command(PyObject *self, PyObject *args, PyObject *kwargs)
--{
--	char *name;
--	PyObject *callback;
--	PyObject *userdata = Py_None;
--	int priority = HEXCHAT_PRI_NORM;
--	char *help = NULL;
--	PyObject *plugin;
--	Hook *hook;
--	char *kwlist[] = {"name", "callback", "userdata",
--			  "priority", "help", 0};
--
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oiz:hook_command",
--					 kwlist, &name, &callback, &userdata,
--					 &priority, &help))
--		return NULL;
--
--	plugin = Plugin_GetCurrent();
--	if (plugin == NULL)
--		return NULL;
--	if (!PyCallable_Check(callback)) {
--		PyErr_SetString(PyExc_TypeError, "callback is not callable");
--		return NULL;
--	}
--
--	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
--	if (hook == NULL)
--		return NULL;
--
--	BEGIN_XCHAT_CALLS(NONE);
--	hook->data = (void*)hexchat_hook_command(ph, name, priority,
--					       Callback_Command, help, hook);
--	END_XCHAT_CALLS();
--
--	return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_server(PyObject *self, PyObject *args, PyObject *kwargs)
--{
--	char *name;
--	PyObject *callback;
--	PyObject *userdata = Py_None;
--	int priority = HEXCHAT_PRI_NORM;
--	PyObject *plugin;
--	Hook *hook;
--	char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
--					 kwlist, &name, &callback, &userdata,
--					 &priority))
--		return NULL;
--
--	plugin = Plugin_GetCurrent();
--	if (plugin == NULL)
--		return NULL;
--	if (!PyCallable_Check(callback)) {
--		PyErr_SetString(PyExc_TypeError, "callback is not callable");
--		return NULL;
--	}
--
--	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
--	if (hook == NULL)
--		return NULL;
--
--	BEGIN_XCHAT_CALLS(NONE);
--	hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
--					      Callback_Server, hook);
--	END_XCHAT_CALLS();
--
--	return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_server_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
--{
--	char *name;
--	PyObject *callback;
--	PyObject *userdata = Py_None;
--	int priority = HEXCHAT_PRI_NORM;
--	PyObject *plugin;
--	Hook *hook;
--	char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
--					 kwlist, &name, &callback, &userdata,
--					 &priority))
--		return NULL;
--
--	plugin = Plugin_GetCurrent();
--	if (plugin == NULL)
--		return NULL;
--	if (!PyCallable_Check(callback)) {
--		PyErr_SetString(PyExc_TypeError, "callback is not callable");
--		return NULL;
--	}
--
--	hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, NULL, NULL);
--	if (hook == NULL)
--		return NULL;
--
--	BEGIN_XCHAT_CALLS(NONE);
--	hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
--					      Callback_Server, hook);
--	END_XCHAT_CALLS();
--
--	return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_print(PyObject *self, PyObject *args, PyObject *kwargs)
--{
--	char *name;
--	PyObject *callback;
--	PyObject *userdata = Py_None;
--	int priority = HEXCHAT_PRI_NORM;
--	PyObject *plugin;
--	Hook *hook;
--	char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print",
--					 kwlist, &name, &callback, &userdata,
--					 &priority))
--		return NULL;
--
--	plugin = Plugin_GetCurrent();
--	if (plugin == NULL)
--		return NULL;
--	if (!PyCallable_Check(callback)) {
--		PyErr_SetString(PyExc_TypeError, "callback is not callable");
--		return NULL;
--	}
--
--	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
--	if (hook == NULL)
--		return NULL;
--
--	BEGIN_XCHAT_CALLS(NONE);
--	hook->data = (void*)hexchat_hook_print(ph, name, priority,
--					     Callback_Print, hook);
--	END_XCHAT_CALLS();
--
--	return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_print_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
--{
--	char *name;
--	PyObject *callback;
--	PyObject *userdata = Py_None;
--	int priority = HEXCHAT_PRI_NORM;
--	PyObject *plugin;
--	Hook *hook;
--	char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print_attrs",
--					 kwlist, &name, &callback, &userdata,
--					 &priority))
--		return NULL;
--
--	plugin = Plugin_GetCurrent();
--	if (plugin == NULL)
--		return NULL;
--	if (!PyCallable_Check(callback)) {
--		PyErr_SetString(PyExc_TypeError, "callback is not callable");
--		return NULL;
--	}
--
--	hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, name, NULL);
--	if (hook == NULL)
--		return NULL;
--
--	BEGIN_XCHAT_CALLS(NONE);
--	hook->data = (void*)hexchat_hook_print_attrs(ph, name, priority,
--					     Callback_Print_Attrs, hook);
--	END_XCHAT_CALLS();
--
--	return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_timer(PyObject *self, PyObject *args, PyObject *kwargs)
--{
--	int timeout;
--	PyObject *callback;
--	PyObject *userdata = Py_None;
--	PyObject *plugin;
--	Hook *hook;
--	char *kwlist[] = {"timeout", "callback", "userdata", 0};
--
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO|O:hook_timer",
--					 kwlist, &timeout, &callback,
--					 &userdata))
--		return NULL;
--
--	plugin = Plugin_GetCurrent();
--	if (plugin == NULL)
--		return NULL;
--	if (!PyCallable_Check(callback)) {
--		PyErr_SetString(PyExc_TypeError, "callback is not callable");
--		return NULL;
--	}
--
--	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
--	if (hook == NULL)
--		return NULL;
--
--	BEGIN_XCHAT_CALLS(NONE);
--	hook->data = (void*)hexchat_hook_timer(ph, timeout,
--					     Callback_Timer, hook);
--	END_XCHAT_CALLS();
--
--	return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_unload(PyObject *self, PyObject *args, PyObject *kwargs)
--{
--	PyObject *callback;
--	PyObject *userdata = Py_None;
--	PyObject *plugin;
--	Hook *hook;
--	char *kwlist[] = {"callback", "userdata", 0};
--
--	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:hook_unload",
--					 kwlist, &callback, &userdata))
--		return NULL;
--
--	plugin = Plugin_GetCurrent();
--	if (plugin == NULL)
--		return NULL;
--	if (!PyCallable_Check(callback)) {
--		PyErr_SetString(PyExc_TypeError, "callback is not callable");
--		return NULL;
--	}
--
--	hook = Plugin_AddHook(HOOK_UNLOAD, plugin, callback, userdata, NULL, NULL);
--	if (hook == NULL)
--		return NULL;
--
--	return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_unhook(PyObject *self, PyObject *args)
--{
--	PyObject *plugin;
--	PyObject *obj;
--	Hook *hook;
--	if (!PyArg_ParseTuple(args, "O:unhook", &obj))
--		return NULL;
--	plugin = Plugin_GetCurrent();
--	if (plugin == NULL)
--		return NULL;
--
--	if (PyUnicode_Check (obj))
--	{
--		hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
--		while (hook)
--		{
--			Plugin_RemoveHook(plugin, hook);
--			hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
--		}
--	}
--	else
--	{
--		hook = (Hook *)PyLong_AsVoidPtr(obj);
--		Plugin_RemoveHook(plugin, hook);
--	}	
--
--	Py_RETURN_NONE;
--}
--
--static PyObject *
--Module_xchat_get_list(PyObject *self, PyObject *args)
--{
--	hexchat_list *list;
--	PyObject *l;
--	const char *name;
--	const char *const *fields;
--	int i;
--
--	if (!PyArg_ParseTuple(args, "s:get_list", &name))
--		return NULL;
--	/* This function is thread safe, and returns statically
--	 * allocated data. */
--	fields = hexchat_list_fields(ph, "lists");
--	for (i = 0; fields[i]; i++) {
--		if (strcmp(fields[i], name) == 0) {
--			/* Use the static allocated one. */
--			name = fields[i];
--			break;
--		}
--	}
--	if (fields[i] == NULL) {
--		PyErr_SetString(PyExc_KeyError, "list not available");
--		return NULL;
--	}
--	l = PyList_New(0);
--	if (l == NULL)
--		return NULL;
--	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
--	list = hexchat_list_get(ph, (char*)name);
--	if (list == NULL)
--		goto error;
--	fields = hexchat_list_fields(ph, (char*)name);
--	while (hexchat_list_next(ph, list)) {
--		PyObject *o = ListItem_New(name);
--		if (o == NULL || PyList_Append(l, o) == -1) {
--			Py_XDECREF(o);
--			goto error;
--		}
--		Py_DECREF(o); /* l is holding a reference */
--		for (i = 0; fields[i]; i++) {
--			const char *fld = fields[i]+1;
--			PyObject *attr = NULL;
--			const char *sattr;
--			int iattr;
--			time_t tattr;
--			switch(fields[i][0]) {
--			case 's':
--				sattr = hexchat_list_str(ph, list, (char*)fld);
--				attr = PyUnicode_FromString(sattr?sattr:"");
--				break;
--			case 'i':
--				iattr = hexchat_list_int(ph, list, (char*)fld);
--				attr = PyLong_FromLong((long)iattr);
--				break;
--			case 't':
--				tattr = hexchat_list_time(ph, list, (char*)fld);
--				attr = PyLong_FromLong((long)tattr);
--				break;
--			case 'p':
--				sattr = hexchat_list_str(ph, list, (char*)fld);
--				if (strcmp(fld, "context") == 0) {
--					attr = Context_FromContext(
--						(hexchat_context*)sattr);
--					break;
--				}
--			default: /* ignore unknown (newly added?) types */
--				continue;
--			}
--			if (attr == NULL)
--				goto error;
--			PyObject_SetAttrString(o, (char*)fld, attr); /* add reference on attr in o */
--			Py_DECREF(attr); /* make o own attr */
--		}
--	}
--	hexchat_list_free(ph, list);
--	goto exit;
--error:
--	if (list)
--		hexchat_list_free(ph, list);
--	Py_DECREF(l);
--	l = NULL;
--
--exit:
--	END_XCHAT_CALLS();
--	return l;
--}
--
--static PyObject *
--Module_xchat_get_lists(PyObject *self, PyObject *args)
--{
--	PyObject *l, *o;
--	const char *const *fields;
--	int i;
--	/* This function is thread safe, and returns statically
--	 * allocated data. */
--	fields = hexchat_list_fields(ph, "lists");
--	l = PyList_New(0);
--	if (l == NULL)
--		return NULL;
--	for (i = 0; fields[i]; i++) {
--		o = PyUnicode_FromString(fields[i]);
--		if (o == NULL || PyList_Append(l, o) == -1) {
--			Py_DECREF(l);
--			Py_XDECREF(o);
--			return NULL;
--		}
--		Py_DECREF(o); /* l is holding a reference */
--	}
--	return l;
--}
--
--static PyObject *
--Module_hexchat_nickcmp(PyObject *self, PyObject *args)
--{
--	char *s1, *s2;
--	if (!PyArg_ParseTuple(args, "ss:nickcmp", &s1, &s2))
--		return NULL;
--	return PyLong_FromLong((long) hexchat_nickcmp(ph, s1, s2));
--}
--
--static PyObject *
--Module_hexchat_strip(PyObject *self, PyObject *args)
--{
--	PyObject *result;
--	char *str, *str2;
--	int len = -1, flags = 1 | 2;
--	if (!PyArg_ParseTuple(args, "s|ii:strip", &str, &len, &flags))
--		return NULL;
--	str2 = hexchat_strip(ph, str, len, flags);
--	result = PyUnicode_FromString(str2);
--	hexchat_free(ph, str2);
--	return result;
--}
--
--static PyMethodDef Module_xchat_methods[] = {
--	{"command",		Module_hexchat_command,
--		METH_VARARGS},
--	{"prnt",		Module_xchat_prnt,
--		METH_VARARGS},
--	{"emit_print",		(PyCFunction)Module_hexchat_emit_print,
--		METH_VARARGS|METH_KEYWORDS},
--	{"get_info",		Module_hexchat_get_info,
--		METH_VARARGS},
--	{"get_prefs",		Module_xchat_get_prefs,
--		METH_VARARGS},
--	{"get_context",		Module_hexchat_get_context,
--		METH_NOARGS},
--	{"find_context",	(PyCFunction)Module_hexchat_find_context,
--		METH_VARARGS|METH_KEYWORDS},
--	{"set_pluginpref", Module_hexchat_pluginpref_set,
--		METH_VARARGS},
--	{"get_pluginpref", Module_hexchat_pluginpref_get,
--		METH_VARARGS},
--	{"del_pluginpref", Module_hexchat_pluginpref_delete,
--		METH_VARARGS},
--	{"list_pluginpref", Module_hexchat_pluginpref_list,
--		METH_VARARGS},
--	{"hook_command",	(PyCFunction)Module_hexchat_hook_command,
--		METH_VARARGS|METH_KEYWORDS},
--	{"hook_server",		(PyCFunction)Module_hexchat_hook_server,
--		METH_VARARGS|METH_KEYWORDS},
--	{"hook_server_attrs",		(PyCFunction)Module_hexchat_hook_server_attrs,
--		METH_VARARGS|METH_KEYWORDS},
--	{"hook_print",		(PyCFunction)Module_hexchat_hook_print,
--		METH_VARARGS|METH_KEYWORDS},
--	{"hook_print_attrs",		(PyCFunction)Module_hexchat_hook_print_attrs,
--		METH_VARARGS|METH_KEYWORDS},
--	{"hook_timer",		(PyCFunction)Module_hexchat_hook_timer,
--		METH_VARARGS|METH_KEYWORDS},
--	{"hook_unload",		(PyCFunction)Module_hexchat_hook_unload,
--		METH_VARARGS|METH_KEYWORDS},
--	{"unhook",		Module_hexchat_unhook,
--		METH_VARARGS},
--	{"get_list",		Module_xchat_get_list,
--		METH_VARARGS},
--	{"get_lists",		Module_xchat_get_lists,
--		METH_NOARGS},
--	{"nickcmp",		Module_hexchat_nickcmp,
--		METH_VARARGS},
--	{"strip",		Module_hexchat_strip,
--		METH_VARARGS},
--	{NULL, NULL}
--};
--
--#ifdef IS_PY3K
--static struct PyModuleDef moduledef = {
--	PyModuleDef_HEAD_INIT,
--	"hexchat",     /* m_name */
--	"HexChat Scripting Interface",  /* m_doc */
--	-1,                  /* m_size */
--	Module_xchat_methods,    /* m_methods */
--	NULL,                /* m_reload */
--	NULL,                /* m_traverse */
--	NULL,                /* m_clear */
--	NULL,                /* m_free */
--};
--
--static struct PyModuleDef xchat_moduledef = {
--	PyModuleDef_HEAD_INIT,
--	"xchat",     /* m_name */
--	"HexChat Scripting Interface",  /* m_doc */
--	-1,                  /* m_size */
--	Module_xchat_methods,    /* m_methods */
--	NULL,                /* m_reload */
--	NULL,                /* m_traverse */
--	NULL,                /* m_clear */
--	NULL,                /* m_free */
--};
--#endif
--
--static PyObject *
--moduleinit_hexchat(void)
--{
--	PyObject *hm;
--#ifdef IS_PY3K
--		hm = PyModule_Create(&moduledef);
--#else
--    hm = Py_InitModule3("hexchat", Module_xchat_methods, "HexChat Scripting Interface");
--#endif
--
--	PyModule_AddIntConstant(hm, "EAT_NONE", HEXCHAT_EAT_NONE);
--	PyModule_AddIntConstant(hm, "EAT_HEXCHAT", HEXCHAT_EAT_HEXCHAT);
--	PyModule_AddIntConstant(hm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT); /* for compat */
--	PyModule_AddIntConstant(hm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
--	PyModule_AddIntConstant(hm, "EAT_ALL", HEXCHAT_EAT_ALL);
--	PyModule_AddIntConstant(hm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
--	PyModule_AddIntConstant(hm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
--	PyModule_AddIntConstant(hm, "PRI_NORM", HEXCHAT_PRI_NORM);
--	PyModule_AddIntConstant(hm, "PRI_LOW", HEXCHAT_PRI_LOW);
--	PyModule_AddIntConstant(hm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
--
--	PyObject_SetAttrString(hm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
--
--	return hm;
--}
--
--static PyObject *
--moduleinit_xchat(void)
--{
--	PyObject *xm;
--#ifdef IS_PY3K
--		xm = PyModule_Create(&xchat_moduledef);
--#else
--    xm = Py_InitModule3("xchat", Module_xchat_methods, "HexChat Scripting Interface");
--#endif
--
--	PyModule_AddIntConstant(xm, "EAT_NONE", HEXCHAT_EAT_NONE);
--	PyModule_AddIntConstant(xm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT);
--	PyModule_AddIntConstant(xm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
--	PyModule_AddIntConstant(xm, "EAT_ALL", HEXCHAT_EAT_ALL);
--	PyModule_AddIntConstant(xm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
--	PyModule_AddIntConstant(xm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
--	PyModule_AddIntConstant(xm, "PRI_NORM", HEXCHAT_PRI_NORM);
--	PyModule_AddIntConstant(xm, "PRI_LOW", HEXCHAT_PRI_LOW);
--	PyModule_AddIntConstant(xm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
--
--	PyObject_SetAttrString(xm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
--
--	return xm;
--}
--
--#ifdef IS_PY3K
--PyMODINIT_FUNC
--PyInit_hexchat(void)
--{
--    return moduleinit_hexchat();
--}
--PyMODINIT_FUNC
--PyInit_xchat(void)
--{
--    return moduleinit_xchat();
--}
--#else
--PyMODINIT_FUNC
--inithexchat(void)
--{
--		moduleinit_hexchat();
--}
--PyMODINIT_FUNC
--initxchat(void)
--{
--		moduleinit_xchat();
--}
--#endif
--
--/* ===================================================================== */
--/* Python interactive interpreter functions */
--
--static void
--IInterp_Exec(char *command)
--{
--	PyObject *m, *d, *o;
--	char *buffer;
--	int len;
--
--	BEGIN_PLUGIN(interp_plugin);
--
--	m = PyImport_AddModule("__main__");
--	if (m == NULL) {
--		hexchat_print(ph, "Can't get __main__ module");
--		goto fail;
--	}
--	d = PyModule_GetDict(m);
--	len = strlen(command);
--
--	buffer = g_malloc(len + 2);
--	memcpy(buffer, command, len);
--	buffer[len] = '\n';
--	buffer[len+1] = 0;
--	PyRun_SimpleString("import hexchat");
--	o = PyRun_StringFlags(buffer, Py_single_input, d, d, NULL);
--	g_free(buffer);
--	if (o == NULL) {
--		PyErr_Print();
--		goto fail;
--	}
--	Py_DECREF(o);
--
--fail:
--	END_PLUGIN(interp_plugin);
--	return;
--}
--
--static int
--IInterp_Cmd(char *word[], char *word_eol[], void *userdata)
--{
--	char *channel = (char *) hexchat_get_info(ph, "channel");
--	if (channel && channel[0] == '>' && strcmp(channel, ">>python<<") == 0) {
--		hexchat_printf(ph, ">>> %s\n", word_eol[1]);
--		IInterp_Exec(word_eol[1]);
--		return HEXCHAT_EAT_HEXCHAT;
--	}
--	return HEXCHAT_EAT_NONE;
--}
--
--
--/* ===================================================================== */
--/* Python command handling */
--
--static void
--Command_PyList(void)
--{
--	GSList *list;
--	list = plugin_list;
--	if (list == NULL) {
--		hexchat_print(ph, "No python modules loaded");
--	} else {
--		hexchat_print(ph,
--		   "Name         Version  Filename             Description\n"
--		   "----         -------  --------             -----------\n");
--		while (list != NULL) {
--			PluginObject *plg = (PluginObject *) list->data;
--			char *basename = g_path_get_basename(plg->filename);
--			hexchat_printf(ph, "%-12s %-8s %-20s %-10s\n",
--				     plg->name,
--				     *plg->version ? plg->version
--				     		  : "<none>",
--				     basename,
--				     *plg->description ? plg->description
--				     		      : "<none>");
--			g_free(basename);
--			list = list->next;
--		}
--		hexchat_print(ph, "\n");
--	}
--}
--
--static void
--Command_PyLoad(char *filename)
--{
--	PyObject *plugin;
--	RELEASE_XCHAT_LOCK();
--	plugin = Plugin_New(filename, xchatout);
--	ACQUIRE_XCHAT_LOCK();
--	if (plugin)
--		plugin_list = g_slist_append(plugin_list, plugin);
--}
--
--static void
--Command_PyUnload(char *name)
--{
--	PluginObject *plugin = Plugin_ByString(name);
--	if (!plugin) {
--		hexchat_print(ph, "Can't find a python plugin with that name");
--	} else {
--		BEGIN_PLUGIN(plugin);
--		Plugin_Delete((PyObject*)plugin);
--		END_PLUGIN(plugin);
--		plugin_list = g_slist_remove(plugin_list, plugin);
--	}
--}
--
--static void
--Command_PyReload(char *name)
--{
--	PluginObject *plugin = Plugin_ByString(name);
--	if (!plugin) {
--		hexchat_print(ph, "Can't find a python plugin with that name");
--	} else {
--		char *filename = g_strdup(plugin->filename);
--		Command_PyUnload(filename);
--		Command_PyLoad(filename);
--		g_free(filename);
--	}
--}
--
--static void
--Command_PyAbout(void)
--{
--	hexchat_print(ph, about);
--}
--
--static int
--Command_Py(char *word[], char *word_eol[], void *userdata)
--{
--	char *cmd = word[2];
--	int ok = 0;
--	if (strcasecmp(cmd, "LIST") == 0) {
--		ok = 1;
--		Command_PyList();
--	} else if (strcasecmp(cmd, "EXEC") == 0) {
--		if (word[3][0]) {
--			ok = 1;
--			IInterp_Exec(word_eol[3]);
--		}
--	} else if (strcasecmp(cmd, "LOAD") == 0) {
--		if (word[3][0]) {
--			ok = 1;
--			Command_PyLoad(word[3]);
--		}
--	} else if (strcasecmp(cmd, "UNLOAD") == 0) {
--		if (word[3][0]) {
--			ok = 1;
--			Command_PyUnload(word[3]);
--		}
--	} else if (strcasecmp(cmd, "RELOAD") == 0) {
--		if (word[3][0]) {
--			ok = 1;
--			Command_PyReload(word[3]);
--		}
--	} else if (strcasecmp(cmd, "CONSOLE") == 0) {
--		ok = 1;
--		hexchat_command(ph, "QUERY >>python<<");
--	} else if (strcasecmp(cmd, "ABOUT") == 0) {
--		ok = 1;
--		Command_PyAbout();
--	}
--	if (!ok)
--		hexchat_print(ph, usage);
--	return HEXCHAT_EAT_ALL;
--}
--
--static int
--Command_Load(char *word[], char *word_eol[], void *userdata)
--{
--	int len = strlen(word[2]);
--	if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
--		Command_PyLoad(word[2]);
--		return HEXCHAT_EAT_HEXCHAT;
--	}
--	return HEXCHAT_EAT_NONE;
--}
--
--static int
--Command_Reload(char *word[], char *word_eol[], void *userdata)
--{
--	int len = strlen(word[2]);
--	if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
--	Command_PyReload(word[2]);
--	return HEXCHAT_EAT_HEXCHAT;
--	}
--	return HEXCHAT_EAT_NONE;
--}
--
--static int
--Command_Unload(char *word[], char *word_eol[], void *userdata)
--{
--	int len = strlen(word[2]);
--	if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
--		Command_PyUnload(word[2]);
--		return HEXCHAT_EAT_HEXCHAT;
--	}
--	return HEXCHAT_EAT_NONE;
--}
--
--/* ===================================================================== */
--/* Autoload function */
--
--/* ===================================================================== */
--/* (De)initialization functions */
--
--static int initialized = 0;
--static int reinit_tried = 0;
--
--void
--hexchat_plugin_get_info(char **name, char **desc, char **version, void **reserved)
--{
--	*name = "Python";
--	*version = VERSION;
--	*desc = "Python scripting interface";
--   if (reserved)
--      *reserved = NULL;
--}
--
--int
--hexchat_plugin_init(hexchat_plugin *plugin_handle,
--		  char **plugin_name,
--		  char **plugin_desc,
--		  char **plugin_version,
--		  char *arg)
--{
--#ifdef IS_PY3K
--	wchar_t *argv[] = { L"<hexchat>", 0 };
--#else
--	char *argv[] = { "<hexchat>", 0 };
--#endif
--
--	ph = plugin_handle;
--
--	/* Block double initalization. */
--	if (initialized != 0) {
--		hexchat_print(ph, "Python interface already loaded");
--		/* deinit is called even when init fails, so keep track
--		 * of a reinit failure. */
--		reinit_tried++;
--		return 0;
--	}
--	initialized = 1;
--
--	*plugin_name = "Python";
--	*plugin_version = VERSION;
--
--	/* FIXME You can't free this since it's used as long as the plugin's
--	 * loaded, but if you unload it, everything belonging to the plugin is
--	 * supposed to be freed anyway.
--	 */
--	*plugin_desc = g_strdup_printf ("Python %d scripting interface", PY_MAJOR_VERSION);
--
--	/* Initialize python. */
--#ifdef IS_PY3K
--	Py_SetProgramName(L"hexchat");
--	PyImport_AppendInittab("hexchat", PyInit_hexchat);
--	PyImport_AppendInittab("xchat", PyInit_xchat);
--#else
--	Py_SetProgramName("hexchat");
--	PyImport_AppendInittab("hexchat", inithexchat);
--	PyImport_AppendInittab("xchat", initxchat);
--#endif
--	Py_Initialize();
--	PySys_SetArgv(1, argv);
--
--	xchatout_buffer = g_string_new (NULL);
--	xchatout = XChatOut_New();
--	if (xchatout == NULL) {
--		hexchat_print(ph, "Can't allocate xchatout object");
--		return 0;
--	}
--
--#ifdef WITH_THREAD
--	PyEval_InitThreads();
--	xchat_lock = PyThread_allocate_lock();
--	if (xchat_lock == NULL) {
--		hexchat_print(ph, "Can't allocate hexchat lock");
--		Py_DECREF(xchatout);
--		xchatout = NULL;
--		return 0;
--	}
--#endif
--
--	main_tstate = PyEval_SaveThread();
--
--	interp_plugin = Plugin_New(NULL, xchatout);
--	if (interp_plugin == NULL) {
--		hexchat_print(ph, "Plugin_New() failed.\n");
--#ifdef WITH_THREAD
--		PyThread_free_lock(xchat_lock);
--#endif
--		Py_DECREF(xchatout);
--		xchatout = NULL;
--		return 0;
--	}
--
--
--	hexchat_hook_command(ph, "", HEXCHAT_PRI_NORM, IInterp_Cmd, 0, 0);
--	hexchat_hook_command(ph, "PY", HEXCHAT_PRI_NORM, Command_Py, usage, 0);
--	hexchat_hook_command(ph, "LOAD", HEXCHAT_PRI_NORM, Command_Load, 0, 0);
--	hexchat_hook_command(ph, "UNLOAD", HEXCHAT_PRI_NORM, Command_Unload, 0, 0);
--	hexchat_hook_command(ph, "RELOAD", HEXCHAT_PRI_NORM, Command_Reload, 0, 0);
--#ifdef WITH_THREAD
--	thread_timer = hexchat_hook_timer(ph, 300, Callback_ThreadTimer, NULL);
--#endif
--
--	hexchat_print(ph, "Python interface loaded\n");
--
--	Util_Autoload();
--	return 1;
--}
--
--int
--hexchat_plugin_deinit(void)
--{
--	GSList *list;
--
--	/* A reinitialization was tried. Just give up and live the
--	 * environment as is. We are still alive. */
--	if (reinit_tried) {
--		reinit_tried--;
--		return 1;
--	}
--
--	list = plugin_list;
--	while (list != NULL) {
--		PyObject *plugin = (PyObject *) list->data;
--		BEGIN_PLUGIN(plugin);
--		Plugin_Delete(plugin);
--		END_PLUGIN(plugin);
--		list = list->next;
--	}
--	g_slist_free(plugin_list);
--	plugin_list = NULL;
--
--	/* Reset xchatout buffer. */
--	g_string_free (xchatout_buffer, TRUE);
--	xchatout_buffer = NULL;
--
--	if (interp_plugin) {
--		Py_DECREF(interp_plugin);
--		interp_plugin = NULL;
--	}
--
--	/* Switch back to the main thread state. */
--	if (main_tstate) {
--		PyEval_RestoreThread(main_tstate);
--		PyThreadState_Swap(main_tstate);
--		main_tstate = NULL;
--	}
--	Py_Finalize();
--
--#ifdef WITH_THREAD
--	if (thread_timer != NULL) {
--		hexchat_unhook(ph, thread_timer);
--		thread_timer = NULL;
--	}
--	PyThread_free_lock(xchat_lock);
--#endif
--
--	hexchat_print(ph, "Python interface unloaded\n");
--	initialized = 0;
--
--	return 1;
--}
--
-diff --git a/plugins/python/python.def b/plugins/python/python.def
-index 6ce04e98..e560f50f 100644
---- a/plugins/python/python.def
-+++ b/plugins/python/python.def
-@@ -1,4 +1,3 @@
- EXPORTS 
- hexchat_plugin_init 
- hexchat_plugin_deinit 
--hexchat_plugin_get_info 
-diff --git a/plugins/python/python.py b/plugins/python/python.py
-new file mode 100644
-index 00000000..3845a79f
---- /dev/null
-+++ b/plugins/python/python.py
-@@ -0,0 +1,497 @@
-+from __future__ import print_function
-+
-+import os
-+import sys
-+from contextlib import contextmanager
-+import importlib
-+import signal
-+import site
-+import traceback
-+import weakref
-+from _hexchat_embedded import ffi, lib
-+
-+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_VERSION = ffi.new('char[]', VERSION)
-+hexchat = None
-+local_interp = None
-+hexchat_stdout = None
-+plugins = set()
-+
-+
-+ at contextmanager
-+def redirected_stdout():
-+    sys.stdout = sys.__stdout__
-+    sys.stderr = sys.__stderr__
-+    yield
-+    sys.stdout = hexchat_stdout
-+    sys.stderr = hexchat_stdout
-+
-+
-+if os.environ.get('HEXCHAT_LOG_PYTHON'):
-+    def log(*args):
-+        with redirected_stdout():
-+            print(*args)
-+else:
-+    def log(*args):
-+        pass
-+
-+
-+class Stdout:
-+    def __init__(self):
-+        self.buffer = bytearray()
-+
-+    def write(self, string):
-+        string = string.encode()
-+        idx = string.rfind(b'\n')
-+        if idx is not -1:
-+            self.buffer += string[:idx]
-+            lib.hexchat_print(lib.ph, bytes(self.buffer))
-+            self.buffer = bytearray(string[idx + 1:])
-+        else:
-+            self.buffer += string
-+
-+    def isatty(self):
-+        # FIXME: help() locks app despite this?
-+        return False
-+
-+
-+class Attribute:
-+    def __init__(self):
-+        self.time = 0
-+
-+    def __repr__(self):
-+        return '<Attribute object at {}>'.format(id(self))
-+
-+
-+class Hook:
-+    def __init__(self, plugin, callback, userdata, is_unload):
-+        self.is_unload = is_unload
-+        self.plugin = weakref.proxy(plugin)
-+        self.callback = callback
-+        self.userdata = userdata
-+        self.hexchat_hook = None
-+        self.handle = ffi.new_handle(weakref.proxy(self))
-+
-+    def __del__(self):
-+        log('Removing hook', id(self))
-+        if self.is_unload is False:
-+            assert self.hexchat_hook is not None
-+            lib.hexchat_unhook(lib.ph, self.hexchat_hook)
-+
-+
-+if sys.version_info[0] is 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
-+            return compile(string, '<string>', 'exec', dont_inherit=True)
-+else:
-+    def compile_file(data, filename):
-+        return compile(data, filename, 'exec', optimize=2, dont_inherit=True)
-+
-+    def compile_line(string):
-+        return compile(string, '<string>', 'eval', optimize=2, dont_inherit=True)
-+
-+
-+class Plugin:
-+    def __init__(self):
-+        self.ph = None
-+        self.name = ''
-+        self.filename = ''
-+        self.version = ''
-+        self.description = ''
-+        self.hooks = set()
-+        self.globals = {
-+            '__plugin': weakref.proxy(self),
-+            '__name__': '__main__',
-+        }
-+
-+    def add_hook(self, callback, userdata, is_unload=False):
-+        hook = Hook(self, callback, userdata, is_unload=is_unload)
-+        self.hooks.add(hook)
-+        return hook
-+
-+    def remove_hook(self, hook):
-+        for h in self.hooks:
-+            if id(h) == hook:
-+                ud = hook.userdata
-+                self.hooks.remove(h)
-+                return ud
-+        else:
-+            log('Hook not found')
-+
-+    def loadfile(self, filename):
-+        try:
-+            self.filename = filename
-+            with open(filename) as f:
-+                data = f.read()
-+            compiled = compile_file(data, filename)
-+            exec(compiled, self.globals)
-+
-+            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)
-+        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):
-+        log('unloading', self.filename)
-+        for hook in self.hooks:
-+            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:
-+    def __decode(string):
-+        return string
-+else:
-+    def __decode(string):
-+        return string.decode()
-+
-+
-+# There can be empty entries between non-empty ones so find the actual last value
-+def wordlist_len(words):
-+    for i in range(31, 1, -1):
-+        if ffi.string(words[i]):
-+            return i
-+    return 0
-+
-+
-+def create_wordlist(words):
-+    size = wordlist_len(words)
-+    return [__decode(ffi.string(words[i])) for i in range(1, size + 1)]
-+
-+
-+# This function only exists for compat reasons with the C plugin
-+# It turns the word list from print hooks into a word_eol list
-+# 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)
-+
-+
-+ at ffi.def_extern()
-+def _on_command_hook(word, word_eol, userdata):
-+    hook = ffi.from_handle(userdata)
-+    word = create_wordlist(word)
-+    word_eol = create_wordlist(word_eol)
-+    return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
-+
-+
-+ at ffi.def_extern()
-+def _on_print_hook(word, userdata):
-+    hook = ffi.from_handle(userdata)
-+    word = create_wordlist(word)
-+    word_eol = create_wordeollist(word)
-+    return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
-+
-+
-+ at ffi.def_extern()
-+def _on_print_attrs_hook(word, attrs, userdata):
-+    hook = ffi.from_handle(userdata)
-+    word = create_wordlist(word)
-+    word_eol = create_wordeollist(word)
-+    attr = Attribute()
-+    attr.time = attrs.server_time_utc
-+    return to_cb_ret(hook.callback(word, word_eol, hook.userdata, attr))
-+
-+
-+ at ffi.def_extern()
-+def _on_server_hook(word, word_eol, userdata):
-+    hook = ffi.from_handle(userdata)
-+    word = create_wordlist(word)
-+    word_eol = create_wordlist(word_eol)
-+    return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
-+
-+
-+ at ffi.def_extern()
-+def _on_server_attrs_hook(word, word_eol, attrs, userdata):
-+    hook = ffi.from_handle(userdata)
-+    word = create_wordlist(word)
-+    word_eol = create_wordlist(word_eol)
-+    attr = Attribute()
-+    attr.time = attrs.server_time_utc
-+    return to_cb_ret(hook.callback(word, word_eol, hook.userdata, attr))
-+
-+
-+ at ffi.def_extern()
-+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
-+
-+
-+ at ffi.def_extern(error=3)
-+def _on_say_command(word, word_eol, userdata):
-+    channel = ffi.string(lib.hexchat_get_info(lib.ph, b'channel'))
-+    if channel == b'>>python<<':
-+        python = ffi.string(word_eol[1])
-+        lib.hexchat_print(lib.ph, b'>>> ' + python)
-+        exec_in_interp(__decode(python))
-+    return 0
-+
-+
-+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)):
-+                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)):
-+                filename = plugin.filename
-+                plugins.remove(plugin)
-+                return load_filename(filename)
-+    return False
-+
-+
-+ at contextmanager
-+def change_cwd(path):
-+    old_cwd = os.getcwd()
-+    os.chdir(path)
-+    yield
-+    os.chdir(old_cwd)
-+
-+
-+def autoload():
-+    configdir = __decode(ffi.string(lib.hexchat_get_info(lib.ph, b'configdir')))
-+    addondir = os.path.join(configdir, 'addons')
-+    try:
-+        with change_cwd(addondir):  # Maintaining old behavior
-+            for f in os.listdir(addondir):
-+                if f.endswith('.py'):
-+                    log('Autoloading', f)
-+                    # TODO: Set cwd
-+                    load_filename(os.path.join(addondir, f))
-+    except FileNotFoundError as e:
-+        log('Autoload failed', e)
-+
-+
-+def list_plugins():
-+    if not plugins:
-+        lib.hexchat_print(lib.ph, b'No python modules loaded')
-+        return
-+
-+    lib.hexchat_print(lib.ph, b'Name         Version  Filename             Description')
-+    lib.hexchat_print(lib.ph, b'----         -------  --------             -----------')
-+    for plugin in plugins:
-+        basename = os.path.basename(plugin.filename).encode()
-+        name = plugin.name.encode()
-+        version = plugin.version.encode() if plugin.version else b'<none>'
-+        description = plugin.description.encode() if plugin.description else b'<none>'
-+        string = b'%-12s %-8s %-20s %-10s' %(name, version, basename, description)
-+        lib.hexchat_print(lib.ph, string)
-+    lib.hexchat_print(lib.ph, b'')
-+
-+
-+def exec_in_interp(python):
-+    global local_interp
-+
-+    if not python:
-+        return
-+
-+    if local_interp is None:
-+        local_interp = Plugin()
-+        local_interp.locals = {}
-+        local_interp.globals['hexchat'] = hexchat
-+
-+    code = compile_line(python)
-+    try:
-+        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)
-+
-+
-+ at ffi.def_extern()
-+def _on_load_command(word, word_eol, userdata):
-+    filename = ffi.string(word[2])
-+    if filename.endswith(b'.py'):
-+        load_filename(__decode(filename))
-+        return 3
-+    return 0
-+
-+
-+ at ffi.def_extern()
-+def _on_unload_command(word, word_eol, userdata):
-+    filename = ffi.string(word[2])
-+    if filename.endswith(b'.py'):
-+        unload_name(__decode(filename))
-+        return 3
-+    return 0
-+
-+
-+ at ffi.def_extern()
-+def _on_reload_command(word, word_eol, userdata):
-+    filename = ffi.string(word[2])
-+    if filename.endswith(b'.py'):
-+        reload_name(__decode(filename))
-+        return 3
-+    return 0
-+
-+
-+ at ffi.def_extern(error=3)
-+def _on_py_command(word, word_eol, userdata):
-+    subcmd = __decode(ffi.string(word[2])).lower()
-+
-+    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')
-+
-+    return 3
-+
-+
-+ at ffi.def_extern()
-+def _on_plugin_init(plugin_name, plugin_desc, plugin_version, arg, libdir):
-+    global hexchat
-+    global hexchat_stdout
-+
-+    signal.signal(signal.SIGINT, signal.SIG_DFL)
-+
-+    plugin_name[0] = PLUGIN_NAME
-+    plugin_desc[0] = PLUGIN_DESC
-+    plugin_version[0] = PLUGIN_VERSION
-+
-+    try:
-+        libdir = __decode(ffi.string(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()
-+    sys.stdout = hexchat_stdout
-+    sys.stderr = hexchat_stdout
-+
-+    lib.hexchat_hook_command(lib.ph, b'', 0, lib._on_say_command, ffi.NULL, ffi.NULL)
-+    lib.hexchat_hook_command(lib.ph, b'LOAD', 0, lib._on_load_command, ffi.NULL, ffi.NULL)
-+    lib.hexchat_hook_command(lib.ph, b'UNLOAD', 0, lib._on_unload_command, ffi.NULL, ffi.NULL)
-+    lib.hexchat_hook_command(lib.ph, b'RELOAD', 0, lib._on_reload_command, ffi.NULL, ffi.NULL)
-+    lib.hexchat_hook_command(lib.ph, b'PY', 0, lib._on_py_command, b'''Usage: /PY LOAD   <filename>
-+           UNLOAD <filename|name>
-+           RELOAD <filename|name>
-+           LIST
-+           EXEC <command>
-+           CONSOLE
-+           ABOUT''', ffi.NULL)
-+
-+    lib.hexchat_print(lib.ph, b'Python interface loaded')
-+    autoload()
-+    return 1
-+
-+
-+ at ffi.def_extern()
-+def _on_plugin_deinit():
-+    global local_interp
-+    global hexchat
-+    global hexchat_stdout
-+    global plugins
-+
-+    plugins = set()
-+    local_interp = None
-+    hexchat = None
-+    hexchat_stdout = None
-+    sys.stdout = sys.__stdout__
-+    sys.stderr = sys.__stderr__
-+
-+    for mod in ('_hexchat', 'hexchat', 'xchat', '_hexchat_embedded'):
-+        try:
-+            del sys.modules[mod]
-+        except KeyError:
-+            pass
-+
-+    return 1
-diff --git a/plugins/python/python2.vcxproj b/plugins/python/python2.vcxproj
-index f914a865..0b098112 100644
---- a/plugins/python/python2.vcxproj
-+++ b/plugins/python/python2.vcxproj
-@@ -37,6 +37,9 @@
-       <AdditionalDependencies>"$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
-       <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-     </Link>
-+    <PreBuildEvent>
-+      <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
-+    </PreBuildEvent>
-   </ItemDefinitionGroup>
-   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-     <ClCompile>
-@@ -48,12 +51,15 @@
-       <AdditionalDependencies>"$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
-       <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-     </Link>
-+    <PreBuildEvent>
-+      <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
-+    </PreBuildEvent>
-   </ItemDefinitionGroup>
-   <ItemGroup>
-     <None Include="python.def" />
-   </ItemGroup>
-   <ItemGroup>
--    <ClCompile Include="python.c" />
-+    <ClCompile Include="$(IntDir)python.c" />
-   </ItemGroup>
-   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- </Project>
-diff --git a/plugins/python/python3.vcxproj b/plugins/python/python3.vcxproj
-index 815dc8b1..5868d3b0 100644
---- a/plugins/python/python3.vcxproj
-+++ b/plugins/python/python3.vcxproj
-@@ -37,6 +37,9 @@
-       <AdditionalDependencies>"$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
-       <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-     </Link>
-+    <PreBuildEvent>
-+      <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
-+    </PreBuildEvent>
-   </ItemDefinitionGroup>
-   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-     <ClCompile>
-@@ -48,12 +51,20 @@
-       <AdditionalDependencies>"$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
-       <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-     </Link>
-+    <PreBuildEvent>
-+      <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
-+    </PreBuildEvent>
-   </ItemDefinitionGroup>
-   <ItemGroup>
-+    <None Include="generate_plugin.py" />
-+    <None Include="hexchat.py" />
-     <None Include="python.def" />
-+    <None Include="python.py" />
-+    <None Include="xchat.py" />
-+    <None Include="_hexchat.py" />
-   </ItemGroup>
-   <ItemGroup>
--    <ClCompile Include="python.c" />
-+    <ClCompile Include="$(IntDir)python.c" />
-   </ItemGroup>
-   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
--</Project>
-+</Project>
-\ No newline at end of file
-diff --git a/plugins/python/python3.vcxproj.filters b/plugins/python/python3.vcxproj.filters
-index 9165e798..5c5834bb 100644
---- a/plugins/python/python3.vcxproj.filters
-+++ b/plugins/python/python3.vcxproj.filters
-@@ -9,13 +9,26 @@
-     </Filter>
-   </ItemGroup>
-   <ItemGroup>
--    <ClCompile Include="python.c">
--      <Filter>Source Files</Filter>
--    </ClCompile>
-+    <ClCompile Include="$(IntDir)python.c" />
-   </ItemGroup>
-   <ItemGroup>
-     <None Include="python.def">
-       <Filter>Resource Files</Filter>
-     </None>
-+    <None Include="_hexchat.py">
-+      <Filter>Source Files</Filter>
-+    </None>
-+    <None Include="generate_plugin.py">
-+      <Filter>Source Files</Filter>
-+    </None>
-+    <None Include="hexchat.py">
-+      <Filter>Source Files</Filter>
-+    </None>
-+    <None Include="python.py">
-+      <Filter>Source Files</Filter>
-+    </None>
-+    <None Include="xchat.py">
-+      <Filter>Source Files</Filter>
-+    </None>
-   </ItemGroup>
- </Project>
-\ No newline at end of file
-diff --git a/plugins/python/xchat.py b/plugins/python/xchat.py
-new file mode 100644
-index 00000000..6922490b
---- /dev/null
-+++ b/plugins/python/xchat.py
-@@ -0,0 +1 @@
-+from _hexchat import *
-diff --git a/src/common/meson.build b/src/common/meson.build
-index a0d6ce2b..09c10daf 100644
---- a/src/common/meson.build
-+++ b/src/common/meson.build
-@@ -93,10 +93,6 @@ endif
- 
- if get_option('with-plugin')
-   common_deps += libgmodule_dep
--  common_cflags += '-DHEXCHATLIBDIR="@0@"'.format(join_paths(get_option('prefix'),
--                                                  get_option('libdir'),
--                                                  'hexchat/plugins'))
--
-   install_headers('hexchat-plugin.h')
- endif
- 
-diff --git a/win32/copy/copy.vcxproj b/win32/copy/copy.vcxproj
-index c508a7f3..72f2c032 100644
---- a/win32/copy/copy.vcxproj
-+++ b/win32/copy/copy.vcxproj
-@@ -64,6 +64,8 @@
-     <LuaShare Include="$(DepsRoot)\share\lua\**\*.lua" />
-     <LuaShare Include="$(DepsRoot)\share\lua\**\**\*.lua" />
-     <Typelib Include="$(DepsRoot)\lib\girepository-1.0\*.typelib" />
-+    <None Include="$(Python3Path)\Lib\site-packages\_cffi_backend.*.pyd" />
-+    <None Include="$(Python2Path)\Lib\site-packages\_cffi_backend.pyd" />
- 
-     <Engines Include="$(DepsRoot)\lib\gtk-2.0\i686-pc-vs14\engines\**\*" />
- 
-@@ -91,6 +93,9 @@
-     <Copy SourceFiles="@(LuaShare)" DestinationFiles="@(LuaShare->'$(HexChatRel)\share\lua\%(RecursiveDir)%(Filename)%(Extension)')" />
-     <Copy SourceFiles="@(LuaLib)" DestinationFiles="@(LuaLib->'$(HexChatRel)\lib\lua\%(RecursiveDir)%(Filename)%(Extension)')" />
-     <Copy SourceFiles="@(Typelib)" DestinationFiles="@(Typelib->'$(HexChatRel)\lib\girepository-1.0\%(Filename)%(Extension)')" />
-+    <Copy SourceFiles="..\..\plugins\python\xchat.py" DestinationFolder="$(HexChatRel)\python" />
-+    <Copy SourceFiles="..\..\plugins\python\hexchat.py" DestinationFolder="$(HexChatRel)\python" />
-+    <Copy SourceFiles="..\..\plugins\python\_hexchat.py" DestinationFolder="$(HexChatRel)\python" />
- 
-     <WriteLinesToFile File="$(HexChatRel)portable-mode" Lines="2" Overwrite="true" />
- 
-diff --git a/win32/installer/hexchat.iss.tt b/win32/installer/hexchat.iss.tt
-index e242ee96..3ac5ec41 100644
---- a/win32/installer/hexchat.iss.tt
-+++ b/win32/installer/hexchat.iss.tt
-@@ -164,6 +164,7 @@ Source: "lib\girepository-1.0\*.typelib"; DestDir: "{app}\lib\girepository-1.0";
- Source: "share\lua\*.lua"; DestDir: "{app}\share\lua"; Flags: ignoreversion; Components: langs\lua
- Source: "share\lua\lgi\*.lua"; DestDir: "{app}\share\lua\lgi"; Flags: ignoreversion; Components: langs\lua
- Source: "share\lua\lgi\override\*.lua"; DestDir: "{app}\share\lua\lgi\override"; Flags: ignoreversion; Components: langs\lua
-+Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua
- 
- Source: "plugins\hcchecksum.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\checksum
- Source: "plugins\hcexec.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\exec
-@@ -175,11 +176,15 @@ Source: "WinSparkle.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: pl
- Source: "plugins\hcwinamp.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\winamp
- Source: "share\system.png"; DestDir: "{app}\share"; Flags: ignoreversion; Components: plugins\sysinfo
- Source: "plugins\hcsysinfo.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\sysinfo
-+Source: "plugins\hcperl.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\perl
-+
-+Source: "python\*.py"; DestDir: "{app}\python"; Flags: ignoreversion; Components: langs\python
- 
- Source: "plugins\hcpython2.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\python\python2
-+Source: "_cffi_backend.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python\python2
-+
- Source: "plugins\hcpython3.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\python\python3
--Source: "plugins\hcperl.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\perl
--Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua
-+Source: "_cffi_backend.cp3*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python\python3
- 
- Source: "hexchat.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
- Source: "hexchat-text.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: xctext
-From a2ff661d40bcd49a0be973b7b60583fde64e09c2 Mon Sep 17 00:00:00 2001
-From: A_D <A_D at snoonet.org>
-Date: Wed, 25 Jul 2018 21:41:05 +0200
-Subject: python: Various cffi fixes
-
-- fixed /py exec behaviour
-- fixed hexchat.unload_hook() failing when passed a hook id
-- fixed get_list() calls in python3
----
- plugins/python/_hexchat.py | 11 ++++++++++-
- plugins/python/python.py   |  5 +++--
- 2 files changed, 13 insertions(+), 3 deletions(-)
-
-diff --git a/plugins/python/_hexchat.py b/plugins/python/_hexchat.py
-index 52b3ec14..50ccfb83 100644
---- a/plugins/python/_hexchat.py
-+++ b/plugins/python/_hexchat.py
-@@ -150,6 +150,15 @@ class ListItem:
-         return '<{} list item at {}>'.format(self._listname, id(self))
- 
- 
-+# done this way for speed
-+if sys.version_info[0] == 2:
-+    def get_getter(name):
-+        return ord(name[0])
-+else:
-+    def get_getter(name):
-+        return name[0]
-+
-+
- def get_list(name):
-     # XXX: This function is extremely inefficient and could be interators and
-     # lazily loaded properties, but for API compat we stay slow
-@@ -189,7 +198,7 @@ def get_list(name):
-     while lib.hexchat_list_next(lib.ph, list_) is 1:
-         item = ListItem(orig_name)
-         for field in fields:
--            getter = getters.get(ord(field[0]))
-+            getter = getters.get(get_getter(field))
-             if getter is not None:
-                 field_name = field[1:]
-                 setattr(item, __cached_decoded_str(field_name), getter(field_name))
-diff --git a/plugins/python/python.py b/plugins/python/python.py
-index 3845a79f..e6a61b5e 100644
---- a/plugins/python/python.py
-+++ b/plugins/python/python.py
-@@ -98,7 +98,8 @@ else:
-         return compile(data, filename, 'exec', optimize=2, dont_inherit=True)
- 
-     def compile_line(string):
--        return compile(string, '<string>', 'eval', optimize=2, dont_inherit=True)
-+        # newline appended to solve unexpected EOF issues
-+        return compile(string + '\n', '<string>', 'single', optimize=2, dont_inherit=True)
- 
- 
- class Plugin:
-@@ -122,7 +123,7 @@ class Plugin:
-     def remove_hook(self, hook):
-         for h in self.hooks:
-             if id(h) == hook:
--                ud = hook.userdata
-+                ud = h.userdata
-                 self.hooks.remove(h)
-                 return ud
-         else:
-From ed55330153e7d85e753d1321c4e46e9bb6833735 Mon Sep 17 00:00:00 2001
-From: Patrick Griffis <tingping at tingping.se>
-Date: Wed, 5 Dec 2018 19:45:30 -0500
-Subject: python: Fix console not eating commands
----
- plugins/python/python.py | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/plugins/python/python.py b/plugins/python/python.py
-index e6a61b5e..1eeb10b4 100644
---- a/plugins/python/python.py
-+++ b/plugins/python/python.py
-@@ -281,6 +281,7 @@ def _on_say_command(word, word_eol, userdata):
-         python = ffi.string(word_eol[1])
-         lib.hexchat_print(lib.ph, b'>>> ' + python)
-         exec_in_interp(__decode(python))
-+        return 1
-     return 0
- 
- 
-From 3ebfa83fdd43335da1dd2d39f0bfae91d67b8c90 Mon Sep 17 00:00:00 2001
-From: A_D <A_D at snoonet.org>
-Date: Wed, 26 Dec 2018 20:37:56 +0200
-Subject: python: Made sure to set sys.argv if it is not set.
- fixes #2282
----
- plugins/python/python.py | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/plugins/python/python.py b/plugins/python/python.py
-index 1eeb10b4..942b0ce5 100644
---- a/plugins/python/python.py
-+++ b/plugins/python/python.py
-@@ -10,6 +10,9 @@ import traceback
- import weakref
- 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'
-From f7713a6a64ee55d3c20e9e27b8f8a5e98385ff57 Mon Sep 17 00:00:00 2001
-From: linuxdaemon <linuxdaemon at users.noreply.github.com>
-Date: Wed, 26 Dec 2018 16:15:25 -0600
-Subject: python: Make the plugins table dynamically sized
- (#2291)
-
-Adjust the width of the columns depending on the length of the data in
-each element
----
- plugins/python/python.py | 21 +++++++++++++++++----
- 1 file changed, 17 insertions(+), 4 deletions(-)
-
-diff --git a/plugins/python/python.py b/plugins/python/python.py
-index 942b0ce5..371fbf40 100644
---- a/plugins/python/python.py
-+++ b/plugins/python/python.py
-@@ -349,15 +349,28 @@ def list_plugins():
-         lib.hexchat_print(lib.ph, b'No python modules loaded')
-         return
- 
--    lib.hexchat_print(lib.ph, b'Name         Version  Filename             Description')
--    lib.hexchat_print(lib.ph, b'----         -------  --------             -----------')
-+    tbl_headers = [b'Name', b'Version', b'Filename', b'Description']
-+    tbl = [
-+        tbl_headers,
-+        [(b'-' * len(s)) for s in tbl_headers]
-+    ]
-+
-     for plugin in plugins:
-         basename = os.path.basename(plugin.filename).encode()
-         name = plugin.name.encode()
-         version = plugin.version.encode() if plugin.version else b'<none>'
-         description = plugin.description.encode() if plugin.description else b'<none>'
--        string = b'%-12s %-8s %-20s %-10s' %(name, version, basename, description)
--        lib.hexchat_print(lib.ph, string)
-+        tbl.append((name, version, basename, description))
-+
-+    column_sizes = [
-+        max(len(item) for item in column)
-+        for column in zip(*tbl)
-+    ]
-+
-+    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'')
- 
- 
-From a5a727122b66c9003b44fcdc199ad56dbe15a131 Mon Sep 17 00:00:00 2001
-From: linuxdaemon <linuxdaemon at users.noreply.github.com>
-Date: Thu, 27 Dec 2018 13:46:02 -0600
-Subject: python: Make sure `help()` doesn't cause hexchat to
- hang (#2290)
-
-* Make sure `help()` doesn't cause hexchat to hang
-
-Replace `pydoc.help` with a copy of `pydoc.Helper` with an empty
-`StringIO` instead of stdin
-
-* Handle BytesIO vs StringIO on 2.7
----
- plugins/python/python.py | 10 +++++++++-
- 1 file changed, 9 insertions(+), 1 deletion(-)
-
-diff --git a/plugins/python/python.py b/plugins/python/python.py
-index 371fbf40..1afb36c4 100644
---- a/plugins/python/python.py
-+++ b/plugins/python/python.py
-@@ -1,6 +1,7 @@
- from __future__ import print_function
- 
- import os
-+import pydoc
- import sys
- from contextlib import contextmanager
- import importlib
-@@ -8,6 +9,12 @@ import signal
- import site
- import traceback
- import weakref
-+
-+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'):
-@@ -57,7 +64,6 @@ class Stdout:
-             self.buffer += string
- 
-     def isatty(self):
--        # FIXME: help() locks app despite this?
-         return False
- 
- 
-@@ -474,6 +480,7 @@ def _on_plugin_init(plugin_name, plugin_desc, plugin_version, arg, libdir):
-     hexchat_stdout = Stdout()
-     sys.stdout = hexchat_stdout
-     sys.stderr = hexchat_stdout
-+    pydoc.help = pydoc.Helper(HelpEater(), HelpEater())
- 
-     lib.hexchat_hook_command(lib.ph, b'', 0, lib._on_say_command, ffi.NULL, ffi.NULL)
-     lib.hexchat_hook_command(lib.ph, b'LOAD', 0, lib._on_load_command, ffi.NULL, ffi.NULL)
-@@ -505,6 +512,7 @@ def _on_plugin_deinit():
-     hexchat_stdout = None
-     sys.stdout = sys.__stdout__
-     sys.stderr = sys.__stderr__
-+    pydoc.help = pydoc.Helper()
- 
-     for mod in ('_hexchat', 'hexchat', 'xchat', '_hexchat_embedded'):
-         try:
-From 7abeb10cf1f82fbad4d167f9e6f6918e1f47650b Mon Sep 17 00:00:00 2001
-From: A_D <A_D at snoonet.org>
-Date: Wed, 26 Dec 2018 20:46:31 +0200
-Subject: python: plugin cleanup and refactor
----
- plugins/python/_hexchat.py           | 91 ++++++++++++++-----------
- plugins/python/python.py             | 99 ++++++++++++++++++----------
- plugins/python/python_style_guide.md | 26 ++++++++
- 3 files changed, 143 insertions(+), 73 deletions(-)
- create mode 100644 plugins/python/python_style_guide.md
-
-diff --git a/plugins/python/_hexchat.py b/plugins/python/_hexchat.py
-index 50ccfb83..ebee5657 100644
---- a/plugins/python/_hexchat.py
-+++ b/plugins/python/_hexchat.py
-@@ -1,6 +1,7 @@
--from contextlib import contextmanager
- import inspect
- import sys
-+from contextlib import contextmanager
-+
- from _hexchat_embedded import ffi, lib
- 
- __all__ = [
-@@ -40,13 +41,15 @@ def __get_current_plugin():
-     while '__plugin' not in frame.f_globals:
-         frame = frame.f_back
-         assert frame is not None
-+
-     return frame.f_globals['__plugin']
- 
- 
- # Keeping API compat
--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()
-@@ -64,16 +67,18 @@ def emit_print(event_name, *args, **kwargs):
-         arg = args[i].encode() if len(args) > i else b''
-         cstring = ffi.new('char[]', arg)
-         cargs.append(cstring)
--    if time is 0:
-+
-+    if time == 0:
-         return lib.hexchat_emit_print(lib.ph, event_name.encode(), *cargs)
--    else:
--        attrs = lib.hexchat_event_attrs_create(lib.ph)
--        attrs.server_time_utc = time
--        ret = lib.hexchat_emit_print(lib.ph, attrs, event_name.encode(), *cargs)
--        lib.hexchat_event_attrs_free(lib.ph, attrs)
--        return ret
-+
-+    attrs = lib.hexchat_event_attrs_create(lib.ph)
-+    attrs.server_time_utc = time
-+    ret = lib.hexchat_emit_print(lib.ph, attrs, event_name.encode(), *cargs)
-+    lib.hexchat_event_attrs_free(lib.ph, attrs)
-+    return ret
- 
- 
-+# TODO: this shadows itself. command should be changed to cmd
- def command(command):
-     lib.hexchat_command(lib.ph, command.encode())
- 
-@@ -97,21 +102,24 @@ def get_info(name):
-         # Surely there is a less dumb way?
-         ptr = repr(ret).rsplit(' ', 1)[1][:-1]
-         return ptr
-+
-     return __decode(ffi.string(ret))
- 
- 
- def get_prefs(name):
-     string_out = ffi.new('char**')
-     int_out = ffi.new('int*')
--    type = lib.hexchat_get_prefs(lib.ph, name.encode(), string_out, int_out)
--    if type is 0:
-+    _type = lib.hexchat_get_prefs(lib.ph, name.encode(), string_out, int_out)
-+    if _type == 0:
-         return None
--    elif type is 1:
-+
-+    if _type == 1:
-         return __decode(ffi.string(string_out[0]))
--    elif type is 2 or type is 3:  # XXX: 3 should be a bool, but keeps API
-+
-+    if _type in (2, 3):  # XXX: 3 should be a bool, but keeps API
-         return int_out[0]
--    else:
--        assert False
-+
-+    raise AssertionError('Out of bounds pref storage')
- 
- 
- def __cstrarray_to_list(arr):
-@@ -120,6 +128,7 @@ def __cstrarray_to_list(arr):
-     while arr[i] != ffi.NULL:
-         ret.append(ffi.string(arr[i]))
-         i += 1
-+
-     return ret
- 
- 
-@@ -127,8 +136,7 @@ __FIELD_CACHE = {}
- 
- 
- def __get_fields(name):
--    return __FIELD_CACHE.setdefault(name,
--                                    __cstrarray_to_list(lib.hexchat_list_fields(lib.ph, name)))
-+    return __FIELD_CACHE.setdefault(name, __cstrarray_to_list(lib.hexchat_list_fields(lib.ph, name)))
- 
- 
- __FIELD_PROPERTY_CACHE = {}
-@@ -154,6 +162,7 @@ class ListItem:
- if sys.version_info[0] == 2:
-     def get_getter(name):
-         return ord(name[0])
-+
- else:
-     def get_getter(name):
-         return name[0]
-@@ -179,6 +188,7 @@ def get_list(name):
-         string = lib.hexchat_list_str(lib.ph, list_, field)
-         if string != ffi.NULL:
-             return __decode(ffi.string(string))
-+
-         return ''
- 
-     def ptr_getter(field):
-@@ -186,6 +196,7 @@ def get_list(name):
-             ptr = lib.hexchat_list_str(lib.ph, list_, field)
-             ctx = ffi.cast('hexchat_context*', ptr)
-             return Context(ctx)
-+
-         return None
- 
-     getters = {
-@@ -195,25 +206,27 @@ def get_list(name):
-         ord('p'): ptr_getter,
-     }
- 
--    while lib.hexchat_list_next(lib.ph, list_) is 1:
-+    while lib.hexchat_list_next(lib.ph, list_) == 1:
-         item = ListItem(orig_name)
--        for field in fields:
--            getter = getters.get(get_getter(field))
-+        for _field in fields:
-+            getter = getters.get(get_getter(_field))
-             if getter is not None:
--                field_name = field[1:]
-+                field_name = _field[1:]
-                 setattr(item, __cached_decoded_str(field_name), getter(field_name))
-+
-         ret.append(item)
- 
-     lib.hexchat_list_free(lib.ph, list_)
-     return ret
- 
- 
-+# TODO: 'command' here shadows command above, and should be renamed to cmd
- def hook_command(command, callback, userdata=None, priority=PRI_NORM, help=None):
-     plugin = __get_current_plugin()
-     hook = plugin.add_hook(callback, userdata)
-     handle = lib.hexchat_hook_command(lib.ph, command.encode(), priority, lib._on_command_hook,
--                                      help.encode() if help is not None else ffi.NULL,
--                                      hook.handle)
-+                                      help.encode() if help is not None else ffi.NULL, hook.handle)
-+
-     hook.hexchat_hook = handle
-     return id(hook)
- 
-@@ -221,8 +234,7 @@ def hook_command(command, callback, userdata=None, priority=PRI_NORM, help=None)
- def hook_print(name, callback, userdata=None, priority=PRI_NORM):
-     plugin = __get_current_plugin()
-     hook = plugin.add_hook(callback, userdata)
--    handle = lib.hexchat_hook_print(lib.ph, name.encode(), priority, lib._on_print_hook,
--                                    hook.handle)
-+    handle = lib.hexchat_hook_print(lib.ph, name.encode(), priority, lib._on_print_hook, hook.handle)
-     hook.hexchat_hook = handle
-     return id(hook)
- 
-@@ -230,8 +242,7 @@ def hook_print(name, callback, userdata=None, priority=PRI_NORM):
- def hook_print_attrs(name, callback, userdata=None, priority=PRI_NORM):
-     plugin = __get_current_plugin()
-     hook = plugin.add_hook(callback, userdata)
--    handle = lib.hexchat_hook_print_attrs(lib.ph, name.encode(), priority, lib._on_print_attrs_hook,
--                                          hook.handle)
-+    handle = lib.hexchat_hook_print_attrs(lib.ph, name.encode(), priority, lib._on_print_attrs_hook, hook.handle)
-     hook.hexchat_hook = handle
-     return id(hook)
- 
-@@ -239,8 +250,7 @@ def hook_print_attrs(name, callback, userdata=None, priority=PRI_NORM):
- def hook_server(name, callback, userdata=None, priority=PRI_NORM):
-     plugin = __get_current_plugin()
-     hook = plugin.add_hook(callback, userdata)
--    handle = lib.hexchat_hook_server(lib.ph, name.encode(), priority, lib._on_server_hook,
--                                     hook.handle)
-+    handle = lib.hexchat_hook_server(lib.ph, name.encode(), priority, lib._on_server_hook, hook.handle)
-     hook.hexchat_hook = handle
-     return id(hook)
- 
-@@ -248,8 +258,7 @@ def hook_server(name, callback, userdata=None, priority=PRI_NORM):
- def hook_server_attrs(name, callback, userdata=None, priority=PRI_NORM):
-     plugin = __get_current_plugin()
-     hook = plugin.add_hook(callback, userdata)
--    handle = lib.hexchat_hook_server_attrs(lib.ph, name.encode(), priority, lib._on_server_attrs_hook,
--                                           hook.handle)
-+    handle = lib.hexchat_hook_server_attrs(lib.ph, name.encode(), priority, lib._on_server_attrs_hook, hook.handle)
-     hook.hexchat_hook = handle
-     return id(hook)
- 
-@@ -276,17 +285,18 @@ def unhook(handle):
- def set_pluginpref(name, value):
-     if isinstance(value, str):
-         return bool(lib.hexchat_pluginpref_set_str(lib.ph, name.encode(), value.encode()))
--    elif isinstance(value, int):
-+
-+    if isinstance(value, int):
-         return bool(lib.hexchat_pluginpref_set_int(lib.ph, name.encode(), value))
--    else:
--        # XXX: This should probably raise but this keeps API
--        return False
-+
-+    # XXX: This should probably raise but this keeps API
-+    return False
- 
- 
- def get_pluginpref(name):
-     name = name.encode()
-     string_out = ffi.new('char[512]')
--    if lib.hexchat_pluginpref_get_str(lib.ph, name, string_out) is not 1:
-+    if lib.hexchat_pluginpref_get_str(lib.ph, name, string_out) != 1:
-         return None
- 
-     string = ffi.string(string_out)
-@@ -308,8 +318,9 @@ def del_pluginpref(name):
- 
- def list_pluginpref():
-     prefs_str = ffi.new('char[4096]')
--    if lib.hexchat_pluginpref_list(lib.ph, prefs_str) is 1:
-+    if lib.hexchat_pluginpref_list(lib.ph, prefs_str) == 1:
-         return __decode(prefs_str).split(',')
-+
-     return []
- 
- 
-@@ -320,6 +331,7 @@ class Context:
-     def __eq__(self, value):
-         if not isinstance(value, Context):
-             return False
-+
-         return self._ctx == value._ctx
- 
-     @contextmanager
-@@ -327,9 +339,9 @@ class Context:
-         old_ctx = lib.hexchat_get_context(lib.ph)
-         if not self.set():
-             # XXX: Behavior change, previously used wrong context
--            lib.hexchat_print(lib.ph,
--                              b'Context object refers to closed context, ignoring call')
-+            lib.hexchat_print(lib.ph, b'Context object refers to closed context, ignoring call')
-             return
-+
-         yield
-         lib.hexchat_set_context(lib.ph, old_ctx)
- 
-@@ -370,4 +382,5 @@ def find_context(server=None, channel=None):
-     ctx = lib.hexchat_find_context(lib.ph, server, channel)
-     if ctx == ffi.NULL:
-         return None
-+
-     return Context(ctx)
-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
- 
-diff --git a/plugins/python/python_style_guide.md b/plugins/python/python_style_guide.md
-new file mode 100644
-index 00000000..41db2474
---- /dev/null
-+++ b/plugins/python/python_style_guide.md
-@@ -0,0 +1,26 @@
-+# HexChat Python Module Style Guide
-+
-+(This is a work in progress).
-+
-+## General rules
-+
-+- PEP8 as general fallback recommendations
-+- Max line length: 120
-+- Avoid overcomplex compound statements. i.e. dont do this: `somevar = x if x == y else z if a == b and c == b else x`
-+
-+## Indentation style
-+
-+### Multi-line functions
-+
-+```python
-+foo(really_long_arg_1,
-+    really_long_arg_2)
-+```
-+
-+### Mutli-line lists/dicts
-+
-+```python
-+foo = {
-+    'bar': 'baz',
-+}
-+```
-From 586f089df6cdc465411ad1805b618ec44acc20ab Mon Sep 17 00:00:00 2001
-From: jacob1 <jfu614 at gmail.com>
-Date: Sun, 23 Jun 2019 15:43:44 -0400
-Subject: Python: Fix error in hexchat.emit_print when passing
- time attribute
----
- plugins/python/_hexchat.py | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/plugins/python/_hexchat.py b/plugins/python/_hexchat.py
-index ebee5657..567b3493 100644
---- a/plugins/python/_hexchat.py
-+++ b/plugins/python/_hexchat.py
-@@ -73,7 +73,7 @@ def emit_print(event_name, *args, **kwargs):
- 
-     attrs = lib.hexchat_event_attrs_create(lib.ph)
-     attrs.server_time_utc = time
--    ret = lib.hexchat_emit_print(lib.ph, attrs, event_name.encode(), *cargs)
-+    ret = lib.hexchat_emit_print_attrs(lib.ph, attrs, event_name.encode(), *cargs)
-     lib.hexchat_event_attrs_free(lib.ph, attrs)
-     return ret
- 

Modified: PKGBUILD
===================================================================
--- PKGBUILD	2019-12-22 19:07:25 UTC (rev 538887)
+++ PKGBUILD	2019-12-22 19:10:51 UTC (rev 538888)
@@ -2,8 +2,8 @@
 # Contributor: TingPing <tingping at tingping.se>
 
 pkgname=hexchat
-pkgver=2.14.2
-pkgrel=6
+pkgver=2.14.3
+pkgrel=1
 pkgdesc='A popular and easy to use graphical IRC (chat) client'
 arch=('x86_64')
 url='https://hexchat.github.io/'
@@ -17,15 +17,12 @@
             'perl: Perl plugin'
             'python-cffi: Python plugin')
 source=("git+https://github.com/hexchat/hexchat.git#tag=v${pkgver}"
-        '0001-python-backport.patch'
         '0002-python-3-8.patch')
 sha256sums=('SKIP'
-            'eb2d038dba5f56ddc156fafea41c6560baf09a401ae9ea4f6b8ee079d542fc38'
             '8d17ce657e744272815b5fb33d8ad959f79ece3294349637eaadcf86d90496fa')
 
 prepare() {
   cd hexchat
-  patch -Np1 < ../0001-python-backport.patch
   patch -Np1 < ../0002-python-3-8.patch
 }
 



More information about the arch-commits mailing list