immlib 1.0.0.dev2__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.
- immlib/__init__.py +131 -0
- immlib/_init.py +108 -0
- immlib/_version.py +235 -0
- immlib/doc/__init__.py +38 -0
- immlib/doc/_core.py +311 -0
- immlib/iolib/__init__.py +29 -0
- immlib/iolib/_core.py +720 -0
- immlib/pathlib/__init__.py +69 -0
- immlib/pathlib/_cache.py +152 -0
- immlib/pathlib/_core.py +869 -0
- immlib/pathlib/_osf.py +538 -0
- immlib/test/__init__.py +16 -0
- immlib/test/__main__.py +10 -0
- immlib/test/doc/__init__.py +6 -0
- immlib/test/doc/test_core.py +91 -0
- immlib/test/iolib/__init__.py +7 -0
- immlib/test/iolib/test_core.py +81 -0
- immlib/test/pathlib/__init__.py +11 -0
- immlib/test/pathlib/test_core.py +146 -0
- immlib/test/pathlib/test_osf.py +54 -0
- immlib/test/types/__init__.py +5 -0
- immlib/test/types/test_core.py +110 -0
- immlib/test/util/__init__.py +11 -0
- immlib/test/util/test_core.py +681 -0
- immlib/test/util/test_numeric.py +1374 -0
- immlib/test/util/test_quantity.py +218 -0
- immlib/test/util/test_url.py +51 -0
- immlib/test/workflow/__init__.py +9 -0
- immlib/test/workflow/test_core.py +418 -0
- immlib/test/workflow/test_plantype.py +248 -0
- immlib/types/__init__.py +29 -0
- immlib/types/_core.py +333 -0
- immlib/util/__init__.py +283 -0
- immlib/util/_core.py +2524 -0
- immlib/util/_numeric.py +2651 -0
- immlib/util/_quantity.py +523 -0
- immlib/util/_url.py +114 -0
- immlib/workflow/__init__.py +48 -0
- immlib/workflow/_core.py +1635 -0
- immlib/workflow/_plantype.py +334 -0
- immlib-1.0.0.dev2.dist-info/METADATA +76 -0
- immlib-1.0.0.dev2.dist-info/RECORD +45 -0
- immlib-1.0.0.dev2.dist-info/WHEEL +5 -0
- immlib-1.0.0.dev2.dist-info/licenses/LICENSE +21 -0
- immlib-1.0.0.dev2.dist-info/top_level.txt +1 -0
immlib/pathlib/_core.py
ADDED
|
@@ -0,0 +1,869 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
###############################################################################
|
|
3
|
+
# immlib/pathlib/_core.py
|
|
4
|
+
|
|
5
|
+
# Dependencies ################################################################
|
|
6
|
+
|
|
7
|
+
from platform import platform
|
|
8
|
+
from urllib.parse import urlparse
|
|
9
|
+
from collections import namedtuple
|
|
10
|
+
from pathlib import (Path, PurePath)
|
|
11
|
+
from os import (PathLike, fspath)
|
|
12
|
+
|
|
13
|
+
from pcollections import (ldict, lazy)
|
|
14
|
+
from cloudpathlib import (
|
|
15
|
+
CloudPath,
|
|
16
|
+
S3Path, AzureBlobPath, GSPath,
|
|
17
|
+
S3Client, AzureBlobClient, GSClient)
|
|
18
|
+
|
|
19
|
+
from ..doc import docwrap
|
|
20
|
+
from ..util import (is_str, is_amap, is_aseq, strstarts, strends)
|
|
21
|
+
|
|
22
|
+
from ._osf import (OSFClient, OSFPath)
|
|
23
|
+
from ._cache import CloudCachePath
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Global Values ###############################################################
|
|
27
|
+
|
|
28
|
+
# The set of drives, if we are in windows; this is used by the path logic to
|
|
29
|
+
# automatically determine if a path like 'c:/a/b/c' is a valid path.
|
|
30
|
+
windows_drives = None
|
|
31
|
+
if 'windows' in platform().lower():
|
|
32
|
+
from pathlib import WindowsPath
|
|
33
|
+
from ctypes import cdll
|
|
34
|
+
_ord_a = ord('a')
|
|
35
|
+
def is_windows_drive(letter):
|
|
36
|
+
try:
|
|
37
|
+
logical_drives = cdll.kernel32.GetLogicalDrives()
|
|
38
|
+
bitno_letter = ord(letter.lower()) - _ord_a
|
|
39
|
+
return bool((logical_drives >> bitno_letter) & 0x01)
|
|
40
|
+
except Exception:
|
|
41
|
+
return False
|
|
42
|
+
else:
|
|
43
|
+
def is_windows_drive(letter):
|
|
44
|
+
return False
|
|
45
|
+
is_windows_drive.__doc__ = \
|
|
46
|
+
"""Determines whether a given string represents a valid Windows drive.
|
|
47
|
+
|
|
48
|
+
On non-windows platforms, ``is_windows_drive`` always returns ``False``. It
|
|
49
|
+
is additionally, possible that ``is_windows_drive`` will always return
|
|
50
|
+
false for certain Windows systems. The function requires the the
|
|
51
|
+
``ctypes.cdll.kernel32`` object contain the ``GetLogicalDrives`` function
|
|
52
|
+
and that the substring 'windows' appear in the ``platform`` package's
|
|
53
|
+
``platform.platform().lower()`` string.
|
|
54
|
+
|
|
55
|
+
If the above requirements are met, then ``is_windows_drive(letter)`` will
|
|
56
|
+
return ``True`` is ``letter`` is the letter of a valid drive and returns
|
|
57
|
+
``False`` otherwise. Case is ignored.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# General Utilities ###########################################################
|
|
62
|
+
|
|
63
|
+
@docwrap
|
|
64
|
+
def pathstr(obj):
|
|
65
|
+
"""Returns a string or bytes representation of a path.
|
|
66
|
+
|
|
67
|
+
``pathstr(obj)`` returns ``obj`` itself if ``obj`` is either a ``str`` or
|
|
68
|
+
``bytes`` object. If ``obj`` is a ``CloudPath`` object, then ``str(obj)``
|
|
69
|
+
is returned. Otherwise, If ``obj`` is a ``PathLike`` object, then
|
|
70
|
+
``os.fspath(obj)`` is returned.
|
|
71
|
+
"""
|
|
72
|
+
return str(obj) if isinstance(obj, CloudPath) else fspath(obj)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# OSFPath Functions ###########################################################
|
|
76
|
+
|
|
77
|
+
@docwrap
|
|
78
|
+
def is_osfpath(obj):
|
|
79
|
+
"""Detects whether the input is an ``OSFPath`` object.
|
|
80
|
+
|
|
81
|
+
``is_osfpath(obj)`` returns ``True`` if ``obj`` is an instance of the
|
|
82
|
+
``OSFPath`` class and ``False`` otherwise.
|
|
83
|
+
|
|
84
|
+
See also: ``like_osfpath``
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
obj : object
|
|
89
|
+
The object whose membership in the ``OSFPath`` class is to be
|
|
90
|
+
determined.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
boolean
|
|
95
|
+
``True`` if ``obj`` is an instance of ``OSFPath`` and ``False``
|
|
96
|
+
otherwise.
|
|
97
|
+
"""
|
|
98
|
+
return isinstance(obj, OSFPath)
|
|
99
|
+
@docwrap
|
|
100
|
+
def like_osfpath(obj):
|
|
101
|
+
"""Detects whether the input can be converted into an ``OSFPath`` object.
|
|
102
|
+
|
|
103
|
+
``like_osfpath(obj)`` returns ``True`` if ``obj`` is an instance of the
|
|
104
|
+
``OSFPath`` class or is a string that forms a valid OSF path, and ``False``
|
|
105
|
+
otherwise.
|
|
106
|
+
|
|
107
|
+
See also: ``is_osfpath``
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
obj : object
|
|
112
|
+
The object whose ability to be converted into an ``OSFPath`` instance
|
|
113
|
+
is to be determined.
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
boolean
|
|
118
|
+
``True`` if ``obj`` is an instance of ``OSFPath`` or is a string that
|
|
119
|
+
could be converted into an ``OSFPath`` and ``False`` otherwise.
|
|
120
|
+
"""
|
|
121
|
+
if isinstance(obj, OSFPath):
|
|
122
|
+
return True
|
|
123
|
+
url = urlparse(pathstr(obj))
|
|
124
|
+
return bool(url.scheme == 'osf' and url.netloc)
|
|
125
|
+
@docwrap
|
|
126
|
+
def osfpath(obj, *args,
|
|
127
|
+
client=None,
|
|
128
|
+
cache_path=Ellipsis,
|
|
129
|
+
file_cache_mode=Ellipsis,
|
|
130
|
+
mkdir_mode=Ellipsis,
|
|
131
|
+
pagesize=Ellipsis,
|
|
132
|
+
local_cache_dir=None):
|
|
133
|
+
"""Creates and returns an ``OSFPath`` representing an OSF.io repository.
|
|
134
|
+
|
|
135
|
+
``osfpath(p)`` creates and returns an ``OSFPath`` object, which is a type
|
|
136
|
+
of ``cloudpathlib.CloudPath`` object, from the path or path-string
|
|
137
|
+
``p``. If ``p`` is an ``OSFPath``, then it is returned as-is. Otherwise
|
|
138
|
+
``str(p)`` is converted into an ``OSFPath``; ``str(p)`` may start with
|
|
139
|
+
``'osf://'`` (not case-sensitive) or, if it does not have a scheme
|
|
140
|
+
specifier, ``'osf://'`` will be prepended to it.
|
|
141
|
+
|
|
142
|
+
``osfpath(p, a1, a2...)`` converts ``p`` into an ``OSFPath`` then joins the
|
|
143
|
+
``a1``, ``a2``, etc. values to the end of the path and returns the joined
|
|
144
|
+
path.
|
|
145
|
+
|
|
146
|
+
OSF paths take the format ``'osf://<project-ID>:<storage>/<path>'`` where
|
|
147
|
+
the storage is optional (defaulting to ``'osfstorage'``) and an empty path
|
|
148
|
+
refers to the project storage's root. The project-ID is the code used to
|
|
149
|
+
find the project online. For example, the webpage reached at the website
|
|
150
|
+
``https://osf.io/tery8/`` is the project page for the project whose ID is
|
|
151
|
+
``tery8``.
|
|
152
|
+
|
|
153
|
+
If any of the optional keyword arguments are given, then the returned path
|
|
154
|
+
will always use the specified options; a new path is returned with updated
|
|
155
|
+
options if necessary; this is done before joining paths if multiple
|
|
156
|
+
positional arguments are given. If the ``client`` keyword is given, then it
|
|
157
|
+
is modified by the keyword options before being used in the path. All
|
|
158
|
+
optional keyword arguments have a default value of ``Ellipsis``, which
|
|
159
|
+
indicates that the value of the ``client`` for that option should be used.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
obj : path-like
|
|
164
|
+
The path or path-like object to convert into an OSFPath, typically a
|
|
165
|
+
string.
|
|
166
|
+
client : OSFClient or None, optional
|
|
167
|
+
The ``OSFClient`` object to use. The ``OSFClient`` is responsible
|
|
168
|
+
primarily for the caching of data locally. If ``OSFClient`` is
|
|
169
|
+
``None``, then an ``OSFClient`` object is created for the project using
|
|
170
|
+
a temporary cache directory.
|
|
171
|
+
cache_path : path-like or None, optional
|
|
172
|
+
The local directory in which cache files should be stored. This option
|
|
173
|
+
is ignored if ``client`` is not ``None``; otherwise it is passed to the
|
|
174
|
+
created client object. The cache directory is the root cache directory
|
|
175
|
+
for the entire OSF project.
|
|
176
|
+
file_cache_mode : cloudpathlib.enums.FileCacheMode, optional
|
|
177
|
+
How often to clear the file cache; see [cloudpathlib's caching
|
|
178
|
+
docs](https://cloudpathlib.drivendata.org/stable/caching/) for more
|
|
179
|
+
information about the options in ``cloudpathlib.enums.FileCacheMode``.
|
|
180
|
+
mkdir_mode : int, optional
|
|
181
|
+
The mode to use when making directories in the cache. By default this
|
|
182
|
+
is ``0o775``. This option is ignored if the ``client`` option is not
|
|
183
|
+
``None``.
|
|
184
|
+
pagesize : int, optional
|
|
185
|
+
The number of items to include in a single page when paging directory
|
|
186
|
+
contents from the OSF server. The default is 100. This option is
|
|
187
|
+
ignored if the ``client`` option is not ``None``.
|
|
188
|
+
"""
|
|
189
|
+
if isinstance(obj, OSFPath):
|
|
190
|
+
# We may want to grab some options out of the argument in this case.
|
|
191
|
+
if client is None:
|
|
192
|
+
client = obj.client
|
|
193
|
+
else:
|
|
194
|
+
# If there's no '://' in the path, then we add 'osf://' to the front.
|
|
195
|
+
obj = pathstr(obj)
|
|
196
|
+
if scheme_sep not in obj:
|
|
197
|
+
obj = 'osf://' + obj
|
|
198
|
+
# If the cache_path is Ellipsis and there's no client provided, then we
|
|
199
|
+
# want to use pimm's default cache path
|
|
200
|
+
if local_cache_dir is None:
|
|
201
|
+
if client is None:
|
|
202
|
+
if cache_path is Ellipsis:
|
|
203
|
+
cache_path = None #TODO
|
|
204
|
+
if cache_path is not None:
|
|
205
|
+
local_cache_dir = Path(cache_path).expanduser() / 'osf'
|
|
206
|
+
# At this point we can go ahead and create the initial path.
|
|
207
|
+
path = OSFPath(obj,
|
|
208
|
+
client=client,
|
|
209
|
+
local_cache_dir=local_cache_dir,
|
|
210
|
+
file_cache_mode=file_cache_mode,
|
|
211
|
+
mkdir_mode=mkdir_mode,
|
|
212
|
+
pagesize=pagesize)
|
|
213
|
+
# If there were additional arguments, append them to the path now.
|
|
214
|
+
for arg in args:
|
|
215
|
+
path = path / arg
|
|
216
|
+
# Return the path.
|
|
217
|
+
return path
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# S3Path Functions ############################################################
|
|
221
|
+
|
|
222
|
+
@docwrap
|
|
223
|
+
def is_s3path(obj):
|
|
224
|
+
"""Detects whether the input is an ``S3Path`` object.
|
|
225
|
+
|
|
226
|
+
``is_s3path(obj)`` returns ``True`` if ``obj`` is an instance of the
|
|
227
|
+
``S3Path`` class and ``False`` otherwise.
|
|
228
|
+
|
|
229
|
+
See also: ``like_s3path``
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
obj : object
|
|
234
|
+
The object whose membership in the ``S3Path`` class is to be
|
|
235
|
+
determined.
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
boolean
|
|
240
|
+
``True`` if ``obj`` is an instance of ``S3Path`` and ``False``
|
|
241
|
+
otherwise.
|
|
242
|
+
"""
|
|
243
|
+
return isinstance(obj, S3Path)
|
|
244
|
+
@docwrap
|
|
245
|
+
def like_s3path(obj):
|
|
246
|
+
"""Detects whether the input can be converted into an ``S3Path`` object.
|
|
247
|
+
|
|
248
|
+
``like_s3path(obj)`` returns ``True`` if ``obj`` is an instance of the
|
|
249
|
+
``S3Path`` class or is a string that forms a valid S3 path, and ``False``
|
|
250
|
+
otherwise.
|
|
251
|
+
|
|
252
|
+
See also: ``is_s3path``
|
|
253
|
+
|
|
254
|
+
Parameters
|
|
255
|
+
----------
|
|
256
|
+
obj : object
|
|
257
|
+
The object whose ability to be converted into an ``S3Path`` instance is
|
|
258
|
+
to be determined.
|
|
259
|
+
|
|
260
|
+
Returns
|
|
261
|
+
-------
|
|
262
|
+
boolean
|
|
263
|
+
``True`` if ``obj`` is an instance of ``S3Path`` or is a string that
|
|
264
|
+
could be converted into an ``S3Path`` and ``False`` otherwise.
|
|
265
|
+
"""
|
|
266
|
+
if isinstance(obj, S3Path):
|
|
267
|
+
return True
|
|
268
|
+
url = urlparse(pathstr(obj))
|
|
269
|
+
return bool(url.scheme == 's3' and url.netloc)
|
|
270
|
+
@docwrap
|
|
271
|
+
def s3path(obj, *args, **kwargs):
|
|
272
|
+
"""Creates and returns an ``S3Path`` representing an AWS S3 repository.
|
|
273
|
+
|
|
274
|
+
``s3path(p)`` creates and returns an ``S3Path`` object, which is a type of
|
|
275
|
+
``cloudpathlib.CloudPath`` object, from the path or path-string ``p``. If
|
|
276
|
+
``p`` is an ``S3Path``, then it is returned as-is. Otherwise ``pathstr(p)``
|
|
277
|
+
is converted into an ``S3Path``; ``pathstr(p)`` may start with ``'s3://'``
|
|
278
|
+
(not case-sensitive) or, if it does not have a scheme specifier,
|
|
279
|
+
``'s3://'`` will be prepended to it.
|
|
280
|
+
|
|
281
|
+
``s3path(p, a1, a2...)`` converts ``p`` into an ``S3Path`` then joins the
|
|
282
|
+
``a1``, ``a2``, etc. values to the end of the path and returns the joined
|
|
283
|
+
path.
|
|
284
|
+
|
|
285
|
+
The ``s3path`` function accepts all the optional arguments of the
|
|
286
|
+
``S3Client`` type from ``cloudpathlib`` as well as the ``client``
|
|
287
|
+
option. If the ``client`` option is given along with additional optional
|
|
288
|
+
arguments, then the optional arguments are ignored.
|
|
289
|
+
|
|
290
|
+
Additionally, ``s3path`` parses the option ``cache_path``, which is not
|
|
291
|
+
normally accepted by ``S3Path``, which instead requires the option
|
|
292
|
+
``local_cache_dir``. Any time that a ``local_cache_dir`` is given, it
|
|
293
|
+
overrides the ``cache_path``; however, if ``local_cache_dir`` is not given
|
|
294
|
+
and ``cache_path`` is, then the directory ``os.path.join(cache_path,
|
|
295
|
+
"s3")`` is given as the ``local_cache_dir`` option.
|
|
296
|
+
"""
|
|
297
|
+
# Extract a few options.
|
|
298
|
+
client = kwargs.pop('client', None)
|
|
299
|
+
cache_path = kwargs.pop('cache_path', None)
|
|
300
|
+
lcd = kwargs.get('local_cache_dir', None)
|
|
301
|
+
# Do some initial configuration.
|
|
302
|
+
if isinstance(obj, S3Path):
|
|
303
|
+
# We may want to grab some options out of the argument in this case.
|
|
304
|
+
if client is None:
|
|
305
|
+
client = obj.client
|
|
306
|
+
else:
|
|
307
|
+
# If there's no '://' in the path, then we add 's3://' to the front.
|
|
308
|
+
obj = pathstr(obj)
|
|
309
|
+
if scheme_sep not in obj:
|
|
310
|
+
obj = 's3://' + obj
|
|
311
|
+
if client is None:
|
|
312
|
+
# If the cache_path is Ellipsis and there's no client provided, then we
|
|
313
|
+
# want to use pimm's default cache path.
|
|
314
|
+
if cache_path is Ellipsis and client is None:
|
|
315
|
+
cache_path = None #TODO
|
|
316
|
+
if cache_path is not None and 'local_cache_dir' not in kwargs:
|
|
317
|
+
kwargs['local_cache_dir'] = Path(cache_path).expanduser() / "s3"
|
|
318
|
+
# At this point we can go ahead and create the client object.
|
|
319
|
+
client = S3Client(**kwargs)
|
|
320
|
+
path = S3Path(obj, client=client)
|
|
321
|
+
# If there were additional arguments, append them to the path now.
|
|
322
|
+
for arg in args:
|
|
323
|
+
path = path / arg
|
|
324
|
+
# Return the path.
|
|
325
|
+
return path
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# GSPath Functions ############################################################
|
|
329
|
+
|
|
330
|
+
@docwrap
|
|
331
|
+
def is_gspath(obj):
|
|
332
|
+
"""Detects whether the input is an ``GSPath`` object.
|
|
333
|
+
|
|
334
|
+
``is_gspath(obj)`` returns ``True`` if `obj` is an instance of the
|
|
335
|
+
``GSPath`` class and ``False`` otherwise.
|
|
336
|
+
|
|
337
|
+
See also: ``like_gspath``
|
|
338
|
+
|
|
339
|
+
Parameters
|
|
340
|
+
----------
|
|
341
|
+
obj : object
|
|
342
|
+
The object whose membership in the ``GSPath`` class is to be
|
|
343
|
+
determined.
|
|
344
|
+
|
|
345
|
+
Returns
|
|
346
|
+
-------
|
|
347
|
+
boolean
|
|
348
|
+
``True`` if `obj` is an instance of ``GSPath`` and ``False``
|
|
349
|
+
otherwise.
|
|
350
|
+
|
|
351
|
+
"""
|
|
352
|
+
return isinstance(obj, GSPath)
|
|
353
|
+
@docwrap
|
|
354
|
+
def like_gspath(obj):
|
|
355
|
+
"""Detects whether the input can be converted into an ``GSPath`` object.
|
|
356
|
+
|
|
357
|
+
``like_gspath(obj)`` returns ``True`` if `obj` is an instance of the
|
|
358
|
+
``GSPath`` class or is a string that forms a valid GS path, and ``False``
|
|
359
|
+
otherwise.
|
|
360
|
+
|
|
361
|
+
See also: ``is_gspath``
|
|
362
|
+
|
|
363
|
+
Parameters
|
|
364
|
+
----------
|
|
365
|
+
obj : object
|
|
366
|
+
The object whose ability to be converted into an ``GSPath`` instance is
|
|
367
|
+
to be determined.
|
|
368
|
+
|
|
369
|
+
Returns
|
|
370
|
+
-------
|
|
371
|
+
boolean
|
|
372
|
+
``True`` if `obj` is an instance of ``GSPath`` or is a string that
|
|
373
|
+
could be converted into an ``GSPath`` and ``False`` otherwise.
|
|
374
|
+
"""
|
|
375
|
+
if isinstance(obj, GSPath):
|
|
376
|
+
return True
|
|
377
|
+
url = urlparse(pathstr(obj))
|
|
378
|
+
return bool(url.scheme == 'gs' and url.netloc)
|
|
379
|
+
@docwrap
|
|
380
|
+
def gspath(obj, *args, **kwargs):
|
|
381
|
+
"""Creates and returns an ``GSPath`` representing a Google Storage
|
|
382
|
+
repository.
|
|
383
|
+
|
|
384
|
+
``gspath(p)`` creates and returns a ``GSPath`` object, which is a type of
|
|
385
|
+
``cloudpathlib.CloudPath`` object, from the path or path-string ``p``. If
|
|
386
|
+
``p`` is a ``GSPath``, then it is returned as-is. Otherwise ``pathstr(p)``
|
|
387
|
+
is converted into an ``GSPath``; ``pathstr(p)`` may start with ``'gs://'``
|
|
388
|
+
(not case-sensitive) or, if it does not have a scheme specifier,
|
|
389
|
+
``'gs://'`` will be prepended to it.
|
|
390
|
+
|
|
391
|
+
``gspath(p, a1, a2...)`` converts ``p`` into an ``GSPath`` then joins the
|
|
392
|
+
``a1``, ``a2``, etc. values to the end of the path and returns the joined
|
|
393
|
+
path.
|
|
394
|
+
|
|
395
|
+
The ``gspath`` function accepts all the optional arguments of the
|
|
396
|
+
``GSClient`` type from ``cloudpathlib`` as well as the ``client``
|
|
397
|
+
option. If the ``client`` option is given along with additional optional
|
|
398
|
+
arguments, then the optional arguments are ignored.
|
|
399
|
+
|
|
400
|
+
Additionally, ``gspath`` parses the option ``cache_path``, which is not
|
|
401
|
+
normally accepted by ``GSPath``, which instead requires the option
|
|
402
|
+
``local_cache_dir``. Any time that a ``local_cache_dir`` is given, it
|
|
403
|
+
overrides the ``cache_path``; however, if ``local_cache_dir`` is not given
|
|
404
|
+
and ``cache_path`` is, then the directory ``os.path.join(cache_path,
|
|
405
|
+
"gs")`` is given as the ``local_cache_dir`` option.
|
|
406
|
+
"""
|
|
407
|
+
# Extract a few options.
|
|
408
|
+
client = kwargs.pop('client', None)
|
|
409
|
+
cache_path = kwargs.pop('cache_path', None)
|
|
410
|
+
lcd = kwargs.get('local_cache_dir', None)
|
|
411
|
+
# Do some initial configuration.
|
|
412
|
+
if isinstance(obj, GSPath):
|
|
413
|
+
# We may want to grab some options out of the argument in this case.
|
|
414
|
+
if client is None:
|
|
415
|
+
client = obj.client
|
|
416
|
+
else:
|
|
417
|
+
# If there's no '://' in the path, then we add 'osf://' to the front.
|
|
418
|
+
obj = pathstr(obj)
|
|
419
|
+
if scheme_sep not in obj:
|
|
420
|
+
obj = 'gs://' + obj
|
|
421
|
+
# At this point we can go ahead and create the client object.
|
|
422
|
+
if client is None:
|
|
423
|
+
# If the cache_path is Ellipsis and there's no client provided, then we
|
|
424
|
+
# want to use pimm's default cache path.
|
|
425
|
+
if cache_path is Ellipsis and client is None:
|
|
426
|
+
cache_path = None #TODO
|
|
427
|
+
if cache_path is not None and 'local_cache_dir' not in kwargs:
|
|
428
|
+
kwargs['local_cache_dir'] = Path(cache_path).expanduser() / "gs"
|
|
429
|
+
client = GSClient(**kwargs)
|
|
430
|
+
path = GSPath(obj, client=client)
|
|
431
|
+
# If there were additional arguments, append them to the path now.
|
|
432
|
+
for arg in args:
|
|
433
|
+
path = path / arg
|
|
434
|
+
# Return the path.
|
|
435
|
+
return path
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
# AzureBlobPath Functions #####################################################
|
|
439
|
+
|
|
440
|
+
@docwrap
|
|
441
|
+
def is_azpath(obj):
|
|
442
|
+
"""Detects whether the input is an ``AzureBlobPath`` object.
|
|
443
|
+
|
|
444
|
+
``is_azpath(obj)`` returns ``True`` if `obj` is an instance of the
|
|
445
|
+
``AzureBlobPath`` class and ``False`` otherwise.
|
|
446
|
+
|
|
447
|
+
See also: ``like_azpath``
|
|
448
|
+
|
|
449
|
+
Parameters
|
|
450
|
+
----------
|
|
451
|
+
obj : object
|
|
452
|
+
The object whose membership in the ``AzureBlobPath`` class is to be
|
|
453
|
+
determined.
|
|
454
|
+
|
|
455
|
+
Returns
|
|
456
|
+
-------
|
|
457
|
+
boolean
|
|
458
|
+
``True`` if `obj` is an instance of ``AzureBlobPath`` and ``False``
|
|
459
|
+
otherwise.
|
|
460
|
+
"""
|
|
461
|
+
return isinstance(obj, AzureBlobPath)
|
|
462
|
+
@docwrap
|
|
463
|
+
def like_azpath(obj):
|
|
464
|
+
"""Detects whether an input can be converted into an ``AzureBlobPath``
|
|
465
|
+
object.
|
|
466
|
+
|
|
467
|
+
``like_azpath(obj)`` returns ``True`` if `obj` is an instance of the
|
|
468
|
+
``AzureBlobPath`` class or is a string that forms a valid Azure path, and
|
|
469
|
+
``False`` otherwise.
|
|
470
|
+
|
|
471
|
+
See also: ``is_azpath``
|
|
472
|
+
|
|
473
|
+
Parameters
|
|
474
|
+
----------
|
|
475
|
+
obj : object
|
|
476
|
+
The object whose ability to be converted into an ``AzureBlobPath``
|
|
477
|
+
instance is to be determined.
|
|
478
|
+
|
|
479
|
+
Returns
|
|
480
|
+
-------
|
|
481
|
+
boolean
|
|
482
|
+
``True`` if `obj` is an instance of ``AzureBlobPath`` or is a string
|
|
483
|
+
that could be converted into an ``AzureBlobPath`` and ``False``
|
|
484
|
+
otherwise.
|
|
485
|
+
"""
|
|
486
|
+
if isinstance(obj, AzureBlobPath):
|
|
487
|
+
return True
|
|
488
|
+
url = urlparse(pathstr(obj))
|
|
489
|
+
return bool(url.scheme == 'az' and url.netloc)
|
|
490
|
+
@docwrap
|
|
491
|
+
def azpath(obj, *args, **kwargs):
|
|
492
|
+
"""Creates and returns an ``AzureBlobPath`` representing an Azure
|
|
493
|
+
repository.
|
|
494
|
+
|
|
495
|
+
``azpath(p)`` creates and returns an ``AzureBlobPath`` object, which is a
|
|
496
|
+
type of ``cloudpathlib.CloudPath`` object, from the path or path-string
|
|
497
|
+
``p``. If ``p`` is an ``AzureBlobPath``, then it is returned
|
|
498
|
+
as-is. Otherwise ``pathstr(p)`` is converted into an ``AzureBlobPath``;
|
|
499
|
+
``pathstr(p)`` may start with ``'az://'`` (not case-sensitive) or, if it
|
|
500
|
+
does not have a scheme specifier, ``'az://'`` will be prepended to it.
|
|
501
|
+
|
|
502
|
+
``azpath(p, a1, a2...)`` converts ``p`` into an ``AzureBlobPath`` then
|
|
503
|
+
joins the ``a1``, ``a2``, etc. values to the end of the path and returns
|
|
504
|
+
the joined path.
|
|
505
|
+
|
|
506
|
+
The ``azpath`` function accepts all the optional arguments of the
|
|
507
|
+
``AzureBlobClient`` type from ``cloudpathlib`` as well as the ``client``
|
|
508
|
+
option. If the ``client`` option is given along with additional optional
|
|
509
|
+
arguments, then the optional arguments are ignored.
|
|
510
|
+
|
|
511
|
+
Additionally, ``azpath`` parses the option ``cache_path``, which is not
|
|
512
|
+
normally accepted by ``AzureBlobPath``, which instead requires the option
|
|
513
|
+
``local_cache_dir``. Any time that a ``local_cache_dir`` is given, it
|
|
514
|
+
overrides the ``cache_path``; however, if ``local_cache_dir`` is not given
|
|
515
|
+
and ``cache_path`` is, then the directory ``os.path.join(cache_path,"az")``
|
|
516
|
+
is given as the ``local_cache_dir`` option.
|
|
517
|
+
"""
|
|
518
|
+
# Extract a few options.
|
|
519
|
+
client = kwargs.pop('client', None)
|
|
520
|
+
cache_path = kwargs.pop('cache_path', None)
|
|
521
|
+
lcd = kwargs.get('local_cache_dir', None)
|
|
522
|
+
# Do some initial configuration.
|
|
523
|
+
if isinstance(obj, AzureBlobPath):
|
|
524
|
+
# We may want to grab some options out of the argument in this case.
|
|
525
|
+
if client is None:
|
|
526
|
+
client = obj.client
|
|
527
|
+
else:
|
|
528
|
+
# If there's no '://' in the path, then we add 'osf://' to the front.
|
|
529
|
+
obj = pathstr(obj)
|
|
530
|
+
if scheme_sep not in obj:
|
|
531
|
+
obj = 'az://' + obj
|
|
532
|
+
if client is None:
|
|
533
|
+
# If the cache_path is Ellipsis and there's no client provided, then we
|
|
534
|
+
# want to use pimm's default cache path.
|
|
535
|
+
if cache_path is Ellipsis and client is None:
|
|
536
|
+
cache_path = None #TODO
|
|
537
|
+
if cache_path is not None and 'local_cache_dir' not in kwargs:
|
|
538
|
+
kwargs['local_cache_dir'] = Path(cache_path).expanduser() / "az"
|
|
539
|
+
# At this point we can go ahead and create the client object.
|
|
540
|
+
client = AzureBlobClient(**kwargs)
|
|
541
|
+
path = AzureBlobPath(obj, client=client)
|
|
542
|
+
# If there were additional arguments, append them to the path now.
|
|
543
|
+
for arg in args:
|
|
544
|
+
path = path / arg
|
|
545
|
+
# Return the path.
|
|
546
|
+
return path
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
# Filesystem Paths ############################################################
|
|
550
|
+
|
|
551
|
+
@docwrap
|
|
552
|
+
def is_filepath(p):
|
|
553
|
+
"""Detects whether an object is a filesystem ``Path`` object.
|
|
554
|
+
|
|
555
|
+
Any object that inherits from the ``Path`` type is considered a filesystem
|
|
556
|
+
path. File-paths correspond to URLs that begin with ``'file://'``.
|
|
557
|
+
|
|
558
|
+
Parameters
|
|
559
|
+
----------
|
|
560
|
+
p : path-like
|
|
561
|
+
The object whose quality as a path is to be assessed.
|
|
562
|
+
|
|
563
|
+
Returns
|
|
564
|
+
-------
|
|
565
|
+
boolean
|
|
566
|
+
``True`` if `p` is an instance of the ``Path`` type and ``False``
|
|
567
|
+
otherwise.
|
|
568
|
+
"""
|
|
569
|
+
return isinstance(p, Path)
|
|
570
|
+
@docwrap
|
|
571
|
+
def like_filepath(obj):
|
|
572
|
+
"""Detects whether an input can be converted into a ``Path`` object.
|
|
573
|
+
|
|
574
|
+
``like_filepath(obj)`` returns ``True`` if `obj` is an instance of the
|
|
575
|
+
``Path`` class or is a string that forms a valid path, and ``False``
|
|
576
|
+
otherwise. Most strings are at least theoretically valid paths, but any
|
|
577
|
+
string that starts with a sheme followed by ``'://'`` must have the
|
|
578
|
+
``'file'`` scheme.
|
|
579
|
+
|
|
580
|
+
See also: ``is_filepath``
|
|
581
|
+
|
|
582
|
+
Parameters
|
|
583
|
+
----------
|
|
584
|
+
obj : object
|
|
585
|
+
The object whose ability to be converted into a ``Path`` instance is to
|
|
586
|
+
be determined.
|
|
587
|
+
|
|
588
|
+
Returns
|
|
589
|
+
-------
|
|
590
|
+
boolean
|
|
591
|
+
``True`` if `obj` is an instance of ``AzureBlobPath`` or is a string
|
|
592
|
+
that could be converted into an ``AzureBlobPath`` and ``False``
|
|
593
|
+
otherwise.
|
|
594
|
+
"""
|
|
595
|
+
if isinstance(obj, Path):
|
|
596
|
+
return True
|
|
597
|
+
url = urlparse(pathstr(obj))
|
|
598
|
+
return bool(url.scheme == '' or url.scheme == 'file')
|
|
599
|
+
@docwrap
|
|
600
|
+
def filepath(p, *args):
|
|
601
|
+
"""Returns a local ``Path`` object for the given path if possible.
|
|
602
|
+
|
|
603
|
+
The ``filepath`` function is intended to coerce remote paths (such as the
|
|
604
|
+
S3 or OSF paths managed through the ``cloudpathlib.CloudPath`` class) into
|
|
605
|
+
paths representing their local caches. If a local file is requested, then
|
|
606
|
+
it is always downloaded before the ``Path`` is returned. For directories,
|
|
607
|
+
the cache directory itself will always exist, but no such guarantee is made
|
|
608
|
+
about its contents.
|
|
609
|
+
|
|
610
|
+
If the argument to ``filepath`` is a string and not a path object, then
|
|
611
|
+
it is converted into a path via the ``immlib.path`` function.
|
|
612
|
+
|
|
613
|
+
Parameters
|
|
614
|
+
----------
|
|
615
|
+
p : path-like
|
|
616
|
+
The object whose local cache path is to be returned after it has been
|
|
617
|
+
downloaded.
|
|
618
|
+
|
|
619
|
+
Returns
|
|
620
|
+
-------
|
|
621
|
+
Path
|
|
622
|
+
If the input path `p` is already a local path, then it is returned. If
|
|
623
|
+
`p` is a remote path, then it is downloaded and its cache path is
|
|
624
|
+
returned. If there is no local cache or if the file does not exist, an
|
|
625
|
+
error is raised.
|
|
626
|
+
"""
|
|
627
|
+
# We are converting the argument p into a Path of some kind.
|
|
628
|
+
if isinstance(p, CloudPath):
|
|
629
|
+
# If p is a CloudPath, we return a path that represents the cache.
|
|
630
|
+
return CloudCachePath(p)
|
|
631
|
+
if isinstance(p, (PathLike, PurePath)):
|
|
632
|
+
# If p is a path, str, or PathLike, we can just return a copy.
|
|
633
|
+
return Path(p)
|
|
634
|
+
elif isinstance(p, str):
|
|
635
|
+
# We need to make sure that it doesn't start with file:// before we
|
|
636
|
+
# pass this string along.
|
|
637
|
+
if p.startswith('file://'):
|
|
638
|
+
p = p[7:]
|
|
639
|
+
return Path(p)
|
|
640
|
+
elif isinstance(p, bytes):
|
|
641
|
+
# Same as above but with bytes.
|
|
642
|
+
if p.startswith(b'file://'):
|
|
643
|
+
p = p[7:]
|
|
644
|
+
return Path(p)
|
|
645
|
+
else:
|
|
646
|
+
# Try to convert it to a pathstr then turn it into a path.
|
|
647
|
+
return Path(pathstr(p))
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
# Other Utility Functions #####################################################
|
|
651
|
+
|
|
652
|
+
# The registry of all recognized path types uses a named tuple to store
|
|
653
|
+
# entries. Pure paths are not included as they are innate to the functions.
|
|
654
|
+
PathTypeRecord = namedtuple(
|
|
655
|
+
'PathTypeRecord',
|
|
656
|
+
('construct', 'isfn', 'likefn', 'fspath'))
|
|
657
|
+
def _cloudtofspath(cp):
|
|
658
|
+
return cp.fspath
|
|
659
|
+
pathtypes = dict(
|
|
660
|
+
s3=PathTypeRecord(s3path, is_s3path, like_s3path, _cloudtofspath),
|
|
661
|
+
gs=PathTypeRecord(gspath, is_gspath, like_gspath, _cloudtofspath),
|
|
662
|
+
az=PathTypeRecord(azpath, is_azpath, like_azpath, _cloudtofspath),
|
|
663
|
+
osf=PathTypeRecord(osfpath, is_osfpath, like_osfpath, _cloudtofspath),
|
|
664
|
+
file=PathTypeRecord(
|
|
665
|
+
filepath, is_filepath, like_filepath, lambda path:path))
|
|
666
|
+
scheme_sep = '://'
|
|
667
|
+
@docwrap
|
|
668
|
+
def pathtype(path, default='file', encoding='utf-8'):
|
|
669
|
+
"""Given a path, returns the pathtype record and remaining path.
|
|
670
|
+
|
|
671
|
+
This is an internal function that looks up the pathtype for a path from the
|
|
672
|
+
``pathtypes`` dictionary and returning both the pathtype record for the
|
|
673
|
+
path.
|
|
674
|
+
|
|
675
|
+
Paths that do not have an explicit scheme marker are considered filesystem
|
|
676
|
+
paths.
|
|
677
|
+
|
|
678
|
+
Parameters
|
|
679
|
+
----------
|
|
680
|
+
path : path-like or str
|
|
681
|
+
The path whose pathtype record is being extracted.
|
|
682
|
+
default : object, optional
|
|
683
|
+
The scheme that is assumed if no scheme is specified (i.e.,
|
|
684
|
+
``str(path)`` does not begin with ``'<scheme>://'``. By default this is
|
|
685
|
+
``'file'``.
|
|
686
|
+
encoding : str, optional
|
|
687
|
+
The encoding to use if the path argument is a ``bytes`` object; the
|
|
688
|
+
default is ``'utf-8'``.
|
|
689
|
+
|
|
690
|
+
Returns
|
|
691
|
+
-------
|
|
692
|
+
PathTypeRecord
|
|
693
|
+
A data record about the path type of the given path.
|
|
694
|
+
|
|
695
|
+
Raises
|
|
696
|
+
------
|
|
697
|
+
KeyError
|
|
698
|
+
If the scheme of the the given path is not recognized.
|
|
699
|
+
"""
|
|
700
|
+
if isinstance(path, bytes):
|
|
701
|
+
path = path.decode('utf-8')
|
|
702
|
+
spath = pathstr(path)
|
|
703
|
+
if scheme_sep not in spath:
|
|
704
|
+
scheme = default
|
|
705
|
+
else:
|
|
706
|
+
scheme = spath.split(scheme_sep)[0]
|
|
707
|
+
return pathtypes[scheme]
|
|
708
|
+
@docwrap
|
|
709
|
+
def is_path(p):
|
|
710
|
+
"""Detects whether an object is either a ``Path`` or a ``CloudPath``
|
|
711
|
+
object.
|
|
712
|
+
|
|
713
|
+
Both ``Path`` and ``CloudPath`` objects abstractly represent paths, but
|
|
714
|
+
they do not share a subclass. ``is_path`` tests whether an object's type is
|
|
715
|
+
a subclass of any of path types recognized by ``immlib``. Additional path
|
|
716
|
+
types can be registered by adding ``immlib.paths.PathTypeRecord`` instances
|
|
717
|
+
to the ``immlib.pathtypes`` dictionary. The key for such a record should be
|
|
718
|
+
the string prefix for the path type (such as ``'s3'`` for an ``S3Path``
|
|
719
|
+
type).
|
|
720
|
+
|
|
721
|
+
Parameters
|
|
722
|
+
----------
|
|
723
|
+
p : path-like
|
|
724
|
+
The object whose quality as a path is to be assessed.
|
|
725
|
+
|
|
726
|
+
Returns
|
|
727
|
+
-------
|
|
728
|
+
boolean
|
|
729
|
+
``True`` if `p` has a type that is recognized by ``immlib`` as a path
|
|
730
|
+
type and ``False`` otherwise.
|
|
731
|
+
"""
|
|
732
|
+
try:
|
|
733
|
+
pt = pathtype(p)
|
|
734
|
+
except (KeyError, TypeError):
|
|
735
|
+
return False
|
|
736
|
+
return pt.isfn(p)
|
|
737
|
+
@docwrap
|
|
738
|
+
def like_path(p):
|
|
739
|
+
"""Detects whether an object is either like a ``Path`` or ``CloudPath``
|
|
740
|
+
object.
|
|
741
|
+
|
|
742
|
+
Both ``Path`` and ``CloudPath`` object abstractly represent paths, but they
|
|
743
|
+
do not share a subclass. ``like_path`` tests whether an object's type is a
|
|
744
|
+
subclass of any of path types recognized by ``immlib`` or is a string or
|
|
745
|
+
bytes object that could be converted into a path. Additional path types can
|
|
746
|
+
be registered by adding ``immlib.pathlib.PathTypeRecord`` instances to the
|
|
747
|
+
``immlib.pathlib.pathtypes`` dictionary. The key for such a record should
|
|
748
|
+
be the string prefix for the path type (such as ``'s3'`` for an ``S3Path``
|
|
749
|
+
type).
|
|
750
|
+
|
|
751
|
+
Parameters
|
|
752
|
+
----------
|
|
753
|
+
p : object
|
|
754
|
+
The object whose quality as a path-like object is to be assessed.
|
|
755
|
+
|
|
756
|
+
Returns
|
|
757
|
+
-------
|
|
758
|
+
boolean
|
|
759
|
+
``True`` if `p` has a type that is recognized by ``immlib`` as a path
|
|
760
|
+
type or is an object that can be converted into a path type and
|
|
761
|
+
``False`` otherwise.
|
|
762
|
+
"""
|
|
763
|
+
try:
|
|
764
|
+
pt = pathtype(p)
|
|
765
|
+
except KeyError:
|
|
766
|
+
return False
|
|
767
|
+
return pt.likefn(p)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
# path ########################################################################
|
|
771
|
+
|
|
772
|
+
@docwrap
|
|
773
|
+
def path(arg0, *args, **kwargs):
|
|
774
|
+
"""Convenience function for instantiating ``Path`` objects.
|
|
775
|
+
|
|
776
|
+
``path(arg)`` returns a ``Path``-like object that references the path given
|
|
777
|
+
by the argument ``arg``. The ``arg`` is converted into a string prior to
|
|
778
|
+
conversion into a path if it is not a path already.
|
|
779
|
+
|
|
780
|
+
``path(arg, *args)`` joins the list of arguments in ``args`` to the path
|
|
781
|
+
created from the ``arg``.
|
|
782
|
+
|
|
783
|
+
Optional keyword arguments may be given as well,
|
|
784
|
+
"""
|
|
785
|
+
nargs = len(args)
|
|
786
|
+
# What kind of pathtype is this?
|
|
787
|
+
try:
|
|
788
|
+
pt = pathtype(arg0)
|
|
789
|
+
except KeyError:
|
|
790
|
+
pt = None
|
|
791
|
+
if pt is None:
|
|
792
|
+
raise ValueError(f"unrecognized pathtype for path: {arg0}")
|
|
793
|
+
# Start by converting the first argument into a path, if it isn't one.
|
|
794
|
+
if not kwargs and pt.isfn(arg0):
|
|
795
|
+
# We only need to update the path if new keyword args were given.
|
|
796
|
+
p = arg0
|
|
797
|
+
else:
|
|
798
|
+
# We have a pathtype, so we can create a path from these arguments.
|
|
799
|
+
p = pt.construct(arg0, **kwargs)
|
|
800
|
+
# At this point, p is a pathtype, so we can append any extra args.
|
|
801
|
+
# Join the rest of the arguments to the path.
|
|
802
|
+
for arg in args:
|
|
803
|
+
p = p / arg
|
|
804
|
+
# That is all that's required.
|
|
805
|
+
return p
|
|
806
|
+
|
|
807
|
+
@docwrap
|
|
808
|
+
def pathdict(arg, all=False, filter=None, ondir=None, onfile=None):
|
|
809
|
+
"""Returns a lazy dictionary of the nested paths beginning at the argument.
|
|
810
|
+
|
|
811
|
+
Searches a directory and all its subdirectories, lazily, for all contents,
|
|
812
|
+
uniting them in a single nested lazy dictionary. The dictionaries in the
|
|
813
|
+
nest represent directories whose keys are the filenames of their
|
|
814
|
+
contents. The value of a key is another lazy dictionaries or a path object
|
|
815
|
+
if the file is not a directory.
|
|
816
|
+
|
|
817
|
+
Parameters
|
|
818
|
+
----------
|
|
819
|
+
arg : path-like
|
|
820
|
+
The path from which to begin the search.
|
|
821
|
+
all : boolean, optional
|
|
822
|
+
Whether to include hidden files (``True``) or not (``False``) in the
|
|
823
|
+
directory lists. The default is ``False``.
|
|
824
|
+
filter : None or function, optional
|
|
825
|
+
A filter that must return either ``True`` (indicating that the path
|
|
826
|
+
should be included in the pathdict) or ``False`` (indicating that the
|
|
827
|
+
path should not be included in the pathdict) for each path that is
|
|
828
|
+
scanned. The default is ``None``, meaning that no filter is applied.
|
|
829
|
+
ondir : function, optional
|
|
830
|
+
A function to run on any path encountered during the ``pathdict``
|
|
831
|
+
search that is a directory. When ``pathdict`` is given a path that is a
|
|
832
|
+
directory, it returns a lazy dictionary whose keys are the filenames of
|
|
833
|
+
the contents of that directory. For subdirectories, their filenames are
|
|
834
|
+
mapped to the return value of ``ondir(path)`` where ``path`` is the
|
|
835
|
+
path object for the subdirectory. By default this is ``pathdict``
|
|
836
|
+
itself, resulting in a nested structure for subdirectories. The
|
|
837
|
+
``all``, ``filter``, ``ondir``, and ``onfile`` parameters are all
|
|
838
|
+
passed to this function.
|
|
839
|
+
onfile : function, optional
|
|
840
|
+
A function to run on any path encountered during the ``pathdict``
|
|
841
|
+
search that is a file. When ``pathdict`` is given a path that is a
|
|
842
|
+
directory, it returns a lazy dictionary whose keys are the filenames of
|
|
843
|
+
the contents of that directory. For files in the directory, their
|
|
844
|
+
filenames are mapped to the return value of ``onfile(path)`` where
|
|
845
|
+
``path`` is the path object for the file. By default this is ``None``,
|
|
846
|
+
indicating that the path itself should be returned.
|
|
847
|
+
|
|
848
|
+
Returns
|
|
849
|
+
-------
|
|
850
|
+
ldict
|
|
851
|
+
A lazy dictionary of the contents of the argument.
|
|
852
|
+
"""
|
|
853
|
+
if ondir is None:
|
|
854
|
+
ondir = pathdict
|
|
855
|
+
root = path(arg)
|
|
856
|
+
if root.is_dir():
|
|
857
|
+
contents = {}
|
|
858
|
+
kw = {'all':all, 'filter':filter, 'ondir':ondir, 'onfile':onfile}
|
|
859
|
+
for p in root.iterdir():
|
|
860
|
+
if p.is_file():
|
|
861
|
+
q = p if onfile is None else lazy(onfile, p)
|
|
862
|
+
else:
|
|
863
|
+
q = p if ondir is None else lazy(ondir, p, **kw)
|
|
864
|
+
contents[p.name] = q
|
|
865
|
+
return ldict(contents)
|
|
866
|
+
elif onfile is None:
|
|
867
|
+
return root
|
|
868
|
+
else:
|
|
869
|
+
return onfile(root)
|