Skip to content

Commit f50b794

Browse files
committed
adds mkdir
1 parent 101c4f6 commit f50b794

File tree

2 files changed

+246
-201
lines changed

2 files changed

+246
-201
lines changed

fastcore/xtras.py

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def maybe_open(f, mode='r', **kwargs):
7474
with open(f, mode, **kwargs) as f: yield f
7575
else: yield f
7676

77-
# %% ../nbs/03_xtras.ipynb 26
77+
# %% ../nbs/03_xtras.ipynb 28
7878
def image_size(fn):
7979
"Tuple of (w,h) for png, gif, or jpg; `None` otherwise"
8080
import imghdr,struct
@@ -98,7 +98,7 @@ def _png_size(f):
9898
d = dict(png=_png_size, gif=_gif_size, jpeg=_jpg_size)
9999
with maybe_open(fn, 'rb') as f: return d[imghdr.what(f)](f)
100100

101-
# %% ../nbs/03_xtras.ipynb 28
101+
# %% ../nbs/03_xtras.ipynb 30
102102
def bunzip(fn):
103103
"bunzip `fn`, raising exception if output already exists"
104104
fn = Path(fn)
@@ -109,15 +109,15 @@ def bunzip(fn):
109109
with bz2.BZ2File(fn, 'rb') as src, out_fn.open('wb') as dst:
110110
for d in iter(lambda: src.read(1024*1024), b''): dst.write(d)
111111

112-
# %% ../nbs/03_xtras.ipynb 30
112+
# %% ../nbs/03_xtras.ipynb 32
113113
def loads(s, **kw):
114114
"Same as `json.loads`, but handles `None`"
115115
if not s: return {}
116116
try: import ujson as json
117117
except ModuleNotFoundError: import json
118118
return json.loads(s, **kw)
119119

120-
# %% ../nbs/03_xtras.ipynb 31
120+
# %% ../nbs/03_xtras.ipynb 33
121121
def loads_multi(s:str):
122122
"Generator of >=0 decoded json dicts, possibly with non-json ignored text at start and end"
123123
import json
@@ -129,22 +129,22 @@ def loads_multi(s:str):
129129
yield obj
130130
s = s[pos:]
131131

132-
# %% ../nbs/03_xtras.ipynb 33
132+
# %% ../nbs/03_xtras.ipynb 35
133133
def dumps(obj, **kw):
134134
"Same as `json.dumps`, but uses `ujson` if available"
135135
try: import ujson as json
136136
except ModuleNotFoundError: import json
137137
else: kw['escape_forward_slashes']=False
138138
return json.dumps(obj, **kw)
139139

140-
# %% ../nbs/03_xtras.ipynb 34
140+
# %% ../nbs/03_xtras.ipynb 36
141141
def _unpack(fname, out):
142142
import shutil
143143
shutil.unpack_archive(str(fname), str(out))
144144
ls = out.ls()
145145
return ls[0] if len(ls) == 1 else out
146146

147-
# %% ../nbs/03_xtras.ipynb 35
147+
# %% ../nbs/03_xtras.ipynb 37
148148
def untar_dir(fname, dest, rename=False, overwrite=False):
149149
"untar `file` into `dest`, creating a directory if the root contains more than one item"
150150
import tempfile,shutil
@@ -162,14 +162,14 @@ def untar_dir(fname, dest, rename=False, overwrite=False):
162162
shutil.move(str(src), dest)
163163
return dest
164164

165-
# %% ../nbs/03_xtras.ipynb 43
165+
# %% ../nbs/03_xtras.ipynb 45
166166
def repo_details(url):
167167
"Tuple of `owner,name` from ssh or https git repo `url`"
168168
res = remove_suffix(url.strip(), '.git')
169169
res = res.split(':')[-1]
170170
return res.split('/')[-2:]
171171

