module.py 8.24 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
32
33
34
35
36
# -*- Mode: Python; py-indent-offset: 4 -*-
# vim: tabstop=4 shiftwidth=4 expandtab
#
# Copyright (C) 2007-2009 Johan Dahlin <johan@gnome.org>
#
#   module.py: dynamic module for introspected libraries.
#
# 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 os
import gobject

from ._gi import \
    Repository, \
    FunctionInfo, \
    RegisteredTypeInfo, \
    EnumInfo, \
    ObjectInfo, \
    InterfaceInfo, \
    ConstantInfo, \
    StructInfo, \
Tomeu Vizoso's avatar
Tomeu Vizoso committed
37
    UnionInfo, \
Simon van der Linden's avatar
Simon van der Linden committed
38
    Struct, \
39
    Boxed, \
Simon van der Linden's avatar
Simon van der Linden committed
40
41
42
43
44
    enum_add, \
    flags_add
from .types import \
    GObjectMeta, \
    StructMeta, \
45
46
    Function, \
    Enum
Simon van der Linden's avatar
Simon van der Linden committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

repository = Repository.get_default()


def get_parent_for_object(object_info):
    parent_object_info = object_info.get_parent()

    if not parent_object_info:
        return object

    namespace = parent_object_info.get_namespace()
    name = parent_object_info.get_name()

    # Workaround for GObject.Object and GObject.InitiallyUnowned.
    if namespace == 'GObject' and name == 'Object' or name == 'InitiallyUnowned':
        return gobject.GObject

    module = __import__('gi.repository.%s' % namespace, fromlist=[name])
    return getattr(module, name)

67

Simon van der Linden's avatar
Simon van der Linden committed
68
69
70
71
72
73
74
75
76
77
78
def get_interfaces_for_object(object_info):
    interfaces = []
    for interface_info in object_info.get_interfaces():
        namespace = interface_info.get_namespace()
        name = interface_info.get_name()

        module = __import__('gi.repository.%s' % namespace, fromlist=[name])
        interfaces.append(getattr(module, name))
    return interfaces


79
class IntrospectionModule(object):
Simon van der Linden's avatar
Simon van der Linden committed
80

81
82
    def __init__(self, namespace, version=None):
        repository.require(namespace, version)
83
        self._namespace = namespace
84
        self.version = version
85
        self.__name__ = 'gi.repository.' + namespace
Simon van der Linden's avatar
Simon van der Linden committed
86

87
88
89
90
91
        repository.require(self._namespace, self.version)

        if self.version is None:
            self.version = repository.get_version(self._namespace)

Simon van der Linden's avatar
Simon van der Linden committed
92
    def __getattr__(self, name):
93
        info = repository.find_by_name(self._namespace, name)
Simon van der Linden's avatar
Simon van der Linden committed
94
95
        if not info:
            raise AttributeError("%r object has no attribute %r" % (
96
                    self.__name__, name))
Simon van der Linden's avatar
Simon van der Linden committed
97
98
99

        if isinstance(info, EnumInfo):
            g_type = info.get_g_type()
100
            wrapper = g_type.pytype
Simon van der Linden's avatar
Simon van der Linden committed
101

102
            if wrapper is None:
Simon van der Linden's avatar
Simon van der Linden committed
103
                if g_type.is_a(gobject.TYPE_ENUM):
104
                    wrapper = enum_add(g_type)
105
106
                elif g_type.is_a(gobject.TYPE_NONE):
                    # An enum with a GType of None is an enum without GType
107
                    wrapper = Enum
Johan Dahlin's avatar
Johan Dahlin committed
108
                else:
109
                    wrapper = flags_add(g_type)
Simon van der Linden's avatar
Simon van der Linden committed
110

111
                wrapper.__info__ = info
112
                wrapper.__module__ = 'gi.repository.' + info.get_namespace()
Simon van der Linden's avatar
Simon van der Linden committed
113
114
115

                for value_info in info.get_values():
                    name = value_info.get_name().upper()
116
                    setattr(wrapper, name, wrapper(value_info.get_value()))
Simon van der Linden's avatar
Simon van der Linden committed
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

        elif isinstance(info, RegisteredTypeInfo):
            g_type = info.get_g_type()

            # Check if there is already a Python wrapper.
            if g_type != gobject.TYPE_NONE:
                type_ = g_type.pytype
                if type_ is not None:
                    self.__dict__[name] = type_
                    return type_

            # Create a wrapper.
            if isinstance(info, ObjectInfo):
                parent = get_parent_for_object(info)
                interfaces = tuple(interface for interface in get_interfaces_for_object(info)
                        if not issubclass(parent, interface))
                bases = (parent,) + interfaces
                metaclass = GObjectMeta
            elif isinstance(info, InterfaceInfo):
                bases = (gobject.GInterface,)
                metaclass = GObjectMeta
