ยปCore Development>Code coverage>Lib/importlib/util.py

Python code coverage for Lib/importlib/util.py

#countcontent
1n/a"""Utility code for constructing importers, etc."""
2n/afrom . import abc
3n/afrom ._bootstrap import module_from_spec
4n/afrom ._bootstrap import _resolve_name
5n/afrom ._bootstrap import spec_from_loader
6n/afrom ._bootstrap import _find_spec
7n/afrom ._bootstrap_external import MAGIC_NUMBER
8n/afrom ._bootstrap_external import cache_from_source
9n/afrom ._bootstrap_external import decode_source
10n/afrom ._bootstrap_external import source_from_cache
11n/afrom ._bootstrap_external import spec_from_file_location
12n/a
13n/afrom contextlib import contextmanager
14n/aimport functools
15n/aimport sys
16n/aimport types
17n/aimport warnings
18n/a
19n/a
20n/adef resolve_name(name, package):
21n/a """Resolve a relative module name to an absolute one."""
22n/a if not name.startswith('.'):
23n/a return name
24n/a elif not package:
25n/a raise ValueError(f'no package specified for {repr(name)} '
26n/a '(required for relative module names)')
27n/a level = 0
28n/a for character in name:
29n/a if character != '.':
30n/a break
31n/a level += 1
32n/a return _resolve_name(name[level:], package, level)
33n/a
34n/a
35n/adef _find_spec_from_path(name, path=None):
36n/a """Return the spec for the specified module.
37n/a
38n/a First, sys.modules is checked to see if the module was already imported. If
39n/a so, then sys.modules[name].__spec__ is returned. If that happens to be
40n/a set to None, then ValueError is raised. If the module is not in
41n/a sys.modules, then sys.meta_path is searched for a suitable spec with the
42n/a value of 'path' given to the finders. None is returned if no spec could
43n/a be found.
44n/a
45n/a Dotted names do not have their parent packages implicitly imported. You will
46n/a most likely need to explicitly import all parent packages in the proper
47n/a order for a submodule to get the correct spec.
48n/a
49n/a """
50n/a if name not in sys.modules:
51n/a return _find_spec(name, path)
52n/a else:
53n/a module = sys.modules[name]
54n/a if module is None:
55n/a return None
56n/a try:
57n/a spec = module.__spec__
58n/a except AttributeError:
59n/a raise ValueError('{}.__spec__ is not set'.format(name)) from None
60n/a else:
61n/a if spec is None:
62n/a raise ValueError('{}.__spec__ is None'.format(name))
63n/a return spec
64n/a
65n/a
66n/adef find_spec(name, package=None):
67n/a """Return the spec for the specified module.
68n/a
69n/a First, sys.modules is checked to see if the module was already imported. If
70n/a so, then sys.modules[name].__spec__ is returned. If that happens to be
71n/a set to None, then ValueError is raised. If the module is not in
72n/a sys.modules, then sys.meta_path is searched for a suitable spec with the
73n/a value of 'path' given to the finders. None is returned if no spec could
74n/a be found.
75n/a
76n/a If the name is for submodule (contains a dot), the parent module is
77n/a automatically imported.
78n/a
79n/a The name and package arguments work the same as importlib.import_module().
80n/a In other words, relative module names (with leading dots) work.
81n/a
82n/a """
83n/a fullname = resolve_name(name, package) if name.startswith('.') else name
84n/a if fullname not in sys.modules:
85n/a parent_name = fullname.rpartition('.')[0]
86n/a if parent_name:
87n/a # Use builtins.__import__() in case someone replaced it.
88n/a parent = __import__(parent_name, fromlist=['__path__'])
89n/a return _find_spec(fullname, parent.__path__)
90n/a else:
91n/a return _find_spec(fullname, None)
92n/a else:
93n/a module = sys.modules[fullname]
94n/a if module is None:
95n/a return None
96n/a try:
97n/a spec = module.__spec__
98n/a except AttributeError:
99n/a raise ValueError('{}.__spec__ is not set'.format(name)) from None
100n/a else:
101n/a if spec is None:
102n/a raise ValueError('{}.__spec__ is None'.format(name))
103n/a return spec
104n/a
105n/a
106n/a@contextmanager
107n/adef _module_to_load(name):
108n/a is_reload = name in sys.modules
109n/a
110n/a module = sys.modules.get(name)
111n/a if not is_reload:
112n/a # This must be done before open() is called as the 'io' module
113n/a # implicitly imports 'locale' and would otherwise trigger an
114n/a # infinite loop.
115n/a module = type(sys)(name)
116n/a # This must be done before putting the module in sys.modules
117n/a # (otherwise an optimization shortcut in import.c becomes wrong)
118n/a module.__initializing__ = True
119n/a sys.modules[name] = module
120n/a try:
121n/a yield module
122n/a except Exception:
123n/a if not is_reload:
124n/a try:
125n/a del sys.modules[name]
126n/a except KeyError:
127n/a pass
128n/a finally:
129n/a module.__initializing__ = False
130n/a
131n/a
132n/adef set_package(fxn):
133n/a """Set __package__ on the returned module.
134n/a
135n/a This function is deprecated.
136n/a
137n/a """
138n/a @functools.wraps(fxn)
139n/a def set_package_wrapper(*args, **kwargs):
140n/a warnings.warn('The import system now takes care of this automatically.',
141n/a DeprecationWarning, stacklevel=2)
142n/a module = fxn(*args, **kwargs)
143n/a if getattr(module, '__package__', None) is None:
144n/a module.__package__ = module.__name__
145n/a if not hasattr(module, '__path__'):
146n/a module.__package__ = module.__package__.rpartition('.')[0]
147n/a return module
148n/a return set_package_wrapper
149n/a
150n/a
151n/adef set_loader(fxn):
152n/a """Set __loader__ on the returned module.
153n/a
154n/a This function is deprecated.
155n/a
156n/a """
157n/a @functools.wraps(fxn)
158n/a def set_loader_wrapper(self, *args, **kwargs):
159n/a warnings.warn('The import system now takes care of this automatically.',
160n/a DeprecationWarning, stacklevel=2)
161n/a module = fxn(self, *args, **kwargs)
162n/a if getattr(module, '__loader__', None) is None:
163n/a module.__loader__ = self
164n/a return module
165n/a return set_loader_wrapper
166n/a
167n/a
168n/adef module_for_loader(fxn):
169n/a """Decorator to handle selecting the proper module for loaders.
170n/a
171n/a The decorated function is passed the module to use instead of the module
172n/a name. The module passed in to the function is either from sys.modules if
173n/a it already exists or is a new module. If the module is new, then __name__
174n/a is set the first argument to the method, __loader__ is set to self, and
175n/a __package__ is set accordingly (if self.is_package() is defined) will be set
176n/a before it is passed to the decorated function (if self.is_package() does
177n/a not work for the module it will be set post-load).
178n/a
179n/a If an exception is raised and the decorator created the module it is
180n/a subsequently removed from sys.modules.
181n/a
182n/a The decorator assumes that the decorated function takes the module name as
183n/a the second argument.
184n/a
185n/a """
186n/a warnings.warn('The import system now takes care of this automatically.',
187n/a DeprecationWarning, stacklevel=2)
188n/a @functools.wraps(fxn)
189n/a def module_for_loader_wrapper(self, fullname, *args, **kwargs):
190n/a with _module_to_load(fullname) as module:
191n/a module.__loader__ = self
192n/a try:
193n/a is_package = self.is_package(fullname)
194n/a except (ImportError, AttributeError):
195n/a pass
196n/a else:
197n/a if is_package:
198n/a module.__package__ = fullname
199n/a else:
200n/a module.__package__ = fullname.rpartition('.')[0]
201n/a # If __package__ was not set above, __import__() will do it later.
202n/a return fxn(self, module, *args, **kwargs)
203n/a
204n/a return module_for_loader_wrapper
205n/a
206n/a
207n/aclass _LazyModule(types.ModuleType):
208n/a
209n/a """A subclass of the module type which triggers loading upon attribute access."""
210n/a
211n/a def __getattribute__(self, attr):
212n/a """Trigger the load of the module and return the attribute."""
213n/a # All module metadata must be garnered from __spec__ in order to avoid
214n/a # using mutated values.
215n/a # Stop triggering this method.
216n/a self.__class__ = types.ModuleType
217n/a # Get the original name to make sure no object substitution occurred
218n/a # in sys.modules.
219n/a original_name = self.__spec__.name
220n/a # Figure out exactly what attributes were mutated between the creation
221n/a # of the module and now.
222n/a attrs_then = self.__spec__.loader_state['__dict__']
223n/a original_type = self.__spec__.loader_state['__class__']
224n/a attrs_now = self.__dict__
225n/a attrs_updated = {}
226n/a for key, value in attrs_now.items():
227n/a # Code that set the attribute may have kept a reference to the
228n/a # assigned object, making identity more important than equality.
229n/a if key not in attrs_then:
230n/a attrs_updated[key] = value
231n/a elif id(attrs_now[key]) != id(attrs_then[key]):
232n/a attrs_updated[key] = value
233n/a self.__spec__.loader.exec_module(self)
234n/a # If exec_module() was used directly there is no guarantee the module
235n/a # object was put into sys.modules.
236n/a if original_name in sys.modules:
237n/a if id(self) != id(sys.modules[original_name]):
238n/a raise ValueError(f"module object for {original_name!r} "
239n/a "substituted in sys.modules during a lazy "
240n/a "load")
241n/a # Update after loading since that's what would happen in an eager
242n/a # loading situation.
243n/a self.__dict__.update(attrs_updated)
244n/a return getattr(self, attr)
245n/a
246n/a def __delattr__(self, attr):
247n/a """Trigger the load and then perform the deletion."""
248n/a # To trigger the load and raise an exception if the attribute
249n/a # doesn't exist.
250n/a self.__getattribute__(attr)
251n/a delattr(self, attr)
252n/a
253n/a
254n/aclass LazyLoader(abc.Loader):
255n/a
256n/a """A loader that creates a module which defers loading until attribute access."""
257n/a
258n/a @staticmethod
259n/a def __check_eager_loader(loader):
260n/a if not hasattr(loader, 'exec_module'):
261n/a raise TypeError('loader must define exec_module()')
262n/a
263n/a @classmethod
264n/a def factory(cls, loader):
265n/a """Construct a callable which returns the eager loader made lazy."""
266n/a cls.__check_eager_loader(loader)
267n/a return lambda *args, **kwargs: cls(loader(*args, **kwargs))
268n/a
269n/a def __init__(self, loader):
270n/a self.__check_eager_loader(loader)
271n/a self.loader = loader
272n/a
273n/a def create_module(self, spec):
274n/a return self.loader.create_module(spec)
275n/a
276n/a def exec_module(self, module):
277n/a """Make the module load lazily."""
278n/a module.__spec__.loader = self.loader
279n/a module.__loader__ = self.loader
280n/a # Don't need to worry about deep-copying as trying to set an attribute
281n/a # on an object would have triggered the load,
282n/a # e.g. ``module.__spec__.loader = None`` would trigger a load from
283n/a # trying to access module.__spec__.
284n/a loader_state = {}
285n/a loader_state['__dict__'] = module.__dict__.copy()
286n/a loader_state['__class__'] = module.__class__
287n/a module.__spec__.loader_state = loader_state
288n/a module.__class__ = _LazyModule