Skip to main content

🛠 Pytility 🛠

PyPI PyPI - Python Version PyPI - License


Source Code: https://gitlab.com/mshepherd/pytility

PyPI: https://pypi.org/project/pytility/


A lean collection of Python utilities — small, dependency-free helpers for strings, iterables, parsing, and files, with no runtime dependencies unless you opt into the dates extra.

Installation

pip install pytility

Date parsing via python-dateutil is an optional extra:

pip install pytility[dates]

Usage

import pytility

pytility.normalize_space("  too   much   space  ")  # "too much space"
pytility.truncate("a long string", 6)  # "a long[…]"
pytility.parse_bool("yes")  # True
pytility.parse_int("2a", base=16)  # 42
pytility.clear_list([1, 1, None, 2, "", 3])  # [1, 2, 3]
list(pytility.flatten([1, [2, [3, 4]], 5]))  # [1, 2, 3, 4, 5]

API reference

Strings (pytility.strings)

  • to_str(string, encoding="utf-8") -> str | None — coerces str or bytes to str, stripping non-printable characters; anything else returns None.
  • normalize_space(item, preserve_newline=False) -> str — collapses runs of whitespace to single spaces; with preserve_newline=True, newlines are kept but whitespace is normalized within each line.
  • truncate(string, length, ellipsis="[…]", respect_word=False) -> str — truncates string to length characters plus an ellipsis; with respect_word=True, extends the cut to the end of the word it lands in rather than splitting it.

Iterables (pytility.iterables)

  • arg_to_iter(arg) -> Iterable — wraps a non-iterable (or a str/bytes/ dict, which are treated as scalars) in a 1-tuple; None becomes (); anything else iterable is returned as-is.
  • clear_list(items) -> list — unique items in order of first occurrence, with falsy values (None, "", 0, …) dropped.
  • flatten(*args) -> Iterable — recursively flattens nested iterables; str/bytes/dict are treated as scalars, not flattened further.
  • take_first(items) -> Any | None — the first item that isn't None or "", or None if there isn't one.
  • batchify(iterable, size) -> Iterable[Iterable] — splits iterable into chunks of at most size items.
  • window(iterable, size=2) -> Iterable[tuple] — a sliding window of size consecutive items over iterable.

Parsers (pytility.parsers)

  • parse_int(string, base=10) -> int | None — safely converts to int, or None on failure.
  • parse_float(number) -> float | None — safely converts to float, or None on failure.
  • parse_bool(item) -> bool — recognizes ints, the strings "True", "true", "Yes", "yes", and anything else parse_int can parse as non-zero; everything else is False.
  • parse_date(date, tzinfo=None, format_str=None) -> datetime | None — parses datetime/date objects, epoch timestamps, a given format_str, free-form strings (via python-dateutil, if installed), or (year, month, day, ...) tuples / time.struct_time. Falsy input (including 0) returns None before any parsing is attempted.

Files (pytility.files)

  • concat_files(dst, srcs, ensure_newline=False) -> int — concatenates srcs into dst (paths or open file objects), returning the total bytes written. With ensure_newline=True, a newline is appended after every source that doesn't already end in one — including the last.

Development

  • Clone this repository
  • Requirements:
    • uv
    • Python 3.10+
  • Create a virtual environment and install the dependencies
uv sync

Testing

uv run pytest

Pre-commit

Pre-commit hooks run the auto-formatter and linter (ruff), the type checker (ty), and other housekeeping checks so the changeset is in good shape before a commit happens.

Install the hooks with (runs on every commit):

uv run pre-commit install

Or run all checks manually against every file:

uv run pre-commit run --all-files

CI (.gitlab-ci.yml) runs the same lint hooks, the test suite across Python 3.10–3.14, and a package build on every push.

Releasing

This project is published to PyPI manually — there's no CI publish job or GitHub-style draft release here, just these steps run locally:

# 1. Bump version (major|minor|patch|stable|alpha|beta|rc|post|dev, or pass an explicit value)
uv version --bump patch
VERSION=$(uv version --short)

# 2. Commit the version bump
git add pyproject.toml uv.lock
git commit -m "Release $VERSION"

# 3. Tag and push to GitLab (the only remote this repo has)
git tag "v$VERSION"
git push gitlab master
git push gitlab "v$VERSION"

# 4. Build and publish to PyPI (needs a token, e.g. via a UV_PUBLISH_TOKEN env var)
uv build
uv publish

Note that uv version only edits pyproject.toml; uv.lock is re-synced alongside it since it also records the project's own version.

Metadata

Release files for pytility 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytility 1.0.0
File Size Uploaded
pytility-1.0.0.tar.gz 44.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytility 1.0.0
File Interpreter ABI Platform
pytility-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 52.7 kB

Release files / pytility-1.0.0.tar.gz

Download URL pytility-1.0.0.tar.gz
Size 44.5 kB
Tags Source
SHA-256 checksum
How to use checksums
03f58287c7e8d80e79e3e1249c8d5227d007750ae80411246dd200ab8073bfaa
BLAKE2b-256 checksum
How to use checksums
dd80a8c0dd02785402130afc06c7a935c26214cc28b04b2e6ed80b3bd65bc53f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / pytility-1.0.0-py3-none-any.whl

Download URL pytility-1.0.0-py3-none-any.whl
Size 8.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
79f7ea3a43c297c02398bee00a187f5088f7ef7393bdff4956605ccf45e2251e
BLAKE2b-256 checksum
How to use checksums
2bda45f922d801b68fbb84179bb21429b1853357ee0b446cd36310991756efdc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page