Skip to content

Commit 26e977e

Browse files
committed
Merge develop
2 parents 2dbf392 + 66c7cca commit 26e977e

18 files changed

+387
-153
lines changed

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
# History of changes
22

3+
## Version 1.4.1 (2022/03/27)
4+
5+
### Pull Requests Merged
6+
7+
* [PR 179](https://github.com/python-lsp/python-lsp-server/pull/179) - Fix Yapf formatting with CRLF line endings, by [@ccordoba12](https://github.com/ccordoba12)
8+
* [PR 174](https://github.com/python-lsp/python-lsp-server/pull/174) - Improved documentation regarding configuration, by [@spookylukey](https://github.com/spookylukey)
9+
10+
In this release 2 pull requests were closed.
11+
12+
----
13+
14+
## Version 1.4.0 (2022/03/11)
15+
316
### New features
417

518
* Support pycodestyle indent-size option
619
* Add `DiagnosticTag` constants from LSP 3.15
720
* Drop support for Python 3.6
821

9-
## Version 1.4.0 (2022/03/11)
10-
1122
### Issues Closed
1223

1324
* [Issue 153](https://github.com/python-lsp/python-lsp-server/issues/153) - Plugin crash crashes whole diagnostic ([PR 158](https://github.com/python-lsp/python-lsp-server/pull/158) by [@ccordoba12](https://github.com/ccordoba12))

CONFIGURATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ This server can be configured using `workspace/didChangeConfiguration` method. E
5858
| `pylsp.plugins.pylint.enabled` | `boolean` | Enable or disable the plugin. | `false` |
5959
| `pylsp.plugins.pylint.args` | `array` of non-unique `string` items | Arguments to pass to pylint. | `null` |
6060
| `pylsp.plugins.pylint.executable` | `string` | Executable to run pylint with. Enabling this will run pylint on unsaved files via stdin. Can slow down workflow. Only works with python3. | `null` |
61-
| `pylsp.plugins.rope_completion.enabled` | `boolean` | Enable or disable the plugin. | `true` |
61+
| `pylsp.plugins.rope_completion.enabled` | `boolean` | Enable or disable the plugin. | `false` |
6262
| `pylsp.plugins.rope_completion.eager` | `boolean` | Resolve documentation and detail eagerly. | `false` |
6363
| `pylsp.plugins.yapf.enabled` | `boolean` | Enable or disable the plugin. | `true` |
6464
| `pylsp.rope.extensionModules` | `string` | Builtin and c-extension modules that are allowed to be imported and inspected by rope. | `null` |

README.md

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The base language server requires [Jedi](https://github.com/davidhalter/jedi) to
1212
```
1313
pip install python-lsp-server
1414
```
15+
This will expose the command `pylsp` on your PATH. Confirm that installation succeeded by running `pylsp --help`.
1516

1617
If the respective dependencies are found, the following optional providers will be enabled:
1718
- [Rope](https://github.com/python-rope/rope) for Completions and renaming
@@ -21,6 +22,9 @@ If the respective dependencies are found, the following optional providers will
2122
- [pydocstyle](https://github.com/PyCQA/pydocstyle) linter for docstring style checking (disabled by default)
2223
- [autopep8](https://github.com/hhatto/autopep8) for code formatting
2324
- [YAPF](https://github.com/google/yapf) for code formatting (preferred over autopep8)
25+
- [flake8](https://github.com/pycqa/flake8) for error checking (disabled by default)
26+
- [pylint](https://github.com/PyCQA/pylint) for code linting (disabled by default)
27+
- [preload](https://github.com/tfiers/preload) for heavy modules (not included by default)
2428

2529
Optional providers can be installed using the `extras` syntax. To install [YAPF](https://github.com/google/yapf) formatting for example:
2630

@@ -44,7 +48,6 @@ pip install -U setuptools
4448

4549
Installing these plugins will add extra functionality to the language server:
4650

47-
- [pyls-flake8](https://github.com/emanspeaks/pyls-flake8/): Error checking using [flake8](https://flake8.pycqa.org/en/latest/).
4851
- [pylsp-mypy](https://github.com/Richardk2n/pylsp-mypy): [MyPy](http://mypy-lang.org/) type checking for Python >=3.7.
4952
- [pyls-isort](https://github.com/paradoxxxzero/pyls-isort): code formatting using [isort](https://github.com/PyCQA/isort) (automatic import sorting).
5053
- [python-lsp-black](https://github.com/python-lsp/python-lsp-black): code formatting using [Black](https://github.com/psf/black).
@@ -59,19 +62,53 @@ Please file an issue if you require assistance writing a plugin.
5962

6063
## Configuration
6164

62-
Configuration is loaded from zero or more configuration sources. Currently implemented are:
65+
Like all language servers, configuration can be passed from the client that talks to this server (i.e. your editor/IDE or other tool that has the same purpose). The details of how this is done depend on the editor or plugin that you are using to communicate with `python-lsp-server`. The configuration options available at that level are documented in [`CONFIGURATION.md`](https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md).
6366

64-
- pycodestyle: discovered in `~/.config/pycodestyle`, `setup.cfg`, `tox.ini` and `pycodestyle.cfg`.
65-
- flake8: discovered in `~/.config/flake8`, `setup.cfg`, `tox.ini` and `flake8.cfg`
67+
`python-lsp-server` depends on other tools, like flake8 and pycodestyle. These tools can be configured via settings passed from the client (as above), or alternatively from other configuration sources. The following sources are available:
6668

67-
The default configuration source is pycodestyle. Change the `pylsp.configurationSources` setting to `['flake8']` in order to respect flake8 configuration instead.
69+
- `pycodestyle`: discovered in `~/.config/pycodestyle`, `setup.cfg`, `tox.ini` and `pycodestyle.cfg`.
70+
- `flake8`: discovered in `~/.config/flake8`, `setup.cfg`, `tox.ini` and `flake8.cfg`
71+
72+
The default configuration sources are `pycodestyle` and `pyflakes`. If you would like to use `flake8`, you will need to:
73+
74+
1. Disable `pycodestyle`, `mccabe`, and `pyflakes`, by setting their corresponding `enabled` configurations, e.g. `pylsp.plugins.pycodestyle.enabled`, to `false`. This will prevent duplicate linting messages as flake8 includes these tools.
75+
1. Set `pylsp.plugins.flake8.enabled` to `true`.
76+
1. Change the `pylsp.configurationSources` setting (in the value passed in from your client) to `['flake8']` in order to use the flake8 configuration instead.
77+
78+
The configuration options available in these config files (`setup.cfg` etc) are documented in the relevant tools:
79+
80+
- [flake8 configuration](https://flake8.pycqa.org/en/latest/user/configuration.html)
81+
- [pycodestyle configuration](https://pycodestyle.pycqa.org/en/latest/intro.html#configuration)
6882

6983
Overall configuration is computed first from user configuration (in home directory), overridden by configuration passed in by the language client, and then overridden by configuration discovered in the workspace.
7084

71-
To enable pydocstyle for linting docstrings add the following setting in your LSP configuration:
72-
`"pylsp.plugins.pydocstyle.enabled": true`
85+
As an example, to change the list of errors that pycodestyle will ignore, assuming you are using the `pycodestyle` configuration source (the default), you can:
86+
87+
1. Add the following to your ~/.config/pycodestyle:
88+
89+
```
90+
[pycodestyle]
91+
ignore = E226,E302,E41
92+
```
93+
94+
2. Set the `pylsp.plugins.pycodestyle.ignore` config value from your editor
95+
3. Same as 1, but add to `setup.cfg` file in the root of the project.
96+
7397

74-
All configuration options are described in [`CONFIGURATION.md`](https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md).
98+
Python LSP Server can communicate over WebSockets when configured as follows:
99+
100+
```
101+
pylsp --ws --port [port]
102+
```
103+
104+
The following libraries are required for Web Sockets support:
105+
- [websockets](https://websockets.readthedocs.io/en/stable/) for Python LSP Server Web sockets using websockets library. refer [Websockets installation](https://websockets.readthedocs.io/en/stable/intro/index.html#installation) for more details
106+
107+
You can install this dependency with command below:
108+
109+
```
110+
pip install 'python-lsp-server[websockets]'
111+
```
75112

76113
## LSP Server Features
77114

RELEASE.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
1. Create pull request to update CHANGELOG.md with
44
* `loghub python-lsp/python-lsp-server -m vX.X.X`
5-
* git add -A && git commit -m "Update Changelog"
5+
* git add -A && git commit -m "Update changelog for X.X.X"
66

77
This is necessary to run our tests before the release, so we can be sure
88
everything is in order.
@@ -16,5 +16,4 @@
1616
6. python setup.py bdist_wheel
1717
7. twine check dist/*
1818
8. twine upload dist/*
19-
9. git push upstream develop
20-
10. git push upstream --tags
19+
9. git push upstream --tags

pylsp/__main__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import json
1414

1515
from .python_lsp import (PythonLSPServer, start_io_lang_server,
16-
start_tcp_lang_server)
16+
start_tcp_lang_server, start_ws_lang_server)
1717
from ._version import __version__
1818

1919
LOG_FORMAT = "%(asctime)s {0} - %(levelname)s - %(name)s - %(message)s".format(
@@ -27,6 +27,10 @@ def add_arguments(parser):
2727
"--tcp", action="store_true",
2828
help="Use TCP server instead of stdio"
2929
)
30+
parser.add_argument(
31+
"--ws", action="store_true",
32+
help="Use Web Sockets server instead of stdio"
33+
)
3034
parser.add_argument(
3135
"--host", default="127.0.0.1",
3236
help="Bind to this address"
@@ -72,6 +76,9 @@ def main():
7276
if args.tcp:
7377
start_tcp_lang_server(args.host, args.port, args.check_parent_process,
7478
PythonLSPServer)
79+
elif args.ws:
80+
start_ws_lang_server(args.port, args.check_parent_process,
81+
PythonLSPServer)
7582
else:
7683
stdin, stdout = _binary_stdio()
7784
start_io_lang_server(stdin, stdout, args.check_parent_process,

pylsp/config/schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@
333333
},
334334
"pylsp.plugins.rope_completion.enabled": {
335335
"type": "boolean",
336-
"default": true,
336+
"default": false,
337337
"description": "Enable or disable the plugin."
338338
},
339339
"pylsp.plugins.rope_completion.eager": {

pylsp/hookspecs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ def pylsp_folding_range(config, workspace, document):
8080

8181

8282
@hookspec(firstresult=True)
83-
def pylsp_format_document(config, workspace, document):
83+
def pylsp_format_document(config, workspace, document, options):
8484
pass
8585

8686

8787
@hookspec(firstresult=True)
88-
def pylsp_format_range(config, workspace, document, range):
88+
def pylsp_format_range(config, workspace, document, range, options):
8989
pass
9090

9191

pylsp/plugins/autopep8_format.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313

1414

1515
@hookimpl(tryfirst=True) # Prefer autopep8 over YAPF
16-
def pylsp_format_document(config, document):
16+
def pylsp_format_document(config, document, options=None): # pylint: disable=unused-argument
1717
log.info("Formatting document %s with autopep8", document)
1818
return _format(config, document)
1919

2020

2121
@hookimpl(tryfirst=True) # Prefer autopep8 over YAPF
22-
def pylsp_format_range(config, document, range): # pylint: disable=redefined-builtin
22+
def pylsp_format_range(config, document, range, options=None): # pylint: disable=redefined-builtin,unused-argument
2323
log.info("Formatting document %s in range %s with autopep8", document, range)
2424

2525
# First we 'round' the range up/down to full lines only

pylsp/plugins/flake8_lint.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,20 @@ def pylsp_lint(workspace, document):
3131
per_file_ignores = settings.get("perFileIgnores")
3232

3333
if per_file_ignores:
34+
prev_file_pat = None
3435
for path in per_file_ignores:
35-
file_pat, errors = path.split(":")
36+
try:
37+
file_pat, errors = path.split(":")
38+
prev_file_pat = file_pat
39+
except ValueError:
40+
# It's legal to just specify another error type for the same
41+
# file pattern:
42+
if prev_file_pat is None:
43+
log.warning(
44+
"skipping a Per-file-ignore with no file pattern")
45+
continue
46+
file_pat = prev_file_pat
47+
errors = path
3648
if PurePath(document.path).match(file_pat):
3749
ignores.extend(errors.split(","))
3850

@@ -81,7 +93,7 @@ def run_flake8(flake8_executable, args, document):
8193
try:
8294
cmd = [flake8_executable]
8395
cmd.extend(args)
84-
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) # pylint: disable=consider-using-with
96+
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
8597
except IOError:
8698
log.debug("Can't execute %s. Trying with '%s -m flake8'", flake8_executable, sys.executable)
8799
cmd = [sys.executable, '-m', 'flake8']

pylsp/plugins/pylint_lint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def _run_pylint_stdio(pylint_executable, document, flags):
246246
cmd = [pylint_executable]
247247
cmd.extend(flags)
248248
cmd.extend(['--from-stdin', document.path])
249-
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) # pylint: disable=consider-using-with
249+
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
250250
except IOError:
251251
log.debug("Can't execute %s. Trying with 'python -m pylint'", pylint_executable)
252252
cmd = ['python', '-m', 'pylint']

pylsp/plugins/yapf_format.py

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import logging
55
import os
66

7-
from yapf.yapflib import file_resources
7+
from yapf.yapflib import file_resources, style
88
from yapf.yapflib.yapf_api import FormatCode
99

1010
import whatthepatch
@@ -16,12 +16,12 @@
1616

1717

1818
@hookimpl
19-
def pylsp_format_document(document):
20-
return _format(document)
19+
def pylsp_format_document(document, options=None):
20+
return _format(document, options=options)
2121

2222

2323
@hookimpl
24-
def pylsp_format_range(document, range): # pylint: disable=redefined-builtin
24+
def pylsp_format_range(document, range, options=None): # pylint: disable=redefined-builtin
2525
# First we 'round' the range up/down to full lines only
2626
range['start']['character'] = 0
2727
range['end']['line'] += 1
@@ -35,25 +35,62 @@ def pylsp_format_range(document, range): # pylint: disable=redefined-builtin
3535

3636
# Add 1 for 1-indexing vs LSP's 0-indexing
3737
lines = [(range['start']['line'] + 1, range['end']['line'] + 1)]
38-
return _format(document, lines=lines)
38+
return _format(document, lines=lines, options=options)
3939

4040

41-
def _format(document, lines=None):
42-
# Yapf doesn't work with CR line endings, so we replace them by '\n'
43-
# and restore them below.
41+
def _format(document, lines=None, options=None):
4442
source = document.source
43+
# Yapf doesn't work with CRLF/CR line endings, so we replace them by '\n'
44+
# and restore them below when adding new lines
4545
eol_chars = get_eol_chars(source)
46-
if eol_chars == '\r':
47-
source = source.replace('\r', '\n')
46+
if eol_chars in ['\r', '\r\n']:
47+
source = source.replace(eol_chars, '\n')
48+
49+
# Get the default styles as a string
50+
# for a preset configuration, i.e. "pep8"
51+
style_config = file_resources.GetDefaultStyleForDir(
52+
os.path.dirname(document.path)
53+
)
54+
if options is not None:
55+
# We have options passed from LSP format request
56+
# let's pass them to the formatter.
57+
# First we want to get a dictionary of the preset style
58+
# to pass instead of a string so that we can modify it
59+
style_config = style.CreateStyleFromConfig(style_config)
60+
61+
use_tabs = style_config['USE_TABS']
62+
indent_width = style_config['INDENT_WIDTH']
63+
64+
if options.get('tabSize') is not None:
65+
indent_width = max(int(options.get('tabSize')), 1)
66+
67+
if options.get('insertSpaces') is not None:
68+
# TODO is it guaranteed to be a boolean, or can it be a string?
69+
use_tabs = not options.get('insertSpaces')
70+
71+
if use_tabs:
72+
# Indent width doesn't make sense when using tabs
73+
# the specifications state: "Size of a tab in spaces"
74+
indent_width = 1
75+
76+
style_config['USE_TABS'] = use_tabs
77+
style_config['INDENT_WIDTH'] = indent_width
78+
style_config['CONTINUATION_INDENT_WIDTH'] = indent_width
79+
80+
for style_option, value in options.items():
81+
# Apply arbitrary options passed as formatter options
82+
if style_option not in style_config:
83+
# ignore if it's not a known yapf config
84+
continue
85+
86+
style_config[style_option] = value
4887

4988
diff_txt, changed = FormatCode(
5089
source,
5190
lines=lines,
5291
filename=document.filename,
5392
print_diff=True,
54-
style_config=file_resources.GetDefaultStyleForDir(
55-
os.path.dirname(document.path)
56-
)
93+
style_config=style_config
5794
)
5895

5996
if not changed:
@@ -112,4 +149,4 @@ def _format(document, lines=None):
112149
})
113150
prev_line_no = lsp_line_no
114151

115-
return textEdits
152+
return textEdits

0 commit comments

Comments
 (0)