dashloom
Functional essentials woven on pydash. dashloom keeps pydash as the engine for map, filter, deep get, grouping, case conversion, chunking, debounce, and the rest of its API, and adds the record, path, diff, and validation tools that show up in everyday Python.
Requires Python 3.10+ and pydash.
Install
After the package is published on PyPI:
pip install dashloom
That installs dashloom and its dependency, pydash. It does not install the test tools.
A chain
loom() (also available as chain()) queues steps and runs them at value(). A chain with no seed can be called like a function. Unknown methods are pydash functions, so sum reaches pydash.sum_ and without reaches pydash.without.
from dashloom import loom, pydash
people = [
{"name": "Ada", "city": "London", "score": 91},
{"name": "Grace", "city": "New York", "score": 88},
{"name": "Lin", "city": "London", "score": 95},
]
leaders = (
loom(people)
.where(city="London")
.order_by("score", "desc")
.select("name", "score")
.value()
)
assert loom().map(lambda n: n * 2).sum()([1, 2, 3]) == 12
assert pydash.debounce is not None
set and set_ on a chain copy the container. Pass inplace=True to mutate. Direct pydash writers are still available on dashloom.pydash.
Records
from dashloom import join, paginate, pivot, where
rows = [
{"id": 1, "region": "us", "amount": 10},
{"id": 2, "region": "eu", "amount": 5},
{"id": 1, "region": "us", "amount": 2},
]
page = paginate(where(rows, region="us"), page=1, per_page=1)
table = pivot(rows, "region", "id", "amount", agg="sum")
paired = join(
[{"id": 1, "name": "Ada"}],
[{"id": 1, "name": "Admin", "role": "owner"}],
on="id",
)
where, order_by, group, index, and unique_by keep the original row objects. select, rename, join, and pivot return new dicts. List order is stable. None sort keys stay last. order_by accepts asc/desc and pydash-style booleans (True ascending, False descending). A leading - on a key means descending when orders are omitted.
join supports inner, left, right, and full. Overlapping non-key fields get the suffixes _x and _y.
Paths, keys, diff
from dashloom import coalesce, deep_defaults, diff, flatten_keys, patch, set_, snake_keys
profile = {"user": {"name": "Ada"}}
renamed = set_(profile, "user.roles[0]", "admin")
city = coalesce({"address": {"city": None}, "city": "London"}, "address.city", "city")
config = deep_defaults({"db": {"host": "local"}}, {"db": {"port": 5432}})
flat = flatten_keys({"user": {"name": "Ada"}, "tags": ["a"]})
before = {"service": {"retries": 1}}
after = {"service": {"retries": 3}, "region": "us"}
assert patch(before, diff(before, after)) == after
snake_keys({"userName": {"cityName": "Paris"}})
Paths use dots, list indexes, and quoted keys: user.name, items[0].id, ["a.b"]. Dict keys in diff and flatten are strings. Lists are compared by index, so a reorder is a series of replacements. patch(before, diff(before, after)) rebuilds after for dicts, lists, and scalars.
get returns a stored None. The default is used only when the path is missing. dig raises PathError when the path is missing.
Validation
from dashloom import Schema, between, email, integer, required, string
account = (
Schema()
.field("email", required(), string(), email())
.field("age", required(), integer(), between(0, 120))
.field("address.city", string(), required())
)
account.assert_valid({"email": "ada@example.com", "age": 36, "address": {"city": "London"}})
Field paths are pydash paths. Every failed rule is reported. required rejects a missing path and None.
Text, numbers, calls, sequences
from dashloom import human_size, mask, parse_duration, retry, slug, window
slug("Crème brûlée")
mask("secret", keep_start=1, keep_end=1)
human_size(1536)
parse_duration("1h30m")
window([1, 2, 3, 4], 2)
@retry(attempts=3, delay=0)
def load():
return "ok"
pluralize is a regular-English heuristic. compact follows pydash and removes every falsey value, including 0 and "". parse_number accepts currency marks, thousands separators, and parentheses for negatives.
pydash
from dashloom import pydash
pydash.map_([1, 2, 3], lambda n: n + 1)
pydash.chain([1, 2, 3, 4]).without(2).value()
from dashloom import pydash is the installed pydash package. Use it for debounce, throttle, curry, merge, clone, and the rest of the pydash 8 API. The main pydash module is the supported surface; dashloom calls that, not pydash's private submodules.
Develop
From this folder, install the local source plus pytest and ruff:
python -m pip install -e ".[dev]"
python -m ruff check src tests examples
python -m pytest
python examples/quickstart.py
-e means editable: Python uses this folder, so edits apply without reinstalling. .[dev] is the optional dev dependency group in pyproject.toml.
Build the files that get uploaded to PyPI:
python -m pip install build twine
python -m build
python -m twine upload dist/*
python -m build writes dist/dashloom-1.1.1.tar.gz and a wheel. twine upload publishes them. After that, other people can run pip install dashloom. Uploading needs a PyPI account and an API token.
License
MIT. pydash is a separate project under its own MIT license.