A JupyterLab extension to facilitate invocation of code formatters for multiple programming languages.
—
Server-side HTTP request handlers for formatter discovery and code formatting operations with authentication, error handling, and caching support.
Handles requests for discovering available code formatters.
class FormattersAPIHandler(APIHandler):
@tornado.web.authenticated
def get(self) -> None:
"""Show what formatters are installed and available."""
passEndpoint: GET /jupyterlab_code_formatter/formatters[?cached]
Query Parameters:
cached (optional) - Use cached formatter availability checksResponse Format:
{
"formatters": {
"formatter_name": {
"enabled": boolean,
"label": "Human-readable name"
}
}
}Handles code formatting requests.
class FormatAPIHandler(APIHandler):
@tornado.web.authenticated
def post(self) -> None:
"""Format code using specified formatter."""
passEndpoint: POST /jupyterlab_code_formatter/format[?cached]
Request Body:
{
"code": ["string array of code"],
"formatter": "formatter_name",
"notebook": boolean,
"options": {
"formatter_specific_options": "values"
}
}Response Format:
{
"code": [
{
"code": "formatted_code_string"
}
]
}Error Response:
{
"code": [
{
"error": "error_message"
}
]
}Registers API handlers with the Jupyter server.
def setup_handlers(web_app) -> None:
"""Register API handlers with Jupyter server."""
passParameters:
web_app - Tornado web application instancefrom jupyterlab_code_formatter.handlers import setup_handlers
def _load_jupyter_server_extension(server_app):
"""Register the API handlers to receive HTTP requests."""
setup_handlers(server_app.web_app)
server_app.log.info("Registered jupyterlab_code_formatter server extension")# Client request: GET /jupyterlab_code_formatter/formatters
# Handler processes request:
def get(self) -> None:
use_cache = self.get_query_argument("cached", default=None)
response_data = {
"formatters": {
name: {
"enabled": formatter.cached_importable if use_cache else formatter.importable,
"label": formatter.label,
}
for name, formatter in SERVER_FORMATTERS.items()
}
}
self.finish(json.dumps(response_data))# Client request: POST /jupyterlab_code_formatter/format
# Handler processes request:
def post(self) -> None:
data = json.loads(self.request.body.decode("utf-8"))
formatter_instance = SERVER_FORMATTERS.get(data["formatter"])
use_cache = self.get_query_argument("cached", default=None)
if formatter_instance is None or not (
formatter_instance.cached_importable if use_cache else formatter_instance.importable
):
self.set_status(404, f"Formatter {data['formatter']} not found!")
self.finish()
return
notebook = data["notebook"]
options = data.get("options", {})
formatted_code = []
for code in data["code"]:
try:
result = formatter_instance.format_code(code, notebook, **options)
formatted_code.append({"code": result})
except Exception as e:
formatted_code.append({"error": str(e)})
self.finish(json.dumps({"code": formatted_code}))URL: /jupyterlab_code_formatter/formatters
Method: GET
Authentication: Required (Jupyter token)
Query Parameters:
cached (optional) - Enable cached availability checksSuccess Response (200):
{
"formatters": {
"black": {
"enabled": true,
"label": "Apply Black Formatter"
},
"isort": {
"enabled": true,
"label": "Apply Isort Formatter"
},
"yapf": {
"enabled": false,
"label": "Apply YAPF Formatter"
}
}
}Example Requests:
# Get current formatter availability
curl -H "Authorization: token YOUR_TOKEN" \
http://localhost:8888/jupyterlab_code_formatter/formatters
# Get cached formatter availability
curl -H "Authorization: token YOUR_TOKEN" \
http://localhost:8888/jupyterlab_code_formatter/formatters?cachedURL: /jupyterlab_code_formatter/format
Method: POST
Authentication: Required (Jupyter token)
Content-Type: application/json
Query Parameters:
cached (optional) - Use cached formatter availabilityRequest Body:
{
"code": [
"def hello():\n pass",
"import os\nimport sys"
],
"formatter": "black",
"notebook": true,
"options": {
"line_length": 88,
"string_normalization": true
}
}Success Response (200):
{
"code": [
{
"code": "def hello():\n pass\n"
},
{
"code": "import os\nimport sys\n"
}
]
}Error Response (200 with errors):
{
"code": [
{
"error": "cannot use --safe with this Python version"
}
]
}Formatter Not Found (404):
Formatter unknown_formatter not found!Example Request:
curl -X POST \
-H "Authorization: token YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"code": ["def hello():\n pass"],
"formatter": "black",
"notebook": true,
"options": {"line_length": 88}
}' \
http://localhost:8888/jupyterlab_code_formatter/formatBoth handlers use Tornado's @tornado.web.authenticated decorator:
Request processing includes validation:
Comprehensive error handling:
def setup_handlers(web_app):
host_pattern = ".*$"
base_url = web_app.settings["base_url"]
# Register formatters endpoint
web_app.add_handlers(
host_pattern,
[
(
url_path_join(base_url, "jupyterlab_code_formatter/formatters"),
FormattersAPIHandler,
)
],
)
# Register format endpoint
web_app.add_handlers(
host_pattern,
[
(
url_path_join(base_url, "/jupyterlab_code_formatter/format"),
FormatAPIHandler,
)
],
)Handlers respect JupyterLab's base URL configuration:
web_app.settings["base_url"]url_path_join() for proper URL constructionBoth handlers support caching for improved performance:
?cached enables cached checksimportable property checksHandlers are designed for async operation:
Robust error handling ensures server stability:
Install with Tessl CLI
npx tessl i tessl/pypi-jupyterlab-code-formatter