Skip to content

Commit 4a15929

Browse files
Adds Pygments JS RPC example (#50)
* Adds Pygments JS RPC example * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 606f91f commit 4a15929

18 files changed

+13822
-0
lines changed

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ repos:
1818
rev: "v2.4.1"
1919
hooks:
2020
- id: codespell
21+
exclude: |
22+
(?x)^(
23+
.*package-lock\.json|
24+
.*worker-configuration\.d\.ts
25+
)$
2126
2227
- repo: https://github.com/astral-sh/ruff-pre-commit
2328
rev: v0.14.3

13-js-api-pygments/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 13-js-api-pygments – Python RPC Server & TS Client
2+
3+
This example shows how to use **JS RPC** to connect a **Python Worker** (server) to a **TypeScript Worker** (client).
4+
5+
The Python Worker exposes a Pygments-powered `highlight_code` RPC method. The TS Worker calls this method to syntax-highlight code on the server side and renders the result as HTML.
6+
7+
## Project Layout
8+
9+
- `py/`
10+
- Python Worker (RPC server)
11+
- Exposes `HighlighterRpcService.highlight_code()` over RPC using Pygments
12+
- `ts/`
13+
- TypeScript Worker (RPC client)
14+
- Calls the Python Worker via a `PYTHON_RPC` service binding and renders the highlighted code
15+
16+
## Prerequisites
17+
18+
- `uv` installed for Python dependency management
19+
- Node.js + npm
20+
21+
## How to Run
22+
23+
### 1. Install and run the Python RPC server
24+
25+
```bash
26+
cd 13-js-api-pygments/py
27+
uv run pywrangler dev
28+
```
29+
30+
This starts the Python Worker defined in `py/wrangler.jsonc`.
31+
32+
### 2. Install and run the TypeScript RPC client
33+
34+
Open a **second terminal**:
35+
36+
```bash
37+
cd 13-js-api-pygments/ts
38+
npm run dev
39+
```
40+
41+
This starts the TypeScript Worker defined in `ts/wrangler.jsonc`, which has a `services` binding:
42+
43+
```jsonc
44+
"services": [
45+
{
46+
"binding": "PYTHON_RPC",
47+
"service": "py-rpc-server"
48+
}
49+
]
50+
```
51+
52+
### 3. Try it out
53+
54+
- Visit the URL printed by `npm run dev` (usually `http://localhost:8787/`).
55+
- The TS Worker will:
56+
- Call the Python Worker via RPC
57+
- Have Pygments highlight a sample TypeScript snippet
58+
- Return a full HTML page with highlighted code and styling
59+
60+
If you change the Python RPC method or the sample code in `ts/src/index.ts`, just reload the page to see the new output.

13-js-api-pygments/py/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "py-rpc-server",
3+
"version": "0.0.0",
4+
"private": true,
5+
"scripts": {
6+
"deploy": "uv run pywrangler deploy",
7+
"dev": "uv run pywrangler dev",
8+
"start": "uv run pywrangler dev"
9+
},
10+
"devDependencies": {
11+
"wrangler": "^4.49.0"
12+
}
13+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[project]
2+
name = "py-rpc-server"
3+
version = "0.1.0"
4+
description = "Python RPC server which responds to highlight_code RPC calls"
5+
readme = "README.md"
6+
requires-python = ">=3.12"
7+
dependencies = [
8+
"webtypy>=0.1.7",
9+
"pygments",
10+
]
11+
12+
[dependency-groups]
13+
dev = [
14+
"workers-py",
15+
"workers-runtime-sdk"
16+
]

13-js-api-pygments/py/src/entry.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from workers import WorkerEntrypoint, Response, Request
2+
from pygments import highlight
3+
from pygments.lexers import get_lexer_by_name, guess_lexer
4+
from pygments.formatters import HtmlFormatter
5+
from pygments.util import ClassNotFound
6+
7+
8+
class Default(WorkerEntrypoint):
9+
async def fetch(self, request: Request):
10+
return Response("Python RPC server is running. Use RPC to call methods.")
11+
12+
async def highlight_code(self, code: str, language: str = None) -> dict:
13+
"""
14+
Syntax highlight code using Pygments.
15+
16+
Args:
17+
code: The source code to highlight
18+
language: Programming language (e.g., 'python', 'javascript', 'rust')
19+
If None, will attempt to guess the language
20+
21+
Returns:
22+
Dict with highlighted HTML and CSS
23+
"""
24+
try:
25+
# Get the appropriate lexer
26+
if language:
27+
lexer = get_lexer_by_name(language, stripall=True)
28+
else:
29+
lexer = guess_lexer(code)
30+
except ClassNotFound:
31+
return {
32+
"error": f"Language '{language}' not found",
33+
"html": f"<pre>{code}</pre>",
34+
"css": "",
35+
"language": "unknown",
36+
}
37+
38+
# Create formatter with line numbers and styling
39+
formatter = HtmlFormatter(linenos=True, cssclass="highlight", style="monokai")
40+
41+
# Generate highlighted HTML
42+
highlighted_html = highlight(code, lexer, formatter)
43+
44+
# Get the CSS for styling
45+
css = formatter.get_style_defs(".highlight")
46+
47+
return {
48+
"html": highlighted_html,
49+
"css": css,
50+
"language": lexer.name,
51+
"language_alias": lexer.aliases[0] if lexer.aliases else None,
52+
}

13-js-api-pygments/py/uv.lock

Lines changed: 172 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"$schema": "node_modules/wrangler/config-schema.json",
3+
"name": "py-rpc-server",
4+
"main": "src/entry.py",
5+
"compatibility_date": "2025-11-02",
6+
"compatibility_flags": [
7+
"python_workers"
8+
],
9+
"observability": {
10+
"enabled": true
11+
}
12+
}

0 commit comments

Comments
 (0)