libspec
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.
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 libspec-10.5.3.tar.gz.
File metadata
- Download URL: libspec-10.5.3.tar.gz
- Upload date:
- Size: 78.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2a5d049652714cf2c70e27e52d823a9c267f0289a069e771e6ca077c45872e6
|
|
| MD5 |
bad1fd8a4e62672bc500d507018f040f
|
|
| BLAKE2b-256 |
d0f13374fa53635ddf13ff9dd154dbf36e74fef26e56b78583820cfe68874715
|
File details
Details for the file libspec-10.5.3-py3-none-any.whl.
File metadata
- Download URL: libspec-10.5.3-py3-none-any.whl
- Upload date:
- Size: 66.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a22a8d2bc043dc0a3cf19d18746c01adc3943193fdbb188c98f2605a837455ea
|
|
| MD5 |
288d5307633f3e36f6a56ac4e2c7d2ad
|
|
| BLAKE2b-256 |
72653d8c87851690b51b57431489a0b91420780b4fed3d01f01aba07bb5045ec
|