A flexible, schema-driven library for building dynamic, context-aware content blocks with variable substitution, nested composition, validation, and asynchronous rendering.
Project description
EBlock (Easy Block Builder)
A flexible, schema-driven library for building dynamic, context-aware content blocks with variable substitution, nested composition, validation, and asynchronous rendering.
Designed for use in page builders, CMS backends, or dynamic UI generation systems where content is defined as structured JSON, rendered with context variables, and validated via Pydantic models.
Installation
pip install eblock
or with uv:
uv add eblock
Core Concepts
Context
A container for variables and metadata used during block rendering.
- Variables are case-normalized to uppercase.
- Supports deep copying for safety.
- Allows dynamic assignment via
ctx["key"] = value. - Stores:
vars: dictionary of scalar or structured values (strings, numbers, lists, dicts, etc.)extra: arbitrary metadata (not used in substitution)
BaseBlock
Abstract base class for all content blocks.
Each block: - Accepts properties (dict) on initialization. -
Optionally validates input/output using Pydantic models. - Recursively
resolves {{ variable }} placeholders from Context. - Supports
nesting inside dicts, lists, tuples, and sets. - Exposes an async
build() method returning (result_dict, output_schema). - Allows
custom output transformation via prepare_output().
Block Registration Mechanism
EBlock uses an automatic class-based registration system powered by the
BlockMeta metaclass.
How It Works
Every subclass of BaseBlock that defines a _type attribute is
automatically registered on import.
class TextBlock(BaseBlock):
_type = "text"
When Python loads this class:
BlockMeta.__new__is triggered.- If
_typeis not"base"and not private (_SomethingBlock), the block is added to the global registry. - Duplicate
_typevalues produce a warning and are ignored.
Accessing Registered Blocks
You can list all registered block types:
from eblock import get_registered_block_types
print(get_registered_block_types())
# ["text", "score_card", ...]
Creating Blocks Dynamically
Use the factory function:
from eblock import create_block
block = create_block("text", {"content": "Hello"})
Building Blocks from JSON-like Config
The library supports nested block trees:
from eblock import create_blocks_from_config
blocks = create_blocks_from_config([
{"type": "text", "properties": {"content": "Hi"}},
{"type": "score_card", "properties": {"title": "Test", "score": 95}},
])
Nested blocks inside dicts/lists are resolved recursively.
Why Use Automatic Registration?
- No need for manual registries.
- Blocks register themselves as soon as they are imported.
- Decoupled plugins/modules can define their own block types.
- Ensures consistency when building from JSON configs.
Features
✅ Variable substitution
Placeholders like {{ user_name }} are replaced with values from
context.
✅ Recursive structure support
Handles dicts, lists, tuples, sets, and nested BaseBlock instances.
✅ Input/Output validation
Define _input_schema and _output_schema using Pydantic models.
✅ Computed fields
Override prepare_output() to modify or add fields before output
validation.
✅ Static dependency analysis
Use .get_vars() to extract all required variables, including in nested
blocks.
✅ Async-first design
Full async/await support throughout the rendering pipeline.
✅ Structured logging
Blocks and context instances log operations through hierarchical
loggers.
Usage Examples
Basic Block
from eblock import BaseBlock, Context
class TextBlock(BaseBlock):
_type = "text"
ctx = Context(vars={"name": "Alice"})
block = TextBlock({"content": "Hello, {{ name }}!"})
result, _ = await block.build(ctx)
# result == {"content": "Hello, Alice!"}
Validated Block with Computed Output
from pydantic import BaseModel
from eblock import BaseBlock
class Input(BaseModel):
title: str
score: int
class Output(BaseModel):
title: str
score: int
badge: str
class ScoreCard(BaseBlock):
_type = "score_card"
_input_schema = Input
_output_schema = Output
async def prepare_output(self, result, ctx):
result["badge"] = "🏆" if result["score"] >= 90 else "📝"
return result
Nested Blocks
header = TextBlock({"text": "Welcome, {{ user }}!"})
page = PageBlock({"header": header, "theme": "{{ theme }}"})
# Both variables will be resolved from context
Variable Inspection
block = TextBlock({"msg": "Hi {{ first }}, {{ last }}!"})
print(block.get_vars()) # {"FIRST", "LAST"}
API Reference
class Context
__init__(self, vars: dict | None = None, **extra)__getitem__(key)__setitem__(key, value)- Properties:
vars--- deep copy of stored variables\extra--- shallow copy of extra data
class BaseBlock
__init__(self, properties: dict)async build(self, ctx: Context) -> tuple[dict, Type[BaseModel] | None]async prepare_output(self, result: dict, ctx: Context) -> dictget_vars(self) -> set[str]- Property:
type--- block identifier
Define in subclasses
_type: str--- identifier (required)_input_schema: Type[BaseModel] | None_output_schema: Type[BaseModel] | None
Registry Helpers
create_block(type, properties)create_blocks_from_config(config)get_registered_block_types()
Requirements
- Python ≥ 3.10
- Pydantic ≥ 2.0
Notes
- Placeholders must use double curly braces:
{{ var_name }}. - Whitespace is trimmed:
{{ user }}→user. - Missing variables resolve to empty string and produce a warning.
- Blocks should be treated as stateless and recreated when reused.
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 eblock-0.3.0.tar.gz.
File metadata
- Download URL: eblock-0.3.0.tar.gz
- Upload date:
- Size: 20.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
206f83b6b02cc0938527ef693fedb69fb44fdc6d031d2117b49a872f9f33d889
|
|
| MD5 |
5a53fe71352aafd1563ff65961662a29
|
|
| BLAKE2b-256 |
1f62c7b4ca5a7217ae70e809d493a5f4262f1ee4b610b6b25f26fc9b77210288
|
Provenance
The following attestation bundles were made for eblock-0.3.0.tar.gz:
Publisher:
python-publish.yml on GAKiknadze/easy_block_builder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
eblock-0.3.0.tar.gz -
Subject digest:
206f83b6b02cc0938527ef693fedb69fb44fdc6d031d2117b49a872f9f33d889 - Sigstore transparency entry: 748081122
- Sigstore integration time:
-
Permalink:
GAKiknadze/easy_block_builder@59600b71f6afae00d0bd145ce5e0497f18f6bf58 -
Branch / Tag:
refs/tags/0.3.0 - Owner: https://github.com/GAKiknadze
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@59600b71f6afae00d0bd145ce5e0497f18f6bf58 -
Trigger Event:
release
-
Statement type:
File details
Details for the file eblock-0.3.0-py3-none-any.whl.
File metadata
- Download URL: eblock-0.3.0-py3-none-any.whl
- Upload date:
- Size: 13.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
acd48586b56dc6ba79b79a218867b46bdd9a37aad8b4e78f58a22582972d788a
|
|
| MD5 |
e66458bd43421f6e9c8f1233fe593ecf
|
|
| BLAKE2b-256 |
ffed80ace570de9d5928e7a0992a97ceb137b6d679a722486b233c492df79973
|
Provenance
The following attestation bundles were made for eblock-0.3.0-py3-none-any.whl:
Publisher:
python-publish.yml on GAKiknadze/easy_block_builder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
eblock-0.3.0-py3-none-any.whl -
Subject digest:
acd48586b56dc6ba79b79a218867b46bdd9a37aad8b4e78f58a22582972d788a - Sigstore transparency entry: 748081123
- Sigstore integration time:
-
Permalink:
GAKiknadze/easy_block_builder@59600b71f6afae00d0bd145ce5e0497f18f6bf58 -
Branch / Tag:
refs/tags/0.3.0 - Owner: https://github.com/GAKiknadze
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@59600b71f6afae00d0bd145ce5e0497f18f6bf58 -
Trigger Event:
release
-
Statement type: