#!/usr/bin/env python3 # syntax-highlighting-server.py: create a simple HTTP server to highlight # source for cgit. improves performance compared to invoking python on every # request. # # Requirements: Python 3, pygments. # # Usage: Configure your system to run this at boot. Note that this program is # not hardened, and it can be trivially DoSed. therefore, do not configure it # to listen on a public network. Once configured, set your cgit source filter # to syntax-highlighting-client.sh. from pygments import highlight from pygments.formatters import HtmlFormatter from pygments.lexers import guess_lexer, guess_lexer_for_filename from pygments.lexers.special import TextLexer from pygments.util import ClassNotFound def do_highlight(filename, data, style): try: lexer = guess_lexer_for_filename(filename, data) except ClassNotFound: try: lexer = guess_lexer(data) # SqlLexer always gives 0.01 if lexer.analyse_text(data) <= 0.01: lexer = TextLexer() except ClassNotFound: lexer = TextLexer() formatter = HtmlFormatter(style=style, nobackground=True) return ''.join([ f'', '', highlight(data, lexer, formatter) ]) def parse_args(): import argparse parser = argparse.ArgumentParser(description='syntax highlighting server', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('--host', type=str, default='127.0.0.1', help='the host to listen on') parser.add_argument('--port', type=int, default=4872, help='the port to listen on') parser.add_argument('--style', type=str, default='pastie', help='pygments formatting style') parser.add_argument('--preload', type=bool, default=True, help='preload lexers and formatters to reduce fork memory usage') return parser.parse_args() async def handle_highlight(request): import asyncio from aiohttp import web loop = asyncio.get_running_loop() text = await request.text() result = await loop.run_in_executor( request.app['pool'], do_highlight, request.query['filename'], text, request.app['style']) return web.Response(text=result) def main(): args = parse_args() from aiohttp import web from concurrent.futures import ProcessPoolExecutor if args.preload: guess_lexer('') with ProcessPoolExecutor() as pool: app = web.Application() app['pool'] = pool app['style'] = args.style app.add_routes([web.post('/highlight', handle_highlight)]) web.run_app(app, host=args.host, port=args.port) if __name__ == '__main__': main()