pathier

1from .pathier import Pathier
2
3__all__ = ["Pathier"]
class Pathier(pathlib.Path):
 13class Pathier(pathlib.Path):
 14    """Subclasses the standard library pathlib.Path class."""
 15
 16    def __new__(cls, *args, **kwargs):
 17        if cls is Pathier:
 18            cls = WindowsPath if os.name == "nt" else PosixPath
 19        self = cls._from_parts(args)
 20        if not self._flavour.is_supported:
 21            raise NotImplementedError(
 22                "cannot instantiate %r on your system" % (cls.__name__,)
 23            )
 24        return self
 25
 26    def moveup(self, name: str) -> Self:
 27        """Return a new Pathier obj that is a parent of this instance.
 28
 29        :param name: The parent directory that should be the stem of the returned Pathier obj.
 30        'name' is case-sensitive and raises an exception if it isn't in self.parts.
 31        >>> p = Pathier("C:\some\directory\in\your\system")
 32        >>> print(p.moveup("directory"))
 33        >>> "C:\some\directory"
 34        >>> print(p.moveup("yeet"))
 35        >>> "Exception: yeet is not a parent of C:\some\directory\in\your\system" """
 36        if name not in self.parts:
 37            raise Exception(f"{name} is not a parent of {self}")
 38        return Pathier(*(self.parts[: self.parts.index(name) + 1]))
 39
 40    def __sub__(self, levels: int) -> Self:
 41        """Return a new Pathier obj moved up 'levels' number of parents from the current path.
 42        >>> p = Pathier("C:\some\directory\in\your\system")
 43        >>> new_p = p - 3
 44        >>> print(new_p)
 45        >>> "C:\some\directory" """
 46        path = self
 47        for _ in range(levels):
 48            path = path.parent
 49        return path
 50
 51    def mkdir(self, mode: int = 511, parents: bool = True, exist_ok: bool = True):
 52        """Create this directory.
 53        Same as Path().mkdir() except
 54        'parents' and 'exist_ok' default
 55        to True instead of False."""
 56        super().mkdir(mode, parents, exist_ok)
 57
 58    def touch(self):
 59        """Create file and parents if necessary."""
 60        self.parent.mkdir()
 61        super().touch()
 62
 63    def write_text(
 64        self,
 65        data: Any,
 66        encoding: Any | None = None,
 67        errors: Any | None = None,
 68        newline: Any | None = None,
 69        parents: bool = True,
 70    ):
 71        """Write data to file. If a TypeError is raised, the function
 72        will attempt to case data to a str and try the write again.
 73        If a FileNotFoundError is raised and parents = True,
 74        self.parent will be created."""
 75        write = functools.partial(
 76            super().write_text,
 77            encoding=encoding,
 78            errors=errors,
 79            newline=newline,
 80        )
 81        try:
 82            write(data)
 83        except TypeError:
 84            data = str(data)
 85            write(data)
 86        except FileNotFoundError:
 87            if parents:
 88                self.parent.mkdir(parents=True)
 89                write(data)
 90            else:
 91                raise
 92        except Exception as e:
 93            raise
 94
 95    def write_bytes(self, data: bytes, parents: bool = True):
 96        """Write bytes to file.
 97
 98        :param parents: If True and the write operation fails
 99        with a FileNotFoundError, make the parent directory
100        and retry the write."""
101        try:
102            super().write_bytes(data)
103        except FileNotFoundError:
104            if parents:
105                self.parent.mkdir(parents=True)
106                super().write_bytes(data)
107            else:
108                raise
109        except Exception as e:
110            raise
111
112    def json_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
113        """Load json file."""
114        return json.loads(self.read_text(encoding, errors))
115
116    def json_dumps(
117        self,
118        data: Any,
119        encoding: Any | None = None,
120        errors: Any | None = None,
121        newline: Any | None = None,
122        sort_keys: bool = False,
123        indent: Any | None = None,
124        default: Any | None = None,
125        parents: bool = True,
126    ) -> Any:
127        """Dump data to json file."""
128        self.write_text(
129            json.dumps(data, indent=indent, default=default, sort_keys=sort_keys),
130            encoding,
131            errors,
132            newline,
133            parents,
134        )
135
136    def toml_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
137        """Load toml file."""
138        return tomlkit.loads(self.read_text(encoding, errors))
139
140    def toml_dumps(
141        self,
142        data: Any,
143        encoding: Any | None = None,
144        errors: Any | None = None,
145        newline: Any | None = None,
146        sort_keys: bool = False,
147        parents: bool = True,
148    ):
149        """Dump data to toml file."""
150        self.write_text(
151            tomlkit.dumps(data, sort_keys), encoding, errors, newline, parents
152        )
153
154    def loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
155        """Load a json or toml file based off this instance's suffix."""
156        match self.suffix:
157            case ".json":
158                return self.json_loads(encoding, errors)
159            case ".toml":
160                return self.toml_loads(encoding, errors)
161
162    def dumps(
163        self,
164        data: Any,
165        encoding: Any | None = None,
166        errors: Any | None = None,
167        newline: Any | None = None,
168        sort_keys: bool = False,
169        indent: Any | None = None,
170        default: Any | None = None,
171        parents: bool = True,
172    ):
173        """Dump data to a json or toml file based off this instance's suffix."""
174        match self.suffix:
175            case ".json":
176                self.json_dumps(
177                    data, encoding, errors, newline, sort_keys, indent, default, parents
178                )
179            case ".toml":
180                self.toml_dumps(data, encoding, errors, newline, sort_keys, parents)
181
182    def delete(self, missing_ok: bool = True):
183        """Delete the file or folder pointed to by this instance.
184        Uses self.unlink() if a file and uses shutil.rmtree() if a directory."""
185        if self.is_file():
186            self.unlink(missing_ok)
187        elif self.is_dir():
188            shutil.rmtree(self)
189
190    def copy(
191        self, new_path: Self | pathlib.Path | str, overwrite: bool = False
192    ) -> Self:
193        """Copy the path pointed to by this instance
194        to the instance pointed to by new_path using shutil.copyfile
195        or shutil.copytree. Returns the new path.
196
197        :param new_path: The copy destination.
198
199        :param overwrite: If True, files already existing in new_path
200        will be overwritten. If False, only files that don't exist in new_path
201        will be copied."""
202        new_path = Pathier(new_path)
203        if self.is_dir():
204            if overwrite or not new_path.exists():
205                shutil.copytree(self, new_path, dirs_exist_ok=True)
206            else:
207                files = self.rglob("*.*")
208                for file in files:
209                    dst = new_path.with_name(file.name)
210                    if not dst.exists():
211                        shutil.copyfile(file, dst)
212        elif self.is_file():
213            if overwrite or not new_path.exists():
214                shutil.copyfile(self, new_path)
215        return new_path

