Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pygments/formatters/html.py: 45%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

422 statements  

1""" 

2 pygments.formatters.html 

3 ~~~~~~~~~~~~~~~~~~~~~~~~ 

4 

5 Formatter for HTML output. 

6 

7 :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. 

8 :license: BSD, see LICENSE for details. 

9""" 

10 

11import functools 

12import os 

13import sys 

14import os.path 

15from io import StringIO 

16from html import escape as _escape 

17 

18from pygments.formatter import Formatter 

19from pygments.token import Token, Text, STANDARD_TYPES 

20from pygments.util import get_bool_opt, get_int_opt, get_list_opt, html_escape 

21 

22try: 

23 import ctags 

24except ImportError: 

25 ctags = None 

26 

27__all__ = ['HtmlFormatter'] 

28 

29 

30def webify(color): 

31 if color == 'transparent' or color.startswith('calc') or color.startswith('var'): 

32 return color 

33 else: 

34 # Check if the color can be shortened from 6 to 3 characters 

35 color = color.upper() 

36 if (len(color) == 6 and 

37 ( color[0] == color[1] 

38 and color[2] == color[3] 

39 and color[4] == color[5])): 

40 return f'#{color[0]}{color[2]}{color[4]}' 

41 else: 

42 return f'#{color}' 

43 

44 

45def _get_ttype_class(ttype): 

46 fname = STANDARD_TYPES.get(ttype) 

47 if fname: 

48 return fname 

49 aname = '' 

50 while fname is None: 

51 aname = '-' + ttype[-1] + aname 

52 ttype = ttype.parent 

53 fname = STANDARD_TYPES.get(ttype) 

54 return fname + aname 

55 

56 

57CSSFILE_TEMPLATE = '''\ 

58/* 

59generated by Pygments <https://pygments.org/> 

60Copyright 2006-present by the Pygments team. 

61Licensed under the BSD license, see LICENSE for details. 

62*/ 

63%(styledefs)s 

64''' 

65 

66DOC_HEADER = '''\ 

67<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" 

68 "http://www.w3.org/TR/html4/strict.dtd"> 

69<!-- 

70generated by Pygments <https://pygments.org/> 

71Copyright 2006-present by the Pygments team. 

72Licensed under the BSD license, see LICENSE for details. 

73--> 

74<html> 

75<head> 

76 <title>%(title)s</title> 

77 <meta http-equiv="content-type" content="text/html; charset=%(encoding)s"> 

78 <style type="text/css"> 

79''' + CSSFILE_TEMPLATE + ''' 

80 </style> 

81</head> 

82<body> 

83<h2>%(title)s</h2> 

84 

85''' 

86 

87DOC_HEADER_EXTERNALCSS = '''\ 

88<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" 

89 "http://www.w3.org/TR/html4/strict.dtd"> 

90 

91<html> 

92<head> 

93 <title>%(title)s</title> 

94 <meta http-equiv="content-type" content="text/html; charset=%(encoding)s"> 

95 <link rel="stylesheet" href="%(cssfile)s" type="text/css"> 

96</head> 

97<body> 

98<h2>%(title)s</h2> 

99 

100''' 

101 

102DOC_FOOTER = '''\ 

103</body> 

104</html> 

105''' 

106 

107 

108class HtmlFormatter(Formatter): 

