Hammocking is an automatic mocking tool for C code. It parses C/C++ source files using libclang, extracts undefined symbols from object files via nm, and generates mock implementations using Jinja2 templates. Output styles: Google Mock (C++) and plain C stubs.
src/hammocking/
├── hammocking.py # Core logic (all key classes)
├── __main__.py # CLI entry point
├── hammocking.ini # Default config (OS-specific sections)
└── templates/
├── gmock/ # Google Mock C++ templates (.j2)
└── plain_c/ # Plain C stub templates (.j2)
Key classes (all in hammocking.py):
HammockConfig/HammockIni— Configuration dataclasses withmashumaroserializationHammock— Main parser: walks libclang AST to find symbols, delegates toMockupWriterMockupWriter— Renders Jinja2 templates into mock.c/.cc/.hfilesNmWrapper— Extracts undefined symbols from partially-linked object files vianmHammockRunner— Orchestrates config loading, parsing, and outputFunction/Variable/RenderableType— AST node wrappers for code generation
Entry point: main() in hammocking.py parses CLI args and runs HammockRunner.
- Language: Python >=3.10, <3.14 (3.13 recommended)
- Package Manager: uv
- Key dependencies: libclang (C parsing), Jinja2 (templating), py-app-dev (subprocess), mashumaro (serialization)
- Testing: pytest + pytest-cov
- Linting: ruff, mypy (strict), pre-commit hooks
- Documentation: Sphinx + ReadTheDocs (Markdown via myst-parser)
- CI/CD: GitHub Actions → semantic-release → PyPI
# Install dependencies
./build.sh --install
# Full build (pre-commit + tests + docs)
./build.sh
# Run tests only
uv run pytest
# Run specific test markers
uv run pytest -m unit
uv run pytest -m integrationOn Windows: .\build.ps1
- Style: ruff with line-length 220, target Python 3.10
- Type checking: mypy strict (
disallow_untyped_defs,disallow_any_generics) - Formatting: 4-space indentation, UTF-8, LF line endings (
.editorconfig) - Imports: isort via ruff (first-party:
hammocking,tests) - Types: Use Python 3.10+ union syntax (
str | None, notOptional[str]) - Config objects: Dataclasses with
DataClassDictMixinfrom mashumaro - Paths: Always use
pathlib.Path, never string paths - Logging: Standard
loggingmodule, no print statements - Naming: PascalCase classes, snake_case functions/methods
- Commits: Conventional Commits (
feat:,fix:,chore:, etc.) — enforced by commitlint and commitizen
- Tests live in
tests/with fixtures intests/data/ - Unit tests: Test classes/functions directly using libclang's in-memory parsing (
clang_parse()helper) - Integration tests: Full CMake build cycles in
tests/data/mini_c_test/ - Mark tests with
@pytest.mark.unitor@pytest.mark.integration - Integration tests require clang/llvm, cmake, ninja-build installed
- Test output goes to
build/test-report.xml(JUnit format)
- Lint — pre-commit hooks + commitlint (conventional commits)
- Test on Windows —
build.ps1on windows-latest - Test on Linux —
build.shon ubuntu-24.04 with Python 3.13 - Release — python-semantic-release to PyPI (develop branch only)
- The tool accepts symbols either directly (
--symbols) or by extracting them from a partially-linked object (--plink) - Templates use Jinja2 with
FunctionandVariableobjects exposed to the template context - Configuration merges CLI args → INI file → defaults, with OS-specific INI sections (
[hammocking.darwin],[hammocking.linux],[hammocking.win32]) - libclang AST traversal skips function bodies and dives into
extern "C"blocks
- The main branch is
develop(notmain) - Pre-Alpha status (v0.9.0) — API may change
- Cross-platform: Windows, Linux, macOS (with OS-specific clang/nm paths)
exclude_pathsfilters prevent mocking system headers- When adding new mock output styles, create a new template directory under
templates/with.j2files