Skip to content

Commit

Permalink
Added .is_dir and .is_file for parity with pathlib.
Browse files Browse the repository at this point in the history
Ref #214
  • Loading branch information
jaraco committed Apr 4, 2024
1 parent 8f325dc commit 6abd966
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 35 deletions.
1 change: 1 addition & 0 deletions newsfragments/214.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added .is_dir and .is_file for parity with pathlib. Deprecates .isdir and .isfile.
38 changes: 26 additions & 12 deletions path/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class Traversal:
Directories beginning with `.` will appear in the results, but
their children will not.
>>> dot_dir = next(item for item in items if item.isdir() and item.startswith('.'))
>>> dot_dir = next(item for item in items if item.is_dir() and item.startswith('.'))
>>> any(item.parent == dot_dir for item in items)
False
"""
Expand Down Expand Up @@ -566,7 +566,7 @@ def dirs(self, *args, **kwargs):
Accepts parameters to :meth:`iterdir`.
"""
return [p for p in self.iterdir(*args, **kwargs) if p.isdir()]
return [p for p in self.iterdir(*args, **kwargs) if p.is_dir()]

def files(self, *args, **kwargs):
"""List of the files in self.
Expand All @@ -577,14 +577,14 @@ def files(self, *args, **kwargs):
Accepts parameters to :meth:`iterdir`.
"""

return [p for p in self.iterdir(*args, **kwargs) if p.isfile()]
return [p for p in self.iterdir(*args, **kwargs) if p.is_file()]

def walk(self, match=None, errors='strict'):
"""Iterator over files and subdirs, recursively.
The iterator yields Path objects naming each child item of
this directory and its descendants. This requires that
``D.isdir()``.
``D.is_dir()``.
This performs a depth-first traversal of the directory tree.
Each directory is returned just before all its children.
Expand All @@ -609,7 +609,7 @@ def walk(self, match=None, errors='strict'):
traverse = None
if match(child):
traverse = yield child
traverse = traverse or child.isdir
traverse = traverse or child.is_dir
try:
do_traverse = traverse()
except Exception as exc:
Expand All @@ -621,11 +621,11 @@ def walk(self, match=None, errors='strict'):

def walkdirs(self, *args, **kwargs):
"""Iterator over subdirs, recursively."""
return (item for item in self.walk(*args, **kwargs) if item.isdir())
return (item for item in self.walk(*args, **kwargs) if item.is_dir())

def walkfiles(self, *args, **kwargs):
"""Iterator over files, recursively."""
return (item for item in self.walk(*args, **kwargs) if item.isfile())
return (item for item in self.walk(*args, **kwargs) if item.is_file())

def fnmatch(self, pattern, normcase=None):
"""Return ``True`` if `self.name` matches the given `pattern`.
Expand Down Expand Up @@ -1097,10 +1097,24 @@ def exists(self):
return self.module.exists(self)

def isdir(self):
warnings.warn(
"isdir is deprecated; use is_dir",
DeprecationWarning,
stacklevel=2,
)

def is_dir(self):
""".. seealso:: :func:`os.path.isdir`"""
return self.module.isdir(self)

def isfile(self):
warnings.warn(
"isfile is deprecated; use is_file",
DeprecationWarning,
stacklevel=2,
)

def is_file(self):
""".. seealso:: :func:`os.path.isfile`"""
return self.module.isfile(self)

