Switch to python-lsp-ruff and overhaul the Python and Eglot sections

This commit is contained in:
Gerard Vermeulen 2022-12-24 16:36:10 +01:00
parent 00a048ef5b
commit eb6cda3025

View File

@ -3824,8 +3824,9 @@ Listing [[lst:configure-writegood-mode]] configures [[https://github.com/bnbeckw
[[https://github.com/joaotavora/eglot#readme][Emacs polyGLOT (Eglot)]] is an Emacs language-server-protocol client that stays
out of the way. The following listings contribute to a programming language
mode independent [[https://github.com/joaotavora/eglot][Eglot]] configuration:
1. Listing [[lst:ensure-eglot-installation]] ensures installation of [[https://github.com/joaotavora/eglot][Eglot]] with
minimal configuration.
1. Listing [[lst:minimal-eglot-setup][minimal Eglot setup]] ensures installation of [[https://github.com/joaotavora/eglot][Eglot]], shows how to get
debug information from the [[https://github.com/python-lsp/python-lsp-server][Python LSP server]], and adds key bindings to
=eglot-mode-keymap=.
2. Listing [[lst:help-setup-org-src-mode-for-eglot]] and
[[lst:setup-python-org-src-mode-for-eglot]] try to prepare any =org-src-mode=
buffers for use with [[https://github.com/joaotavora/eglot][Eglot]]. They are a refactored implementation of the post
@ -3841,15 +3842,20 @@ mode independent [[https://github.com/joaotavora/eglot][Eglot]] configuration:
file [[info:emacs#Directory Variables][.dir-locals.el]] in the root directory of any project using proper
programming modes).
#+caption[Ensure =eglot= installation]:
#+caption: Ensure =eglot= installation.
#+name: lst:ensure-eglot-installation
#+caption[Ensure =eglot= installation with minimal configuration]:
#+caption: Ensure =eglot= installation with minimal configuration.
#+name: lst:minimal-eglot-setup
#+begin_src emacs-lisp
(when (and (version< emacs-version "28.9.9")
(with-eval-after-load 'emacs
(when (version< emacs-version "28.9.9")
(ensure-package-installation 'eglot))
;; (defvar eglot-server-programs
;; `((python-mode . ("pylsp" "-vvv")))
;; "Shadow the definition of `eglot-server-programs' in `eglot'.")
;; Replace `nil' with `t' for debugging.
(when nil
(defvar eglot-server-programs
`((python-mode . ("pylsp" "-vvv")))
"Shadow the definition of `eglot-server-programs' in `eglot'."))
(with-eval-after-load 'eglot
(define-key eglot-mode-map (kbd "C-c n") #'flymake-goto-next-error)
(define-key eglot-mode-map (kbd "C-c p") #'flymake-goto-prev-error)
@ -4018,6 +4024,7 @@ Listing [[lst:configure-format-all]]:
:PROPERTIES:
:CUSTOM_ID: sec:flymake
:END:
Flymake is an universal on-the-fly syntax checker for Emacs. It is a requirement
of [[https://github.com/joaotavora/eglot][eglot]], but you can use it without [[https://github.com/joaotavora/eglot][eglot]] for instance in [[https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/progmodes/python.el][python-mode]] buffers
that do not visit a file.
@ -4031,6 +4038,7 @@ that do not visit a file.
:PROPERTIES:
:CUSTOM_ID: sec:common-lisp-programming
:END:
Listing [[lst:configure-sly]] configures the [[info:sly#Top][Sly (info)]] Common Lisp IDE for Emacs
for use with [[http://www.sbcl.org/][Steel Bank Common Lisp (sbcl)]]:
1. It configures =sly-default-lisp= and =sly-lisp-implementations= as in the
@ -4302,45 +4310,62 @@ server, before plunging into the configuration steps:
:CUSTOM_ID: sec:python-mode
:END:
Listing [[lst:configure-python]] configures [[https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/progmodes/python.el][python-mode]] to use [[https://flake8.pycqa.org/en/latest/][flake8]] as style
checker and [[https://ipython.org/][IPython]] as shell interpreter and selects the Python interpreter by
means of src_emacs-lisp{(choose-common-python-interpreter)} defined in listing
[[lst:choose-common-python-interpreter]]. The [[https://github.com/pythonic-emacs/pythonic#readme][pythonic]] and [[https://github.com/jorgenschaefer/pyvenv#readme][pyvenv]] packages provide support
to handle Python virtual environments within Emacs. The [[https://github.com/pyenv/pyenv][pyenv]] package provides
support to work with [[https://github.com/pyenv/pyenv#readme][pyenv]] (eventually with [[https://github.com/pyenv/pyenv-virtualenv#readme][pyenv-virtualenv]]) to select between
different python versions (eventually each with different environments). In the
end, all those packages do is to set =python-shell-virtualenv-root= (in case of
[[https://github.com/pyenv/pyenv#readme][pyenv]] and [[https://github.com/pythonic-emacs/pythonic#readme][pythonic]]) and tweak the environment variables and restart the relevant
Python child processes (in case of [[https://github.com/jorgenschaefer/pyvenv#readme][pyvenv]]). Therefore, this setup replaces
those packages with listing [[lst:manage-pyenv]] to manage [[https://github.com/pyenv/pyenv#readme][pyenv]] from within Emacs
and listing [[lst:setting-python-shell-virtualenv-root]] to set
=python-shell-virtualenv-root=.
Listing [[lst:setup-python-mode][setup Python mode]] and [[lst:choose-common-python-interpreter][choose common Python interpreter]] select a common
Python interpreter in a virtual environment for use in =python-mode= and
=ob-python= by means of src_emacs-lisp{(choose-common-python-interpreter)}. The
[[https://github.com/pythonic-emacs/pythonic#readme][pythonic]], [[https://github.com/jorgenschaefer/pyvenv#readme][pyvenv]], and [[https://github.com/jorgenschaefer/pyvenv#readme][pyvenv]] packages allow to handle Python virtual
environments within Emacs. The [[https://github.com/pyenv/pyenv][pyenv]] package provides support to work with
[[https://github.com/pyenv/pyenv#readme][pyenv]] (eventually with [[https://github.com/pyenv/pyenv-virtualenv#readme][pyenv-virtualenv]]) to select between different python
versions (eventually each with different environments). In the end, all those
packages do is to set =python-shell-virtualenv-root= (in case of [[https://github.com/pyenv/pyenv#readme][pyenv]] and
[[https://github.com/pythonic-emacs/pythonic#readme][pythonic]]) or tweak the environment variables and restart the relevant Python
child processes (in case of [[https://github.com/jorgenschaefer/pyvenv#readme][pyvenv]]). Therefore, I replace those packages with
listing [[lst:access-pyenv][access pyenv]] and [[lst:select-python-virtual-environment][select a Python interpreter in a virtual environment]] to
set =python-shell-virtualenv-root= from within Emacs.
#+caption[Configure =python=]:
#+caption: Configure =python=.
#+name: lst:configure-python
Listing [[lst:setup-python-mode][setup Python mode]] and [[lst:choose-common-python-linter][choose common Python linter]] select a common Python
linter for the =python-check-command= and the =python-flymake-command= by means
of src_emacs-lisp{(choose-common-python-linter)} from:
1. [[https://github.com/PyCQA/pyflakes][Pyflakes - simple tool which checks Python source files for errors]].
2. [[https://flake8.pycqa.org/en/latest/][Flake8 - your tool for style guide enforcement]].
3. [[https://github.com/charliermarsh/ruff][Ruff - an extremely fast Python linter written in Rust]].
I use [[https://pypi.org/project/ruff/][Ruff]] to replace [[https://pypi.org/project/flake8/][Flake8]] with a variety of its plugins. [[https://notes.crmarsh.com/][Charlie Marsh]]
explains why he started [[https://pypi.org/project/ruff/][Ruff]] in the post [[https://notes.crmarsh.com/python-tooling-could-be-much-much-faster][Python tooling could be much faster]].
Listing [[lst:pyproject-toml-kickoff][kickoff pyproject.toml proposal]] facilitates dropping a [[https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml][pyproject.toml]]
file into Python projects which anyhow should switch to using a [[https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml][pyproject.toml]]
file as explained in the post [[https://bbc.github.io/cloudfit-public-docs/packaging/this_way_up.html][This Way Up: A Bottom-Up Look At Python Packaging]].
Listing [[lst:setup-cfg-kickoff][kickoff setup.cfg proposal]] implements the [[https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html][using black with other tools]]
rules which make [[https://flake8.pycqa.org/en/latest/][flake8]] or [[https://pycodestyle.pycqa.org/en/latest/][pycodestyle]] agree with [[https://black.readthedocs.io/en/stable/index.html][black's uncompromising style]].
Finally, listing [[lst:flake8-nocolor][flake8-nocolor]] and [[lst:ruff-nocolor][ruff-nocolor]] pipe the =stdout= output of the
[[https://pypi.org/project/flake8/][flake8]] and [[https://pypi.org/project/ruff/][ruff]] executables through =cat= to remove escape sequences.
#+caption[Setup Python mode]:
#+caption: Setup Python mode.
#+name: lst:setup-python-mode
#+begin_src emacs-lisp
(with-eval-after-load 'python
(custom-set-variables
`(python-check-command ,(executable-find "flake8"))
`(python-flymake-command '(,(executable-find "flake8") "-"))
'(python-indent-guess-indent-offset nil)
'(python-shell-completion-native-disabled-interpreters '("ipython3" "pypy")))
(choose-common-python-interpreter 'python))
(choose-common-python-interpreter 'python)
(choose-common-python-linter 'ruff-nocolor))
#+end_src
#+caption[Choose common =python= interpreter for =ob-python= and =python-mode=]:
#+caption: Choose =python= interpreter for =ob-python= and =python-mode=.
#+caption[Choose a common Python interpreter]:
#+caption: Choose a common Python interpreter for =ob-python= and =python-mode=.
#+name: lst:choose-common-python-interpreter
#+begin_src emacs-lisp
(defun choose-common-python-interpreter (&optional program)
"Choose Python interpreter PROGRAM for `ob-python' and `python-mode'."
(defun choose-common-python-interpreter (&optional interpreter)
"Let `ob-python' and `python-mode' use the same Python INTERPRETER."
(interactive)
(let* ((prompt (format "Choose Python (%s): "
(bound-and-true-p python-shell-interpreter)))
(choices '(ipython python))
(choice (if (member program choices)
(symbol-name program)
(choice (if (member interpreter choices)
(symbol-name interpreter)
(completing-read prompt choices nil t))))
(when (boundp 'org-babel-python-command)
(pcase choice
@ -4375,9 +4400,44 @@ and listing [[lst:setting-python-shell-virtualenv-root]] to set
python-shell-interpreter))))
#+end_src
#+caption[Manage =pyenv=]:
#+caption: Manage =pyenv=.
#+name: lst:manage-pyenv
#+caption[Choose a common Python linter]:
#+caption: Choose a common Python linter for =python-check-command= and
#+caption: =python-flymake-command=.
#+name: lst:choose-common-python-linter
#+begin_src emacs-lisp
(defun choose-common-python-linter (&optional linter)
"Let `python-check-command' and `python-flymake-command' use the same LINTER."
(interactive)
(let* ((prompt (format "Choose Python checker (%s): "
(bound-and-true-p python-check-command)))
(choices '(flake8-nocolor pyflakes ruff-nocolor))
(choice (if (member linter choices)
(symbol-name linter)
(completing-read prompt choices nil t))))
(when (and (boundp 'python-check-command) (boundp 'python-flymake-command))
(pcase choice
("flake8-nocolor"
(custom-set-variables
`(python-check-command ,(executable-find choice))
`(python-flymake-command (list ,(executable-find choice) "-"))))
("pyflakes"
(custom-set-variables
`(python-check-command ,(executable-find choice))
`(python-flymake-command `(,(executable-find choice)))))
("ruff-nocolor"
(custom-set-variables
`(python-check-command ,(executable-find choice))
`(python-flymake-command
(list ,(executable-find choice) "--stdin-filename" "stdin" "-")))))
(when (bound-and-true-p python-check-custom-command)
(setq python-check-custom-command nil))
(message "Python checker commands are %S and %S"
python-check-command python-flymake-command))))
#+end_src
#+caption[Access =pyenv=]:
#+caption: Access =pyenv=.
#+name: lst:access-pyenv
#+begin_src emacs-lisp
(when (executable-find "pyenv")
(defun pyenv-full-path (version)
@ -4421,9 +4481,9 @@ Complete the result with \"system\"."
(error "%s" (string-trim output))))))
#+end_src
#+caption[Setting =python-shell-virtualenv-root=]:
#+caption: Setting =python-shell-virtualenv-root=.
#+name: lst:setting-python-shell-virtualenv-root
#+caption[Select the Python virtual environment]:
#+caption: Select the Python virtual environment.
#+name: lst:select-python-virtual-environment
#+begin_src emacs-lisp
(with-eval-after-load 'python
(when (cl-every #'fboundp '(pyenv-full-path
@ -4464,65 +4524,174 @@ Complete the result with \"system\"."
python-shell-virtualenv-root)))))
#+end_src
#+caption[Kickoff =pyproject.toml= proposal]:
#+caption: Kickoff =pyproject.toml= proposal.
#+name: lst:pyproject-toml-kickoff
#+begin_src toml :tangle pyproject.toml
# [project]
# name = "fancy-name"
# [build-system]
# requires = ["setuptools", "wheel"]
# build-backend = "setuptools.build_meta"
[tool.black]
line-length = 88
[tool.ruff]
line-length = 88
select = [
"ARG", # flake8-unused-arguments
"B", # flake8-bugbear
"C", # mccabe
"E", # pycodestyle
"D", # pydocstyle
"F", # pyflakes
# "W", # ruff emits no pycodestyle warnings yet
]
ignore = [
# https://www.pydocstyle.org/en/stable/error_codes.html#default-conventions
# pydocstyle numpy convention:
"D107", # ignore: missing docstring in __init__
"D203", # ignore: single blank line required before class docstring
"D212", # ignore: multi-line docstring summary should start at the first line
"D213", # ignore: multi-line docstring summary should start at the second line
"D402", # ignore: first line should not be the function signature
"D413", # ignore: missing blank line after last section
"D415", # ignore: 1st line should end with a ".", "?", or "?"
"D416", # ignore: section name should end with a ":"
"D417", # ignore: missing argument descriptions in the docstring
]
[tool.ruff.mccabe]
max-complexity = 15
# Local Variables:
# mode: conf-toml
# End:
#+end_src
#+caption[Kickoff =setup.cfg= proposal]:
#+caption: Kickoff =setup.cfg= proposal.
#+name: lst:setup-cfg-kickoff
#+begin_src toml :tangle setup.cfg
[flake8]
docstring-convention = numpy
extend-select = B,F,W
extend-ignore = W503
max-complexity = 15
max-line-length = 88
[pycodestyle]
ignore = W503
max-line-length = 88
# Local Variables:
# mode: conf-toml
# End:
#+end_src
#+caption[Wrap =flake8= to remove color from text output]:
#+caption: Wrap =flake8= to remove color from text output.
#+header: :tangle-mode (identity #o755)
#+name: lst:flake8-nocolor
#+begin_src shell :noeval :tangle ~/bin/flake8-nocolor
#!/bin/sh
flake8 "$@" | cat
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# sh-basic-offset: 2
# End:
#+end_src
#+caption[Wrap =ruff= to remove color from text output]:
#+caption: Wrap =ruff= to remove color from text output.
#+header: :tangle-mode (identity #o755)
#+name: lst:ruff-nocolor
#+begin_src shell :noeval :tangle ~/bin/ruff-nocolor
#!/bin/sh
ruff "$@" | cat
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# sh-basic-offset: 2
# End:
#+end_src
*** [[https://github.com/joaotavora/eglot][Eglot]] for [[https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/progmodes/python.el][python-mode]]
:PROPERTIES:
:CUSTOM_ID: sec:eglot-python
:END:
Listing [[lst:configure-eglot+pylsp-with-pyls-flake8]] configures [[https://github.com/joaotavora/eglot][eglot]] for [[https://www.python.org][Python]]
using the [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] with the [[https://github.com/emanspeaks/pyls-flake8#readme][pyls-flake8]] plugin for the easiest way to
let [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] use [[https://github.com/PyCQA/flake8][flake8]]. In order to enable all builtin
[[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] capabilities, ensure installation of the Python packages
[[https://github.com/hhatto/autopep8#readme][autopep8]], [[https://github.com/PyCQA/flake8][flake8]], [[https://github.com/PyCQA/pydocstyle#readme][pydocstyle]], [[https://github.com/PyCQA/pylint#readme][pylint]], [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]], [[https://github.com/python-rope/rope#readme][rope]], and [[https://github.com/google/yapf#readme][yapf]]. The
latest [[https://github.com/python-lsp/python-lsp-server#readme][python-lsp-server]] documentation tells to configure it for [[https://github.com/PyCQA/flake8][flake8]] as in
listing [[lst:configure-eglot+pylsp-sans-pyls-flake8]]. This method is under
investigation (it feels flaky since it shows some error messages twice).
Listing [[lst:configure-eglot+pylsp-ruff]] configures [[https://github.com/joaotavora/eglot][eglot]] for [[https://www.python.org][Python]] using the
[[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] with the [[https://github.com/python-lsp/python-lsp-ruff#readme][pylsp-ruff]] plugin for the easiest way to let
[[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] use [[https://pypi.org/project/ruff/][Ruff]]. The latest [[https://github.com/python-lsp/python-lsp-server#readme][python-lsp-server]] documentation tells to
configure it for [[https://github.com/PyCQA/flake8][flake8]] as in listing [[lst:configure-eglot+pylsp+flake8]]. In order
to enable all builtin [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] capabilities, ensure installation of the
Python packages [[https://github.com/hhatto/autopep8#readme][autopep8]], [[https://github.com/PyCQA/flake8][flake8]], [[https://github.com/PyCQA/pydocstyle#readme][pydocstyle]], [[https://github.com/PyCQA/pylint#readme][pylint]], [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]], [[https://github.com/python-rope/rope#readme][rope]],
and [[https://github.com/google/yapf#readme][yapf]]. This method is under investigation (it feels flaky since it shows
some error messages twice).
Listing [[lst:eglot-directory-variables-for-python]] shows a proper [[info:emacs#Directory Variables][.dir-locals.el]]
file in the root directory of any [[https://www.python.org][Python]] project to start [[https://github.com/joaotavora/eglot][eglot]] automatically
according to the configuration in listing [[lst:eglot-maybe-ensure]]. Type
{{{kbd(M-x eglot-show-workspace-configuration)}}} to dump a =JSON=
representation of src_emacs-lisp{eglot-workspace-configuration} for debugging.
The comment in listing [[lst:ensure-eglot-installation]] also shows how to increase
the the verbosity of [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] log output for debugging.
The comment in listing [[lst:minimal-eglot-setup][minimal Eglot setup]] also shows how to decrease
or to increase the verbosity of [[https://github.com/python-lsp/python-lsp-server][Python LSP server]] log output for debugging.
#+caption[Configure =eglot= with =python-lsp-server= with =pyls_flake8=]:
#+caption: Configure =eglot= with =python-lsp-server= with =pyls_flake8=.
#+name: lst:configure-eglot+pylsp-with-pyls-flake8
#+caption[Configure =eglot= with =python-lsp-ruff=]:
#+caption: Configure =eglot= with =python-lsp-ruff=.
#+name: lst:configure-eglot+pylsp-ruff
#+begin_src emacs-lisp
(with-eval-after-load 'eglot
(setq-default
eglot-workspace-configuration
;; Disable the `:pyls_flake8' plugin to fall back to pycodestyle.
;; Enable the `:pyls_ruff' plugin and ensure to disable the
;; `:flake8', `:mccabe', and `:pycodestye' plugins.
'(:pylsp (:plugins
(:pyls_flake8
(:pylsp_ruff
(:enabled t)
:flake8
(:enabled :json-false)
:mccabe
(:enabled :json-false)
:pycodestyle
(:enabled :json-false)
:jedi
(:auto_import_modules ["numpy"])
:jedi_completion
(:cache_for ["astropy"]))))))
#+end_src
#+caption[Configure =eglot= with =python-lsp-server= sans =pyls_flake8=]:
#+caption: Configure =eglot= with =python-lsp-server= sans =pyls_flake8=.
#+name: lst:configure-eglot+pylsp-sans-pyls-flake8
#+caption[Configure =eglot= with =python-lsp-server= and the =flake8= plugin]:
#+caption: Configure =eglot= with =python-lsp-server= and the =flake8= plugin.
#+name: lst:configure-eglot+pylsp+flake8
#+begin_src emacs-lisp :tangle no
(with-eval-after-load 'eglot
(setq-default
eglot-workspace-configuration
;; How to use flake8 instead of pycodestyle officially, see:
;; How to use flake8 instead of pycodestyle or ruff, see:
;; https://github.com/python-lsp/python-lsp-server#configuration
'(:pylsp (:configurationSources
["flake8"]
:plugins
(:pycodestyle
(:enabled :json-false)
(:flake8
(:enabled t)
:mccabe
(:enabled :json-false)
:pycodestyle
(:enabled :json-false)
:pyflakes
(:enabled
:json-false)
:flake8
(:enabled t)
(:enabled :json-false)
:pylsp-ruff
(:enabled :json-false)
:jedi
(:auto_import_modules ["numpy"])
:jedi_completion
@ -4530,9 +4699,8 @@ the the verbosity of [[https://github.com/python-lsp/python-lsp-server][python-l
#+end_src
#+caption[A =.dir-locals.el= proposal to launch =eglot= automatically]:
#+caption: A =.dir-locals.el= proposal for any Python project
#+caption: or any Org-mode project tangling Python files
#+caption: to launch =eglot= automatically.
#+caption: A =.dir-locals.el= file proposal for Python projects or Org-mode
#+caption: projects tangling Python files to launch =eglot= automatically.
#+name: lst:eglot-directory-variables-for-python
#+begin_src emacs-lisp :tangle dir-locals.el
;; A .dir-locals.el file proposal in the root of any
@ -4540,10 +4708,17 @@ the the verbosity of [[https://github.com/python-lsp/python-lsp-server][python-l
;; to launch eglot automatically.
((nil ;; nil, since Emacs-29.1 filters out irrelevant variable names.
. ((eglot-workspace-configuration
;; Disable the `:pyls_flake8' plugin to fall back to pycodestyle.
;; Enable the `:pyls_ruff' plugin and ensure to disable the
;; `:flake8', `:mccabe', and `:pycodestye' plugins.
. (:pylsp (:plugins
(:pyls_flake8
(:pylsp_ruff
(:enabled t)
:flake8
(:enabled :json-false)
:mccabe
(:enabled :json-false)
:pycodestyle
(:enabled :json-false)
:jedi
(:auto_import_modules ["numpy"])
:jedi_completion
@ -4565,43 +4740,6 @@ the the verbosity of [[https://github.com/python-lsp/python-lsp-server][python-l
| eldoc-doc-buffer | eglot-mode-map | {{{kbd(C-h .)}}} |
|-------------------------+----------------+------------------|
Listing [[lst:pyproject-toml-kick-off]] and [[lst:setup-cfg-kick-off]] implement the
rules in [[https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html][using black with other tools]] in order to make [[https://flake8.pycqa.org/en/latest/][flake8]] or [[https://pycodestyle.pycqa.org/en/latest/][pycodestyle]]
agree with [[https://black.readthedocs.io/en/stable/index.html][black's uncompromising style]].
#+caption[Kick starting a =pyproject.toml= file]:
#+caption: Kick starting a =pyproject.toml= file.
#+name: lst:pyproject-toml-kick-off
#+begin_src toml :tangle pyproject.toml
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[tool.black]
line-length = 88
# Local Variables:
# mode: conf-toml
# End:
#+end_src
#+caption[Kick starting a =setup.cfg= file]:
#+caption: Kick starting a =setup.cfg= file.
#+name: lst:setup-cfg-kick-off
#+begin_src toml :tangle setup.cfg
[flake8]
max-line-length = 88
extend-ignore = E203
[pycodestyle]
ignore = E203
max-line-length = 88
# Local Variables:
# mode: conf-toml
# End:
#+end_src
Listing [[lst:make-pylsp-server-patch]] is useful to propagate eventual patches of
a local fork of [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]].