Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/mako/template.py: 25%

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

300 statements  

1# mako/template.py 

2# Copyright 2006-2026 the Mako authors and contributors <see AUTHORS file> 

3# 

4# This module is part of Mako and is released under 

5# the MIT License: http://www.opensource.org/licenses/mit-license.php 

6 

7"""Provides the Template class, a facade for parsing, generating and executing 

8template strings, as well as template runtime operations.""" 

9 

10import contextlib 

11from importlib import abc 

12from importlib import machinery 

13import json 

14import os 

15import re 

16import shutil 

17import stat 

18import tempfile 

19import types 

20import warnings 

21import weakref 

22 

23from mako import cache 

24from mako import codegen 

25from mako import compat 

26from mako import exceptions 

27from mako import pyparser 

28from mako import runtime 

29from mako import util 

30from mako.lexer import Lexer 

31 

32 

33class Template: 

34 r"""Represents a compiled template. 

35 

36 :class:`.Template` includes a reference to the original 

37 template source (via the :attr:`.source` attribute) 

38 as well as the source code of the 

39 generated Python module (i.e. the :attr:`.code` attribute), 

40 as well as a reference to an actual Python module. 

41 

42 :class:`.Template` is constructed using either a literal string 

43 representing the template text, or a filename representing a filesystem 

44 path to a source file. 

45 

46 :param text: textual template source. This argument is mutually 

47 exclusive versus the ``filename`` parameter. 

48 

49 :param filename: filename of the source template. This argument is 

50 mutually exclusive versus the ``text`` parameter. 

51 

52 :param buffer_filters: string list of filters to be applied 

53 to the output of ``%def``\ s which are buffered, cached, or otherwise 

54 filtered, after all filters 

55 defined with the ``%def`` itself have been applied. Allows the 

56 creation of default expression filters that let the output 

57 of return-valued ``%def``\ s "opt out" of that filtering via 

58 passing special attributes or objects. 

59 

60 :param cache_args: Dictionary of cache configuration arguments that 

61 will be passed to the :class:`.CacheImpl`. See :ref:`caching_toplevel`. 

62 

63 :param cache_dir: 

64 

65 .. deprecated:: 0.6 

66 Use the ``'dir'`` argument in the ``cache_args`` dictionary. 

67 See :ref:`caching_toplevel`. 

68 

69 :param cache_enabled: Boolean flag which enables caching of this 

70 template. See :ref:`caching_toplevel`. 

71 

72 :param cache_impl: String name of a :class:`.CacheImpl` caching 

73 implementation to use. Defaults to ``'beaker'``. 

74 

75 :param cache_type: 

76 

77 .. deprecated:: 0.6 

78 Use the ``'type'`` argument in the ``cache_args`` dictionary. 

79 See :ref:`caching_toplevel`. 

80 

81 :param cache_url: 

82 

83 .. deprecated:: 0.6 

84 Use the ``'url'`` argument in the ``cache_args`` dictionary. 

85 See :ref:`caching_toplevel`. 

86 

87 :param default_filters: List of string filter names that will 

88 be applied to all expressions. See :ref:`filtering_default_filters`. 

89 

90 :param enable_loop: When ``True``, enable the ``loop`` context variable. 

91 This can be set to ``False`` to support templates that may 

92 be making usage of the name "``loop``". Individual templates can 

93 re-enable the "loop" context by placing the directive 

94 ``enable_loop="True"`` inside the ``<%page>`` tag -- see 

95 :ref:`migrating_loop`. 

96 

97 :param encoding_errors: Error parameter passed to ``encode()`` when 

98 string encoding is performed. See :ref:`usage_unicode`. 

99 

100 :param error_handler: Python callable which is called whenever 

101 compile or runtime exceptions occur. The callable is passed 

102 the current context as well as the exception. If the 

103 callable returns ``True``, the exception is considered to 

104 be handled, else it is re-raised after the function 

105 completes. Is used to provide custom error-rendering 

106 functions. 

107 

108 .. seealso:: 

109 

110 :paramref:`.Template.include_error_handler` - include-specific 

111 error handler function 

112 

113 :param format_exceptions: if ``True``, exceptions which occur during 

114 the render phase of this template will be caught and 

115 formatted into an HTML error page, which then becomes the 

116 rendered result of the :meth:`.render` call. Otherwise, 

117 runtime exceptions are propagated outwards. 

118 

119 :param imports: String list of Python statements, typically individual 

120 "import" lines, which will be placed into the module level 

121 preamble of all generated Python modules. See the example 

122 in :ref:`filtering_default_filters`. 

123 

124 :param future_imports: String list of names to import from `__future__`. 

125 These will be concatenated into a comma-separated string and inserted 

126 into the beginning of the template, e.g. ``futures_imports=['FOO', 

127 'BAR']`` results in ``from __future__ import FOO, BAR``. 

128 

129 :param include_error_handler: An error handler that runs when this template 

130 is included within another one via the ``<%include>`` tag, and raises an 

131 error. Compare to the :paramref:`.Template.error_handler` option. 

132 

133 .. versionadded:: 1.0.6 

134 

135 .. seealso:: 

136 

137 :paramref:`.Template.error_handler` - top-level error handler function 

138 

139 :param input_encoding: Encoding of the template's source code. Can 

140 be used in lieu of the coding comment. See 

141 :ref:`usage_unicode` as well as :ref:`unicode_toplevel` for 

142 details on source encoding. 

143 

144 :param lookup: a :class:`.TemplateLookup` instance that will be used 

145 for all file lookups via the ``<%namespace>``, 

146 ``<%include>``, and ``<%inherit>`` tags. See 

147 :ref:`usage_templatelookup`. 

148 

149 :param module_directory: Filesystem location where generated 

150 Python module files will be placed. 

151 

152 :param module_filename: Overrides the filename of the generated 

153 Python module file. For advanced usage only. 

154 

155 :param module_writer: A callable which overrides how the Python 

156 module is written entirely. The callable is passed the 

157 encoded source content of the module and the destination 

158 path to be written to. The default behavior of module writing 

159 uses a tempfile in conjunction with a file move in order 

160 to make the operation atomic. So a user-defined module 

161 writing function that mimics the default behavior would be: 

162 

163 .. sourcecode:: python 

164 

165 import tempfile 

166 import os 

167 import shutil 

168 

169 def module_writer(source, outputpath): 

170 (dest, name) = \\ 

171 tempfile.mkstemp( 

172 dir=os.path.dirname(outputpath) 

173 ) 

174 

175 os.write(dest, source) 

176 os.close(dest) 

177 shutil.move(name, outputpath) 

178 

179 from mako.template import Template 

180 mytemplate = Template( 

181 filename="index.html", 

182 module_directory="/path/to/modules", 

183 module_writer=module_writer 

184 ) 

185 

186 The function is provided for unusual configurations where 

187 certain platform-specific permissions or other special 

188 steps are needed. 

189 

190 :param output_encoding: The encoding to use when :meth:`.render` 

191 is called. 

192 See :ref:`usage_unicode` as well as :ref:`unicode_toplevel`. 

193 

194 :param preprocessor: Python callable which will be passed 

195 the full template source before it is parsed. The return 

196 result of the callable will be used as the template source 

197 code. 

198 

199 :param lexer_cls: A :class:`.Lexer` class used to parse 

200 the template. The :class:`.Lexer` class is used by 

201 default. 

202 

203 .. versionadded:: 0.7.4 

204 

205 :param strict_undefined: Replaces the automatic usage of 

206 ``UNDEFINED`` for any undeclared variables not located in 

207 the :class:`.Context` with an immediate raise of 

208 ``NameError``. The advantage is immediate reporting of 

209 missing variables which include the name. 

210 

211 .. versionadded:: 0.3.6 

212 

213 :param uri: string URI or other identifier for this template. 

214 If not provided, the ``uri`` is generated from the filesystem 

215 path, or from the in-memory identity of a non-file-based 

216 template. The primary usage of the ``uri`` is to provide a key 

217 within :class:`.TemplateLookup`, as well as to generate the 

218 file path of the generated Python module file, if 

219 ``module_directory`` is specified. 

220 

221 """ 

