-
-
Save jbwhit/eecdd1cac2756df85ad165f437445b0b to your computer and use it in GitHub Desktop.
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"id": "f98530b7-48d0-4cec-994c-cc0782ddcb2d", | |
"metadata": {}, | |
"source": [ | |
"# Using `ruff` in JupyterLab with `jupyterlab_code_formatter`\n", | |
"\n", | |
"- **ruff** formatter: https://docs.astral.sh/ruff/ \n", | |
"- **jupyterlab_code_formatter** JupyterLab plugin: https://jupyterlab-code-formatter.readthedocs.io/\n", | |
"\n", | |
"\n", | |
"# Watch the walkthrough video for details: https://youtu.be/FXA6PJUabKQ " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "753ec684-e78a-4820-ab86-eec0ff4d394f", | |
"metadata": {}, | |
"source": [ | |
"## Install `jupyterlab_code_formatter` \n", | |
"\n", | |
"This snippet creates a conda (mamba) environment called `example`.\n", | |
"\n", | |
"```bash\n", | |
"mamba create -n example python=3.11\n", | |
"mamba activate example\n", | |
"mamba install -c conda-forge black ruff isort jupyterlab_code_formatter jupyterlab\n", | |
"```\n", | |
"\n", | |
"## Generate (or if it exists, update) your jupyter config\n", | |
"\n", | |
"This command will generate (or if it already exists ask if you want to overwrite) a jupyter config for your system.\n", | |
"\n", | |
"```bash\n", | |
"jupyter lab --generate-config\n", | |
"```\n", | |
"\n", | |
"This command returns the path to the new config.\n", | |
"\n", | |
"## Copy and paste this code snippet to the end of your jupyter config file:\n", | |
"\n", | |
"Special thanks to [Ryan Tam](https://twitter.com/ryantam626) for providing not only the `jupyterlab_code_formatter` plugin itself, but the modification code snippet below to make the new features of ruff available.\n", | |
"\n", | |
"Misc links: \n", | |
"\n", | |
"```python\n", | |
"from typing import List\n", | |
"from jupyterlab_code_formatter.formatters import BaseFormatter, handle_line_ending_and_magic, SERVER_FORMATTERS, logger\n", | |
"import subprocess\n", | |
"\n", | |
"class RuffFormatFormatter(BaseFormatter):\n", | |
"\n", | |
" label = \"Apply Ruff Format Formatter - Confirmed working for 0.1.3\"\n", | |
" \n", | |
" def __init__(self) -> None:\n", | |
" try:\n", | |
" from ruff.__main__ import find_ruff_bin\n", | |
"\n", | |
" self.ruff_bin = find_ruff_bin()\n", | |
" except (ImportError, FileNotFoundError):\n", | |
" self.ruff_bin = \"ruff\"\n", | |
"\n", | |
" @property\n", | |
" def importable(self) -> bool:\n", | |
" return True\n", | |
"\n", | |
" @handle_line_ending_and_magic\n", | |
" def format_code(self, code: str, notebook: bool, args: List[str] = [], **options) -> str:\n", | |
" process = subprocess.run(\n", | |
" [self.ruff_bin, \"format\", \"-\"],\n", | |
" input=code,\n", | |
" stdout=subprocess.PIPE,\n", | |
" stderr=subprocess.PIPE,\n", | |
" universal_newlines=True,\n", | |
" encoding=\"utf-8\",\n", | |
" )\n", | |
"\n", | |
" if process.stderr:\n", | |
" logger.info(process.stderr)\n", | |
" return code\n", | |
" else:\n", | |
" return process.stdout\n", | |
"\n", | |
" \n", | |
"SERVER_FORMATTERS[\"ruff_format\"] = RuffFormatFormatter()\n", | |
"```\n", | |
"\n", | |
"Remember to restart your jupyter instance. " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "36a0c2dd-7432-49d3-a948-60c3f1e2ba4b", | |
"metadata": {}, | |
"source": [ | |
"## Start jupyter\n", | |
"\n", | |
" Edit the settings \n", | |
" > click the JSON editor \n", | |
" > Jupyterlab Code Formatter\n", | |
" > Edit the python default formatter to say \"ruff_format\"\n", | |
"\n", | |
"The top few lines will look like this after you've made this edit: \n", | |
"\n", | |
"```JSON\n", | |
"{\n", | |
" \"preferences\": {\n", | |
" \"default_formatter\": {\n", | |
" \"python\": \"ruff_format\",\n", | |
"```\n", | |
"\n", | |
"And now if you click the button in the toolbar, it will format the whole notebook using `ruff`. " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "ff6b2d90-69b1-4302-a2ee-bff168939290", | |
"metadata": {}, | |
"source": [ | |
"# Bonus -- if you want to run the formatter on only a single cell with a keyboard shortcut\n", | |
"\n", | |
"## Add this snippet to the Jupyter keyboard shortcuts to enable formatting a single cell\n", | |
"\n", | |
" Edit the settings \n", | |
" > click the JSON editor \n", | |
" > Keyboard Shortcuts\n", | |
" > Edit the right half (User preferences)\n", | |
"\n", | |
"copy and paste this snippet to bind `Ctrl K` to format the cell.\n", | |
"\n", | |
"```json\n", | |
" {\n", | |
" \"command\": \"jupyterlab_code_formatter:format\",\n", | |
" \"keys\": [\n", | |
" \"Ctrl K\"\n", | |
" ],\n", | |
" \"selector\": \".jp-Notebook.jp-mod-editMode\",\n", | |
" \"args\": {}\n", | |
" },\n", | |
"```\n", | |
"\n", | |
"\n", | |
"Note, you have to be in Edit mode in the cell for this shortcut to work. " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "e5195f92-e679-4f1e-b3cd-94ce7e2ecc5c", | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "python3", | |
"language": "python", | |
"name": "python3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |
Thank you for the comment! I'm thinking of revisiting this and seeing if I can make a PR to the extension itself. I'll add your change.
Hi!
Nice Guide!
in this Guide you just use the formater right?
Do you know how to get the ruff linter to work in jupyterlab?
I had to add the import "from typing import List" to the config file snippet. @jbwhit
Thanksk @nicornk -- I've added that to my snippet!
❤️
I am very happy with your config that can also reflect ruff configs written in pyproject.toml
such as:
[tool.ruff.format]
quote-style = "single"
If you guys also want it to run check
before format
, then use this:
@handle_line_ending_and_magic
def format_code(
self, code: str, notebook: bool, args: List[str] = [], **options
) -> str:
# Lint
linting = subprocess.run(
[self.ruff_bin, "check", "--fix", "--exit-zero", "-"],
input=code,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
encoding="utf-8",
)
# Format
linted_code = linting.stdout
process = subprocess.run(
[self.ruff_bin, "format", "-"],
input=linted_code,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
encoding="utf-8",
)
if process.stderr:
logger.info(process.stderr)
return code
else:
return process.stdout
I followed steps from YT vid, still could not get
Formatter_icon
work w/ruff_format
in activeJupyter notebook
. I have installed python 3.10<= usingvenv
. I likedkeyboard shortcut
which also doesn't work ss attached below. Any help appreciated. Discord: JordanTheDodger
Update: I finally figured out the error. I am elated to capture my first error with new kid on the block RUFFFFF :100 Kudos @jbwhit
I followed steps from YT vid, still could not get
Formatter_icon
work w/ruff_format
in activeJupyter notebook
. I have installed python 3.10<= usingvenv
. I likedkeyboard shortcut
which also doesn't work ss attached below. Any help appreciated. Discord: JordanTheDodgerUpdate: I finally figured out the error. I am elated to capture my first error with new kid on the block RUFFFFF :100 Kudos @jbwhit
Was the error on from this document or something on your side?
I configured virtual env
using conda. For some reason, ruff
configuration didn't register. I had to shutdown Jupyter and virtual env. Technically not a "true" error just operational hiccup. 👍
Thank you very much for the great guide! A problem for me was that cells containing non-ascii unicode characters were not formatted. I could fix this by adding
encoding="utf-8"
in thesubprocess.run()
call.