Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.0.0 BREAKING CHANGES release thread #273

Merged
merged 20 commits into from
Dec 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]

runs-on: ${{ matrix.os }}

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning][semver].
### Changed
### Fixed

## [1.0.0] - 2/12/2022
### Changed
BREAKING CHANGE: Replaced `pydantic` with [`lsprotocol`](https://github.com/microsoft/lsprotocol)

## [0.13.1] - 1/12/2022
### Changed
Docs now state that the v1 alpha branch is the recommended way to start new projects
Expand Down
17 changes: 7 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
> **Note**
> We will soon release a major version bump with breaking changes. We recommend starting new projects with the v1 alpha release at https://github.com/openlawlibrary/pygls/pull/273

# _pygls_

[![PyPI Version](https://img.shields.io/pypi/v/pygls.svg)](https://pypi.org/project/pygls/) [![Build Status](https://dev.azure.com/openlawlibrary/pygls/_apis/build/status/openlawlibrary.pygls?branchName=master)](https://dev.azure.com/openlawlibrary/pygls/_build/latest?definitionId=2&branchName=master) ![!pyversions](https://img.shields.io/pypi/pyversions/pygls.svg) ![license](https://img.shields.io/pypi/l/pygls.svg) [![Documentation Status](https://img.shields.io/badge/docs-latest-green.svg)](https://pygls.readthedocs.io/en/latest/)

_pygls_ (pronounced like "pie glass") is a pythonic generic implementation of the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/specification) for use as a foundation for writing language servers using Python (e.g. Python, XML, etc.). It allows you to write your own [language server](https://langserver.org/) in just a few lines of code.

## Quick Intro

> **_IMPORTANT NOTE:_**
>
> In order to support type-checking, we added `pydantic` library which requires passing keyword arguments when creating [LSP models](https://github.com/openlawlibrary/pygls/blob/master/pygls/lsp/methods.py).

Here's how to create a server and register a code completion feature:

```python
from pygls.capabilities import COMPLETION
from pygls.server import LanguageServer
from pygls.lsp import CompletionItem, CompletionList, CompletionOptions, CompletionParams
from lsprotocol.types import (
TEXT_DOCUMENT_COMPLETION,
CompletionItem,
CompletionList,
CompletionOptions,
CompletionParams
)

server = LanguageServer('example-server', 'v0.1')

Expand Down
2 changes: 2 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ Release notes are kept in CHANGELOG.md
# Python's `setuptools` automatically derives the version from the latest Git tag.
# NB. If the latest commit doesn't have a tag, then `setuptools` will add `dev-[hash]` to the version.
git tag v"$(python -c 'from pygls import __version__; print(__version__)')"
git push --tags # not required for releasing, just needed because normal `git push` won't send the tags

# Build the project into the Source and Wheel formats (they go into `./dist`)
rm -rf dist pygls.egg-info
python -m build

# Upload to Pypi
Expand Down
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = 'en'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
Expand All @@ -88,7 +88,7 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
#html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ User Guide
pages/tutorial
pages/advanced_usage
pages/testing
pages/migrating-to-v1


.. _Language Server Protocol: https://microsoft.github.io/language-server-protocol/specification
Expand Down
15 changes: 3 additions & 12 deletions docs/source/pages/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ servers that are based on it.
Installation
------------

.. note::
*December 2022*
We will soon release a major version bump with breaking changes.
We recommend starting new projects with the v1 alpha release at:
https://github.com/openlawlibrary/pygls/pull/273

To get the latest release from *PyPI*, simply run:

.. code:: console
Expand Down Expand Up @@ -64,7 +58,7 @@ Register Features and Commands

.. code:: python

@server.feature(COMPLETION, CompletionOptions(trigger_characters=[',']))
@server.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=[',']))
def completions(params: CompletionParams):
"""Returns completion items."""
return CompletionList(
Expand All @@ -84,9 +78,7 @@ Register Features and Commands
def cmd_return_hello_world(ls, *args):
return 'Hello World!'

Features that are currently supported by the LSP specification can be
found in `pygls.lsp.methods`_ module, while corresponding request/response
classes can be found in `pygls.lsp.types`_ module.
See the `lsprotocol`_ module for the complete and canonical list of avaiable features.

Advanced usage
--------------
Expand All @@ -102,5 +94,4 @@ haven't worked with language servers before.


.. _GitHub: https://github.com/openlawlibrary/pygls
.. _pygls.lsp.methods: https://github.com/openlawlibrary/pygls/blob/master/pygls/lsp/methods.py
.. _pygls.lsp.types: https://github.com/openlawlibrary/pygls/tree/master/pygls/lsp/types
.. _lsprotocol: https://github.com/microsoft/lsprotocol/blob/main/lsprotocol/types.py
238 changes: 238 additions & 0 deletions docs/source/pages/migrating-to-v1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
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:
- `jedi-language-server`_
- `vscode-ruff`_
- `esbonio`_

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.

.. code-block:: python

# 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")

.. code-block:: python

# 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.

.. code-block:: pycon

>>> 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.

.. code-block:: pycon

>>> 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`_

.. code-block:: python

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

.. code-block:: python

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

.. code-block:: python

from pygls.server import LanguageServer

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


``ClientCapabilities.get_capability`` is now ``get_capability``
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

.. code-block:: python

# 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
)

.. code-block:: python

# 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
)

.. _attrs: https://www.attrs.org/en/stable/index.html
.. _cattrs: https://cattrs.readthedocs.io/en/stable/
.. _converter: https://cattrs.readthedocs.io/en/stable/converters.html
.. _hooks.py: https://github.com/microsoft/lsprotocol/blob/main/lsprotocol/_hooks.py
.. _lsprotocol: https://github.com/microsoft/lsprotocol
.. _pydantic: https://pydantic-docs.helpmanual.io/
.. _structure hooks: https://cattrs.readthedocs.io/en/stable/structuring.html#registering-custom-structuring-hooks
.. _jedi-language-serer: https://github.com/pappasam/jedi-language-server/pull/230
.. _vscode-ruff: https://github.com/charliermarsh/vscode-ruff/pull/37
.. _esbonio: https://github.com/swyddfa/esbonio/pull/484
8 changes: 5 additions & 3 deletions docs/source/pages/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
Tutorial
========

In order to help you with *pygls*, we have created a simple
`json-extension`_ example.
In order to help you with using *pygls* in VSCode, we have created a simple `json-extension`_ example.

.. note::
You do not need this extension when using *pygls* with other text editors.

Prerequisites
-------------

In order to setup and run the example extension, you need following software
In order to setup and run the example VSCode extension, you need following software
installed:

* `Visual Studio Code <https://code.visualstudio.com/>`_ editor
Expand Down
Loading