Subclasses the standard library pathlib.Path class.

Pathier()
def moveup(self, name: str) -> Self:
26    def moveup(self, name: str) -> Self:
27        """Return a new Pathier obj that is a parent of this instance.
28
29        :param name: The parent directory that should be the stem of the returned Pathier obj.
30        'name' is case-sensitive and raises an exception if it isn't in self.parts.
31        >>> p = Pathier("C:\some\directory\in\your\system")
32        >>> print(p.moveup("directory"))
33        >>> "C:\some\directory"
34        >>> print(p.moveup("yeet"))
35        >>> "Exception: yeet is not a parent of C:\some\directory\in\your\system" """
36        if name not in self.parts:
37            raise Exception(f"{name} is not a parent of {self}")
38        return Pathier(*(self.parts[: self.parts.index(name) + 1]))

Return a new Pathier obj that is a parent of this instance.

Parameters
  • name: The parent directory that should be the stem of the returned Pathier obj. 'name' is case-sensitive and raises an exception if it isn't in self.parts. >>> p = Pathier("C:\some\directory\in\your\system") >>> print(p.moveup("directory")) >>> "C:\some\directory" >>> print(p.moveup("yeet")) >>> "Exception: yeet is not a parent of C:\some\directory\in\your\system"
def mkdir(self, mode: int = 511, parents: bool = True, exist_ok: bool = True):
51    def mkdir(self, mode: int = 511, parents: bool = True, exist_ok: bool = True):
52        """Create this directory.
53        Same as Path().mkdir() except
54        'parents' and 'exist_ok' default
55        to True instead of False."""
56        super().mkdir(mode, parents, exist_ok)

