A lightweight, immutable struct library with type enforcement.
Project description
# pystructlight
**A lightweight, memory‑efficient, and type‑safe immutable data structure for Python.**
`pystructlight` lets you define schemas for your data objects and ensures that every instance adheres to those types at runtime – all while keeping a tiny memory footprint thanks to `__slots__`.
## Features
- **Immutable** – instances cannot be modified after creation
- **Runtime type enforcement** – validates that all field values match your schema
- **Memory efficient** – uses `__slots__` to avoid per‑instance `__dict__`
- **Flexible field definitions** – support for default values, custom validators, and type coercion
- **Rich type support** – `Union`, `Optional`, `Literal`, `Any`, generic containers (`list[T]`, `dict[K, V]`, etc.)
- **Convenient methods** – `evolve()`, `matches()`, `to_dict()`, `from_dict()`, `is_instance()`
- **Positional or keyword instantiation** – choose the style you prefer
## Installation
```bash
pip install pystructlight
Quick Start
1. Define a Struct
Pass a name and a dictionary mapping field names to their expected types (or to a FieldDef for advanced options).
from pystructlight import Struct, field
# Simple type‑only schema
Book = Struct("Book", {
"title": str,
"author": str,
"pages": int,
"is_hardcover": bool,
})
# Schema with defaults, validator, and coercion
Person = Struct("Person", {
"name": str,
"age": field(int, default=0, validator=lambda x: x >= 0),
"email": str,
"tags": field(list[str], default=list),
})
2. Create Instances
Use keyword arguments…
book1 = Book.new(
title="The Great Gatsby",
author="F. Scott Fitzgerald",
pages=180,
is_hardcover=False
)
…or positional arguments (in the order the fields were defined):
book2 = Book.new("1984", "George Orwell", 328, True)
3. Access and Use
print(book1.title) # "The Great Gatsby"
print(book1.pages) # 180
# Immutability
try:
book1.pages = 200
except AttributeError:
print("Cannot modify immutable struct!")
# Runtime type checking
try:
Book.new("The Hobbit", "J.R.R. Tolkien", "many", True)
except TypeError as e:
print(e) # Field 'pages': expected int, got str
# Convert to dict
data = book1.to_dict()
# {'title': 'The Great Gatsby', 'author': 'F. Scott Fitzgerald', 'pages': 180, 'is_hardcover': False}
# Create from dict
book3 = Book.from_dict({"title": "Moby Dick", "author": "Herman Melville", "pages": 585, "is_hardcover": False})
Advanced Field Definitions
Use the field() helper to add defaults, validators, or type coercion.
| Parameter | Description |
|---|---|
type |
Expected type (e.g. str, int, list[str], Union[int, str], Any) |
default |
Default value. Can be a constant or a callable (e.g. list, dict). |
validator |
Callable that receives the value and returns True, a string error, or False. |
coerce |
If True, attempts to convert the input to the expected type (e.g. int("42")). |
from pystructlight import Struct, field
Config = Struct("Config", {
"timeout": field(float, default=5.0, coerce=True),
"retries": field(int, default=3, validator=lambda x: 0 <= x <= 10),
"tags": field(list[str], default=list),
})
c = Config.new(timeout="2.5", retries=5, tags=["prod", "api"])
print(c.timeout) # 2.5 (coerced from string)
print(c.retries) # 5
print(c.tags) # ['prod', 'api']
Methods on Struct Instances
Every instance provides the following methods:
| Method | Description |
|---|---|
to_dict() |
Returns a dict mapping field names to values. |
evolve(**changes) |
Returns a new instance with updated fields (original unchanged). |
matches(**kwargs) |
Returns True if all given fields equal the specified values. |
__iter__() |
Iterates over field values (in definition order). |
__len__() |
Number of fields. |
__repr__() |
Human‑readable representation. |
book = Book.new(title="Brave New World", author="Aldous Huxley", pages=268, is_hardcover=False)
# Evolve
new_book = book.evolve(pages=300, is_hardcover=True)
print(new_book.pages) # 300
print(book.pages) # 268 (unchanged)
# Matching
if book.matches(title="Brave New World"):
print("It's the same book!")
# Iteration
for value in book:
print(value)
Methods on the Struct Type
The object returned by Struct() (a StructType) provides:
| Method | Description |
|---|---|
new(*args, **kwargs) |
Creates a new instance. |
from_dict(data: dict) |
Creates an instance from a dictionary (calls new(**data)). |
is_instance(obj) |
Returns True if obj is an instance of this exact struct type. |
Book.is_instance(book) # True
Book.is_instance("foo") # False
Runtime Type Checking
pystructlight validates values against your schema when you create the instance.
Supported types include:
- Built‑in types:
int,float,str,bool,bytes,None - Type unions:
Union[int, str],int | str(Python 3.10+) Optional[T](same asUnion[T, None])Literal["red", "green"]Any(no checking)- Generic containers:
list[T],tuple[T],set[T],frozenset[T],dict[K, V] - Any user‑defined class
Example:
from typing import Union, Literal, Any
Data = Struct("Data", {
"id": Union[int, str],
"status": Literal["active", "inactive"],
"payload": Any,
})
d = Data.new(id=42, status="active", payload={"anything": 123}) # OK
d2 = Data.new(id="abc", status="inactive", payload=None) # OK
# This fails:
# Data.new(id=3.14, status="active", payload=None) # id must be int or str
Memory Efficiency
Instances do not have a __dict__. All fields are stored in a compact tuple, and access is routed through __slots__. The overhead is roughly the same as a namedtuple, but with full runtime type checking.
import sys
Book = Struct("Book", {"title": str, "pages": int})
b = Book.new("The Pearl", 96)
print(sys.getsizeof(b)) # typically 56 bytes (plus the tuple, but overall very small)
API Reference
Struct(name: str, schema: dict) -> StructType
Creates a new struct type.
name– the name of the struct (used inreprand error messages)schema– dictionary where each key is a field name and each value is either a type or aFieldDef(created byfield()).
field(type, default=_MISSING, validator=None, coerce=False) -> FieldDef
Returns a field definition for use in a schema.
type– the expected type (e.g.str,list[int]).default– optional default value (if callable, it is called each time a default is needed).validator– optional callable(value) -> bool | str | None. ReturnFalseor a string to raise an error.coerce– ifTrue, tries to convert the input to the given type usingtyp(value).
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pystructlight-0.1.3.tar.gz.
File metadata
- Download URL: pystructlight-0.1.3.tar.gz
- Upload date:
- Size: 13.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e1f112e61c172314b9fc244c51a34a6436e75321727783d6537d261f5249f3d5
|
|
| MD5 |
d0a54329aedc241e1b342543f2619ced
|
|
| BLAKE2b-256 |
597558f1965b68413bd559790ccc0151d03d75e3da02bd248033365af29bdc36
|
File details
Details for the file pystructlight-0.1.3-py3-none-any.whl.
File metadata
- Download URL: pystructlight-0.1.3-py3-none-any.whl
- Upload date:
- Size: 11.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
470faad1d922f22d09d672276f10ec493e9d56251315dcd6d08d40b6b62003d3
|
|
| MD5 |
3f1010c706c2450c84a18992e369b913
|
|
| BLAKE2b-256 |
4d2de4d5c37e5abbf69bdb592fcb8a2291c4c251ebacdc51799b1066369eaf37
|