222 

223 lexer_cls = Lexer 

224 

225 def __init__( 

226 self, 

227 text=None, 

228 filename=None, 

229 uri=None, 

230 format_exceptions=False, 

231 error_handler=None, 

232 lookup=None, 

233 output_encoding=None, 

234 encoding_errors="strict", 

235 module_directory=None, 

236 cache_args=None, 

237 cache_impl="beaker", 

238 cache_enabled=True, 

239 cache_type=None, 

240 cache_dir=None, 

241 cache_url=None, 

242 module_filename=None, 

243 input_encoding=None, 

244 module_writer=None, 

245 default_filters=None, 

246 buffer_filters=(), 

247 strict_undefined=False, 

248 imports=None, 

249 future_imports=None, 

250 enable_loop=True, 

251 preprocessor=None, 

252 lexer_cls=None, 

253 include_error_handler=None, 

254 ): 

255 if uri: 

256 self.module_id = re.sub(r"\W", "_", uri) 

257 self.uri = uri 

258 elif filename: 

259 self.module_id = re.sub(r"\W", "_", filename) 

260 drive, path = os.path.splitdrive(filename) 

261 path = os.path.normpath(path).replace(os.path.sep, "/") 

262 self.uri = path 

263 else: 

264 self.module_id = "memory:" + hex(id(self)) 

