pathier

1from .pathier import Pathier
2
3__all__ = ["Pathier"]
class Pathier(pathlib.Path):
 15class Pathier(pathlib.Path):
 16    """Subclasses the standard library pathlib.Path class."""
 17
 18    def __new__(cls, *args, **kwargs):
 19        if cls is Pathier:
 20            cls = WindowsPath if os.name == "nt" else PosixPath
 21        self = cls._from_parts(args)
 22        if not self._flavour.is_supported:
 23            raise NotImplementedError(
 24                "cannot instantiate %r on your system" % (cls.__name__,)
 25            )
 26        return self
 27
 28    # ===============================================stats===============================================
 29    @property
 30    def dob(self) -> datetime.datetime | None:
 31        """Returns the creation date of this file or directory as a `dateime.datetime` object."""
 32        if self.exists():
 33            return datetime.datetime.fromtimestamp(self.stat().st_ctime)
 34        else:
 35            return None
 36
 37    @property
 38    def age(self) -> float | None:
 39        """Returns the age in seconds of this file or directory."""
 40        if self.exists():
 41            return (datetime.datetime.now() - self.dob).total_seconds()
 42        else:
 43            return None
 44
 45    @property
 46    def mod_date(self) -> datetime.datetime | None:
 47        """Returns the modification date of this file or directory as a `datetime.datetime` object."""
 48        if self.exists():
 49            return datetime.datetime.fromtimestamp(self.stat().st_mtime)
 50        else:
 51            return None
 52
 53    @property
 54    def mod_delta(self) -> float | None:
 55        """Returns how long ago in seconds this file or directory was modified."""
 56        if self.exists():
 57            return (datetime.datetime.now() - self.mod_date).total_seconds()
 58        else:
 59            return None
 60
 61    def size(self, format: bool = False) -> int | str | None:
 62        """Returns the size in bytes of this file or directory.
 63        Returns `None` if this path doesn't exist.
 64
 65        #### :params:
 66
 67        `format`: If `True`, return value as a formatted string."""
 68        if not self.exists():
 69            return None
 70        if self.is_file():
 71            size = self.stat().st_size
 72        if self.is_dir():
 73            size = sum(file.stat().st_size for file in self.rglob("*.*"))
 74        if format:
 75            return self.format_size(size)
 76        return size
 77
 78    @staticmethod
 79    def format_size(size: int) -> str:
 80        """Format `size` with common file size abbreviations and rounded to two decimal places.
 81        >>> 1234 -> "1.23 kb" """
 82        for unit in ["bytes", "kb", "mb", "gb", "tb", "pb"]:
 83            if unit != "bytes":
 84                size *= 0.001
 85            if size < 1000 or unit == "pb":
 86                return f"{round(size, 2)} {unit}"
 87
 88    def is_larger(self, path: Self) -> bool:
 89        """Returns whether this file or folder is larger than the one pointed to by `path`."""
 90        return self.size() > path.size()
 91
 92    def is_older(self, path: Self) -> bool:
 93        """Returns whether this file or folder is older than the one pointed to by `path`."""
 94        return self.dob < path.dob
 95
 96    def modified_more_recently(self, path: Self) -> bool:
 97        """Returns whether this file or folder was modified more recently than the one pointed to by `path`."""
 98        return self.mod_date > path.mod_date
 99
100    # ===============================================navigation===============================================
101    def mkcwd(self):
102        """Make this path your current working directory."""
103        os.chdir(self)
104
105    @property
106    def in_PATH(self) -> bool:
107        """Return `True` if this path is in `sys.path`."""
108        return str(self) in sys.path
109
110    def add_to_PATH(self, index: int = 0):
111        """Insert this path into `sys.path` if it isn't already there.
112
113        #### :params:
114
115        `index`: The index of `sys.path` to insert this path at."""
116        path = str(self)
117        if not self.in_PATH:
118            sys.path.insert(index, path)
119
120    def append_to_PATH(self):
121        """Append this path to `sys.path` if it isn't already there."""
122        path = str(self)
123        if not self.in_PATH:
124            sys.path.append(path)
125
126    def remove_from_PATH(self):
127        """Remove this path from `sys.path` if it's in `sys.path`."""
128        if self.in_PATH:
129            sys.path.remove(str(self))
130
131    def moveup(self, name: str) -> Self:
132        """Return a new `Pathier` object that is a parent of this instance.
133
134        `name` is case-sensitive and raises an exception if it isn't in `self.parts`.
135        >>> p = Pathier("C:\some\directory\in\your\system")
136        >>> print(p.moveup("directory"))
137        >>> "C:\some\directory"
138        >>> print(p.moveup("yeet"))
139        >>> "Exception: yeet is not a parent of C:\some\directory\in\your\system" """
140        if name not in self.parts:
141            raise Exception(f"{name} is not a parent of {self}")
142        return Pathier(*(self.parts[: self.parts.index(name) + 1]))
143
144    def __sub__(self, levels: int) -> Self:
145        """Return a new `Pathier` object moved up `levels` number of parents from the current path.
146        >>> p = Pathier("C:\some\directory\in\your\system")
147        >>> new_p = p - 3
148        >>> print(new_p)
149        >>> "C:\some\directory" """
150        path = self
151        for _ in range(levels):
152            path = path.parent
153        return path
154
155    def move_under(self, name: str) -> Self:
156        """Return a new `Pathier` object such that the stem is one level below the given folder `name`.
157
158        `name` is case-sensitive and raises an exception if it isn't in `self.parts`.
159        >>> p = Pathier("a/b/c/d/e/f/g")
160        >>> print(p.move_under("c"))
161        >>> 'a/b/c/d'"""
162        if name not in self.parts:
163            raise Exception(f"{name} is not a parent of {self}")
164        return self - (len(self.parts) - self.parts.index(name) - 2)
165
166    def separate(self, name: str, keep_name: bool = False) -> Self:
167        """Return a new `Pathier` object that is the relative child path after `name`.
168
169        `name` is case-sensitive and raises an exception if it isn't in `self.parts`.
170
171        #### :params:
172
173        `keep_name`: If `True`, the returned path will start with `name`.
174        >>> p = Pathier("a/b/c/d/e/f/g")
175        >>> print(p.separate("c"))
176        >>> 'd/e/f/g'
177        >>> print(p.separate("c", True))
178        >>> 'c/d/e/f/g'"""
179        if name not in self.parts:
180            raise Exception(f"{name} is not a parent of {self}")
181        if keep_name:
182            return Pathier(*self.parts[self.parts.index(name) :])
183        return Pathier(*self.parts[self.parts.index(name) + 1 :])
184
185    # ============================================write and read============================================
186    def mkdir(self, mode: int = 511, parents: bool = True, exist_ok: bool = True):
187        """Create this directory.
188
189        Same as `Path().mkdir()` except `parents` and `exist_ok` default to `True` instead of `False`."""
190        super().mkdir(mode, parents, exist_ok)
191
192    def touch(self):
193        """Create file (and parents if necessary)."""
194        self.parent.mkdir()
195        super().touch()
196
197    def write_text(
198        self,
199        data: Any,
200        encoding: Any | None = None,
201        errors: Any | None = None,
202        newline: Any | None = None,
203        parents: bool = True,
204    ):
205        """Write data to file.
206
207        If a `TypeError` is raised, the function  will attempt to cast `data` to a `str` and try the write again.
208
209        If a `FileNotFoundError` is raised and `parents = True`, `self.parent` will be created."""
210        write = functools.partial(
211            super().write_text,
212            encoding=encoding,
213            errors=errors,
214            newline=newline,
215        )
216        try:
217            write(data)
218        except TypeError:
219            data = str(data)
220            write(data)
221        except FileNotFoundError:
222            if parents:
223                self.parent.mkdir(parents=True)
224                write(data)
225            else:
226                raise
227        except Exception as e:
228            raise
229
230    def write_bytes(self, data: bytes, parents: bool = True):
231        """Write bytes to file.
232
233        #### :params:
234
235        `parents`: If `True` and the write operation fails with a `FileNotFoundError`,
236        make the parent directory and retry the write."""
237        try:
238            super().write_bytes(data)
239        except FileNotFoundError:
240            if parents:
241                self.parent.mkdir(parents=True)
242                super().write_bytes(data)
243            else:
244                raise
245        except Exception as e:
246            raise
247
248    def append(self, data: str, new_line: bool = True, encoding: Any | None = None):
249        """Append `data` to the file pointed to by this `Pathier` object.
250
251        #### :params:
252
253        `new_line`: If `True`, add `\\n` to `data`.
254
255        `encoding`: The file encoding to use."""
256        if new_line:
257            data += "\n"
258        with self.open("a", encoding=encoding) as file:
259            file.write(data)
260
261    def replace(
262        self,
263        old_string: str,
264        new_string: str,
265        count: int = -1,
266        encoding: Any | None = None,
267    ):
268        """Replace `old_string` in a file with `new_string`.
269
270        #### :params:
271
272        `count`: Only replace this many occurences of `old_string`.
273        By default (`-1`), all occurences are replaced.
274
275        `encoding`: The file encoding to use.
276
277        e.g.
278        >>> path = Pathier("somefile.txt")
279        >>>
280        >>> path.replace("hello", "yeet")
281        equivalent to
282        >>> path.write_text(path.read_text().replace("hello", "yeet"))"""
283        self.write_text(
284            self.read_text(encoding).replace(old_string, new_string, count),
285            encoding=encoding,
286        )
287
288    def join(self, data: list[str], encoding: Any | None = None):
289        """Write a list of strings, joined by "\\n", to the file pointed at by this instance.
290
291        Equivalent to `Pathier("somefile.txt").write_text("\\n".join(data), encoding=encoding)
292
293        #### :params:
294
295        `encoding`: The file encoding to use."""
296        self.write_text("\n".join(data), encoding=encoding)
297
298    def split(self, encoding: Any | None = None) -> list[str]:
299        """Returns the content of the pointed at file as a list of strings, splitting at new line characters.
300
301        Equivalent to `Pathier("somefile.txt").read_text(encoding=encoding).splitlines()`
302
303        #### :params:
304
305        `encoding`: The file encoding to use."""
306        return self.read_text(encoding=encoding).splitlines()
307
308    def json_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
309        """Load json file."""
310        return json.loads(self.read_text(encoding, errors))
311
312    def json_dumps(
313        self,
314        data: Any,
315        encoding: Any | None = None,
316        errors: Any | None = None,
317        newline: Any | None = None,
318        sort_keys: bool = False,
319        indent: Any | None = None,
320        default: Any | None = None,
321        parents: bool = True,
322    ) -> Any:
323        """Dump `data` to json file."""
324        self.write_text(
325            json.dumps(data, indent=indent, default=default, sort_keys=sort_keys),
326            encoding,
327            errors,
328            newline,
329            parents,
330        )
331
332    def toml_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
333        """Load toml file."""
334        return tomlkit.loads(self.read_text(encoding, errors))
335
336    def toml_dumps(
337        self,
338        data: Any,
339        encoding: Any | None = None,
340        errors: Any | None = None,
341        newline: Any | None = None,
342        sort_keys: bool = False,
343        parents: bool = True,
344    ):
345        """Dump `data` to toml file."""
346        self.write_text(
347            tomlkit.dumps(data, sort_keys), encoding, errors, newline, parents
348        )
349
350    def loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
351        """Load a json or toml file based off this instance's suffix."""
352        match self.suffix:
353            case ".json":
354                return self.json_loads(encoding, errors)
355            case ".toml":
356                return self.toml_loads(encoding, errors)
357
358    def dumps(
359        self,
360        data: Any,
361        encoding: Any | None = None,
362        errors: Any | None = None,
363        newline: Any | None = None,
364        sort_keys: bool = False,
365        indent: Any | None = None,
366        default: Any | None = None,
367        parents: bool = True,
368    ):
369        """Dump `data` to a json or toml file based off this instance's suffix."""
370        match self.suffix:
371            case ".json":
372                self.json_dumps(
373                    data, encoding, errors, newline, sort_keys, indent, default, parents
374                )
375            case ".toml":
376                self.toml_dumps(data, encoding, errors, newline, sort_keys, parents)
377
378    def delete(self, missing_ok: bool = True):
379        """Delete the file or folder pointed to by this instance.
380
381        Uses `self.unlink()` if a file and uses `shutil.rmtree()` if a directory."""
382        if self.is_file():
383            self.unlink(missing_ok)
384        elif self.is_dir():
385            shutil.rmtree(self)
386
387    def copy(
388        self, new_path: Self | pathlib.Path | str, overwrite: bool = False
389    ) -> Self:
390        """Copy the path pointed to by this instance
391        to the instance pointed to by `new_path` using `shutil.copyfile`
392        or `shutil.copytree`.
393
394        Returns the new path.
395
396        #### :params:
397
398        `new_path`: The copy destination.
399
400        `overwrite`: If `True`, files already existing in `new_path` will be overwritten.
401        If `False`, only files that don't exist in `new_path` will be copied."""
402        new_path = Pathier(new_path)
403        if self.is_dir():
404            if overwrite or not new_path.exists():
405                shutil.copytree(self, new_path, dirs_exist_ok=True)
406            else:
407                files = self.rglob("*.*")
408                for file in files:
409                    dst = new_path.with_name(file.name)
410                    if not dst.exists():
411                        shutil.copyfile(file, dst)
412        elif self.is_file():
413            if overwrite or not new_path.exists():
414                shutil.copyfile(self, new_path)
415        return new_path
416
417    def backup(self, timestamp: bool = False) -> Self | None:
418        """Create a copy of this file or directory with `_backup` appended to the path stem.
419        If the path to be backed up doesn't exist, `None` is returned.
420        Otherwise a `Pathier` object for the backup is returned.
421
422        #### :params:
423
424        `timestamp`: Add a timestamp to the backup name to prevent overriding previous backups.
425
426        >>> path = Pathier("some_file.txt")
427        >>> path.backup()
428        >>> list(path.iterdir())
429        >>> ['some_file.txt', 'some_file_backup.txt']
430        >>> path.backup(True)
431        >>> list(path.iterdir())
432        >>> ['some_file.txt', 'some_file_backup.txt', 'some_file_backup_04-28-2023-06_25_52_PM.txt']"""
433        if not self.exists():
434            return None
435        backup_stem = f"{self.stem}_backup"
436        if timestamp:
437            backup_stem = f"{backup_stem}_{datetime.datetime.now().strftime('%m-%d-%Y-%I_%M_%S_%p')}"
438        backup_path = self.with_stem(backup_stem)
439        self.copy(backup_path, True)
440        return backup_path
441
442    def execute(self, command: str = "", args: str = "") -> int:
443        """Make a call to `os.system` using the path pointed to by this Pathier object.
444
445        #### :params:
446
447        `command`: Program/command to precede the path with.
448
449        `args`: Any arguments that should come after the path.
450
451        :returns: The integer output of `os.system`.
452
453        e.g.
454        >>> path = Pathier("mydirectory") / "myscript.py"
455        then
456        >>> path.execute("py", "--iterations 10")
457        equivalent to
458        >>> os.system(f"py {path} --iterations 10")"""
459        return os.system(f"{command} {self} {args}")

