Skip to content

Commit 7819959

Browse files
committed
fixes #336
1 parent eadc5bf commit 7819959

File tree

7 files changed

+100
-86
lines changed

7 files changed

+100
-86
lines changed

fastcore/_nbdev.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
"Path.mk_write": "03_xtras.ipynb",
164164
"Path.ls": "03_xtras.ipynb",
165165
"Path.__repr__": "03_xtras.ipynb",
166+
"Path.delete": "03_xtras.ipynb",
166167
"truncstr": "03_xtras.ipynb",
167168
"spark_chars": "03_xtras.ipynb",
168169
"sparkline": "03_xtras.ipynb",
@@ -201,6 +202,7 @@
201202
"urlcheck": "03b_net.ipynb",
202203
"urlclean": "03b_net.ipynb",
203204
"urlretrieve": "03b_net.ipynb",
205+
"urldest": "03b_net.ipynb",
204206
"urlsave": "03b_net.ipynb",
205207
"urlvalid": "03b_net.ipynb",
206208
"urlrequest": "03b_net.ipynb",

fastcore/foundation.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,16 +246,22 @@ def read_config_file(file, **kwargs):
246246
# Cell
247247
class Config:
248248
"Reading and writing `ConfigParser` ini files"
249-
def __init__(self, cfg_path, cfg_name):
249+
def __init__(self, cfg_path, cfg_name, create=None):
250250
cfg_path = Path(cfg_path).expanduser().absolute()
251251
self.config_path,self.config_file = cfg_path,cfg_path/cfg_name
252-
assert self.config_file.exists(), f"Could not find {cfg_name}"
252+
if not self.config_file.exists():
253+
if create:
254+
self.d = create
255+
cfg_path.mkdir(exist_ok=True, parents=True)
256+
self.save()
257+
else: raise Exception(f"Could not find {cfg_name}")
253258
self.d = read_config_file(self.config_file)
254259

255260
def __setitem__(self,k,v): self.d[k] = str(v)
256261
def __contains__(self,k): return k in self.d
257262
def save(self): save_config_file(self.config_file,self.d)
258263
def __getattr__(self,k): return stop(AttributeError(k)) if k=='d' or k not in self.d else self.get(k)
264+
def __getitem__(self,k): return stop(IndexError(k)) if k not in self.d else self.get(k)
259265
def get(self,k,default=None): return self.d.get(k, default)
260266

261267
def path(self,k,default=None):

fastcore/net.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
'HTTP422UnprocessableEntityError', 'HTTP423LockedError', 'HTTP424FailedDependencyError',
1111
'HTTP425TooEarlyError', 'HTTP426UpgradeRequiredError', 'HTTP428PreconditionRequiredError',
1212
'HTTP429TooManyRequestsError', 'HTTP431HeaderFieldsTooLargeError', 'HTTP451LegalReasonsError', 'urlopen',
13-
'urlread', 'urljson', 'urlcheck', 'urlclean', 'urlretrieve', 'urlsave', 'urlvalid', 'urlrequest', 'urlsend',
14-
'do_request', 'start_server', 'start_client']
13+
'urlread', 'urljson', 'urlcheck', 'urlclean', 'urlretrieve', 'urldest', 'urlsave', 'urlvalid', 'urlrequest',
14+
'urlsend', 'do_request', 'start_server', 'start_client']
1515

1616
# Cell
1717
from .imports import *
@@ -164,12 +164,16 @@ def urlretrieve(url, filename=None, reporthook=None, data=None):
164164
return filename,headers
165165

166166
# Cell
167-
def urlsave(url, dest=None, reporthook=None):
168-
"Retrieve `url` and save based on its name"
167+
def urldest(url, dest=None):
169168
name = urlclean(Path(url).name)
170169
if dest is None: dest = name
171170
dest = Path(dest)
172-
if dest.is_dir(): dest = dest/name
171+
return dest/name if dest.is_dir() else dest
172+
173+
# Cell
174+
def urlsave(url, dest=None, reporthook=None):
175+
"Retrieve `url` and save based on its name"
176+
dest = urldest(url, dest)
173177
dest.parent.mkdir(parents=True, exist_ok=True)
174178
nm,msg = urlretrieve(url, dest, reporthook)
175179
return nm

fastcore/xtras.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from functools import wraps
1414

1515
import mimetypes,pickle,random,json,subprocess,shlex,bz2,gzip,zipfile,tarfile
16-
import imghdr,struct,distutils.util,tempfile,time,string,collections
16+
import imghdr,struct,distutils.util,tempfile,time,string,collections,shutil
1717
from contextlib import contextmanager,ExitStack
1818
from pdb import set_trace
1919
from datetime import datetime, timezone
@@ -148,15 +148,18 @@ def loads_multi(s:str):
148148
s = s[pos:]
149149