265 self.uri = self.module_id 

266 

267 u_norm = self.uri.replace("\\", "/").lstrip("/") 

268 u_norm = os.path.normpath(u_norm) 

269 if u_norm.startswith(".."): 

270 raise exceptions.TemplateLookupException( 

271 'Template uri "%s" is invalid - ' 

272 "it cannot be relative outside " 

273 "of the root path." % self.uri 

274 ) 

275 

276 self.input_encoding = input_encoding 

277 self.output_encoding = output_encoding 

278 self.encoding_errors = encoding_errors 

279 self.enable_loop = enable_loop 

280 self.strict_undefined = strict_undefined 

281 self.module_writer = module_writer 

282 

283 if default_filters is None: 

284 self.default_filters = ["str"] 

285 else: 

286 self.default_filters = default_filters 

287 self.buffer_filters = buffer_filters 

288 

289 self.imports = imports 

290 self.future_imports = future_imports 

291 self.preprocessor = preprocessor 

292 

293 if lexer_cls is not None: 

294 self.lexer_cls = lexer_cls 

295 

296 # if plain text, compile code in memory only 

297 if text is not None: 

298 code, module = _compile_text(self, text, filename) 

299 self._code = code 

300 self._source = text 

301 ModuleInfo(module, None, self, filename, code, text, uri) 

302 elif filename is not None: 

303 # if template filename and a module directory, load 

304 # a filesystem-based module file, generating if needed 

305 if module_filename is not None: 

306 path = module_filename 

307 elif module_directory is not None: 

308 path = os.path.abspath( 

309 os.path.join( 

310 os.path.normpath(module_directory), u_norm + ".py" 

311 ) 

312 ) 

313 else: 

314 path = None 

315 module = self._compile_from_file(path, filename) 

316 else: 

317 raise exceptions.RuntimeException( 

318 "Template requires text or filename" 

319 ) 

320 

321 self.module = module 

322 self.filename = filename 

323 self.callable_ = self.module.render_body 

324 self.format_exceptions = format_exceptions 

325 self.error_handler = error_handler 

326 self.include_error_handler = include_error_handler 

327 self.lookup = lookup 

328 

329 self.module_directory = module_directory 

330 

331 self._setup_cache_args( 

332 cache_impl, 

333 cache_enabled, 

334 cache_args, 

335 cache_type, 

336 cache_dir, 

337 cache_url, 

338 ) 

339 

340 @util.memoized_property 

341 def reserved_names(self): 

342 if self.enable_loop: 

343 return codegen.RESERVED_NAMES 

344 else: 

345 return codegen.RESERVED_NAMES.difference(["loop"]) 

