Skip to main content

handyobj

handyobj provides two small, typed collection helpers:

  • SmartList, a list subclass with chainable map, filter, grouping, sorting, reduction, and threaded mapping operations.
  • ObjectDict, a dict subclass 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) and filter_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(), and one_or_none()
  • sorted(key=..., reverse=...), which returns a sorted copy
  • threaded_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

handyobj-0.3.0.tar.gz (12.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

handyobj-0.3.0-py3-none-any.whl (10.4 kB view details)

Uploaded Python 3

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

Hashes for handyobj-0.3.0.tar.gz
Algorithm Hash digest
SHA256 a3949fa102b95634e8fae2db97dcb9826dd1e662f35469ec0d1960bc4fc6eb84
MD5 37a5d1369f33d6a720790c4561d43713
BLAKE2b-256 4b0e751478fd7f07758a75d8ca1546e4ca86cfeecfa48748f86584deec1e65a5

See more details on using hashes here.

Provenance

The following attestation bundles were made for handyobj-0.3.0.tar.gz:

Publisher: release.yml on Senhaji-Rhazi-Hamza/handyobj

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for handyobj-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e1fde42f5123b57ed507eef33eed6f4e1a5218e8424772e8f683c76f6d05829b
MD5 c3b4f96ec445b4a89e5fe8c2bd07ae0b
BLAKE2b-256 3d77aebd9e7849f6d71b272af89e478bafb1050080edfefe1c98296892dd3493

See more details on using hashes here.

Provenance

The following attestation bundles were made for handyobj-0.3.0-py3-none-any.whl:

Publisher: release.yml on Senhaji-Rhazi-Hamza/handyobj

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.0

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 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