Subclasses the standard library pathlib.Path class.

Pathier()
dob: datetime.datetime | None

Returns the creation date of this file or directory as a dateime.datetime object.

age: float | None

Returns the age in seconds of this file or directory.

mod_date: datetime.datetime | None

Returns the modification date of this file or directory as a datetime.datetime object.

mod_delta: float | None

Returns how long ago in seconds this file or directory was modified.

def size(self, format: bool = False) -> int | str | None:
61    def size(self, format: bool = False) -> int | str | None:
62        """Returns the size in bytes of this file or directory.
63        Returns `None` if this path doesn't exist.
64
65        #### :params:
66
67        `format`: If `True`, return value as a formatted string."""
68        if not self.exists():
69            return None
70        if self.is_file():
71            size = self.stat().st_size
72        if self.is_dir():
73            size = sum(file.stat().st_size for file in self.rglob("*.*"))
74        if format:
75            return self.format_size(size)
76        return size

Returns the size in bytes of this file or directory. Returns None if this path doesn't exist.

:params:

format: If True, return value as a formatted string.

@staticmethod
def format_size(size: int) -> str:
78    @staticmethod
79    def format_size(size: int) -> str:
80        """Format `size` with common file size abbreviations and rounded to two decimal places.
81        >>> 1234 -> "1.23 kb" """
82        for unit in ["bytes", "kb", "mb", "gb", "tb", "pb"]:
83            if unit != "bytes":
84                size *= 0.001
85            if size < 1000 or unit == "pb":
86                return f"{round(size, 2)} {unit}"

