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.21.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.21.0
File Size Uploaded
nginx_lint_plugin-0.21.0.tar.gz 29.8 kB Details

Built distributions (wheels)

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

Download URL nginx_lint_plugin-0.21.0.tar.gz
Size 29.8 kB
Tags Source
SHA-256 checksum
How to use checksums
7536d271331ad762a3927168d775870af8d2135ce6e0ac5df01859cd943730bf
BLAKE2b-256 checksum
How to use checksums
d18f30ec2492e81c691c34b3eb8ce766744b9d3fc1baee018695522599fd642a
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 13, 2026.

Transparency log

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

Download URL nginx_lint_plugin-0.21.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
008dfcaa39afa55cc98a6c418d97186bbab301ecee743764d496be3ed93958a9
BLAKE2b-256 checksum
How to use checksums
5372b079ae337870b9430c315f16fd384c215a30f0ed0153b249b9e2511c807e
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 13, 2026.

Transparency log

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

Download URL nginx_lint_plugin-0.21.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
836669180c3d98ae3612265e479ea8f04ee51ae17743723c8c534e649b812e27
BLAKE2b-256 checksum
How to use checksums
8e2f8e631d9f8d46e108bd471d6b28d1f18490a28c7e4140e0b58cd92d8c686d
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 13, 2026.

Transparency log

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

Download URL nginx_lint_plugin-0.21.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
aed4a77bdd065599bdf637909dca45c30c7c158ef14ff9a63eb08e75ef6ef480
BLAKE2b-256 checksum
How to use checksums
e3bc090bf449d0aec87c8e523a95743a3899aca96307cdbacb8e236c5daa0d41
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 13, 2026.

Transparency log

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

Download URL nginx_lint_plugin-0.21.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
0e89341b9bb33f02745b268d4463b9a6d635de3c396b7576195478fc24929f55
BLAKE2b-256 checksum
How to use checksums
04064d7508f802b2fb9ae743dfd2293480e426cb8fc399543a86008875b08047
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 13, 2026.

Transparency log

Release history Release notifications | RSS feed

0.24.0

5 release files

0.23.0

5 release files

0.22.0

5 release files

This release

0.21.0 This release

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