lib.files

  1import os
  2from pathlib import Path
  3from typing import Literal
  4import json
  5import loguru
  6
  7
  8def readFile(filePath: str, mode: Literal["txt", "json"] = "txt"):
  9    """Read a file and return its contents.
 10
 11    Args:
 12        filePath: Path to the file.
 13        mode: `txt` to return raw str, `json` to return parsed JSON.
 14
 15    Returns:
 16        File contents as str or parsed JSON.
 17    """
 18    with open(filePath, encoding="utf-8") as openedFile:
 19        if mode == "json":
 20            return json.load(openedFile)
 21        else:
 22            return openedFile.read()
 23
 24
 25def readFileLines(filePath: str) -> list[str]:
 26    """Read a text file and return its lines as a list.
 27
 28    Args:
 29        filePath: Path to the text file.
 30    Returns:
 31        List of lines from the file, with whitespace stripped.
 32    """
 33    with open(filePath, encoding="utf-8") as openedFile:
 34        return [line.strip() for line in openedFile.readlines()]
 35
 36
 37def createFolder(path: str) -> str:
 38    """Create folder if non-existent, return its path.
 39
 40    Args:
 41        path: Path to the folder.
 42
 43    Returns:
 44        The path if created or exists, otherwise an empty string.
 45    """
 46    try:
 47        # ? Create parent directories recursively, ignore error if exists
 48        os.makedirs(path, exist_ok=True)
 49        return path
 50    except Exception as e:
 51        if isDirectory(path):
 52            return path
 53        else:
 54            loguru.logger.warning(
 55                "Error creating folder for path '{}' with exception {}", path, e
 56            )
 57            # Return empty string if failed => write to root
 58            return ""
 59
 60
 61def saveImage(filePath: str | Path) -> str:
 62    """Save the current DrawBot drawing with an incremented unique suffix.
 63
 64    The helper always saves to a suffixed path and scans sibling files to pick
 65    the next numeric suffix before the extension, e.g. ``image.png`` ->
 66    ``image-1.png`` -> ``image-2.png``.
 67
 68    Args:
 69        filePath: Target file path including file name and extension.
 70            Accepts both str and pathlib.Path.
 71
 72    Returns:
 73        The final saved file path as str.
 74    """
 75    import drawBot
 76
 77    filePath = str(filePath)
 78    folderPath = os.path.dirname(filePath)
 79    if folderPath:
 80        createFolder(folderPath)
 81
 82    fileStem, fileExtension = os.path.splitext(filePath)
 83    highestSuffix = 0
 84    baseName = os.path.basename(fileStem)
 85
 86    for siblingName in os.listdir(folderPath or "."):
 87        siblingStem, siblingExtension = os.path.splitext(siblingName)
 88        if siblingExtension != fileExtension:
 89            continue
 90
 91        if not siblingStem.startswith(f"{baseName}-"):
 92            continue
 93
 94        siblingSuffix = siblingStem.removeprefix(f"{baseName}-")
 95        if siblingSuffix.isdigit():
 96            highestSuffix = max(highestSuffix, int(siblingSuffix))
 97
 98    resolvedPath = f"{fileStem}-{highestSuffix + 1}{fileExtension}"
 99
100    drawBot.saveImage(resolvedPath)
101    return resolvedPath
102
103
104def isFile(string: str) -> bool:
105    """Returns True if the string is a path to a non-empty file."""
106    return (
107        isinstance(string, str)
108        and os.path.isfile(string)
109        and os.path.getsize(string) > 0
110    )
111
112
113def isDirectory(string: str) -> bool:
114    """Returns True if the string is a directory path."""
115    return os.path.isdir(string)
def readFile(filePath: str, mode: Literal['txt', 'json'] = 'txt'):
 9def readFile(filePath: str, mode: Literal["txt", "json"] = "txt"):
10    """Read a file and return its contents.
11
12    Args:
13        filePath: Path to the file.
14        mode: `txt` to return raw str, `json` to return parsed JSON.
15
16    Returns:
17        File contents as str or parsed JSON.
18    """
19    with open(filePath, encoding="utf-8") as openedFile:
20        if mode == "json":
21            return json.load(openedFile)
22        else:
23            return openedFile.read()