150150
# Cell
151-
def untar_dir(fname, dest):
152-
"untar `file` into `dest`"
151+
def untar_dir(fname, dest, rename=False, overwrite=False):
152+
"untar `file` into `dest`, creating a directory if the root contains more than one item"
153153
with tempfile.TemporaryDirectory(dir='.') as d:
154-
out = Path(d)/'out'
154+
out = Path(d)/remove_suffix(Path(fname).stem, '.tar')
155155
out.mkdir()
156156
shutil.unpack_archive(fname, out)
157157
ls = out.ls()
158-
if len(ls) == 1: shutil.move(str(ls[0]), dest)
159-
else: shutil.move(str(out), dest/remove_suffix(Path(fname).stem, '.tar'))
158+
src = ls[0] if len(ls) == 1 else out
159+
dest = dest/(out if rename else src).name
160+
if overwrite: shutil.rmtree(dest, ignore_errors=True)
161+
shutil.move(str(src), dest)
162+
return dest
160163

161164
# Cell
162165
def repo_details(url):
@@ -247,6 +250,14 @@ def __repr__(self:Path):
247250
except: pass
248251
return f"Path({self.as_posix()!r})"
249252

253+
# Cell
254+
@patch
255+
def delete(self:Path):
256+
"Delete a file, symlink, or directory tree"
257+
if not self.exists(): return
258+
if self.is_dir(): shutil.rmtree(self)
259+
else: self.unlink()
260+
250261
# Cell
251262
def truncstr(s:str, maxlen:int, suf:str='…', space='')->str:
252263
"Truncate `s` to length `maxlen`, adding suffix `suf` if truncated"

