Skip to content

Commit 1d1883b

Browse files
authored
Merge pull request #3050 from effigies/fix/pathlib_patch
RF: Provide functions to augment old Path.mkdir, Path.resolve methods
2 parents 56a0335 + 9eefdcd commit 1d1883b

File tree

4 files changed

+54
-53
lines changed

4 files changed

+54
-53
lines changed

nipype/interfaces/base/traits_extension.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
from traits.api import Unicode
3535
from future import standard_library
36-
from ...utils.filemanip import Path, USING_PATHLIB2
36+
from ...utils.filemanip import Path, USING_PATHLIB2, path_resolve
3737

3838
if USING_PATHLIB2:
3939
from future.types.newstr import newstr
@@ -147,7 +147,7 @@ def validate(self, objekt, name, value, return_pathlike=False):
147147
self.error(objekt, name, str(value))
148148

149149
if self.resolve:
150-
value = value.resolve(strict=self.exists)
150+
value = path_resolve(value, strict=self.exists)
151151

152152
if not return_pathlike:
153153
value = str(value)

nipype/pipeline/engine/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from ... import logging, config, LooseVersion
2626
from ...utils.filemanip import (
2727
Path,
28+
path_mkdir,
2829
indirectory,
2930
relpath,
3031
makedirs,
@@ -123,7 +124,7 @@ def write_node_report(node, result=None, is_mapnode=False):
123124

124125
cwd = node.output_dir()
125126
report_file = Path(cwd) / '_report' / 'report.rst'
126-
report_file.parent.mkdir(exist_ok=True, parents=True)
127+
path_mkdir(report_file.parent, exist_ok=True, parents=True)
127128

128129
lines = [
129130
write_rst_header('Node: %s' % get_print_name(node), level=0),

nipype/utils/filemanip.py

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -59,34 +59,42 @@ def __init__(self, path):
5959
from pathlib2 import Path
6060
USING_PATHLIB2 = True
6161

62-
try: # PY35 - strict mode was added in 3.6
63-
Path('/invented/file/path').resolve(strict=True)
64-
except TypeError:
65-
def _patch_resolve(self, strict=False):
66-
"""Add the argument strict to signature in Python>3,<3.6."""
67-
resolved = Path().old_resolve() / self
68-
69-
if strict and not resolved.exists():
70-
raise FileNotFoundError(resolved)
71-
return resolved
72-
73-
Path.old_resolve = Path.resolve
74-
Path.resolve = _patch_resolve
75-
except FileNotFoundError:
76-
pass
77-
except OSError:
78-
# PY2
79-
def _patch_resolve(self, strict=False):
80-
"""Raise FileNotFoundError instead of OSError with pathlib2."""
81-
try:
82-
resolved = self.old_resolve(strict=strict)
83-
except OSError:
84-
raise FileNotFoundError(self.old_resolve())
8562

86-
return resolved
63+
def _resolve_with_filenotfound(path, **kwargs):
64+
""" Raise FileNotFoundError instead of OSError """
65+
try:
66+
return path.resolve(**kwargs)
67+
except OSError as e:
68+
if isinstance(e, FileNotFoundError):
69+
raise
70+
raise FileNotFoundError(str(path))
71+
72+
73+
def path_resolve(path, strict=False):
74+
try:
75+
return _resolve_with_filenotfound(path, strict=strict)
76+
except TypeError: # PY35
77+
pass
78+
79+
path = path.absolute()
80+
if strict or path.exists():
81+
return _resolve_with_filenotfound(path)
82+
83+
# This is a hacky shortcut, using path.absolute() unmodified
84+
# In cases where the existing part of the path contains a
85+
# symlink, different results will be produced
86+
return path
87+
88+
89+
def path_mkdir(path, mode=0o777, parents=False, exist_ok=False):
90+
try:
91+
return path.mkdir(mode=mode, parents=parents, exist_ok=exist_ok)
92+
except TypeError: # PY27/PY34
93+
if parents:
94+
return makedirs(str(path), mode=mode, exist_ok=exist_ok)
95+
elif not exist_ok or not path.exists():
96+
return os.mkdir(str(path), mode=mode)
8797

88-
Path.old_resolve = Path.resolve
89-
Path.resolve = _patch_resolve
9098

9199
if not hasattr(Path, 'write_text'):
92100
# PY34 - Path does not have write_text
@@ -95,19 +103,6 @@ def _write_text(self, text):
95103
f.write(text)
96104
Path.write_text = _write_text
97105

98-
try: # PY27/PY34 - mkdir does not have exist_ok
99-
from .tmpdirs import TemporaryDirectory
100-
with TemporaryDirectory() as tmpdir:
101-
(Path(tmpdir) / 'exist_ok_test').mkdir(exist_ok=True)
102-
except TypeError:
103-
def _mkdir(self, mode=0o777, parents=False, exist_ok=False):
104-
if parents:
105-
makedirs(str(self), mode=mode, exist_ok=exist_ok)
106-
elif not exist_ok or not self.exists():
107-
os.mkdir(str(self), mode=mode)
108-
109-
Path.mkdir = _mkdir
110-
111106

112107
def split_filename(fname):
113108
"""Split a filename into parts: path, base filename and extension.

nipype/utils/tests/test_filemanip.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
check_forhash, _parse_mount_table, _cifs_table, on_cifs, copyfile,
1717
copyfiles, ensure_list, simplify_list, check_depends,
1818
split_filename, get_related_files, indirectory,
19-
loadpkl, loadcrash, savepkl, FileNotFoundError, Path)
19+
loadpkl, loadcrash, savepkl, FileNotFoundError, Path,
20+
path_mkdir, path_resolve)
2021

2122

2223
def _ignore_atime(stat):
@@ -572,21 +573,24 @@ def test_unversioned_pklization(tmpdir):
572573
loadpkl('./pickled.pkz')
573574

574575

575-
def test_Path_strict_resolve(tmpdir):
576+
def test_path_strict_resolve(tmpdir):
576577
"""Check the monkeypatch to test strict resolution of Path."""
577578
tmpdir.chdir()
578579

579580
# Default strict=False should work out out of the box
580581
testfile = Path('somefile.txt')
581-
assert '%s/somefile.txt' % tmpdir == '%s' % testfile.resolve()
582+
resolved = '%s/somefile.txt' % tmpdir
583+
assert str(path_resolve(testfile)) == resolved
584+
# Strict keyword is always allowed
585+
assert str(path_resolve(testfile, strict=False)) == resolved
582586

583587
# Switching to strict=True must raise FileNotFoundError (also in Python2)
584588
with pytest.raises(FileNotFoundError):
585-
testfile.resolve(strict=True)
589+
path_resolve(testfile, strict=True)
586590

587591
# If the file is created, it should not raise
588592
open('somefile.txt', 'w').close()
589-
assert '%s/somefile.txt' % tmpdir == '%s' % testfile.resolve(strict=True)
593+
assert str(path_resolve(testfile, strict=True)) == resolved
590594

591595

592596
@pytest.mark.parametrize("save_versioning", [True, False])
@@ -598,17 +602,18 @@ def test_pickle(tmp_path, save_versioning):
598602
assert outobj == testobj
599603

600604

601-
def test_Path(tmpdir):
605+
def test_path_mkdir(tmpdir):
602606
tmp_path = Path(tmpdir.strpath)
603607

604-
(tmp_path / 'textfile').write_text('some text')
608+
# PY34: Leave as monkey-patch
609+
Path.write_text(tmp_path / 'textfile', 'some text')
605610

606611
with pytest.raises(OSError):
607-
(tmp_path / 'no' / 'parents').mkdir(parents=False)
612+
path_mkdir(tmp_path / 'no' / 'parents', parents=False)
608613

609-
(tmp_path / 'no' / 'parents').mkdir(parents=True)
614+
path_mkdir(tmp_path / 'no' / 'parents', parents=True)
610615

611616
with pytest.raises(OSError):
612-
(tmp_path / 'no' / 'parents').mkdir(parents=False)
617+
path_mkdir(tmp_path / 'no' / 'parents', parents=False)
613618

614-
(tmp_path / 'no' / 'parents').mkdir(parents=True, exist_ok=True)
619+
path_mkdir(tmp_path / 'no' / 'parents', parents=True, exist_ok=True)

0 commit comments

Comments
 (0)