Migrating to v1.0

The most notable change of the v1.0 release of pygls is the removal of its hand written LSP type and method definitions in favour of relying on the types provided by the lsprotocol library which are automatically generated from the LSP specification. As as side effect this has also meant the removal of pydantic as a dependency, since lsprotocol uses attrs and cattrs for serialisation and validation.

This guide outlines how to adapt an existing server to the breaking changes introduced in this release.

Known Migrations

You may find insight and inspiration from these projects that have already successfully migrated to v1:

Updating Imports

The pygls.lsp.methods and pygls.lsp.types modules no longer exist. Instead, all types and method names should now be imported from the lsprotocol.types module.

Additionally, the following types and constants have been renamed.

pygls lsprotocol
CODE_ACTION TEXT_DOCUMENT_CODE_ACTION
CODE_LENS TEXT_DOCUMENT_CODE_LENS
COLOR_PRESENTATION TEXT_DOCUMENT_COLOR_PRESENTATION
COMPLETION TEXT_DOCUMENT_COMPLETION
DECLARATION TEXT_DOCUMENT_DECLARATION
DEFINITION TEXT_DOCUMENT_DEFINITION
DOCUMENT_COLOR TEXT_DOCUMENT_DOCUMENT_COLOR
DOCUMENT_HIGHLIGHT TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT
DOCUMENT_LINK TEXT_DOCUMENT_DOCUMENT_LINK
DOCUMENT_SYMBOL TEXT_DOCUMENT_DOCUMENT_SYMBOL
FOLDING_RANGE TEXT_DOCUMENT_FOLDING_RANGE
FORMATTING TEXT_DOCUMENT_FORMATTING
HOVER TEXT_DOCUMENT_HOVER
IMPLEMENTATION TEXT_DOCUMENT_IMPLEMENTATION
LOG_TRACE_NOTIFICATION LOG_TRACE
ON_TYPE_FORMATTING TEXT_DOCUMENT_ON_TYPE_FORMATTING
PREPARE_RENAME TEXT_DOCUMENT_PREPARE_RENAME
PROGRESS_NOTIFICATION PROGRESS
RANGE_FORMATTING TEXT_DOCUMENT_RANGE_FORMATTING
REFERENCES TEXT_DOCUMENT_REFERENCES
RENAME TEXT_DOCUMENT_RENAME
SELECTION_RANGE TEXT_DOCUMENT_SELECTION_RANGE
SET_TRACE_NOTIFICATION SET_TRACE
SIGNATURE_HELP TEXT_DOCUMENT_SIGNATURE_HELP
TEXT_DOCUMENT_CALL_HIERARCHY_INCOMING_CALLS CALL_HIERARCHY_INCOMING_CALLS
TEXT_DOCUMENT_CALL_HIERARCHY_OUTGOING_CALLS CALL_HIERARCHY_OUTGOING_CALLS
TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY
TYPE_DEFINITION TEXT_DOCUMENT_TYPE_DEFINITION
WORKSPACE_FOLDERS WORKSPACE_WORKSPACE_FOLDERS
ApplyWorkspaceEditResponse ApplyWorkspaceEditResult
ClientInfo InitializeParamsClientInfoType
CodeActionDisabled CodeActionDisabledType
CodeActionLiteralSupportActionKindClientCapabilities CodeActionClientCapabilitiesCodeActionLiteralSupportTypeCodeActionKindType
CodeActionLiteralSupportClientCapabilities CodeActionClientCapabilitiesCodeActionLiteralSupportType
CompletionItemClientCapabilities CompletionClientCapabilitiesCompletionItemType
CompletionItemKindClientCapabilities CompletionClientCapabilitiesCompletionItemKindType
CompletionTagSupportClientCapabilities CompletionClientCapabilitiesCompletionItemTypeTagSupportType
DocumentSymbolCapabilitiesTagSupport DocumentSymbolClientCapabilitiesTagSupportType
InsertTextModeSupportClientCapabilities CompletionClientCapabilitiesCompletionItemTypeInsertTextModeSupportType
MarkedStringType MarkedString
MarkedString MarkedString_Type1
PrepareRename PrepareRenameResult_Type1
PublishDiagnosticsTagSupportClientCapabilities PublishDiagnosticsClientCapabilitiesTagSupportType
ResolveSupportClientCapabilities CodeActionClientCapabilitiesResolveSupportType
SemanticTokensRequestsFull SemanticTokensRegistrationOptionsFullType1
SemanticTokensRequests SemanticTokensClientCapabilitiesRequestsType
ServerInfo InitializeResultServerInfoType
ShowMessageRequestActionItem ShowMessageRequestClientCapabilitiesMessageActionItemType
SignatureHelpInformationClientCapabilities SignatureHelpClientCapabilitiesSignatureInformationType
SignatureHelpInformationParameterInformationClientCapabilities SignatureHelpClientCapabilitiesSignatureInformationTypeParameterInformationType
TextDocumentContentChangeEvent TextDocumentContentChangeEvent_Type1
TextDocumentContentChangeTextEvent TextDocumentContentChangeEvent_Type2
TextDocumentSyncOptionsServerCapabilities TextDocumentSyncOptions
Trace TraceValues
URI str
WorkspaceCapabilitiesSymbolKind WorkspaceSymbolClientCapabilitiesSymbolKindType
WorkspaceCapabilitiesTagSupport WorkspaceSymbolClientCapabilitiesTagSupportType
WorkspaceFileOperationsServerCapabilities FileOperationOptions
WorkspaceServerCapabilities ServerCapabilitiesWorkspaceType

Custom Models

One of the most obvious changes is the switch to attrs and cattrs for serialization and deserialisation. This means that any custom models used by your language server will need to be converted to an attrs style class.

# Before
from pydantic import BaseModel, Field

class ExampleConfig(BaseModel):
    build_dir: Optional[str] = Field(None, alias="buildDir")

    builder_name: str = Field("html", alias="builderName")

    conf_dir: Optional[str] = Field(None, alias="confDir")
# After
import attrs

@attrs.define
class ExampleConfig:
    build_dir: Optional[str] = attrs.field(default=None)

    builder_name: str = attrs.field(default="html")

    conf_dir: Optional[str] = attrs.field(default=None)

Pygls provides a default converter that it will use when converting your models to/from JSON, which should be sufficient for most scenarios.

>>> from pygls.protocol import default_converter
>>> converter = default_converter()

>>> config = ExampleConfig(builder_name='epub', conf_dir='/path/to/conf')
>>> converter.unstructure(config)
{'builderName': 'epub', 'confDir': '/path/to/conf'}   # Note how snake_case is converted to camelCase

>>> converter.structure({'builderName': 'epub', 'confDir': '/path/to/conf'}, ExampleConfig)
ExampleConfig(build_dir=None, builder_name='epub', conf_dir='/path/to/conf')

However, depending on the complexity of your type definitions you may find the default converter fail to parse some of your types.

>>> from typing import Literal, Union

>>> @attrs.define
... class ExampleConfig:
...     num_jobs: Union[Literal["auto"], int] = attrs.field(default='auto')
...

>>> converter.structure({'numJobs': 'auto'}, ExampleConfig)
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 1, in <module>
  |   File "/.../python3.10/site-packages/cattrs/converters.py", li
ne 309, in structure
  |     return self._structure_func.dispatch(cl)(obj, cl)
  |   File "<cattrs generated structure __main__.ExampleConfig-2>", line 10, in structure_ExampleConfig
  |     if errors: raise __c_cve('While structuring ' + 'ExampleConfig', errors, __cl)
  | cattrs.errors.ClassValidationError: While structuring ExampleConfig (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "<cattrs generated structure __main__.ExampleConfig-2>", line 6, in structure_ExampleConfig
    |     res['num_jobs'] = __c_structure_num_jobs(o['numJobs'], __c_type_num_jobs)
    |   File "/.../python3.10/site-packages/cattrs/converters.py",
line 377, in _structure_error
    |     raise StructureHandlerNotFoundError(msg, type_=cl)
    | cattrs.errors.StructureHandlerNotFoundError: Unsupported type: typing.Union[typing.Literal['auto'], int].
 Register a structure hook for it.
    | Structuring class ExampleConfig @ attribute num_jobs
    +------------------------------------

In which case you can extend the converter provided by pygls with your own structure hooks

from pygls.protocol import default_converter

def custom_converter():
    converter = default_converter()
    converter.register_structure_hook(Union[Literal['auto', int], lambda obj, _: obj)

    return converter

You can then override the default converter used by pygls when constructing your language server instance

server = LanguageServer(
    name="my-language-server", version="v1.0", converter_factory=custom_converter
)

See the hooks.py module in lsprotocol for some example structure hooks

Miscellaneous

Mandatory name and version

It is now necessary to provide a name and version when constructing an instance of the LanguageServer class

from pygls.server import LanguageServer

server = LanguageServer(name="my-language-server", version="v1.0")

ClientCapabilities.get_capability is now get_capability

# Before
from pygls.lsp.types import ClientCapabilities

client_capabilities = ClientCapabilities()
commit_character_support = client_capabilities.get_capability(
   "text_document.completion.completion_item.commit_characters_support", False
)
# After
from lsprotocol.types import ClientCapabilities
from pygls.capabilities import get_capability

client_capabilities = ClientCapabilities()
commit_character_support = get_capability(
   client_capabilities,
   "text_document.completion.completion_item.commit_characters_support",
   False
)