Skip to content

Commit 3b20450

Browse files
committed
Improve TargetPath compatibility
1 parent 01373f4 commit 3b20450

File tree

11 files changed

+279
-670
lines changed

11 files changed

+279
-670
lines changed

dissect/target/exceptions.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import errno
34
import os
45
import sys
56
import traceback
@@ -76,22 +77,32 @@ class PluginNotFoundError(PluginError):
7677
class FileNotFoundError(FilesystemError, FileNotFoundError):
7778
"""The requested path could not be found."""
7879

80+
errno = errno.ENOENT
81+
7982

8083
class IsADirectoryError(FilesystemError, IsADirectoryError):
8184
"""The entry is a directory."""
8285

86+
errno = errno.EISDIR
87+
8388

8489
class NotADirectoryError(FilesystemError, NotADirectoryError):
8590
"""The entry is not a directory."""
8691

92+
errno = errno.ENOTDIR
93+
8794

8895
class NotASymlinkError(FilesystemError):
8996
"""The entry is not a symlink."""
9097

98+
errno = errno.EINVAL
99+
91100

92101
class SymlinkRecursionError(FilesystemError):
93102
"""A symlink loop is detected for the entry."""
94103

104+
errno = errno.ELOOP
105+
95106

96107
class RegistryError(Error):
97108
"""A registry error occurred."""

dissect/target/helpers/compat/path_310.py

Lines changed: 18 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,15 @@
2020
import fnmatch
2121
import re
2222
from pathlib import Path, PurePath, _Accessor, _PosixFlavour
23-
from stat import S_ISBLK, S_ISCHR, S_ISFIFO, S_ISSOCK
24-
from typing import IO, TYPE_CHECKING, Any, Callable, Iterator, Optional
23+
from typing import IO, TYPE_CHECKING, Any, Callable, Iterator
2524

2625
from dissect.target import filesystem
2726
from dissect.target.exceptions import FilesystemError, SymlinkRecursionError
28-
from dissect.target.helpers.compat.path_common import (
29-
_DissectPathParents,
30-
io_open,
31-
isjunction,
32-
realpath,
33-
scandir,
34-
)
35-
from dissect.target.helpers.polypath import normalize
27+
from dissect.target.helpers import polypath
28+
from dissect.target.helpers.compat import path_common
3629

3730
if TYPE_CHECKING:
3831
from dissect.target.filesystem import Filesystem, FilesystemEntry
39-
from dissect.target.helpers.compat.path_common import _DissectScandirIterator
4032
from dissect.target.helpers.fsutil import stat_result
4133

4234

@@ -82,9 +74,9 @@ def open(
8274
path: TargetPath,
8375
mode: str = "rb",
8476
buffering: int = 0,
85-
encoding: Optional[str] = None,
86-
errors: Optional[str] = None,
87-
newline: Optional[str] = None,
77+
encoding: str | None = None,
78+
errors: str | None = None,
79+
newline: str | None = None,
8880
) -> IO:
8981
"""Open file and return a stream.
9082
@@ -93,15 +85,15 @@ def open(
9385
Note: in contrast to regular Python, the mode is binary by default. Text mode
9486
has to be explicitly specified. Buffering is also disabled by default.
9587
"""
96-
return io_open(path, mode, buffering, encoding, errors, newline)
88+
return path_common.io_open(path, mode, buffering, encoding, errors, newline)
9789

9890
@staticmethod
9991
def listdir(path: TargetPath) -> Iterator[str]:
10092
return path.get().listdir()
10193

10294
@staticmethod
103-
def scandir(path: TargetPath) -> _DissectScandirIterator:
104-
return scandir(path)
95+
def scandir(path: TargetPath) -> path_common._DissectScandirIterator:
96+
return path_common.scandir(path)
10597

10698
@staticmethod
10799
def chmod(path: TargetPath, mode: int, *, follow_symlinks: bool = True) -> None:
@@ -163,10 +155,10 @@ def getcwd() -> str:
163155
def expanduser(path: str) -> str:
164156
raise NotImplementedError("TargetPath.expanduser() is unsupported")
165157

166-
realpath = staticmethod(realpath)
158+
realpath = staticmethod(path_common.realpath)
167159

168160
# NOTE: Forward compatibility with CPython >= 3.12
169-
isjunction = staticmethod(isjunction)
161+
isjunction = staticmethod(path_common.isjunction)
170162

171163