346 

347 def _setup_cache_args( 

348 self, 

349 cache_impl, 

350 cache_enabled, 

351 cache_args, 

352 cache_type, 

353 cache_dir, 

354 cache_url, 

355 ): 

356 self.cache_impl = cache_impl 

357 self.cache_enabled = cache_enabled 

358 self.cache_args = cache_args or {} 

359 # transfer deprecated cache_* args 

360 if cache_type: 

361 self.cache_args["type"] = cache_type 

362 if cache_dir: 

363 self.cache_args["dir"] = cache_dir 

364 if cache_url: 

365 self.cache_args["url"] = cache_url 

366 

367 def _compile_from_file(self, path, filename): 

368 if path is not None: 

369 util.verify_directory(os.path.dirname(path)) 

370 filemtime = os.stat(filename)[stat.ST_MTIME] 

371 with _translate_module_warnings( 

372 lambda: util.read_python_file(path), path, filename 

373 ): 

374 if ( 

375 not os.path.exists(path) 

376 or os.stat(path)[stat.ST_MTIME] < filemtime 

377 ): 

378 data = util.read_file(filename) 

379 with _drop_expression_warnings(): 

380 _compile_module_file( 

381 self, data, filename, path, self.module_writer 

382 ) 

383 module = compat.load_module(self.module_id, path) 

384 if module._magic_number != codegen.MAGIC_NUMBER: 

385 data = util.read_file(filename) 

386 with _drop_expression_warnings(): 

387 _compile_module_file( 

388 self, data, filename, path, self.module_writer 

389 ) 

390 module = compat.load_module(self.module_id, path) 

391 

392 ModuleInfo(module, path, self, filename, None, None, None) 

393 else: 

394 # template filename and no module directory, compile code 

395 # in memory 

396 data = util.read_file(filename) 

397 code, module = _compile_text(self, data, filename) 

398 self._source = None 

399 self._code = code 

400 ModuleInfo(module, None, self, filename, code, None, None) 

401 return module 

402 

403 @property 

404 def source(self): 

405 """Return the template source code for this :class:`.Template`.""" 

406 

407 return _get_module_info_from_callable(self.callable_).source 

408 

409 @property 

410 def code(self): 

411 """Return the module source code for this :class:`.Template`.""" 

412 

413 return _get_module_info_from_callable(self.callable_).code 

414 

415 @util.memoized_property 

416 def cache(self): 

417 return cache.Cache(self) 

418 

419 @property 

420 def cache_dir(self): 

421 return self.cache_args["dir"] 

422 

423 @property 

424 def cache_url(self): 

425 return self.cache_args["url"] 

426 

427 @property 

428 def cache_type(self): 

429 return self.cache_args["type"] 

430 

431 def render(self, *args, **data): 

432 """Render the output of this template as a string. 

433 

434 If the template specifies an output encoding, the string 

435 will be encoded accordingly, else the output is raw (raw 

436 output uses `StringIO` and can't handle multibyte 

437 characters). A :class:`.Context` object is created corresponding 

438 to the given data. Arguments that are explicitly declared 

439 by this template's internal rendering method are also 

440 pulled from the given ``*args``, ``**data`` members. 

441 

442 """ 

443 return runtime._render(self, self.callable_, args, data) 

444 

445 def render_unicode(self, *args, **data): 

446 """Render the output of this template as a unicode object.""" 

447 

448 return runtime._render( 

449 self, self.callable_, args, data, as_unicode=True 

450 ) 

451 

452 def render_context(self, context, *args, **kwargs): 

453 """Render this :class:`.Template` with the given context. 

454 

455 The data is written to the context's buffer. 

456 

457 """ 

458 if getattr(context, "_with_template", None) is None: 

459 context._set_with_template(self) 

460 runtime._render_context(self, self.callable_, context, *args, **kwargs) 

461 

462 def has_def(self, name): 

463 return hasattr(self.module, "render_%s" % name) 

464 

465 def get_def(self, name): 

466 """Return a def of this template as a :class:`.DefTemplate`.""" 

467 

