A Python formatter with Black-compatible output
This Python formatter runs the Ruff formatter, compiled to WebAssembly, directly in your browser. Ruff's formatter was designed as a drop-in replacement for Black, the "uncompromising" formatter adopted by a large part of the Python ecosystem, and produces nearly identical output. You get the same deterministic style Black would give you, without installing anything.
Example
import os,sys
from typing import Optional
def load(path:str,default:Optional[dict]=None)->dict:
if not os.path.exists(path): return default or {}
with open(path,encoding='utf-8') as f:
rows=[l.strip() for l in f if l.strip() and not l.startswith('#')]
return {k:v for k,v in (r.split('=',1) for r in rows)}
class Config( object ):
def __init__(self,values={ 'debug':False,'level':3, }):
self.values=values
import os, sys
from typing import Optional
def load(path: str, default: Optional[dict] = None) -> dict:
if not os.path.exists(path):
return default or {}
with open(path, encoding="utf-8") as f:
rows = [l.strip() for l in f if l.strip() and not l.startswith("#")]
return {k: v for k, v in (r.split("=", 1) for r in rows)}
class Config(object):
def __init__(
self,
values={
"debug": False,
"level": 3,
},
):
self.values = values
Indentation became a consistent 4 spaces, the one-line if body moved to its own line, operators and annotations gained PEP 8 spacing, strings switched to double quotes, and top-level definitions are separated by two blank lines. The dictionary in __init__ was exploded one item per line because of the magic trailing comma after 'level':3,.
The magic trailing comma
Black and Ruff treat a trailing comma inside brackets as an instruction: keep this collection expanded, one element per line, even if it would fit on one line. Remove the comma and the formatter joins the elements again when they fit.
colors = [
"red",
"green",
]
sizes = ["s", "m"]
The first list had a trailing comma in the input, the second did not. This is the easiest way to control layout without any configuration.
Options explained
- Indent – 4 spaces is the PEP 8 recommendation and the default. Tabs are available, but PEP 8 advises spaces for new code.
- Line width – 88 by default, Black's choice: about 10% more than PEP 8's 79 characters, which Black's authors found reduces awkward wrapping. Set 79 for strict PEP 8, or 100–120 if your team prefers.
- Quotes – Double matches Black. Single prefers
'. Preserve leaves each string's quotes as written. In all modes, a string keeps the quote that avoids escaping, so"it's"is never turned into'it\'s'.
Formatter vs. linter
A formatter only changes layout; it never changes what your program does. That also means it leaves some PEP 8 issues alone:
import os, sysstays on one line; PEP 8 prefers separate import lines, a rule enforced by linters such as Ruff'sE401or isort-style import sorting.- The mutable default argument
values={...}in the example is a classic bug, flagged by linters (flake8-bugbear'sB006), but not by a formatter. - Unused imports and variables are not removed.
In practice teams run both: the formatter on save, the linter in CI.
Common Python syntax errors
- "Expected ':'" – a missing colon after
if,for,deforclass. - "Unexpected indentation" / "Expected an indented block" – inconsistent indentation, often from mixing tabs and spaces in pasted code.
- Unclosed brackets – a missing
)is reported at the end of the file or at the next statement, so look upward from the reported line. - Python 2 code such as
print "hi"is not valid Python 3 and cannot be formatted.
What is supported
Ruff's parser handles modern Python 3 syntax: f-strings, match statements, walrus :=, type hints with X | Y, async / await, decorators and the type statement. Comments, docstrings and # fmt: off / # fmt: on regions are respected.
Need Python classes for JSON data? JSON to Python generates dataclasses you can format here. For quick one-off snippets in other languages, the Online Code Editor is handy. Your code is formatted locally and never uploaded.