1+ import re
2+
13import pygments
2- from pygments .lexers import get_lexer_for_filename , guess_lexer
3- from pygments .formatters import HtmlFormatter # pylint: disable=no-name-in-module
4- import re , os
5- from .helper import make_async
6- from sanic .exceptions import NotFound , InvalidUsage
74from pronounceable import PronounceableWord
5+ from pygments .formatters import HtmlFormatter # type: ignore
6+ from pygments .lexers import get_lexer_for_filename
7+ from sanic .exceptions import InvalidUsage
88
9- matchers = (
9+ from .helper import make_async
10+
11+ matchers : list [tuple [str , re .Pattern [str ]]] = [
1012 # First try shebangs and other things at the beginning of the file
1113 (".sh" , re .compile (r"^#!/bin/(ba)?sh\s" )),
1214 (".scala" , re .compile (r"^#!/.*scala\s" )),
2527 (".java" , re .compile (r"^public class \w+" , re .MULTILINE )),
2628 (".cpp" , re .compile (r"#include .*\w::\w|using namespace \w+;" , re .DOTALL )),
2729 (".c" , re .compile (r"#include .*(malloc|printf)\(|int main\(void\)" , re .DOTALL )),
28- (".js" , re .compile (r"^\s*console.log\(|^\s*(var|let|const) \w+ = require|\) => {$|^\s*function( \w+)?\(" , re .MULTILINE )),
30+ (
31+ ".js" ,
32+ re .compile (
33+ r"^\s*console.log\(|^\s*(var|let|const) \w+ = require|\) => {$|^\s*function( \w+)?\(" ,
34+ re .MULTILINE ,
35+ ),
36+ ),
2937 (".php" , re .compile (r"<\?php.*\?>" )),
30- (".css" , re .compile (r"^\s*(color: *#[0-9a-fA-F]{3,6}|width: \d+(px|r?em|%));$" , re .IGNORECASE | re .MULTILINE )),
31- )
38+ (
39+ ".css" ,
40+ re .compile (
41+ r"^\s*(color: *#[0-9a-fA-F]{3,6}|width: \d+(px|r?em|%));$" ,
42+ re .IGNORECASE | re .MULTILINE ,
43+ ),
44+ ),
45+ ]
46+
3247
3348class Formatter (HtmlFormatter ):
3449 # Link & anchor line numbers
@@ -37,55 +52,82 @@ def _wrap_lineanchors(self, inner):
3752 for t , line in inner :
3853 if t :
3954 i += 1
40- yield 1 , f' <a class=line href=#{ i } id={ i } tabindex=-1></a>' + line
55+ yield 1 , f" <a class=line href=#{ i } id={ i } tabindex=-1></a>" + line
4156 else :
4257 yield 0 , line
58+
4359 # A bit shorter wrapper, and link URLs
4460 def _wrap_pre (self , inner ):
45- yield 0 , ' <pre><code>'
61+ yield 0 , " <pre><code>"
4662 for i , t in inner :
4763 if i == 1 :
48- t = re .sub (r' (http[s]?://\S+)' , r'<a href="\1">\1</a>' , t )
64+ t = re .sub (r" (http[s]?://\S+)" , r'<a href="\1">\1</a>' , t )
4965 yield i , t
50- yield 0 , '</code></pre>'
66+ yield 0 , "</code></pre>"
67+
5168
52- def prettyprint (paste , paste_id ):
53- n = 1 + re .search ("^\s*" , paste )[0 ].count ("\n " ) # Pygments removes initial empty lines, account for that
69+ def prettyprint (paste : str , paste_id : str ) -> str :
70+ # Pygments removes initial empty lines, account for that
71+ initialws = re .search (r"^\s*" , paste )
72+ n = 1 + initialws [0 ].count ("\n " ) if initialws else 1
5473 formatter = Formatter (lineanchors = True , linenostart = n )
55- try : lexer = get_lexer_for_filename (paste_id )
56- except Exception : lexer = get_lexer_for_filename (paste_id + ".txt" )
74+ try :
75+ lexer = get_lexer_for_filename (paste_id )
76+ except Exception :
77+ lexer = get_lexer_for_filename (paste_id + ".txt" )
5778 return pygments .highlight (paste , lexer , formatter )
5879
59- def decode (text : bytes , fallback_charset : str = None ) -> str :
80+
81+ def decode (text : bytes , fallback_charset : str | None = None ) -> str :
6082 """Decode with charset autodetection. Removes Unicode BOMs automatically."""
6183 # Unicode strings with BOMs
62- boms = (b"\xEF \xBB \xBF " , "UTF-8" ), (b"\xFF \xFE " , "UTF-16LE" ), (b"\xFE \xFF " , "UTF-16BE" ), (b"\xFF \xFE \0 \0 " , "UTF-32LE" ), (b"\0 \0 \xFE \xFF " , "UTF-32BE" )
84+ boms = (
85+ (b"\xef \xbb \xbf " , "UTF-8" ),
86+ (b"\xff \xfe " , "UTF-16LE" ),
87+ (b"\xfe \xff " , "UTF-16BE" ),
88+ (b"\xff \xfe \0 \0 " , "UTF-32LE" ),
89+ (b"\0 \0 \xfe \xff " , "UTF-32BE" ),
90+ )
6391 for bom , charset in boms :
64- if text .startswith (bom ): return text [len (bom ):].decode (charset , errors = "replace" )
92+ if text .startswith (bom ):
93+ return text [len (bom ) :].decode (charset , errors = "replace" )
6594 # Try UTF-8 without BOM
66- try : return text .decode ()
67- except UnicodeDecodeError : pass
95+ try :
96+ return text .decode ()
97+ except UnicodeDecodeError :
98+ pass
6899 # If fallback is provided, just use that
69- if fallback_charset : return text .decode (fallback_charset , errors = "replace" )
100+ if fallback_charset :
101+ return text .decode (fallback_charset , errors = "replace" )
70102 # 8-bit guesswork
71103 # - NUL usually means binary data (could be actual NUL or UTF-16/32 w/o BOM, but all those are rare)
72- if 0 in text : raise InvalidUsage ("Looks like binary data" )
104+ if 0 in text :
105+ raise InvalidUsage ("Looks like binary data" )
73106 # - With CR/LF line terminators, CP437 umlauts are more likely than ISO-8859-1 extended control chars
74- if any (0x80 <= ch < 0xA0 for ch in text ) and b"\r \n " in text : return text .decode ("CP437" )
107+ if any (0x80 <= ch < 0xA0 for ch in text ) and b"\r \n " in text :
108+ return text .decode ("CP437" )
75109 # - The most common 8-bit encoding is a reasonable final fallback
76110 return text .decode ("ISO-8859-1" )
77111
112+
78113@make_async
79- def process_paste (paste , paste_id , fallback_charset = None ):
80- if isinstance (paste , bytes ): paste = decode (paste , fallback_charset )
81- elif paste is None : raise InvalidUsage ("Malformed request (no paste found)" )
82- elif paste [0 ] == "\uFEFF " : paste = paste [1 :] # Remove Unicode BOM
114+ def process_paste (paste , paste_id , fallback_charset = None ):
115+ if isinstance (paste , bytes ):
116+ paste = decode (paste , fallback_charset )
117+ elif paste is None :
118+ raise InvalidUsage ("Malformed request (no paste found)" )
119+ elif paste [0 ] == "\ufeff " :
120+ paste = paste [1 :] # Remove Unicode BOM
83121 paste = paste .replace ("\r \n " , "\n " )
84- if not paste .strip (): raise InvalidUsage ("Empty paste (no data found)" )
85- if not paste .endswith ("\n " ): paste += "\n "
122+ if not paste .strip ():
123+ raise InvalidUsage ("Empty paste (no data found)" )
124+ if not paste .endswith ("\n " ):
125+ paste += "\n "
86126 if paste_id :
87- paste_id = "" .join ([c for c in paste_id .replace (" " , "_" ) if re .match (r'[-_\w\.]' , c )])
127+ paste_id = "" .join (
128+ [c for c in paste_id .replace (" " , "_" ) if re .match (r"[-_\w\.]" , c )]
129+ )
88130 if not paste_id or len (paste_id ) < 3 :
89131 ext = next ((e for e , r in matchers if r .search (paste )), ".txt" )
90- paste_id = PronounceableWord ().length (6 , 15 ) + ext
132+ paste_id = PronounceableWord ().length (6 , 15 ) + ext # type: ignore
91133 return paste_id , dict (text = paste , html = prettyprint (paste , paste_id ))
0 commit comments