109 r""" 

110 Format tokens as HTML 4 ``<span>`` tags. By default, the content is enclosed 

111 in a ``<pre>`` tag, itself wrapped in a ``<div>`` tag (but see the `nowrap` option). 

112 The ``<div>``'s CSS class can be set by the `cssclass` option. 

113 

114 If the `linenos` option is set to ``"table"``, the ``<pre>`` is 

115 additionally wrapped inside a ``<table>`` which has one row and two 

116 cells: one containing the line numbers and one containing the code. 

117 Example: 

118 

119 .. sourcecode:: html 

120 

121 <div class="highlight" > 

122 <table><tr> 

123 <td class="linenos" title="click to toggle" 

124 onclick="with (this.firstChild.style) 

125 { display = (display == '') ? 'none' : '' }"> 

126 <pre>1 

127 2</pre> 

128 </td> 

129 <td class="code"> 

130 <pre><span class="Ke">def </span><span class="NaFu">foo</span>(bar): 

131 <span class="Ke">pass</span> 

132 </pre> 

133 </td> 

134 </tr></table></div> 

135 

136 (whitespace added to improve clarity). 

137 

138 A list of lines can be specified using the `hl_lines` option to make these 

139 lines highlighted (as of Pygments 0.11). 

140 

141 With the `full` option, a complete HTML 4 document is output, including 

142 the style definitions inside a ``<style>`` tag, or in a separate file if 

143 the `cssfile` option is given. 

144 

145 When `tagsfile` is set to the path of a ctags index file, it is used to 

146 generate hyperlinks from names to their definition. You must enable 

147 `lineanchors` and run ctags with the `-n` option for this to work. The 

148 `python-ctags` module from PyPI must be installed to use this feature; 

149 otherwise a `RuntimeError` will be raised. 

150 

151 The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string 

152 containing CSS rules for the CSS classes used by the formatter. The 

153 argument `arg` can be used to specify additional CSS selectors that 

154 are prepended to the classes. A call `fmter.get_style_defs('td .code')` 

155 would result in the following CSS classes: 

156 

157 .. sourcecode:: css 

158 

159 td .code .kw { font-weight: bold; color: #00FF00 } 

160 td .code .cm { color: #999999 } 

161 ... 

162 

163 If you have Pygments 0.6 or higher, you can also pass a list or tuple to the 

164 `get_style_defs()` method to request multiple prefixes for the tokens: 

165 

166 .. sourcecode:: python 

167 

168 formatter.get_style_defs(['div.syntax pre', 'pre.syntax']) 

169 

170 The output would then look like this: 

171 

172 .. sourcecode:: css 

173 

174 div.syntax pre .kw, 

175 pre.syntax .kw { font-weight: bold; color: #00FF00 } 

176 div.syntax pre .cm, 

177 pre.syntax .cm { color: #999999 } 

178 ... 

179 

180 Additional options accepted: 

181 

182 `nowrap` 

183 If set to ``True``, don't add a ``<pre>`` and a ``<div>`` tag 

184 around the tokens. This disables most other options (default: ``False``). 

185 

186 `full` 

187 Tells the formatter to output a "full" document, i.e. a complete 

188 self-contained document (default: ``False``). 

189 

190 `title` 

191 If `full` is true, the title that should be used to caption the 

192 document (default: ``''``). 

193 

194 `style` 

195 The style to use, can be a string or a Style subclass (default: 

196 ``'default'``). This option has no effect if the `cssfile` 

197 and `noclobber_cssfile` option are given and the file specified in 

198 `cssfile` exists. 

199 

200 `noclasses` 

201 If set to true, token ``<span>`` tags (as well as line number elements) 

202 will not use CSS classes, but inline styles. This is not recommended 

203 for larger pieces of code since it increases output size by quite a bit 

204 (default: ``False``). 

205 

206 `classprefix` 

207 Since the token types use relatively short class names, they may clash 

208 with some of your own class names. In this case you can use the 

209 `classprefix` option to give a string to prepend to all Pygments-generated 

210 CSS class names for token types. 

211 Note that this option also affects the output of `get_style_defs()`. 

212 

213 `cssclass` 

214 CSS class for the wrapping ``<div>`` tag (default: ``'highlight'``). 

215 If you set this option, the default selector for `get_style_defs()` 

216 will be this class. 

217 

218 .. versionadded:: 0.9 

219 If you select the ``'table'`` line numbers, the wrapping table will 

220 have a CSS class of this string plus ``'table'``, the default is 

221 accordingly ``'highlighttable'``. 

222 

223 `cssstyles` 

224 Inline CSS styles for the wrapping ``<div>`` tag (default: ``''``). 

225 

226 `prestyles` 

227 Inline CSS styles for the ``<pre>`` tag (default: ``''``). 

228 

229 .. versionadded:: 0.11 

230 

231 `cssfile` 

232 If the `full` option is true and this option is given, it must be the 

233 name of an external file. If the filename does not include an absolute 

234 path, the file's path will be assumed to be relative to the main output 

235 file's path, if the latter can be found. The stylesheet is then written 

236 to this file instead of the HTML file. 

237 

238 .. versionadded:: 0.6 

239 

240 `noclobber_cssfile` 

241 If `cssfile` is given and the specified file exists, the css file will 

242 not be overwritten. This allows the use of the `full` option in 

243 combination with a user specified css file. Default is ``False``. 

244 

245 .. versionadded:: 1.1 

246 

247 `linenos` 

248 If set to ``'table'``, output line numbers as a table with two cells, 

249 one containing the line numbers, the other the whole code. This is 

250 copy-and-paste-friendly, but may cause alignment problems with some 

251 browsers or fonts. If set to ``'inline'``, the line numbers will be 

252 integrated in the ``<pre>`` tag that contains the code (that setting 

253 is *new in Pygments 0.8*). 

254 

255 For compatibility with Pygments 0.7 and earlier, every true value 

256 except ``'inline'`` means the same as ``'table'`` (in particular, that 

257 means also ``True``). 

258 

259 The default value is ``False``, which means no line numbers at all. 

260 

261 **Note:** with the default ("table") line number mechanism, the line 

262 numbers and code can have different line heights in Internet Explorer 

263 unless you give the enclosing ``<pre>`` tags an explicit ``line-height`` 

264 CSS property (you get the default line spacing with ``line-height: 

265 125%``). 

266 

267 `hl_lines` 

268 Specify a list of lines to be highlighted. The line numbers are always 

269 relative to the input (i.e. the first line is line 1) and are 

270 independent of `linenostart`. 

271 

272 .. versionadded:: 0.11 

273 

274 `linenostart` 

275 The line number for the first line (default: ``1``). 

276 

277 `linenostep` 

278 If set to a number n > 1, only every nth line number is printed. 

279 

280 `linenospecial` 

281 If set to a number n > 0, every nth line number is given the CSS 

282 class ``"special"`` (default: ``0``). 

283 

284 `nobackground` 

285 If set to ``True``, the formatter won't output the background color 

286 for the wrapping element (this automatically defaults to ``False`` 

287 when there is no wrapping element [eg: no argument for the 

288 `get_syntax_defs` method given]) (default: ``False``). 

289 

290 .. versionadded:: 0.6 

291 

292 `lineseparator` 

293 This string is output between lines of code. It defaults to ``"\n"``, 

294 which is enough to break a line inside ``<pre>`` tags, but you can 

295 e.g. set it to ``"<br>"`` to get HTML line breaks. 

296 

297 .. versionadded:: 0.7 

298 

299 `lineanchors` 

300 If set to a nonempty string, e.g. ``foo``, the formatter will wrap each 

301 output line in an anchor tag with an ``id`` (and `name`) of ``foo-linenumber``. 

302 This allows easy linking to certain lines. 

303 

304 .. versionadded:: 0.9 

305 

306 `linespans` 

307 If set to a nonempty string, e.g. ``foo``, the formatter will wrap each 

308 output line in a span tag with an ``id`` of ``foo-linenumber``. 

309 This allows easy access to lines via javascript. 

310 

311 .. versionadded:: 1.6 

312 

313 `anchorlinenos` 

314 If set to `True`, will wrap line numbers in <a> tags. Used in 

315 combination with `linenos` and `lineanchors`. 

316 

317 `tagsfile` 

318 If set to the path of a ctags file, wrap names in anchor tags that 

319 link to their definitions. `lineanchors` should be used, and the 

320 tags file should specify line numbers (see the `-n` option to ctags). 

321 The tags file is assumed to be encoded in UTF-8. 

322 

323 .. versionadded:: 1.6 

324 

325 `tagurlformat` 

326 A string formatting pattern used to generate links to ctags definitions. 

327 Available variables are `%(path)s`, `%(fname)s` and `%(fext)s`. 

328 Defaults to an empty string, resulting in just `#prefix-number` links. 

329 

330 .. versionadded:: 1.6 

331 

332 `filename` 

333 A string used to generate a filename when rendering ``<pre>`` blocks, 

334 for example if displaying source code. If `linenos` is set to 

335 ``'table'`` then the filename will be rendered in an initial row 

336 containing a single `<th>` which spans both columns. 

337 

338 .. versionadded:: 2.1 

339 

340 `wrapcode` 

341 Wrap the code inside ``<pre>`` blocks using ``<code>``, as recommended 

342 by the HTML5 specification. 

343 

344 .. versionadded:: 2.4 

345 

346 `debug_token_types` 

347 Add ``title`` attributes to all token ``<span>`` tags that show the 

348 name of the token. 

349 

350 .. versionadded:: 2.10 

351 

352 

353 **Subclassing the HTML formatter** 

354 

355 .. versionadded:: 0.7 

356 

357 The HTML formatter is now built in a way that allows easy subclassing, thus 

358 customizing the output HTML code. The `format()` method calls 

359 `self._format_lines()` which returns a generator that yields tuples of ``(1, 

360 line)``, where the ``1`` indicates that the ``line`` is a line of the 

361 formatted source code. 

362 

363 If the `nowrap` option is set, the generator is the iterated over and the 

364 resulting HTML is output. 

365 

366 Otherwise, `format()` calls `self.wrap()`, which wraps the generator with 

367 other generators. These may add some HTML code to the one generated by 

368 `_format_lines()`, either by modifying the lines generated by the latter, 

369 then yielding them again with ``(1, line)``, and/or by yielding other HTML 

370 code before or after the lines, with ``(0, html)``. The distinction between 

371 source lines and other code makes it possible to wrap the generator multiple 

372 times. 

373 

374 The default `wrap()` implementation adds a ``<div>`` and a ``<pre>`` tag. 

375 

376 A custom `HtmlFormatter` subclass could look like this: 

377 

378 .. sourcecode:: python 

379 

380 class CodeHtmlFormatter(HtmlFormatter): 

381 

382 def wrap(self, source, *, include_div): 

383 return self._wrap_code(source) 

384 

385 def _wrap_code(self, source): 

386 yield 0, '<code>' 

387 for i, t in source: 

388 if i == 1: 

389 # it's a line of formatted code 

390 t += '<br>' 

391 yield i, t 

392 yield 0, '</code>' 

393 

394 This results in wrapping the formatted lines with a ``<code>`` tag, where the 

395 source lines are broken using ``<br>`` tags. 

396 

397 After calling `wrap()`, the `format()` method also adds the "line numbers" 

398 and/or "full document" wrappers if the respective options are set. Then, all 

399 HTML yielded by the wrapped generator is output. 

400 """ 

