Skip to main content

nginx-lint-plugin (Python SDK)

Python SDK for writing and testing nginx-lint WASM plugins. The Python counterpart of the TypeScript SDK at plugins/typescript/nginx-lint-plugin, built as a single maturin mixed Rust/Python project: one wheel ships the pure-Python SDK, the componentize-py bindings, and the Rust parser compiled as a native module.

Layout

  • python/nginx_lint_plugin/ — the SDK package. Everything a plugin needs is re-exported from its root (Plugin, Config, LintError, Fix, Severity, …), so plugin code never imports from the generated bindings directly. It ships a PEP 561 py.typed marker, so mypy and pyright use the annotations.
    • builders — plugin_spec() and error_builder(), mirroring the Rust SDK's PluginSpec::new() and spec().error_builder(). The generated dataclasses have no defaults, so without these a spec means spelling out all eleven fields and every error repeats the plugin's rule and category.
    • testing — parse_config() and PluginTestRunner for plain-pytest unit tests against the real Rust parser, plus apply_fixes() and PluginTestRunner.assert_fixed() to check what a rule's fixes actually produce, through the same applier nginx-lint --fix uses
    • config_builder — reconstructs method-based Config/Directive objects (matching the componentize-py binding surface, e.g. directive.is_(...)) from parser output or a host snapshot
    • _native — the Rust parser bridge (built by maturin from src/lib.rs; only testing imports it, so the rest of the SDK stays bundleable into a WASM component). Built against the stable ABI (abi3-py311), so one wheel per platform covers every Python ≥ 3.11.
    • API_VERSION — the plugin API version, kept in sync with crates/nginx-lint-plugin
  • python/wit_world/, python/componentize_py_types.py — componentize-py bindings generated from wit/nginx-lint-plugin.wit by make bindings, placed as top-level modules so the same imports resolve inside a componentized plugin (the Python analog of the TS SDK's dist/generated). Like the TS SDK's, they are not committed — regenerated before every install, so they cannot drift from the WIT. They still ship in the wheel and sdist via tool.maturin.include, which applies regardless of .gitignore.
  • Cargo.toml / src/lib.rs — the native parser module (its own cargo workspace, excluded from the repository's root workspace; the crate version is the wheel version, kept in sync with the repository version). It depends on nginx-lint-parser and nginx-lint-common by exact version rather than by path so that the sdist stands alone; inside this repository ../.cargo/config.toml patches both back to the working tree, and that file explains why.

Install

Both targets regenerate the bindings and copy the WIT in first, which needs the componentize-py CLI; make develop also needs maturin. Both are in the dev dependency group:

cd plugins/python/nginx-lint-plugin
pip install --group dev

make install   # pip install .
make develop   # editable install for SDK work (maturin develop)
make test      # pytest

Outside this repository the SDK is a plain dependency — pip install nginx-lint-plugin is all a plugin author needs, for both testing and building. The WIT ships inside the package, so componentize-py has an interface definition to build against:

componentize-py -d "$(python -c 'import nginx_lint_plugin as p; print(p.wit_dir())')" \
    -w plugin componentize app -o plugin.wasm --stub-wasi \
    -p . -p "$(python -c 'import nginx_lint_plugin as p, pathlib; print(pathlib.Path(p.__file__).parent.parent)')"

The second -p is only needed for editable installs, whose .pth link componentize-py does not follow; it is harmless otherwise. See ../server-tokens-enabled-py/Makefile for the same commands in a form you can copy.

Writing a plugin

from nginx_lint_plugin import Config, LintError, Plugin, plugin_spec, error_builder


class WitWorld(Plugin):          # the class must keep this name
    def spec(self):
        return plugin_spec("my-rule", "style", "What it checks",
                           severity="warning")

    def check(self, cfg: Config, path: str) -> list[LintError]:
        err = error_builder(self.spec())
        return [
            err.warning_at("autoindex should be off", ctx.directive,
                           fixes=[ctx.directive.replace_with("autoindex off;")])
            for ctx in cfg.all_directives_with_context()
            if ctx.directive.is_("autoindex") and ctx.directive.first_arg_is("on")
        ]

Testing a plugin

Tests are ordinary pytest. Parsing goes through the same Rust parser the production linter uses:

from app import WitWorld
from nginx_lint_plugin.testing import PluginTestRunner, parse_config

plugin = WitWorld()
runner = PluginTestRunner(plugin.spec, plugin.check)

def test_detects_server_tokens_on():
    runner.assert_errors("http {\n    server_tokens on;\n}", 1)

def test_include_context():
    cfg = parse_config("server_tokens on;", include_context=["http"])
    assert len(plugin.check(cfg, "test.conf")) == 1

def test_fix():
    # Asserting on the applied output, not just the reported findings: a fix
    # the linter normalizes into a different operation than intended shows up
    # here rather than in a user's config.
    runner.assert_fixed(
        "http {\n    server_tokens on;\n}\n",
        "http {\n    server_tokens off;\n}\n",
    )

The same WitWorld class runs unmodified under pytest and inside the WASM component — the test Config/Directive objects reproduce the exact method surface of the componentize-py bindings.

Why a native module instead of the parser WASM component?

The TS SDK runs the parser as a WASM component inside Node (via jco). Python currently has no maintained component-model runtime — wasmtime-py removed its bindgen support — so this SDK compiles the parser natively via pyo3 instead. Same parser code, same output shape. If wasmtime-py regrows component support, the WASM path can return without changing the test-writing API.

Checking the built plugin

The CLI can check a built plugin end to end from the examples in its spec — the bad one has to be reported, the good one clean, and the fixes have to resolve the bad one:

nginx-lint test-plugins --plugins <dir>

Metadata

Release files for nginx-lint-plugin 0.23.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nginx-lint-plugin 0.23.0
File Size Uploaded
nginx_lint_plugin-0.23.0.tar.gz 30.5 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for nginx-lint-plugin 0.23.0
File
nginx_lint_plugin-0.23.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 abi3 Linux glibc 2.17+ x86-64 Details
nginx_lint_plugin-0.23.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details
nginx_lint_plugin-0.23.0-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details
nginx_lint_plugin-0.23.0-cp311-abi3-macosx_10_12_x86_64.whl CPython 3.11 abi3 macOS 10.12+ x86-64 Details

Total release size: 1.6 MB

Release files / nginx_lint_plugin-0.23.0.tar.gz

Download URL nginx_lint_plugin-0.23.0.tar.gz
Size 30.5 kB
Tags Source
SHA-256 checksum
How to use checksums
7c860d1163de2cb4d79ea49ddd8106ec90c3db8b988f676576db4244a39f5d2b
BLAKE2b-256 checksum
How to use checksums
0875be9050e0a40ee50c0d6658fa5f08fc46c3dc99a38ba7fce7a382136f5a13
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / nginx_lint_plugin-0.23.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL nginx_lint_plugin-0.23.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 431.9 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
e34637917aea3348a04aa43c89724aafc9fe4cc7b2d53e7ce3473a31a8442b81
BLAKE2b-256 checksum
How to use checksums
181b85464dd262f320e60a1ec9fe867a994d317c92c626d843c33ebfb5df36ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / nginx_lint_plugin-0.23.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL nginx_lint_plugin-0.23.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 392.8 kB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
b3520a9430fa173cdf508d4ad7c1e99a85e309a0dcbc4fc24683cff7213e157c
BLAKE2b-256 checksum
How to use checksums
bfd71e2a2709b3299cf64280bd5dde749d6733b613c114f3f32ced712ff0914d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / nginx_lint_plugin-0.23.0-cp311-abi3-macosx_11_0_arm64.whl

Download URL nginx_lint_plugin-0.23.0-cp311-abi3-macosx_11_0_arm64.whl
Size 353.2 kB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
10d26580a59a03a592883f8059f40a551ba5d93fa4173b8e75b5121b5881e272
BLAKE2b-256 checksum
How to use checksums
43dd7bcad82de5b430f2420353fcf0c05bb1450d9d1b3340277d9ec887357d4b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / nginx_lint_plugin-0.23.0-cp311-abi3-macosx_10_12_x86_64.whl

Download URL nginx_lint_plugin-0.23.0-cp311-abi3-macosx_10_12_x86_64.whl
Size 358.6 kB
Tags CPython 3.11 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
49a56288bf05b8b81b3531170b09a062a480b8b97c917ee63f7379f3932fc498
BLAKE2b-256 checksum
How to use checksums
351eed1bbab99fd573a617c94427a52fc93c3b699c5f59f611dbb055742c14fc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release history Release notifications | RSS feed

0.24.0

5 release files

This release

0.23.0 This release

5 release files

0.22.0

5 release files

0.21.0

5 release files

0.20.0

5 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page