Linter
Xian has two related linting surfaces:
- the core contract linter in
xian-contracting - the optional standalone
xian-linterHTTP service
Both ultimately enforce the same contract-language rules. The standalone package can also run an optional xian_vm_v1 validation mode for tooling that wants to check that a contract lowers to Xian VM IR before deployment.
The published PyPI package name for the standalone service is xian-tech-linter. The import package and console command remain xian_linter and xian-linter.
Using The Core Linter
Use contracting.compilation.linter.Linter when you are already inside Python:
from contracting.compilation.linter import Linter
source = """
balances = Hash(default_value=0)
@export
def transfer(to: str, amount: float):
assert amount > 0, "Amount must be positive"
balances[ctx.caller] -= amount
balances[to] += amount
"""
linter = Linter()
errors = linter.check(source) or []
for error in errors:
print(error.code.value, error.line, error.col, error.message)The core linter returns structured LintError objects with:
codemessagelinecolend_lineend_col
Using The Standalone HTTP Service
Install the base package for inline use, or the server extra for the HTTP service:
uv add xian-tech-linter
uv add "xian-tech-linter[server]"Install the VM extra when you want native Xian VM IR validation as part of xian_vm_v1 mode:
uv add "xian-tech-linter[vm]"
uv add "xian-tech-linter[server,vm]"Start the service:
xian-linterThe default listen address is http://localhost:8000.
You can also run the ASGI app explicitly:
uvicorn xian_linter.server:create_app --factory --host 0.0.0.0 --port 8000HTTP Endpoints
The service accepts raw, base64, or gzip request bodies:
POST /lintPOST /lint_base64POST /lint_gzip
Request bodies are capped at 1,000,000 bytes before decoding. All endpoints also accept an optional comma-separated whitelist_patterns query parameter when a controlled integration needs to allow additional PyFlakes names:
POST /lint?whitelist_patterns=my_helper,con_*All endpoints use the default source-linting mode unless a mode is selected:
POST /lint?mode=xian_vm_v1The alias lint_mode=xian_vm_v1 is also accepted for integrations that already use mode for their own routing.
Lint Modes
The standalone package supports two modes:
| Mode | What it checks |
|---|---|
python | default source linting with the shared xian-contracting rules plus PyFlakes warnings |
xian_vm_v1 | source linting, VM-profile compatibility, Xian VM IR lowering, and native IR validation when xian_vm_core is installed |
The VM mode is still linting only. It does not execute the contract or simulate storage, imports, environment values, or host syscalls. Use runtime tests or node preflight flows for execution behavior.
Inline usage:
from xian_linter import lint_code_sync
errors = lint_code_sync(source, mode="xian_vm_v1")Raw Source Example
curl -X POST "http://localhost:8000/lint?mode=xian_vm_v1" \
-H "Content-Type: text/plain" \
--data-binary $'import os\n\n@export\ndef hack():\n os.system("rm -rf /")'Base64 Example
base64 < contract.py > contract.py.b64
curl -X POST http://localhost:8000/lint_base64 --data-binary @contract.py.b64Gzip Example
gzip -c contract.py > contract.py.gz
curl -X POST http://localhost:8000/lint_gzip \
-H "Content-Type: application/gzip" \
--data-binary @contract.py.gzResponse Shape
The HTTP service returns:
{
"success": false,
"errors": [
{
"code": "E005",
"message": "Cannot import stdlib module 'os'",
"severity": "error",
"position": {
"line": 1,
"col": 0,
"end_line": 1,
"end_col": 9
}
}
]
}xian-linter may also include PyFlakes warnings with code W001. Processing errors from the wrapper itself are returned as E000. In xian_vm_v1 mode, IR lowering failures are returned as XVM001; native VM IR validation failures are returned as XVM002.
Error Codes
| Code | Meaning |
|---|---|
E000 | standalone wrapper processing error |
E001 | illegal syntax form |
E002 | name starts or ends with underscore |
E003 | import inside a function |
E004 | from x import y used |
E005 | stdlib import attempted |
E006 | class definition |
E007 | async function |
E008 | invalid decorator |
E009 | multiple constructors |
E010 | multiple decorators on one function |
E011 | forbidden ORM kwarg |
E012 | tuple unpacking for ORM declarations |
E013 | no exported function |
E014 | forbidden builtin or reserved name |
E015 | exported arg shadows ORM name |
E016 | invalid exported argument annotation |
E017 | missing exported argument annotation |
E018 | invalid exported return annotation |
E019 | nested function |
E020 | syntax error |
E021 | invalid decorator arguments |
E022 | syntax not supported by the active validation profile |
E023 | builtin not supported by the active validation profile |
W001 | PyFlakes warning from the standalone service |
XVM001 | Xian VM IR lowering failed |
XVM002 | native Xian VM IR validation failed |
See Valid Code & Restrictions for the current language surface.