pathlibutil


Namepathlibutil JSON
Version 0.1.9 PyPI version JSON
download
home_pagehttps://d-chris.github.io
Summaryinherits from pathlib.Path with methods for hashing, copying, deleting and more
upload_time2024-04-12 19:40:19
maintainerNone
docs_urlNone
authorChristoph Dörrer
requires_python<3.13,>=3.8.1
licenseMIT
keywords pathlib hashlib shutil
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # pathlibutil

[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pathlibutil)](https://pypi.org/project/pathlibutil/)
[![PyPI](https://img.shields.io/pypi/v/pathlibutil)](https://pypi.org/project/pathlibutil/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/pathlibutil)](https://pypi.org/project/pathlibutil/)
[![PyPI - License](https://img.shields.io/pypi/l/pathlibutil)](https://raw.githubusercontent.com/d-chris/pathlibutil/main/LICENSE)
[![GitHub Workflow Test)](https://img.shields.io/github/actions/workflow/status/d-chris/pathlibutil/pytest.yml?logo=github&label=pytest)](https://github.com/d-chris/pathlibutil/actions/workflows/pytest.yml)
[![Website](https://img.shields.io/website?url=https%3A%2F%2Fd-chris.github.io%2Fpathlibutil&up_message=pdoc&logo=github&label=documentation)](https://d-chris.github.io/pathlibutil)
[![GitHub tag (with filter)](https://img.shields.io/github/v/tag/d-chris/pathlibutil?logo=github&label=github)](https://github.com/d-chris/pathlibutil)
[![Coverage](https://img.shields.io/website?url=https%3A%2F%2Fd-chris.github.io%2Fpathlibutil%2Fhtmlcov&up_message=available&down_message=missing&logo=codecov&label=coverage)](https://d-chris.github.io/pathlibutil/htmlcov)

---

`pathlibutil.Path` inherits from  `pathlib.Path` with some useful built-in python functions from `shutil` and `hashlib`

- `Path.hexdigest()` to calculate and `Path.verify()` for verification of hexdigest from a file
- `Path.default_hash` to configurate default hash algorithm for `Path` class (default: *'md5'*)
- `Path.size()` to get size in bytes of a file or directory
  - `byteint` function decorator converts the return value of `int` to a `ByteInt` object
- `Path.read_lines()` to yield over all lines from a file until EOF
- `contextmanager` to change current working directory with `with` statement
- `Path.copy()` copy a file or directory to a new path destination
- `Path.delete()` delete a file or directory-tree
- `Path.move()` move a file or directory to a new path destination
- `Path.make_archive()` creates and `Path.unpack_archive()` uncompresses an archive from a file or directory
- `Path.archive_formats` to get all available archive formats
- `Path.stat()` returns a `StatResult` object to get file or directory information containing
  - `TimeInt` objects for `atime`, `ctime`, `mtime` and `birthtime`
  - `ByteInt` object for `size`
- `Path.relative_to()` to get relative path from a file or directory, `walk_up` to walk up the directory tree.
- `Path.with_suffix()` to change the multiple suffixes of a file
- `Path.cwd()` to get the current working directory or executable path when script is bundled, e.g. with `pyinstaller`
- `Path.resolve()` to resolve a unc path to a mapped windows drive.

## Installation

```bash
pip install pathlibutil
```

### 7zip support

to handle 7zip archives, an extra package `py7zr>=0.20.2` is required!

[![PyPI - Version](https://img.shields.io/pypi/v/py7zr?logo=python&logoColor=white&label=py7zr&color=FFFF33)](https://pypi.org/project/py7zr/)

```bash
# install as extra dependency
pip install pathlibutil[7z]
```

## Usage

```python
from pathlibutil import Path

readme = Path('README.md')

print(f'File size: {readme.size()} Bytes')
```

## Example 1

Read a file and print its content and some file information to stdout.
> `Path.read_lines()`

```python
from pathlibutil import Path

readme = Path("README.md")

print(f"File size: {readme.size()} Bytes")
print(f'File sha1: {readme.hexdigest("sha1")}')

print("File content".center(80, "="))

for line in readme.read_lines(encoding="utf-8"):
  print(line, end="")

print("EOF".center(80, "="))
```

## Example 2

Write a file with md5 checksums of all python files in the pathlibutil-directory.
> `Path.hexdigest()`

```python
from pathlibutil import Path

file = Path("pathlibutil.md5")

with file.open("w") as f:
  f.write(
      f"# MD5 checksums generated with pathlibutil "
      "(https://pypi.org/project/pathlibutil/)\n\n"
  )

  i = 0
  for i, filename in enumerate(Path("./pathlibutil").glob("*.py"), start=1):
    f.write(f"{filename.hexdigest()} *{filename}\n")

print(f"\nwritten: {i:>5} {file.default_hash}-hashes to: {file}")
```

## Example 3

Read a file with md5 checksums and verify them.
> `Path.verify()`, `Path.default_hash` and `contextmanager`

```python
from pathlibutil import Path

file = Path("pathlibutil.md5")


def no_comment(line: str) -> bool:
  return not line.startswith("#")


with file.parent as cwd:
  miss = 0
  ok = 0
  fail = 0

  for line in filter(no_comment, file.read_lines()):
    try:
      digest, filename = line.strip().split(" *")
      verification = Path(filename).verify(digest, "md5")
    except ValueError as split_failed:
      continue
    except FileNotFoundError as verify_failed:
      tag = "missing"
      miss += 1
    else:
      if verification:
        tag = "ok"
        ok += 1
      else:
        tag = "fail"
        fail += 1

    print(f'{tag.ljust(len(digest), ".")} *{filename}')

  print(f"\nok: {ok:<5} fail: {fail:<5} missing: {miss}")
```

## Example 4

Search all pycache directories and free the memory and display the number of
deleted directories and the amount of memory freed in MB.
> `Path.delete()`, `Path.size()` and `ByteInt`

```python
from pathlibutil import Path, ByteInt

mem = ByteInt(0)
i = 0

for i, cache in enumerate(Path(".").rglob("*/__pycache__/"), start=1):
  cache_size = cache.size()
  try:
    cache.delete(recursive=True)
  except OSError:
    print(f"Failed to delete {cache}")
  else:
    mem += cache_size

print(f"{i} cache directories deleted, {mem:.1mb} MB freed.")
```

## Example 5

Inherit from `pathlibutil.Path` to register new a archive format. Specify a
`archive` as keyword argument in the new subclass, which has to be the suffix
without `.` of the archives. Implement a classmethod `_register_archive_format()`
to register new archive formats.

> Path.make_archive(), Path.archive_formats and Path.move()

```python
import pathlibutil
import shutil


class RegisterFooBarFormat(pathlibutil.Path, archive="foobar"):
  @classmethod
  def _register_archive_format(cls):
    """
    implement new register functions for given `archive`
    """
    try:
      import required_package_name
    except ModuleNotFoundError:
      raise ModuleNotFoundError("pip install <required_package_name>")

    def pack_foobar(
        base_name, base_dir, owner=None, group=None, dry_run=None, logger=None
    ) -> str:
      """callable that will be used to unpack archives.

      Args:
          base_name (`str`): name of the file to create
          base_dir (`str`): directory to start archiving from, defaults to `os.curdir`
          owner (`Any`, optional): as passed in `make_archive(*args, owner=None, **kwargs)`. Defaults to None.
          group (`Any`, optional): as passed in `make_archive(*args, group=None, **kwargs)`. Defaults to None.
          dry_run (`Any`, optional): as passed in `make_archive(*args, dry_run=None, **kwargs)`. Defaults to None.
          logger (`logging.Logger`, optional): as passed in `make_archive(*args, logger=None, **kwargs)`. Defaults to None.

      Returns:
          str: path of the new created archive
      """
      raise NotImplementedError("implement your own pack function")

    def unpack_foobar(archive, path, filter=None, extra_args=None) -> None:
      """callable that will be used to unpack archives.

      Args:
          archive (`str`): path of the archive
          path (`str`): directory the archive must be extracted to
          filter (`Any`, optional): as passed in `unpack_archive(*args, filter=None, **kwargs)`. Defaults to None.
          extra_args (`Sequence[Tuple[name, value]]`, optional): additional keyword arguments, specified by `register_unpack_format(*args, extra_args=None, **kwargs)`. Defaults to None.
      """
      raise NotImplementedError("implement your own unpack function")

    shutil.register_archive_format(
        "foobar", pack_foobar, description="foobar archives"
    )
    shutil.register_unpack_format("foobar", [".foo.bar"], unpack_foobar)


file = pathlibutil.Path("README.md")

print(f"available archive formats: {file.archive_formats}")

archive = file.make_archive("README.foo.bar")

backup = archive.move("./backup/")

print(f"archive created: {archive.name} and moved to: {backup.parent}")
```

## Example 6

Access the current working directory with optional parameter `frozen` to determine
different directories when script is bundled to an executable,
e.g. with `pyinstaller`.
> `Path.cwd()`

```cmd
>>> poetry run examples/example6.py -b
Building frozen: K:/pathlibutil/examples/example6.exe
Build succeeded: 0

>>> poetry run examples/example6.py
we are                          not frozen

bundle dir is                   K:/pathlibutil/examples
sys.argv[0] is                  K:/pathlibutil/examples/example6.py
sys.executable is               K:/pathlibutil/.venv/Scripts/python.exe
os.getcwd is                    K:/pathlibutil

Path.cwd(frozen=True) is        K:/pathlibutil
Path.cwd(frozen=False) is       K:/pathlibutil
Path.cwd(frozen=_MEIPASS) is    K:/pathlibutil

>>> examples/example6.exe
we are                          ever so frozen

bundle dir is                   C:/Users/CHRIST~1.DOE/AppData/Local/Temp/_MEI106042
sys.argv[0] is                  examples/example6.exe
sys.executable is               K:/pathlibutil/examples/example6.exe
os.getcwd is                    K:/pathlibutil

Path.cwd(frozen=True) is        K:/pathlibutil/examples
Path.cwd(frozen=False) is       K:/pathlibutil
Path.cwd(frozen=_MEIPASS) is    C:/Users/CHRIST~1.DOE/AppData/Local/Temp/_MEI106042
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://d-chris.github.io",
    "name": "pathlibutil",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<3.13,>=3.8.1",
    "maintainer_email": null,
    "keywords": "pathlib, hashlib, shutil",
    "author": "Christoph D\u00f6rrer",
    "author_email": "d-chris@web.de",
    "download_url": "https://files.pythonhosted.org/packages/8c/a6/02080ccfb2d3b203a94f6a0bc94cc9a5adff4cb2d3c6c0c43065d4162b1a/pathlibutil-0.1.9.tar.gz",
    "platform": null,
    "description": "# pathlibutil\n\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pathlibutil)](https://pypi.org/project/pathlibutil/)\n[![PyPI](https://img.shields.io/pypi/v/pathlibutil)](https://pypi.org/project/pathlibutil/)\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/pathlibutil)](https://pypi.org/project/pathlibutil/)\n[![PyPI - License](https://img.shields.io/pypi/l/pathlibutil)](https://raw.githubusercontent.com/d-chris/pathlibutil/main/LICENSE)\n[![GitHub Workflow Test)](https://img.shields.io/github/actions/workflow/status/d-chris/pathlibutil/pytest.yml?logo=github&label=pytest)](https://github.com/d-chris/pathlibutil/actions/workflows/pytest.yml)\n[![Website](https://img.shields.io/website?url=https%3A%2F%2Fd-chris.github.io%2Fpathlibutil&up_message=pdoc&logo=github&label=documentation)](https://d-chris.github.io/pathlibutil)\n[![GitHub tag (with filter)](https://img.shields.io/github/v/tag/d-chris/pathlibutil?logo=github&label=github)](https://github.com/d-chris/pathlibutil)\n[![Coverage](https://img.shields.io/website?url=https%3A%2F%2Fd-chris.github.io%2Fpathlibutil%2Fhtmlcov&up_message=available&down_message=missing&logo=codecov&label=coverage)](https://d-chris.github.io/pathlibutil/htmlcov)\n\n---\n\n`pathlibutil.Path` inherits from  `pathlib.Path` with some useful built-in python functions from `shutil` and `hashlib`\n\n- `Path.hexdigest()` to calculate and `Path.verify()` for verification of hexdigest from a file\n- `Path.default_hash` to configurate default hash algorithm for `Path` class (default: *'md5'*)\n- `Path.size()` to get size in bytes of a file or directory\n  - `byteint` function decorator converts the return value of `int` to a `ByteInt` object\n- `Path.read_lines()` to yield over all lines from a file until EOF\n- `contextmanager` to change current working directory with `with` statement\n- `Path.copy()` copy a file or directory to a new path destination\n- `Path.delete()` delete a file or directory-tree\n- `Path.move()` move a file or directory to a new path destination\n- `Path.make_archive()` creates and `Path.unpack_archive()` uncompresses an archive from a file or directory\n- `Path.archive_formats` to get all available archive formats\n- `Path.stat()` returns a `StatResult` object to get file or directory information containing\n  - `TimeInt` objects for `atime`, `ctime`, `mtime` and `birthtime`\n  - `ByteInt` object for `size`\n- `Path.relative_to()` to get relative path from a file or directory, `walk_up` to walk up the directory tree.\n- `Path.with_suffix()` to change the multiple suffixes of a file\n- `Path.cwd()` to get the current working directory or executable path when script is bundled, e.g. with `pyinstaller`\n- `Path.resolve()` to resolve a unc path to a mapped windows drive.\n\n## Installation\n\n```bash\npip install pathlibutil\n```\n\n### 7zip support\n\nto handle 7zip archives, an extra package `py7zr>=0.20.2` is required!\n\n[![PyPI - Version](https://img.shields.io/pypi/v/py7zr?logo=python&logoColor=white&label=py7zr&color=FFFF33)](https://pypi.org/project/py7zr/)\n\n```bash\n# install as extra dependency\npip install pathlibutil[7z]\n```\n\n## Usage\n\n```python\nfrom pathlibutil import Path\n\nreadme = Path('README.md')\n\nprint(f'File size: {readme.size()} Bytes')\n```\n\n## Example 1\n\nRead a file and print its content and some file information to stdout.\n> `Path.read_lines()`\n\n```python\nfrom pathlibutil import Path\n\nreadme = Path(\"README.md\")\n\nprint(f\"File size: {readme.size()} Bytes\")\nprint(f'File sha1: {readme.hexdigest(\"sha1\")}')\n\nprint(\"File content\".center(80, \"=\"))\n\nfor line in readme.read_lines(encoding=\"utf-8\"):\n  print(line, end=\"\")\n\nprint(\"EOF\".center(80, \"=\"))\n```\n\n## Example 2\n\nWrite a file with md5 checksums of all python files in the pathlibutil-directory.\n> `Path.hexdigest()`\n\n```python\nfrom pathlibutil import Path\n\nfile = Path(\"pathlibutil.md5\")\n\nwith file.open(\"w\") as f:\n  f.write(\n      f\"# MD5 checksums generated with pathlibutil \"\n      \"(https://pypi.org/project/pathlibutil/)\\n\\n\"\n  )\n\n  i = 0\n  for i, filename in enumerate(Path(\"./pathlibutil\").glob(\"*.py\"), start=1):\n    f.write(f\"{filename.hexdigest()} *{filename}\\n\")\n\nprint(f\"\\nwritten: {i:>5} {file.default_hash}-hashes to: {file}\")\n```\n\n## Example 3\n\nRead a file with md5 checksums and verify them.\n> `Path.verify()`, `Path.default_hash` and `contextmanager`\n\n```python\nfrom pathlibutil import Path\n\nfile = Path(\"pathlibutil.md5\")\n\n\ndef no_comment(line: str) -> bool:\n  return not line.startswith(\"#\")\n\n\nwith file.parent as cwd:\n  miss = 0\n  ok = 0\n  fail = 0\n\n  for line in filter(no_comment, file.read_lines()):\n    try:\n      digest, filename = line.strip().split(\" *\")\n      verification = Path(filename).verify(digest, \"md5\")\n    except ValueError as split_failed:\n      continue\n    except FileNotFoundError as verify_failed:\n      tag = \"missing\"\n      miss += 1\n    else:\n      if verification:\n        tag = \"ok\"\n        ok += 1\n      else:\n        tag = \"fail\"\n        fail += 1\n\n    print(f'{tag.ljust(len(digest), \".\")} *{filename}')\n\n  print(f\"\\nok: {ok:<5} fail: {fail:<5} missing: {miss}\")\n```\n\n## Example 4\n\nSearch all pycache directories and free the memory and display the number of\ndeleted directories and the amount of memory freed in MB.\n> `Path.delete()`, `Path.size()` and `ByteInt`\n\n```python\nfrom pathlibutil import Path, ByteInt\n\nmem = ByteInt(0)\ni = 0\n\nfor i, cache in enumerate(Path(\".\").rglob(\"*/__pycache__/\"), start=1):\n  cache_size = cache.size()\n  try:\n    cache.delete(recursive=True)\n  except OSError:\n    print(f\"Failed to delete {cache}\")\n  else:\n    mem += cache_size\n\nprint(f\"{i} cache directories deleted, {mem:.1mb} MB freed.\")\n```\n\n## Example 5\n\nInherit from `pathlibutil.Path` to register new a archive format. Specify a\n`archive` as keyword argument in the new subclass, which has to be the suffix\nwithout `.` of the archives. Implement a classmethod `_register_archive_format()`\nto register new archive formats.\n\n> Path.make_archive(), Path.archive_formats and Path.move()\n\n```python\nimport pathlibutil\nimport shutil\n\n\nclass RegisterFooBarFormat(pathlibutil.Path, archive=\"foobar\"):\n  @classmethod\n  def _register_archive_format(cls):\n    \"\"\"\n    implement new register functions for given `archive`\n    \"\"\"\n    try:\n      import required_package_name\n    except ModuleNotFoundError:\n      raise ModuleNotFoundError(\"pip install <required_package_name>\")\n\n    def pack_foobar(\n        base_name, base_dir, owner=None, group=None, dry_run=None, logger=None\n    ) -> str:\n      \"\"\"callable that will be used to unpack archives.\n\n      Args:\n          base_name (`str`): name of the file to create\n          base_dir (`str`): directory to start archiving from, defaults to `os.curdir`\n          owner (`Any`, optional): as passed in `make_archive(*args, owner=None, **kwargs)`. Defaults to None.\n          group (`Any`, optional): as passed in `make_archive(*args, group=None, **kwargs)`. Defaults to None.\n          dry_run (`Any`, optional): as passed in `make_archive(*args, dry_run=None, **kwargs)`. Defaults to None.\n          logger (`logging.Logger`, optional): as passed in `make_archive(*args, logger=None, **kwargs)`. Defaults to None.\n\n      Returns:\n          str: path of the new created archive\n      \"\"\"\n      raise NotImplementedError(\"implement your own pack function\")\n\n    def unpack_foobar(archive, path, filter=None, extra_args=None) -> None:\n      \"\"\"callable that will be used to unpack archives.\n\n      Args:\n          archive (`str`): path of the archive\n          path (`str`): directory the archive must be extracted to\n          filter (`Any`, optional): as passed in `unpack_archive(*args, filter=None, **kwargs)`. Defaults to None.\n          extra_args (`Sequence[Tuple[name, value]]`, optional): additional keyword arguments, specified by `register_unpack_format(*args, extra_args=None, **kwargs)`. Defaults to None.\n      \"\"\"\n      raise NotImplementedError(\"implement your own unpack function\")\n\n    shutil.register_archive_format(\n        \"foobar\", pack_foobar, description=\"foobar archives\"\n    )\n    shutil.register_unpack_format(\"foobar\", [\".foo.bar\"], unpack_foobar)\n\n\nfile = pathlibutil.Path(\"README.md\")\n\nprint(f\"available archive formats: {file.archive_formats}\")\n\narchive = file.make_archive(\"README.foo.bar\")\n\nbackup = archive.move(\"./backup/\")\n\nprint(f\"archive created: {archive.name} and moved to: {backup.parent}\")\n```\n\n## Example 6\n\nAccess the current working directory with optional parameter `frozen` to determine\ndifferent directories when script is bundled to an executable,\ne.g. with `pyinstaller`.\n> `Path.cwd()`\n\n```cmd\n>>> poetry run examples/example6.py -b\nBuilding frozen: K:/pathlibutil/examples/example6.exe\nBuild succeeded: 0\n\n>>> poetry run examples/example6.py\nwe are                          not frozen\n\nbundle dir is                   K:/pathlibutil/examples\nsys.argv[0] is                  K:/pathlibutil/examples/example6.py\nsys.executable is               K:/pathlibutil/.venv/Scripts/python.exe\nos.getcwd is                    K:/pathlibutil\n\nPath.cwd(frozen=True) is        K:/pathlibutil\nPath.cwd(frozen=False) is       K:/pathlibutil\nPath.cwd(frozen=_MEIPASS) is    K:/pathlibutil\n\n>>> examples/example6.exe\nwe are                          ever so frozen\n\nbundle dir is                   C:/Users/CHRIST~1.DOE/AppData/Local/Temp/_MEI106042\nsys.argv[0] is                  examples/example6.exe\nsys.executable is               K:/pathlibutil/examples/example6.exe\nos.getcwd is                    K:/pathlibutil\n\nPath.cwd(frozen=True) is        K:/pathlibutil/examples\nPath.cwd(frozen=False) is       K:/pathlibutil\nPath.cwd(frozen=_MEIPASS) is    C:/Users/CHRIST~1.DOE/AppData/Local/Temp/_MEI106042\n```\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "inherits from pathlib.Path with methods for hashing, copying, deleting and more",
    "version": "0.1.9",
    "project_urls": {
        "Documentation": "https://d-chris.github.io/pathlibutil",
        "Homepage": "https://d-chris.github.io",
        "Repository": "https://github.com/d-chris/pathlibutil"
    },
    "split_keywords": [
        "pathlib",
        " hashlib",
        " shutil"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4cf183eb7cba3ba0c55c6f4202b4271dd3204dbc0a4a9582d7b967f9748c8753",
                "md5": "913b111472e7b95aae67c440315375ff",
                "sha256": "0d257635a2ea6556c087fc85732d7651a64a896027ad9f2ead945bd578960cdc"
            },
            "downloads": -1,
            "filename": "pathlibutil-0.1.9-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "913b111472e7b95aae67c440315375ff",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<3.13,>=3.8.1",
            "size": 14942,
            "upload_time": "2024-04-12T19:40:17",
            "upload_time_iso_8601": "2024-04-12T19:40:17.714196Z",
            "url": "https://files.pythonhosted.org/packages/4c/f1/83eb7cba3ba0c55c6f4202b4271dd3204dbc0a4a9582d7b967f9748c8753/pathlibutil-0.1.9-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8ca602080ccfb2d3b203a94f6a0bc94cc9a5adff4cb2d3c6c0c43065d4162b1a",
                "md5": "40a3cbc6a8e21619767ec4c834d101f1",
                "sha256": "9250049ad9e0fb0d188d7ff2fac88300891081da81e8cd9ac634a50898dbb879"
            },
            "downloads": -1,
            "filename": "pathlibutil-0.1.9.tar.gz",
            "has_sig": false,
            "md5_digest": "40a3cbc6a8e21619767ec4c834d101f1",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<3.13,>=3.8.1",
            "size": 15595,
            "upload_time": "2024-04-12T19:40:19",
            "upload_time_iso_8601": "2024-04-12T19:40:19.571577Z",
            "url": "https://files.pythonhosted.org/packages/8c/a6/02080ccfb2d3b203a94f6a0bc94cc9a5adff4cb2d3c6c0c43065d4162b1a/pathlibutil-0.1.9.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-12 19:40:19",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "d-chris",
    "github_project": "pathlibutil",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "pathlibutil"
}
        
Elapsed time: 0.25639s