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 replace(
249        self,
250        old_string: str,
251        new_string: str,
252        count: int = -1,
253        encoding: Any | None = None,
254    ):
255        """Replace `old_string` in a file with `new_string`.
256
257        #### :params:
258
259        `count`: Only replace this many occurences of `old_string`.
260        By default (`-1`), all occurences are replaced.
261
262        `encoding`: The file encoding to use.
263
264        e.g.
265        >>> path = Pathier("somefile.txt")
266        >>>
267        >>> path.replace("hello", "yeet")
268        equivalent to
269        >>> path.write_text(path.read_text().replace("hello", "yeet"))"""
270        self.write_text(
271            self.read_text(encoding).replace(old_string, new_string, count),
272            encoding=encoding,
273        )
274
275    def json_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
276        """Load json file."""
277        return json.loads(self.read_text(encoding, errors))
278
279    def json_dumps(
280        self,
281        data: Any,
282        encoding: Any | None = None,
283        errors: Any | None = None,
284        newline: Any | None = None,
285        sort_keys: bool = False,
286        indent: Any | None = None,
287        default: Any | None = None,
288        parents: bool = True,
289    ) -> Any:
290        """Dump `data` to json file."""
291        self.write_text(
292            json.dumps(data, indent=indent, default=default, sort_keys=sort_keys),
293            encoding,
294            errors,
295            newline,
296            parents,
297        )
298
299    def toml_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
300        """Load toml file."""
301        return tomlkit.loads(self.read_text(encoding, errors))
302
303    def toml_dumps(
304        self,
305        data: Any,
306        encoding: Any | None = None,
307        errors: Any | None = None,
308        newline: Any | None = None,
309        sort_keys: bool = False,
310        parents: bool = True,
311    ):
312        """Dump `data` to toml file."""
313        self.write_text(
314            tomlkit.dumps(data, sort_keys), encoding, errors, newline, parents
315        )
316
317    def loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
318        """Load a json or toml file based off this instance's suffix."""
319        match self.suffix:
320            case ".json":
321                return self.json_loads(encoding, errors)
322            case ".toml":
323                return self.toml_loads(encoding, errors)
324
325    def dumps(
326        self,
327        data: Any,
328        encoding: Any | None = None,
329        errors: Any | None = None,
330        newline: Any | None = None,
331        sort_keys: bool = False,
332        indent: Any | None = None,
333        default: Any | None = None,
334        parents: bool = True,
335    ):
336        """Dump `data` to a json or toml file based off this instance's suffix."""
337        match self.suffix:
338            case ".json":
339                self.json_dumps(
340                    data, encoding, errors, newline, sort_keys, indent, default, parents
341                )
342            case ".toml":
343                self.toml_dumps(data, encoding, errors, newline, sort_keys, parents)
344
345    def delete(self, missing_ok: bool = True):
346        """Delete the file or folder pointed to by this instance.
347
348        Uses `self.unlink()` if a file and uses `shutil.rmtree()` if a directory."""
349        if self.is_file():
350            self.unlink(missing_ok)
351        elif self.is_dir():
352            shutil.rmtree(self)
353
354    def copy(
355        self, new_path: Self | pathlib.Path | str, overwrite: bool = False
356    ) -> Self:
357        """Copy the path pointed to by this instance
358        to the instance pointed to by `new_path` using `shutil.copyfile`
359        or `shutil.copytree`.
360
361        Returns the new path.
362
363        #### :params:
364
365        `new_path`: The copy destination.
366
367        `overwrite`: If `True`, files already existing in `new_path` will be overwritten.
368        If `False`, only files that don't exist in `new_path` will be copied."""
369        new_path = Pathier(new_path)
370        if self.is_dir():
371            if overwrite or not new_path.exists():
372                shutil.copytree(self, new_path, dirs_exist_ok=True)
373            else:
374                files = self.rglob("*.*")
375                for file in files:
376                    dst = new_path.with_name(file.name)
377                    if not dst.exists():
378                        shutil.copyfile(file, dst)
379        elif self.is_file():
380            if overwrite or not new_path.exists():
381                shutil.copyfile(self, new_path)
382        return new_path
383
384    def backup(self, timestamp: bool = False) -> Self | None:
385        """Create a copy of this file or directory with `_backup` appended to the path stem.
386        If the path to be backed up doesn't exist, `None` is returned.
387        Otherwise a `Pathier` object for the backup is returned.
388
389        #### :params:
390
391        `timestamp`: Add a timestamp to the backup name to prevent overriding previous backups.
392
393        >>> path = Pathier("some_file.txt")
394        >>> path.backup()
395        >>> list(path.iterdir())
396        >>> ['some_file.txt', 'some_file_backup.txt']
397        >>> path.backup(True)
398        >>> list(path.iterdir())
399        >>> ['some_file.txt', 'some_file_backup.txt', 'some_file_backup_04-28-2023-06_25_52_PM.txt']"""
400        if not self.exists():
401            return None
402        backup_stem = f"{self.stem}_backup"
403        if timestamp:
404            backup_stem = f"{backup_stem}_{datetime.datetime.now().strftime('%m-%d-%Y-%I_%M_%S_%p')}"
405        backup_path = self.with_stem(backup_stem)
406        self.copy(backup_path, True)
407        return backup_path
408
409    def execute(self, command: str = "", args: str = "") -> int:
410        """Make a call to `os.system` using the path pointed to by this Pathier object.
411
412        #### :params:
413
414        `command`: Program/command to precede the path with.
415
416        `args`: Any arguments that should come after the path.
417
418        :returns: The integer output of `os.system`.
419
420        e.g.
421        >>> path = Pathier("mydirectory") / "myscript.py"
422        then
423        >>> path.execute("py", "--iterations 10")
424        equivalent to
425        >>> os.system(f"py {path} --iterations 10")"""
426        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 replace( self, old_string: str, new_string: str, count: int = -1, encoding: typing.Any | None = None):
248    def replace(
249        self,
250        old_string: str,
251        new_string: str,
252        count: int = -1,
253        encoding: Any | None = None,
254    ):
255        """Replace `old_string` in a file with `new_string`.
256
257        #### :params:
258
259        `count`: Only replace this many occurences of `old_string`.
260        By default (`-1`), all occurences are replaced.
261
262        `encoding`: The file encoding to use.
263
264        e.g.
265        >>> path = Pathier("somefile.txt")
266        >>>
267        >>> path.replace("hello", "yeet")
268        equivalent to
269        >>> path.write_text(path.read_text().replace("hello", "yeet"))"""
270        self.write_text(
271            self.read_text(encoding).replace(old_string, new_string, count),
272            encoding=encoding,
273        )

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 json_loads( self, encoding: typing.Any | None = None, errors: typing.Any | None = None) -> Any:
275    def json_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
276        """Load json file."""
277        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:
279    def json_dumps(
280        self,
281        data: Any,
282        encoding: Any | None = None,
283        errors: Any | None = None,
284        newline: Any | None = None,
285        sort_keys: bool = False,
286        indent: Any | None = None,
287        default: Any | None = None,
288        parents: bool = True,
289    ) -> Any:
290        """Dump `data` to json file."""
291        self.write_text(
292            json.dumps(data, indent=indent, default=default, sort_keys=sort_keys),
293            encoding,
294            errors,
295            newline,
296            parents,
297        )