Tomeu Vizoso's avatar
Tomeu Vizoso committed
138
            elif isinstance(info, (StructInfo, UnionInfo)):
Simon van der Linden's avatar
Simon van der Linden committed
139
140
141
142
143
                if g_type.is_a(gobject.TYPE_BOXED):
                    bases = (Boxed,)
                elif g_type.is_a(gobject.TYPE_POINTER) or g_type == gobject.TYPE_NONE:
                    bases = (Struct,)
                else:
144
                    raise TypeError("unable to create a wrapper for %s.%s" % (info.get_namespace(), info.get_name()))
Simon van der Linden's avatar
Simon van der Linden committed
145
146
147
148
149
150
151
                metaclass = StructMeta
            else:
                raise NotImplementedError(info)

            name = info.get_name()
            dict_ = {
                '__info__': info,
152
                '__module__': 'gi.repository.' + self._namespace,
Simon van der Linden's avatar
Simon van der Linden committed
153
154
                '__gtype__': g_type
            }
155
            wrapper = metaclass(name, bases, dict_)
Simon van der Linden's avatar
Simon van der Linden committed
156
157
158

            # Register the new Python wrapper.
            if g_type != gobject.TYPE_NONE:
159
                g_type.pytype = wrapper
Simon van der Linden's avatar
Simon van der Linden committed
160
161

        elif isinstance(info, FunctionInfo):
162
            wrapper = Function(info)
Simon van der Linden's avatar
Simon van der Linden committed
163
        elif isinstance(info, ConstantInfo):
164
            wrapper = info.get_value()
Simon van der Linden's avatar
Simon van der Linden committed
165
166
167
        else:
            raise NotImplementedError(info)

168
169
        self.__dict__[name] = wrapper
        return wrapper
Simon van der Linden's avatar
Simon van der Linden committed
170

171
172
    def __repr__(self):
        path = repository.get_typelib_path(self._namespace)
173
        return "<IntrospectionModule %r from %r>" % (self._namespace, path)
174
175


176
class DynamicGObjectModule(IntrospectionModule):
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
    """Wrapper for the GObject module

    This class allows us to access both the static PyGObject module and the GI GObject module
    through the same interface.  It is returned when by importing GObject from the gi repository:

    from gi.repository import GObject

    We use this because some PyGI interfaces generated from the GIR require GObject types not wrapped
    by the static bindings.  This also allows access to module attributes in a way that is more
    familiar to GI application developers.  Take signal flags as an example.  The G_SIGNAL_RUN_FIRST
    flag would be accessed as GObject.SIGNAL_RUN_FIRST in the static bindings but in the dynamic bindings
    can be accessed as GObject.SignalFlags.RUN_FIRST.  The latter follows a GI naming convention which
    would be familiar to GI application developers in a number of languages.
    """

    def __init__(self):
193
        IntrospectionModule.__init__(self, namespace='GObject')
194
195
196

    def __getattr__(self, name):
        # first see if this attr is in the gobject module
197
        attr = getattr(gobject, name, None)
198
199
200
201
202
203
204

        # if not in module assume request for an attr exported through GI
        if attr is None:
            attr = super(DynamicGObjectModule, self).__getattr__(name)

        return attr

205

206
207
class DynamicModule(object):
    def __init__(self, namespace):
208
        self._namespace = namespace
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
        self.introspection_module = None
        self._version = None
        self._overrides_module = None

    def require_version(self, version):
        if self.introspection_module is not None and \
                self.introspection_module.version != version:
            raise RuntimeError('Module has been already loaded ')
        self._version = version

    def _import(self):
        self.introspection_module = IntrospectionModule(self._namespace,
                                                        self._version)

        overrides_modules = __import__('gi.overrides', fromlist=[self._namespace])
        self._overrides_module = getattr(overrides_modules, self._namespace, None)
225
226

    def __getattr__(self, name):
227
228
        if self.introspection_module is None:
            self._import()
229

230
231
232
233
        if self._overrides_module is not None:
            override_exports = getattr(self._overrides_module, '__all__', ())
            if name in override_exports:
                return getattr(self._overrides_module, name, None)
Simon van der Linden's avatar
Simon van der Linden committed
234

235
        return getattr(self.introspection_module, name)