468 return DefTemplate(self, getattr(self.module, "render_%s" % name)) 

469 

470 def list_defs(self): 

471 """return a list of defs in the template. 

472 

473 .. versionadded:: 1.0.4 

474 

475 """ 

476 return [i[7:] for i in dir(self.module) if i[:7] == "render_"] 

477 

478 def _get_def_callable(self, name): 

479 return getattr(self.module, "render_%s" % name) 

480 

481 @property 

482 def last_modified(self): 

483 return self.module._modified_time 

484 

485 

486class ModuleTemplate(Template): 

487 """A Template which is constructed given an existing Python module. 

488 

489 e.g.:: 

490 

491 t = Template("this is a template") 

492 f = file("mymodule.py", "w") 

493 f.write(t.code) 

494 f.close() 

495 

496 import mymodule 

497 

498 t = ModuleTemplate(mymodule) 

499 print(t.render()) 

500 

501 """ 

502 

503 def __init__( 

504 self, 

505 module, 

506 module_filename=None, 

507 template=None, 

508 template_filename=None, 

509 module_source=None, 

510 template_source=None, 

511 output_encoding=None, 

512 encoding_errors="strict", 

513 format_exceptions=False, 

514 error_handler=None, 

515 lookup=None, 

516 cache_args=None, 

517 cache_impl="beaker", 

518 cache_enabled=True, 

519 cache_type=None, 

520 cache_dir=None, 

521 cache_url=None, 

522 include_error_handler=None, 

523 ): 

524 self.module_id = re.sub(r"\W", "_", module._template_uri) 

525 self.uri = module._template_uri 

526 self.input_encoding = module._source_encoding 

527 self.output_encoding = output_encoding 

528 self.encoding_errors = encoding_errors 

529 self.enable_loop = module._enable_loop 

530 

531 self.module = module 

532 self.filename = template_filename 

533 ModuleInfo( 

534 module, 

535 module_filename, 

536 self, 

537 template_filename, 

538 module_source, 

539 template_source, 

540 module._template_uri, 

541 ) 

542 

543 self.callable_ = self.module.render_body 

544 self.format_exceptions = format_exceptions 

545 self.error_handler = error_handler 

546 self.include_error_handler = include_error_handler 

547 self.lookup = lookup 

548 self._setup_cache_args( 

549 cache_impl, 

550 cache_enabled, 

551 cache_args, 

552 cache_type, 

553 cache_dir, 

554 cache_url, 

555 ) 

556 

557 

558class DefTemplate(Template): 

559 """A :class:`.Template` which represents a callable def in a parent 

560 template.""" 

561 

562 def __init__(self, parent, callable_): 

563 self.parent = parent 

564 self.callable_ = callable_ 

565 self.output_encoding = parent.output_encoding 

566 self.module = parent.module 

567 self.encoding_errors = parent.encoding_errors 

568 self.format_exceptions = parent.format_exceptions 

569 self.error_handler = parent.error_handler 

570 self.include_error_handler = parent.include_error_handler 

571 self.enable_loop = parent.enable_loop 

572 self.lookup = parent.lookup 

573 

574 def get_def(self, name): 

575 return self.parent.get_def(name) 

576 

577 

578class ModuleInfo: 

579 """Stores information about a module currently loaded into 

580 memory, provides reverse lookups of template source, module 

581 source code based on a module's identifier. 

582 

583 """ 

584 

585 _modules = weakref.WeakValueDictionary() 

586 

587 def __init__( 

588 self, 

589 module, 

590 module_filename, 

591 template, 

592 template_filename, 

593 module_source, 

594 template_source, 

595 template_uri, 

596 ): 

597 self.module = module 

598 self.module_filename = module_filename 

599 self.template_filename = template_filename 

600 self.module_source = module_source 

601 self.template_source = template_source 

602 self.template_uri = template_uri 

603 self._modules[module.__name__] = template._mmarker = self 

604 if module_filename: 

605 self._modules[module_filename] = self 

606 

607 @classmethod 

608 def get_module_source_metadata(cls, module_source, full_line_map=False): 