172-
# %% ../nbs/03_xtras.ipynb 45
172+
# %% ../nbs/03_xtras.ipynb 47
173173
def run(cmd, *rest, same_in_win=False, ignore_ex=False, as_bytes=False, stderr=False):
174174
"Pass `cmd` (splitting with `shlex` if string) to `subprocess.run`; return `stdout`; raise `IOError` if fails"
175175
# Even the command is same on Windows, we have to add `cmd /c `"
@@ -193,7 +193,7 @@ def run(cmd, *rest, same_in_win=False, ignore_ex=False, as_bytes=False, stderr=F
193193
if res.returncode: raise IOError(stdout)
194194
return stdout
195195

196-
# %% ../nbs/03_xtras.ipynb 53
196+
# %% ../nbs/03_xtras.ipynb 55
197197
def open_file(fn, mode='r', **kwargs):
198198
"Open a file, with optional compression if gz or bz2 suffix"
199199
if isinstance(fn, io.IOBase): return fn
@@ -204,81 +204,81 @@ def open_file(fn, mode='r', **kwargs):
204204
elif fn.suffix=='.zip': return zipfile.ZipFile(fn, mode, **kwargs)
205205
else: return open(fn,mode, **kwargs)
206206

207-
# %% ../nbs/03_xtras.ipynb 54
207+
# %% ../nbs/03_xtras.ipynb 56
208208
def save_pickle(fn, o):
209209
"Save a pickle file, to a file name or opened file"
210210
import pickle
211211
with open_file(fn, 'wb') as f: pickle.dump(o, f)
212212

213-
# %% ../nbs/03_xtras.ipynb 55
213+
# %% ../nbs/03_xtras.ipynb 57
214214
def load_pickle(fn):
215215
"Load a pickle file from a file name or opened file"
216216
import pickle
217217
with open_file(fn, 'rb') as f: return pickle.load(f)
218218

219-
# %% ../nbs/03_xtras.ipynb 58
219+
# %% ../nbs/03_xtras.ipynb 60
220220
def dict2obj(d, list_func=L, dict_func=AttrDict):
221221
"Convert (possibly nested) dicts (or lists of dicts) to `AttrDict`"
222222
if isinstance(d, (L,list)): return list_func(d).map(dict2obj)
223223
if not isinstance(d, dict): return d
224224
return dict_func(**{k:dict2obj(v) for k,v in d.items()})
225225

226-
# %% ../nbs/03_xtras.ipynb 63
226+
# %% ../nbs/03_xtras.ipynb 65
227227
def obj2dict(d):
228228
"Convert (possibly nested) AttrDicts (or lists of AttrDicts) to `dict`"
229229
if isinstance(d, (L,list)): return list(L(d).map(obj2dict))
230230
if not isinstance(d, dict): return d
231231
return dict(**{k:obj2dict(v) for k,v in d.items()})
232232

233-
# %% ../nbs/03_xtras.ipynb 66
233+
# %% ../nbs/03_xtras.ipynb 68
234234
def _repr_dict(d, lvl):
235235
if isinstance(d,dict):
236236
its = [f"{k}: {_repr_dict(v,lvl+1)}" for k,v in d.items()]
237237
elif isinstance(d,(list,L)): its = [_repr_dict(o,lvl+1) for o in d]
238238
else: return str(d)
239239
return '\n' + '\n'.join([" "*(lvl*2) + "- " + o for o in its])
240240

241-
# %% ../nbs/03_xtras.ipynb 67
241+
# %% ../nbs/03_xtras.ipynb 69
242242
def repr_dict(d):
243243
"Print nested dicts and lists, such as returned by `dict2obj`"
244244
return _repr_dict(d,0).strip()
245245

246-
# %% ../nbs/03_xtras.ipynb 69
246+
# %% ../nbs/03_xtras.ipynb 71
247247
def is_listy(x):
248248
"`isinstance(x, (tuple,list,L,slice,Generator))`"
249249
return isinstance(x, (tuple,list,L,slice,Generator))
250250

251-
# %% ../nbs/03_xtras.ipynb 71
251+
# %% ../nbs/03_xtras.ipynb 73
252252
def mapped(f, it):
253253
"map `f` over `it`, unless it's not listy, in which case return `f(it)`"
254254
return L(it).map(f) if is_listy(it) else f(it)
255255

256-
# %% ../nbs/03_xtras.ipynb 75
256+
# %% ../nbs/03_xtras.ipynb 77
257257
@patch
258258
def readlines(self:Path, hint=-1, encoding='utf8'):
259259
"Read the content of `self`"
260260
with self.open(encoding=encoding) as f: return f.readlines(hint)
261261

262-
# %% ../nbs/03_xtras.ipynb 76
262+
# %% ../nbs/03_xtras.ipynb 78
263263
@patch
264264
def read_json(self:Path, encoding=None, errors=None):
265265
"Same as `read_text` followed by `loads`"
266266
return loads(self.read_text(encoding=encoding, errors=errors))
267267

268-
# %% ../nbs/03_xtras.ipynb 77
268+
# %% ../nbs/03_xtras.ipynb 79
269269
@patch
270270
def mk_write(self:Path, data, encoding=None, errors=None, mode=511):
271271
"Make all parent dirs of `self`, and write `data`"
272272
self.parent.mkdir(exist_ok=True, parents=True, mode=mode)
273273
self.write_text(data, encoding=encoding, errors=errors)
274274

275-
# %% ../nbs/03_xtras.ipynb 78
275+
# %% ../nbs/03_xtras.ipynb 80
276276
@patch
277277
def relpath(self:Path, start=None):
278278
"Same as `os.path.relpath`, but returns a `Path`, and resolves symlinks"
279279
return Path(os.path.relpath(self.resolve(), Path(start).resolve()))
280280

281-
# %% ../nbs/03_xtras.ipynb 81
281+
# %% ../nbs/03_xtras.ipynb 83
282282
@patch
283283
def ls(self:Path, n_max=None, file_type=None, file_exts=None):
284284
"Contents of path as a list"
@@ -290,7 +290,7 @@ def ls(self:Path, n_max=None, file_type=None, file_exts=None):
290290
if n_max is not None: res = itertools.islice(res, n_max)
291291
return L(res)
292292

293-
# %% ../nbs/03_xtras.ipynb 87
293+
# %% ../nbs/03_xtras.ipynb 89
294294
@patch
295295
def __repr__(self:Path):
296296
b = getattr(Path, 'BASE_PATH', None)
@@ -299,7 +299,7 @@ def __repr__(self:Path):
299299
except: pass
300300
return f"Path({self.as_posix()!r})"
301301

302-
# %% ../nbs/03_xtras.ipynb 90
302+
# %% ../nbs/03_xtras.ipynb 92
303303
@patch
304304
def delete(self:Path):
305305
"Delete a file, symlink, or directory tree"
@@ -309,12 +309,12 @@ def delete(self:Path):
309309
shutil.rmtree(self)
310310
else: self.unlink()
311311

312-
# %% ../nbs/03_xtras.ipynb 92
312+
# %% ../nbs/03_xtras.ipynb 94
313313
class IterLen:
314314
"Base class to add iteration to anything supporting `__len__` and `__getitem__`"
315315
def __iter__(self): return (self[i] for i in range_of(self))
316316

317-
# %% ../nbs/03_xtras.ipynb 93
317+
# %% ../nbs/03_xtras.ipynb 95
318318
@docs
319319
class ReindexCollection(GetAttr, IterLen):
320320
"Reindexes collection `coll` with indices `idxs` and optional LRU cache of size `cache`"
@@ -339,7 +339,7 @@ def __setstate__(self, s): self.coll,self.idxs,self.cache,self.tfm = s['coll'],s
339339
shuffle="Randomly shuffle indices",
340340
cache_clear="Clear LRU cache")
341341

