[arch-commits] Commit in hexchat/repos (3 files)

Felix Yan felixonmars at archlinux.org
Mon Nov 4 08:22:28 UTC 2019


    Date: Monday, November 4, 2019 @ 08:22:27
  Author: felixonmars
Revision: 523701

archrelease: copy trunk to community-staging-x86_64

Added:
  hexchat/repos/community-staging-x86_64/
  hexchat/repos/community-staging-x86_64/0001-python-backport.patch
    (from rev 523700, hexchat/trunk/0001-python-backport.patch)
  hexchat/repos/community-staging-x86_64/PKGBUILD
    (from rev 523700, hexchat/trunk/PKGBUILD)

----------------------------+
 0001-python-backport.patch | 4995 +++++++++++++++++++++++++++++++++++++++++++
 PKGBUILD                   |   40 
 2 files changed, 5035 insertions(+)

Copied: hexchat/repos/community-staging-x86_64/0001-python-backport.patch (from rev 523700, hexchat/trunk/0001-python-backport.patch)
===================================================================
--- community-staging-x86_64/0001-python-backport.patch	                        (rev 0)
+++ community-staging-x86_64/0001-python-backport.patch	2019-11-04 08:22:27 UTC (rev 523701)
@@ -0,0 +1,4995 @@
+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
+ 

Copied: hexchat/repos/community-staging-x86_64/PKGBUILD (from rev 523700, hexchat/trunk/PKGBUILD)
===================================================================
--- community-staging-x86_64/PKGBUILD	                        (rev 0)
+++ community-staging-x86_64/PKGBUILD	2019-11-04 08:22:27 UTC (rev 523701)
@@ -0,0 +1,40 @@
+# Maintainer: Maxime Gauduin <alucryd at archlinux.org>
+# Contributor: TingPing <tingping at tingping.se>
+
+pkgname=hexchat
+pkgver=2.14.2
+pkgrel=5
+pkgdesc='A popular and easy to use graphical IRC (chat) client'
+arch=('x86_64')
+url='https://hexchat.github.io/'
+license=('GPL')
+depends=('dbus-glib' 'desktop-file-utils' 'gdk-pixbuf2' 'glib2' 'gtk2'
+         'libcanberra' 'libnotify' 'libproxy' 'openssl' 'pango' 'pciutils')
+makedepends=('git' 'intltool' 'iso-codes' 'lua' 'meson' 'perl' 'python-cffi')
+optdepends=('enchant: Spell check'
+            'iso-codes: Display language names instead of codes'
+            'lua: Lua plugin'
+            'perl: Perl plugin'
+            'python-cffi: Python plugin')
+source=("git+https://github.com/hexchat/hexchat.git#tag=v${pkgver}"
+        '0001-python-backport.patch')
+sha256sums=('SKIP'
+            'eb2d038dba5f56ddc156fafea41c6560baf09a401ae9ea4f6b8ee079d542fc38')
+
+prepare() {
+  cd hexchat
+  patch -Np1 < ../0001-python-backport.patch
+}
+
+build() {
+  arch-meson hexchat build \
+    -Dwith-lua='lua' \
+    -Dwith-text='true'
+  ninja -C build
+}
+
+package() {
+  DESTDIR="${pkgdir}" ninja -C build install
+}
+
+# vim: ts=2 sw=2 et:



More information about the arch-commits mailing list