Skip to content

Home

CI Nightly Package Tests Codecov Documentation (stable)
PyPI version Python BSD-3-Clause
Ruff Doc style: google OpenSSF Scorecard
Downloads Monthly downloads GitHub stars

Overview

coola is a lightweight Python library to compare, hash and summarize complex and nested data structures. It provides simple, extensible functions that work on objects containing PyTorch tensors, NumPy arrays, pandas/polars DataFrames, and other scientific computing objects:

  • Compare: objects_are_equal, objects_are_allclose, assert_objects_equal and compare check equality (exact or with a tolerance) and tell where two objects differ.
  • Hash: hash_object computes a deterministic hash of a nested object.
  • Summarize: summarize creates a short, readable description of a large structure for logs.

Quick Links:

Why coola?

Python's native equality operator (==) doesn't work well with complex nested structures containing tensors, arrays, or DataFrames. You'll often encounter errors or unexpected behavior. coola solves this with intuitive comparison functions:

Check exact equality:

>>> import numpy as np
>>> import torch
>>> from coola.equality import objects_are_equal
>>> data1 = {"torch": torch.ones(2, 3), "numpy": np.zeros((2, 3))}
>>> data2 = {"torch": torch.ones(2, 3), "numpy": np.zeros((2, 3))}
>>> objects_are_equal(data1, data2)
True

Compare with numerical tolerance:

>>> from coola.equality import objects_are_allclose
>>> data1 = {"value": 1.0}
>>> data2 = {"value": 1.0 + 1e-9}
>>> objects_are_allclose(data1, data2)
True

Assert equality in your tests, with the path of the difference:

>>> from coola.equality import assert_objects_equal
>>> actual = {"users": [{"id": 1, "score": 95}, {"id": 2, "score": 87}]}
>>> expected = {"users": [{"id": 1, "score": 95}, {"id": 2, "score": 88}]}
>>> assert_objects_equal(actual, expected)
Traceback (most recent call last):
    ...
AssertionError: objects are not equal at actual['users'][1]['score']
numbers are different:
  actual   : 87
  expected : 88

assert_objects_allclose does the same with a tolerance.

Get the difference as data:

>>> from coola.equality import compare
>>> result = compare(actual, expected)
>>> result.equal
False
>>> result.path
('users', 1, 'score')
>>> print(result.reason)
numbers are different:
  actual   : 87
  expected : 88

Hash and summarize:

>>> from coola.hashing import hash_object
>>> from coola.summary import summarize
>>> len(hash_object(actual))
64
>>> print(summarize(actual, max_depth=2))
<class 'dict'> (length=1)
  (users): <class 'list'> (length=2)
      (0): {'id': 1, 'score': 95}
      (1): {'id': 2, 'score': 87}

compare(actual, expected) explains the first difference (its path and its reason), and assert_objects_equal(actual, expected) raises an AssertionError with the same message.

See the user guide for detailed examples.

Features

coola provides a comprehensive set of utilities for working with complex data structures:

πŸ” Equality Comparison

Compare complex nested objects with support for multiple data types:

  • Exact equality: objects_are_equal() for strict comparison
  • Approximate equality: objects_are_allclose() for numerical tolerance
  • Assertions for tests: assert_objects_equal() and assert_objects_allclose() raise an AssertionError with the path of the difference
  • Structured results: compare() returns a ComparisonResult with the path and the reason
  • Extensible: Add custom equality testers for your own types

Learn more β†’

Supported types: JAX β€’ NumPy β€’ pandas β€’ polars β€’ PyArrow β€’ pydantic β€’ PyTorch β€’ SciPy (sparse matrices and arrays) β€’ xarray β€’ Python built-ins (dict, list, tuple, set, etc.)

Learn more about supported types β†’

πŸ“Š Data Summarization

Generate human-readable summaries of nested data structures for debugging and logging:

  • Configurable depth control
  • Type-specific formatting
  • Truncation for large collections

Learn more β†’

πŸ”„ Data Conversion

Transform data between different nested structures:

  • Convert between list-of-dicts and dict-of-lists formats
  • Useful for working with tabular data and different data representations

Learn more β†’

πŸ—‚οΈ Mapping Utilities

Work with nested dictionaries efficiently:

  • Flatten nested dictionaries into flat key-value pairs
  • Extract specific values from complex nested structures
  • Filter dictionary keys based on patterns or criteria, rename keys and merge mappings

Learn more β†’

πŸ” Iteration

Traverse nested data structures systematically:

  • Depth-first search (DFS) traversal for nested containers
  • Breadth-first search (BFS) traversal for level-by-level processing
  • Filter and extract specific types from heterogeneous collections

Learn more β†’

πŸ“ˆ Reduction

Compute statistics on sequences with flexible backends:

  • Calculate min, max, mean, median, quantile, std on numeric sequences
  • Support for multiple backends: native Python, NumPy, PyTorch
  • Consistent API regardless of backend choice

Learn more β†’

#️⃣ Hashing

Compute stable hashes of nested objects:

  • Hash dicts, lists, sets, dataclasses, pydantic models, arrays and data frames
  • Independent of the insertion order of mappings and sets
  • Extensible with custom hashers

Learn more β†’

πŸ†” Identifiers

Generate unique identifiers: ULID, UUID (v5, v7), Snowflake, ObjectId, and checksummed and content-based identifiers.

Learn more β†’

πŸ’Ύ Data Loading and Saving

Load and save JSON, pickle, text and PyTorch files with atomic writes, an overwrite policy and optional durability (fsync).

Learn more β†’

🎲 Random Number Generators

Seed, save and restore the random number generators of random, NumPy and PyTorch in one call.

Learn more β†’

πŸ”§ Recursive Transformation and Validation

  • Apply a function to every leaf of a nested structure
  • Validate arguments with clear error messages

Learn more about transformation β†’ Β· Learn more about validation β†’

🧩 Registries and Factories

Build extensible components:

  • Thread-safe Registry and TypeRegistry (lookup by Method Resolution Order, lazy loading of optional libraries)
  • Build any importable class or function from a configuration dictionary with coola.factory

Learn more about registries β†’ Β· Learn more about factories β†’

🧾 JSON Conversion

Convert nested data (dataclasses, sets, paths, arrays, tensors, pydantic models, ...) to JSON data with to_jsonable, with a strict, extensible and cycle-safe conversion.

Learn more β†’

πŸ”’ Rounding

Round, floor or ceil numbers, NumPy arrays, PyTorch tensors, and pandas, polars and PyArrow data with round_values.

Learn more β†’

πŸ–¨οΈ Display and Formatting

Build consistent __repr__/__str__ with display mixins, mask secrets, format sizes and durations, and configure rich and colorlog output.

Learn more about display β†’ Β· Learn more about formatting β†’

πŸ§ͺ Testing and Utilities

pytest marks and fixtures to test code with optional dependencies, and helpers for imports, paths, environment variables, caches and more.

Learn more about testing β†’ Β· Learn more about utilities β†’

Contributing

Contributions are welcome! We appreciate bug fixes, feature additions, documentation improvements, and more. Please check the contributing guidelines for details on:

  • Setting up the development environment
  • Code style and testing requirements
  • Submitting pull requests

Whether you're fixing a bug or proposing a new feature, please open an issue first to discuss your changes.

API Stability

⚠ Important: As coola is under active development, its API is not yet stable and may change between releases. We recommend pinning a specific version in your project’s dependencies to ensure consistent behavior.

License

coola is licensed under BSD 3-Clause "New" or "Revised" license available in LICENSE file.