Readme
pytest-language-server 🔥
A blazingly fast Language Server Protocol (LSP) implementation for pytest, built with Rust, with
full support for fixture discovery, go to definition, code completion, find references, hover
documentation, diagnostics, and more!
Table of Contents
Features
Built with AI, maintained with care 🤖
This project was built with the help of AI coding agents, but carefully reviewed to ensure
correctness, reliability, security and performance. If you find any issues, please open an issue
or submit a pull request!
🎯 Go to Definition
Jump directly to fixture definitions from anywhere they're used:
Local fixtures in the same file
Fixtures in conftest.py files
Third-party fixtures from pytest plugins (pytest-mock, pytest-asyncio, etc.)
Respects pytest's fixture shadowing/priority rules
🔧 Go to Implementation
Jump to the yield statement in generator fixtures:
Generator fixtures : Navigates to where yield produces the fixture value
Teardown navigation : Useful for reviewing fixture cleanup logic
Non-generator fallback : Falls back to definition for simple return-based fixtures
Example:
@ pytest . fixture
def database ( ) :
conn = connect ( )
yield conn # <-- Go to Implementation jumps here
conn.close() # Teardown code after yield
🔗 Call Hierarchy
Explore fixture dependencies with Call Hierarchy support:
Incoming Calls : See which tests and fixtures depend on a fixture
Outgoing Calls : See which fixtures a fixture depends on
Works with your editor's "Show Call Hierarchy" command
Helps understand complex fixture dependency chains
@ pytest . fixture
def database ( ) : # <-- Call Hierarchy shows:
... # Incoming: test_query, test_insert (tests using this)
# Outgoing: connection (fixtures this depends on)
✨ Code Completion
Smart auto-completion for pytest fixtures:
Context-aware : Only triggers inside test functions and fixture functions
Hierarchy-respecting : Suggests fixtures based on pytest's priority rules (same file > conftest.py > third-party)
Rich information : Shows fixture source file and docstring
No duplicates : Automatically filters out shadowed fixtures
Works everywhere : Completions available in both function parameters and function bodies
Supports both sync and async functions
🔍 Find References
Find all usages of a fixture across your entire test suite:
Works from fixture definitions or usage sites
Character-position aware (distinguishes between fixture name and parameters)
Shows references in all test files
Correctly handles fixture overriding and hierarchies
LSP spec compliant : Always includes the current position in results
📚 Hover Documentation
View fixture information on hover:
Fixture signature
Source file location
Docstring (with proper formatting and dedenting)
Markdown support in docstrings
📑 Document Symbols
Navigate fixtures within a file using the document outline:
File outline view : See all fixtures defined in the current file (Cmd+Shift+O / Ctrl+Shift+O)
Breadcrumb navigation : Shows fixture hierarchy in editor breadcrumbs
Return type display : Shows fixture return types when available
Sorted by position : Fixtures appear in definition order
🔎 Workspace Symbols
Search for fixtures across your entire workspace:
Global search : Find any fixture by name (Cmd+T / Ctrl+T)
Fuzzy matching : Case-insensitive substring search
File context : Shows which file each fixture is defined in
Fast lookup : Instant results from in-memory fixture database
🔢 Code Lens
See fixture usage counts directly in your editor:
Usage count : Shows "N usages" above each fixture definition
Click to navigate : Clicking the lens shows all references (find-references integration)
Real-time updates : Counts update as you add/remove fixture usages
Local fixtures only : Only shows lenses for project fixtures, not third-party
🏷️ Inlay Hints
See fixture return types inline without leaving your code:
Type annotations : Shows return types next to fixture parameters (e.g., db: Database )
Explicit types only : Only displays hints when fixtures have explicit return type annotations
Generator support : Extracts yielded type from Generator[ T, None , None ] annotations
Non-intrusive : Hints appear as subtle inline decorations that don't modify your code
Example:
# With a fixture defined as:
@pytest.fixture
def database() -> Database:
return Database()
# In your test, you'll see:
def test_example(database): # Shows ": Database" after "database"
pass
💡 Code Actions (Quick Fixes)
One-click fixes for common pytest issues:
Add missing fixture parameters : Automatically add undeclared fixtures to function signatures
Smart insertion : Handles both empty and existing parameter lists
Editor integration : Works with any LSP-compatible editor's quick fix menu
LSP compliant : Full support for CodeActionKind:: QUICKFIX
⚠️ Diagnostics & Quick Fixes
Detect and fix common pytest fixture issues with intelligent code actions:
Fixture Scope Validation:
Detects when a broader-scoped fixture depends on a narrower-scoped fixture
Example: A session -scoped fixture cannot depend on a function -scoped fixture
Warnings show both the fixture's scope and its dependency's scope
Prevents hard-to-debug test failures from scope violations
Circular Dependency Detection:
Detects when fixtures form circular dependency chains (A → B → C → A)
Reports the full cycle path for easy debugging
Works across files (conftest.py hierarchies)
Scope mismatch example:
# ⚠️ Scope mismatch! session-scoped fixture depends on function-scoped
@pytest.fixture(scope="session")
def shared_db(temp_dir): # temp_dir is function-scoped
return Database(temp_dir)
@pytest.fixture # Default is function scope
def temp_dir(tmp_path):
return tmp_path / "test"
Undeclared Fixture Detection:
Detects when fixtures are used in function bodies but not declared as parameters
Line-aware scoping : Correctly handles local variables assigned later in the function
Hierarchy-aware : Only reports fixtures that are actually available in the current file's scope
Works in tests and fixtures : Detects undeclared usage in both test functions and fixture functions
Excludes built-in names (self , request ) and actual local variables
One-Click Quick Fixes:
Code actions to automatically add missing fixture parameters
Intelligent parameter insertion (handles both empty and existing parameter lists)
Works with both single-line and multi-line function signatures
Triggered directly from diagnostic warnings
Example:
@ pytest . fixture
def user_db ( ) :
return Database ( )
def test_user ( user_db ) : # ✅ user_db properly declared
user = user_db.get_user(1)
assert user.name == "Alice"
def test_broken(): # ⚠️ Warning: 'user_db' used but not declared
user = user_db.get_user(1) # 💡 Quick fix: Add 'user_db' fixture parameter
assert user.name == "Alice"
How to use quick fixes:
Place cursor on the warning squiggle
Trigger code actions menu (usually Cmd+. or Ctrl+. in most editors)
Select "Add 'fixture_name' fixture parameter"
The parameter is automatically added to your function signature
Built with Rust for maximum performance:
Fast workspace scanning with concurrent file processing
Efficient AST parsing using rustpython-parser
Lock-free data structures with DashMap
Minimal memory footprint
Installation
Choose your preferred installation method:
📦 PyPI (Recommended)
The easiest way to install for Python projects:
# Using uv (recommended)
uv tool install pytest-language-server
# Or with pip
pip install pytest-language-server
# Or with pipx (isolated environment)
pipx install pytest-language-server
🍺 Homebrew (macOS/Linux)
Install via Homebrew for system-wide availability:
brew install bellini666/tap/pytest-language-server
To add the tap first:
brew tap bellini666/tap https://github.com/bellini666/pytest-language-server
brew install pytest-language-server
🦀 Cargo (Rust)
Install from crates.io if you have Rust installed:
cargo install pytest-language-server
📥 Pre-built Binaries
Download pre-built binaries from the GitHub Releases page.
Available for:
Linux : x86_64, aarch64, armv7 (glibc and musl)
macOS : Intel and Apple Silicon
Windows : x64 and x86
🔨 From Source
Build from source for development or customization:
git clone https://github.com/bellini666/pytest-language-server
cd pytest-language-server
cargo build -- release
The binary will be at target/ release/ pytest- language- server .
Setup
Neovim
Add this to your config:
vim . lsp . config ( ' pytest_lsp' , {
cmd = { ' pytest-language-server' } ,
filetypes = { ' python' } ,
root_markers = { ' pyproject.toml' , ' setup.py' , ' setup.cfg' , ' pytest.ini' , ' .git' } ,
} )
vim . lsp . enable ( ' pytest_lsp' )
Zed
Install from the Zed Extensions Marketplace :
Open Zed
Open the command palette (Cmd+Shift+P / Ctrl+Shift+P)
Search for "zed: extensions"
Search for "pytest Language Server"
Click "Install"
The extension downloads platform-specific binaries from GitHub Releases. If you prefer to use your own installation (via pip, cargo, or brew), place pytest-language-server in your PATH.
After installing the extension, you need to enable the language server for Python files. Add the following to your Zed settings. json :
{
" languages" : {
" Python" : {
" language_servers" : [ " pyright" , " pytest-language-server" , " ..." ]
}
}
}
VS Code
The extension includes pre-built binaries - no separate installation required!
Install from the Visual Studio Marketplace :
Open VS Code
Go to Extensions (Cmd+Shift+X / Ctrl+Shift+X)
Search for "pytest Language Server"
Click "Install"
Works out of the box with zero configuration!
IntelliJ IDEA / PyCharm
The plugin includes pre-built binaries - no separate installation required!
Install from the JetBrains Marketplace :
Open PyCharm or IntelliJ IDEA
Go to Settings/Preferences → Plugins
Search for "pytest Language Server"
Click "Install"
Requires PyCharm 2024.2+ or IntelliJ IDEA 2024.2+ with Python plugin.
Other Editors
Any editor with LSP support can use pytest-language-server. Configure it to run the pytest-language-server command.
Configuration
pyproject.toml
Configure pytest-language-server via your project's pyproject.toml :
[ tool.pytest-language-server ]
# Glob patterns for files/directories to exclude from scanning
exclude = [ " build/**" , " dist/**" , " .tox/**" ]
# Disable specific diagnostics
# Valid codes: "undeclared-fixture", "scope-mismatch", "circular-dependency"
disabled_diagnostics = [ " undeclared-fixture" ]
# Additional directories to scan for fixtures (planned feature)
fixture_paths = [ " fixtures/" , " shared/fixtures/" ]
# Third-party plugins to skip when scanning venv (planned feature)
skip_plugins = [ " pytest-xdist" ]
Available Options:
Option
Type
Description
exclude
string[ ]
Glob patterns for paths to exclude from workspace scanning
disabled_diagnostics
string[ ]
Diagnostic codes to suppress
fixture_paths
string[ ]
Additional fixture directories (planned)
skip_plugins
string[ ]
Third-party plugins to skip (planned)
Diagnostic Codes:
undeclared-fixture - Fixture used in function body but not declared as parameter
scope-mismatch - Broader-scoped fixture depends on narrower-scoped fixture
circular-dependency - Circular fixture dependency detected
Logging
Control log verbosity with the RUST_LOG environment variable:
# Minimal logging (default)
RUST_LOG=warn pytest-language-server
# Info level
RUST_LOG=info pytest-language-server
# Debug level (verbose)
RUST_LOG=debug pytest-language-server
# Trace level (very verbose)
RUST_LOG=trace pytest-language-server
Logs are written to stderr, so they won't interfere with LSP communication.
Virtual Environment Detection
The server automatically detects your Python virtual environment:
Checks for . venv/ , venv/ , or env/ in your project root
Falls back to $ VIRTUAL_ENV environment variable
Scans third-party pytest plugins for fixtures
Code Actions / Quick Fixes
Code actions are automatically available on diagnostic warnings. If code actions don't appear in your editor:
Check LSP capabilities : Ensure your editor supports code actions (most modern editors do)
Enable debug logging : Use RUST_LOG = info to see if actions are being created
Verify diagnostics : Code actions only appear where there are warnings
Trigger manually : Use your editor's code action keybinding (Cmd+. / Ctrl+.)
CLI Commands
In addition to the LSP server mode, pytest-language-server provides useful command-line tools:
Fixtures List
View all fixtures in your test suite with a hierarchical tree view:
# List all fixtures
pytest-language-server fixtures list tests/
# Skip unused fixtures
pytest-language-server fixtures list tests/ --skip-unused
# Show only unused fixtures
pytest-language-server fixtures list tests/ --only-unused
The output includes:
Color-coded display : Files in cyan, directories in blue, used fixtures in green, unused in gray
Usage statistics : Shows how many times each fixture is used
Smart filtering : Hides files and directories with no matching fixtures
Hierarchical structure : Visualizes fixture organization across conftest.py files
Example output:
Fixtures tree for : / path/ to/ tests
conftest. py ( 7 fixtures)
├── another_fixture ( used 2 times)
├── cli_runner ( used 7 times)
├── database ( used 6 times)
├── generator_fixture ( used 1 time)
├── iterator_fixture ( unused)
├── sample_fixture ( used 7 times)
└── shared_resource ( used 5 times)
subdir/
└── conftest. py ( 4 fixtures)
├── cli_runner ( used 7 times)
├── database ( used 6 times)
├── local_fixture ( used 4 times)
└── sample_fixture ( used 7 times)
This command is useful for:
Auditing fixture usage across your test suite
Finding unused fixtures that can be removed
Understanding fixture organization and hierarchy
Documentation - visualizing available fixtures for developers
Fixtures Unused
Find unused fixtures in your test suite, with CI-friendly exit codes:
# List unused fixtures (text format)
pytest-language-server fixtures unused tests/
# JSON output for programmatic use
pytest-language-server fixtures unused tests/ --format json
Exit codes:
0 : All fixtures are used
1 : Unused fixtures found
Example text output:
Found 4 unused fixture ( s) :
• iterator_fixture in conftest. py
• auto_cleanup in utils/ conftest. py
• temp_dir in utils/ conftest. py
• temp_file in utils/ conftest. py
Tip: Remove unused fixtures or add tests that use them.
Example JSON output:
[
{ " file" : " conftest.py" , " fixture" : " iterator_fixture" } ,
{ " file" : " utils/conftest.py" , " fixture" : " auto_cleanup" } ,
{ " file" : " utils/conftest.py" , " fixture" : " temp_dir" } ,
{ " file" : " utils/conftest.py" , " fixture" : " temp_file" }
]
This command is ideal for:
CI/CD pipelines - fail builds when unused fixtures accumulate
Code cleanup - identify dead code in test infrastructure
Linting - integrate with pre-commit hooks or quality gates
Supported Fixture Patterns
Decorator Style
@ pytest . fixture
def my_fixture ( ) :
""" Fixture docstring."""
return 42
Assignment Style (pytest-mock)
mocker = pytest . fixture ( ) ( _mocker )
Async Fixtures
@ pytest . fixture
async def async_fixture ( ) :
return await some_async_operation ( )
Fixture Dependencies
@ pytest . fixture
def fixture_a ( ) :
return " a"
@ pytest . fixture
def fixture_b ( fixture_a ) : # Go to definition works on fixture_a
return fixture_a + "b"
@pytest.mark.usefixtures
@ pytest . mark . usefixtures ( " database" , " cache" )
class TestWithFixtures :
def test_something ( self ) :
pass # database and cache are available
@pytest.mark.parametrize with indirect
@ pytest . fixture
def user ( request ) :
return User ( name = request . param )
# All parameters treated as fixtures
@pytest.mark.parametrize("user", ["alice", "bob"], indirect=True)
def test_user(user):
pass
# Selective indirect fixtures
@pytest.mark.parametrize("user,value", [("alice", 1)], indirect=["user"])
def test_user_value(user, value):
pass
Fixture Priority Rules
pytest-language-server correctly implements pytest's fixture shadowing rules:
Same file : Fixtures defined in the same file have highest priority
Closest conftest.py : Searches parent directories for conftest.py files
Virtual environment : Third-party plugin fixtures
Fixture Overriding
The LSP correctly handles complex fixture overriding scenarios:
# conftest.py (parent)
@pytest.fixture
def cli_runner():
return "parent runner"
# tests/conftest.py (child)
@pytest.fixture
def cli_runner(cli_runner): # Overrides parent
return cli_runner # Uses parent
# tests/test_example.py
def test_example(cli_runner): # Uses child
pass
When using find-references:
Clicking on the function name def cli_runner ( ... ) shows references to the child fixture
Clicking on the parameter cli_runner ( cli_runner) shows references to the parent fixture
Character-position aware to distinguish between the two
Supported Third-Party Fixtures
Automatically discovers fixtures from 50+ popular pytest plugins , including:
Testing frameworks : pytest-mock, pytest-asyncio, pytest-bdd, pytest-cases
Web frameworks : pytest-flask, pytest-django, pytest-aiohttp, pytest-tornado, pytest-sanic, pytest-fastapi
HTTP clients : pytest-httpx
Databases : pytest-postgresql, pytest-mongodb, pytest-redis, pytest-mysql, pytest-elasticsearch
Infrastructure : pytest-docker, pytest-kubernetes, pytest-rabbitmq, pytest-celery
Browser testing : pytest-selenium, pytest-playwright, pytest-splinter
Performance : pytest-benchmark, pytest-timeout
Test data : pytest-factoryboy, pytest-freezegun, pytest-mimesis
And many more...
The server automatically scans your virtual environment for any pytest plugin and makes their fixtures available.
Architecture
Language : Rust 🦀
LSP Framework : tower-lsp-server
Parser : rustpython-parser
Concurrency : tokio async runtime
Data Structures : DashMap for lock-free concurrent access
Development
Prerequisites
Rust 1.85+ (2021 edition)
Python 3.10+ (for testing)
Building
cargo build -- release
Running Tests
cargo test
Logging During Development
RUST_LOG = debug cargo run
Security
Security is a priority. This project includes:
Automated dependency vulnerability scanning (cargo-audit)
License compliance checking (cargo-deny)
Daily security audits in CI/CD
Dependency review on pull requests
Pre-commit security hooks
See SECURITY.md for our security policy and how to report vulnerabilities.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Development Setup
Install pre-commit hooks:
pre-commit install
Run security checks locally:
cargo audit
cargo clippy
cargo test
License
MIT License - see LICENSE file for details.
Acknowledgments
Built with:
Special thanks to the pytest team for creating such an amazing testing framework.
Made with ❤️ and Rust. Blazingly fast 🔥
Built with AI assistance, maintained with care.