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
|
# Copyright (C) 2005-2009 Johan Dahlin <johan@gnome.org>
# 2015 Christoph Reiter
#
# importer.py: dynamic importer 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
import sys
import warnings
import importlib
from contextlib import contextmanager
import gi
from ._gi import Repository, RepositoryError
from ._gi import PyGIWarning
from .module import get_introspection_module
from .overrides import load_overrides
repository = Repository.get_default()
# only for backwards compatibility
modules = {}
@contextmanager
def _check_require_version(namespace, stacklevel):
"""A context manager which tries to give helpful warnings
about missing gi.require_version() which could potentially
break code if only an older version than expected is installed
or a new version gets introduced.
::
with _check_require_version("Gtk", stacklevel):
load_namespace_and_overrides()
"""
was_loaded = repository.is_registered(namespace)
yield
if was_loaded:
# it was loaded before by another import which depended on this
# namespace or by C code like libpeas
return
if namespace in ("GLib", "GObject", "Gio", "GioUnix", "GioWin32"):
# part of glib (we have bigger problems if versions change there)
return
if gi.get_required_version(namespace) is not None:
# the version was forced using require_version()
return
version = repository.get_version(namespace)
warnings.warn(
f"{namespace} was imported without specifying a version first. "
f"Use gi.require_version('{namespace}', '{version}') before "
"import to ensure that the right version gets loaded.",
PyGIWarning,
stacklevel=stacklevel,
)
def get_import_stacklevel(import_hook):
"""Returns the stacklevel value for warnings.warn() for when the warning
gets emitted by an imported module, but the warning should point at the
code doing the import.
Pass import_hook=True if the warning gets generated by an import hook
(warn() gets called in load_module(), see PEP302)
"""
py_version = sys.version_info[:2]
if py_version <= (3, 2):
# 2.7 included
return 4 if import_hook else 2
if py_version == (3, 3):
return 8 if import_hook else 10
if py_version == (3, 4):
return 10 if import_hook else 8
# fixed again in 3.5+, see https://bugs.python.org/issue24305
return 4 if import_hook else 2
class DynamicImporter:
# Note: see PEP302 for the Importer Protocol implemented below.
def __init__(self, path):
self.path = path
def _find_module_check(self, fullname):
if not fullname.startswith(self.path):
return False
path, _namespace = fullname.rsplit(".", 1)
return path == self.path
def find_spec(self, fullname, path=None, target=None):
if self._find_module_check(fullname):
return importlib.util.spec_from_loader(fullname, self)
return None
def find_module(self, fullname, path=None):
if self._find_module_check(fullname):
return self
return None
def create_module(self, spec):
_path, namespace = spec.name.rsplit(".", 1)
# is_registered() is faster than enumerate_versions() and
# in the common case of a namespace getting loaded before its
# dependencies, is_registered() returns True for all dependencies.
if not repository.is_registered(
namespace
) and not repository.enumerate_versions(namespace):
raise ImportError(
f"cannot import name {namespace}, introspection typelib not found"
)
stacklevel = get_import_stacklevel(import_hook=True)
with _check_require_version(namespace, stacklevel=stacklevel):
try:
introspection_module = get_introspection_module(namespace)
except RepositoryError as e:
raise ImportError(e) from e
# Import all dependencies first so their init functions
# (gdk_init, ..) in overrides get called.
# https://bugzilla.gnome.org/show_bug.cgi?id=656314
for dep in repository.get_immediate_dependencies(namespace):
importlib.import_module("gi.repository." + dep.split("-")[0])
return load_overrides(introspection_module)
def exec_module(self, fullname):
# “exec” the module and consequently populate the module's namespace
pass
|