Create this directory. Same as Path().mkdir() except 'parents' and 'exist_ok' default to True instead of False.

def touch(self):
58    def touch(self):
59        """Create file and parents if necessary."""
60        self.parent.mkdir()
61        super().touch()

Create file and parents if necessary.

def write_text( self, data: Any, encoding: typing.Any | None = None, errors: typing.Any | None = None, newline: typing.Any | None = None, parents: bool = True):
63    def write_text(
64        self,
65        data: Any,
66        encoding: Any | None = None,
67        errors: Any | None = None,
68        newline: Any | None = None,
69        parents: bool = True,
70    ):
71        """Write data to file. If a TypeError is raised, the function
72        will attempt to case data to a str and try the write again.
73        If a FileNotFoundError is raised and parents = True,
74        self.parent will be created."""
75        write = functools.partial(
76            super().write_text,
77            encoding=encoding,
78            errors=errors,
79            newline=newline,
80        )
81        try:
82            write(data)
83        except TypeError:
84            data = str(data)
85            write(data)
86        except FileNotFoundError:
87            if parents:
88                self.parent.mkdir(parents=True)
89                write(data)
90            else:
91                raise
92        except Exception as e:
93            raise

Write data to file. If a TypeError is raised, the function will attempt to case data to a str and try the write again. If a FileNotFoundError is raised and parents = True, self.parent will be created.

def write_bytes(self, data: bytes, parents: bool = True):
 95    def write_bytes(self, data: bytes, parents: bool = True):
 96        """Write bytes to file.
 97
 98        :param parents: If True and the write operation fails
 99        with a FileNotFoundError, make the parent directory
100        and retry the write."""
101        try:
102            super().write_bytes(data)
103        except FileNotFoundError:
104            if parents:
105                self.parent.mkdir(parents=True)
106                super().write_bytes(data)
107            else:
108                raise
109        except Exception as e:
110            raise

Write bytes to file.

Parameters
  • parents: If True and the write operation fails with a FileNotFoundError, make the parent directory and retry the write.
def json_loads( self, encoding: typing.Any | None = None, errors: typing.Any | None = None) -> Any:
112    def json_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
113        """Load json file."""
114        return json.loads(self.read_text(encoding, errors))

Load json file.

def json_dumps( self, data: Any, encoding: typing.Any | None = None, errors: typing.Any | None = None, newline: typing.Any | None = None, sort_keys: bool = False, indent: typing.Any | None = None, default: typing.Any | None = None, parents: bool = True) -> Any:
116    def json_dumps(
117        self,
118        data: Any,
119        encoding: Any | None = None,
120        errors: Any | None = None,
121        newline: Any | None = None,
122        sort_keys: bool = False,
123        indent: Any | None = None,
124        default: Any | None = None,
125        parents: bool = True,
126    ) -> Any:
127        """Dump data to json file."""
128        self.write_text(
129            json.dumps(data, indent=indent, default=default, sort_keys=sort_keys),
130            encoding,
131            errors,
132            newline,
133            parents,
134        )

Dump data to json file.

def toml_loads( self, encoding: typing.Any | None = None, errors: typing.Any | None = None) -> Any:
136    def toml_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
137        """Load toml file."""
138        return tomlkit.loads(self.read_text(encoding, errors))

Load toml file.

def toml_dumps( self, data: Any, encoding: typing.Any | None = None, errors: typing.Any | None = None, newline: typing.Any | None = None, sort_keys: bool = False, parents: bool = True):
140    def toml_dumps(
141        self,
142        data: Any,
143        encoding: Any | None = None,
144        errors: Any | None = None,
145        newline: Any | None = None,
146        sort_keys: bool = False,
147        parents: bool = True,
148    ):
149        """Dump data to toml file."""
150        self.write_text(
151            tomlkit.dumps(data, sort_keys), encoding, errors, newline, parents
152        )

