9 from typing import Union, Optional
10 from .abc import ResourceReader, Traversable
12 from ._compat import wrap_spec
14 Package = Union[types.ModuleType, str]
18 # type: (Package) -> Traversable
20 Get a Traversable resource from a package
22 return from_package(get_package(package))
25 def get_resource_reader(package):
26 # type: (types.ModuleType) -> Optional[ResourceReader]
28 Return the package's loader if it's a ResourceReader.
31 # a issubclass() check here because apparently abc.'s __subclasscheck__()
32 # hook wants to create a weak reference to the object, but
33 # zipimport.zipimporter does not support weak references, resulting in a
34 # TypeError. That seems terrible.
35 spec = package.__spec__
36 reader = getattr(spec.loader, 'get_resource_reader', None) # type: ignore
39 return reader(spec.name) # type: ignore
43 # type: (Package) -> types.ModuleType
44 return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand)
47 def get_package(package):
48 # type: (Package) -> types.ModuleType
49 """Take a package name or module object and return the module.
51 Raise an exception if the resolved module is not a package.
53 resolved = resolve(package)
54 if wrap_spec(resolved).submodule_search_locations is None:
55 raise TypeError(f'{package!r} is not a package')
59 def from_package(package):
61 Return a Traversable object for the given package.
64 spec = wrap_spec(package)
65 reader = spec.loader.get_resource_reader(spec.name)
69 @contextlib.contextmanager
70 def _tempfile(reader, suffix=''):
71 # Not using tempfile.NamedTemporaryFile as it leads to deeper 'try'
72 # blocks due to the need to close the temporary file to work on Windows
74 fd, raw_path = tempfile.mkstemp(suffix=suffix)
77 os.write(fd, reader())
81 yield pathlib.Path(raw_path)
85 except FileNotFoundError:
89 @functools.singledispatch
92 Given a Traversable object, return that object as a
93 path on the local file system in a context manager.
95 return _tempfile(path.read_bytes, suffix=path.name)
98 @as_file.register(pathlib.Path)
99 @contextlib.contextmanager
102 Degenerate behavior for pathlib.Path objects.