@@ -64,7 +64,7 @@ from typing.io import BinaryIO
64
64
65
65
66
66
Package = Union[str , types.ModuleType]
67
- Path = Union[str , os.PathLike]
67
+ FileName = Union[str , os.PathLike]
68
68
69
69
70
70
def _get_package (package ):
@@ -82,35 +82,34 @@ def _get_package(package):
82
82
83
83
84
84
def _normalize_path (path ):
85
- if os.path.isabs(path):
86
- raise ValueError (f " { path!r } is absolute " )
87
- normalized_path = os.path.normpath(path)
88
- if normalized_path.startswith(" .." ):
89
- raise ValueError (f " { path!r } attempts to traverse past package " )
85
+ directory, file_name = os.path.split(path)
86
+ if directory:
87
+ raise ValueError (f " { path!r } is not just a file name " )
90
88
else :
91
- return normalized_path
89
+ return file_name
92
90
93
91
94
- def open (module_name : Package, path : Path ) -> BinaryIO:
92
+ def open (module_name : Package, file_name : FileName ) -> BinaryIO:
95
93
""" Return a file-like object opened for binary-reading of the resource."""
96
- normalized_path = _normalize_path(path )
94
+ normalized_path = _normalize_path(file_name )
97
95
module = _get_package(module_name)
98
96
return module.__spec__ .loader.open_resource(normalized_path)
99
97
100
98
101
- def read (module_name : Package, path : Path , encoding : str = " utf-8" ,
99
+ def read (module_name : Package, file_name : FileName , encoding : str = " utf-8" ,
102
100
errors : str = " strict" ) -> str :
103
101
""" Return the decoded string of the resource.
104
102
105
103
The decoding-related arguments have the same semantics as those of
106
104
bytes.decode().
107
105
"""
108
- with open (module_name, path) as file :
106
+ # Note this is **not** builtins.open()!
107
+ with open (module_name, file_name) as file :
109
108
return file .read().decode(encoding = encoding, errors = errors)
110
109
111
110
112
111
@contextlib.contextmanager
113
- def path (module_name : Package, path : Path ) -> Iterator[pathlib.Path]:
112
+ def path (module_name : Package, file_name : FileName ) -> Iterator[pathlib.Path]:
114
113
""" A context manager providing a file path object to the resource.
115
114
116
115
If the resource does not already exist on its own on the file system,
@@ -119,7 +118,7 @@ def path(module_name: Package, path: Path) -> Iterator[pathlib.Path]:
119
118
raised if the file was deleted prior to the context manager
120
119
exiting).
121
120
"""
122
- normalized_path = _normalize_path(path )
121
+ normalized_path = _normalize_path(file_name )
123
122
module = _get_package(module_name)
124
123
try :
125
124
yield pathlib.Path(module.__spec__ .resource_path(normalized_path))
@@ -143,12 +142,10 @@ side-effect of the call. The specified module is expected to be a
143
142
package, otherwise ` TypeError ` is raised. The module is expected to
144
143
have a loader specified on ` __spec__.loader ` which
145
144
146
- For the * path* argument, it is expected to be a relative path. If
147
- there are implicit references to the parent directory (i.e. ` .. ` ), they
148
- will be resolved. If the normalized, relative path attempts to reference
149
- beyond the location of the specified module, a ` ValueError ` will be
150
- raised. The provided path is expected to be UNIX-style (i.e. to use
151
- ` / ` as its path separator). Bytes-based paths are not supported.
145
+ For the * file_name* argument, it is expected to be only a file name with
146
+ no other path parts. If any parts beyond a file name are found, a ` ValueError `
147
+ will be raised. The expectation is that all data files will exist within
148
+ a directory that can be imported by Python as a package.
152
149
153
150
All functions raise ` FileNotFoundError ` if the resource does not exist.
154
151
0 commit comments