Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8207434

Browse filesBrowse files
committed
Add _adapters module, making SpecLoaderAdapter a first-class feature and not a compatibility feature. Add DegenerateFiles for readers that don't implement the files API such that the rest of the API can rely on it being implemented. Fixes #214.
1 parent 06da7de commit 8207434
Copy full SHA for 8207434

File tree

3 files changed

+109
-28
lines changed
Filter options

3 files changed

+109
-28
lines changed

‎importlib_resources/_adapters.py

Copy file name to clipboard
+90Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from contextlib import suppress
2+
3+
from . import abc
4+
5+
6+
class SpecLoaderAdapter:
7+
"""
8+
Adapt a package spec to adapt the underlying loader.
9+
"""
10+
11+
def __init__(self, spec, adapter=lambda spec: spec.loader):
12+
self.spec = spec
13+
self.loader = adapter(spec)
14+
15+
def __getattr__(self, name):
16+
return getattr(self.spec, name)
17+
18+
19+
class TraversableResourcesLoader:
20+
"""
21+
Adapt a loader to provide TraversableResources.
22+
"""
23+
24+
def __init__(self, spec):
25+
self.spec = spec
26+
27+
def get_resource_reader(self, name):
28+
return DegenerateFiles(self.spec)._native()
29+
30+
31+
class DegenerateFiles:
32+
"""
33+
Adapter for an existing or non-existant resource reader
34+
to provide a degenerate .files().
35+
"""
36+
37+
class Path(abc.Traversable):
38+
def iterdir(self):
39+
return iter(())
40+
41+
def read_bytes(self):
42+
raise ValueError()
43+
44+
def read_text(self):
45+
raise ValueError()
46+
47+
def is_dir(self):
48+
return False
49+
50+
is_file = exists = is_dir
51+
52+
def joinpath(self, other):
53+
return DegenerateFiles.Path()
54+
55+
__truediv__ = joinpath
56+
57+
def name(self):
58+
return ''
59+
60+
def open(self):
61+
raise ValueError()
62+
63+
def __init__(self, spec):
64+
self.spec = spec
65+
66+
@property
67+
def _reader(self):
68+
with suppress(AttributeError):
69+
return self.spec.loader.get_resource_reader(self.spec.name)
70+
71+
def _native(self):
72+
"""
73+
Return the native reader if it supports files().
74+
"""
75+
reader = self._reader
76+
return reader if hasattr(reader, 'files') else self
77+
78+
def __getattr__(self, attr):
79+
return getattr(self._reader, attr)
80+
81+
def files(self):
82+
return DegenerateFiles.Path()
83+
84+
85+
def wrap_spec(package):
86+
"""
87+
Construct a package spec with traversable compatibility
88+
on the spec/loader/reader.
89+
"""
90+
return SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)

‎importlib_resources/_common.py

Copy file name to clipboardExpand all lines: importlib_resources/_common.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from typing import Union, Any, Optional
1010
from .abc import ResourceReader
1111

12-
from ._compat import package_spec
12+
from ._compat import wrap_spec
1313

1414
Package = Union[types.ModuleType, str]
1515

@@ -63,7 +63,7 @@ def get_package(package):
6363
Raise an exception if the resolved module is not a package.
6464
"""
6565
resolved = resolve(package)
66-
if package_spec(resolved).submodule_search_locations is None:
66+
if wrap_spec(resolved).submodule_search_locations is None:
6767
raise TypeError('{!r} is not a package'.format(package))
6868
return resolved
6969

@@ -73,7 +73,7 @@ def from_package(package):
7373
Return a Traversable object for the given package.
7474
7575
"""
76-
spec = package_spec(package)
76+
spec = wrap_spec(package)
7777
reader = spec.loader.get_resource_reader(spec.name)
7878
return reader.files()
7979

‎importlib_resources/_compat.py

Copy file name to clipboardExpand all lines: importlib_resources/_compat.py
+16-25Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
# flake8: noqa
2+
13
import abc
24
import sys
5+
import pathlib
36
from contextlib import suppress
47

5-
# flake8: noqa
6-
78
try:
89
from zipfile import Path as ZipPath # type: ignore
910
except ImportError:
@@ -24,19 +25,6 @@ def runtime_checkable(cls): # type: ignore
2425
Protocol = abc.ABC # type: ignore
2526

2627

27-
class SpecLoaderAdapter:
28-
"""
29-
Adapt a package spec to adapt the underlying loader.
30-
"""
31-
32-
def __init__(self, spec, adapter=lambda spec: spec.loader):
33-
self.spec = spec
34-
self.loader = adapter(spec)
35-
36-
def __getattr__(self, name):
37-
return getattr(self.spec, name)
38-
39-
4028
class TraversableResourcesLoader:
4129
"""
4230
Adapt loaders to provide TraversableResources and other
@@ -52,7 +40,7 @@ def path(self):
5240

5341
def get_resource_reader(self, name):
5442
# Python < 3.9
55-
from . import readers
43+
from . import readers, _adapters
5644

5745
def _zip_reader(spec):
5846
with suppress(AttributeError):
@@ -70,6 +58,10 @@ def _native_reader(spec):
7058
reader = _available_reader(spec)
7159
return reader if hasattr(reader, 'files') else None
7260

61+
def _file_reader(spec):
62+
if pathlib.Path(self.path).exists():
63+
return readers.FileReader(self)
64+
7365
return (
7466
# native reader if it supplies 'files'
7567
_native_reader(self.spec)
@@ -81,17 +73,16 @@ def _native_reader(spec):
8173
_namespace_reader(self.spec)
8274
or
8375
# local FileReader
84-
readers.FileReader(self)
76+
_file_reader(self.spec)
77+
or _adapters.DegenerateFiles(self.spec)
8578
)
8679

8780

88-
def package_spec(package):
81+
def wrap_spec(package):
8982
"""
90-
Construct a minimal package spec suitable for
91-
matching the interfaces this library relies upon
92-
in later Python versions.
83+
Construct a package spec with traversable compatibility
84+
on the spec/loader/reader.
9385
"""
94-
spec = package.__spec__
95-
# avoid adapting the spec in test_package_has_no_reader_fallback
96-
adapt = spec.loader.__class__ is not object
97-
return SpecLoaderAdapter(spec, TraversableResourcesLoader) if adapt else spec
86+
from . import _adapters
87+
88+
return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.