Skip to content

Commit 5b22d1e

Browse files
committed
fixes #786
1 parent 61e2fce commit 5b22d1e

3 files changed

Lines changed: 74 additions & 0 deletions

File tree

fastcore/_modidx.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,7 @@
702702
'fastcore.xtras.Path.delete': ('xtras.html#path.delete', 'fastcore/xtras.py'),
703703
'fastcore.xtras.Path.ls': ('xtras.html#path.ls', 'fastcore/xtras.py'),
704704
'fastcore.xtras.Path.mk_write': ('xtras.html#path.mk_write', 'fastcore/xtras.py'),
705+
'fastcore.xtras.Path.mkdir_perms': ('xtras.html#path.mkdir_perms', 'fastcore/xtras.py'),
705706
'fastcore.xtras.Path.normpath': ('xtras.html#path.normpath', 'fastcore/xtras.py'),
706707
'fastcore.xtras.Path.read_json': ('xtras.html#path.read_json', 'fastcore/xtras.py'),
707708
'fastcore.xtras.Path.readlines': ('xtras.html#path.readlines', 'fastcore/xtras.py'),

fastcore/xtras.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,12 +384,31 @@ def _expand_import(node, mod, existing):
384384
code = _replace_node(code, node, new_import)
385385
return code
386386

387+
# %% ../nbs/03_xtras.ipynb #15c2b62c
388+
@patch
389+
def mkdir_perms(self:Path, mode=0o777, parents=False, exist_ok=False, uid=-1, gid=-1):
390+
"Create directory like Path.mkdir but optionally set uid/gid on newly created dirs"
391+
if self.exists() and not exist_ok: raise FileExistsError(self)
392+
to_create = []
393+
p = self
394+
while not p.exists():
395+
to_create.append(p)
396+
if parents: p = p.parent
397+
else: break
398+
for d in reversed(to_create):
399+
try: d.mkdir(mode=mode)
400+
except FileExistsError:
401+
if d==self and not exist_ok: raise
402+
continue
403+
if uid>-1 or gid>-1: os.chown(d, uid, gid)
404+
387405
# %% ../nbs/03_xtras.ipynb #832397a3
388406
@contextmanager
389407
def atomic_save(fn, mode='wb', uid=-1, gid=-1, **kwargs):
390408
"Context manager for writing a file atomically via a temp file that is renamed on close"
391409
from tempfile import NamedTemporaryFile as ntf
392410
fn = Path(fn)
411+
fn.parent.mkdir_perms(parents=True, exist_ok=True, uid=uid, gid=gid)
393412
with ntf(mode=mode, dir=fn.parent, delete=False, suffix=fn.suffix, **kwargs) as f:
394413
try: yield f
395414
except:

nbs/03_xtras.ipynb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,59 @@
12981298
"test_eq(expand_wildcards(inp), exp)"
12991299
]
13001300
},
1301+
{
1302+
"cell_type": "code",
1303+
"execution_count": null,
1304+
"id": "15c2b62c",
1305+
"metadata": {},
1306+
"outputs": [],
1307+
"source": [
1308+
"#| export\n",
1309+
"@patch\n",
1310+
"def mkdir_perms(self:Path, mode=0o777, parents=False, exist_ok=False, uid=-1, gid=-1):\n",
1311+
" \"Create directory like Path.mkdir but optionally set uid/gid on newly created dirs\"\n",
1312+
" if self.exists() and not exist_ok: raise FileExistsError(self)\n",
1313+
" to_create = []\n",
1314+
" p = self\n",
1315+
" while not p.exists():\n",
1316+
" to_create.append(p)\n",
1317+
" if parents: p = p.parent\n",
1318+
" else: break\n",
1319+
" for d in reversed(to_create):\n",
1320+
" try: d.mkdir(mode=mode)\n",
1321+
" except FileExistsError:\n",
1322+
" if d==self and not exist_ok: raise\n",
1323+
" continue\n",
1324+
" if uid>-1 or gid>-1: os.chown(d, uid, gid)"
1325+
]
1326+
},
1327+
{
1328+
"cell_type": "code",
1329+
"execution_count": null,
1330+
"id": "5c6c3416",
1331+
"metadata": {},
1332+
"outputs": [],
1333+
"source": [
1334+
"import tempfile\n",
1335+
"from fastcore.test import test_fail\n",
1336+
"\n",
1337+
"with tempfile.TemporaryDirectory() as tmpdir:\n",
1338+
" base = Path(tmpdir)\n",
1339+
" p1 = base/'test1'\n",
1340+
" p1.mkdir_perms()\n",
1341+
" assert p1.exists() and p1.is_dir()\n",
1342+
" \n",
1343+
" p2 = base/'a'/'b'/'c'\n",
1344+
" p2.mkdir_perms(parents=True)\n",
1345+
" assert p2.exists() and (base/'a').exists()\n",
1346+
" \n",
1347+
" p1.mkdir_perms(exist_ok=True)\n",
1348+
" assert p1.exists()\n",
1349+
" \n",
1350+
" test_fail(p1.mkdir_perms, exc=FileExistsError)\n",
1351+
" test_fail((base/'missing'/'child').mkdir_perms, exc=FileNotFoundError)"
1352+
]
1353+
},
13011354
{
13021355
"cell_type": "code",
13031356
"execution_count": null,
@@ -1311,6 +1364,7 @@
13111364
" \"Context manager for writing a file atomically via a temp file that is renamed on close\"\n",
13121365
" from tempfile import NamedTemporaryFile as ntf\n",
13131366
" fn = Path(fn)\n",
1367+
" fn.parent.mkdir_perms(parents=True, exist_ok=True, uid=uid, gid=gid)\n",
13141368
" with ntf(mode=mode, dir=fn.parent, delete=False, suffix=fn.suffix, **kwargs) as f:\n",
13151369
" try: yield f\n",
13161370
" except:\n",

0 commit comments

Comments
 (0)