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.1.tar.gz (19.0 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.1-py3-none-any.whl (15.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: rich_object-0.2.1.tar.gz
  • Upload date:
  • Size: 19.0 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.1.tar.gz
Algorithm Hash digest
SHA256 bf91a887d6300957f895211c4457850513bee90127930c8e1362ff83ee00992a
MD5 d2ce3e0e2f0adc9475bb5eca4d10b3ef
BLAKE2b-256 cf641dd63f7e075ea45cf4353d2cc4e7724aedd1154c1d581743b4a8d194a66f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: rich_object-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 15.0 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f6f9835f03fa690facb71492c59953c6489bfbfa3ae645d7ced613161164fb59
MD5 1848b7b1379aa72e334b78f0576a6612
BLAKE2b-256 47fa8338b7a7c106a5ddc77d3aa7c10d81e2376427617aa2325bca54dfffac2b

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