🛠 Pytility 🛠
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— coercesstrorbytestostr, stripping non-printable characters; anything else returnsNone.normalize_space(item, preserve_newline=False) -> str— collapses runs of whitespace to single spaces; withpreserve_newline=True, newlines are kept but whitespace is normalized within each line.truncate(string, length, ellipsis="[…]", respect_word=False) -> str— truncatesstringtolengthcharacters plus an ellipsis; withrespect_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 astr/bytes/dict, which are treated as scalars) in a 1-tuple;Nonebecomes(); 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/dictare treated as scalars, not flattened further.take_first(items) -> Any | None— the first item that isn'tNoneor"", orNoneif there isn't one.batchify(iterable, size) -> Iterable[Iterable]— splitsiterableinto chunks of at mostsizeitems.window(iterable, size=2) -> Iterable[tuple]— a sliding window ofsizeconsecutive items overiterable.
Parsers (pytility.parsers)
parse_int(string, base=10) -> int | None— safely converts toint, orNoneon failure.parse_float(number) -> float | None— safely converts tofloat, orNoneon failure.parse_bool(item) -> bool— recognizesints, the strings"True","true","Yes","yes", and anything elseparse_intcan parse as non-zero; everything else isFalse.parse_date(date, tzinfo=None, format_str=None) -> datetime | None— parsesdatetime/dateobjects, epoch timestamps, a givenformat_str, free-form strings (viapython-dateutil, if installed), or(year, month, day, ...)tuples /time.struct_time. Falsy input (including0) returnsNonebefore any parsing is attempted.
Files (pytility.files)
concat_files(dst, srcs, ensure_newline=False) -> int— concatenatessrcsintodst(paths or open file objects), returning the total bytes written. Withensure_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)
| File | Size | Uploaded | |
|---|---|---|---|
| pytility-1.0.0.tar.gz | 44.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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}
|