609 source_map = re.search( 

610 r"__M_BEGIN_METADATA(.+?)__M_END_METADATA", module_source, re.S 

611 ).group(1) 

612 source_map = json.loads(source_map) 

613 source_map["line_map"] = { 

614 int(k): int(v) for k, v in source_map["line_map"].items() 

615 } 

616 if full_line_map: 

617 f_line_map = source_map["full_line_map"] = [] 

618 line_map = source_map["line_map"] 

619 

620 curr_templ_line = 1 

621 for mod_line in range(1, max(line_map)): 

622 if mod_line in line_map: 

623 curr_templ_line = line_map[mod_line] 

624 f_line_map.append(curr_templ_line) 

625 return source_map 

626 

627 @property 

628 def code(self): 

629 if self.module_source is not None: 

630 return self.module_source 

631 else: 

632 return util.read_python_file(self.module_filename) 

633 

634 @property 

635 def source(self): 

636 if self.template_source is None: 

637 data = util.read_file(self.template_filename) 

638 if self.module._source_encoding: 

639 return data.decode(self.module._source_encoding) 

640 else: 

641 return data 

642 

643 elif self.module._source_encoding and not isinstance( 

644 self.template_source, str 

645 ): 

646 return self.template_source.decode(self.module._source_encoding) 

647 else: 

648 return self.template_source 

649 

650 

651def _compile(template, text, filename, generate_magic_comment): 

652 lexer = template.lexer_cls( 

653 text, 

654 filename, 

655 input_encoding=template.input_encoding, 

656 preprocessor=template.preprocessor, 

657 ) 

658 node = lexer.parse() 

659 source = codegen.compile( 

660 node, 

661 template.uri, 

662 filename, 

663 default_filters=template.default_filters, 

664 buffer_filters=template.buffer_filters, 

665 imports=template.imports, 

666 future_imports=template.future_imports, 

667 source_encoding=lexer.encoding, 

668 generate_magic_comment=generate_magic_comment, 

669 strict_undefined=template.strict_undefined, 

670 enable_loop=template.enable_loop, 

671 reserved_names=template.reserved_names, 

672 ) 

673 return source, lexer 

674 

675 

676class _ModuleSourceLoader(abc.Loader): 

677 """Provide the generated source of an in-memory template module. 

678 

679 A module created directly from :class:`types.ModuleType` has ``__spec__`` 

680 and ``__loader__`` present in its namespace but set to ``None``. The 

681 :mod:`linecache` module consults these while a traceback is being built, 

682 and as of Python 3.15 emits a :class:`DeprecationWarning` when it finds a 

683 ``__spec__`` that has no loader; a module lacking a real loader also has 

684 no source lines available to show for its frames. Supplying a loader 

685 that can produce the generated module source addresses both. 

686 

687 """ 

688 

689 def __init__(self, name, source): 

690 self.name = name 

691 self._source = source 

692 

693 def get_source(self, name): 

694 return self._source 

695 

696 def is_package(self, name): 

697 return False 

698 

699 

700@contextlib.contextmanager 

701def _show_warnings_as(locate): 

702 """Replace the warnings display hook for the duration of the block. 

703 

704 ``locate`` is passed the message, category, filename and line number of 

705 each warning, and returns the filename and line number it should be 

706 shown as, or ``None`` for a warning that should not be shown at all. 

707 

708 The display hook is replaced, rather than the warnings being recorded 

709 and raised a second time, so that each warning passes through the 

710 warnings filters exactly once. A filter with a stateful action such as 

711 "once" would otherwise suppress the second occurrence, and the warning 

712 would be lost entirely. 

713 

714 """ 

715 

716 show_warning = warnings.showwarning 

717 

718 def _show(message, category, filename, lineno, file=None, line=None): 

719 location = locate(message, category, filename, lineno) 

720 if location is None: 

721 return 

722 

723 if location != (filename, lineno): 

724 # the source line, if given, is that of the original location; 

725 # allow it to be looked up again for the new one 

726 line = None 

727 filename, lineno = location 

728 

729 show_warning(message, category, filename, lineno, file, line) 

