Skip to main content

Python DSL for Testing Zig Code

Project description

PZSpec - Python DSL for Testing Zig Code

PyPI version Python 3.8+ License: MIT CI

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: .pzspec file 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

  1. Zig: Install Zig (version 0.11+ recommended)

  2. 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.zig or first .zig file in src/
  • 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

  1. Zig Compilation: The Zig code is compiled to a shared library (.so, .dylib, or .dll) using the C ABI.

  2. FFI Loading: Python's ctypes library loads the shared library and provides type-safe wrappers for Zig functions.

  3. Test Execution: The Python DSL collects tests and executes them, reporting results in a readable format.

Adding New Zig Functions

  1. Add the function to src/lib.zig with the export keyword:

    export fn my_function(a: i32) i32 {
        return a * 2;
    }
    
  2. The library will be automatically rebuilt when you run tests. No manual build needed!

  3. 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 .pzspec file

License

MIT

Contributing

Contributions welcome! This is a simple framework designed to be extended for your specific testing needs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pzspec-0.1.0.tar.gz (46.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pzspec-0.1.0-py3-none-any.whl (48.9 kB view details)

Uploaded Python 3

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

Hashes for pzspec-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0f5d9e1e767dfaf5fc56d1ef4c799a1ebcaecce5ef89019f137f1b30d4de0cd5
MD5 b338fa4b07c5181a4ea35d315ad38d30
BLAKE2b-256 e391b21755914ce847f6081d1ad1e2b0e8a788277cd709b45ccfaad02083a4b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for pzspec-0.1.0.tar.gz:

Publisher: publish.yml on apotema/pzspec

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for pzspec-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 64ad39492b0f628874919e3402c9c8cebf4adf140c86e28dfa3ac19653fe4d4b
MD5 f13720ae205fda0096cfad91995196b3
BLAKE2b-256 e09ecfdaca8f2736bf45506d41d2b0db85cb5ccd1e7ecfc83e1c25abdc64dbde

See more details on using hashes here.

Provenance

The following attestation bundles were made for pzspec-0.1.0-py3-none-any.whl:

Publisher: publish.yml on apotema/pzspec

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page