pax_global_header00006660000000000000000000000064145401732040014512gustar00rootroot0000000000000052 comment=744296948badb2ba8db4d5280555d614e095364a python-lsp-black-2.0.0/000077500000000000000000000000001454017320400147005ustar00rootroot00000000000000python-lsp-black-2.0.0/.envrc000066400000000000000000000000161454017320400160130ustar00rootroot00000000000000layout python python-lsp-black-2.0.0/.github/000077500000000000000000000000001454017320400162405ustar00rootroot00000000000000python-lsp-black-2.0.0/.github/workflows/000077500000000000000000000000001454017320400202755ustar00rootroot00000000000000python-lsp-black-2.0.0/.github/workflows/python.yml000066400000000000000000000011461454017320400223430ustar00rootroot00000000000000name: Python on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: python -m pip install -e .[dev] - name: pre-commit checks uses: pre-commit/action@v2.0.2 - name: Tests run: pytest -v tests/ python-lsp-black-2.0.0/.gitignore000066400000000000000000000001201454017320400166610ustar00rootroot00000000000000.venv .mypy_cache *.egg-info *.pyc *.orig .pytest_cache .ropeproject build dist python-lsp-black-2.0.0/.pre-commit-config.yaml000066400000000000000000000011241454017320400211570ustar00rootroot00000000000000repos: - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: - id: isort - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black exclude: fixtures args: [--check, --config=pyproject.toml] - repo: https://github.com/PyCQA/flake8 rev: 6.0.0 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.3.0 hooks: - id: mypy additional_dependencies: [black, types-pkg_resources, types-setuptools] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-merge-conflict - id: debug-statements python-lsp-black-2.0.0/CHANGELOG.md000066400000000000000000000172171454017320400165210ustar00rootroot00000000000000# History of changes ## Version 2.0.0 (2023-12-19) ### New features * Add support to format indented selections of code. This requires Black 23.11.0+ * Change entrypoint name to be `black`. This changes the options namespace for this plugin from `pylsp.pylsp_black` to `pylsp.black`. * Drop support for Python 3.7. ### Issues Closed * [Issue 42](https://github.com/python-lsp/python-lsp-black/issues/42) - Ineffective range formatting ([PR 52](https://github.com/python-lsp/python-lsp-black/pull/52) by [@remisalmon](https://github.com/remisalmon)) * [Issue 41](https://github.com/python-lsp/python-lsp-black/issues/41) - Configuration key and plugin name mismatch ([PR 39](https://github.com/python-lsp/python-lsp-black/pull/39) by [@chantera](https://github.com/chantera)) In this release 2 issues were closed. ### Pull Requests Merged * [PR 53](https://github.com/python-lsp/python-lsp-black/pull/53) - Drop support for Python 3.7, by [@ccordoba12](https://github.com/ccordoba12) * [PR 52](https://github.com/python-lsp/python-lsp-black/pull/52) - Use new `lines` option in Black 23.11 to format range, by [@remisalmon](https://github.com/remisalmon) ([42](https://github.com/python-lsp/python-lsp-black/issues/42)) * [PR 49](https://github.com/python-lsp/python-lsp-black/pull/49) - Read skip options from plugin settings, by [@seruman](https://github.com/seruman) * [PR 39](https://github.com/python-lsp/python-lsp-black/pull/39) - Change entrypoint name to simply be `black`, by [@chantera](https://github.com/chantera) ([41](https://github.com/python-lsp/python-lsp-black/issues/41)) In this release 4 pull requests were closed. ## Version 1.3.0 (2023/05/19) ### Issues Closed * [Issue 36](https://github.com/python-lsp/python-lsp-black/issues/36) - python-lsp-black ignores skip-magic-trailing-comma in .config/black ([PR 37](https://github.com/python-lsp/python-lsp-black/pull/37) by [@wstevick](https://github.com/wstevick)) * [Issue 35](https://github.com/python-lsp/python-lsp-black/issues/35) - python-lsp-black does not respect black configurations In this release 2 issues were closed. ### Pull Requests Merged * [PR 47](https://github.com/python-lsp/python-lsp-black/pull/47) - direnv support, by [@haplo](https://github.com/haplo) * [PR 46](https://github.com/python-lsp/python-lsp-black/pull/46) - Add Python 3.11, drop 3.7 from test matrix, by [@haplo](https://github.com/haplo) * [PR 45](https://github.com/python-lsp/python-lsp-black/pull/45) - Test preview and skip-magic-trailing-comma config parsing, by [@haplo](https://github.com/haplo) * [PR 44](https://github.com/python-lsp/python-lsp-black/pull/44) - pre-commit autoupdate, by [@haplo](https://github.com/haplo) * [PR 40](https://github.com/python-lsp/python-lsp-black/pull/40) - Replace the obsolete toml package with tomllib/tomli, by [@mgorny](https://github.com/mgorny) * [PR 38](https://github.com/python-lsp/python-lsp-black/pull/38) - Added missing `preview` kwarg in `black.FileMode`. Fixes #35., by [@JesusTorrado](https://github.com/JesusTorrado) * [PR 37](https://github.com/python-lsp/python-lsp-black/pull/37) - Add the possibility to configure skip-magic-trailing-comma, by [@wstevick](https://github.com/wstevick) ([36](https://github.com/python-lsp/python-lsp-black/issues/36)) In this release 7 pull requests were closed. ## Version 1.2.1 (2022-04-12) ### Pull Requests Merged * [PR 34](https://github.com/python-lsp/python-lsp-black/pull/34) - Disable Autopep8 and Yapf if this plugin is installed, by [@bageljrkhanofemus](https://github.com/bageljrkhanofemus) In this release 1 pull request was closed. ## Version 1.2.0 (2022-03-28) ### Issues Closed * [Issue 24](https://github.com/python-lsp/python-lsp-black/issues/24) - Option to cache black configuration per-file In this release 1 issue was closed. ### Pull Requests Merged * [PR 33](https://github.com/python-lsp/python-lsp-black/pull/33) - Update pre-commit hooks' versions, by [@haplo](https://github.com/haplo) * [PR 32](https://github.com/python-lsp/python-lsp-black/pull/32) - Fix PyPI badge in Readme, by [@ccordoba12](https://github.com/ccordoba12) * [PR 28](https://github.com/python-lsp/python-lsp-black/pull/28) - Correctly format files and ranges with line endings other than LF, by [@ccordoba12](https://github.com/ccordoba12) * [PR 26](https://github.com/python-lsp/python-lsp-black/pull/26) - Add client side configuration and cache configuration per file, by [@haplo](https://github.com/haplo) In this release 4 pull requests were closed. ## Version 1.1.0 (2022-01-30) ### Issues Closed * [Issue 29](https://github.com/python-lsp/python-lsp-black/issues/29) - TypeError when formatting with Black 22.1 ([PR 30](https://github.com/python-lsp/python-lsp-black/pull/30) by [@wlcx](https://github.com/wlcx)) * [Issue 25](https://github.com/python-lsp/python-lsp-black/issues/25) - Support global config file for black ([PR 19](https://github.com/python-lsp/python-lsp-black/pull/19) by [@jdost](https://github.com/jdost)) In this release 2 issues were closed. ### Pull Requests Merged * [PR 30](https://github.com/python-lsp/python-lsp-black/pull/30) - Fix TypeError when formatting with black 22.1.0+, by [@wlcx](https://github.com/wlcx) ([29](https://github.com/python-lsp/python-lsp-black/issues/29)) * [PR 19](https://github.com/python-lsp/python-lsp-black/pull/19) - Support global config as a fallback, by [@jdost](https://github.com/jdost) ([25](https://github.com/python-lsp/python-lsp-black/issues/25)) In this release 2 pull requests were closed. ## Version 1.0.1 (2021-12-01) ### Issues Closed * [Issue 20](https://github.com/python-lsp/python-lsp-black/issues/20) - Formatting fails silently * [Issue 12](https://github.com/python-lsp/python-lsp-black/issues/12) - Fix MyPy linting * [Issue 9](https://github.com/python-lsp/python-lsp-black/issues/9) - Ignore virtualenv in linters * [Issue 8](https://github.com/python-lsp/python-lsp-black/issues/8) - Add Development section to README * [Issue 7](https://github.com/python-lsp/python-lsp-black/issues/7) - Add pre-commit checks In this release 5 issues were closed. ### Pull Requests Merged * [PR 23](https://github.com/python-lsp/python-lsp-black/pull/23) - Add pre-commit hooks, by [@haplo](https://github.com/haplo) * [PR 22](https://github.com/python-lsp/python-lsp-black/pull/22) - Log black errors to stderr, by [@haplo](https://github.com/haplo) ([20](https://github.com/python-lsp/python-lsp-black/issues/20)) * [PR 14](https://github.com/python-lsp/python-lsp-black/pull/14) - Add virtualenv to gitignore and Python 3.9 to black target versions, by [@haplo](https://github.com/haplo) * [PR 13](https://github.com/python-lsp/python-lsp-black/pull/13) - Install MyPy stubs, by [@haplo](https://github.com/haplo) ([12](https://github.com/python-lsp/python-lsp-black/issues/12)) * [PR 11](https://github.com/python-lsp/python-lsp-black/pull/11) - Add Development section to README, by [@haplo](https://github.com/haplo) ([8](https://github.com/python-lsp/python-lsp-black/issues/8)) * [PR 10](https://github.com/python-lsp/python-lsp-black/pull/10) - Exclude venv and other directories from linters, by [@haplo](https://github.com/haplo) ([9](https://github.com/python-lsp/python-lsp-black/issues/9)) In this release 6 pull request was closed. ## Version 1.0.0 (2021/05/18) ### Issues Closed - [Issue 3](https://github.com/python-lsp/python-lsp-black/issues/3) - Update README and add RELEASE instructions - [Issue 2](https://github.com/python-lsp/python-lsp-black/issues/2) - Release v1.0.0 In this release 2 issues were closed. ### Pull Requests Merged - [PR 1](https://github.com/python-lsp/python-lsp-black/pull/1) - PR: Python LSP server migration, by [@andfoy](https://github.com/andfoy) In this release 1 pull request was closed. python-lsp-black-2.0.0/LICENSE000066400000000000000000000021371454017320400157100ustar00rootroot00000000000000MIT License Copyright (c) 2018-2020 Rupert Bedford Copyright (c) 2021 Python LSP contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. python-lsp-black-2.0.0/MAINTENANCE.md000066400000000000000000000010041454017320400167370ustar00rootroot00000000000000The following are instructions to maintain python-lsp-black 1. Releases are tracked using Github milestones, which can be created and closed under the `Issues > Milestones` page: https://github.com/python-lsp/python-lsp-black/milestones. 1. If a issue will be fixed as part of a particular release, then its milestone should be the release-corresponding one. 1. Please make sure that PRs are also tracked under a specific milestone. 1. Please follow the [RELEASE.md](./RELEASE.md) file when making a release. python-lsp-black-2.0.0/Makefile000066400000000000000000000005741454017320400163460ustar00rootroot00000000000000lint: pre-commit run -a black: pre-commit run -a black flake8: pre-commit run -a flake8 isort: pre-commit run -a isort mypy: pre-commit run -a mypy test: pytest -vv . build: lint test python3 setup.py sdist bdist_wheel test-upload: twine upload --repository-url https://test.pypi.org/legacy/ dist/* upload: twine upload dist/* clean: rm -rf dist .PHONY: build python-lsp-black-2.0.0/README.md000066400000000000000000000071241454017320400161630ustar00rootroot00000000000000# python-lsp-black [![PyPI](https://img.shields.io/pypi/v/python-lsp-black.svg)](https://pypi.org/project/python-lsp-black) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Python](https://github.com/python-lsp/python-lsp-black/actions/workflows/python.yml/badge.svg)](https://github.com/python-lsp/python-lsp-black/actions/workflows/python.yml) [Black](https://github.com/psf/black) plugin for the [Python LSP Server](https://github.com/python-lsp/python-lsp-server). ## Install In the same `virtualenv` as `python-lsp-server`: ```shell pip install python-lsp-black ``` # Usage This plugin will disable the yapf and autopep8 plugins if installed. - `python-lsp-black` can either format an entire file or just the selected text. - The code will only be formatted if it is syntactically valid Python. - Text selections are treated as if they were a separate Python file. Unfortunately this means you can't format an indented block of code. - `python-lsp-black` will use your project's [pyproject.toml](https://github.com/psf/black#pyprojecttoml) if it has one. - `python-lsp-black` only officially supports the latest stable version of [black](https://github.com/psf/black). An effort is made to keep backwards-compatibility but older black versions will not be actively tested. - The plugin can cache the black configuration that applies to each Python file, this improves performance of the plugin. When configuration caching is enabled any changes to black's configuration will need the LSP server to be restarted. Configuration caching can be disabled with the `cache_config` option, see *Configuration* below. # Configuration The plugin follows [python-lsp-server's configuration](https://github.com/python-lsp/python-lsp-server/#configuration). These are the valid configuration keys: - `pylsp.plugins.black.enabled`: boolean to enable/disable the plugin. - `pylsp.plugins.black.cache_config`: a boolean to enable black configuration caching (see *Usage*). `false` by default. - `pylsp.plugins.black.line_length`: an integer that maps to [black's `max-line-length`](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-length) setting. Defaults to 88 (same as black's default). This can also be set through black's configuration files, which should be preferred for multi-user projects. - `pylsp.plugins.black.preview`: a boolean to enable or disable [black's `--preview` setting](https://black.readthedocs.io/en/stable/the_black_code_style/future_style.html#preview-style). `false` by default. - `pylsp.plugins.black.skip_string_normalization`: a boolean to enable or disable black's `--skip-string-normalization` setting. `false` by default. - `pylsp.plugins.black.skip_magic_trailing_comma`: a boolean to enable or disable black's `skip-magic-trailing-comma` setting. `false` by default. # Development To install the project for development you need to specify the dev optional dependencies: ```shell python -m venv .venv . .venv/bin/activate pip install -e .[dev] ``` This project uses [pre-commit](https://pre-commit.com/) hooks to control code quality, install them to run automatically when creating a git commit, thus avoiding seeing errors when you create a pull request: ```shell pre-commit install ``` To run tests: ```shell make test ``` To run linters: ```shell make lint # just a shortcut to pre-commit run -a make # black, flake8, isort, mypy ``` To upgrade the version of the pre-commit hooks: ```shell pre-commit autoupdate # check and git commit changes to .pre-commit-config.yaml ``` python-lsp-black-2.0.0/RELEASE.md000066400000000000000000000014341454017320400163040ustar00rootroot00000000000000To release a new version of python-lsp-black: 1. git fetch upstream && git checkout upstream/master 1. Close milestone on GitHub 1. git clean -xfdi 1. Update CHANGELOG.md with loghub: `loghub python-lsp/python-lsp-black --milestone vX.X.X` 1. git add -A && git commit -m "Update Changelog" 1. Update release version in `setup.cfg` (set release version, remove '.dev0') 1. git add -A && git commit -m "Release vX.X.X" 1. python setup.py sdist 1. python setup.py bdist_wheel 1. twine check dist/\* 1. twine upload dist/\* 1. git tag -a vX.X.X -m "Release vX.X.X" 1. Update development version in `setup.cfg` (add '.dev0' and increment minor) 1. git add -A && git commit -m "Back to work" 1. git push upstream master 1. git push upstream --tags 1. Draft a new release in GitHub using the new tag. python-lsp-black-2.0.0/pylsp_black/000077500000000000000000000000001454017320400172035ustar00rootroot00000000000000python-lsp-black-2.0.0/pylsp_black/__init__.py000066400000000000000000000000001454017320400213020ustar00rootroot00000000000000python-lsp-black-2.0.0/pylsp_black/plugin.py000066400000000000000000000151111454017320400210520ustar00rootroot00000000000000import logging import os import sys from functools import lru_cache from pathlib import Path from typing import Dict, Optional import black from pylsp import hookimpl from pylsp._utils import get_eol_chars from pylsp.config.config import Config if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib logger = logging.getLogger(__name__) GLOBAL_CONFIG: Optional[Path] = None try: if os.name == "nt": GLOBAL_CONFIG = Path.home() / ".black" elif "XDG_CONFIG_HOME" in os.environ: GLOBAL_CONFIG = Path(os.environ["XDG_CONFIG_HOME"]) / "black" else: GLOBAL_CONFIG = Path.home() / ".config" / "black" except Exception as e: logger.error("Error determining black global config file path: %s", e) else: if GLOBAL_CONFIG is not None and GLOBAL_CONFIG.exists(): logger.info("Found black global config file at %s", GLOBAL_CONFIG) @hookimpl(tryfirst=True) def pylsp_format_document(config, document): return format_document(config, document) @hookimpl(tryfirst=True) def pylsp_format_range(config, document, range): range["start"]["character"] = 0 range["end"]["line"] += 1 range["end"]["character"] = 0 return format_document(config, document, range) @hookimpl def pylsp_settings(): """Configuration options that can be set on the client.""" return { "plugins": { "black": { "enabled": True, "line_length": 88, "preview": False, "cache_config": False, }, "yapf": {"enabled": False}, "autopep8": {"enabled": False}, } } def format_document(client_config, document, range=None): text = document.source config = load_config(document.path, client_config) # Black lines indices are "1-based and inclusive on both ends" lines = [(range["start"]["line"] + 1, range["end"]["line"])] if range else () try: formatted_text = format_text(text=text, config=config, lines=lines) except black.NothingChanged: # raised when the file is already formatted correctly return [] if range: formatted_lines = formatted_text.splitlines(True) start = range["start"]["line"] end = range["end"]["line"] + (len(formatted_lines) - len(document.lines)) formatted_text = "".join(formatted_lines[start:end]) else: range = { "start": {"line": 0, "character": 0}, "end": {"line": len(document.lines), "character": 0}, } return [{"range": range, "newText": formatted_text}] def format_text(*, text, config, lines): mode = black.FileMode( target_versions=config["target_version"], line_length=config["line_length"], is_pyi=config["pyi"], string_normalization=not config["skip_string_normalization"], magic_trailing_comma=not config["skip_magic_trailing_comma"], preview=config["preview"], ) try: # Black's format_file_contents only works reliably when eols are '\n'. It gives # an error for '\r' and produces wrong formatting for '\r\n'. So we replace # those eols by '\n' before formatting and restore them afterwards. replace_eols = False eol_chars = get_eol_chars(text) if eol_chars is not None and eol_chars != "\n": replace_eols = True text = text.replace(eol_chars, "\n") # Will raise black.NothingChanged, we want to bubble that exception up formatted_text = black.format_file_contents( text, fast=config["fast"], mode=mode, lines=lines ) # Restore eols if necessary. if replace_eols: formatted_text = formatted_text.replace("\n", eol_chars) return formatted_text except ( # raised when the file has syntax errors ValueError, # raised when the file being formatted has an indentation error IndentationError, # raised when black produces invalid Python code or formats the file # differently on the second pass AssertionError, ) as e: # errors will show on lsp stderr stream logger.error("Error formatting with black: %s", e) raise black.NothingChanged from e @lru_cache(100) def _load_config(filename: str, client_config: Config) -> Dict: settings = client_config.plugin_settings("black") defaults = { "line_length": settings.get("line_length", 88), "fast": False, "pyi": filename.endswith(".pyi"), "skip_string_normalization": settings.get("skip_string_normalization", False), "skip_magic_trailing_comma": settings.get("skip_magic_trailing_comma", False), "target_version": set(), "preview": settings.get("preview", False), } root = black.find_project_root((filename,)) # Black 22.1.0+ returns a tuple if isinstance(root, tuple): pyproject_filename = root[0] / "pyproject.toml" else: pyproject_filename = root / "pyproject.toml" if not pyproject_filename.is_file(): if GLOBAL_CONFIG is not None and GLOBAL_CONFIG.exists(): pyproject_filename = GLOBAL_CONFIG logger.info("Using global black config at %s", pyproject_filename) else: logger.info("Using defaults: %r", defaults) return defaults try: with open(pyproject_filename, "rb") as f: pyproject_toml = tomllib.load(f) except (tomllib.TOMLDecodeError, OSError): logger.warning( "Error decoding pyproject.toml, using defaults: %r", defaults, ) return defaults file_config = pyproject_toml.get("tool", {}).get("black", {}) file_config = { key.replace("--", "").replace("-", "_"): value for key, value in file_config.items() } config = { key: file_config.get(key, default_value) for key, default_value in defaults.items() } if file_config.get("target_version"): target_version = set( black.TargetVersion[x.upper()] for x in file_config["target_version"] ) else: target_version = set() config["target_version"] = target_version logger.info("Using config from %s: %r", pyproject_filename, config) return config def load_config(filename: str, client_config: Config) -> Dict: settings = client_config.plugin_settings("black") # Use the original, not cached function to load settings if requested if not settings.get("cache_config", False): return _load_config.__wrapped__(filename, client_config) return _load_config(filename, client_config) python-lsp-black-2.0.0/pyproject.toml000066400000000000000000000002311454017320400176100ustar00rootroot00000000000000[tool.black] target-version = ['py38', 'py39', 'py310', 'py311'] exclude = ''' /( \.venv | \.git | \.mypy_cache | build | dist | fixtures )/ ''' python-lsp-black-2.0.0/setup.cfg000066400000000000000000000023331454017320400165220ustar00rootroot00000000000000[metadata] name = python-lsp-black version = 2.0.0 author = Python LSP contributors author_email = f@fidelramos.net description = Black plugin for the Python LSP Server url = https://github.com/python-lsp/python-lsp-black long_description = file: README.md long_description_content_type = text/markdown project_urls = Bug Tracker = https://github.com/python-lsp/python-lsp-black/issues Changelog = https://github.com/python-lsp/python-lsp-black/blob/master/CHANGELOG.md Source Code = https://github.com/python-lsp/python-lsp-black classifiers = Programming Language :: Python License :: OSI Approved :: MIT License Operating System :: OS Independent [options] packages = find: install_requires = python-lsp-server>=1.4.0 black>=23.11.0 tomli; python_version<'3.11' python_requires = >= 3.8 [options.entry_points] pylsp = black = pylsp_black.plugin [options.extras_require] # add any types-* packages to .pre-commit-config.yaml mypy additional_dependencies dev = isort>=5.0; flake8; pre-commit; pytest; mypy; pytest; types-pkg_resources; types-setuptools [flake8] max-line-length = 88 ignore = E203 exclude = .venv [mypy] ignore_missing_imports = true [isort] profile = black skip_glob = [".venv"] python-lsp-black-2.0.0/setup.py000066400000000000000000000001051454017320400164060ustar00rootroot00000000000000from setuptools import setup if __name__ == "__main__": setup() python-lsp-black-2.0.0/tests/000077500000000000000000000000001454017320400160425ustar00rootroot00000000000000python-lsp-black-2.0.0/tests/fixtures/000077500000000000000000000000001454017320400177135ustar00rootroot00000000000000python-lsp-black-2.0.0/tests/fixtures/config/000077500000000000000000000000001454017320400211605ustar00rootroot00000000000000python-lsp-black-2.0.0/tests/fixtures/config/config.txt000066400000000000000000000000531454017320400231640ustar00rootroot00000000000000run(these, arguments, should, be, wrapped) python-lsp-black-2.0.0/tests/fixtures/config/pyproject.toml000066400000000000000000000002071454017320400240730ustar00rootroot00000000000000[tool.black] line-length = 20 --fast = true pyi = true skip-magic-trailing-comma = true skip-string-normalization = true preview = truepython-lsp-black-2.0.0/tests/fixtures/formatted-crlf.py000066400000000000000000000000641454017320400231760ustar00rootroot00000000000000if True: print("foo") print("bar") # noqa python-lsp-black-2.0.0/tests/fixtures/formatted-line-length.py000066400000000000000000000001651454017320400244600ustar00rootroot00000000000000def foo( aaaaa, bbbbb, ccccc, ddddd, eeeee, fffff, ggggg, hhhhh, iiiii, jjjjj, kkkkk ): return aaaaa # noqa python-lsp-black-2.0.0/tests/fixtures/formatted.pyi000066400000000000000000000000551454017320400224230ustar00rootroot00000000000000def foo() -> None: ... def bar() -> int: ... python-lsp-black-2.0.0/tests/fixtures/formatted.txt000066400000000000000000000002341454017320400224400ustar00rootroot00000000000000a = "hello" b = [ "a", "very", "very", "very", "very", "very", "very", "very", "very", "long", "line", ] c = 42 python-lsp-black-2.0.0/tests/fixtures/invalid.txt000066400000000000000000000000141454017320400220750ustar00rootroot00000000000000 x = 1+2 python-lsp-black-2.0.0/tests/fixtures/pyproject.toml000066400000000000000000000000001454017320400226150ustar00rootroot00000000000000python-lsp-black-2.0.0/tests/fixtures/target_version/000077500000000000000000000000001454017320400227465ustar00rootroot00000000000000python-lsp-black-2.0.0/tests/fixtures/target_version/pyproject.toml000066400000000000000000000000471454017320400256630ustar00rootroot00000000000000[tool.black] target-version = ['py39'] python-lsp-black-2.0.0/tests/fixtures/unformatted-crlf.py000066400000000000000000000000621454017320400235370ustar00rootroot00000000000000if True: print("foo") print("bar") # noqapython-lsp-black-2.0.0/tests/fixtures/unformatted-line-length.py000066400000000000000000000001571454017320400250240ustar00rootroot00000000000000 def foo(aaaaa, bbbbb, ccccc, ddddd, eeeee, fffff, ggggg, hhhhh, iiiii, jjjjj, kkkkk): return aaaaa # noqapython-lsp-black-2.0.0/tests/fixtures/unformatted.pyi000066400000000000000000000000661454017320400227700ustar00rootroot00000000000000def foo() -> None: ... def bar() -> int: ... python-lsp-black-2.0.0/tests/fixtures/unformatted.txt000066400000000000000000000001561454017320400230060ustar00rootroot00000000000000a = 'hello' b = ["a", "very", "very", "very", "very", "very", "very", "very", "very", "long", "line"] c = 42 python-lsp-black-2.0.0/tests/test_plugin.py000066400000000000000000000236601454017320400207600ustar00rootroot00000000000000# Standard library imports import types from pathlib import Path from unittest.mock import Mock # Third-party imports import black import pkg_resources import pytest # Python LSP imports from pylsp import uris from pylsp.config.config import Config from pylsp.workspace import Document, Workspace # Local imports from pylsp_black.plugin import ( _load_config, load_config, pylsp_format_document, pylsp_format_range, pylsp_settings, ) here = Path(__file__).parent fixtures_dir = here / "fixtures" @pytest.fixture def workspace(tmpdir): """Return a workspace.""" return Workspace(uris.from_fs_path(str(tmpdir)), Mock()) @pytest.fixture def config(workspace): """Return a config object.""" cfg = Config(workspace.root_uri, {}, 0, {}) cfg._plugin_settings = { "plugins": {"black": {"line_length": 88, "cache_config": False}} } return cfg @pytest.fixture def config_with_skip_options(workspace): """Return a config object.""" cfg = Config(workspace.root_uri, {}, 0, {}) cfg._plugin_settings = { "plugins": { "black": { "line_length": 88, "cache_config": False, "skip_string_normalization": True, "skip_magic_trailing_comma": True, } } } return cfg @pytest.fixture def unformatted_document(workspace): path = fixtures_dir / "unformatted.txt" uri = f"file:/{path}" # noqa return Document(uri, workspace) @pytest.fixture def unformatted_pyi_document(workspace): path = fixtures_dir / "unformatted.pyi" uri = f"file:/{path}" # noqa return Document(uri, workspace) @pytest.fixture def unformatted_crlf_document(workspace): path = fixtures_dir / "unformatted-crlf.py" uri = f"file:/{path}" # noqa with open(path, "r", newline="") as f: source = f.read() return Document(uri, workspace, source=source) @pytest.fixture def formatted_document(workspace): path = fixtures_dir / "formatted.txt" uri = f"file:/{path}" # noqa return Document(uri, workspace) @pytest.fixture def formatted_pyi_document(workspace): path = fixtures_dir / "formatted.pyi" uri = f"file:/{path}" # noqa return Document(uri, workspace) @pytest.fixture def formatted_crlf_document(workspace): path = fixtures_dir / "formatted-crlf.py" uri = f"file:/{path}" # noqa with open(path, "r", newline="") as f: source = f.read() return Document(uri, workspace, source=source) @pytest.fixture def invalid_document(workspace): path = fixtures_dir / "invalid.txt" uri = f"file:/{path}" # noqa return Document(uri, workspace) @pytest.fixture def config_document(workspace): path = fixtures_dir / "config" / "config.txt" uri = f"file:/{path}" # noqa return Document(uri, workspace) @pytest.fixture def unformatted_line_length(workspace): path = fixtures_dir / "unformatted-line-length.py" uri = f"file:/{path}" # noqa return Document(uri, workspace) @pytest.fixture def formatted_line_length(workspace): path = fixtures_dir / "formatted-line-length.py" uri = f"file:/{path}" # noqa return Document(uri, workspace) def test_pylsp_format_document(config, unformatted_document, formatted_document): result = pylsp_format_document(config, unformatted_document) assert result == [ { "range": { "start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 0}, }, "newText": formatted_document.source, } ] def test_pyls_format_pyi_document( config, unformatted_pyi_document, formatted_pyi_document ): result = pylsp_format_document(config, unformatted_pyi_document) assert result == [ { "range": { "start": {"line": 0, "character": 0}, "end": {"line": 5, "character": 0}, }, "newText": formatted_pyi_document.source, } ] def test_pylsp_format_document_unchanged(config, formatted_document): result = pylsp_format_document(config, formatted_document) assert result == [] def test_pyls_format_pyi_document_unchanged(config, formatted_pyi_document): result = pylsp_format_document(config, formatted_pyi_document) assert result == [] def test_pylsp_format_document_syntax_error(config, invalid_document): result = pylsp_format_document(config, invalid_document) assert result == [] def test_pylsp_format_document_with_config(config, config_document): result = pylsp_format_document(config, config_document) assert result == [ { "range": { "start": {"line": 0, "character": 0}, "end": {"line": 1, "character": 0}, }, "newText": ( "run(\n" " these,\n" " arguments,\n" " should,\n" " be,\n" " wrapped,\n" ")\n" ), } ] @pytest.mark.parametrize( ("start", "end", "expected"), [ (0, 0, 'a = "hello"\n'), ( 1, 1, 'b = [\n "a",\n "very",\n "very",\n "very",\n "very",\n "very",\n "very",\n "very",\n "very",\n "long",\n "line",\n]\n', # noqa: E501 ), (2, 2, "c = 42\n"), ( 0, 2, 'a = "hello"\nb = [\n "a",\n "very",\n "very",\n "very",\n "very",\n "very",\n "very",\n "very",\n "very",\n "long",\n "line",\n]\nc = 42\n', # noqa: E501 ), ], ) def test_pylsp_format_range(config, unformatted_document, start, end, expected): range = { "start": {"line": start, "character": 0}, "end": {"line": end, "character": 0}, } result = pylsp_format_range(config, unformatted_document, range=range) assert result == [ { "range": { "start": {"line": start, "character": 0}, "end": {"line": end + 1, "character": 0}, }, "newText": expected, } ] def test_pylsp_format_range_unchanged(config, formatted_document): range = {"start": {"line": 0, "character": 0}, "end": {"line": 1, "character": 0}} result = pylsp_format_range(config, formatted_document, range=range) assert result == [] def test_pylsp_format_range_syntax_error(config, invalid_document): range = {"start": {"line": 0, "character": 0}, "end": {"line": 1, "character": 0}} result = pylsp_format_range(config, invalid_document, range=range) assert result == [] def test_load_config(config): config = load_config(str(fixtures_dir / "config" / "example.py"), config) # TODO split into smaller tests assert config == { "line_length": 20, "target_version": set(), "pyi": True, "fast": True, "skip_magic_trailing_comma": True, "skip_string_normalization": True, "preview": True, } def test_load_config_target_version(config): config = load_config(str(fixtures_dir / "target_version" / "example.py"), config) assert config["target_version"] == {black.TargetVersion.PY39} def test_load_config_defaults(config): config = load_config(str(fixtures_dir / "example.py"), config) assert config == { "line_length": 88, "target_version": set(), "pyi": False, "fast": False, "skip_magic_trailing_comma": False, "skip_string_normalization": False, "preview": False, } def test_load_config_with_skip_options(config_with_skip_options): config = load_config( str(fixtures_dir / "skip_options" / "example.py"), config_with_skip_options ) assert config == { "line_length": 88, "target_version": set(), "pyi": False, "fast": False, "skip_magic_trailing_comma": True, "skip_string_normalization": True, "preview": False, } def test_entry_point(): distribution = pkg_resources.get_distribution("python-lsp-black") entry_point = distribution.get_entry_info("pylsp", "black") assert entry_point is not None module = entry_point.load() assert isinstance(module, types.ModuleType) def test_pylsp_format_crlf_document( config, unformatted_crlf_document, formatted_crlf_document ): result = pylsp_format_document(config, unformatted_crlf_document) assert result == [ { "range": { "start": {"line": 0, "character": 0}, "end": {"line": 4, "character": 0}, }, "newText": formatted_crlf_document.source, } ] def test_pylsp_format_line_length( config, unformatted_line_length, formatted_line_length ): config.update({"plugins": {"black": {"line_length": 79}}}) result = pylsp_format_document(config, unformatted_line_length) assert result == [ { "range": { "start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 0}, }, "newText": formatted_line_length.source, } ] def test_cache_config(config, unformatted_document): # Cache should be off by default for _ in range(5): pylsp_format_document(config, unformatted_document) assert _load_config.cache_info().hits == 0 # Enable cache config.update({"plugins": {"black": {"cache_config": True}}}) # Cache should be working now for _ in range(5): pylsp_format_document(config, unformatted_document) assert _load_config.cache_info().hits == 4 def test_pylsp_settings(config): plugins = dict(config.plugin_manager.list_name_plugin()) assert "black" in plugins assert plugins["black"] not in config.disabled_plugins config.update({"plugins": {"black": {"enabled": False}}}) assert plugins["black"] in config.disabled_plugins config.update(pylsp_settings()) assert plugins["black"] not in config.disabled_plugins