401 

402 name = 'HTML' 

403 aliases = ['html'] 

404 filenames = ['*.html', '*.htm'] 

405 

406 def __init__(self, **options): 

407 Formatter.__init__(self, **options) 

408 self.title = self._decodeifneeded(self.title) 

409 self.nowrap = get_bool_opt(options, 'nowrap', False) 

410 self.noclasses = get_bool_opt(options, 'noclasses', False) 

411 self.classprefix = options.get('classprefix', '') 

412 self.cssclass = html_escape(self._decodeifneeded(options.get('cssclass', 'highlight'))) 

413 self.cssstyles = html_escape(self._decodeifneeded(options.get('cssstyles', ''))) 

414 self.prestyles = self._decodeifneeded(options.get('prestyles', '')) 

415 self.cssfile = self._decodeifneeded(options.get('cssfile', '')) 

416 self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False) 

417 self.tagsfile = self._decodeifneeded(options.get('tagsfile', '')) 

418 self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', '')) 

419 self.filename = html_escape(self._decodeifneeded(options.get('filename', ''))) 

420 self.wrapcode = get_bool_opt(options, 'wrapcode', False) 

421 self.span_element_openers = {} 

422 self.debug_token_types = get_bool_opt(options, 'debug_token_types', False) 

423 

424 if self.tagsfile: 

425 if not ctags: 

426 raise RuntimeError('The "ctags" package must to be installed ' 

427 'to be able to use the "tagsfile" feature.') 

428 self._ctags = ctags.CTags(self.tagsfile) 

429 

430 linenos = options.get('linenos', False) 

431 if linenos == 'inline': 

432 self.linenos = 2 

433 elif linenos: 

434 # compatibility with <= 0.7 

435 self.linenos = 1 

436 else: 

437 self.linenos = 0 

438 self.linenostart = abs(get_int_opt(options, 'linenostart', 1)) 

439 self.linenostep = abs(get_int_opt(options, 'linenostep', 1)) 

440 self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0)) 

441 self.nobackground = get_bool_opt(options, 'nobackground', False) 

442 self.lineseparator = html_escape(options.get('lineseparator', '\n')) 

443 self.lineanchors = html_escape(options.get('lineanchors', '')) 

444 self.linespans = html_escape(options.get('linespans', '')) 

445 self.anchorlinenos = get_bool_opt(options, 'anchorlinenos', False) 

446 self.hl_lines = set() 

447 for lineno in get_list_opt(options, 'hl_lines', []): 

448 try: 

449 self.hl_lines.add(int(lineno)) 

450 except ValueError: 

451 pass 

452 

453 self._create_stylesheet() 

454 

455 def _get_css_class(self, ttype): 

456 """Return the css class of this token type prefixed with 

457 the classprefix option.""" 

458 ttypeclass = _get_ttype_class(ttype) 

459 if ttypeclass: 

460 return self.classprefix + ttypeclass 

461 return '' 

462 

463 def _get_css_classes(self, ttype): 

464 """Return the CSS classes of this token type prefixed with the classprefix option.""" 

465 cls = self._get_css_class(ttype) 

466 while ttype not in STANDARD_TYPES: 

467 ttype = ttype.parent 

468 cls = self._get_css_class(ttype) + ' ' + cls 

469 return cls or '' 

470 

471 def _get_css_inline_styles(self, ttype): 

472 """Return the inline CSS styles for this token type.""" 

473 cclass = self.ttype2class.get(ttype) 

474 while cclass is None: 

475 ttype = ttype.parent 

476 cclass = self.ttype2class.get(ttype) 

477 return cclass or '' 

478 

479 def _create_stylesheet(self): 

