Python Language Server Protocol implementation providing code intelligence features for Python development
—
Hierarchical configuration system supporting workspace-level, plugin-specific, and document-specific settings with integration to external configuration files. Provides comprehensive configuration management for server behavior and plugin customization.
Main configuration management class with hierarchical settings resolution.
class Config:
def __init__(self, root_uri, init_opts, process_id, capabilities):
"""
Initialize configuration.
Parameters:
- root_uri: str, workspace root URI
- init_opts: dict, initialization options from client
- process_id: int, client process ID
- capabilities: dict, client capabilities
"""
@property
def disabled_plugins(self):
"""
Get list of disabled plugin names.
Returns:
list: Disabled plugin names
"""
@property
def plugin_manager(self):
"""
Get Pluggy plugin manager.
Returns:
PluginManager: Plugin manager instance
"""
@property
def init_opts(self):
"""
Get initialization options.
Returns:
dict: Client initialization options
"""
@property
def root_uri(self):
"""
Get workspace root URI.
Returns:
str: Root URI
"""
@property
def process_id(self):
"""
Get client process ID.
Returns:
int: Process ID
"""
@property
def capabilities(self):
"""
Get client capabilities.
Returns:
dict: Client capabilities
"""
def settings(self, document_path=None):
"""
Get merged settings for document or workspace.
Parameters:
- document_path: str, optional document path for document-specific settings
Returns:
dict: Merged configuration settings
"""
def find_parents(self, path, names):
"""
Find parent configuration files.
Parameters:
- path: str, starting path
- names: list, configuration file names to find
Returns:
list: Found configuration file paths
"""
def plugin_settings(self, plugin, document_path=None):
"""
Get plugin-specific settings.
Parameters:
- plugin: str, plugin name
- document_path: str, optional document path
Returns:
dict: Plugin configuration
"""
def update(self, settings):
"""
Update LSP settings.
Parameters:
- settings: dict, new settings to merge
"""Configuration resolution follows this hierarchy (later sources override earlier):
DEFAULT_CONFIG_SOURCES = ["pycodestyle"] # Default configuration sources
# Configuration file names searched
CONFIG_FILE_NAMES = [
"pycodestyle.cfg",
"setup.cfg",
"tox.ini",
".flake8"
]Enhanced Pluggy plugin manager with error handling.
class PluginManager(pluggy.PluginManager):
"""
Extended PluginManager with enhanced error handling.
Wraps hook execution to provide better error reporting.
"""Top-level server settings structure.
# Server settings schema
SERVER_SETTINGS = {
"pylsp": {
"type": "object",
"properties": {
"configurationSources": {
"type": "array",
"items": {"type": "string"},
"default": ["pycodestyle"],
"description": "Configuration sources to use"
},
"plugins": {
"type": "object",
"description": "Plugin-specific settings"
},
"rope": {
"type": "object",
"properties": {
"extensionModules": {
"type": "array",
"items": {"type": "string"},
"description": "Rope extension modules"
},
"ropeFolder": {
"type": "string",
"description": "Rope project folder"
}
}
}
}
}
}Standard plugin configuration patterns.
# Standard plugin settings pattern
PLUGIN_SETTINGS_SCHEMA = {
"enabled": {
"type": "boolean",
"default": True,
"description": "Enable/disable plugin"
},
"exclude": {
"type": "array",
"items": {"type": "string"},
"description": "Files/patterns to exclude"
},
"filename": {
"type": "array",
"items": {"type": "string"},
"description": "Files/patterns to include"
}
}
# Linter-specific settings
LINTER_SETTINGS_SCHEMA = {
"ignore": {
"type": "array",
"items": {"type": "string"},
"description": "Error codes to ignore"
},
"select": {
"type": "array",
"items": {"type": "string"},
"description": "Error codes to select"
},
"maxLineLength": {
"type": "integer",
"description": "Maximum line length"
}
}
# Formatter-specific settings
FORMATTER_SETTINGS_SCHEMA = {
"args": {
"type": "array",
"items": {"type": "string"},
"description": "Additional formatter arguments"
}
}Settings for Jedi-based language features.
JEDI_SETTINGS = {
"jedi_completion": {
"enabled": True,
"include_params": True,
"include_class_objects": True,
"fuzzy": False,
"eager": False,
"resolve_at_most": 25,
"cache_for": ["pandas", "numpy", "tensorflow", "matplotlib"]
},
"jedi_definition": {
"enabled": True,
"follow_imports": True,
"follow_builtin_imports": True
},
"jedi_hover": {
"enabled": True
},
"jedi_references": {
"enabled": True
},
"jedi_signature_help": {
"enabled": True
},
"jedi_symbols": {
"enabled": True,
"all_scopes": True,
"include_import_symbols": True
}
}Configuration for built-in linters.
LINTER_SETTINGS = {
"pyflakes": {
"enabled": True
},
"pycodestyle": {
"enabled": True,
"ignore": [],
"select": [],
"filename": [],
"exclude": [],
"hangClosing": False,
"maxLineLength": 79
},
"mccabe": {
"enabled": True,
"threshold": 15
},
"pydocstyle": {
"enabled": False,
"ignore": [],
"select": [],
"convention": None,
"addIgnore": [],
"addSelect": [],
"match": "(?!test_).*\\.py",
"matchDir": "[^\\.].*"
},
"flake8": {
"enabled": False,
"ignore": [],
"select": [],
"filename": [],
"exclude": [],
"maxLineLength": 79,
"hangClosing": False,
"args": []
},
"pylint": {
"enabled": False,
"args": [],
"executable": "pylint"
}
}Configuration for code formatters.
FORMATTER_SETTINGS = {
"autopep8": {
"enabled": True,
"args": []
},
"yapf": {
"enabled": True,
"args": []
},
"black": {
"enabled": True,
"line_length": 88,
"cache_config": True
}
}Configuration for Rope integration.
ROPE_SETTINGS = {
"rope_completion": {
"enabled": True,
"eager": False
},
"rope_autoimport": {
"enabled": True,
"completions": {
"enabled": True
},
"code_actions": {
"enabled": True
},
"memory": False
}
}from pylsp.config.config import Config
# Initialize configuration
config = Config(
root_uri="file:///project",
init_opts={"pylsp": {"plugins": {"pyflakes": {"enabled": True}}}},
process_id=1234,
capabilities={"textDocument": {"completion": {"completionItem": {"snippetSupport": True}}}}
)
# Get workspace settings
settings = config.settings()
print(settings["pylsp"]["plugins"]["pyflakes"]["enabled"]) # True
# Get document-specific settings
doc_settings = config.settings("/project/src/main.py")
# Get plugin settings
pyflakes_settings = config.plugin_settings("pyflakes", "/project/src/main.py")# Configure plugin settings
config.update({
"pylsp": {
"plugins": {
"pycodestyle": {
"enabled": True,
"ignore": ["E203", "W503"],
"maxLineLength": 88
},
"flake8": {
"enabled": False
},
"jedi_completion": {
"enabled": True,
"fuzzy": True,
"eager": True
}
}
}
})
# Access updated settings
jedi_settings = config.plugin_settings("jedi_completion")
print(jedi_settings["fuzzy"]) # True# Find configuration files
config_files = config.find_parents("/project/src/main.py", [
"setup.cfg",
"pyproject.toml",
".flake8"
])
print(config_files) # ['/project/setup.cfg', '/project/.flake8']# Update configuration at runtime
config.update({
"pylsp": {
"configurationSources": ["flake8"],
"plugins": {
"pycodestyle": {"enabled": False},
"flake8": {"enabled": True}
}
}
})
# Configuration changes take effect immediately
settings = config.settings()
print(settings["pylsp"]["plugins"]["flake8"]["enabled"]) # True# Define custom plugin with settings
@hookimpl
def pylsp_settings(config):
return {
"plugins": {
"my_custom_plugin": {
"type": "object",
"properties": {
"enabled": {"type": "boolean", "default": True},
"severity": {
"type": "string",
"enum": ["error", "warning", "info"],
"default": "warning"
},
"patterns": {
"type": "array",
"items": {"type": "string"},
"default": ["*.py"]
}
}
}
}
}
# Use custom plugin settings
@hookimpl
def pylsp_lint(config, workspace, document, is_saved):
settings = config.plugin_settings("my_custom_plugin", document.path)
if not settings.get("enabled", True):
return []
severity_map = {"error": 1, "warning": 2, "info": 3}
severity = severity_map.get(settings.get("severity", "warning"), 2)
# Use settings in plugin logic
patterns = settings.get("patterns", ["*.py"])
# ... plugin implementation# Configuration hierarchy example:
# 1. Default plugin settings
# 2. User home directory config
# 3. Workspace settings (init_opts)
# 4. Project config files (setup.cfg, etc.)
# 5. Document-specific overrides
# Each level can override previous levels
config.update({
"pylsp": {
"plugins": {
"pycodestyle": {
"enabled": True, # Workspace level
"maxLineLength": 100 # Override default 79
}
}
}
})
# setup.cfg in project root might further override:
# [pycodestyle]
# max-line-length = 120
# ignore = E203,W503
# Final resolved settings will have maxLineLength=120
final_settings = config.settings("/project/src/module.py")Install with Tessl CLI
npx tessl i tessl/pypi-python-lsp-server