Data is a highly flexible Python package that provides a superior alternative to basic dictionaries and traditional dataclasses. It combines the dynamic nature of a dictionary with the rigid, type-safe structure of an object, offering features like runtime validation, immutability, and transactional updates.
- Hybrid Access: Access data fields using both attribute syntax (
d.key) and dictionary syntax (d['key']). - Runtime Type Checking: Enforces type hints upon initialization and modification, including support for complex generics like
List[int],Dict[str, float], andOptional[str]. - Immutability: Easily create "frozen" (immutable) data objects.
- Transactional Updates (Context Manager): Use the
withstatement to temporarily unfreeze a locked object and safely commit or rollback changes. - Factory Decorator: Use
@data_factoryto easily convert standard classes into feature-richDataobjects.
Since this package is not yet on PyPI, you must install it directly from the GitHub repository using pip.
pip install git+https://github.com/NeoZett-School/Data-System.git1. The Base Data Class (Dynamic Dictionary)
The Data class is perfect for dynamic data that needs type validation.
from Data import Data
# Data accepts arbitrary keywords like a dictionary, but can enforce behavior
config = Data(host="localhost", port=8080, frozen=False) # include_methods have been depricated for performance
# Hybrid Access
print(config.host) # Output: localhost
print(config['port']) # Output: 8080
# Type-checked Mutability
try:
# If the Data object was initialized without a type hint for 'port',
# the runtime type check defaults to the type of the initial value (int).
config.timeout = 5.5 # This works if no specific type was specified for 'timeout'
except TypeError as e:
print(f"Error: {e}")s'2. The data_factory (Dataclass-like Structure)
Use the @data_factory decorator to define structures with explicit type annotations and class-level defaults.
from Data import data_factory, Data
from typing import List
@data_factory(frozen=True)
class UserData:
# Include methods defaults to false. Now you can also run methods you make.
id: int
name: str = "Anonymous"
roles: List[str] = ["guest"]
class Inherited(UserData, frozen=False):
# When inheriting, you must respecify wheter the object is frozen.
id = 100
# Create a new instance
user = UserData(id=101, name="Alice")
print(user)
# Output: UserData(id=101, name='Alice', roles=['guest'])
# Attempting mutation will fail because frozen=True was set on the factory
try:
user.name = "Bob"
except AttributeError:
print("Cannot modify user: object is frozen.")3. Transactional Context Manager (Rollback/Commit)
The context manager enables safe, temporary mutations on a frozen object, with an automatic rollback on exception.
# Create an immutable configuration object
config = Data(host="db.prod", retries=3, frozen=True)
print(f"Before: {config.retries}") # Output: Before: 3
# Scenario 1: Successful changes (Commit)
with config as temp_config:
temp_config.retries = 5
temp_config.timeout = 15 # New field added
print(f"After Commit: {config.retries}") # Output: After Commit: 5
print(f"New Field: {config.timeout}") # Output: New Field: 15
# Scenario 2: Failing changes (Rollback)
try:
with config as temp_config:
temp_config.retries = 99 # Tentative change
raise RuntimeError("Network failed!") # Exception causes rollback
except RuntimeError:
print("Rollback executed due to error.")
print(f"After Rollback: {config.retries}") # Output: After Rollback: 5 (Restored)4. Fields for dynamic values
The use of field() and computed_field() allows better control over dynamic variables. If you want to make a property, then you should use the computed_field() for a value codependent on other features.
@data_factory
class Date:
year: int = 2025
month: int = 10
day: int = field(required=True)
year_month_day = computed_field(lambda self: f"{self.year} - {self.month} - {self.day}")
#@computed_field
#def year_month(self): ...
# Works. But this will show as a function.
today = Date(day=31) # Day is required, or you'll get an error.
print(pretty_repr(today)) # You can import "pretty_repr" from the package.
# prints: (This has been fixed in "Small patches 7")
# year: int = 2025
# month: int = 10
# day: int = 31
# year_month_day: str = '2025 - 10 - 31'
print(today.year_month_day) # -> 2025 - 10 - 31The package provides utility functions for introspection:
from Data import (
FrozenData, # This class will contain frozen data. It doesn't matter what you do (you can't use the `with` keyword), it is fully immutable.
is_data_factory,
make_data_factory,
validate_data, # For inconsistencies between annotations and actual values
inspect_data, # Alike pretty_repr, but more detailed. It also works for data factories directly.
patch_data, # Patch data
diff_data, # Get what differentiate between two data objects
sync_data, # Sync two different data objects
to_schema, # Generate a schema with annotations. Each key will be given a value that is its type.
diff_schema, # Get what differentiate between two different schemas.
clone, # Clone the data (like `Data.copy()`) with new content.
pretty_repr # Quickly and efficiently understand what the object contains.
)
@data_factory
class MyModel(Data, frozen=True):
pass
class IllegalModel(Data, frozen=True):
# This ONLY works if you decorate the class as a data_factory.
# Otherwise, this will not be loaded correctly.
# What would and wouldn't work:
# - Use the class like the Data class, it will now always be frozen.
# - Anything defined inside the class will not be presented.
# - One exception: If `include_methods` is True, then all methods defined will still be available.
pass
# Check if a class was created using the factory
print(is_data_factory(MyModel)) # Output: True
# Instantiation using the helper function
instance = make_data_factory(MyModel, field='value')
# For any other tool, please checkout the docstrings and the imports.This package is built entirely on standard Python features for maximum compatibility and performance. It uses advanced techniques (metaclasses, dunder methods, __slots__) to achieve its goals.
We welcome issue reports and contributions!
Copyright (c) 2025-2026 Neo Zetterberg