480 t2c = self.ttype2class = {Token: ''} 

481 c2s = self.class2style = {} 

482 for ttype, ndef in self.style: 

483 name = self._get_css_class(ttype) 

484 style = '' 

485 if ndef['color']: 

486 style += 'color: {}; '.format(webify(ndef['color'])) 

487 if ndef['bold']: 

488 style += 'font-weight: bold; ' 

489 if ndef['italic']: 

490 style += 'font-style: italic; ' 

491 if ndef['underline']: 

492 style += 'text-decoration: underline; ' 

493 if ndef['bgcolor']: 

494 style += 'background-color: {}; '.format(webify(ndef['bgcolor'])) 

495 if ndef['border']: 

496 style += 'border: 1px solid {}; '.format(webify(ndef['border'])) 

497 if style: 

498 t2c[ttype] = name 

499 # save len(ttype) to enable ordering the styles by 

500 # hierarchy (necessary for CSS cascading rules!) 

501 c2s[name] = (style[:-2], ttype, len(ttype)) 

502 

503 def get_style_defs(self, arg=None): 

504 """ 

505 Return CSS style definitions for the classes produced by the current 

506 highlighting style. ``arg`` can be a string or list of selectors to 

507 insert before the token type classes. 

508 """ 

509 style_lines = [] 

510 

511 style_lines.extend(self.get_linenos_style_defs()) 

512 style_lines.extend(self.get_background_style_defs(arg)) 

513 style_lines.extend(self.get_token_style_defs(arg)) 

514 

515 return '\n'.join(style_lines) 

516 

517 def get_token_style_defs(self, arg=None): 

518 prefix = self.get_css_prefix(arg) 

519 

520 styles = [ 

521 (level, ttype, cls, style) 

522 for cls, (style, ttype, level) in self.class2style.items() 

523 if cls and style 

524 ] 

525 styles.sort() 

526 

527 lines = [ 

528 f'{prefix(cls)} {{ {style} }} /* {repr(ttype)[6:]} */' 

529 for (level, ttype, cls, style) in styles 

530 ] 

531 

532 return lines 

533 

534 def get_background_style_defs(self, arg=None): 

535 prefix = self.get_css_prefix(arg) 

536 bg_color = self.style.background_color 

537 hl_color = self.style.highlight_color 

538 

539 lines = [] 

540 

541 if arg and not self.nobackground and bg_color is not None: 

542 text_style = '' 

543 if Text in self.ttype2class: 

544 text_style = ' ' + self.class2style[self.ttype2class[Text]][0] 

545 lines.insert( 

546 0, '{}{{ background: {};{} }}'.format( 

547 prefix(''), bg_color, text_style 

548 ) 

549 ) 

550 if hl_color is not None: 

551 lines.insert( 

552 0, '{} {{ background-color: {} }}'.format(prefix('hll'), hl_color) 

553 ) 

554 

555 return lines 

556 

557 def get_linenos_style_defs(self): 

558 lines = [ 

559 f'pre {{ {self._pre_style} }}', 

560 f'td.linenos .normal {{ {self._linenos_style} }}', 

561 f'span.linenos {{ {self._linenos_style} }}', 

562 f'td.linenos .special {{ {self._linenos_special_style} }}', 

563 f'span.linenos.special {{ {self._linenos_special_style} }}', 

564 ] 

565 

566 return lines 

567 

568 def get_css_prefix(self, arg): 

569 if arg is None: 

570 arg = ('cssclass' in self.options and '.'+self.cssclass or '') 

571 if isinstance(arg, str): 

572 args = [arg] 

573 else: 

574 args = list(arg) 

575 

576 def prefix(cls): 

577 if cls: 

578 cls = '.' + cls 

579 tmp = [] 

580 for arg in args: 

581 tmp.append((arg and arg + ' ' or '') + cls) 

582 return ', '.join(tmp) 

583 

584 return prefix 

585 

586 @property 

587 def _pre_style(self): 

588 return 'line-height: 125%;' 

589 

590 @property 

591 def _linenos_style(self): 

592 color = self.style.line_number_color 

593 background_color = self.style.line_number_background_color 

594 return f'color: {color}; background-color: {background_color}; padding-left: 5px; padding-right: 5px;' 

595 

596 @property 

597 def _linenos_special_style(self): 

598 color = self.style.line_number_special_color 

599 background_color = self.style.line_number_special_background_color 

600 return f'color: {color}; background-color: {background_color}; padding-left: 5px; padding-right: 5px;' 

601 

602 def _decodeifneeded(self, value): 

603 if isinstance(value, bytes): 

604 if self.encoding: 

605 return value.decode(self.encoding) 

606 return value.decode() 

