123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- # -*- 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, \
- _gobject
- from .types import \
- GObjectMeta, \
- StructMeta
- GInterface = _gobject.GInterface
- 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
- # (_gobject.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
- self.__path__ = repository.get_typelib_path(self._namespace)
- if _have_py3:
- # get_typelib_path() delivers bytes, not a string
- self.__path__ = self.__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
|