730 

731 warnings.showwarning = _show 

732 try: 

733 yield 

734 finally: 

735 warnings.showwarning = show_warning 

736 

737 

738def _drop_expression_warnings(): 

739 """Drop warnings raised while individual expressions are parsed. 

740 

741 These carry no location that can be related back to the template, as the 

742 expression is parsed on its own, and the same warning is raised again 

743 when the module as a whole is compiled, where the location can be 

744 translated. 

745 

746 """ 

747 

748 def _locate(message, category, filename, lineno): 

749 if filename != pyparser.EXPRESSION_FILENAME: 

750 return filename, lineno 

751 

752 # a "once" filter records the warning globally as it is passed 

753 # over, so remove that record, else the occurrence that can be 

754 # translated would be suppressed 

755 warnings.onceregistry.pop((str(message), category), None) 

756 return None 

757 

758 return _show_warnings_as(_locate) 

759 

760 

761def _translate_module_warnings(get_source, module_id, filename): 

762 """Report warnings raised for a generated module against the template 

763 it was generated from, translating the line number through the module's 

764 line map. 

765 

766 Any other warning, such as one raised by a module imported from a 

767 ``<%! %>`` block as the template module executes, is shown unchanged, as 

768 is one whose line cannot be translated. 

769 

770 """ 

771 

772 line_map = None 

773 

774 def _locate(message, category, warning_filename, lineno): 

775 nonlocal line_map 

776 

777 if warning_filename != module_id: 

778 return warning_filename, lineno 

779 

780 if line_map is None: 

781 try: 

782 line_map = ModuleInfo.get_module_source_metadata( 

783 get_source(), full_line_map=True 

784 )["full_line_map"] 

785 except Exception: 

786 # a module file that carries no usable metadata, having 

787 # been written by some other means or damaged. the warning 

788 # is worth more than the translation is 

789 line_map = [] 

790 

791 try: 

792 translated = line_map[lineno - 1] 

793 except IndexError: 

794 return warning_filename, lineno 

795 else: 

796 return filename, translated 

797 

798 return _show_warnings_as(_locate) 

799 

800 

801def _compile_text(template, text, filename): 

802 identifier = template.module_id 

803 

804 cid = identifier 

805 module = types.ModuleType(cid) 

806 

807 with _drop_expression_warnings(): 

808 source, lexer = _compile( 

809 template, text, filename, generate_magic_comment=False 

810 ) 

811 

812 loader = _ModuleSourceLoader(cid, source) 

813 module.__loader__ = loader 

814 module.__spec__ = machinery.ModuleSpec(cid, loader, origin=cid) 

815 

816 with _translate_module_warnings( 

817 lambda: source, cid, filename or template.uri 

818 ): 

819 code = compile(source, cid, "exec") 

820 

821 # the module body, which is the code of any <%! %> blocks, is 

822 # executed within the same block, so that a warning it raises is 

823 # reported the same way here as it is when the template is loaded 

824 # from a module file, where the import system compiles and executes 

825 # the module as one step 

826 exec(code, module.__dict__, module.__dict__) 

827 

828 return (source, module) 

829 

830 

831def _compile_module_file(template, text, filename, outputpath, module_writer): 

832 source, lexer = _compile( 

833 template, text, filename, generate_magic_comment=True 

834 ) 

835 

836 if isinstance(source, str): 

837 source = source.encode(lexer.encoding or "ascii") 

838 

839 if module_writer: 

840 module_writer(source, outputpath) 

841 else: 

842 # make tempfiles in the same location as the ultimate 

843 # location. this ensures they're on the same filesystem, 

844 # avoiding synchronization issues. 

845 dest, name = tempfile.mkstemp(dir=os.path.dirname(outputpath)) 

846 

847 os.write(dest, source) 

848 os.close(dest) 

849 shutil.move(name, outputpath) 

850 

851 

852def _get_module_info_from_callable(callable_): 

853 return _get_module_info(callable_.__globals__["__name__"]) 

854 

855 

856def _get_module_info(filename): 

857 return ModuleInfo._modules[filename]