607 return value 

608 

609 def _wrap_full(self, inner, outfile): 

610 if self.cssfile: 

611 if os.path.isabs(self.cssfile): 

612 # it's an absolute filename 

613 cssfilename = self.cssfile 

614 else: 

615 try: 

616 filename = outfile.name 

617 if not filename or filename[0] == '<': 

618 # pseudo files, e.g. name == '<fdopen>' 

619 raise AttributeError 

620 cssfilename = os.path.join(os.path.dirname(filename), 

621 self.cssfile) 

622 except AttributeError: 

623 print('Note: Cannot determine output file name, ' 

624 'using current directory as base for the CSS file name', 

625 file=sys.stderr) 

626 cssfilename = self.cssfile 

627 # write CSS file only if noclobber_cssfile isn't given as an option. 

628 try: 

629 if not os.path.exists(cssfilename) or not self.noclobber_cssfile: 

630 with open(cssfilename, "w", encoding="utf-8") as cf: 

631 cf.write(CSSFILE_TEMPLATE % 

632 {'styledefs': self.get_style_defs('body')}) 

633 except OSError as err: 

634 err.strerror = 'Error writing CSS file: ' + err.strerror 

635 raise 

636 

637 yield 0, (DOC_HEADER_EXTERNALCSS % 

638 dict(title=self.title, 

639 cssfile=self.cssfile, 

640 encoding=self.encoding)) 

641 else: 

642 yield 0, (DOC_HEADER % 

643 dict(title=self.title, 

644 styledefs=self.get_style_defs('body'), 

645 encoding=self.encoding)) 

646 

647 yield from inner 

648 yield 0, DOC_FOOTER 

649 

650 def _wrap_tablelinenos(self, inner): 

651 dummyoutfile = StringIO() 

652 lncount = 0 

653 for t, line in inner: 

654 if t: 

655 lncount += 1 

656 dummyoutfile.write(line) 

657 

658 fl = self.linenostart 

659 mw = len(str(lncount + fl - 1)) 

660 sp = self.linenospecial 

661 st = self.linenostep 

662 anchor_name = self.lineanchors or self.linespans 

663 aln = self.anchorlinenos 

664 nocls = self.noclasses 

665 

666 lines = [] 

667 

668 for i in range(fl, fl+lncount): 

669 print_line = i % st == 0 

670 special_line = sp and i % sp == 0 

671 

672 if print_line: 

673 line = '%*d' % (mw, i) 

674 if aln: 

675 line = '<a href="#%s-%d">%s</a>' % (anchor_name, i, line) 

676 else: 

677 line = ' ' * mw 

678 

679 if nocls: 

680 if special_line: 

681 style = f' style="{self._linenos_special_style}"' 

682 else: 

683 style = f' style="{self._linenos_style}"' 

684 else: 

685 if special_line: 

686 style = ' class="special"' 

687 else: 

688 style = ' class="normal"' 

689 

690 if style: 

691 line = f'<span{style}>{line}</span>' 

692 

693 lines.append(line) 

694 

695 ls = '\n'.join(lines) 

696 

697 # If a filename was specified, we can't put it into the code table as it 

698 # would misalign the line numbers. Hence we emit a separate row for it. 

699 filename_tr = "" 

700 if self.filename: 

701 filename_tr = ( 

702 '<tr><th colspan="2" class="filename">' 

703 '<span class="filename">' + self.filename + '</span>' 

704 '</th></tr>') 

705 

706 # in case you wonder about the seemingly redundant <div> here: since the 

707 # content in the other cell also is wrapped in a div, some browsers in 

708 # some configurations seem to mess up the formatting... 

709 yield 0, (f'<table class="{self.cssclass}table">' + filename_tr + 

710 '<tr><td class="linenos"><div class="linenodiv"><pre>' + 

711 ls + '</pre></div></td><td class="code">') 

712 yield 0, '<div>' 

713 yield 0, dummyoutfile.getvalue() 

714 yield 0, '</div>' 

715 yield 0, '</td></tr></table>' 

716 

717 

718 def _wrap_inlinelinenos(self, inner): 

719 # need a list of lines since we need the width of a single number :( 

720 inner_lines = list(inner) 

721 sp = self.linenospecial 

722 st = self.linenostep 

723 num = self.linenostart 

724 mw = len(str(len(inner_lines) + num - 1)) 

725 anchor_name = self.lineanchors or self.linespans 

726 aln = self.anchorlinenos 

727 nocls = self.noclasses 

728 

729 for _, inner_line in inner_lines: 

730 print_line = num % st == 0 

731 special_line = sp and num % sp == 0 

732 

733 if print_line: 