Expand Down Expand Up @@ -1556,7 +1570,7 @@ def ignored(item):
if symlinks and source.islink():
target = source.readlink()
target.symlink(dest)
elif source.isdir():
elif source.is_dir():
source.merge_tree(
dest,
symlinks=symlinks,
Expand Down Expand Up @@ -1613,7 +1627,7 @@ def in_place(
For example, to add line numbers to a file::
p = Path(filename)
assert p.isfile()
assert p.is_file()
with p.in_place() as (reader, writer):
for number, line in enumerate(reader, 1):
writer.write('{0:3}: '.format(number)))
Expand Down Expand Up @@ -1747,7 +1761,7 @@ class ExtantFile(Path):
"""

def _validate(self):
if not self.isfile():
if not self.is_file():
raise FileNotFoundError(f"{self} does not exist as a file.")


Expand Down Expand Up @@ -1818,12 +1832,12 @@ class TempDir(Path):
For example:
>>> with TempDir() as d:
... d.isdir() and isinstance(d, Path)
... d.is_dir() and isinstance(d, Path)
True
The directory is deleted automatically.
>>> d.isdir()
>>> d.is_dir()
False
.. seealso:: :func:`tempfile.mkdtemp`
Expand Down
2 changes: 2 additions & 0 deletions path/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ class Path(str):
def isabs(self) -> bool: ...
def exists(self) -> bool: ...
def isdir(self) -> bool: ...
def is_dir(self) -> bool: ...
def isfile(self) -> bool: ...
def is_file(self) -> bool: ...
def islink(self) -> bool: ...
def ismount(self) -> bool: ...
def samefile(self, other: str) -> bool: ...
Expand Down
46 changes: 23 additions & 23 deletions test_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,10 @@ def test_removedirs_p(self, tmpdir):
(dir / 'file').touch()
(dir / 'sub').mkdir()
dir.removedirs_p()
assert dir.isdir()
assert (dir / 'file').isfile()
assert dir.is_dir()
assert (dir / 'file').is_file()
# TODO: shouldn't sub get removed?
# assert not (dir / 'sub').isdir()
# assert not (dir / 'sub').is_dir()


class TestReadWriteText:
Expand Down Expand Up @@ -360,7 +360,7 @@ def test_symlink_none(self, tmpdir):
with root:
file = (Path('dir').mkdir() / 'file').touch()
file.symlink()
assert Path('file').isfile()
assert Path('file').is_file()

def test_readlinkabs_passthrough(self, tmpdir):
link = Path(tmpdir) / 'link'
Expand All @@ -383,7 +383,7 @@ def test_skip_symlinks(self, tmpdir):
assert len(list(root.walk())) == 4

skip_links = path.Traversal(
lambda item: item.isdir() and not item.islink(),
lambda item: item.is_dir() and not item.islink(),
)
assert len(list(skip_links(root.walk()))) == 3

Expand Down Expand Up @@ -474,7 +474,7 @@ def test_touch(self, tmpdir):
t1 = time.time() + threshold

assert f.exists()
assert f.isfile()
assert f.is_file()
assert f.size == 0
assert t0 <= f.mtime <= t1
if hasattr(os.path, 'getctime'):
Expand All @@ -493,7 +493,7 @@ def test_touch(self, tmpdir):
assert t0 <= t1 < t2 <= t3 # sanity check

assert f.exists()
assert f.isfile()
assert f.is_file()
assert f.size == 10
assert t2 <= f.mtime <= t3
if hasattr(os.path, 'getctime'):
Expand Down Expand Up @@ -593,7 +593,7 @@ def test_makedirs(self, tmpdir):
boz = foo / 'bar' / 'baz' / 'boz'
boz.makedirs()
try:
assert boz.isdir()
assert boz.is_dir()
finally:
boz.removedirs()
assert not foo.exists()
Expand All @@ -602,7 +602,7 @@ def test_makedirs(self, tmpdir):
foo.mkdir(0o750)
boz.makedirs(0o700)
try:
assert boz.isdir()
assert boz.is_dir()
finally:
boz.removedirs()
assert not foo.exists()
Expand Down Expand Up @@ -648,21 +648,21 @@ def test_shutil(self, tmpdir):

# Test simple file copying.
testFile.copyfile(testCopy)
assert testCopy.isfile()
assert testCopy.is_file()
assert testFile.bytes() == testCopy.bytes()

# Test copying into a directory.
testCopy2 = testA / testFile.name
testFile.copy(testA)
assert testCopy2.isfile()
assert testCopy2.is_file()
assert testFile.bytes() == testCopy2.bytes()

# Make a link for the next test to use.
testFile.symlink(testLink)

# Test copying directory tree.
testA.copytree(testC)
assert testC.isdir()
assert testC.is_dir()
self.assertSetsEqual(
testC.iterdir(),
[testC / testCopy.name, testC / testFile.name, testCopyOfLink],
Expand All @@ -675,7 +675,7 @@ def test_shutil(self, tmpdir):

# Copy again, preserving symlinks.
testA.copytree(testC, True)
assert testC.isdir()
assert testC.is_dir()
self.assertSetsEqual(
testC.iterdir(),
[testC / testCopy.name, testC / testFile.name, testCopyOfLink],
Expand All @@ -698,7 +698,7 @@ def test_patterns(self, tmpdir):
dirs = [d, d / 'xdir', d / 'xdir.tmp', d / 'xdir.tmp' / 'xsubdir']

for e in dirs:
if not e.isdir():
if not e.is_dir():
e.makedirs()

for name in names:
Expand Down Expand Up @@ -913,12 +913,12 @@ def testing_structure(self, tmpdir):

def check_link(self):
target = Path(self.subdir_b / self.test_link.name)
check = target.islink if hasattr(os, 'symlink') else target.isfile
check = target.islink if hasattr(os, 'symlink') else target.is_file
assert check()

def test_with_nonexisting_dst_kwargs(self):
self.subdir_a.merge_tree(self.subdir_b, symlinks=True)
assert self.subdir_b.isdir()
assert self.subdir_b.is_dir()
expected = {
self.subdir_b / self.test_file.name,
self.subdir_b / self.test_link.name,
Expand All @@ -928,7 +928,7 @@ def test_with_nonexisting_dst_kwargs(self):

def test_with_nonexisting_dst_args(self):
self.subdir_a.merge_tree(self.subdir_b, True)
assert self.subdir_b.isdir()
assert self.subdir_b.is_dir()
expected = {
self.subdir_b / self.test_file.name,
self.subdir_b / self.test_link.name,
Expand All @@ -948,7 +948,7 @@ def test_with_existing_dst(self):

self.subdir_a.merge_tree(self.subdir_b, True)

assert self.subdir_b.isdir()
assert self.subdir_b.is_dir()
expected = {
self.subdir_b / self.test_file.name,
self.subdir_b / self.test_link.name,
Expand All @@ -965,7 +965,7 @@ def test_copytree_parameters(self):
ignore = shutil.ignore_patterns('testlink*')
self.subdir_a.merge_tree(self.subdir_b, ignore=ignore)

assert self.subdir_b.isdir()
assert self.subdir_b.is_dir()
assert list(self.subdir_b.iterdir()) == [self.subdir_b / self.test_file.name]

def test_only_newer(self):
Expand All @@ -984,7 +984,7 @@ def test_only_newer(self):
def test_nested(self):
self.subdir_a.joinpath('subsub').mkdir()
self.subdir_a.merge_tree(self.subdir_b)
assert self.subdir_b.joinpath('subsub').isdir()
assert self.subdir_b.joinpath('subsub').is_dir()

def test_listdir(self):
with pytest.deprecated_call():
Expand Down Expand Up @@ -1040,7 +1040,7 @@ def test_constructor(self):
d = TempDir()
assert isinstance(d, path.Path)
assert d.exists()
assert d.isdir()
assert d.is_dir()
d.rmdir()
assert not d.exists()

Expand Down Expand Up @@ -1074,8 +1074,8 @@ def test_context_manager_using_with(self):
"""

with TempDir() as d:
assert d.isdir()
assert not d.isdir()
assert d.is_dir()
assert not d.is_dir()

def test_cleaned_up_on_interrupt(self):
with contextlib.suppress(KeyboardInterrupt):
Expand Down

0 comments on commit 6abd966

Please sign in to comment.