genvm-linter
Fast validation and schema extraction for GenLayer intelligent contracts.
Installation
pip install genvm-linter
Usage
# Run both lint and validate (default)
genvm-lint check contract.py
# Fast AST safety checks only (~50ms)
genvm-lint lint contract.py
# Full SDK semantic validation (~200ms cached)
genvm-lint validate contract.py
# Extract ABI schema
genvm-lint schema contract.py
genvm-lint schema contract.py --output abi.json
# Pyright type checking with SDK auto-configured
genvm-lint typecheck contract.py
genvm-lint typecheck contract.py --strict # Strict mode
genvm-lint typecheck contract.py --all # Show all errors (disable SDK suppressions)
# IDE setup — download SDK and output extraPaths for Pylance
genvm-lint setup # Latest version
genvm-lint setup --contract contract.py # Auto-detect version from header
genvm-lint setup --version v0.2.12 # Specific version
genvm-lint setup --json # JSON output for IDE integration
# Pre-download GenVM artifacts
genvm-lint download # Latest
genvm-lint download --version v0.2.12 # Specific version
genvm-lint download --list # Show cached
# JSON output (all commands)
genvm-lint check contract.py --json
How It Works
Layer 1: AST Lint Checks (Fast)
- Forbidden imports (
random,os,time, etc.) - Non-deterministic patterns (
float(),time.time()) - Structure validation (dependency header)
- Semantic consensus rule (GL-S03)
Semantic Rules (GL-S)
These rules run as part of Layer 1 and catch common mistakes in GenLayer consensus patterns.
GL-S03 — eq_principle_strict_eq nondeterminism mismatch (ERROR)
Flags eq_principle_strict_eq (all API generations: gl.eq_principle.strict_eq,
eq_principle_strict_eq, eq_principle.strict_eq) wrapping a lambda or function that
returns raw nondeterministic output directly. LLM outputs and raw web content vary
across validators, so strict-equality consensus will always fail.
Detection is conservative — only flagged when the wrapped function/lambda returns
the nondeterministic value without any transformation. Processed output (boolean comparisons,
json.loads, sorted results, etc.) is not flagged.
Nondeterministic calls detected in two forms:
- Namespace-qualified (v0.1.0 and v0.1.3+ APIs):
gl.exec_prompt,gl.get_webpage,gl.nondet.exec_prompt,gl.nondet.web.render, and theirgenlayer.*equivalents - Bare names (direct SDK import convention):
exec_prompt,get_webpage— matched when called without a namespace prefix, representingfrom genlayer.std.nondet_fns import exec_promptstyle imports
Flagged patterns:
# Bad — namespace-qualified, LLM output is non-deterministic (v0.1.0 API)
gl.eq_principle.strict_eq(lambda: gl.exec_prompt("What is 2+2?"))
# Bad — namespace-qualified, raw web content varies (v0.1.0 API)
gl.eq_principle.strict_eq(lambda: gl.get_webpage("https://example.com"))
# Bad — namespace-qualified, v0.1.3+ API
gl.eq_principle.strict_eq(lambda: gl.nondet.exec_prompt("What is 2+2?"))
gl.eq_principle.strict_eq(lambda: gl.nondet.web.render("https://example.com"))
# Bad — bare name (direct import: from genlayer.std.nondet_fns import exec_prompt)
gl.eq_principle.strict_eq(lambda: exec_prompt("What is 2+2?"))
# Bad — named function returning raw nondet
def fetch():
return gl.exec_prompt("What is 2+2?")
gl.eq_principle.strict_eq(fetch)
# Bad — simple passthrough variable
def fetch():
result = gl.exec_prompt("What is 2+2?")
return result
gl.eq_principle.strict_eq(fetch)
Not flagged (processed output or third-party calls):
# OK — bool is deterministic
gl.eq_principle.strict_eq(lambda: "Paris" in gl.get_webpage("https://example.com"))
# OK — comparison produces deterministic bool
def classify():
data = gl.exec_prompt("Answer YES or NO")
return data == "YES"
gl.eq_principle.strict_eq(classify)
# OK — third-party: my_service.exec_prompt is not a GL SDK call
gl.eq_principle.strict_eq(lambda: my_service.exec_prompt("What is 2+2?"))
Alias handling: GL-S03 matches by name convention, not by import tracking. Bare
names exec_prompt and get_webpage are flagged when called without a namespace prefix
(representing direct SDK imports such as from genlayer.std.nondet_fns import exec_prompt).
Calls through a non-SDK object — e.g. my_service.exec_prompt() — are not flagged
because my_service.exec_prompt is not in the matched name list.
Fix — switch to a comparative principle:
gl.eq_principle.prompt_comparative(
lambda: gl.exec_prompt("What is 2+2?", response_format=int),
principle="Return YES if both answers are numerically equal",
)
Layer 2: SDK Validation (Accurate)
- Downloads GenVM release artifacts (cached at
~/.cache/genvm-linter/) - Loads exact SDK version specified in contract header
- Validates types, decorators, storage fields
- Extracts ABI schema
Exit Codes
0- All checks passed1- Lint or validation errors2- Contract file not found3- SDK download failed
IDE Integration
VS Code Extension
This linter is used by the GenLayer VS Code Extension for real-time contract validation.
Manual Pylance Setup
Use genvm-lint setup to configure Pylance with the correct SDK paths. This gives you hover docs, go-to-definition, and type checking without the extension.
genvm-lint setup --contract contract.py
Add the output paths to your VS Code settings.json:
{
"python.analysis.extraPaths": ["<output paths>"],
"python.analysis.reportMissingModuleSource": "none"
}
Type Checking
genvm-lint typecheck runs Pyright with the SDK auto-configured. By default it suppresses SDK-internal noise (dynamic attributes, NewType compat). Use --all to see everything, --strict for strict mode.
Development
git clone https://github.com/genlayerlabs/genvm-linter.git
cd genvm-linter
pip install -e ".[dev]"
pytest
License
MIT
Metadata
Release files for genvm-linter 0.11.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| genvm_linter-0.11.0.tar.gz | 78.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| genvm_linter-0.11.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 119.0 kB
Release files / genvm_linter-0.11.0.tar.gz
| Download URL | genvm_linter-0.11.0.tar.gz |
|---|---|
| Size | 78.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c287ae64ba58dfe2a62b538c17a31c5b89d4dcf50718a8a4d35137dc0ebc4ad3
|
|
BLAKE2b-256 checksum How to use checksums |
ab50e72b51176c229b528c8dba26caa3343f54fbe552cac6fea173c4ef8eefb5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / genvm_linter-0.11.0-py3-none-any.whl
| Download URL | genvm_linter-0.11.0-py3-none-any.whl |
|---|---|
| Size | 40.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
58af368831d670e5a0b717400252fdfae88ff71fd65f9fb5fdd3ced326b6d90e
|
|
BLAKE2b-256 checksum How to use checksums |
53a7c9bc251e4b07954a31ac1365063e2c6b0d24ca28e7fcdec20cec328341bd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|