Dump data to json file.

def toml_loads( self, encoding: typing.Any | None = None, errors: typing.Any | None = None) -> Any:
299    def toml_loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
300        """Load toml file."""
301        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):
303    def toml_dumps(
304        self,
305        data: Any,
306        encoding: Any | None = None,
307        errors: Any | None = None,
308        newline: Any | None = None,
309        sort_keys: bool = False,
310        parents: bool = True,
311    ):
312        """Dump `data` to toml file."""
313        self.write_text(
314            tomlkit.dumps(data, sort_keys), encoding, errors, newline, parents
315        )

Dump data to toml file.

def loads( self, encoding: typing.Any | None = None, errors: typing.Any | None = None) -> Any:
317    def loads(self, encoding: Any | None = None, errors: Any | None = None) -> Any:
318        """Load a json or toml file based off this instance's suffix."""
319        match self.suffix:
320            case ".json":
321                return self.json_loads(encoding, errors)
322            case ".toml":
323                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):
325    def dumps(
326        self,
327        data: Any,
328        encoding: Any | None = None,
329        errors: Any | None = None,
330        newline: Any | None = None,
331        sort_keys: bool = False,
332        indent: Any | None = None,
333        default: Any | None = None,
334        parents: bool = True,
335    ):
336        """Dump `data` to a json or toml file based off this instance's suffix."""
337        match self.suffix:
338            case ".json":
339                self.json_dumps(
340                    data, encoding, errors, newline, sort_keys, indent, default, parents
341                )
342            case ".toml":
343                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):
345    def delete(self, missing_ok: bool = True):
346        """Delete the file or folder pointed to by this instance.
347
348        Uses `self.unlink()` if a file and uses `shutil.rmtree()` if a directory."""
349        if self.is_file():
350            self.unlink(missing_ok)
351        elif self.is_dir():
352            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:
354    def copy(
355        self, new_path: Self | pathlib.Path | str, overwrite: bool = False
356    ) -> Self:
357        """Copy the path pointed to by this instance
358        to the instance pointed to by `new_path` using `shutil.copyfile`
359        or `shutil.copytree`.
360
361        Returns the new path.
362
363        #### :params:
364
365        `new_path`: The copy destination.
366
367        `overwrite`: If `True`, files already existing in `new_path` will be overwritten.
368        If `False`, only files that don't exist in `new_path` will be copied."""
369        new_path = Pathier(new_path)
370        if self.is_dir():
371            if overwrite or not new_path.exists():
372                shutil.copytree(self, new_path, dirs_exist_ok=True)
373            else:
374                files = self.rglob("*.*")
375                for file in files:
376                    dst = new_path.with_name(file.name)
377                    if not dst.exists():
378                        shutil.copyfile(file, dst)
379        elif self.is_file():
380            if overwrite or not new_path.exists():
381                shutil.copyfile(self, new_path)
382        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]:
384    def backup(self, timestamp: bool = False) -> Self | None:
385        """Create a copy of this file or directory with `_backup` appended to the path stem.
386        If the path to be backed up doesn't exist, `None` is returned.
387        Otherwise a `Pathier` object for the backup is returned.
388
389        #### :params:
390
391        `timestamp`: Add a timestamp to the backup name to prevent overriding previous backups.
392
393        >>> path = Pathier("some_file.txt")
394        >>> path.backup()
395        >>> list(path.iterdir())
396        >>> ['some_file.txt', 'some_file_backup.txt']
397        >>> path.backup(True)
398        >>> list(path.iterdir())
399        >>> ['some_file.txt', 'some_file_backup.txt', 'some_file_backup_04-28-2023-06_25_52_PM.txt']"""
400        if not self.exists():
401            return None
402        backup_stem = f"{self.stem}_backup"
403        if timestamp:
404            backup_stem = f"{backup_stem}_{datetime.datetime.now().strftime('%m-%d-%Y-%I_%M_%S_%p')}"
405        backup_path = self.with_stem(backup_stem)
406        self.copy(backup_path, True)
407        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:
409    def execute(self, command: str = "", args: str = "") -> int:
410        """Make a call to `os.system` using the path pointed to by this Pathier object.
411
412        #### :params:
413
414        `command`: Program/command to precede the path with.
415
416        `args`: Any arguments that should come after the path.
417
418        :returns: The integer output of `os.system`.
419
420        e.g.
421        >>> path = Pathier("mydirectory") / "myscript.py"
422        then
423        >>> path.execute("py", "--iterations 10")
424        equivalent to
425        >>> os.system(f"py {path} --iterations 10")"""
426        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