Skip to main content

A Python package containing rich-object utilities

Project description

rich-object

A powerful dictionary wrapper subclass that enables dot-notation attribute access, automatic nested path creation (autovivification), recursive locking, template rendering, JSONPath query resolution, deep structure diffing, and multi-format serialization (JSON, YAML, TOML).

Table of Contents

Installation

pip install rich-object

Features & Examples

1. Basic Usage & Dot Access

Create an Object from mappings or keyword arguments and access properties with standard dot notation.

from rich_object import Object

# Initialize using keywords or raw dictionaries
obj = Object(name="John", address={"city": "New York"})

print(obj.name)          # -> "John"
print(obj.address.city)  # -> "New York"

2. Autovivification

Missing nested directories/attributes are automatically initialized when accessed, resolving nested paths on the fly.

obj = Object()
obj.user.profile.settings.theme = "dark"

print(obj.to_dict())
# -> {'user': {'profile': {'settings': {'theme': 'dark'}}}}

3. Schema Validation (validate)

Ensure your configuration or data matches strict rules using standard JSON Schema.

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer", "minimum": 18}
    },
    "required": ["name", "age"]
}

user = Object({"name": "Alice", "age": 30})

# Validates successfully (returns True)
user.validate(schema) 

# Raises jsonschema.exceptions.ValidationError
invalid_user = Object({"name": "Bob", "age": 15})
invalid_user.validate(schema)

4. Structural Locks

Prevent mutations (creation, updates, deletes) recursively across all nested dictionaries and lists by passing lock=True.

frozen = Object({"items": [1, 2], "user": {"id": 42}}, lock=True)

# Any mutation throws a TypeError
try:
    frozen.user.id = 99
except TypeError as e:
    print(e)  # -> 'Object' is locked and cannot be modified

try:
    frozen.items.append(3)
except TypeError as e:
    print(e)  # -> 'ObjectList' is locked and cannot be modified

5. Advanced Property Setting (set)

Assign values safely to deeply nested paths using dot notation and bracket indices. Intermediary dictionary keys and list slots expand automatically.

obj = Object()
obj.set("store.books[1].title", "Moby Dick")

print(obj.store.books[1].title)  # -> "Moby Dick"
print(obj.store.books[0])        # -> None (automatically padded slot)

6. Deep Path & JSONPath Querying (get)

Query the data structure dynamically using standard dot paths or JSONPath queries (starting with $).

data = Object({
    "store": {
        "book": [
            {"category": "fiction", "price": 8.95},
            {"category": "reference", "price": 12.00}
        ]
    }
})

# Retrieve a single value using fast dot paths (no dependencies required)
print(data.get("store.book[0].category"))  # -> "fiction"

# Retrieve matching node value list using JSONPath
print(data.get("$..price"))                  # -> [8.95, 12.0]

7. Deep Merging (+ and |)

Perform clean, recursive merges of two structures using the + operator or the Python 3.9+ | operator. Nested lists are automatically concatenated, and conflicting keys default to the right-hand value. In-place merges (|= or +=) are also supported.

obj1 = Object({"a": {"x": 1}, "b": [1, 2]})
obj2 = Object({"a": {"y": 2}, "b": [3, 4]})

# Both operators do exactly the same thing
res = obj1 + obj2
res = obj1 | obj2

print(res.to_dict())
# -> {'a': {'x': 1, 'y': 2}, 'b': [1, 2, 3, 4]}

# In-place merge
obj1 |= obj2

8. Data Transformation (pick and omit)

Easily shape your data using dot-paths. pick keeps only what you need, and omit removes what you don't.

user = Object({
    "id": 101,
    "profile": {
        "name": "Alice",
        "email": "alice@example.com",
        "secret": "xyz123"
    }
})

# Keep only specific fields (reconstructs structure automatically)
public_user = user.pick("id", "profile.name")
# -> {'id': 101, 'profile': {'name': 'Alice'}}

# Omit specific fields
safe_user = user.omit("profile.secret")
# -> {'id': 101, 'profile': {'name': 'Alice', 'email': 'alice@example.com'}}

You can also use the deep=True flag with omit() to aggressively scrub a key from the entire nested structure. This is incredibly powerful for removing passwords or PII from giant API payloads:

