sybil
$
npx mdskill add anam-org/metaxy/sybilTests code examples in documentation using Sybil with pytest integration
- Validates embedded code examples in docstrings and documentation files
- Uses pytest integration, parsers, and skip directives for test execution
- Parses and executes code blocks based on file patterns and parser rules
- Reports test results as part of standard pytest test runs
SKILL.md
.github/skills/sybilView on GitHub ↗
---
name: sybil
description: Use Sybil for testing code examples in documentation and docstrings. Covers pytest integration, parsers, skip directives, and namespace management.
---
# Sybil Documentation Testing
Sybil validates code examples embedded in documentation and docstrings by parsing and executing them as part of normal test runs.
**Official Documentation**: https://sybil.readthedocs.io/en/latest/
## Installation
```bash
pip install sybil[pytest]
```
## Pytest Integration
Configure in `conftest.py`. See [pytest integration docs](https://sybil.readthedocs.io/en/latest/integration.html#pytest-integration).
```python
from sybil import Sybil
from sybil.parsers.markdown.codeblock import PythonCodeBlockParser
from sybil.parsers.markdown.skip import SkipParser
pytest_collect_file = Sybil(
parsers=[
SkipParser(),
PythonCodeBlockParser(),
],
patterns=["*.md", "**/*.py"],
).pytest()
```
### Sybil Parameters
See [API reference](https://sybil.readthedocs.io/en/latest/api.html#sybil.Sybil).
| Parameter | Description |
| ---------------- | --------------------------------------------------------------- |
| `parsers` | Sequence of parser callables |
| `patterns` | File glob patterns to include (e.g., `["*.md", "src/**/*.py"]`) |
| `excludes` | File glob patterns to exclude |
| `setup` | Callable receiving namespace dict, called before each document |
| `teardown` | Callable receiving namespace dict, called after each document |
| `fixtures` | List of pytest fixture names to inject into namespace |
| `document_types` | Map file extensions to Document classes |
### Document Types
See [API reference](https://sybil.readthedocs.io/en/latest/api.html#documents).
- **Default**: Parse entire file
- `PythonDocument`: Import `.py` file as module, names available in namespace
- `PythonDocStringDocument`: Parse only docstrings from `.py` files
```python
from sybil.document import PythonDocStringDocument
pytest_collect_file = Sybil(
parsers=[...],
patterns=["src/**/*.py"],
document_types={".py": PythonDocStringDocument},
).pytest()
```
### Fixtures
```python
import pytest
from sybil import Sybil
@pytest.fixture
def my_fixture():
return {"key": "value"}
pytest_collect_file = Sybil(
parsers=[...],
patterns=["*.md"],
fixtures=["my_fixture"], # Available in document namespace
).pytest()
```
### Setup/Teardown
```python
def sybil_setup(namespace):
namespace["helper"] = lambda x: x * 2
def sybil_teardown(namespace):
pass # Cleanup if needed
pytest_collect_file = Sybil(
parsers=[...],
setup=sybil_setup,
teardown=sybil_teardown,
).pytest()
```
## Disable pytest's Built-in Doctest
Add to `pyproject.toml` to prevent conflicts:
```toml
[tool.pytest.ini_options]
addopts = "-p no:doctest"
```
## Markdown Parsers
See [Markdown parsers docs](https://sybil.readthedocs.io/en/latest/markdown.html).
```python
from sybil.parsers.markdown.codeblock import PythonCodeBlockParser, CodeBlockParser
from sybil.parsers.markdown.skip import SkipParser
from sybil.parsers.markdown.clear import ClearNamespaceParser
```
## Skip Directives
See [skip directive docs](https://sybil.readthedocs.io/en/latest/markdown.html#skipping-examples).
**SkipParser must come before other parsers** to handle skip directives.
````markdown
<!-- skip: next -->
```python
# This example is skipped
```
````
<!-- skip: next "reason for skipping" -->
```python
# Skipped and reported as skipped test with reason
```
<!-- skip: start -->
```python
# Multiple examples
```
```python
# All skipped
```
<!-- skip: end -->
<!-- skip: next if(condition_var) -->
```python
# Conditionally skipped based on namespace variable
```
````
## Invisible Code Blocks
Setup code that doesn't render in documentation. See [invisible code blocks docs](https://sybil.readthedocs.io/en/latest/markdown.html#invisible-code-blocks).
```markdown
<!-- invisible-code-block: python
setup_var = "hidden setup"
-->
````
## Clear Namespace
Reset the document namespace for isolation. See [clear namespace docs](https://sybil.readthedocs.io/en/latest/markdown.html#clearing-the-namespace).
```markdown
<!-- clear-namespace -->
```
## Custom Evaluators
See [evaluators API](https://sybil.readthedocs.io/en/latest/api.html#evaluators).
```python
from sybil.parsers.markdown.codeblock import CodeBlockParser
from sybil.evaluators.python import PythonEvaluator
# Custom evaluator with future imports
evaluator = PythonEvaluator(future_imports=["annotations"])
parser = CodeBlockParser(language="python", evaluator=evaluator)
```
## Running Sybil Tests
Sybil tests are collected like regular pytest tests. To run only Sybil tests:
```bash
# Run tests from specific directory containing documented code
pytest src/mypackage/ -v
# Exclude regular tests, only run documentation examples
pytest docs/ -v
```
To exclude Sybil tests from regular test runs, use pytest's `--ignore` flag or configure `addopts` in `pyproject.toml`.
## Example: Complete conftest.py
```python
"""Sybil configuration for docstring testing."""
from sybil import Sybil
from sybil.document import PythonDocStringDocument
from sybil.evaluators.python import PythonEvaluator
from sybil.parsers.markdown.codeblock import CodeBlockParser
from sybil.parsers.markdown.skip import SkipParser
import mypackage
def sybil_setup(namespace):
"""Pre-populate namespace for all examples."""
namespace["pkg"] = mypackage
def sybil_teardown(namespace):
"""Cleanup after document."""
pass
pytest_collect_file = Sybil(
parsers=[
SkipParser(),
CodeBlockParser(language="python", evaluator=PythonEvaluator()),
CodeBlockParser(language="py", evaluator=PythonEvaluator()),
],
patterns=["src/mypackage/**/*.py"],
document_types={".py": PythonDocStringDocument},
setup=sybil_setup,
teardown=sybil_teardown,
excludes=[
"**/tests/**",
"**/_internal/**",
],
).pytest()
```
More from anam-org/metaxy
- claude-improve-configSelf-reflect on the current session to identify mistakes and propose improvements to .claude configuration (CLAUDE.md, hooks, skills).
- docs-page-frontmatterWrite YAML front matter for documentation pages with appropriate titles and descriptions for social cards.
- hypothesisUse Hypothesis for property-based testing to automatically generate comprehensive test cases, find edge cases, and write more robust tests with minimal example shrinking. Includes Polars parametric testing integration.
- metaxyThis skill should be used when the user asks to "define a feature", "create a BaseFeature class", "track feature versions", "set up metadata store", "field-level lineage", "FieldSpec", "FeatureDep", "run metaxy CLI", "metaxy migrations", "metaxy lock", "lock features", "external features", "multi-environment", "monorepo features", "enable Map datatype", "enable_map_datatype", or needs guidance on metaxy feature definitions, versioning, metadata stores, CLI commands, testing patterns, feature locking, Map datatype configuration, or multi-environment configuration.
- narwhalsEffectively use Narwhals to write dataframe-agnostic code that works seamlessly across multiple Python dataframe libraries. Write correct type annotations for code using Narwhals.
- syrupyUse syrupy for pytest snapshot testing to ensure the immutability of computed results, manage snapshots, customize serialization, and handle complex data structures with built-in matchers and filters.
- tachThis skill should be used when the user asks to "add a tach module", "configure tach layers", "define module boundaries", "set up interfaces", "run tach check", "check module boundaries", "tach sync", "tach show", "deprecate a dependency", "tach-ignore", "unchecked modules", "tach test", "skip tests with tach", "configure tach.toml", "source roots", "forbid circular dependencies", "enforce module boundaries", "set up architectural layers", or "tach init".