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.22.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.22.0
File Size Uploaded
nginx_lint_plugin-0.22.0.tar.gz 29.8 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for nginx-lint-plugin 0.22.0
File
nginx_lint_plugin-0.22.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.22.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details
nginx_lint_plugin-0.22.0-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details
nginx_lint_plugin-0.22.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.22.0.tar.gz

Download URL nginx_lint_plugin-0.22.0.tar.gz
Size 29.8 kB
Tags Source
SHA-256 checksum
How to use checksums
11c1a5deb0cf29f6c362a6a3f50a84a13fed4722fec886c1a9637e4ba7a713c2
BLAKE2b-256 checksum
How to use checksums
17eecc44cd2b109d4d51d706e2ef46073cd1e42ebd61fcb7a4e0340db6ac72c5
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 17, 2026.

Transparency log

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

Download URL nginx_lint_plugin-0.22.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 431.3 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
2cc76efba9cc13dba647b867923371e1f6fe74102967fba7b57b18999c5fbca0
BLAKE2b-256 checksum
How to use checksums
f588185ddb03a6b847622becf8925a98d3822d9d445b97d0bec673b1adb7edb4
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 17, 2026.

Transparency log

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

Download URL nginx_lint_plugin-0.22.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 391.9 kB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
213cdf30b339008c1dce2b56c2413652f5845adb1acd1da64064d02fd1653e30
BLAKE2b-256 checksum
How to use checksums
7fce0420ebbbc743b2181909859b1f56151071b33a76f7d2bf3e872723481c23
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 17, 2026.

Transparency log

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

Download URL nginx_lint_plugin-0.22.0-cp311-abi3-macosx_11_0_arm64.whl
Size 352.5 kB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
c30d9d7aa94615c12cb8512994cee34a8fd14309f9354cc868bfbb2a46333dad
BLAKE2b-256 checksum
How to use checksums
ec43990595a0d58f2ca0b3a44db53e561d27dc5a4e225a53fa5913c601c1b2ac
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 17, 2026.

Transparency log

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

Download URL nginx_lint_plugin-0.22.0-cp311-abi3-macosx_10_12_x86_64.whl
Size 357.7 kB
Tags CPython 3.11 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
2c72416914c2a584fbd6996cf14b10faf5a9d5beb46f52ac999d1f2698dc2ba1
BLAKE2b-256 checksum
How to use checksums
3e990c8e0e86b77eb3cbf9ed462fbb821ad46d71430b9bcb43f414836e685378
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 17, 2026.

Transparency log

Release history Release notifications | RSS feed

0.24.0

5 release files

0.23.0

5 release files

This release

0.22.0 This release

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