# Removes "password" from the top level, inside 'profile', inside arrays, etc.
scrubbed_data = data.omit("password", deep=True)

9. Template Rendering (render)

Render Jinja2 templates in all string values recursively across the entire structure. Because the object passes itself as context, it allows for seamless self-referencing (using other properties from the same object inside a template).

obj = Object({
    "first_name": "Jane",
    "greeting": "Hello {{ first_name }}!",
    "matrix": [["Welcome {{ first_name }}"]]
})

rendered = obj.render()
print(rendered.greeting)  # -> "Hello Jane!"
print(rendered.matrix)    # -> [["Welcome Jane"]]

You can also pass entire modules or objects to the render method to use them inside your templates:

import datetime

obj = Object({
    "year": "{{ dt.datetime(2026, 7, 6).year }}",
    "future_date": "{{ (dt.datetime(2026, 7, 6) + dt.timedelta(days=5)).strftime('%Y-%m-%d') }}"
})

res = obj.render(dt=datetime)
print(res.year)         # -> 2026
print(res.future_date)  # -> "2026-07-11"

10. Structural Diffing (diff)

Identify differences between two objects using the built-in DeepDiff interface.

obj1 = Object({"a": 1, "b": 2})
obj2 = Object({"a": 1, "b": 3})

difference = obj1.diff(obj2)
print(difference)
# -> {'values_changed': {"root['b']": {'new_value': 3, 'old_value': 2}}}

11. JSON Serialization

Serialize to/from JSON strings and files. Uses the built-in json module — no extra dependencies.

obj = Object(name="John", scores=[95, 87, 92])

# To JSON string
json_str = obj.to_json(indent=2)

# From JSON string
restored = Object.from_json('{"name": "John", "scores": [95, 87, 92]}')
print(restored.name)  # -> "John"

# File I/O
obj.to_json_file("config.json")
config = Object.from_json_file("config.json", lock=True)

12. YAML Serialization

Serialize to/from YAML strings and files. Requires pyyaml (pip install rich-object[yaml]).

obj = Object(database={"host": "localhost", "port": 5432}, debug=True)

# To YAML string
print(obj.to_yaml())
# database:
#   host: localhost
#   port: 5432
# debug: true

# From YAML string
config = Object.from_yaml("database:\n  host: localhost\n  port: 5432")
print(config.database.host)  # -> "localhost"

# File I/O — one-liner config loading
config = Object.from_yaml_file("config.yaml", lock=True)

13. TOML Serialization

Serialize to/from TOML strings and files. Requires tomli-w for writing; uses built-in tomllib on Python 3.11+ for reading (pip install rich-object[toml]).

obj = Object(title="My App", database={"host": "localhost", "port": 5432})

# To TOML string
print(obj.to_toml())
# title = "My App"
#
# [database]
# host = "localhost"
# port = 5432

# From TOML string
config = Object.from_toml('[database]\nhost = "localhost"\nport = 5432')
print(config.database.host)  # -> "localhost"

# File I/O
config = Object.from_toml_file("pyproject.toml", lock=True)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rich_object-0.2.0.tar.gz (19.3 kB view details)

Uploaded Source

Built Distribution

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

rich_object-0.2.0-py3-none-any.whl (15.1 kB view details)

Uploaded Python 3

File details

Details for the file rich_object-0.2.0.tar.gz.

File metadata

  • Download URL: rich_object-0.2.0.tar.gz
  • Upload date:
  • Size: 19.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for rich_object-0.2.0.tar.gz
Algorithm Hash digest
SHA256 be9842da02f6de623ada943557a03fe03563e01d004b07d22bce2f1492b9bb91
MD5 24c615539966c9814e0e89a36001b15d
BLAKE2b-256 ee9d0b71c536e04e483afd90e445413a470f7806da5f65766c9e1f2b9046010f

See more details on using hashes here.

File details

Details for the file rich_object-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: rich_object-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 15.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for rich_object-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9b0b9de2dc477cdd56378e3cc1386b416a24d48ec710debaf3435a8e41c696ef
MD5 93b3d9aa9c0c25a90925fe3f9af95f99
BLAKE2b-256 a6607ca0894e669bbad49acd73f3230771b93ec36add414f8f73a49254a9838d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page