Skip to main content
# 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 as Union[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 in repr and error messages)
  • schema – dictionary where each key is a field name and each value is either a type or a FieldDef (created by field()).

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. Return False or a string to raise an error.
  • coerce – if True, tries to convert the input to the given type using typ(value).

Metadata

Release files for pystructlight 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pystructlight 0.1.3
File Size Uploaded Provenance
pystructlight-0.1.3.tar.gz 13.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pystructlight 0.1.3
File Interpreter ABI Platform
pystructlight-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 25.1 kB

Release files / pystructlight-0.1.3.tar.gz

Download URL pystructlight-0.1.3.tar.gz
Size 13.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e1f112e61c172314b9fc244c51a34a6436e75321727783d6537d261f5249f3d5
BLAKE2b-256 checksum
How to use checksums
597558f1965b68413bd559790ccc0151d03d75e3da02bd248033365af29bdc36
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Release files / pystructlight-0.1.3-py3-none-any.whl

Download URL pystructlight-0.1.3-py3-none-any.whl
Size 11.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
470faad1d922f22d09d672276f10ec493e9d56251315dcd6d08d40b6b62003d3
BLAKE2b-256 checksum
How to use checksums
4d2de4d5c37e5abbf69bdb592fcb8a2291c4c251ebacdc51799b1066369eaf37
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Security for pystructlight 0.1.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page