Skip to content

Commit 8cd6e04

Browse files
authored
Merge pull request pappasam#281 from pappasam/remove-pydantic
2 parents 143d6ce + 82ed904 commit 8cd6e04

File tree

7 files changed

+145
-234
lines changed

7 files changed

+145
-234
lines changed

jedi_language_server/initialization_options.py

Lines changed: 113 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,115 +4,165 @@
44
initialization options.
55
"""
66

7-
from typing import List, Optional, Pattern, Set
7+
import re
8+
import sys
9+
from dataclasses import dataclass, field, fields, is_dataclass
10+
from typing import Any, List, Optional, Pattern, Set
811

12+
from cattrs import Converter
13+
from cattrs.gen import make_dict_structure_fn, override
914
from lsprotocol.types import MarkupKind
10-
from pydantic import BaseModel, ConfigDict, Field
1115

1216
# pylint: disable=missing-class-docstring
1317
# pylint: disable=too-few-public-methods
1418

15-
16-
def snake_to_camel(string: str) -> str:
17-
"""Convert from snake_case to camelCase."""
18-
return "".join(
19-
word.capitalize() if idx > 0 else word
20-
for idx, word in enumerate(string.split("_"))
21-
)
19+
if sys.version_info >= (3, 10):
20+
# pylint: disable-next=unexpected-keyword-arg
21+
light_dataclass = dataclass(kw_only=True, eq=False, match_args=False)
22+
else:
23+
light_dataclass = dataclass(eq=False)
2224

2325

24-
class Model(BaseModel):
25-
model_config = ConfigDict(alias_generator=snake_to_camel)
26-
27-
28-
class CodeAction(Model):
26+
@light_dataclass
27+
class CodeAction:
2928
name_extract_variable: str = "jls_extract_var"
3029
name_extract_function: str = "jls_extract_def"
3130

3231

33-
class Completion(Model):
32+
@light_dataclass
33+
class Completion:
3434
disable_snippets: bool = False
3535
resolve_eagerly: bool = False
36-
ignore_patterns: List[Pattern[str]] = []
36+
ignore_patterns: List[Pattern[str]] = field(default_factory=list)
3737

3838

39-
class Diagnostics(Model):
39+
@light_dataclass
40+
class Diagnostics:
4041
enable: bool = True
4142
did_open: bool = True
4243
did_save: bool = True
4344
did_change: bool = True
4445

4546

46-
class HoverDisableOptions(Model):
47+
@light_dataclass
48+
class HoverDisableOptions:
4749
all: bool = False
48-
names: Set[str] = set()
49-
full_names: Set[str] = set()
50+
names: Set[str] = field(default_factory=set)
51+
full_names: Set[str] = field(default_factory=set)
5052

5153

52-
class HoverDisable(Model):
54+
@light_dataclass
55+
class HoverDisable:
5356
"""All Attributes have _ appended to avoid syntax conflicts.
5457
5558
For example, the keyword class would have required a special case.
5659
To get around this, I decided it's simpler to always assume an
5760
underscore at the end.
5861
"""
5962

60-
keyword_: HoverDisableOptions = Field(
61-
default=HoverDisableOptions(), alias="keyword"
62-
)
63-
module_: HoverDisableOptions = Field(
64-
default=HoverDisableOptions(), alias="module"
65-
)
66-
class_: HoverDisableOptions = Field(
67-
default=HoverDisableOptions(), alias="class"
68-
)
69-
instance_: HoverDisableOptions = Field(
70-
default=HoverDisableOptions(), alias="instance"
71-
)
72-
function_: HoverDisableOptions = Field(
73-
default=HoverDisableOptions(), alias="function"
74-
)
75-
param_: HoverDisableOptions = Field(
76-
default=HoverDisableOptions(), alias="param"
77-
)
78-
path_: HoverDisableOptions = Field(
79-
default=HoverDisableOptions(), alias="path"
80-
)
81-
property_: HoverDisableOptions = Field(
82-
default=HoverDisableOptions(), alias="property"
83-
)
84-
statement_: HoverDisableOptions = Field(
85-
default=HoverDisableOptions(), alias="statement"
63+
keyword_: HoverDisableOptions = field(default_factory=HoverDisableOptions)
64+
module_: HoverDisableOptions = field(default_factory=HoverDisableOptions)
65+
class_: HoverDisableOptions = field(default_factory=HoverDisableOptions)
66+
instance_: HoverDisableOptions = field(default_factory=HoverDisableOptions)
67+
function_: HoverDisableOptions = field(default_factory=HoverDisableOptions)
68+
param_: HoverDisableOptions = field(default_factory=HoverDisableOptions)
69+
path_: HoverDisableOptions = field(default_factory=HoverDisableOptions)
70+
property_: HoverDisableOptions = field(default_factory=HoverDisableOptions)
71+
statement_: HoverDisableOptions = field(
72+
default_factory=HoverDisableOptions
8673
)
8774

8875

89-
class Hover(Model):
76+
@light_dataclass
77+
class Hover:
9078
enable: bool = True
91-
disable: HoverDisable = HoverDisable()
79+
disable: HoverDisable = field(default_factory=HoverDisable)
9280

9381

94-
class JediSettings(Model):
95-
auto_import_modules: List[str] = []
82+
@light_dataclass
83+
class JediSettings:
84+
auto_import_modules: List[str] = field(default_factory=list)
9685
case_insensitive_completion: bool = True
9786
debug: bool = False
9887

9988

100-
class Symbols(Model):
101-
ignore_folders: List[str] = [".nox", ".tox", ".venv", "__pycache__"]
89+
@light_dataclass
90+
class Symbols:
91+
ignore_folders: List[str] = field(
92+
default_factory=lambda: [".nox", ".tox", ".venv", "__pycache__"]
93+
)
10294
max_symbols: int = 20
10395

10496

105-
class Workspace(Model):
97+
@light_dataclass
98+
class Workspace:
10699
environment_path: Optional[str] = None
107-
extra_paths: List[str] = []
108-
symbols: Symbols = Symbols()
100+
extra_paths: List[str] = field(default_factory=list)
101+
symbols: Symbols = field(default_factory=Symbols)
109102

110103

111-
class InitializationOptions(Model):
112-
code_action: CodeAction = CodeAction()
113-
completion: Completion = Completion()
114-
diagnostics: Diagnostics = Diagnostics()
115-
hover: Hover = Hover()
116-
jedi_settings: JediSettings = JediSettings()
104+
@light_dataclass
105+
class InitializationOptions:
106+
code_action: CodeAction = field(default_factory=CodeAction)
107+
completion: Completion = field(default_factory=Completion)
108+
diagnostics: Diagnostics = field(default_factory=Diagnostics)
109+
hover: Hover = field(default_factory=Hover)
110+
jedi_settings: JediSettings = field(default_factory=JediSettings)
117111
markup_kind_preferred: Optional[MarkupKind] = None
118-
workspace: Workspace = Workspace()
112+
workspace: Workspace = field(default_factory=Workspace)
113+
114+
115+
initialization_options_converter = Converter()
116+
117+
WEIRD_NAMES = {
118+
"keyword_": "keyword",
119+
"module_": "module",
120+
"class_": "class",
121+
"instance_": "instance",
122+
"function_": "function",
123+
"param_": "param",
124+
"path_": "path",
125+
"property_": "property",
126+
"statement_ ": "statement",
127+
}
128+
129+
130+
def convert_class_keys(string: str) -> str:
131+
"""Convert from snake_case to camelCase.
132+
133+
Also handles random special cases for keywords.
134+
"""
135+
if string in WEIRD_NAMES:
136+
return WEIRD_NAMES[string]
137+
return "".join(
138+
word.capitalize() if idx > 0 else word
139+
for idx, word in enumerate(string.split("_"))
140+
)
141+
142+
143+
def structure(cls: type) -> Any:
144+
"""Hook to convert names when marshalling initialization_options."""
145+
return make_dict_structure_fn(
146+
cls,
147+
initialization_options_converter,
148+
**{
149+
a.name: override(rename=convert_class_keys(a.name))
150+
for a in fields(cls)
151+
}
152+
)
153+
154+
155+
initialization_options_converter.register_structure_hook_factory(
156+
is_dataclass, structure
157+
)
158+
159+
160+
initialization_options_converter.register_structure_hook_factory(
161+
lambda x: x == Pattern[str],
162+
lambda _: lambda x, _: re.compile(x),
163+
)
164+
165+
initialization_options_converter.register_unstructure_hook_factory(
166+
lambda x: x == Pattern[str],
167+
lambda _: lambda x: x.pattern,
168+
)

jedi_language_server/server.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import itertools
1010
from typing import Any, List, Optional, Union
1111

12+
import cattrs
1213
from jedi import Project, __version__
1314
from jedi.api.refactoring import RefactoringError
1415
from lsprotocol.types import (
@@ -63,13 +64,15 @@
6364
WorkspaceEdit,
6465
WorkspaceSymbolParams,
6566
)
66-
from pydantic import ValidationError
6767
from pygls.capabilities import get_capability
6868
from pygls.protocol import LanguageServerProtocol, lsp_method
6969
from pygls.server import LanguageServer
7070

7171
from . import jedi_utils, pygls_utils, text_edit_utils
72-
from .initialization_options import InitializationOptions
72+
from .initialization_options import (
73+
InitializationOptions,
74+
initialization_options_converter,
75+
)
7376

7477

7578
class JediLanguageServerProtocol(LanguageServerProtocol):
@@ -84,13 +87,19 @@ def lsp_initialize(self, params: InitializeParams) -> InitializeResult:
8487
"""
8588
server: "JediLanguageServer" = self._server
8689
try:
87-
server.initialization_options = InitializationOptions.parse_obj(
88-
{}
89-
if params.initialization_options is None
90-
else params.initialization_options
90+
server.initialization_options = (
91+
initialization_options_converter.structure(
92+
{}
93+
if params.initialization_options is None
94+
else params.initialization_options,
95+
InitializationOptions,
96+
)
97+
)
98+
except cattrs.BaseValidationError as error:
99+
msg = (
100+
"Invalid InitializationOptions, using defaults:"
101+
f" {cattrs.transform_error(error)}"
91102
)
92-
except ValidationError as error:
93-
msg = f"Invalid InitializationOptions, using defaults: {error}"
94103
server.show_message(msg, msg_type=MessageType.Error)
95104
server.show_message_log(msg, msg_type=MessageType.Error)
96105
server.initialization_options = InitializationOptions()

mypy.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
[mypy]
2-
plugins = pydantic.mypy
32
strict = True
43
enable_error_code = ignore-without-code,redundant-expr,truthy-bool
54

0 commit comments

Comments
 (0)