Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

# -*- 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 sys 

import importlib 

 

_have_py3 = (sys.version_info[0] >= 3) 

 

try: 

maketrans = ''.maketrans 

except AttributeError: 

# fallback for Python 2 

from string import maketrans 

 

import gi 

 

from ._gi import \ 

Repository, \ 

FunctionInfo, \ 

RegisteredTypeInfo, \ 

EnumInfo, \ 

ObjectInfo, \ 

InterfaceInfo, \ 

ConstantInfo, \ 

StructInfo, \ 

UnionInfo, \ 

CallbackInfo, \ 

Struct, \ 

Boxed, \ 

CCallback, \ 

enum_add, \ 

enum_register_new_gtype_and_add, \ 

flags_add, \ 

flags_register_new_gtype_and_add, \ 

GInterface 

from .types import \ 

GObjectMeta, \ 

StructMeta 

 

from ._constants import \ 

TYPE_NONE, \ 

TYPE_BOXED, \ 

TYPE_POINTER, \ 

TYPE_ENUM, \ 

TYPE_FLAGS 

 

 

repository = Repository.get_default() 

 

# Cache of IntrospectionModules that have been loaded. 

_introspection_modules = {} 

 

 

def get_parent_for_object(object_info): 

parent_object_info = object_info.get_parent() 

 

if not parent_object_info: 

# If we reach the end of the introspection info class hierarchy, look 

# for an existing wrapper on the GType and use it as a base for the 

# new introspection wrapper. This allows static C wrappers already 

# registered with the GType to be used as the introspection base 

# (_gi.GObject for example) 

gtype = object_info.get_g_type() 

if gtype and gtype.pytype: 

return gtype.pytype 

 

# Otherwise use builtins.object as the base 

return object 

 

namespace = parent_object_info.get_namespace() 

name = parent_object_info.get_name() 

 

module = importlib.import_module('gi.repository.' + namespace) 

return getattr(module, name) 

 

 

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 = importlib.import_module('gi.repository.' + namespace) 

interfaces.append(getattr(module, name)) 

return interfaces 

 

 

class IntrospectionModule(object): 

"""An object which wraps an introspection typelib. 

 

This wrapping creates a python module like representation of the typelib 

using gi repository as a foundation. Accessing attributes of the module 

will dynamically pull them in and create wrappers for the members. 

These members are then cached on this introspection module. 

""" 

def __init__(self, namespace, version=None): 

"""Might raise gi._gi.RepositoryError""" 

 

repository.require(namespace, version) 

self._namespace = namespace 

self._version = version 

self.__name__ = 'gi.repository.' + namespace 

 

path = repository.get_typelib_path(self._namespace) 

self.__path__ = [path] 

if _have_py3: 

# get_typelib_path() delivers bytes, not a string 

self.__path__ = [path.decode('UTF-8')] 

 

if self._version is None: 

self._version = repository.get_version(self._namespace) 

 

def __getattr__(self, name): 

info = repository.find_by_name(self._namespace, name) 

if not info: 

raise AttributeError("%r object has no attribute %r" % ( 

self.__name__, name)) 

 

if isinstance(info, EnumInfo): 

g_type = info.get_g_type() 

wrapper = g_type.pytype 

 

if wrapper is None: 

if info.is_flags(): 

if g_type.is_a(TYPE_FLAGS): 

wrapper = flags_add(g_type) 

else: 

assert g_type == TYPE_NONE 

wrapper = flags_register_new_gtype_and_add(info) 

else: 

if g_type.is_a(TYPE_ENUM): 

wrapper = enum_add(g_type) 

else: 

assert g_type == TYPE_NONE 

wrapper = enum_register_new_gtype_and_add(info) 

 

wrapper.__info__ = info 

wrapper.__module__ = 'gi.repository.' + info.get_namespace() 

 

# Don't use upper() here to avoid locale specific 

# identifier conversion (e. g. in Turkish 'i'.upper() == 'i') 

# see https://bugzilla.gnome.org/show_bug.cgi?id=649165 

ascii_upper_trans = maketrans( 

'abcdefgjhijklmnopqrstuvwxyz', 

'ABCDEFGJHIJKLMNOPQRSTUVWXYZ') 

for value_info in info.get_values(): 

value_name = value_info.get_name_unescaped().translate(ascii_upper_trans) 

setattr(wrapper, value_name, wrapper(value_info.get_value())) 

for method_info in info.get_methods(): 

setattr(wrapper, method_info.__name__, method_info) 

 

if g_type != TYPE_NONE: 

g_type.pytype = wrapper 

 

elif isinstance(info, RegisteredTypeInfo): 

g_type = info.get_g_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, CallbackInfo): 

bases = (CCallback,) 

metaclass = GObjectMeta 

elif isinstance(info, InterfaceInfo): 

bases = (GInterface,) 

metaclass = GObjectMeta 

elif isinstance(info, (StructInfo, UnionInfo)): 

if g_type.is_a(TYPE_BOXED): 

bases = (Boxed,) 

elif (g_type.is_a(TYPE_POINTER) or 

g_type == TYPE_NONE or 

g_type.fundamental == g_type): 

bases = (Struct,) 

else: 

raise TypeError("unable to create a wrapper for %s.%s" % (info.get_namespace(), info.get_name())) 

metaclass = StructMeta 

else: 

raise NotImplementedError(info) 

 

# Check if there is already a Python wrapper that is not a parent class 

# of the wrapper being created. If it is a parent, it is ok to clobber 

# g_type.pytype with a new child class wrapper of the existing parent. 

# Note that the return here never occurs under normal circumstances due 

# to caching on the __dict__ itself. 

if g_type != TYPE_NONE: 

type_ = g_type.pytype 

if type_ is not None and type_ not in bases: 

self.__dict__[name] = type_ 

return type_ 

 

dict_ = { 

'__info__': info, 

'__module__': 'gi.repository.' + self._namespace, 

'__gtype__': g_type 

} 

wrapper = metaclass(name, bases, dict_) 

 

# Register the new Python wrapper. 

if g_type != TYPE_NONE: 

g_type.pytype = wrapper 

 

elif isinstance(info, FunctionInfo): 

wrapper = info 

elif isinstance(info, ConstantInfo): 

wrapper = info.get_value() 

else: 

raise NotImplementedError(info) 

 

# Cache the newly created wrapper which will then be 

# available directly on this introspection module instead of being 

# lazily constructed through the __getattr__ we are currently in. 

self.__dict__[name] = wrapper 

return wrapper 

 

def __repr__(self): 

path = repository.get_typelib_path(self._namespace) 

if _have_py3: 

# get_typelib_path() delivers bytes, not a string 

path = path.decode('UTF-8') 

return "<IntrospectionModule %r from %r>" % (self._namespace, path) 

 

def __dir__(self): 

# Python's default dir() is just dir(self.__class__) + self.__dict__.keys() 

result = set(dir(self.__class__)) 

result.update(self.__dict__.keys()) 

 

# update *set* because some repository attributes have already been 

# wrapped by __getattr__() and included in self.__dict__; but skip 

# Callback types, as these are not real objects which we can actually 

# get 

namespace_infos = repository.get_infos(self._namespace) 

result.update(info.get_name() for info in namespace_infos if 

not isinstance(info, CallbackInfo)) 

 

return list(result) 

 

 

def get_introspection_module(namespace): 

""" 

:Returns: 

An object directly wrapping the gi module without overrides. 

 

Might raise gi._gi.RepositoryError 

""" 

if namespace in _introspection_modules: 

return _introspection_modules[namespace] 

 

version = gi.get_required_version(namespace) 

module = IntrospectionModule(namespace, version) 

_introspection_modules[namespace] = module 

return module