734 line = '%*d' % (mw, num) 

735 else: 

736 line = ' ' * mw 

737 

738 if nocls: 

739 if special_line: 

740 style = f' style="{self._linenos_special_style}"' 

741 else: 

742 style = f' style="{self._linenos_style}"' 

743 else: 

744 if special_line: 

745 style = ' class="linenos special"' 

746 else: 

747 style = ' class="linenos"' 

748 

749 if style: 

750 linenos = f'<span{style}>{line}</span>' 

751 else: 

752 linenos = line 

753 

754 if aln: 

755 yield 1, ('<a href="#%s-%d">%s</a>' % (anchor_name, num, linenos) + 

756 inner_line) 

757 else: 

758 yield 1, linenos + inner_line 

759 num += 1 

760 

761 def _wrap_lineanchors(self, inner): 

762 s = self.lineanchors 

763 # subtract 1 since we have to increment i *before* yielding 

764 i = self.linenostart - 1 

765 for t, line in inner: 

766 if t: 

767 i += 1 

768 href = "" if self.linenos else ' href="#%s-%d"' % (s, i) 

769 yield 1, '<a id="%s-%d" name="%s-%d"%s></a>' % (s, i, s, i, href) + line 

770 else: 

771 yield 0, line 

772 

773 def _wrap_linespans(self, inner): 

774 s = self.linespans 

775 i = self.linenostart - 1 

776 for t, line in inner: 

777 if t: 

778 i += 1 

779 yield 1, '<span id="%s-%d">%s</span>' % (s, i, line) 

780 else: 

781 yield 0, line 

782 

783 def _wrap_div(self, inner): 

784 style = [] 

785 if (self.noclasses and not self.nobackground and 

786 self.style.background_color is not None): 

787 style.append(f'background: {self.style.background_color}') 

788 if self.cssstyles: 

789 style.append(self.cssstyles) 

790 style = '; '.join(style) 

791 

792 yield 0, ('<div' + (self.cssclass and f' class="{self.cssclass}"') + 

793 (style and (f' style="{style}"')) + '>') 

794 yield from inner 

795 yield 0, '</div>\n' 

796 

797 def _wrap_pre(self, inner): 

798 style = [] 

799 if self.prestyles: 

800 style.append(self.prestyles) 

801 if self.noclasses: 

802 style.append(self._pre_style) 

803 style = '; '.join(style) 

804 

805 if self.filename and self.linenos != 1: 

806 yield 0, ('<span class="filename">' + self.filename + '</span>') 

807 

808 # the empty span here is to keep leading empty lines from being 

809 # ignored by HTML parsers 

810 yield 0, ('<pre' + (style and f' style="{style}"') + '><span></span>') 

811 yield from inner 

812 yield 0, '</pre>' 

813 

814 def _wrap_code(self, inner): 

815 yield 0, '<code>' 

816 yield from inner 

817 yield 0, '</code>' 

818 

819 @functools.lru_cache(maxsize=100) 

820 def _translate_parts(self, value): 

821 """HTML-escape a value and split it by newlines. 

822 

823 ``quote=False`` is intentional: token values are emitted as element 

824 text content (inside ``<span>``/``<pre>``), where ``"`` and ``'`` do 

825 not need escaping. Skipping those two replacements is measurably 

826 faster on the per-token hot path. 

827 """ 

828 return _escape(value, quote=False).split('\n') 

829 

830 def _format_lines(self, tokensource): 

831 """ 

832 Just format the tokens, without any wrapping tags. 

833 Yield individual lines. 

834 """ 

835 nocls = self.noclasses 

836 lsep = self.lineseparator 

837 tagsfile = self.tagsfile 

838 span_openers = self.span_element_openers 

839 translate = self._translate_parts 

840 

841 lspan = '' 

842 line = [] 

843 for ttype, value in tokensource: 

844 try: 

845 cspan = span_openers[ttype] 

846 except KeyError: 

847 title = ' title="{}"'.format('.'.join(ttype)) if self.debug_token_types else '' 

848 if nocls: 

849 css_style = self._get_css_inline_styles(ttype) 

850 if css_style: 

851 css_style = self.class2style[css_style][0] 

852 cspan = f'<span style="{css_style}"{title}>' 

853 else: 

854 cspan = '' 

855 else: 

856 css_class = self._get_css_classes(ttype) 

857 if css_class: 

858 cspan = f'<span class="{css_class}"{title}>' 

859 else: 

860 cspan = '' 

861 span_openers[ttype] = cspan 

862 

863 parts = translate(value) 

864 

865 if tagsfile and ttype in Token.Name: 

866 filename, linenumber = self._lookup_ctag(value) 