172164
_dissect_accessor = _DissectAccessor()
@@ -193,7 +185,7 @@ def _from_parts(cls, args: list) -> TargetPath:
193185
path_args = []
194186
for arg in args[1:]:
195187
if isinstance(arg, str):
196-
arg = normalize(arg, alt_separator=alt_separator)
188+
arg = polypath.normalize(arg, alt_separator=alt_separator)
197189
path_args.append(arg)
198190

199191
self = super()._from_parts(path_args)
@@ -247,8 +239,8 @@ def parent(self) -> TargetPath:
247239
return result
248240

249241
@property
250-
def parents(self) -> _DissectPathParents:
251-
return _DissectPathParents(self)
242+
def parents(self) -> path_common._DissectPathParents:
243+
return path_common._DissectPathParents(self)
252244

253245

254246
class TargetPath(Path, PureDissectPath):
@@ -382,9 +374,9 @@ def open(
382374
self,
383375
mode: str = "rb",
384376
buffering: int = 0,
385-
encoding: Optional[str] = None,
386-
errors: Optional[str] = None,
387-
newline: Optional[str] = None,
377+
encoding: str | None = None,
378+
errors: str | None = None,
379+
newline: str | None = None,
388380
) -> IO:
389381
"""Open file and return a stream.
390382
@@ -402,7 +394,7 @@ def write_bytes(self, data: bytes) -> int:
402394
raise NotImplementedError("TargetPath.write_bytes() is unsupported")
403395

404396
def write_text(
405-
self, data: str, encoding: Optional[str] = None, errors: Optional[str] = None, newline: Optional[str] = None
397+
self, data: str, encoding: str | None = None, errors: str | None = None, newline: str | None = None
406398
) -> int:
407399
"""
408400
Open the file in text mode, write to it, and close the file.
@@ -417,88 +409,13 @@ def readlink(self) -> TargetPath:
417409
obj = self._from_parts((self._fs, path))
418410
return obj
419411

420-
def exists(self) -> bool:
421-
"""
422-
Whether this path exists.
423-
"""
424-
try:
425-
# .exists() must resolve possible symlinks
426-
self.get().stat()
427-
return True
428-
except (FilesystemError, ValueError):
429-
return False
430-
431-
def is_dir(self) -> bool:
432-
"""
433-
Whether this path is a directory.
434-
"""
435-
try:
436-
return self.get().is_dir()
437-
except (FilesystemError, ValueError):
438-
return False
439-
440-
def is_file(self) -> bool:
441-
"""
442-
Whether this path is a regular file (also True for symlinks pointing
443-
to regular files).
444-
"""
445-
try:
446-
return self.get().is_file()
447-
except (FilesystemError, ValueError):
448-
return False
449-
450-
def is_symlink(self) -> bool:
451-
"""
452-
Whether this path is a symbolic link.
453-
"""
454-
try:
455-
return self.get().is_symlink()
456-
except (FilesystemError, ValueError):
457-
return False
458-
459412
# NOTE: Forward compatibility with CPython >= 3.12
460413
def is_junction(self) -> bool:
461414
"""
462415
Whether this path is a junction.
463416
"""
464417
return self._accessor.isjunction(self)
465418

466-
def is_block_device(self) -> bool:
467-
"""
468-
Whether this path is a block device.
469-
"""
470-
try:
471-
return S_ISBLK(self.stat().st_mode)
472-
except (FilesystemError, ValueError):
473-
return False
474-
475-
def is_char_device(self) -> bool:
476-
"""
477-
Whether this path is a character device.
478-
"""
479-
try:
480-
return S_ISCHR(self.stat().st_mode)
481-
except (FilesystemError, ValueError):
482-
return False
483-
484-
def is_fifo(self) -> bool:
485-
"""
486-
Whether this path is a FIFO.
487-
"""
488-
try:
489-
return S_ISFIFO(self.stat().st_mode)
490-
except (FilesystemError, ValueError):
491-
return False
492-
493-
def is_socket(self) -> bool:
494-
"""
495-
Whether this path is a socket.
496-
"""
497-
try:
498-
return S_ISSOCK(self.stat().st_mode)
499-
except (FilesystemError, ValueError):
500-
return False
501-
502419
def expanduser(self) -> TargetPath:
503420
"""Return a new path with expanded ~ and ~user constructs
504421
(as returned by os.path.expanduser)

0 commit comments

Comments
 (0)