342-
# %% ../nbs/03_xtras.ipynb 112
342+
# %% ../nbs/03_xtras.ipynb 114
343343
def _is_type_dispatch(x): return type(x).__name__ == "TypeDispatch"
344344
def _unwrapped_type_dispatch_func(x): return x.first() if _is_type_dispatch(x) else x
345345

@@ -366,15 +366,15 @@ def get_source_link(func):
366366
return f"{nbdev_mod.git_url}{module}#L{line}"
367367
except: return f"{module}#L{line}"
368368

369-
# %% ../nbs/03_xtras.ipynb 115
369+
# %% ../nbs/03_xtras.ipynb 117
370370
def truncstr(s:str, maxlen:int, suf:str='…', space='')->str:
371371
"Truncate `s` to length `maxlen`, adding suffix `suf` if truncated"
372372
return s[:maxlen-len(suf)]+suf if len(s)+len(space)>maxlen else s+space
373373

374-
# %% ../nbs/03_xtras.ipynb 117
374+
# %% ../nbs/03_xtras.ipynb 119
375375
spark_chars = '▁▂▃▅▆▇'
376376

377-
# %% ../nbs/03_xtras.ipynb 118
377+
# %% ../nbs/03_xtras.ipynb 120
378378
def _ceil(x, lim=None): return x if (not lim or x <= lim) else lim
379379

380380
def _sparkchar(x, mn, mx, incr, empty_zero):
@@ -383,7 +383,7 @@ def _sparkchar(x, mn, mx, incr, empty_zero):
383383
res = int((_ceil(x,mx)-mn)/incr-0.5)
384384
return spark_chars[res]
385385

386-
# %% ../nbs/03_xtras.ipynb 119
386+
# %% ../nbs/03_xtras.ipynb 121
387387
def sparkline(data, mn=None, mx=None, empty_zero=False):
388388
"Sparkline for `data`, with `None`s (and zero, if `empty_zero`) shown as empty column"
389389
valid = [o for o in data if o is not None]
@@ -392,7 +392,7 @@ def sparkline(data, mn=None, mx=None, empty_zero=False):
392392
res = [_sparkchar(x=o, mn=mn, mx=mx, incr=(mx-mn)/n, empty_zero=empty_zero) for o in data]
393393
return ''.join(res)
394394

395-
# %% ../nbs/03_xtras.ipynb 123
395+
# %% ../nbs/03_xtras.ipynb 125
396396
def modify_exception(
397397
e:Exception, # An exception
398398
msg:str=None, # A custom message
@@ -402,14 +402,14 @@ def modify_exception(
402402
e.args = [f'{e.args[0]} {msg}'] if not replace and len(e.args) > 0 else [msg]
403403
return e
404404

405-
# %% ../nbs/03_xtras.ipynb 125
405+
# %% ../nbs/03_xtras.ipynb 127
406406
def round_multiple(x, mult, round_down=False):
407407
"Round `x` to nearest multiple of `mult`"
408408
def _f(x_): return (int if round_down else round)(x_/mult)*mult
409409
res = L(x).map(_f)
410410
return res if is_listy(x) else res[0]
411411

412-
# %% ../nbs/03_xtras.ipynb 127
412+
# %% ../nbs/03_xtras.ipynb 129
413413
def set_num_threads(nt):
414414
"Get numpy (and others) to use `nt` threads"
415415
try: import mkl; mkl.set_num_threads(nt)
@@ -420,14 +420,14 @@ def set_num_threads(nt):
420420
for o in ['OPENBLAS_NUM_THREADS','NUMEXPR_NUM_THREADS','OMP_NUM_THREADS','MKL_NUM_THREADS']:
421421
os.environ[o] = str(nt)
422422

423-
# %% ../nbs/03_xtras.ipynb 129
423+
# %% ../nbs/03_xtras.ipynb 131
424424
def join_path_file(file, path, ext=''):
425425
"Return `path/file` if file is a string or a `Path`, file otherwise"
426426
if not isinstance(file, (str, Path)): return file
427427
path.mkdir(parents=True, exist_ok=True)
428428
return path/f'{file}{ext}'
429429

430-
# %% ../nbs/03_xtras.ipynb 132
430+
# %% ../nbs/03_xtras.ipynb 134
431431
def autostart(g):
432432
"Decorator that automatically starts a generator"
433433
@functools.wraps(g)
@@ -437,7 +437,7 @@ def f():
437437
return r
438438
return f
439439

440-
# %% ../nbs/03_xtras.ipynb 133
440+
# %% ../nbs/03_xtras.ipynb 135
441441
class EventTimer:
442442
"An event timer with history of `store` items of time `span`"
443443

@@ -461,15 +461,15 @@ def duration(self): return time.perf_counter()-self.start
461461
@property
462462
def freq(self): return self.events/self.duration
463463

464-
# %% ../nbs/03_xtras.ipynb 137
464+
# %% ../nbs/03_xtras.ipynb 139
465465
_fmt = string.Formatter()
466466

467-
# %% ../nbs/03_xtras.ipynb 138
467+
# %% ../nbs/03_xtras.ipynb 140
468468
def stringfmt_names(s:str)->list:
469469
"Unique brace-delimited names in `s`"
470470
return uniqueify(o[1] for o in _fmt.parse(s) if o[1])
471471

472-
# %% ../nbs/03_xtras.ipynb 140
472+
# %% ../nbs/03_xtras.ipynb 142
473473
class PartialFormatter(string.Formatter):
474474
"A `string.Formatter` that doesn't error on missing fields, and tracks missing fields and unused args"
475475
def __init__(self):
@@ -485,24 +485,24 @@ def get_field(self, nm, args, kwargs):
485485
def check_unused_args(self, used, args, kwargs):
486486
self.xtra = filter_keys(kwargs, lambda o: o not in used)
487487

488-
# %% ../nbs/03_xtras.ipynb 142
488+
# %% ../nbs/03_xtras.ipynb 144
489489
def partial_format(s:str, **kwargs):
490490
"string format `s`, ignoring missing field errors, returning missing and extra fields"
491491
fmt = PartialFormatter()
492492
res = fmt.format(s, **kwargs)
493493
return res,list(fmt.missing),fmt.xtra
494494

495-
# %% ../nbs/03_xtras.ipynb 145
495+
# %% ../nbs/03_xtras.ipynb 147
496496
def utc2local(dt:datetime)->datetime:
497497
"Convert `dt` from UTC to local time"
498498
return dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
499499

500-
# %% ../nbs/03_xtras.ipynb 147
500+
# %% ../nbs/03_xtras.ipynb 149
501501
def local2utc(dt:datetime)->datetime:
502502
"Convert `dt` from local to UTC time"
503503
return dt.replace(tzinfo=None).astimezone(tz=timezone.utc)
504504

505-
# %% ../nbs/03_xtras.ipynb 149
505+
# %% ../nbs/03_xtras.ipynb 151
506506
def trace(f):
507507
"Add `set_trace` to an existing function `f`"
508508
from pdb import set_trace
@@ -513,7 +513,7 @@ def _inner(*args,**kwargs):
513513
_inner._traced = True
514514
return _inner
515515

516-
# %% ../nbs/03_xtras.ipynb 151
516+
# %% ../nbs/03_xtras.ipynb 153
517517
@contextmanager
518518
def modified_env(*delete, **replace):
519519
"Context manager temporarily modifying `os.environ` by deleting `delete` and replacing `replace`"
@@ -526,21 +526,21 @@ def modified_env(*delete, **replace):
526526
os.environ.clear()
527527
os.environ.update(prev)
528528

529-
# %% ../nbs/03_xtras.ipynb 153
529+
# %% ../nbs/03_xtras.ipynb 155
530530
class ContextManagers(GetAttr):
531531
"Wrapper for `contextlib.ExitStack` which enters a collection of context managers"
532532
def __init__(self, mgrs): self.default,self.stack = L(mgrs),ExitStack()
533533
def __enter__(self): self.default.map(self.stack.enter_context)
534534
def __exit__(self, *args, **kwargs): self.stack.__exit__(*args, **kwargs)
535535

536-
# %% ../nbs/03_xtras.ipynb 155
536+
# %% ../nbs/03_xtras.ipynb 157
537537
def shufflish(x, pct=0.04):
538538
"Randomly relocate items of `x` up to `pct` of `len(x)` from their starting location"
539539
n = len(x)
540540
import random
541541
return L(x[i] for i in sorted(range_of(x), key=lambda o: o+n*(1+random.random()*pct)))
542542

543-
# %% ../nbs/03_xtras.ipynb 156
543+
# %% ../nbs/03_xtras.ipynb 158
544544
def console_help(
545545
libname:str): # name of library for console script listing
546546
"Show help for all console scripts from `libname`"

0 commit comments

Comments
 (0)