Passed
Push — main ( af1065...15d22f )
by Douglas
04:30
created

pocketutils.tools.path_info   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 149
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 85
dl 0
loc 149
rs 10
c 0
b 0
f 0
wmc 23

19 Methods

Rating   Name   Duplication   Size   Complexity  
A PathInfo.is_socket() 0 3 1
A PathInfo.is_broken_symlink() 0 3 1
A PathInfo.exists() 0 6 1
A PathInfo._get_dt() 0 5 2
A PathInfo.is_readable_file() 0 3 1
A PathInfo.is_dir() 0 3 1
A PathInfo.access_dt() 0 7 1
A PathInfo.is_writeable_dir() 0 3 1
A PathInfo.is_char_device() 0 3 1
A PathInfo.is_symlink() 0 3 1
A PathInfo.create_dt() 0 9 2
A PathInfo.is_fifo() 0 3 1
A PathInfo.mod_or_create_dt() 0 10 2
A PathInfo.is_file() 0 3 1
A PathInfo.is_writeable_file() 0 3 1
A PathInfo.mod_dt() 0 9 2
A PathInfo.is_valid_symlink() 0 3 1
A PathInfo.is_readable_dir() 0 3 1
A PathInfo.is_block_device() 0 3 1
1
import logging
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
import os
3
import stat
4
from dataclasses import dataclass
5
from datetime import datetime
6
from pathlib import Path
7
8
logger = logging.getLogger("pocketutils")
9
COMPRESS_LEVEL = 9
10
11
12
@dataclass(frozen=True, repr=True)
0 ignored issues
show
best-practice introduced by
Too many instance attributes (8/7)
Loading history...
13
class PathInfo:
14
    """
15
    Info about an extant or nonexistent path as it was at some time.
16
    Use this to avoid making repeated filesystem calls (e.g. ``.is_dir()``):
17
    None of the properties defined here make OS calls.
18
19
    Attributes:
20
        source: The original path used for lookup; may be a symlink
21
        resolved: The fully resolved path, or None if it does not exist
22
        as_of: A datetime immediately before the system calls (system timezone)
23
        real_stat: ``os.stat_result``, or None if the path does not exist
24
        link_stat: ``os.stat_result``, or None if the path is not a symlink
25
        has_access: Path exists and has the 'a' flag set
26
        has_read: Path exists and has the 'r' flag set
27
        has_write: Path exists and has the 'w' flag set
28
29
    All the additional properties refer to the resolved path,
30
    except for :meth:`is_symlink`, :meth:`is_valid_symlink`,
31
    and :meth:`is_broken_symlink`.
32
    """
33
34
    source: Path
35
    resolved: Path | None
36
    as_of: datetime
37
    real_stat: os.stat_result | None
38
    link_stat: os.stat_result | None
39
    has_access: bool
40
    has_read: bool
41
    has_write: bool
42
43
    @property
44
    def mod_or_create_dt(self) -> datetime | None:
45
        """
46
        Returns the modification or access datetime.
47
        Uses whichever is available: creation on Windows and modification on Unix-like.
48
        """
49
        if os.name == "nt":
50
            return self._get_dt("st_ctime")
51
        # will work on posix; on java try anyway
52
        return self._get_dt("st_mtime")
53
54
    @property
55
    def mod_dt(self) -> datetime | None:
56
        """
57
        Returns the modification datetime, if known.
58
        Returns None on Windows or if the path does not exist.
59
        """
60
        if os.name == "nt":
61
            return None
62
        return self._get_dt("st_mtime")
63
64
    @property
65
    def create_dt(self) -> datetime | None:
66
        """
67
        Returns the creation datetime, if known.
68
        Returns None on Unix-like systems or if the path does not exist.
69
        """
70
        if os.name == "posix":
71
            return None
72
        return self._get_dt("st_ctime")
73
74
    @property
75
    def access_dt(self) -> datetime | None:
76
        """
77
        Returns the access datetime.
78
        *Should* never return None if the path exists, but not guaranteed.
79
        """
80
        return self._get_dt("st_atime")
81
82
    @property
83
    def exists(self) -> bool:
84
        """
85
        Returns whether the resolved path exists.
86
        """
87
        return self.real_stat is not None
88
89
    @property
90
    def is_file(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
91
        return self.exists and stat.S_ISREG(self.real_stat.st_mode)
92
93
    @property
94
    def is_dir(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
95
        return self.exists and stat.S_ISDIR(self.real_stat.st_mode)
96
97
    @property
98
    def is_readable_dir(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
99
        return self.is_file and self.has_access and self.has_read
100
101
    @property
102
    def is_writeable_dir(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
103
        return self.is_dir and self.has_access and self.has_write
104
105
    @property
106
    def is_readable_file(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
107
        return self.is_file and self.has_access and self.has_read
108
109
    @property
110
    def is_writeable_file(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
111
        return self.is_file and self.has_access and self.has_write
112
113
    @property
114
    def is_block_device(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
115
        return self.exists and stat.S_ISBLK(self.real_stat.st_mode)
116
117
    @property
118
    def is_char_device(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
119
        return self.exists and stat.S_ISCHR(self.real_stat.st_mode)
120
121
    @property
122
    def is_socket(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
123
        return self.exists and stat.S_ISSOCK(self.real_stat.st_mode)
124
125
    @property
126
    def is_fifo(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
127
        return self.exists and stat.S_ISFIFO(self.real_stat.st_mode)
128
129
    @property
130
    def is_symlink(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
131
        return self.link_stat is not None
132
133
    @property
134
    def is_valid_symlink(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
135
        return self.is_symlink and self.exists
136
137
    @property
138
    def is_broken_symlink(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
139
        return self.is_symlink and not self.exists
140
141
    def _get_dt(self, attr: str) -> datetime | None:
142
        if self.real_stat is None:
143
            return None
144
        sec = getattr(self.real_stat, attr)
145
        return datetime.fromtimestamp(sec).astimezone()
146
147
148
__all__ = ["PathInfo"]
149