Skip to main content

bunches

Version PyPI Latest Release GitHub Latest Release
Status Build Status Development Status Project Stability
Documentation Hosted By
Tools Documentation Linter Dependency Manager Pre-commit CI Editor Settings Repository Template Dependency Maintainer
Compatibility Compatible Python Versions Linux MacOS Windows
Stats PyPI Download Rate (per month) GitHub Stars GitHub Contributors GitHub Issues GitHub Forks

What is bunches?

bunches provides drop-in replacements for Python's built-in dict and list that add a consistent set of extra tools. Every class in the package:

  • stores its data in a plain Python collection in the contents attribute, so you can always drop back to the built-in type;
  • has an add method, the default way to put data in the collection;
  • has a delete method, the default way to remove data (it is also what del collection[item] calls);
  • has a subset method that returns a new collection containing only some of the data;
  • supports + (returns a new collection with an item added) and += (adds the item in place), both of which use add.

The package is fully typed, has no dependencies, and supports Python 3.11 and later.

Why use bunches?

The built-in collections are excellent, but they leave you to re-implement the same conveniences whenever you build a registry, a set of options, or an ordered group of named items. bunches supplies those conveniences once: wildcard lookups, keys inferred from the items you store, layered dictionaries, and lists that can be searched by name.

Classes

Mappings

  • Dictionary: drop-in replacement for a python dict with an add method for a default mechanism of adding data, a delete method for a default mechanism of deleting data, and a subset method for returning a subset of the key/value pairs in a new Dictionary. Its keys, values and items methods return tuples and it has defaultdict-like behavior when using get.
  • Catalog: wildcard-accepting dict which is intended for storing different options and strategies. It recognizes the special keys all, default and none and returns lists of matches if a list of keys is provided.
  • ChainDict: a Dictionary that stores several dictionaries and searches them in order, like collections.ChainMap.
  • Repository: a dictionary that automatically supplies key names for stored items. The overwrite argument determines if a unique key should always be created or whether entries may be overwritten.

Sequences

  • Listing: drop-in replacement for a python list with an add method for a default mechanism of adding data, a delete method for a default mechanism of deleting data, a prepend method, and a subset method for returning a subset of the items in a new Listing.
  • DictList: iterable with both dict and list interfaces. Stored items must be hashable or have a name attribute.

Getting started

Requirements

bunches requires Python 3.11 or later and runs on Linux, macOS and Windows. It has no third-party dependencies.

Installation

To install bunches, use pip:

pip install bunches

Usage

Dictionary

A Dictionary works like a dict. Its add method updates the stored data, subset returns a new Dictionary, and get falls back on default_factory (a value or a callable) when a key is missing.

from bunches import Dictionary

colors = Dictionary(contents={"red": 1, "green": 2}, default_factory=0)
colors.add({"blue": 3})
colors["blue"]  # 3
colors.get("purple")  # 0
colors.subset(include=["red", "blue"]).contents  # {'red': 1, 'blue': 3}
colors.subset(exclude="red").keys()  # ('green', 'blue')
del colors["green"]
colors.contents  # {'red': 1, 'blue': 3}
(colors + {"pink": 4}).contents  # {'red': 1, 'blue': 3, 'pink': 4}
colors.contents  # {'red': 1, 'blue': 3}

Catalog

A Catalog is meant for storing options. The all, default and none keys are special and a list of keys returns a list of values.

from bunches import Catalog

models = Catalog(contents={"tree": "DecisionTree", "forest": "RandomForest"})
models["tree"]  # 'DecisionTree'
models[["tree", "forest"]]  # ['DecisionTree', 'RandomForest']
models["all"]  # ['DecisionTree', 'RandomForest']
models.default = "forest"
models["default"]  # 'RandomForest'
models["none"] is None  # True

ChainDict

A ChainDict searches its stored dictionaries in order and returns the first match. Set return_first to False to get every match.

from bunches import ChainDict, Dictionary

chain = ChainDict(contents=[Dictionary({"a": 1}), Dictionary({"a": 2, "b": 3})])
chain["a"], chain["b"]  # (1, 3)
chain.return_first = False
chain["a"]  # [1, 2]
chain.new_child(Dictionary({"c": 4}))
chain.keys()  # ('c', 'a', 'a', 'b')

Repository

