Marshmallow-like schematization of a directory structure
Project description
fschema
Marshmallow-like schematization of a directory structure
Installation
Simply install it from PyPI
pip install fschema
Quickstart Example
Let's say you have the following directory/file structure:
config/
+ plugins/
| + java/
| | + plugin.yaml
| + python/
| | + plugin.yaml
+ profiles/
| + new.yaml
| + old.yaml
+ env
+ config.yaml
You can describe it as a Python model and load everything into a single structure.
Schemas describe the structure, fields describe how each node is loaded, and filesystem access goes
through an FSInterface. FSLoader uses LocalFSInterface by default, but custom filesystem-like
backends can be provided with FSLoader(schema=..., fs=...).
from fschema.fields import meta, node
from fschema.schema import Schema
from fschema.fs_loader import FSLoader
class PluginConfigSchema(Schema):
name = meta.NodeName()
config = node.File(fs_name="plugin.yaml")
class ProfileConfigSchema(Schema):
name = meta.NodeName()
config = meta.Content()
class ServiceConfigSchema(Schema):
config = node.File(fs_name="config.yaml")
env = node.File(fs_name="env")
plugins = node.ListDirectory(node.SchematizedDirectory(PluginConfigSchema()))
profiles = node.ListDirectory(node.SchematizedFile(ProfileConfigSchema()))
data = FSLoader(schema=ServiceConfigSchema()).load("/path/to/config")
print(data)
This will load the following data:
{
"config": "<file-content>",
"env": "<file-content>",
"plugins": [
{"name": "java", "config": "<file-content>"},
{"name": "python", "config": "<file-content>"}
],
"profiles": [
{"name": "new.yaml", "config": "<file-content>"},
{"name": "old.yaml", "config": "<file-content>"}
]
}
If you want to add post-processing of the data to your schema
(e.g. validate it or convert it to an object), you can define a __fschema_post_load__ method:
class ServiceConfigSchema(Schema):
...
def __fschema_post_load__(self, data: dict) -> ServiceConfiguration:
return ServiceConfiguration(**data)
Reference
Fields
Meta Fields
Meta fields are the fields that use the metadata of the respective filesystem node (directory/file)
and provide access to its various properties.
All meta fields inherit from MetaField.
Meta field types:
NodeName()- special type of field that loads the name of the current node (directory or file)Content(reader: Reader, data_transformer: DataTransformer)- for use inside a sub-schema of aSchematizedFile;readerparses content provided byFSLoaderto JSON-like data;data_transformerloads it into an object and/or validates the data
Node Fields
Node fields correspond to actual filesystem nodes (directories/fields).
All node fields inherit from NodeField.
All node fields have the optional argument fs_name - this is the name of the filesystem node
the field corresponds to - useful if the filename has a period (.) in it,
and, therefore cannot be used as the field's attribute name.
Exposed properties:
effective_fs_name- the resolved filesystem name: the explicitfs_namewhen provided, otherwise the schema attribute name.
Node field types:
SchematizedDirectory(directory_schema: Schema, fs_name: str | None)- load directory as a key-value mapping and apply the given sub-schema to the directory itself; this means nested files and directories must have fixed namesDictDirectory(nested_field: Field, fs_name: str | None)- load directory as a free mapping, without fixed key values; the given field instance is applied to all nested nodesListDirectory(nested_field: Field, fs_name: str | None)- load directory as a list of nodes; the given field instance is applied to all nested nodesFile(fs_name: str | None, reader: Reader, data_transformer: DataTransformer)- load file content;readerparses content provided byFSLoaderto JSON-like data;data_transformerloads it into an object and/or validates the dataSchematizedFile(file_schema: Schema, fs_name: str | None)- load the file as a schematized mapping instead of a single flat object; this is useful if you need access to its metadata (e.g. viaNodeName);
Content Readers
Available content readers:
JSONReader- parses content as JSON (as adict)YamlReader- parses content as YAML (as adict)TextReader- returns content as text (str); this is the default reader
Filesystem Interface
Filesystem access is abstracted behind FSInterface, available from fschema.fs.
The default LocalFSInterface, also available from fschema.fs, supports local paths via pathlib.
Custom backends can implement:
node_name(path: Path) -> strchild_path(path: Path, fs_name: str) -> Pathlist_directory(path: Path) -> list[Path]require_file(path: Path) -> Nonerequire_directory(path: Path) -> Noneread_file(path: Path, encoding="utf-8") -> str
Data Transformers
Available data transformers:
MarshmallowLoader(schema: Any)- loads the file data via amarshmallowschema
Extending
If you need custom behavior, the main extension points are:
fschema.fields.meta.MetaField- base class for fields that read the current filesystem nodefschema.fields.node.NodeField- base class for fields that resolve/load filesystem child nodesfschema.fs.FSInterface- base class for filesystem-like backendsfschema.readers.Reader- base class for content readersfschema.data.DataTransformer- base class for data transformers
See the corresponding source modules for the expected method shapes and examples.
Contribute
Feel free to contribute. I can't guarantee I'll review PRs fast, but I'll do my best.
Setup
For local development, create a virtual environment and install the development extras:
python -m venv .venv
.venv/bin/python -m pip install -e ".[dev]"
If your tools live somewhere else, copy .make.env.example to .make.env and adjust the paths:
cp .make.env.example .make.env
The local .make.env file is ignored by git and can contain machine-specific values:
PYTHON = venv-fschema-3.14/bin/python
RUFF = venv-fschema-3.14/bin/ruff
Checking
Before opening a pull request, run:
make test
make test first runs make format-check, then runs the unit tests.
To fix lint and formatting issues automatically, run:
make format
You can also run the validation step directly:
make format-check
Project details
Release history Release notifications | RSS feed
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 fschema-0.1.2.tar.gz.
File metadata
- Download URL: fschema-0.1.2.tar.gz
- Upload date:
- Size: 13.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f67f04ec47ac707d6216e968f519cdaa82d03431316c3d82468d992e437151c
|
|
| MD5 |
4f0ab246082afc0763e48f5541bffdff
|
|
| BLAKE2b-256 |
d92009a2bbd9430978250215a06845c4050ce1e9773af677e931c4d37910d7ef
|
File details
Details for the file fschema-0.1.2-py3-none-any.whl.
File metadata
- Download URL: fschema-0.1.2-py3-none-any.whl
- Upload date:
- Size: 13.8 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 |
8a542d41364cc5f8dbf7dc044fff9f8463f1a76cdb3a55550371808a37ad4ccd
|
|
| MD5 |
91b8ee80efe6785ce7eca3547654eb4d
|
|
| BLAKE2b-256 |
6c71b134d74f2222442059795955478c60a4b16383e60463e7c22da2d232f88a
|