handyobj
handyobj provides two small, typed collection helpers:
SmartList, alistsubclass with chainable map, filter, grouping, sorting, reduction, and threaded mapping operations.ObjectDict, adictsubclass that recursively exposes string keys through attribute access.
The package supports Python 3.10 and newer and has no runtime dependencies.
Installation
python -m pip install handyobj
Quick start
from handyobj import ObjectDict, SmartList
people = SmartList(
[
ObjectDict(name="Ada", role="Engineer"),
ObjectDict(name="Grace", role="Admiral"),
ObjectDict(name="Margaret", role="Engineer"),
]
)
engineers = (
people.filter_by_attribute_values(role="Engineer")
.map(lambda person: person.name)
.sorted()
)
assert engineers == ["Ada", "Margaret"]
SmartList
Every non-mutating collection operation returns another SmartList, so calls can
be chained:
numbers = SmartList([1, 2, 3, 4])
result = numbers.filter_by_predicates(lambda number: number % 2 == 0).map(
lambda number: number * 10
)
assert result == [20, 40]
assert numbers == [1, 2, 3, 4]
Useful methods include:
map(function)andfilter_by_predicates(*predicates)filter_by_attribute_values(**values)filter_by_matched_attributes(**patterns)group_by_labeled_predicates(*(label, predicate))reduce(reducer, initial)first(),last(),first_or_none(),last_or_none(), andone_or_none()sorted(key=..., reverse=...), which returns a sorted copythreaded_map(function, max_workers=...)for synchronous, I/O-bound work
SmartList retains the normal list.sort() contract: it sorts in place and returns
None. Use sorted() when building a chain.
Groups are independent, so an item can belong to multiple labeled groups:
groups = SmartList(range(1, 7)).group_by_labeled_predicates(
("even", lambda number: number % 2 == 0),
("large", lambda number: number >= 5),
)
assert groups == [("even", [2, 4, 6]), ("large", [5, 6])]
ObjectDict
Mappings are recursively wrapped at assignment time. Mutating a nested object therefore updates the original structure:
config = ObjectDict(
database={"host": "localhost", "ports": [5432, 5433]},
services=[{"name": "api", "enabled": True}],
)
config.database.host = "db.example.com"
config.services[0].enabled = False
assert config["database"]["host"] == "db.example.com"
assert config.services[0]["enabled"] is False
Dictionary methods take precedence over colliding keys. For a key such as items,
use item access (value["items"]) rather than attribute access (value.items).
clone() returns a deep, independent copy. drop_keys() and keep_only_keys()
mutate the object and return it, allowing them to be chained.
Concurrency
threaded_map() uses Python's ThreadPoolExecutor, preserves input order, and
propagates worker exceptions. It accepts ordinary synchronous functions and is
mainly useful for I/O-bound operations. It does not execute async def functions;
use an asyncio-native workflow for coroutine functions.
Development
uv sync --all-groups
uv run pytest --cov
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv build
See CHANGELOG.md for release notes.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file handyobj-0.3.0.tar.gz.
File metadata
- Download URL: handyobj-0.3.0.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3949fa102b95634e8fae2db97dcb9826dd1e662f35469ec0d1960bc4fc6eb84
|
|
| MD5 |
37a5d1369f33d6a720790c4561d43713
|
|
| BLAKE2b-256 |
4b0e751478fd7f07758a75d8ca1546e4ca86cfeecfa48748f86584deec1e65a5
|
Provenance
The following attestation bundles were made for handyobj-0.3.0.tar.gz:
Publisher:
release.yml on Senhaji-Rhazi-Hamza/handyobj
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
handyobj-0.3.0.tar.gz -
Subject digest:
a3949fa102b95634e8fae2db97dcb9826dd1e662f35469ec0d1960bc4fc6eb84 - Sigstore transparency entry: 2408791486
- Sigstore integration time:
-
Permalink:
Senhaji-Rhazi-Hamza/handyobj@4e8f12a3302da090f76a137db69c499add7564e3 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Senhaji-Rhazi-Hamza
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4e8f12a3302da090f76a137db69c499add7564e3 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file handyobj-0.3.0-py3-none-any.whl.
File metadata
- Download URL: handyobj-0.3.0-py3-none-any.whl
- Upload date:
- Size: 10.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e1fde42f5123b57ed507eef33eed6f4e1a5218e8424772e8f683c76f6d05829b
|
|
| MD5 |
c3b4f96ec445b4a89e5fe8c2bd07ae0b
|
|
| BLAKE2b-256 |
3d77aebd9e7849f6d71b272af89e478bafb1050080edfefe1c98296892dd3493
|
Provenance
The following attestation bundles were made for handyobj-0.3.0-py3-none-any.whl:
Publisher:
release.yml on Senhaji-Rhazi-Hamza/handyobj
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
handyobj-0.3.0-py3-none-any.whl -
Subject digest:
e1fde42f5123b57ed507eef33eed6f4e1a5218e8424772e8f683c76f6d05829b - Sigstore transparency entry: 2408791613
- Sigstore integration time:
-
Permalink:
Senhaji-Rhazi-Hamza/handyobj@4e8f12a3302da090f76a137db69c499add7564e3 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Senhaji-Rhazi-Hamza
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4e8f12a3302da090f76a137db69c499add7564e3 -
Trigger Event:
workflow_dispatch
-
Statement type: