Skip to content

Commit 45db215

Browse files
committed
enhance pyrefly config support
fix type errors in lsp config override bump min python to 3.12+
1 parent c0634d0 commit 45db215

10 files changed

Lines changed: 358 additions & 159 deletions

File tree

.github/workflows/python-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
python-version: ["3.11", "3.12", "3.13"]
19+
python-version: ["3.12", "3.13", "3.14"]
2020

2121
steps:
2222
- uses: actions/checkout@v4

CLAUDE.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ This is a zero-dependency Python library providing typed LSP (Language Server Pr
6363

6464
**Pyrefly Integration (`lsp_types/pyrefly/`)**
6565
- `PyreflyBackend`: Backend implementation for Pyrefly LSP server (Facebook's Rust-based type checker)
66-
- `config_schema.py`: Auto-generated Pyrefly configuration types
66+
- `config_schema.py`: Pyrefly configuration types (TypedDict with known fields)
6767
- `session.py`: Factory functions and backward-compatible wrappers
6868
- **Key Design**: Uses consolidated `Session` class with `PyreflyBackend` for specialization
69+
- **Config Flexibility**: Supports arbitrary configuration fields via TOML serialization (using `tomli-w`)
6970

7071
### Type Generation Pipeline
7172

@@ -99,17 +100,21 @@ This is a zero-dependency Python library providing typed LSP (Language Server Pr
99100

100101
### Dependencies
101102

102-
**Runtime:** Zero dependencies (core design goal)
103+
**Runtime:**
104+
- `tomli-w>=1.0.0` - TOML writing support for Pyrefly configuration serialization
105+
103106
**Development:** uv-managed dependencies in `pyproject.toml`
104107
- `pytest` with async support for testing
105108
- `datamodel-code-generator` for type generation
106109
- `httpx` for schema downloading
107110

111+
**Note:** Previously a zero-dependency library. Added `tomli-w` to support arbitrary configuration fields in Pyrefly backend.
112+
108113
### Important Notes
109114

110115
- Always prefix test commands with `uv run`
111116
- Pool tests require `pyright-langserver` and/or `pyrefly` binaries available in PATH
112-
- Type generation requires Python 3.11+ for modern TypedDict features
117+
- Type generation requires Python 3.12+ for modern TypedDict features
113118
- Generated types should not be manually edited - regenerate from schemas
114119

115120
### Architecture Design Patterns

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ generate-lsp-schema:
3030
--input ./assets/lsprotocol/lsp.schema.json \
3131
--output ./assets/scripts/lsp_schema.py \
3232
--output-model-type "typing.TypedDict" \
33-
--target-python-version "3.11" \
33+
--target-python-version "3.12" \
3434
--input-file-type "jsonschema" \
3535
--use-field-description \
3636
--use-schema-description \
@@ -44,7 +44,7 @@ generate-pyright-schema:
4444
--input ./assets/lsps/pyright.schema.json \
4545
--output ./lsp_types/pyright/config_schema.py \
4646
--output-model-type "typing.TypedDict" \
47-
--target-python-version "3.11" \
47+
--target-python-version "3.12" \
4848
--input-file-type "jsonschema" \
4949
--use-field-description \
5050
--use-schema-description \

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# LSP Types
22

33
[![PyPI version](https://badge.fury.io/py/lsp-types.svg)](https://badge.fury.io/py/lsp-types)
4-
[![Python](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
4+
[![Python](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)
55
[![Tests](https://github.com/Mazyod/lsp-python-types/actions/workflows/python-tests.yml/badge.svg)](https://github.com/Mazyod/lsp-python-types/actions/workflows/python-tests.yml)
66
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
77

@@ -87,7 +87,7 @@ def greet(name: str) -> str:
8787

8888
## Development
8989

90-
- Requires Python 3.11+.
90+
- Requires Python 3.12+.
9191
- Requires `uv` for dev dependencies.
9292

9393
Generate latest types in one go:

lsp_types/pyrefly/backend.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from pathlib import Path
44

5+
import tomli_w
6+
57
import lsp_types
68
from lsp_types import types
79
from lsp_types.process import ProcessLaunchInfo
@@ -14,21 +16,18 @@ class PyreflyBackend(LSPBackend):
1416
"""Pyrefly-specific LSP backend implementation"""
1517

1618
def write_config(self, base_path: Path, options: PyreflyConfig) -> None:
17-
"""Write pyrefly.toml configuration file"""
18-
config_path = base_path / "pyrefly.toml"
19+
"""
20+
Write pyrefly.toml configuration file.
1921
20-
# Convert options to TOML format (basic implementation)
21-
# Note: Pyrefly's config format is still evolving, so we keep it minimal
22-
toml_content = ""
23-
if options.get("verbose"):
24-
toml_content += "verbose = true\n"
25-
if "threads" in options and options["threads"] is not None:
26-
toml_content += f"threads = {options['threads']}\n"
27-
if "color" in options:
28-
toml_content += f'color = "{options["color"]}"\n'
29-
if "indexing_mode" in options:
30-
toml_content += f'indexing-mode = "{options["indexing_mode"]}"\n'
22+
Accepts any mapping (including PyreflyConfig TypedDict and plain dicts
23+
with arbitrary fields). Field names are converted from snake_case to
24+
kebab-case to match Pyrefly's official TOML format.
25+
"""
26+
# Convert snake_case keys to kebab-case for TOML file
27+
kebab_options = {key.replace("_", "-"): value for key, value in options.items()}
3128

29+
config_path = base_path / "pyrefly.toml"
30+
toml_content = tomli_w.dumps(kebab_options)
3231
config_path.write_text(toml_content)
3332

3433
def create_process_launch_info(

lsp_types/pyrefly/config_schema.py

Lines changed: 131 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Pyrefly configuration schema
2-
# Based on CLI options documented in PYREFLY_GUIDE.md
3-
# Note: Pyrefly configuration is still evolving, this is a minimal implementation
2+
# Based on official Pyrefly documentation: https://pyrefly.org/en/docs/configuration/
3+
# CLI reference: https://github.com/facebook/pyrefly
4+
#
5+
# Note: Field names use snake_case (Python convention) but are automatically
6+
# converted to kebab-case when written to pyrefly.toml (official format).
47

58
from __future__ import annotations
69

@@ -9,28 +12,141 @@
912
# Basic configuration options based on Pyrefly CLI
1013
IndexingMode = Literal["none", "lazy-non-blocking-background", "lazy-blocking"]
1114

15+
# Type checking behavior options
16+
UntypedDefBehavior = Literal[
17+
"check-and-infer-return-type",
18+
"check-and-infer-return-any",
19+
"skip-and-infer-return-any",
20+
]
21+
22+
# Error severity configuration (error-code -> enabled/disabled)
23+
ErrorConfig = dict[str, bool]
24+
1225

1326
class Model(TypedDict):
1427
"""
1528
Pyrefly Configuration Schema
1629
17-
Based on available CLI options and environment variables.
18-
Note: Pyrefly's configuration format is still evolving.
30+
Comprehensive type definitions for all Pyrefly configuration options.
31+
Field names use snake_case following Python conventions. Pyrefly accepts
32+
both snake_case and kebab-case in TOML configuration files.
33+
34+
All fields are NotRequired for maximum flexibility. For arbitrary fields
35+
not yet in this schema:
36+
- Use cast(dict, config) to add extra keys
37+
- Or pass plain dict to write_config (accepts Mapping[str, Any])
38+
39+
Official Documentation: https://pyrefly.org/en/docs/configuration/
1940
"""
2041

21-
# Core options
42+
# ========================================================================
43+
# CORE OPTIONS (CLI-compatible)
44+
# ========================================================================
45+
2246
verbose: NotRequired[bool]
23-
threads: NotRequired[int] # 0 = auto, 1 = sequential, higher = parallel
47+
"""Enable detailed logging output"""
48+
49+
threads: NotRequired[int]
50+
"""Thread count (0=auto, 1=sequential, >1=parallel)"""
51+
2452
color: NotRequired[Literal["auto", "always", "never"]]
53+
"""Control colored output in terminal"""
54+
55+
# ========================================================================
56+
# LSP SERVER OPTIONS
57+
# ========================================================================
58+
59+
indexing_mode: NotRequired[IndexingMode]
60+
"""Indexing strategy for LSP server (default: lazy-non-blocking-background)"""
61+
62+
disable_type_errors_in_ide: NotRequired[bool]
63+
"""Hide type errors when running in IDE/language server mode"""
64+
65+
# ========================================================================
66+
# FILE SELECTION
67+
# ========================================================================
68+
69+
project_includes: NotRequired[list[str]]
70+
"""Glob patterns for files to type check (default: ["**/*.py*"])"""
71+
72+
project_excludes: NotRequired[list[str]]
73+
"""Glob patterns to exclude from type checking"""
74+
75+
disable_project_excludes_heuristics: NotRequired[bool]
76+
"""Disable automatic exclusion patterns (allows custom specification)"""
77+
78+
use_ignore_files: NotRequired[bool]
79+
"""Use .gitignore, .ignore, .git/info/exclude for exclusions (default: true)"""
80+
81+
# ========================================================================
82+
# PYTHON ENVIRONMENT (User-requested: search_path, python_version)
83+
# ========================================================================
84+
85+
search_path: NotRequired[list[str]]
86+
"""Directories where imports are resolved from (USER REQUESTED)"""
87+
88+
disable_search_path_heuristics: NotRequired[bool]
89+
"""Prevent automatic search path detection"""
90+
91+
site_package_path: NotRequired[list[str]]
92+
"""Third-party package directories for import resolution"""
93+
94+
python_version: NotRequired[str]
95+
"""Python version for sys.version checks, e.g. "3.13.0" (USER REQUESTED)"""
96+
97+
python_platform: NotRequired[str]
98+
"""Platform for sys.platform checks, e.g. "linux", "darwin", "win32" """
99+
100+
conda_environment: NotRequired[str]
101+
"""Conda environment name for querying Python configuration"""
102+
103+
python_interpreter_path: NotRequired[str]
104+
"""Path to Python executable for environment detection"""
105+
106+
fallback_python_interpreter_name: NotRequired[str]
107+
"""Interpreter name on $PATH for automatic discovery (default: "python3")"""
108+
109+
skip_interpreter_query: NotRequired[bool]
110+
"""Skip querying Python interpreter for environment setup"""
111+
112+
# ========================================================================
113+
# TYPE CHECKING BEHAVIOR
114+
# ========================================================================
115+
116+
typeshed_path: NotRequired[str]
117+
"""Override bundled typeshed with custom path"""
118+
119+
untyped_def_behavior: NotRequired[UntypedDefBehavior]
120+
"""How to handle untyped function definitions (default: check-and-infer-return-type)"""
121+
122+
infer_with_first_use: NotRequired[bool]
123+
"""Infer container types from first usage patterns (default: true)"""
124+
125+
ignore_errors_in_generated_code: NotRequired[bool]
126+
"""Skip type checking for files containing @generated marker"""
127+
128+
permissive_ignores: NotRequired[bool]
129+
"""Respect ignore annotations from non-Pyrefly tools (e.g. # type: ignore)"""
130+
131+
enabled_ignores: NotRequired[list[str]]
132+
"""Tool ignore directives to recognize (default: ["type", "pyrefly"])"""
133+
134+
# ========================================================================
135+
# IMPORT HANDLING
136+
# ========================================================================
137+
138+
replace_imports_with_any: NotRequired[list[str]]
139+
"""Module globs to unconditionally replace with typing.Any"""
140+
141+
ignore_missing_imports: NotRequired[list[str]]
142+
"""Module globs to replace with typing.Any when not found"""
25143

26-
# LSP server options
27-
indexing_mode: NotRequired[IndexingMode] # Indexing strategy for LSP server
144+
ignore_missing_source: NotRequired[bool]
145+
"""Ignore missing source packages when only type stubs are available"""
28146

29-
# File inclusion/exclusion (basic patterns)
30-
include: NotRequired[list[str]]
31-
exclude: NotRequired[list[str]]
147+
# ========================================================================
148+
# ERROR CONFIGURATION
149+
# ========================================================================
32150

33-
# Environment variables that can be configured
34-
pyrefly_threads: NotRequired[int]
35-
pyrefly_color: NotRequired[str]
36-
pyrefly_verbose: NotRequired[bool]
151+
errors: NotRequired[ErrorConfig]
152+
"""Error severity configuration: {"error-code": bool, ...}"""

lsp_types/session.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
from .process import LSPProcess, ProcessLaunchInfo
1010

1111

12-
class LSPBackend(t.Protocol):
12+
class LSPBackend[TConfig: t.Mapping](t.Protocol):
1313
"""Protocol defining backend-specific LSP operations"""
1414

15-
def write_config(self, base_path: Path, options: t.Mapping) -> None:
15+
def write_config(self, base_path: Path, options: TConfig) -> None:
1616
"""Write backend-specific configuration file"""
1717
...
1818

1919
def create_process_launch_info(
20-
self, base_path: Path, options: t.Mapping
20+
self, base_path: Path, options: TConfig
2121
) -> ProcessLaunchInfo:
2222
"""Create process launch info for the LSP server"""
2323
...
@@ -27,7 +27,7 @@ def get_lsp_capabilities(self) -> types.ClientCapabilities:
2727
...
2828

2929
def get_workspace_settings(
30-
self, options: t.Mapping
30+
self, options: TConfig
3131
) -> types.DidChangeConfigurationParams:
3232
"""Get workspace settings for didChangeConfiguration"""
3333
...

pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ name = "lsp-types"
33
version = "0.10.0"
44
description = "Zero-dependency Python library for Language Server Protocol types"
55
authors = [{ name = "Mazyad Alabduljaleel", email = "mazjaleel@gmail.com" }]
6-
requires-python = ">=3.11"
6+
requires-python = ">=3.12"
77
readme = "README.md"
88
license = "MIT"
9+
dependencies = [
10+
"tomli-w>=1.0.0", # TOML writing support for Pyrefly config
11+
]
912
keywords = [
1013
"lsp",
1114
"language-server-protocol",
@@ -18,9 +21,9 @@ classifiers = [
1821
"Intended Audience :: Developers",
1922
"License :: OSI Approved :: MIT License",
2023
"Programming Language :: Python :: 3",
21-
"Programming Language :: Python :: 3.11",
2224
"Programming Language :: Python :: 3.12",
2325
"Programming Language :: Python :: 3.13",
26+
"Programming Language :: Python :: 3.14",
2427
"Topic :: Software Development :: Libraries :: Python Modules",
2528
"Topic :: Text Editors :: Integrated Development Environments (IDE)",
2629
"Typing :: Typed",

0 commit comments

Comments
 (0)