Skip to main content

libspec

PyPI CI/CD Documentation License

an ounce of spec is worth a pound of tokens

libspec is a Specification Management System in Python. Features and requirements are declared as Python classes; their docstrings are compiled into content-addressed snapshots, diff'd over time, and served to LLM coding agents via MCP.

The deepest capability: generic specifications

The most powerful thing libspec enables is generic specifications — published base classes that encode how features should be documented and specified, not what any particular feature does.

from libspec import Feature
from libspec.diataxis import Diataxis        # pip install libspec-diataxis
from libspec.conventional_commits import Commit # pip install libspec-conventional-commits

# Inherit once at your project base class.
class MyFeature(Feature, Diataxis, Commit): pass

# Every downstream feature automatically carries both contracts —
# enforced at spec-generation time, not as a style guide to forget.
class AwesomeNavBar(MyFeature):
    def tutorial(self):    return "In this tutorial we will build..."
    def how_to(self):      return "To highlight the active route..."
    def reference(self):   return "AwesomeNavBar(items, active_index, ...)"
    def explanation(self): return "The nav bar uses a slot-based model because..."

Miss a quadrant and UnimplementedMethodError tells you exactly where. The contract propagates through inheritance automatically. See Generic Specifications for the full picture.


Example Spec

Here is the specification spec/err.py used to establish fundamental code quality, error handling, and robustness constraints across the entire project via multiple inheritance:

from libspec import Ctx, Feature, Requirement


# The Err docstrings are compiled into specification snapshots and 
# injected as prompt context for LLM code generation.
class Err(Ctx):
    """
    It is important that error handling be done excellently.

    If a function can fail, then it needs to do so in the most elegant way
    possible. Error reporting, handling, exceptions and all aspects of failure
    must be taken to extreme. It should be possible to understand the program
    by reading the error messages.

    When an error occurs there should be a story about the failure at each step
    of the way. What went wrong and why.
    """


class BoilerPlate(Ctx):
    """
    If you can see a way to reduce boiler plate, then do it.
    """


class FunctionLines(Ctx):
    """
    Try to keep functions under 20 lines.
    """


class Indentation(Ctx):
    """
    Try to keep indentation under 4 levels.
    """


class PreCondition(Ctx):
    """
    Functions should validate preconditions at their entry point.

    Instead of using `assert` statements (which can be disabled globally),
    raise explicit, descriptive exceptions (e.g., ValueError, TypeError, or
    custom domain exceptions) to robustly reject malformed input.
    """


class GlobalMutableState(Ctx):
    """
    Broadly you should avoid global mutable state.
    """


class PostCondition(Ctx):
    """
    Before a function returns, it should verify postconditions to ensure
    invariant properties hold true.

    Raise explicit, descriptive exceptions (such as RuntimeError or domain
    exceptions) rather than using `assert` statements to handle post-execution
    verification failures.
    """


# Composite specification aggregating precondition, postcondition, and global state avoidance guidelines.
class DefensiveProgramming(PreCondition, PostCondition, GlobalMutableState):
    pass


class Refactor(BoilerPlate, FunctionLines, Indentation):
    """
    Always keep an eye out for ways to generalize a function if it's utility
    might be helpful to other functions.

    Classes should be implemented in their own files with filename being the
    classname with correct naming convention
    """


class Robustness(DefensiveProgramming):
    """
    Always prioritize library-provided constructors for complex objects. Ensure
    all components are fully initialized before calling any state- mutating
    methods. Assume private internal state is uninitialized until the official
    constructor has returned. When extending library components, prioritize
    composition (pointers) over embedding by value to avoid risky state-copying
    bugs.

    Use dependency injection for system level objects for composability and to
    make testing easier.
    """


# Use multiple inheritance to endow Feature and Requirement specs with
# disciplined error handling guidance from above.
class Feat(Err, Refactor, Robustness, Feature):
    pass


class Req(Err, Refactor, Robustness, Requirement):
    pass

Ecosystem: libspec.* Extension Libraries

libspec is designed to be extended by independent, separately-published packages that contribute new modules to the libspec.* namespace. A single line in libspec/__init__.py makes this possible:

import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)

This causes Python to search every libspec/ directory on sys.path when resolving libspec.* imports, so any installed sibling library is discovered automatically — no coordination with the libspec maintainers required.

Example: mixing extensions

from libspec import Feature
from libspec.diataxis import Diataxis        # pip install libspec-diataxis
from libspec.conventional_commits import Commit # pip install libspec-conventional-commits

class MyBaseFeature(Feature, Diataxis, Commit): pass

class AwesomeNavBar(MyBaseFeature):
    ...

Writing your own extension

A sibling library needs only a single source file — no hooks, no .pth tricks:

libspec-myextension/
└── src/
    └── libspec/
        └── myextension.py   ← your module, no __init__.py needed
# pyproject.toml
[tool.hatch.build.targets.wheel]
packages = ["src/libspec"]

See the full guide and explanation in the docs.


The Object Model

Each class declares a specification fragment that is optionally a Jinja2 template string. More about that later...

Inheritance

Inheritance means "does this and more." The inherited superclass docstrings are normative, but compiled spec snapshots preserve them as references instead of prepending their prose into the child docstring. Renderers such as libspec diff can expand those refs when a review needs the inherited context.

Mixins

Mixins help get around the diamond problem. (TODO: write more about this)

Versioning

Note that the versioning of libspec is still being hammered out. Currently, the version of libspec appears in generated spec snapshots (libspec-version field). But, how diffs will be performed on different versions is unexplored.

Feature Branch & Multi-Commit Spec Diffing

When working on feature branches with intermediate commits, running uv run libspec diff right after a commit will report No changes detected because HEAD matches the live spec on disk.

To track the cumulative specification delta across all intermediate commits on a feature branch, pass the base branch target:

uv run libspec diff main

This tracks all specification additions, requirement modifications, and docstring changes relative to main throughout multi-stage development workflows.

Metadata

Release files for libspec 11.7.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 libspec 11.7.1
File Size Uploaded
libspec-11.7.1.tar.gz 106.5 kB Details

Built distribution (wheel)

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

Total release size: 193.4 kB

Release files / libspec-11.7.1.tar.gz

Download URL libspec-11.7.1.tar.gz
Size 106.5 kB
Tags Source
SHA-256 checksum
How to use checksums
026e401560d8c26eb447e4a3029188f560de31f60a771f90b4e8ba1e81f86b9f
BLAKE2b-256 checksum
How to use checksums
cf29d44d492d69c961a9ea91c76c19ee9a2cd438a1bce9b8f8d2a79e77a1c521
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 11, 2026.

Transparency log

Release files / libspec-11.7.1-py3-none-any.whl

Download URL libspec-11.7.1-py3-none-any.whl
Size 86.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1d8c759d52d9629243b0fe0bcf4a5e5c2238ee950a1389e3e0cc96682566e8da
BLAKE2b-256 checksum
How to use checksums
f57e0f840bd39ab744952407c45cebc64eba0a7f1c12295e44b30fad02858a8c
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 11, 2026.

Transparency log

Release history Release notifications | RSS feed

11.7.2

2 release files

This release

11.7.1 This release

2 release files

11.2.0

2 release files

10.6.0

2 release files

10.5.7

2 release files

10.5.3

2 release files

10.5.2

2 release files

10.5.1

2 release files

10.3.3

2 release files

10.3.1

2 release files

10.2.0

2 release files

1.2.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