Read a file and return its contents.

Arguments:
  • filePath: Path to the file.
  • mode: txt to return raw str, json to return parsed JSON.
Returns:

File contents as str or parsed JSON.

def readFileLines(filePath: str) -> list[str]:
26def readFileLines(filePath: str) -> list[str]:
27    """Read a text file and return its lines as a list.
28
29    Args:
30        filePath: Path to the text file.
31    Returns:
32        List of lines from the file, with whitespace stripped.
33    """
34    with open(filePath, encoding="utf-8") as openedFile:
35        return [line.strip() for line in openedFile.readlines()]

Read a text file and return its lines as a list.

Arguments:
  • filePath: Path to the text file.
Returns:

List of lines from the file, with whitespace stripped.

def createFolder(path: str) -> str:
38def createFolder(path: str) -> str:
39    """Create folder if non-existent, return its path.
40
41    Args:
42        path: Path to the folder.
43
44    Returns:
45        The path if created or exists, otherwise an empty string.
46    """
47    try:
48        # ? Create parent directories recursively, ignore error if exists
49        os.makedirs(path, exist_ok=True)
50        return path
51    except Exception as e:
52        if isDirectory(path):
53            return path
54        else:
55            loguru.logger.warning(
56                "Error creating folder for path '{}' with exception {}", path, e
57            )
58            # Return empty string if failed => write to root
59            return ""

Create folder if non-existent, return its path.

Arguments:
  • path: Path to the folder.
Returns:

The path if created or exists, otherwise an empty string.

def saveImage(filePath: str | pathlib.Path) -> str:
 62def saveImage(filePath: str | Path) -> str:
 63    """Save the current DrawBot drawing with an incremented unique suffix.
 64
 65    The helper always saves to a suffixed path and scans sibling files to pick
 66    the next numeric suffix before the extension, e.g. ``image.png`` ->
 67    ``image-1.png`` -> ``image-2.png``.
 68
 69    Args:
 70        filePath: Target file path including file name and extension.
 71            Accepts both str and pathlib.Path.
 72
 73    Returns:
 74        The final saved file path as str.
 75    """
 76    import drawBot
 77
 78    filePath = str(filePath)
 79    folderPath = os.path.dirname(filePath)
 80    if folderPath:
 81        createFolder(folderPath)
 82
 83    fileStem, fileExtension = os.path.splitext(filePath)
 84    highestSuffix = 0
 85    baseName = os.path.basename(fileStem)
 86
 87    for siblingName in os.listdir(folderPath or "."):
 88        siblingStem, siblingExtension = os.path.splitext(siblingName)
 89        if siblingExtension != fileExtension:
 90            continue
 91
 92        if not siblingStem.startswith(f"{baseName}-"):
 93            continue
 94
 95        siblingSuffix = siblingStem.removeprefix(f"{baseName}-")
 96        if siblingSuffix.isdigit():
 97            highestSuffix = max(highestSuffix, int(siblingSuffix))
 98
 99    resolvedPath = f"{fileStem}-{highestSuffix + 1}{fileExtension}"
100
101    drawBot.saveImage(resolvedPath)
102    return resolvedPath

Save the current DrawBot drawing with an incremented unique suffix.

The helper always saves to a suffixed path and scans sibling files to pick the next numeric suffix before the extension, e.g. image.png -> image-1.png -> image-2.png.

Arguments:
  • filePath: Target file path including file name and extension. Accepts both str and pathlib.Path.
Returns:

The final saved file path as str.

def isFile(string: str) -> bool:
105def isFile(string: str) -> bool:
106    """Returns True if the string is a path to a non-empty file."""
107    return (
108        isinstance(string, str)
109        and os.path.isfile(string)
110        and os.path.getsize(string) > 0
111    )

Returns True if the string is a path to a non-empty file.

def isDirectory(string: str) -> bool:
114def isDirectory(string: str) -> bool:
115    """Returns True if the string is a directory path."""
116    return os.path.isdir(string)

Returns True if the string is a directory path.