From 6d562d10f9a32db5526316cfe5153a08c56011d1 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Wed, 18 Feb 2026 17:58:21 -0600 Subject: [PATCH 1/6] feat: use Pygments for code sample syntax highlighting Replace manual regex-based HTML formatting with Pygments PythonConsoleLexer for proper syntax highlighting of homepage code samples. Addresses reviewer feedback on #2538. - Add Pygments dependency - Clean up code samples to plain text (no hand-written HTML spans) - Map Pygments CSS classes (.c1, .go, .gp) in stylesheet - Fix HTML entity escaping (now handled by Pygments automatically) Co-Authored-By: Tim Hoffmann Co-Authored-By: Claude Opus 4.6 --- apps/codesamples/factories.py | 77 ++++++++++++++++++----------------- pyproject.toml | 1 + static/sass/style.css | 9 ++-- 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/apps/codesamples/factories.py b/apps/codesamples/factories.py index 654364d7a..d770cccea 100644 --- a/apps/codesamples/factories.py +++ b/apps/codesamples/factories.py @@ -4,6 +4,9 @@ import factory from factory.django import DjangoModelFactory +from pygments import highlight +from pygments.formatters import HtmlFormatter +from pygments.lexers import PythonConsoleLexer from apps.codesamples.models import CodeSample from apps.users.factories import UserFactory @@ -26,22 +29,28 @@ class Meta: is_published = True +def _highlight_python_console(code): + """Highlight a Python console code snippet using Pygments.""" + code = textwrap.dedent(code).strip() + html = highlight(code, PythonConsoleLexer(), HtmlFormatter(nowrap=True)) + return f"
{html}
" + + def initial_data(): """Create the default set of homepage code samples.""" code_samples = [ ( - """\ -
# Simple output (with Unicode)
+            """
+            # Simple output (with Unicode)
             >>> print("Hello, I'm Python!")
-            Hello, I'm Python!
+            Hello, I'm Python!
 
-            # Input, assignment
-            >>> name = input('What is your name?\n')
-            What is your name?
-            Python
+            # Input, assignment
+            >>> name = input('What is your name?\\n')
             >>> print(f'Hi, {name}.')
-            Hi, Python.
-            
+ What is your name? + Python + Hi, Python. """, """\

Quick & Easy to Learn

@@ -53,16 +62,16 @@ def initial_data(): """, ), ( - """\ -
# Simple arithmetic
+            """
+            # Simple arithmetic
             >>> 1 / 2
-            0.5
+            0.5
             >>> 2 ** 3
-            8
-            >>> 17 / 3  # true division returns a float
-            5.666666666666667
-            >>> 17 // 3  # floor division
-            5
+ 8 + >>> 17 / 3 # true division returns a float + 5.666666666666667 + >>> 17 // 3 # floor division + 5 """, """\

Intuitive Interpretation

@@ -76,16 +85,16 @@ def initial_data(): """, ), ( - """\ -
# List comprehensions
+            """
+            # List comprehensions
             >>> fruits = ['Banana', 'Apple', 'Lime']
             >>> loud_fruits = [fruit.upper() for fruit in fruits]
             >>> print(loud_fruits)
-            ['BANANA', 'APPLE', 'LIME']
+            ['BANANA', 'APPLE', 'LIME']
 
-            # List and the enumerate function
+            # List and the enumerate function
             >>> list(enumerate(fruits))
-            [(0, 'Banana'), (1, 'Apple'), (2, 'Lime')]
+ [(0, 'Banana'), (1, 'Apple'), (2, 'Lime')] """, """\

Compound Data Types

@@ -97,19 +106,15 @@ def initial_data(): """, ), ( - """\ -
-            
-            # For loop on a list
+            """
+            # For loop on a list
             >>> numbers = [2, 4, 6, 8]
             >>> product = 1
             >>> for number in numbers:
             ...     product = product * number
             ...
             >>> print('The product is:', product)
-            The product is: 384
-            
-            
+ The product is: 384 """, """\

All the Flow You’d Expect

@@ -122,21 +127,17 @@ def initial_data(): """, ), ( - """\ -
-            
-            # Write Fibonacci series up to n
+            """
+            # Write Fibonacci series up to n
             >>> def fib(n):
             ...     a, b = 0, 1
-            ...     while a < n:
+            ...     while a < n:
             ...         print(a, end=' ')
             ...         a, b = b, a+b
             ...     print()
             ...
             >>> fib(1000)
-            0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610
-            
-            
+ 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 """, """\

Functions Defined

@@ -151,7 +152,7 @@ def initial_data(): return { "boxes": [ CodeSampleFactory( - code=textwrap.dedent(code), + code=_highlight_python_console(code), copy=textwrap.dedent(copy), ) for code, copy in code_samples diff --git a/pyproject.toml b/pyproject.toml index e2233b91b..5d8981208 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ dependencies = [ "docutils==0.21.2", "Markdown==3.7", "cmarkgfm==2024.11.20", + "Pygments>=2.17", "Pillow==10.4.0", "psycopg2-binary==2.9.9", "python3-openid==3.2.0", diff --git a/static/sass/style.css b/static/sass/style.css index f937ab4d4..60c93733a 100644 --- a/static/sass/style.css +++ b/static/sass/style.css @@ -1529,10 +1529,13 @@ input#s, .slide-code code { display: inline-block; color: #0d870d; } - .slide-code code .comment { - color: #666666; } - .slide-code code .output { + .slide-code code .c1 { + color: #666666; + font-style: normal; } + .slide-code code .go { color: #dddddd; } + .slide-code code .gp { + color: #c65d09; } .js .launch-shell, .no-js .launch-shell { display: none; } From ebe2ae361208d2d88563f66dbdffd06f164e5c18 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Wed, 18 Feb 2026 18:40:32 -0600 Subject: [PATCH 2/6] style: add full Pygments token colors for code samples Add CSS rules for all Pygments token classes used in the homepage code samples: keywords, builtins, function names, strings, numbers, operators, comments, output, and prompts. Uses a dark-background color scheme consistent with the existing slide-code styling. Co-Authored-By: Claude Opus 4.6 --- static/sass/style.css | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/static/sass/style.css b/static/sass/style.css index 60c93733a..d63cdfcb5 100644 --- a/static/sass/style.css +++ b/static/sass/style.css @@ -1529,13 +1529,31 @@ input#s, .slide-code code { display: inline-block; color: #0d870d; } + .slide-code code .k, + .slide-code code .ow { + color: #6ab825; + font-weight: bold; } + .slide-code code .nb { + color: #24909d; } + .slide-code code .nf { + color: #447fcf; } + .slide-code code .s1, + .slide-code code .s2, + .slide-code code .sa, + .slide-code code .si { + color: #ed9d13; } + .slide-code code .mi { + color: #3677a9; } + .slide-code code .o { + color: #999999; } .slide-code code .c1 { - color: #666666; - font-style: normal; } + color: #999999; + font-style: italic; } .slide-code code .go { - color: #dddddd; } + color: #cccccc; } .slide-code code .gp { - color: #c65d09; } + color: #c65d09; + font-weight: bold; } .js .launch-shell, .no-js .launch-shell { display: none; } From e8f4c78c181b827caf97075feed75f3f7bc905de Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Wed, 18 Feb 2026 18:54:28 -0600 Subject: [PATCH 3/6] fix: use --group for RTD docs dependency install The docs dependencies are in [dependency-groups], not [project.optional-dependencies], so pip install '.[docs]' fails. Use pip install --group docs to match the pattern from #2902. Co-Authored-By: Claude Opus 4.6 --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 9912b27b5..5c929bb35 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -10,6 +10,6 @@ build: python: "3" commands: - - python -m pip install '.[docs]' + - python -m pip install --group docs . - make -C docs html JOBS=$(nproc) BUILDDIR=_readthedocs - mv docs/_readthedocs _readthedocs From 0057520c2bc20c93a6816faa327c25b2887ab018 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Wed, 18 Feb 2026 18:58:21 -0600 Subject: [PATCH 4/6] fix: update SCSS source with Pygments token classes Update style.scss to use Pygments token classes instead of old .comment/.output classes. Fix .go output color to match SCSS variable $grey-lighterer (#ddd). Keeps SCSS and compiled CSS in sync. Co-Authored-By: Claude Opus 4.6 --- static/sass/style.css | 16 ++++++---------- static/sass/style.scss | 12 +++++++++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/static/sass/style.css b/static/sass/style.css index d63cdfcb5..63680c580 100644 --- a/static/sass/style.css +++ b/static/sass/style.css @@ -1529,18 +1529,14 @@ input#s, .slide-code code { display: inline-block; color: #0d870d; } - .slide-code code .k, - .slide-code code .ow { + .slide-code code .k, .slide-code code .ow { color: #6ab825; font-weight: bold; } .slide-code code .nb { color: #24909d; } .slide-code code .nf { color: #447fcf; } - .slide-code code .s1, - .slide-code code .s2, - .slide-code code .sa, - .slide-code code .si { + .slide-code code .s1, .slide-code code .s2, .slide-code code .sa, .slide-code code .si { color: #ed9d13; } .slide-code code .mi { color: #3677a9; } @@ -1550,7 +1546,7 @@ input#s, color: #999999; font-style: italic; } .slide-code code .go { - color: #cccccc; } + color: #dddddd; } .slide-code code .gp { color: #c65d09; font-weight: bold; } @@ -2188,7 +2184,7 @@ table .checksum { /* ! ===== Success Stories landing page ===== */ .featured-success-story { padding: 1.3125em 0; - background: center -230px no-repeat url('../img/success-glow2.png?1694722768') transparent; + background: center -230px no-repeat url('../img/success-glow2.png?1726783859') transparent; /*blockquote*/ } .featured-success-story img { padding: 10px 30px; } @@ -3174,11 +3170,11 @@ span.highlighted { .python .site-headline a:before { width: 290px; height: 82px; - content: url('../img/python-logo_print.png?1694722768'); } + content: url('../img/python-logo_print.png?1726783859'); } .psf .site-headline a:before { width: 334px; height: 82px; - content: url('../img/psf-logo_print.png?1694722768'); } } + content: url('../img/psf-logo_print.png?1726783859'); } } /* * When we want to review the markup for W3 and similar errors, turn some of these on * Uses :not selectors a bunch, so only modern browsers will support them diff --git a/static/sass/style.scss b/static/sass/style.scss index c16e823dc..a414a0d5a 100644 --- a/static/sass/style.scss +++ b/static/sass/style.scss @@ -697,9 +697,15 @@ input#s, display: inline-block; color: $code-green; - .comment { color: $grey; } - - .output { color: $grey-lighterer; } + .k, .ow { color: #6ab825; font-weight: bold; } + .nb { color: #24909d; } + .nf { color: #447fcf; } + .s1, .s2, .sa, .si { color: #ed9d13; } + .mi { color: #3677a9; } + .o { color: $grey-light; } + .c1 { color: $grey-light; font-style: italic; } + .go { color: $grey-lighterer; } + .gp { color: #c65d09; font-weight: bold; } } } From 78f5cc6f64ce0544ed5dd1b6f6ea9e01597d93c5 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Wed, 18 Feb 2026 19:09:11 -0600 Subject: [PATCH 5/6] Update pyproject.toml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5d8981208..9eb2c0b79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "docutils==0.21.2", "Markdown==3.7", "cmarkgfm==2024.11.20", - "Pygments>=2.17", + "Pygments>=2.17,<3", "Pillow==10.4.0", "psycopg2-binary==2.9.9", "python3-openid==3.2.0", From bf23dca77a0090e66442541f78146ac8301ad313 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Wed, 18 Feb 2026 19:10:27 -0600 Subject: [PATCH 6/6] fix: revert unrelated cache-bust changes, cap Pygments version - Revert unintentional cache-busting query string changes on image assets that were introduced during rebase - Add upper bound to Pygments dependency (>=2.17,<3) to prevent potential breaking changes from a future major release Co-Authored-By: Claude Opus 4.6 --- static/sass/style.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/static/sass/style.css b/static/sass/style.css index 63680c580..357ea40ee 100644 --- a/static/sass/style.css +++ b/static/sass/style.css @@ -2184,7 +2184,7 @@ table .checksum { /* ! ===== Success Stories landing page ===== */ .featured-success-story { padding: 1.3125em 0; - background: center -230px no-repeat url('../img/success-glow2.png?1726783859') transparent; + background: center -230px no-repeat url('../img/success-glow2.png?1694722768') transparent; /*blockquote*/ } .featured-success-story img { padding: 10px 30px; } @@ -3170,11 +3170,11 @@ span.highlighted { .python .site-headline a:before { width: 290px; height: 82px; - content: url('../img/python-logo_print.png?1726783859'); } + content: url('../img/python-logo_print.png?1694722768'); } .psf .site-headline a:before { width: 334px; height: 82px; - content: url('../img/psf-logo_print.png?1726783859'); } } + content: url('../img/psf-logo_print.png?1694722768'); } } /* * When we want to review the markup for W3 and similar errors, turn some of these on * Uses :not selectors a bunch, so only modern browsers will support them