867 if linenumber: 

868 base, filename = os.path.split(filename) 

869 if base: 

870 base += '/' 

871 filename, extension = os.path.splitext(filename) 

872 url = self.tagurlformat % {'path': base, 'fname': filename, 

873 'fext': extension} 

874 parts[0] = "<a href=\"%s#%s-%d\">%s" % \ 

875 (url, self.lineanchors, linenumber, parts[0]) 

876 parts[-1] = parts[-1] + "</a>" 

877 

878 # for all but the last line (skipped entirely for single-line 

879 # tokens, which are the common case, to avoid the parts[:-1] slice) 

880 if len(parts) > 1: 

881 for part in parts[:-1]: 

882 if line: 

883 # Also check for part being non-empty, so we avoid 

884 # creating empty <span> tags 

885 if lspan != cspan and part: 

886 line.extend(((lspan and '</span>'), cspan, part, 

887 (cspan and '</span>'), lsep)) 

888 else: # both are the same, or the current part was empty 

889 line.extend((part, (lspan and '</span>'), lsep)) 

890 yield 1, ''.join(line) 

891 line = [] 

892 elif part: 

893 yield 1, ''.join((cspan, part, (cspan and '</span>'), lsep)) 

894 else: 

895 yield 1, lsep 

896 # for the last line 

897 last = parts[-1] 

898 if line and last: 

899 if lspan != cspan: 

900 line.extend(((lspan and '</span>'), cspan, last)) 

901 lspan = cspan 

902 else: 

903 line.append(last) 

904 elif last: 

905 line = [cspan, last] 

906 lspan = cspan 

907 # else we neither have to open a new span nor set lspan 

908 

909 if line: 

910 line.extend(((lspan and '</span>'), lsep)) 

911 yield 1, ''.join(line) 

912 

913 def _lookup_ctag(self, token): 

914 entry = ctags.TagEntry() 

915 if self._ctags.find(entry, token.encode(), 0): 

916 return entry['file'].decode(), entry['lineNumber'] 

917 else: 

918 return None, None 

919 

920 def _highlight_lines(self, tokensource): 

921 """ 

922 Highlighted the lines specified in the `hl_lines` option by 

923 post-processing the token stream coming from `_format_lines`. 

924 """ 

925 hls = self.hl_lines 

926 

927 for i, (t, value) in enumerate(tokensource): 

928 if t != 1: 

929 yield t, value 

930 if i + 1 in hls: # i + 1 because Python indexes start at 0 

931 if self.noclasses: 

932 style = '' 

933 if self.style.highlight_color is not None: 

934 style = (f' style="background-color: {self.style.highlight_color}"') 

935 yield 1, f'<span{style}>{value}</span>' 

936 else: 

937 yield 1, f'<span class="hll">{value}</span>' 

938 else: 

939 yield 1, value 

940 

941 def wrap(self, source): 

942 """ 

943 Wrap the ``source``, which is a generator yielding 

944 individual lines, in custom generators. See docstring 

945 for `format`. Can be overridden. 

946 """ 

947 

948 output = source 

949 if self.wrapcode: 

950 output = self._wrap_code(output) 

951 

952 output = self._wrap_pre(output) 

953 

954 return output 

955 

956 def format_unencoded(self, tokensource, outfile): 

957 """ 

958 The formatting process uses several nested generators; which of 

959 them are used is determined by the user's options. 

960 

961 Each generator should take at least one argument, ``inner``, 

962 and wrap the pieces of text generated by this. 

963 

964 Always yield 2-tuples: (code, text). If "code" is 1, the text 

965 is part of the original tokensource being highlighted, if it's 

966 0, the text is some piece of wrapping. This makes it possible to 

967 use several different wrappers that process the original source 

968 linewise, e.g. line number generators. 

969 """ 

970 source = self._format_lines(tokensource) 

971 

972 # As a special case, we wrap line numbers before line highlighting 

973 # so the line numbers get wrapped in the highlighting tag. 

974 if not self.nowrap and self.linenos == 2: 

975 source = self._wrap_inlinelinenos(source) 

976 

977 if self.hl_lines: 

978 source = self._highlight_lines(source) 

979 

980 if not self.nowrap: 

981 if self.lineanchors: 

982 source = self._wrap_lineanchors(source) 

983 if self.linespans: 

984 source = self._wrap_linespans(source) 

985 source = self.wrap(source) 

986 if self.linenos == 1: 

987 source = self._wrap_tablelinenos(source) 

988 source = self._wrap_div(source) 

989 if self.full: 

990 source = self._wrap_full(source, outfile) 

991 

992 for t, piece in source: 

993 outfile.write(piece)