A Repository infers the key for each item added: the item itself if it is a str, its name attribute, or the snake-cased name of its class. Unless overwrite is True, a counter is appended to avoid overwriting existing items.

from dataclasses import dataclass
from bunches import Repository


@dataclass
class Worker:
    name: str


class DataLoader:
    pass


repository = Repository()
repository.add(Worker("ann"))
repository.add(DataLoader())
repository.add(DataLoader())
repository.add(Worker("bob"), key="boss")
repository.keys()  # ('ann', 'data_loader', 'data_loader2', 'boss')

Listing

A Listing works like a list. add extends the list with a sequence and appends anything else, and prepend does the same at the beginning.

from bunches import Listing

letters = Listing(contents=["b", "c"])
letters.add("d")
letters.add(["e", "f"])
letters.prepend("a")
letters.contents  # ['a', 'b', 'c', 'd', 'e', 'f']
letters.subset(include=["a", "b", "c"], exclude="b").contents  # ['a', 'c']
letters.delete(0)
letters[0]  # 'b'

DictList

A DictList is a list you can also search by name. Items are found by their name attribute or, if they have none, by their own value. Duplicate names are allowed; a DictList of the matches is returned.

from bunches import DictList

workers = DictList(contents=[Worker("ann"), Worker("bob"), Worker("ann")])
workers.keys()  # ('ann', 'bob', 'ann')
workers["bob"]  # Worker(name='bob')
len(workers["ann"])  # 2
workers[1]  # Worker(name='bob')
workers.delete("ann")
workers.keys()  # ('bob',)

Choosing what subset returns

Every subset method has a returns argument. 'class' (the default) returns a new instance of the same class, 'copy' returns a deep copy of the instance (keeping its other settings) and 'simple' returns the built-in type. To change the default for the whole package, use bunches.settings.set_subset_return.

colors = Dictionary({"red": 1, "green": 2})
colors.subset(include="red", returns="simple")  # {'red': 1}

Naming items

Repository and DictList name items with a global function that you can replace using bunches.settings.set_key_namer.

from bunches import settings

settings.set_key_namer(lambda item: str(item).upper())
Repository({"a": 1}).add("b")
DictList(["x", "y"]).keys()  # ('X', 'Y')
settings.set_key_namer(settings.utilities._namify)

Contributing

Contributors are always welcome. Feel free to grab an issue to work on or make a suggested improvement. If you wish to contribute, please read the Contribution Guide and Code of Conduct.

Similar Projects

  • collections in the Python standard library provides UserDict, UserList, ChainMap and defaultdict, which inspired several of the classes in bunches.
  • sortedcontainers provides sorted drop-in replacements for list, dict and set.

Acknowledgments

I'd also like to extend a special thanks to pawamoy whose excellent mkdocs extensions and utilities are incorporated into snickerdoodle. Some of the scripts, documentation, configuration files, and other CI code were adapted from pawamoy's repositories.

I would also like to thank the University of Kansas School of Law for tolerating and supporting this law professor's coding efforts, an endeavor which is well outside the typical scholarly activities in the discipline.

License

Use of this repository is authorized under the Apache Software License 2.0.

Metadata

Release files for bunches 0.2.1

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

Source distribution (sdist)

Source distribution for bunches 0.2.1
File Size Uploaded
bunches-0.2.1.tar.gz 125.8 kB Details

Built distribution (wheel)

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

Total release size: 148.2 kB

Release files / bunches-0.2.1.tar.gz

Download URL bunches-0.2.1.tar.gz
Size 125.8 kB
Tags Source
SHA-256 checksum
How to use checksums
8eace5ec0c76e417a9e183db7787d5878826ad1b94647181e99b2b7bbce46784
BLAKE2b-256 checksum
How to use checksums
3007a8941929af1422e793b40638174945c9f287a29d007b7865130bc4601029
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / bunches-0.2.1-py3-none-any.whl

Download URL bunches-0.2.1-py3-none-any.whl
Size 22.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7d0d4aac80ebe8d838345887ffcfcfc5e9e5fa86308a4c3af2c78aecfe9cd06b
BLAKE2b-256 checksum
How to use checksums
16c74d6396d90c5d5e4e97bda37f86972d2131ee5b84391fb65c1f06d3d01087
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.3

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