fpathlib 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fpathlib/__init__.py +15 -0
- fpathlib/__pycache__/__init__.cpython-313.pyc +0 -0
- fpathlib/__pycache__/path.cpython-313.pyc +0 -0
- fpathlib/__pycache__/utils.cpython-313.pyc +0 -0
- fpathlib/_version.py +24 -0
- fpathlib/ext/__init__.py +0 -0
- fpathlib/ext/__pycache__/__init__.cpython-313.pyc +0 -0
- fpathlib/ext/__pycache__/polars.cpython-313.pyc +0 -0
- fpathlib/ext/polars.py +116 -0
- fpathlib/path.py +309 -0
- fpathlib/utils.py +22 -0
- fpathlib-0.1.0.dist-info/METADATA +19 -0
- fpathlib-0.1.0.dist-info/RECORD +16 -0
- fpathlib-0.1.0.dist-info/WHEEL +5 -0
- fpathlib-0.1.0.dist-info/licenses/LICENSE +21 -0
- fpathlib-0.1.0.dist-info/top_level.txt +1 -0
fpathlib/__init__.py
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
fpathlib/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
fpathlib/ext/__init__.py
ADDED
|
File without changes
|
|
Binary file
|
|
Binary file
|
fpathlib/ext/polars.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from polars import *
|
|
3
|
+
import polars as pl
|
|
4
|
+
from fpathlib import expand_fpath_decorator
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def join_metadata(df, expanded_fpath):
|
|
8
|
+
return df.join(
|
|
9
|
+
expanded_fpath.to_polars(lazy=isinstance(df, pl.LazyFrame)),
|
|
10
|
+
on="fname",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@expand_fpath_decorator(post_process=join_metadata)
|
|
15
|
+
def scan_csv(expanded_fpath, *args, **kwargs):
|
|
16
|
+
"""
|
|
17
|
+
Scan the paths in the collection as CSV files, and return a
|
|
18
|
+
:obj:`polars.LazyFrame` along with the metadata captured from the path
|
|
19
|
+
variables.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
expanded_fpath : :obj:`fpathlib.ExpandedFPath`
|
|
24
|
+
An expanded f-string path.
|
|
25
|
+
*args
|
|
26
|
+
Positional arguments to pass to :meth:`polars.scan_csv`.
|
|
27
|
+
**kwargs
|
|
28
|
+
Keyword arguments to pass to :meth:`polars.scan_csv`.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
:obj:`polars.LazyFrame`
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
lf = pl.scan_csv(
|
|
36
|
+
expanded_fpath,
|
|
37
|
+
include_file_paths="fname",
|
|
38
|
+
*args,
|
|
39
|
+
**kwargs,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return lf
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@expand_fpath_decorator(post_process=join_metadata)
|
|
46
|
+
def scan_txt(
|
|
47
|
+
expanded_fpath,
|
|
48
|
+
filter_expr=None,
|
|
49
|
+
separator=None,
|
|
50
|
+
new_columns=None,
|
|
51
|
+
has_header=False,
|
|
52
|
+
*args,
|
|
53
|
+
**kwargs,
|
|
54
|
+
):
|
|
55
|
+
"""
|
|
56
|
+
Scan the paths in the collection as text files, where each line is a
|
|
57
|
+
record, and return a :obj:`polars.LazyFrame` along with the metadata
|
|
58
|
+
captured from the path. The text files can also be delimited by
|
|
59
|
+
`separator`, in which case the lines are split by the separator and each
|
|
60
|
+
field is a record. The names of these field can be set by `new_columns`.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
expanded_fpath : :obj:`fpathlib.ExpandedFPath`
|
|
65
|
+
An expanded f-string path.
|
|
66
|
+
filter_expr : :obj:`polars.Expr`, optional
|
|
67
|
+
Filter the lines before splitting by the separator (if provided).
|
|
68
|
+
separator : :obj:`str`, optional
|
|
69
|
+
Deliminatorg to split each line into fields.
|
|
70
|
+
new_columns : :obj:`list`[:obj:`str`], optional
|
|
71
|
+
List of new column names to rename the fields after splitting by the separator.
|
|
72
|
+
If not provided, the fields are named as `field_0`, `field_1`, etc.
|
|
73
|
+
has_header : :obj:`bool`
|
|
74
|
+
Whether the text files have a header line that should be skipped. The header
|
|
75
|
+
must have the same delimiter as the separator provided in `separator`.
|
|
76
|
+
(Default: False)
|
|
77
|
+
*args
|
|
78
|
+
Positional arguments to pass to :meth:`polars.scan_csv`.
|
|
79
|
+
**kwargs
|
|
80
|
+
Keyword arguments to pass to :meth:`polars.scan_csv`.
|
|
81
|
+
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
:obj:`polars.LazyFrame`
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
# TODO there are forbidden variables that should not be in expanded_fpath
|
|
88
|
+
# such as 'line' and 'fields' and 'fname'
|
|
89
|
+
|
|
90
|
+
lf = pl.scan_csv(
|
|
91
|
+
expanded_fpath,
|
|
92
|
+
include_file_paths="fname",
|
|
93
|
+
separator="\n",
|
|
94
|
+
new_columns=["line"],
|
|
95
|
+
has_header=False,
|
|
96
|
+
**kwargs,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if filter_expr is not None:
|
|
100
|
+
lf = lf.filter(filter_expr)
|
|
101
|
+
|
|
102
|
+
if separator is not None:
|
|
103
|
+
lf = lf.with_columns(fields=pl.col("line").str.split(separator, literal=False))
|
|
104
|
+
n_fields = lf.select(pl.col("fields").list.len().unique()).collect()
|
|
105
|
+
if n_fields.shape[0] > 1:
|
|
106
|
+
msg = "number of fields must be consistent across all lines", n_fields
|
|
107
|
+
raise ValueError(msg)
|
|
108
|
+
n_fields = n_fields.item()
|
|
109
|
+
lf = lf.with_columns(
|
|
110
|
+
[pl.col("fields").list.get(i).alias(f"field_{i}") for i in range(n_fields)]
|
|
111
|
+
).drop(["fields", "line"])
|
|
112
|
+
|
|
113
|
+
if new_columns is not None:
|
|
114
|
+
lf = lf.rename({f"field_{i}": col for i, col in enumerate(new_columns)})
|
|
115
|
+
|
|
116
|
+
return lf
|
fpathlib/path.py
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from functools import wraps
|
|
3
|
+
from glob import glob
|
|
4
|
+
import parse
|
|
5
|
+
import pathlib
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from .utils import import_optional_dependency
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Path(pathlib.Path):
|
|
12
|
+
"""
|
|
13
|
+
:obj:`Path` is simply :obj:`pathlib.Path` with metadata.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
*pathsegments
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, *pathsegments, metadata=None):
|
|
21
|
+
super().__init__(*pathsegments)
|
|
22
|
+
self.metadata = metadata
|
|
23
|
+
|
|
24
|
+
def match(self, path_patterns, case_sensitive=None):
|
|
25
|
+
"""
|
|
26
|
+
Perform :meth:`pathlib.Path.match` on several `path_patterns`.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
path_patterns : :obj:`str` or :obj:`Iterable`[:obj:`str`]
|
|
31
|
+
One or more path patterns to match against. (Default: None).
|
|
32
|
+
case_sensitive : :obj:`bool`
|
|
33
|
+
Whether to perform case-sensitive matching. If None, the default, then the
|
|
34
|
+
behavior is determined by the operating system. (Default: None).
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
:obj:`generator`[:obj:`bool`]
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
if isinstance(path_patterns, str):
|
|
42
|
+
path_patterns = [path_patterns]
|
|
43
|
+
|
|
44
|
+
for path_pattern in path_patterns:
|
|
45
|
+
yield super().match(path_pattern, case_sensitive=case_sensitive)
|
|
46
|
+
|
|
47
|
+
def match_all(self, path_patterns, case_sensitive=None):
|
|
48
|
+
"""
|
|
49
|
+
Perform :meth:`pathlib.Path.match` on several `path_patterns`. All patterns
|
|
50
|
+
must match.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
path_patterns : :obj:`str` or :obj:`Iterable`[:obj:`str`]
|
|
55
|
+
One or more path patterns to match against. (Default: None).
|
|
56
|
+
case_sensitive : :obj:`bool`
|
|
57
|
+
Whether to perform case-sensitive matching. If None, the default, then the
|
|
58
|
+
behavior is determined by the operating system. (Default: None).
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
:obj:`bool`
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
for match in self.match(path_patterns, case_sensitive=case_sensitive):
|
|
66
|
+
if not match:
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
def match_any(self, path_patterns, case_sensitive=None):
|
|
72
|
+
"""
|
|
73
|
+
Perform :meth:`pathlib.Path.match` on several `path_patterns`. Any pattern
|
|
74
|
+
must match.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
path_patterns : :obj:`str` or :obj:`Iterable`[:obj:`str`]
|
|
79
|
+
One or more path patterns to match against. (Default: None).
|
|
80
|
+
case_sensitive : :obj:`bool`
|
|
81
|
+
Whether to perform case-sensitive matching. If None, the default, then the
|
|
82
|
+
behavior is determined by the operating system. (Default: None).
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
:obj:`bool`
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
for match in self.match(path_patterns):
|
|
90
|
+
if match:
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class FPath:
|
|
97
|
+
"""
|
|
98
|
+
:obj:`FPath` is an f-string version of :obj:`Path`. This does not behave like a
|
|
99
|
+
typical Path, because typically there are several matches to the f-string Path.
|
|
100
|
+
Therefore, the :meth:`.FPath.expand` method must be used to create an
|
|
101
|
+
:obj:`ExpandedFPath` that lists all the possibilities.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
fpath : :obj:`str`
|
|
106
|
+
An f-string path, where the variables are captured and stored along the path
|
|
107
|
+
name.
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
:obj:`.FPath`
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def __init__(self, fpath):
|
|
115
|
+
self.fpath = fpath
|
|
116
|
+
|
|
117
|
+
def __repr__(self):
|
|
118
|
+
return "FPath({!r})".format(self.fpath)
|
|
119
|
+
|
|
120
|
+
def expand(self, exclude_path_patterns=None, require_metadata=True, errors="raise"):
|
|
121
|
+
"""
|
|
122
|
+
Use an f-string to extract out a collection of paths, where the f-string
|
|
123
|
+
variables are captured and stored along the path name.
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
exclude_path_patterns : :obj:`str` or :obj:`Iterable`[:obj:`str`]
|
|
128
|
+
Exclude paths that match the supplied pattern. (Default: None).
|
|
129
|
+
require_metadata : :obj:`bool`
|
|
130
|
+
Require that all paths identified must have found metadata. (Default: True).
|
|
131
|
+
errors : :obj:`str`
|
|
132
|
+
How to handle errors. If "raise", then raise an error. If "warn", then warn
|
|
133
|
+
and return an empty collection. If "ignore", then ignore the error and
|
|
134
|
+
return an empty collection. (Default: "raise").
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
:obj:`.ExpandedFPath`
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
if errors not in {"raise", "warn", "ignore"}:
|
|
142
|
+
msg = f"invalid value for 'errors': {errors}"
|
|
143
|
+
raise ValueError(msg)
|
|
144
|
+
|
|
145
|
+
parser = parse.compile(self.fpath)
|
|
146
|
+
|
|
147
|
+
paths = []
|
|
148
|
+
for fname in glob(re.sub(r"\{.*?\}", "*", self.fpath)):
|
|
149
|
+
path = Path(fname)
|
|
150
|
+
if exclude_path_patterns and path.match_any(exclude_path_patterns):
|
|
151
|
+
continue
|
|
152
|
+
path.metadata = getattr(parser.parse(fname), "named", None)
|
|
153
|
+
if require_metadata and path.metadata is None:
|
|
154
|
+
msg = f"metadata not found for '{fname}' with '{self.fpath}'"
|
|
155
|
+
raise AttributeError(msg)
|
|
156
|
+
paths.append(path)
|
|
157
|
+
|
|
158
|
+
if len(paths) == 0 and errors != "ignore":
|
|
159
|
+
msg = f"no paths found for {self.fpath.__repr__()}"
|
|
160
|
+
if errors == "raise":
|
|
161
|
+
raise IOError(msg)
|
|
162
|
+
elif errors == "warn":
|
|
163
|
+
import warnings
|
|
164
|
+
|
|
165
|
+
warnings.warn(msg)
|
|
166
|
+
|
|
167
|
+
return ExpandedFPath(paths=paths, fpath=self)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# TODO does scan_csv belong here?
|
|
171
|
+
class ExpandedFPath(Sequence):
|
|
172
|
+
"""
|
|
173
|
+
:obj:`ExpandedFPath` is the result of expanding an :obj:`FPath`. It is a
|
|
174
|
+
collection of :obj:`Path` objects that match the f-string pattern, and their
|
|
175
|
+
associated metadata.
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
paths : :obj:`Iterable`[:obj:`Path`]
|
|
180
|
+
A collection of :obj:`Path` objects that match the f-string pattern, and their
|
|
181
|
+
associated metadata.
|
|
182
|
+
fpath : :obj:`FPath`
|
|
183
|
+
The original :obj:`FPath` that was expanded to create this
|
|
184
|
+
:obj:`ExpandedFPath`.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
def __init__(self, paths, fpath):
|
|
188
|
+
if len(paths) != len(set(paths)):
|
|
189
|
+
raise AttributeError("paths are not unique")
|
|
190
|
+
self.paths = paths
|
|
191
|
+
self.fpath = fpath
|
|
192
|
+
|
|
193
|
+
def __getitem__(self, item):
|
|
194
|
+
return self.paths[item]
|
|
195
|
+
|
|
196
|
+
def __len__(self):
|
|
197
|
+
return len(self.paths)
|
|
198
|
+
|
|
199
|
+
def __repr__(self):
|
|
200
|
+
n = len(self)
|
|
201
|
+
|
|
202
|
+
txt = f"{self.fpath.__repr__()}\n\n"
|
|
203
|
+
txt += f"{n} matches found:\n\n"
|
|
204
|
+
|
|
205
|
+
for path in self.paths[:10]:
|
|
206
|
+
txt += f"{repr(path)}\n"
|
|
207
|
+
if n > 10:
|
|
208
|
+
txt += "...\n"
|
|
209
|
+
|
|
210
|
+
return txt
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def metadata(self):
|
|
214
|
+
"""
|
|
215
|
+
Return the metadata for each path in the collection.
|
|
216
|
+
|
|
217
|
+
Returns
|
|
218
|
+
-------
|
|
219
|
+
:obj:`dict`[:obj:`str`, :obj:`dict`]
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
metadata = {path: path.metadata for path in self.paths}
|
|
223
|
+
return metadata
|
|
224
|
+
|
|
225
|
+
# TODO fix value types?
|
|
226
|
+
def to_polars(self, lazy=False):
|
|
227
|
+
"""
|
|
228
|
+
Convert the metadata for each path in the collection to a polars DataFrame.
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
lazy : :obj:`bool`
|
|
233
|
+
Return a lazy polars DataFrame? (Default: False).
|
|
234
|
+
|
|
235
|
+
Returns
|
|
236
|
+
-------
|
|
237
|
+
:obj:`polars.DataFrame` or :obj:`polars.LazyFrame`
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
pl = import_optional_dependency("polars")
|
|
241
|
+
|
|
242
|
+
data = [{"fname": str(key), **value} for key, value in self.metadata.items()]
|
|
243
|
+
df = pl.DataFrame(data)
|
|
244
|
+
if lazy:
|
|
245
|
+
df = df.lazy()
|
|
246
|
+
return df
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def expand_fpath(fpath, *, exclude_path_patterns=None, require_metadata=True):
|
|
250
|
+
"""
|
|
251
|
+
Use an f-string to extract out a collection of paths, where the f-string variables
|
|
252
|
+
are captured and stored along the path name. This is a convenience function that
|
|
253
|
+
simply creates an :obj:`FPath` and calls its :meth:`.expand` method.
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
fpath : :obj:`str`
|
|
258
|
+
An f-string path, where the variables are captured and stored along the path
|
|
259
|
+
name.
|
|
260
|
+
exclude_path_patterns : :obj:`str` or :obj:`Iterable`[:obj:`str`]
|
|
261
|
+
Exclude paths that match the supplied pattern. (Default: None).
|
|
262
|
+
require_metadata : :obj:`bool`
|
|
263
|
+
Require that all paths identified must have found metadata. (Default: True).
|
|
264
|
+
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
:obj:`.ExpandedFPath`
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
return FPath(fpath).expand(
|
|
271
|
+
exclude_path_patterns=exclude_path_patterns,
|
|
272
|
+
require_metadata=require_metadata,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def expand_fpath_decorator(f=None, post_process=None):
|
|
277
|
+
"""
|
|
278
|
+
Decorator for :func:`.expand_fpath`.
|
|
279
|
+
|
|
280
|
+
Parameters
|
|
281
|
+
----------
|
|
282
|
+
f : :obj:`callable`
|
|
283
|
+
A function that takes an :ref:`.ExpandedFPath` as its first argument.
|
|
284
|
+
post_process : :obj:`callable`
|
|
285
|
+
A function that takes the output of `f` and the :ref:`.ExpandedFPath`.
|
|
286
|
+
(Default: None).
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
def decorator(f):
|
|
290
|
+
@wraps(f)
|
|
291
|
+
def wrapper(fpath, *args, **kwargs):
|
|
292
|
+
exclude_path_patterns = kwargs.pop("exclude_path_patterns", None)
|
|
293
|
+
require_metadata = kwargs.pop("require_metadata", True)
|
|
294
|
+
expanded_fpath = expand_fpath(
|
|
295
|
+
fpath,
|
|
296
|
+
exclude_path_patterns=exclude_path_patterns,
|
|
297
|
+
require_metadata=require_metadata,
|
|
298
|
+
)
|
|
299
|
+
result = f(expanded_fpath, *args, **kwargs)
|
|
300
|
+
if post_process is not None:
|
|
301
|
+
result = post_process(result, expanded_fpath)
|
|
302
|
+
return result
|
|
303
|
+
|
|
304
|
+
return wrapper
|
|
305
|
+
|
|
306
|
+
if f is None:
|
|
307
|
+
return decorator
|
|
308
|
+
else:
|
|
309
|
+
return decorator(f)
|
fpathlib/utils.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from importlib import import_module
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def import_optional_dependency(package):
|
|
5
|
+
"""
|
|
6
|
+
Try to import `package`. If it is not installed, raise an ImportError.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
package : :obj:`str`
|
|
11
|
+
The name of the package to import.
|
|
12
|
+
|
|
13
|
+
Returns
|
|
14
|
+
-------
|
|
15
|
+
:obj:`module`
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
return import_module(package)
|
|
20
|
+
except ModuleNotFoundError:
|
|
21
|
+
msg = f"optional dependency '{package}' is not installed; please install it and try again."
|
|
22
|
+
raise ModuleNotFoundError(msg) from None
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fpathlib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A package to combine paths with metadata
|
|
5
|
+
Author-email: "C. Lockhart" <clockha2@gmu.edu>
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: parse
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest; extra == "dev"
|
|
12
|
+
Requires-Dist: black; extra == "dev"
|
|
13
|
+
Requires-Dist: sphinx; extra == "dev"
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
fpathlib
|
|
17
|
+
========
|
|
18
|
+
|
|
19
|
+
A Python package for adding metadata to file paths.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
fpathlib/__init__.py,sha256=BQhOA7UY0wS0OfGlw5T9bbNaYZlcb7nKiA0q5F89ofo,227
|
|
2
|
+
fpathlib/_version.py,sha256=n_5vdJsPNu7wZ57LGuRL585uvll-hiuvZUBWzdG0RQU,520
|
|
3
|
+
fpathlib/path.py,sha256=J9T0exhefoIUywynkOZGLnpArOERVVwA7KfaYinRxD4,9505
|
|
4
|
+
fpathlib/utils.py,sha256=os9ggFllqOZXYcjfKcxPIy92LuRfvt6jWL0gNfet1N0,545
|
|
5
|
+
fpathlib/__pycache__/__init__.cpython-313.pyc,sha256=YqTERPaAJJgHJ5kCpVw6c92d1Me7gfudZZ8rUAxKo50,321
|
|
6
|
+
fpathlib/__pycache__/path.cpython-313.pyc,sha256=qlcwnudDL4jkFkCOVHdV14W-NTByOnHWdjvV5iIjc4I,11533
|
|
7
|
+
fpathlib/__pycache__/utils.cpython-313.pyc,sha256=V4KSHMjm3ePQfaOKWwAkmaocvYb4VOul6Zooh0EvLa0,772
|
|
8
|
+
fpathlib/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
fpathlib/ext/polars.py,sha256=_TBjWt61e8miV5W_5UXcjdm67PvPnndWnFKa9K8dfTM,3633
|
|
10
|
+
fpathlib/ext/__pycache__/__init__.cpython-313.pyc,sha256=lrgZbujuH_tlWGfe_Huo46srJdYJd1NQ6ZQFWamc6tc,153
|
|
11
|
+
fpathlib/ext/__pycache__/polars.cpython-313.pyc,sha256=_Xz06TsHan10-xO87r7-AeT2S7OEKB-GTjIFnuXIuJA,4626
|
|
12
|
+
fpathlib-0.1.0.dist-info/licenses/LICENSE,sha256=76rZ9i4ZumZ1M4jlYM9ZDOTYEeyzuxdsTKjF2qha3Lc,1069
|
|
13
|
+
fpathlib-0.1.0.dist-info/METADATA,sha256=7bPl-Pu-RKuUVRophpn8LdoVxzZ_0ARwISM0Q9PcnQY,482
|
|
14
|
+
fpathlib-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
15
|
+
fpathlib-0.1.0.dist-info/top_level.txt,sha256=QT5pDgO4--AJn7X4RB51xpNTt0pD5_U2mCjsydpQAQw,9
|
|
16
|
+
fpathlib-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lockhart Lab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fpathlib
|