Imported Upstream version 3.18.1
[platform/upstream/pygobject2.git] / gi / importer.py
1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
3 #
4 # Copyright (C) 2005-2009 Johan Dahlin <johan@gnome.org>
5 #               2015 Christoph Reiter
6 #
7 #   importer.py: dynamic importer for introspected libraries.
8 #
9 # This library is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation; either
12 # version 2.1 of the License, or (at your option) any later version.
13 #
14 # This library is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # Lesser General Public License for more details.
18 #
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with this library; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
22 # USA
23
24 from __future__ import absolute_import
25 import sys
26 import warnings
27 from contextlib import contextmanager
28
29 import gi
30 from ._gi import Repository
31 from ._gi import PyGIWarning
32 from .module import get_introspection_module
33 from .overrides import load_overrides
34
35
36 repository = Repository.get_default()
37
38 # only for backwards compatibility
39 modules = {}
40
41
42 @contextmanager
43 def _check_require_version(namespace, stacklevel):
44     """A context manager which tries to give helpful warnings
45     about missing gi.require_version() which could potentially
46     break code if only an older version than expected is installed
47     or a new version gets introduced.
48
49     ::
50
51         with _check_require_version("Gtk", stacklevel):
52             load_namespace_and_overrides()
53     """
54
55     was_loaded = repository.is_registered(namespace)
56
57     yield
58
59     if was_loaded:
60         # it was loaded before by another import which depended on this
61         # namespace or by C code like libpeas
62         return
63
64     if namespace in ("GLib", "GObject", "Gio"):
65         # part of glib (we have bigger problems if versions change there)
66         return
67
68     if gi.get_required_version(namespace) is not None:
69         # the version was forced using require_version()
70         return
71
72     version = repository.get_version(namespace)
73     warnings.warn(
74         "%(namespace)s was imported without specifying a version first. "
75         "Use gi.require_version('%(namespace)s', '%(version)s') before "
76         "import to ensure that the right version gets loaded."
77         % {"namespace": namespace, "version": version},
78         PyGIWarning, stacklevel=stacklevel)
79
80
81 class DynamicImporter(object):
82
83     # Note: see PEP302 for the Importer Protocol implemented below.
84
85     def __init__(self, path):
86         self.path = path
87
88     def find_module(self, fullname, path=None):
89         if not fullname.startswith(self.path):
90             return
91
92         path, namespace = fullname.rsplit('.', 1)
93         if path != self.path:
94             return
95
96         if repository.enumerate_versions(namespace):
97             return self
98         else:
99             raise ImportError('cannot import name %s, '
100                               'introspection typelib not found' % namespace)
101
102     def load_module(self, fullname):
103         if fullname in sys.modules:
104             return sys.modules[fullname]
105
106         path, namespace = fullname.rsplit('.', 1)
107
108         # we want the warning to point to the line doing the import
109         if sys.version_info >= (3, 0):
110             stacklevel = 10
111         else:
112             stacklevel = 4
113         with _check_require_version(namespace, stacklevel=stacklevel):
114             introspection_module = get_introspection_module(namespace)
115             dynamic_module = load_overrides(introspection_module)
116
117         dynamic_module.__file__ = '<%s>' % fullname
118         dynamic_module.__loader__ = self
119         sys.modules[fullname] = dynamic_module
120
121         return dynamic_module