mo-dots: Dot-Access to Python Dictionaries
mo-dots is a Python library that provides 'dot-access' functionality to dictionary-like data structures, similar to JavaScript objects. It boxes JSON-like data into a `Data` object, offering null-safe attribute access and a consistent interface. The library aims to be a more consistent and easier-to-use replacement for standard Python dictionaries, facilitating data transformation tasks. It is actively maintained, with the current version being 10.685.25166.
Common errors
-
TypeError: Object of type Data is not JSON serializable
cause `json.dumps()` was called directly on a `mo_dots.Data` object without a custom default serializer.fixImport `from_data` and pass it to the `default` argument of `json.dumps()`: `json.dumps(my_data, default=from_data)`. -
KeyError: 'some_missing_key'
cause Attempting to access a non-existent key on a `mo_dots.Data` object using standard dictionary bracket notation, but `mo-dots` usually returns `Null` for missing keys accessed via dot notation or path strings, and handles some bracket accesses differently.fixFor `Data` objects, non-existent keys accessed via dot notation (e.g., `data.some_missing_key`) or path strings (e.g., `data['a.b.c']`) will return `Null`. If you are explicitly trying to catch `KeyError`, ensure you are using standard dicts or adapt to `mo-dots`'s null-safe behavior. If you are getting this with `data[key]`, check if `key` actually exists or use `data.get(key)`. -
AttributeError: 'Null' object has no attribute 'some_method'
cause A chained dot access or an operation was attempted on a `Null` value, which was returned by `mo-dots` for a non-existent path.fixAlways check if a path exists or returns `Null` before performing further operations on it. For example, `if data.path and data.path.attribute: ...` or `value = data.get('path.attribute', None); if value is not None: ...`
Warnings
- gotcha The standard Python `json` library does not natively recognize `mo_dots.Data` objects for serialization, leading to `TypeError`.
- gotcha Accessing a non-existent key or attribute on a `Data` object using dot notation (`data.non_existent`) or bracket notation (`data['non.existent']`) will return `Null` instead of raising a `KeyError` or `AttributeError`.
- gotcha The `+=` operator on a non-existent path (`data.new_path += value`) has special behavior: for numerical types, `new_path` is initialized to `0` before adding `value`; for list types, `new_path` is initialized to `[]` before appending `value`.
Install
-
pip install mo-dots
Imports
- Data
import mo_dots.Data
from mo_dots import Data
- to_data
from mo_dots import to_data
- from_data
from mo_dots import from_data
Quickstart
import json
from mo_dots import Data, to_data, from_data
# Create a Data object directly
d = Data(a=1, b={'c': 2, 'd': [3, 4]})
print(f"Initial Data object: {d}")
# Access with dot notation (null-safe)
print(f"d.a: {d.a}")
print(f"d.b.c: {d.b.c}")
print(f"d.b.e: {d.b.e} (returns Null, not KeyError)")
# Path assignment
d['x.y.z'] = 99
print(f"After path assignment: {d}")
# Path accumulation with +=
d.stats.count += 1 # Initializes to 0 then adds 1
d.stats.items += ['apple'] # Initializes to [] then appends
d.stats.items += ['banana']
print(f"After path accumulation: {d}")
# Convert an existing dictionary
my_dict = {'user': {'name': 'Alice', 'id': 123}}
data_from_dict = to_data(my_dict)
print(f"Data from dict: {data_from_dict.user.name}")
# Serialize to JSON (requires from_data)
try:
json.dumps(d) # This will raise TypeError without default=from_data
except TypeError as e:
print(f"Caught expected TypeError during direct JSON serialization: {e}")
json_output = json.dumps(d, default=from_data)
print(f"JSON output: {json_output}")
assert json.loads(json_output) == {'a': 1, 'b': {'c': 2, 'd': [3, 4]}, 'x': {'y': {'z': 99}}, 'stats': {'count': 1, 'items': ['apple', 'banana']}}