nbs/02_foundation.ipynb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@
792792
{
793793
"data": {
794794
"text/plain": [
795-
"[7, 0, 'j']"
795+
"[2, 'k', 11]"
796796
]
797797
},
798798
"execution_count": null,
@@ -1880,16 +1880,22 @@
18801880
"#export\n",
18811881
"class Config:\n",
18821882
" \"Reading and writing `ConfigParser` ini files\"\n",
1883-
" def __init__(self, cfg_path, cfg_name):\n",
1883+
" def __init__(self, cfg_path, cfg_name, create=None):\n",
18841884
" cfg_path = Path(cfg_path).expanduser().absolute()\n",
18851885
" self.config_path,self.config_file = cfg_path,cfg_path/cfg_name\n",
1886-
" assert self.config_file.exists(), f\"Could not find {cfg_name}\"\n",
1886+
" if not self.config_file.exists():\n",
1887+
" if create:\n",
1888+
" self.d = create\n",
1889+
" cfg_path.mkdir(exist_ok=True, parents=True)\n",
1890+
" self.save()\n",
1891+
" else: raise Exception(f\"Could not find {cfg_name}\")\n",
18871892
" self.d = read_config_file(self.config_file)\n",
18881893
"\n",
18891894
" def __setitem__(self,k,v): self.d[k] = str(v)\n",
18901895
" def __contains__(self,k): return k in self.d\n",
18911896
" def save(self): save_config_file(self.config_file,self.d)\n",
18921897
" def __getattr__(self,k): return stop(AttributeError(k)) if k=='d' or k not in self.d else self.get(k)\n",
1898+
" def __getitem__(self,k): return stop(IndexError(k)) if k not in self.d else self.get(k)\n",
18931899
" def get(self,k,default=None): return self.d.get(k, default)\n",
18941900
"\n",
18951901
" def path(self,k,default=None):\n",
@@ -1910,13 +1916,11 @@
19101916
"metadata": {},
19111917
"outputs": [],
19121918
"source": [
1913-
"try:\n",
1914-
" save_config_file('../tmp.ini', _d)\n",
1915-
" cfg = Config('..', 'tmp.ini')\n",
1919+
"try: cfg = Config('..', 'tmp.ini', create=_d)\n",
19161920
"finally: os.unlink('../tmp.ini')\n",
19171921
"\n",
19181922
"test_eq(cfg.user,'fastai')\n",
1919-
"test_eq(cfg.get('some_path'), 'test')\n",
1923+
"test_eq(cfg['some_path'], 'test')\n",
19201924
"test_eq(cfg.path('some_path'), Path('../test').absolute())\n",
19211925
"test_eq(cfg.get('foo','bar'),'bar')"
19221926
]

nbs/03_xtras.ipynb

Lines changed: 38 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"from functools import wraps\n",
2323
"\n",
2424
"import mimetypes,pickle,random,json,subprocess,shlex,bz2,gzip,zipfile,tarfile\n",
25-
"import imghdr,struct,distutils.util,tempfile,time,string,collections\n",
25+
"import imghdr,struct,distutils.util,tempfile,time,string,collections,shutil\n",
2626
"from contextlib import contextmanager,ExitStack\n",
2727
"from pdb import set_trace\n",
2828
"from datetime import datetime, timezone\n",
@@ -618,7 +618,7 @@
618618
{
619619
"data": {
620620
"text/plain": [
621-
"['c', 'b', 'e', 'f', 'g', 'a', 'd', 'h']"
621+
"['g', 'b', 'c', 'e', 'a', 'h', 'd', 'f']"
622622
]
623623
},
624624
"execution_count": null,
@@ -1007,22 +1007,18 @@
10071007
"outputs": [],
10081008
"source": [
10091009
"#export\n",
1010-
"def untar_dir(fname, dest):\n",
1011-
" \"untar `file` into `dest`\"\n",
1010+
"def untar_dir(fname, dest, rename=False, overwrite=False):\n",
1011+
" \"untar `file` into `dest`, creating a directory if the root contains more than one item\"\n",
10121012
" with tempfile.TemporaryDirectory(dir='.') as d:\n",
1013-
" out = Path(d)/'out'\n",
1013+
" out = Path(d)/remove_suffix(Path(fname).stem, '.tar')\n",
10141014
" out.mkdir()\n",
10151015
" shutil.unpack_archive(fname, out)\n",
10161016
" ls = out.ls()\n",
1017-
" if len(ls) == 1: shutil.move(str(ls[0]), dest)\n",
1018-
" else: shutil.move(str(out), dest/remove_suffix(Path(fname).stem, '.tar'))"
1019-
]
1020-
},
1021-
{
1022-
"cell_type": "markdown",
1023-
"metadata": {},
1024-
"source": [
1025-
"If the contents of `fname` contain just one file or directory, it is placed directly in `dest`:"
1017+
" src = ls[0] if len(ls) == 1 else out\n",
1018+
" dest = dest/(out if rename else src).name\n",
1019+
" if overwrite: shutil.rmtree(dest, ignore_errors=True)\n",
1020+
" shutil.move(str(src), dest)\n",
1021+
" return dest"
10261022
]
10271023
},
10281024
{
@@ -1041,6 +1037,13 @@
10411037
" test_eq(d2.ls(), [d2/foldername])"
10421038
]
10431039
},
1040+
{
1041+
"cell_type": "markdown",
1042+
"metadata": {},
1043+
"source": [
1044+
"If the contents of `fname` contain just one file or directory, it is placed directly in `dest`:"
1045+
]
1046+
},
10441047
{
10451048
"cell_type": "code",
10461049
"execution_count": null,
@@ -1134,22 +1137,10 @@
11341137
"execution_count": null,
11351138
"metadata": {},
11361139
"outputs": [
1137-
{
1138-
"name": "stdout",
1139-
"output_type": "stream",
1140-
"text": [
1141-
"popenargs (['cmd', '/c', 'echo'],)\n",
1142-
"kwargs {'stdout': -1, 'stderr': -1}\n",
1143-
"popenargs (('cmd', '/c', 'pip', '--version'),)\n",
1144-
"kwargs {'stdout': -1, 'stderr': -1}\n",
1145-
"popenargs (['cmd', '/c', 'pip', '--version'],)\n",
1146-
"kwargs {'stdout': -1, 'stderr': -1}\n"
1147-
]
1148-
},
11491140
{
11501141
"data": {
11511142
"text/plain": [
1152-
"'pip 21.0.1 from D:\\\\programs\\\\envs\\\\nbdev\\\\lib\\\\site-packages\\\\pip (python 3.8)'"
1143+
"'pip 21.1.3 from /home/jhoward/miniconda3/lib/python3.8/site-packages/pip (python 3.8)'"
11531144
]
11541145
},
11551146
"execution_count": null,
@@ -1167,20 +1158,7 @@
11671158
"cell_type": "code",
11681159
"execution_count": null,
11691160
"metadata": {},
1170-
"outputs": [
1171-
{
1172-
"name": "stdout",
1173-
"output_type": "stream",
1174-
"text": [
1175-
"popenargs (['cmd', '/c', 'dir', '/p'],)\n",
1176-
"kwargs {'stdout': -1, 'stderr': -1}\n",
1177-
"popenargs (['cmd', '/c', 'dir', '/p'],)\n",
1178-
"kwargs {'stdout': -1, 'stderr': -1}\n",
1179-
"popenargs (('cmd', '/c', 'dir', '/p'),)\n",
1180-
"kwargs {'stdout': -1, 'stderr': -1}\n"
1181-
]
1182-
}
1183-
],
1161+
"outputs": [],
11841162
"source": [
11851163
"if sys.platform == 'win32':\n",
11861164
" assert 'ipynb' in run('cmd /c dir /p')\n",
@@ -1203,16 +1181,7 @@
12031181
"cell_type": "code",
12041182
"execution_count": null,
12051183
"metadata": {},
1206-
"outputs": [
1207-
{
1208-
"name": "stdout",
1209-
"output_type": "stream",
1210-
"text": [
1211-
"popenargs (['cmd', '/c', 'findstr', 'asdfds', '00_test.ipynb'],)\n",
1212-
"kwargs {'stdout': -1, 'stderr': -1}\n"
1213-
]
1214-
}
1215-
],
1184+
"outputs": [],
12161185
"source": [
12171186
"if sys.platform == 'win32':\n",
12181187
" test_eq(run('cmd /c findstr asdfds 00_test.ipynb', ignore_ex=True)[0], 1)\n",
@@ -1231,16 +1200,7 @@
12311200
"cell_type": "code",
12321201
"execution_count": null,
12331202
"metadata": {},
1234-
"outputs": [
1235-
{
1236-
"name": "stdout",
1237-
"output_type": "stream",
1238-
"text": [
1239-
"popenargs (['cmd', '/c', 'echo', 'hi'],)\n",
1240-
"kwargs {'stdout': -1, 'stderr': -1}\n"
1241-
]
1242-
}
1243-
],
1203+
"outputs": [],
12441204
"source": [
12451205
"if sys.platform == 'win32':\n",
12461206
" # why I ignore as_types, because every time nbdev_clean_nbs will update \\n to \\r\\n\n",
@@ -1394,7 +1354,7 @@
13941354
{
13951355
"data": {
13961356
"text/plain": [
1397-
"Path('.gitattributes')"
1357+
"Path('05_transform.ipynb')"
13981358
]
13991359
},
14001360
"execution_count": null,
@@ -1428,7 +1388,7 @@
14281388
{
14291389
"data": {
14301390
"text/plain": [
1431-
"(Path('../fastcore/all.py'), Path('00_test.ipynb'))"
1391+
"(Path('../fastcore/all.py'), Path('05_transform.ipynb'))"
14321392
]
14331393
},
14341394
"execution_count": null,
@@ -1494,6 +1454,21 @@
14941454
"finally: Path.BASE_PATH = None"
14951455
]
14961456
},
1457+
{
1458+
"cell_type": "code",
1459+
"execution_count": null,
1460+
"metadata": {},
1461+
"outputs": [],
1462+
"source": [
1463+
"#export\n",
1464+
"@patch\n",
1465+
"def delete(self:Path):\n",
1466+
" \"Delete a file, symlink, or directory tree\"\n",
1467+
" if not self.exists(): return\n",
1468+
" if self.is_dir(): shutil.rmtree(self)\n",
1469+
" else: self.unlink()"
1470+
]
1471+
},
14971472
{
14981473
"cell_type": "markdown",
14991474
"metadata": {},

nbs/03b_net.ipynb

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,23 @@
401401
"outputs": [],
402402
"source": [
403403
"#export\n",
404-
"def urlsave(url, dest=None, reporthook=None):\n",
405-
" \"Retrieve `url` and save based on its name\"\n",
404+
"def urldest(url, dest=None):\n",
406405
" name = urlclean(Path(url).name)\n",
407406
" if dest is None: dest = name\n",
408407
" dest = Path(dest)\n",
409-
" if dest.is_dir(): dest = dest/name\n",
408+
" return dest/name if dest.is_dir() else dest"
409+
]
410+
},
411+
{
412+
"cell_type": "code",
413+
"execution_count": null,
414+
"metadata": {},
415+
"outputs": [],
416+
"source": [
417+
"#export\n",
418+
"def urlsave(url, dest=None, reporthook=None):\n",
419+
" \"Retrieve `url` and save based on its name\"\n",
420+
" dest = urldest(url, dest)\n",
410421
" dest.parent.mkdir(parents=True, exist_ok=True)\n",
411422
" nm,msg = urlretrieve(url, dest, reporthook)\n",
412423
" return nm"
@@ -672,6 +683,7 @@
672683
"Converted 05_transform.ipynb.\n",
673684
"Converted 07_meta.ipynb.\n",
674685
"Converted 08_script.ipynb.\n",
686+
"Converted index.ipynb.\n",
675687
"Converted parallel_win.ipynb.\n"
676688
]
677689
}

0 commit comments

Comments
 (0)