types.py 6.92 KB
Newer Older
Simon van der Linden's avatar
Simon van der Linden committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
# -*- Mode: Python; py-indent-offset: 4 -*-
# vim: tabstop=4 shiftwidth=4 expandtab
#
# Copyright (C) 2005-2009 Johan Dahlin <johan@gnome.org>
#
#   types.py: base types for introspected items.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
# USA

from __future__ import absolute_import

import sys
import gobject

from ._gi import \
    InterfaceInfo, \
    ObjectInfo, \
    StructInfo, \
Tomeu Vizoso's avatar
Tomeu Vizoso committed
32
    set_object_has_new_constructor, \
Tomeu Vizoso's avatar
Tomeu Vizoso committed
33
    register_interface_info, \
34
    hook_up_vfunc_implementation
Simon van der Linden's avatar
Simon van der Linden committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52


def Function(info):

    def function(*args):
        return info.invoke(*args)
    function.__info__ = info
    function.__name__ = info.get_name()
    function.__module__ = info.get_namespace()

    return function


def Constructor(info):

    def constructor(cls, *args):
        cls_name = info.get_container().get_name()
        if cls.__name__ != cls_name:
53
            raise TypeError('%s constructor cannot be used to create instances of a subclass' % cls_name)
54
        return info.invoke(*args)
Simon van der Linden's avatar
Simon van der Linden committed
55 56 57 58 59 60 61 62 63 64 65 66 67

    constructor.__info__ = info
    constructor.__name__ = info.get_name()
    constructor.__module__ = info.get_namespace()

    return constructor


class MetaClassHelper(object):

    def _setup_methods(cls):
        for method_info in cls.__info__.get_methods():
            name = method_info.get_name()
68 69 70

            if method_info.is_constructor():
                method = classmethod(Constructor(method_info))
Simon van der Linden's avatar
Simon van der Linden committed
71
            if method_info.is_method():
72
                method = Function(method_info)
Simon van der Linden's avatar
Simon van der Linden committed
73
            else:
74
                method = staticmethod(Function(method_info))
Simon van der Linden's avatar
Simon van der Linden committed
75 76 77 78 79 80 81 82 83 84 85 86 87
            setattr(cls, name, method)

    def _setup_fields(cls):
        for field_info in cls.__info__.get_fields():
            name = field_info.get_name().replace('-', '_')
            setattr(cls, name, property(field_info.get_value, field_info.set_value))

    def _setup_constants(cls):
        for constant_info in cls.__info__.get_constants():
            name = constant_info.get_name()
            value = constant_info.get_value()
            setattr(cls, name, value)

88
    def _setup_vfuncs(cls, impl):
Tomeu Vizoso's avatar
Tomeu Vizoso committed
89 90 91 92 93
        for base in cls.__bases__:
            if not hasattr(base, '__info__') or \
                    not hasattr(base.__info__, 'get_vfuncs'):
                continue
            for vfunc_info in base.__info__.get_vfuncs():
94 95
                vfunc_name = 'do_' + vfunc_info.get_name()
                vfunc = getattr(impl, vfunc_name, None)
96
                if vfunc is not None:
97 98 99 100 101 102 103 104 105 106 107 108 109 110
                    # check to see if there are vfuncs with the same name in the bases
                    # we have no way of specifying which one we are supposed to override
                    ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
                    if ambiguous_base is not None:
                        raise TypeError('Method %s() on class %s.%s is ambiguous '
                                'with methods in base classes %s.%s and %s.%s' %
                                (vfunc_name,
                                 impl.__info__.get_namespace(),
                                 impl.__info__.get_name(),
                                 base.__info__.get_namespace(),
                                 base.__info__.get_name(),
                                 ambiguous_base.__info__.get_namespace(),
                                 ambiguous_base.__info__.get_name()))

John (J5) Palmieri's avatar
John (J5) Palmieri committed
111 112 113 114
                    function = vfunc
                    if sys.version_info < (3, 0):
                        function = vfunc.im_func
 
115 116
                    if not is_function_in_classes(function, impl.__bases__):
                        hook_up_vfunc_implementation(vfunc_info, impl.__gtype__,
John (J5) Palmieri's avatar
John (J5) Palmieri committed
117
                                                     vfunc)
Tomeu Vizoso's avatar
Tomeu Vizoso committed
118

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
            base._setup_vfuncs(impl)

def find_vfunc_conflict_in_bases(vfunc, bases):
    for klass in bases:
        if not hasattr(klass, '__info__') or \
                not hasattr(klass.__info__, 'get_vfuncs'):
            continue
        vfuncs = klass.__info__.get_vfuncs()
        vfunc_name = vfunc.get_name()
        for v in vfuncs:
            if v.get_name() == vfunc_name and v != vfunc:
                return klass

        aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
        if aklass is not None:
            return aklass
    return None

137 138 139 140 141 142 143
def is_function_in_classes(function, classes):
    for klass in classes:
        if function in klass.__dict__.values():
            return True
        elif is_function_in_classes(function, klass.__bases__):
            return True
    return False
Simon van der Linden's avatar
Simon van der Linden committed
144 145 146 147 148 149

class GObjectMeta(gobject.GObjectMeta, MetaClassHelper):

    def __init__(cls, name, bases, dict_):
        super(GObjectMeta, cls).__init__(name, bases, dict_)

150 151 152 153 154 155 156 157 158
        is_gi_defined = False
        if cls.__module__ == 'gi.repository.' + cls.__info__.get_namespace():
            is_gi_defined = True

        is_python_defined = False
        if not is_gi_defined and cls.__module__ != GObjectMeta.__module__:
            is_python_defined = True

        if is_python_defined:
159
            cls._setup_vfuncs(cls)
160
        elif is_gi_defined:
Tomeu Vizoso's avatar
Tomeu Vizoso committed
161 162 163 164 165 166 167 168
            cls._setup_methods()
            cls._setup_constants()

            if isinstance(cls.__info__, ObjectInfo):
                cls._setup_fields()
                set_object_has_new_constructor(cls.__info__.get_g_type())
            elif isinstance(cls.__info__, InterfaceInfo):
                register_interface_info(cls.__info__.get_g_type())
Simon van der Linden's avatar
Simon van der Linden committed
169 170 171 172 173 174 175

class StructMeta(type, MetaClassHelper):

    def __init__(cls, name, bases, dict_):
        super(StructMeta, cls).__init__(name, bases, dict_)

        # Avoid touching anything else than the base class.
176 177
        g_type = cls.__info__.get_g_type()
        if g_type != gobject.TYPE_INVALID and g_type.pytype is not None:
178
            return
Simon van der Linden's avatar
Simon van der Linden committed
179 180 181 182

        cls._setup_fields()
        cls._setup_methods()

183 184 185 186 187 188 189 190 191 192 193 194 195
class Enum(int):
    __info__ = None
    def __init__(self, value):
        int.__init__(value)

    def __repr__(self):
        value_name = str(self)
        for value_info in self.__info__.get_values():
            if self == value_info.get_value():
                value_name = value_info.get_name().upper()
        return "<enum %s of type %s.%s>" % (value_name,
                                            self.__info__.get_namespace(),
                                            self.__info__.get_name())