Dump data to toml file.

def loads( self, encoding: typing.Any | None = None, errors: typing.Any | None = None) -> Any:
154    def loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
155        """Load a json or toml file based off this instance's suffix."""
156        match self.suffix:
157            case ".json":
158                return self.json_loads(encoding, errors)
159            case ".toml":
160                return self.toml_loads(encoding, errors)

Load a json or toml file based off this instance's suffix.

def dumps( self, data: Any, encoding: typing.Any | None = None, errors: typing.Any | None = None, newline: typing.Any | None = None, sort_keys: bool = False, indent: typing.Any | None = None, default: typing.Any | None = None, parents: bool = True):
162    def dumps(
163        self,
164        data: Any,
165        encoding: Any | None = None,
166        errors: Any | None = None,
167        newline: Any | None = None,
168        sort_keys: bool = False,
169        indent: Any | None = None,
170        default: Any | None = None,
171        parents: bool = True,
172    ):
173        """Dump data to a json or toml file based off this instance's suffix."""
174        match self.suffix:
175            case ".json":
176                self.json_dumps(
177                    data, encoding, errors, newline, sort_keys, indent, default, parents
178                )
179            case ".toml":
180                self.toml_dumps(data, encoding, errors, newline, sort_keys, parents)

Dump data to a json or toml file based off this instance's suffix.

def delete(self, missing_ok: bool = True):
182    def delete(self, missing_ok: bool = True):
183        """Delete the file or folder pointed to by this instance.
184        Uses self.unlink() if a file and uses shutil.rmtree() if a directory."""
185        if self.is_file():
186            self.unlink(missing_ok)
187        elif self.is_dir():
188            shutil.rmtree(self)

Delete the file or folder pointed to by this instance. Uses self.unlink() if a file and uses shutil.rmtree() if a directory.

def copy( self, new_path: Union[Self, pathlib.Path, str], overwrite: bool = False) -> Self:
190    def copy(
191        self, new_path: Self | pathlib.Path | str, overwrite: bool = False
192    ) -> Self:
193        """Copy the path pointed to by this instance
194        to the instance pointed to by new_path using shutil.copyfile
195        or shutil.copytree. Returns the new path.
196
197        :param new_path: The copy destination.
198
199        :param overwrite: If True, files already existing in new_path
200        will be overwritten. If False, only files that don't exist in new_path
201        will be copied."""
202        new_path = Pathier(new_path)
203        if self.is_dir():
204            if overwrite or not new_path.exists():
205                shutil.copytree(self, new_path, dirs_exist_ok=True)
206            else:
207                files = self.rglob("*.*")
208                for file in files:
209                    dst = new_path.with_name(file.name)
210                    if not dst.exists():
211                        shutil.copyfile(file, dst)
212        elif self.is_file():
213            if overwrite or not new_path.exists():
214                shutil.copyfile(self, new_path)
215        return new_path

Copy the path pointed to by this instance to the instance pointed to by new_path using shutil.copyfile or shutil.copytree. Returns the new path.

Parameters
  • new_path: The copy destination.

  • overwrite: If True, files already existing in new_path will be overwritten. If False, only files that don't exist in new_path will be copied.

Inherited Members
pathlib.Path
cwd
home
samefile
iterdir
glob
rglob
absolute
resolve
stat
owner
group
open
read_bytes
read_text
chmod
lchmod
rmdir
lstat
rename
replace
exists
is_dir
is_file
is_mount
is_block_device
is_char_device
is_fifo
is_socket
expanduser
pathlib.PurePath
as_posix
as_uri
drive
root
anchor
name
suffix
suffixes
stem
with_name
with_stem
with_suffix
relative_to
is_relative_to
parts
joinpath
parent
parents
is_absolute
is_reserved
match