AST-based linter enforcing SciTeX reproducible research patterns
Project description
SciTeX Linter
scitex.ai · docs · pip install scitex-linter
AST-based Python linter enforcing SciTeX reproducible research patterns.
Part of the SciTeX ecosystem — guides both human researchers and AI agents toward reproducible science.
Why SciTeX Linter?
SciTeX scripts follow strict patterns for reproducibility: @stx.session decorators, stx.io for provenance-tracked I/O, stx.stats for complete statistical reporting, and relative paths for portability. SciTeX Linter enforces these patterns at the AST level — catching issues before they become irreproducible results.
Quick Start
pip install scitex-linter
# Lint a file
scitex-linter check script.py
# Lint then execute
scitex-linter python experiment.py --strict
# List all 47 rules
scitex-linter rule
Six Interfaces
| Interface | For | Description |
|---|---|---|
| 🖥️ CLI | Terminal users | scitex-linter check, scitex-linter python |
| ✨ Format | Auto-fix | scitex-linter format — savefig, np.save/load, pd.read_csv |
| 🐍 Python API | Programmatic use | scitex-linter api or from scitex_linter.checker import lint_file |
| 🔌 flake8 Plugin | CI pipelines | flake8 --select STX |
| 🔧 MCP Server | AI agents | 3 tools for Claude/GPT integration |
| 📋 Claude Code Hook | AI coding | Auto-lint on every file write/edit |
🖥️ CLI Commands
scitex-linter --help # Show all commands
scitex-linter --help-recursive # Show help for all subcommands
# Check - Check for SciTeX pattern violations
scitex-linter check script.py # Check a file
scitex-linter check ./src/ # Check a directory
scitex-linter check script.py --severity error # Only errors
scitex-linter check script.py --category path # Only path rules
scitex-linter check script.py --json # JSON output for CI
# Format - Auto-fix SciTeX pattern issues
scitex-linter format script.py # Fix in place
scitex-linter format script.py --check # Dry run (exit 1 if changes needed)
scitex-linter format script.py --diff # Show diff of changes
scitex-linter format ./src/ # Format a directory
# Python - Lint then execute
scitex-linter python experiment.py # Lint and run
scitex-linter python experiment.py --strict # Abort on errors
scitex-linter python experiment.py -- --lr 0.001 # Pass script args
# Rules - Browse available rules
scitex-linter rule # List all 47 rules
scitex-linter rule --category stats # Filter by category
scitex-linter rule --json # JSON output
# API - Inspect public Python API
scitex-linter api # Tree view of 12 public APIs
scitex-linter api --json # JSON output
# MCP - AI agent server
scitex-linter mcp start # Start MCP server (stdio)
scitex-linter mcp list-tools # List MCP tools
🐍 Python API
from scitex_linter.checker import lint_file
from scitex_linter.formatter import format_issue
# Lint a file
issues = lint_file("script.py")
for issue in issues:
print(format_issue(issue, "script.py"))
# Check source code directly
from scitex_linter.checker import lint_source
issues = lint_source("import argparse\npass\n")
🔌 flake8 Plugin
SciTeX Linter registers as a flake8 plugin with the STX prefix:
flake8 --select STX script.py
flake8 --select STX ./src/ --format=json
Integrates with existing flake8 workflows, pre-commit hooks, and CI pipelines.
🔧 MCP Server — 3 Tools for AI Agents
| Tool | Description |
|---|---|
linter_check |
Check a Python file for SciTeX compliance |
linter_list_rules |
List all available rules |
linter_check_source |
Lint source code string |
Claude Desktop (~/.config/Claude/claude_desktop_config.json):
{
"mcpServers": {
"scitex-linter": {
"command": "scitex-linter",
"args": ["mcp", "start"]
}
}
}
Install MCP extra: pip install scitex-linter[mcp]
45 Rules Across 8 Categories
| Category | Rules | Severity | What It Enforces |
|---|---|---|---|
| S Structure | 6 | error/warning | @stx.session, __main__ guard, INJECTED params |
| I Import | 7 | warning/info | Use stx.plt, stx.stats, stx.io instead of raw libs |
| IO I/O Calls | 7 | warning | Use stx.io.save()/stx.io.load() for provenance |
| P Plot | 5 | info | Use stx.plt tracked methods, logger over print() |
| ST Stats | 6 | warning | Use stx.stats for auto effect size + CI + power |
| PA Path | 5 | warning/info | Relative paths with ./, no open(), no os.chdir() |
| FM Figure | 9 | warning/info | mm-based layout, stx.io.save() for figures (opt-in) |
Example Output
script.py:1 STX-S003 [error] argparse detected — @stx.session auto-generates CLI
Suggestion: Remove `import argparse` and define parameters as function arguments:
@stx.session
def main(data_path: str, threshold: float = 0.5):
# Auto-generates: --data-path, --threshold
script.py:5 STX-PA001 [warning] Absolute path in `stx.io` call — use relative paths
Suggestion: Use `stx.io.save(obj, './relative/path.ext')` — paths resolve to script_out/.
script.py: 2 issues (1 error, 1 warning)
Claude Code Hook
SciTeX Linter works as a post-tool-use hook for Claude Code, automatically linting every Python file Claude writes or edits:
# In ~/.claude/to_claude/hooks/post-tool-use/run_lint.sh
# Errors (exit 2) → Claude must fix
# Warnings (exit 1) → Claude sees feedback
This ensures AI-generated code follows SciTeX patterns from the start.
Configuration
Configure via pyproject.toml or environment variables
[tool.scitex-linter]
severity = "info" # Minimum severity: error, warning, info
disable = ["STX-P004", "STX-I003"] # Disable specific rules
exclude-dirs = ["venv", ".venv"] # Directories to skip
library-dirs = ["src"] # Exempt from script-only rules
[tool.scitex-linter.per-rule-severity]
STX-S003 = "warning" # Downgrade argparse rule
[tool.scitex-linter.session]
required-injected = ["CONFIG", "plt", "COLORS", "rngg", "logger"]
Environment variables (highest priority):
SCITEX_LINTER_SEVERITY=error
SCITEX_LINTER_DISABLE=STX-P004,STX-I003
Priority: CLI flags > env vars > pyproject.toml > defaults
What a Clean Script Looks Like
import scitex as stx
@stx.session
def main(data_path="./data.csv", threshold=0.5):
df = stx.io.load(data_path)
results = stx.stats.ttest_ind(df["group_a"], df["group_b"])
stx.io.save(results, "./results.csv")
return 0
if __name__ == "__main__":
main()
Zero lint issues. Fully reproducible. Auto-CLI from function signature.
Lint Rules
Detected by scitex-linter when this package is installed.
| Rule | Severity | Message |
|---|---|---|
STX-S001 |
error | Missing @stx.session or @stx.module decorator on main function |
STX-S002 |
error | Missing if __name__ == '__main__' guard |
STX-S003 |
error | argparse detected — @stx.session auto-generates CLI from function signature |
STX-S004 |
warning | @stx.session function should return an integer exit code |
STX-S005 |
warning | Missing import scitex as stx |
STX-S006 |
warning | @stx.session function missing explicit INJECTED parameters |
STX-S007 |
warning | load_configs() result should be assigned to an UPPER_CASE variable |
STX-S008 |
info | Magic number in module scope — consider centralizing in config/ |
STX-I001 |
warning | Use stx.plt instead of importing matplotlib.pyplot directly |
STX-I002 |
warning | Use stx.stats instead of importing scipy.stats directly |
STX-I003 |
warning | Use stx.io instead of pickle for file I/O |
STX-I004 |
warning | Use stx.io for CSV/DataFrame I/O instead of pandas I/O functions |
STX-I005 |
warning | Use stx.io for array I/O instead of numpy save/load |
STX-I006 |
info | Use rngg (injected by @stx.session) for reproducible randomness |
STX-I007 |
warning | Use logger (injected by @stx.session) instead of logging module |
STX-PA001 |
warning | Absolute path in stx.io call — use relative paths for reproducibility |
STX-PA002 |
warning | open() detected — use stx.io.save()/stx.io.load() which includes auto-logging |
STX-PA003 |
info | os.makedirs()/mkdir() detected — stx.io.save() creates directories automatically |
STX-PA004 |
warning | os.chdir() detected — scripts should be run from project root |
STX-PA005 |
info | Path without ./ prefix in stx.io call — use ./ for explicit relative intent |
Additional rules are contributed by downstream packages via the scitex_linter.plugins entry point. Install a package to activate its rules automatically.
Documentation
📚 Full Documentation on Read the Docs
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 scitex_linter-0.3.0.tar.gz.
File metadata
- Download URL: scitex_linter-0.3.0.tar.gz
- Upload date:
- Size: 59.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
370df998950e2e02040835f390ea53ded5a540eae4bef44a31741edf4fd9cd60
|
|
| MD5 |
6e55e95c35233ad465b46c1317cbe76e
|
|
| BLAKE2b-256 |
53b308ed2843e14a40a576568e83d30a8fb4f346a8341b84349d763c25516d33
|
Provenance
The following attestation bundles were made for scitex_linter-0.3.0.tar.gz:
Publisher:
publish-pypi.yml on ywatanabe1989/scitex-linter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scitex_linter-0.3.0.tar.gz -
Subject digest:
370df998950e2e02040835f390ea53ded5a540eae4bef44a31741edf4fd9cd60 - Sigstore transparency entry: 1079809123
- Sigstore integration time:
-
Permalink:
ywatanabe1989/scitex-linter@bafb58190b88c2fba75d18d534b75751de146118 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/ywatanabe1989
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@bafb58190b88c2fba75d18d534b75751de146118 -
Trigger Event:
push
-
Statement type:
File details
Details for the file scitex_linter-0.3.0-py3-none-any.whl.
File metadata
- Download URL: scitex_linter-0.3.0-py3-none-any.whl
- Upload date:
- Size: 55.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa65341c872bb946d71afaf5f90fbdcd1fc9a2adf8605078998b788ad8deb6c7
|
|
| MD5 |
03b4e23aee4f0bb9dee3c45ff97236a3
|
|
| BLAKE2b-256 |
db4362994c70e1e9fd474fb5a6b6600c1e4ff7c2ec1570b706784ec73b917ced
|
Provenance
The following attestation bundles were made for scitex_linter-0.3.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on ywatanabe1989/scitex-linter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scitex_linter-0.3.0-py3-none-any.whl -
Subject digest:
aa65341c872bb946d71afaf5f90fbdcd1fc9a2adf8605078998b788ad8deb6c7 - Sigstore transparency entry: 1079809196
- Sigstore integration time:
-
Permalink:
ywatanabe1989/scitex-linter@bafb58190b88c2fba75d18d534b75751de146118 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/ywatanabe1989
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@bafb58190b88c2fba75d18d534b75751de146118 -
Trigger Event:
push
-
Statement type: