Skip to main content

mgnolia

Declarative filesystem structure and content validation for Python.

Define the expected layout of a directory using Dir and File nodes, optionally attach content validation rules, then call validate_all() to check a real directory against your schema. All errors are collected and returned as typed objects—nothing raises.

Installation

pip:

pip install mgnolia

uv:

uv add mgnolia

Quick Start

from mgnolia import Dir, File, Schema

# Define expected structure
schema = Schema(
    children=[
        Dir(
            path="data",
            description="Data directory",
            children=[
                File(path="input.csv"),
                File(path="output.json"),
            ],
        ),
        File(path="config.yaml"),
    ]
)

# Validate a directory
ok = schema.validate_all("/path/to/project")

if not ok:
    for error in schema.errors:
        print(error)
else:
    print("All checks passed!")

Content Rules

Attach validation rules to files to check their contents:

from mgnolia import File
from mgnolia.content import FileNotEmptyRule, CSVSchemaRule
from pydantic import BaseModel

class UserRow(BaseModel):
    id: int
    name: str
    email: str

File(
    path="users.csv",
    content_rules=[
        FileNotEmptyRule(),
        CSVSchemaRule(schema=UserRow),
    ],
)

Built-in rules:

  • FileNotEmptyRule() — fails if file is empty
  • CSVSchemaRule(schema=PydanticModel) — validates CSV against a Pydantic model using pandera

Rules can also publish a value for a later node to use:

from mgnolia import File, Schema, Value
from mgnolia.content import RowCountRule

user_count = Value[int]("users.row_count")
avatar_count = Value[int]("avatars.match_count")

schema = Schema(children=[
    File(path="users.parquet", content_rules=[
        RowCountRule(min=1, max=10_000, output=user_count),
    ]),
    File(
        path="user_avatars/*.png",
        min_matches=0,
        max_matches=user_count.ref(),
        output=avatar_count,
    ),
])

Custom rules: Custom rules are subclasses of ContentRule (and optionally raising subclasses of ContentValidationError), which implemennt a validate method on the node (Dir or more typically File). They may also publish custom Values for use in later rules.

As a realistic example, to validate that a directory contains TSV files named by a known format like an Accession, e.g. ERR123456.tsv, ERR987654.tsv etc, AND that each file contains the same accession it is named by (so like grep -q 'ERR123456' ERR123456.tsv) we could define the following custom rules and values:

import re
from pathlib import Path

from mgnolia import Dir, File, Ref, Schema, Value
from mgnolia.content import ContentRule
from mgnolia.errors import ContentValidationError
from mgnolia.schema import Node


class FilenameRule(ContentRule):
    def __init__(self, output: Value[str]) -> None:
        super().__init__(output)
        self.accession = output

    def validate(self, node: Node, path: Path) -> list[ContentValidationError]:
        self.accession.set(path.stem)
        if re.fullmatch(r"ERR\d{6}\.tsv", path.name):
            return []
        return [ContentValidationError(node, path)]


class ContainsTextRule(ContentRule):
    def __init__(self, expected: Ref[str]) -> None:
        super().__init__()
        self.expected = expected

    def validate(self, node: Node, path: Path) -> list[ContentValidationError]:
        expected = self.expected.resolve()
        if expected in path.read_text(encoding="utf-8"):
            return []
        return [ContentValidationError(node, path)]


accession = Value[str]("current_accession")

schema = Schema(children=[
    Dir(path="runs", children=[
        File(
            path="*.tsv",
            max_matches=None,
            content_rules=[
                FilenameRule(output=accession),
                ContainsTextRule(accession.ref()),
            ],
        ),
    ]),
])

This example works because content rules run in list order for each matched file. Thus, for ERR123456.tsv, FilenameRule publishes ERR123456 and ContainsTextRule consumes it while validating that same file.

References between separate nodes resolve in declaration order, so the top-level subtree producing a value must appear before the subtree consuming it. Compatible Ref values can be used for any parameter of the built-in content rules, as well as min_matches and max_matches.

Observations can be published with output= even when their validation fails (so that the validation report is as "complete" as possible):

  • File and Dir output their actual number of matches.
  • FileNotEmptyRule outputs the file size in bytes.
  • CSVSchemaRule and ParquetSchemaRule output the actual Polars schema.
  • RowCountRule outputs the actual row count.
  • SortedRule outputs the column's (min, max) values.

Error Types

When validation fails, schema.errors contains typed error objects:

  • Structure errors: FileMissingError, DirectoryMissingError, MinMatchError, MaxMatchError
  • Content errors: FileEmptyError, ContentSchemaError

All inherit from ValidationError and have .path and .node attributes. Use str(error) to get a formatted message.

Pattern Matching

Dir and File paths support glob patterns to match multiple files or directories:

File(path="*.log", min_matches=1)  # At least one .log file
Dir(path="test_*", max_matches=5)  # At most 5 test_* directories

License

MIT

Metadata

Release files for mgnolia 0.4.1

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

Source distribution (sdist)

Source distribution for mgnolia 0.4.1
File Size Uploaded
mgnolia-0.4.1.tar.gz 8.8 kB Details

Built distribution (wheel)

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

Total release size: 19.5 kB

Release files / mgnolia-0.4.1.tar.gz

Download URL mgnolia-0.4.1.tar.gz
Size 8.8 kB
Tags Source
SHA-256 checksum
How to use checksums
8583bb9d10799647b8a38b9e67024c8336c2d63ae5981025e782422777b5750a
BLAKE2b-256 checksum
How to use checksums
2c910b0c49f25aaf61cdd3c2cb48041fd844e4255ad8d83e773dbe8c1092cb76
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.

Transparency log

Release files / mgnolia-0.4.1-py3-none-any.whl

Download URL mgnolia-0.4.1-py3-none-any.whl
Size 10.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1ecc552c4fa75a9dab09498b6719ffef2340ef19aef3daef757a50133931bcc5
BLAKE2b-256 checksum
How to use checksums
5ade8ea6c53f2d4dd7400163f116e3a7a9542ce6bc8e8f4f37e88681e0340b72
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

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