Format size with common file size abbreviations and rounded to two decimal places.

>>> 1234 -> "1.23 kb"
def is_larger(self, path: Self) -> bool:
88    def is_larger(self, path: Self) -> bool:
89        """Returns whether this file or folder is larger than the one pointed to by `path`."""
90        return self.size() > path.size()

Returns whether this file or folder is larger than the one pointed to by path.

def is_older(self, path: Self) -> bool:
92    def is_older(self, path: Self) -> bool:
93        """Returns whether this file or folder is older than the one pointed to by `path`."""
94        return self.dob < path.dob

Returns whether this file or folder is older than the one pointed to by path.

def modified_more_recently(self, path: Self) -> bool:
96    def modified_more_recently(self, path: Self) -> bool:
97        """Returns whether this file or folder was modified more recently than the one pointed to by `path`."""
98        return self.mod_date > path.mod_date

Returns whether this file or folder was modified more recently than the one pointed to by path.

def mkcwd(self):
101    def mkcwd(self):
102        """Make this path your current working directory."""
103        os.chdir(self)

Make this path your current working directory.

in_PATH: bool

Return True if this path is in sys.path.

def add_to_PATH(self, index: int = 0):
110    def add_to_PATH(self, index: int = 0):
111        """Insert this path into `sys.path` if it isn't already there.
112
113        #### :params:
114
115        `index`: The index of `sys.path` to insert this path at."""
116        path = str(self)
117        if not self.in_PATH:
118            sys.path.insert(index, path)

Insert this path into sys.path if it isn't already there.

:params:

index: The index of sys.path to insert this path at.

def append_to_PATH(self):
120    def append_to_PATH(self):
121        """Append this path to `sys.path` if it isn't already there."""
122        path = str(self)
123        if not self.in_PATH:
124            sys.path.append(path)

Append this path to sys.path if it isn't already there.

def remove_from_PATH(self):
126    def remove_from_PATH(self):
127        """Remove this path from `sys.path` if it's in `sys.path`."""
128        if self.in_PATH:
129            sys.path.remove(str(self))

Remove this path from sys.path if it's in sys.path.

def moveup(self, name: str) -> Self:
131    def moveup(self, name: str) -> Self:
132        """Return a new `Pathier` object that is a parent of this instance.
133
134        `name` is case-sensitive and raises an exception if it isn't in `self.parts`.
135        >>> p = Pathier("C:\some\directory\in\your\system")
136        >>> print(p.moveup("directory"))
137        >>> "C:\some\directory"
138        >>> print(p.moveup("yeet"))
139        >>> "Exception: yeet is not a parent of C:\some\directory\in\your\system" """
140        if name not in self.parts:
141            raise Exception(f"{name} is not a parent of {self}")
142        return Pathier(*(self.parts[: self.parts.index(name) + 1]))

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

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 move_under(self, name: str) -> Self:
155    def move_under(self, name: str) -> Self:
156        """Return a new `Pathier` object such that the stem is one level below the given folder `name`.
157
158        `name` is case-sensitive and raises an exception if it isn't in `self.parts`.
159        >>> p = Pathier("a/b/c/d/e/f/g")
160        >>> print(p.move_under("c"))
161        >>> 'a/b/c/d'"""
162        if name not in self.parts:
163            raise Exception(f"{name} is not a parent of {self}")
164        return self - (len(self.parts) - self.parts.index(name) - 2)

Return a new Pathier object such that the stem is one level below the given folder name.

name is case-sensitive and raises an exception if it isn't in self.parts.

>>> p = Pathier("a/b/c/d/e/f/g")
>>> print(p.move_under("c"))
>>> 'a/b/c/d'
def separate(self, name: str, keep_name: bool = False) -> Self:
166    def separate(self, name: str, keep_name: bool = False) -> Self:
167        """Return a new `Pathier` object that is the relative child path after `name`.
168
169        `name` is case-sensitive and raises an exception if it isn't in `self.parts`.
170
171        #### :params:
172
173        `keep_name`: If `True`, the returned path will start with `name`.
174        >>> p = Pathier("a/b/c/d/e/f/g")
175        >>> print(p.separate("c"))
176        >>> 'd/e/f/g'
177        >>> print(p.separate("c", True))
178        >>> 'c/d/e/f/g'"""
179        if name not in self.parts:
180            raise Exception(f"{name} is not a parent of {self}")
181        if keep_name:
182            return Pathier(*self.parts[self.parts.index(name) :])
183        return Pathier(*self.parts[self.parts.index(name) + 1 :])

Return a new Pathier object that is the relative child path after name.

name is case-sensitive and raises an exception if it isn't in self.parts.

:params:

keep_name: If True, the returned path will start with name.

>>> p = Pathier("a/b/c/d/e/f/g")
>>> print(p.separate("c"))
>>> 'd/e/f/g'
>>> print(p.separate("c", True))
>>> 'c/d/e/f/g'
def mkdir(self, mode: int = 511, parents: bool = True, exist_ok: bool = True):
186    def mkdir(self, mode: int = 511, parents: bool = True, exist_ok: bool = True):
187        """Create this directory.
188
189        Same as `Path().mkdir()` except `parents` and `exist_ok` default to `True` instead of `False`."""
190        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):
192    def touch(self):
193        """Create file (and parents if necessary)."""
194        self.parent.mkdir()
195        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):
197    def write_text(
198        self,
199        data: Any,
200        encoding: Any | None = None,
201        errors: Any | None = None,
202        newline: Any | None = None,
203        parents: bool = True,
204    ):
205        """Write data to file.
206
207        If a `TypeError` is raised, the function  will attempt to cast `data` to a `str` and try the write again.
208
209        If a `FileNotFoundError` is raised and `parents = True`, `self.parent` will be created."""
210        write = functools.partial(
211            super().write_text,
212            encoding=encoding,
213            errors=errors,
214            newline=newline,
215        )
216        try:
217            write(data)
218        except TypeError:
219            data = str(data)
220            write(data)
221        except FileNotFoundError:
222            if parents:
223                self.parent.mkdir(parents=True)
224                write(data)
225            else:
226                raise
227        except Exception as e:
228            raise

Write data to file.

If a TypeError is raised, the function will attempt to cast 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):
230    def write_bytes(self, data: bytes, parents: bool = True):
231        """Write bytes to file.
232
233        #### :params:
234
235        `parents`: If `True` and the write operation fails with a `FileNotFoundError`,
236        make the parent directory and retry the write."""
237        try:
238            super().write_bytes(data)
239        except FileNotFoundError:
240            if parents:
241                self.parent.mkdir(parents=True)
242                super().write_bytes(data)
243            else:
244                raise
245        except Exception as e:
246            raise

Write bytes to file.

:params:

parents: If True and the write operation fails with a FileNotFoundError, make the parent directory and retry the write.

def append( self, data: str, new_line: bool = True, encoding: typing.Any | None = None):
248    def append(self, data: str, new_line: bool = True, encoding: Any | None = None):
249        """Append `data` to the file pointed to by this `Pathier` object.
250
251        #### :params:
252
253        `new_line`: If `True`, add `\\n` to `data`.
254
255        `encoding`: The file encoding to use."""
256        if new_line:
257            data += "\n"
258        with self.open("a", encoding=encoding) as file:
259            file.write(data)

Append data to the file pointed to by this Pathier object.

:params:

new_line: If True, add \n to data.

encoding: The file encoding to use.

def replace( self, old_string: str, new_string: str, count: int = -1, encoding: typing.Any | None = None):
261    def replace(
262        self,
263        old_string: str,
264        new_string: str,
265        count: int = -1,
266        encoding: Any | None = None,
267    ):
268        """Replace `old_string` in a file with `new_string`.
269
270        #### :params:
271
272        `count`: Only replace this many occurences of `old_string`.
273        By default (`-1`), all occurences are replaced.
274
275        `encoding`: The file encoding to use.
276
277        e.g.
278        >>> path = Pathier("somefile.txt")
279        >>>
280        >>> path.replace("hello", "yeet")
281        equivalent to
282        >>> path.write_text(path.read_text().replace("hello", "yeet"))"""
283        self.write_text(
284            self.read_text(encoding).replace(old_string, new_string, count),
285            encoding=encoding,
286        )

Replace old_string in a file with new_string.

:params:

count: Only replace this many occurences of old_string. By default (-1), all occurences are replaced.

encoding: The file encoding to use.

e.g.

>>> path = Pathier("somefile.txt")
>>>
>>> path.replace("hello", "yeet")
equivalent to
>>> path.write_text(path.read_text().replace("hello", "yeet"))
def join(self, data: list[str], encoding: typing.Any | None = None):
288    def join(self, data: list[str], encoding: Any | None = None):
289        """Write a list of strings, joined by "\\n", to the file pointed at by this instance.
290
291        Equivalent to `Pathier("somefile.txt").write_text("\\n".join(data), encoding=encoding)
292
293        #### :params:
294
295        `encoding`: The file encoding to use."""
296        self.write_text("\n".join(data), encoding=encoding)

Write a list of strings, joined by "\n", to the file pointed at by this instance.

Equivalent to `Pathier("somefile.txt").write_text("\n".join(data), encoding=encoding)

:params:

encoding: The file encoding to use.

def split(self, encoding: typing.Any | None = None) -> list[str]:
298    def split(self, encoding: Any | None = None) -> list[str]:
299        """Returns the content of the pointed at file as a list of strings, splitting at new line characters.
300
301        Equivalent to `Pathier("somefile.txt").read_text(encoding=encoding).splitlines()`
302
303        #### :params:
304
305        `encoding`: The file encoding to use."""
306        return self.read_text(encoding=encoding).splitlines()

Returns the content of the pointed at file as a list of strings, splitting at new line characters.

Equivalent to Pathier("somefile.txt").read_text(encoding=encoding).splitlines()

:params:

encoding: The file encoding to use.

def json_loads( self, encoding: typing.Any | None = None, errors: typing.Any | None = None) -> Any:
308    def json_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
309        """Load json file."""
310        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:
312    def json_dumps(
313        self,
314        data: Any,
315        encoding: Any | None = None,
316        errors: Any | None = None,
317        newline: Any | None = None,
318        sort_keys: bool = False,
319        indent: Any | None = None,
320        default: Any | None = None,
321        parents: bool = True,
322    ) -> Any:
323        """Dump `data` to json file."""
324        self.write_text(
325            json.dumps(data, indent=indent, default=default, sort_keys=sort_keys),
326            encoding,
327            errors,
328            newline,
329            parents,
330        )

Dump data to json file.

def toml_loads( self, encoding: typing.Any | None = None, errors: typing.Any | None = None) -> Any:
332    def toml_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
333        """Load toml file."""
334        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):
336    def toml_dumps(
337        self,
338        data: Any,
339        encoding: Any | None = None,
340        errors: Any | None = None,
341        newline: Any | None = None,
342        sort_keys: bool = False,
343        parents: bool = True,
344    ):
345        """Dump `data` to toml file."""
346        self.write_text(
347            tomlkit.dumps(data, sort_keys), encoding, errors, newline, parents
348        )

Dump data to toml file.

def loads( self, encoding: typing.Any | None = None, errors: typing.Any | None = None) -> Any:
350    def loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
351        """Load a json or toml file based off this instance's suffix."""
352        match self.suffix:
353            case ".json":
354                return self.json_loads(encoding, errors)
355            case ".toml":
356                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):
358    def dumps(
359        self,
360        data: Any,
361        encoding: Any | None = None,
362        errors: Any | None = None,
363        newline: Any | None = None,
364        sort_keys: bool = False,
365        indent: Any | None = None,
366        default: Any | None = None,
367        parents: bool = True,
368    ):
369        """Dump `data` to a json or toml file based off this instance's suffix."""
370        match self.suffix:
371            case ".json":
372                self.json_dumps(
373                    data, encoding, errors, newline, sort_keys, indent, default, parents
374                )
375            case ".toml":
376                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):
378    def delete(self, missing_ok: bool = True):
379        """Delete the file or folder pointed to by this instance.
380
381        Uses `self.unlink()` if a file and uses `shutil.rmtree()` if a directory."""
382        if self.is_file():
383            self.unlink(missing_ok)
384        elif self.is_dir():
385            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:
387    def copy(
388        self, new_path: Self | pathlib.Path | str, overwrite: bool = False
389    ) -> Self:
390        """Copy the path pointed to by this instance
391        to the instance pointed to by `new_path` using `shutil.copyfile`
392        or `shutil.copytree`.
393
394        Returns the new path.
395
396        #### :params:
397
398        `new_path`: The copy destination.
399
400        `overwrite`: If `True`, files already existing in `new_path` will be overwritten.
401        If `False`, only files that don't exist in `new_path` will be copied."""
402        new_path = Pathier(new_path)
403        if self.is_dir():
404            if overwrite or not new_path.exists():
405                shutil.copytree(self, new_path, dirs_exist_ok=True)
406            else:
407                files = self.rglob("*.*")
408                for file in files:
409                    dst = new_path.with_name(file.name)
410                    if not dst.exists():
411                        shutil.copyfile(file, dst)
412        elif self.is_file():
413            if overwrite or not new_path.exists():
414                shutil.copyfile(self, new_path)
415        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.

:params:

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.

def backup(self, timestamp: bool = False) -> Optional[Self]:
417    def backup(self, timestamp: bool = False) -> Self | None:
418        """Create a copy of this file or directory with `_backup` appended to the path stem.
419        If the path to be backed up doesn't exist, `None` is returned.
420        Otherwise a `Pathier` object for the backup is returned.
421
422        #### :params:
423
424        `timestamp`: Add a timestamp to the backup name to prevent overriding previous backups.
425
426        >>> path = Pathier("some_file.txt")
427        >>> path.backup()
428        >>> list(path.iterdir())
429        >>> ['some_file.txt', 'some_file_backup.txt']
430        >>> path.backup(True)
431        >>> list(path.iterdir())
432        >>> ['some_file.txt', 'some_file_backup.txt', 'some_file_backup_04-28-2023-06_25_52_PM.txt']"""
433        if not self.exists():
434            return None
435        backup_stem = f"{self.stem}_backup"
436        if timestamp:
437            backup_stem = f"{backup_stem}_{datetime.datetime.now().strftime('%m-%d-%Y-%I_%M_%S_%p')}"
438        backup_path = self.with_stem(backup_stem)
439        self.copy(backup_path, True)
440        return backup_path

Create a copy of this file or directory with _backup appended to the path stem. If the path to be backed up doesn't exist, None is returned. Otherwise a Pathier object for the backup is returned.

:params:

timestamp: Add a timestamp to the backup name to prevent overriding previous backups.

>>> path = Pathier("some_file.txt")
>>> path.backup()
>>> list(path.iterdir())
>>> ['some_file.txt', 'some_file_backup.txt']
>>> path.backup(True)
>>> list(path.iterdir())
>>> ['some_file.txt', 'some_file_backup.txt', 'some_file_backup_04-28-2023-06_25_52_PM.txt']
def execute(self, command: str = '', args: str = '') -> int:
442    def execute(self, command: str = "", args: str = "") -> int:
443        """Make a call to `os.system` using the path pointed to by this Pathier object.
444
445        #### :params:
446
447        `command`: Program/command to precede the path with.
448
449        `args`: Any arguments that should come after the path.
450
451        :returns: The integer output of `os.system`.
452
453        e.g.
454        >>> path = Pathier("mydirectory") / "myscript.py"
455        then
456        >>> path.execute("py", "--iterations 10")
457        equivalent to
458        >>> os.system(f"py {path} --iterations 10")"""
459        return os.system(f"{command} {self} {args}")

Make a call to os.system using the path pointed to by this Pathier object.

:params:

command: Program/command to precede the path with.

args: Any arguments that should come after the path.

:returns: The integer output of os.system.

e.g.

>>> path = Pathier("mydirectory") / "myscript.py"
then
>>> path.execute("py", "--iterations 10")
equivalent to
>>> os.system(f"py {path} --iterations 10")
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
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