A Python linter for GenLayer GenVM intelligent contracts
Project description
GenVM Linter
A Python linter specifically designed for GenLayer GenVM intelligent contracts. This linter validates GenLayer intelligent contracts according to GenVM's type system and coding conventions.
Features
โจ Comprehensive Validation
- Magic comment validation (
# { "Depends": "py-genlayer:test" }) - GenLayer import checking (
from genlayer import *) - Contract class structure validation
- Method decorator validation (
@gl.public.view,@gl.public.write) - Type system enforcement (sized integers, collections, dataclasses)
๐ง Type System Rules
- Enforces use of sized integers (
u64,u256, etc.) in storage fields - Prevents use of
intreturn types (should useintinstead ofu256) - Validates proper collection types (
DynArrayvslist,TreeMapvsdict) - Checks
@allow_storagedecorator usage on dataclasses
๐ฏ Smart Error Detection
- Detects missing or incorrect decorators
- Identifies state modification in view methods
- Validates constructor decoration rules
- Comprehensive error messages with suggestions
Installation
From Source
git clone https://github.com/genlayerlabs/genvm-linter.git
cd genvm-linter
pip install -e .
Using pip (when published)
pip install genvm-linter
Usage
Command Line
# Lint a single file
genvm-lint contract.py
# Lint all Python files in a directory
genvm-lint contracts/
# Show only errors
genvm-lint --severity error contract.py
# Output as JSON
genvm-lint --format json contract.py
# Show statistics
genvm-lint --stats contracts/
# Run specific rules only
genvm-lint --rule genvm-types --rule genvm-decorators contract.py
# Exclude specific rules
genvm-lint --exclude-rule genvm-magic-comment contract.py
Python API
from genvm_linter import GenVMLinter
# Create linter instance
linter = GenVMLinter()
# Lint a file
results = linter.lint_file("path/to/contract.py")
# Lint source code directly
source_code = '''
# { "Depends": "py-genlayer:test" }
from genlayer import *
class MyContract(gl.Contract):
balance: u256
def __init__(self, initial_balance: int):
self.balance = initial_balance
@gl.public.view
def get_balance(self) -> int:
return self.balance
'''
results = linter.lint_source(source_code)
# Process results
for result in results:
print(f"{result.severity.value}: {result.message}")
if result.suggestion:
print(f"๐ก {result.suggestion}")
Validation Rules
Required Structure Rules
| Rule ID | Description |
|---|---|
genvm-magic-comment |
First line must contain # { "Depends": "py-genlayer:test" } |
genvm-import |
Must include from genlayer import * |
genvm-contract-class |
Exactly one class extending gl.Contract |
Decorator Rules
| Rule ID | Description |
|---|---|
genvm-decorators |
Proper usage of @gl.public.view and @gl.public.write |
Decorator Requirements:
__init__methods must NOT have public decorators- Public methods must have exactly one
@gl.public.*decorator - Private methods (starting with
_) should not have public decorators - Use
@gl.public.viewfor read-only methods - Use
@gl.public.writefor state-modifying methods
Type System Rules
| Rule ID | Description |
|---|---|
genvm-types |
Validates GenVM type system usage |
Type System Requirements:
Storage Fields
- โ
Use sized integers:
u8,u16,u32,u64,u128,u256,i8,i16, etc. - โ Don't use plain
intin storage annotations - โ
Use
DynArray[T]instead oflist[T] - โ
Use
TreeMap[K, V]instead ofdict[K, V]
Method Return Types
- โ
Use
intfor return type annotations - โ Don't use sized integers (
u256, etc.) in return types
Dataclasses
- Use
@allow_storagedecorator for dataclasses used in storage - Consider sized integers for dataclass fields
Example: Valid Contract
# { "Depends": "py-genlayer:test" }
from genlayer import *
from dataclasses import dataclass
@allow_storage
@dataclass
class UserData:
name: str
balance: u256
is_active: bool
class TokenContract(gl.Contract):
owner: Address
users: TreeMap[Address, UserData]
total_supply: u256
def __init__(self, initial_supply: int):
self.owner = gl.message.sender_address
self.total_supply = initial_supply
@gl.public.view
def get_balance(self, user: str) -> int:
address = Address(user)
user_data = self.users.get(address)
return user_data.balance if user_data else 0
@gl.public.write
def transfer(self, to: str, amount: int):
# Transfer logic here
to_address = Address(to)
# ... implementation
Example: Common Issues
# โ Missing magic comment
from genlayer import * # Should have magic comment above
class BadContract(gl.Contract):
balance: int # โ Should be u256
users: dict[str, int] # โ Should be TreeMap[Address, u256]
items: list[str] # โ Should be DynArray[str]
def __init__(self, initial_balance: int):
self.balance = initial_balance
# โ Missing decorator
def get_balance(self) -> u256: # โ Should return int
return self.balance
@gl.public.view # โ Wrong decorator for state modification
def set_balance(self, amount: int):
self.balance = amount
Development
Setup Development Environment
git clone https://github.com/genlayerlabs/genvm-linter.git
cd genvm-linter
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest
# Run linter on itself
genvm-lint src/
# Format code
black src/ tests/
isort src/ tests/
# Type checking
mypy src/
Project Structure
genvm-linter/
โโโ src/genvm_linter/
โ โโโ __init__.py # Main package
โ โโโ linter.py # Core linter logic
โ โโโ cli.py # Command-line interface
โ โโโ rules/ # Validation rules
โ โโโ __init__.py
โ โโโ base.py # Base rule classes
โ โโโ contract.py # Contract structure rules
โ โโโ decorators.py # Decorator validation
โ โโโ types.py # Type system rules
โ โโโ genvm_patterns.py # GenVM API patterns
โ โโโ python_types.py # MyPy integration
โโโ tests/
โ โโโ unit/ # Unit tests
โ โโโ integration/ # Integration tests
โ โโโ fixtures/ # Test contract files
โ โโโ examples/ # Example contracts
โโโ ARCHITECTURE.md # System architecture
โโโ CONTRIBUTING.md # Contribution guidelines
โโโ CHANGELOG.md # Version history
โโโ pyproject.toml # Package configuration
โโโ README.md # This file
Documentation
- ARCHITECTURE.md - Python linter architecture and rule system
- CONTRIBUTING.md - Guidelines for contributing to the project
- CHANGELOG.md - Version history and release notes
VS Code Extension
The VS Code extension for this linter is maintained in a separate repository: GenLayer VS Code Extension
Contributing
Please see CONTRIBUTING.md for detailed guidelines on how to contribute to this project.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Related Projects
- GenLayer - The GenLayer protocol
- GenLayer CLI - Command-line tools for GenLayer
- GenLayer Studio - Web IDE for GenLayer development
- GenLayer Documentation - Complete GenLayer documentation
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file genvm_linter-0.1.0.tar.gz.
File metadata
- Download URL: genvm_linter-0.1.0.tar.gz
- Upload date:
- Size: 46.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05c7676f32ab53499bff0c29e092d5589062f6dd368b86163fc22f71991347ca
|
|
| MD5 |
0e7216edefb3cde4511d7eb853db0d5d
|
|
| BLAKE2b-256 |
9bc17937326f0c4561e400f18877773c21690b4727144d32f3ce21be8bfed3c9
|
File details
Details for the file genvm_linter-0.1.0-py3-none-any.whl.
File metadata
- Download URL: genvm_linter-0.1.0-py3-none-any.whl
- Upload date:
- Size: 32.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92c6315339a48b148799cfb8c07862924f3b5c1c4d7e7194e67dabe3fb2ba0c6
|
|
| MD5 |
f21c706c2a99543eb8b998329fed32b8
|
|
| BLAKE2b-256 |
8fa91dfec69f18dcba886cc1d4c705eb6360072a39d3b3b1baa7e0f1e8878075
|