Python DSL for Testing Zig Code
Project description
PZSpec - Python DSL for Testing Zig Code
A Domain-Specific Language built in Python for writing concise, readable tests for Zig code using FFI (Foreign Function Interface).
Overview
PZSpec allows you to write expressive, readable tests in Python that call into Zig code compiled as a shared library. This combines Python's ease of use for test scripting with Zig's performance and safety guarantees.
Key Features:
- 🚀 Zero Configuration: Automatic library building, no build scripts needed
- 📁 Convention Over Configuration: Follows RSpec-style conventions
- 🔧 Optional Customization:
.pzspecfile for advanced configuration - 🎯 Simple Setup: Just write tests, PZSpec handles the rest
Project Structure
your-project/
├── src/
│ └── lib.zig # Zig library with functions to test
├── .pzspec # Optional configuration file
└── pzspec/ # Test files (RSpec convention)
└── test_*.py # Test files
No run_tests.py needed! Install PZSpec and use the pzspec command globally.
Setup
Prerequisites
-
Zig: Install Zig (version 0.11+ recommended)
- Download from: https://ziglang.org/download/
- Or use a package manager:
brew install zig(macOS)
-
Python: Python 3.8+ (uses standard library only, no external dependencies)
Build the Zig Library
PZSpec automatically builds your Zig library when needed! No build scripts required.
Conventions (automatic):
- Source file:
src/lib.zigor first.zigfile insrc/ - Library name: Project directory name
- Output:
zig-out/lib/lib<name>.<ext>
Customization (optional):
Create a .pzspec file in your project root to customize:
{
"library_name": "mylib",
"source_file": "src/custom.zig",
"optimize": "ReleaseSafe",
"build_dir": "zig-out/lib",
"build_flags": ["-fstack-protector"]
}
The library will be automatically built when you run tests if it doesn't exist.
Install PZSpec
Option 1: Install from PyPI (Recommended)
pip install pzspec
Option 2: Install from source
pip install -e .
After installation, use the pzspec command from any directory:
cd your-project
pzspec # Automatically finds and runs tests
Writing Tests
PZSpec follows the RSpec convention: test files go in the pzspec/ directory and should be named test_*.py.
Basic Example
Create a file pzspec/test_math.py:
from pzspec import ZigLibrary, describe, it, expect
# Load the Zig library
zig = ZigLibrary()
# Define test suites
with describe("Math Operations"):
@it("should add two numbers")
def test_add():
result = zig.add(2, 3)
expect(result).to_equal(5)
DSL Features
The DSL provides several ways to write tests:
Fluent API:
expect(zig.add(2, 3)).to_equal(5)
expect(zig.is_even(4)).to_be_true()
expect(zig.divide(10.0, 3.0)).to_be_almost_equal(3.333, delta=0.001)
Function-based API:
from pzspec import assert_equal, assert_true
assert_equal(zig.add(2, 3), 5)
assert_true(zig.is_even(4))
Test organization:
with describe("Suite Name"):
@it("test case 1")
def test1():
# test code
@it("test case 2")
def test2():
# test code
Running Tests
Using the pzspec command (after installation):
# From your project directory
pzspec
# From any directory
pzspec --project-root /path/to/project
# Quiet mode
pzspec --quiet
Using run_tests.py (without installation):
# From PZSpec project directory
python run_tests.py
# Quiet mode
python run_tests.py --quiet
How It Works
-
Zig Compilation: The Zig code is compiled to a shared library (
.so,.dylib, or.dll) using the C ABI. -
FFI Loading: Python's
ctypeslibrary loads the shared library and provides type-safe wrappers for Zig functions. -
Test Execution: The Python DSL collects tests and executes them, reporting results in a readable format.
Adding New Zig Functions
-
Add the function to
src/lib.zigwith theexportkeyword:export fn my_function(a: i32) i32 { return a * 2; }
-
The library will be automatically rebuilt when you run tests. No manual build needed!
-
Call it in your tests using
get_function():func = zig.get_function("my_function", [ctypes.c_int32], ctypes.c_int32) result = func(42) expect(result).to_equal(84)
Example Test Suite
See pzspec/test_math.py for a complete example covering:
- Basic math operations
- Array operations
- String operations
- Boolean logic
- Error handling
Type Mapping
| Zig Type | Python/ctypes Type |
|---|---|
i32 |
ctypes.c_int32 |
i64 |
ctypes.c_int64 |
f64 |
ctypes.c_double |
f32 |
ctypes.c_float |
bool |
ctypes.c_bool |
[*:0]const u8 |
ctypes.c_char_p |
[*]i32 |
ctypes.POINTER(ctypes.c_int32) |
usize |
ctypes.c_size_t |
Conventions
PZSpec follows convention over configuration. See CONVENTIONS.md for details on:
- Automatic source file detection
- Library naming conventions
- Build output locations
- Configuration via
.pzspecfile
License
MIT
Contributing
Contributions welcome! This is a simple framework designed to be extended for your specific testing needs.
Project details
Release history Release notifications | RSS feed
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 pzspec-0.1.0.tar.gz.
File metadata
- Download URL: pzspec-0.1.0.tar.gz
- Upload date:
- Size: 46.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f5d9e1e767dfaf5fc56d1ef4c799a1ebcaecce5ef89019f137f1b30d4de0cd5
|
|
| MD5 |
b338fa4b07c5181a4ea35d315ad38d30
|
|
| BLAKE2b-256 |
e391b21755914ce847f6081d1ad1e2b0e8a788277cd709b45ccfaad02083a4b2
|
Provenance
The following attestation bundles were made for pzspec-0.1.0.tar.gz:
Publisher:
publish.yml on apotema/pzspec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pzspec-0.1.0.tar.gz -
Subject digest:
0f5d9e1e767dfaf5fc56d1ef4c799a1ebcaecce5ef89019f137f1b30d4de0cd5 - Sigstore transparency entry: 729709738
- Sigstore integration time:
-
Permalink:
apotema/pzspec@cf17c800e22ae1bf636899dbe93af361fda6073d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/apotema
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cf17c800e22ae1bf636899dbe93af361fda6073d -
Trigger Event:
release
-
Statement type:
File details
Details for the file pzspec-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pzspec-0.1.0-py3-none-any.whl
- Upload date:
- Size: 48.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
64ad39492b0f628874919e3402c9c8cebf4adf140c86e28dfa3ac19653fe4d4b
|
|
| MD5 |
f13720ae205fda0096cfad91995196b3
|
|
| BLAKE2b-256 |
e09ecfdaca8f2736bf45506d41d2b0db85cb5ccd1e7ecfc83e1c25abdc64dbde
|
Provenance
The following attestation bundles were made for pzspec-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on apotema/pzspec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pzspec-0.1.0-py3-none-any.whl -
Subject digest:
64ad39492b0f628874919e3402c9c8cebf4adf140c86e28dfa3ac19653fe4d4b - Sigstore transparency entry: 729709740
- Sigstore integration time:
-
Permalink:
apotema/pzspec@cf17c800e22ae1bf636899dbe93af361fda6073d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/apotema
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cf17c800e22ae1bf636899dbe93af361fda6073d -
Trigger Event:
release
-
Statement type: