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

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

301 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 posixpath 

16import re 

17import shutil 

18import stat 

19import tempfile 

20import types 

21import warnings 

22import weakref 

23 

24from mako import cache 

25from mako import codegen 

26from mako import compat 

27from mako import exceptions 

28from mako import pyparser 

29from mako import runtime 

30from mako import util 

31from mako.lexer import Lexer 

32 

33 

34class Template: 

35 r"""Represents a compiled template. 

36 

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

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

39 as well as the source code of the 

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

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

42 

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

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

45 path to a source file. 

46 

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

48 exclusive versus the ``filename`` parameter. 

49 

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

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

52 

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

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

55 filtered, after all filters 

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

57 creation of default expression filters that let the output 

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

59 passing special attributes or objects. 

60 

61 :param cache_args: Dictionary of cache configuration arguments that 

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

63 

64 :param cache_dir: 

65 

66 .. deprecated:: 0.6 

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

68 See :ref:`caching_toplevel`. 

69 

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

71 template. See :ref:`caching_toplevel`. 

72 

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

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

75 

76 :param cache_type: 

77 

78 .. deprecated:: 0.6 

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

80 See :ref:`caching_toplevel`. 

81 

82 :param cache_url: 

83 

84 .. deprecated:: 0.6 

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

86 See :ref:`caching_toplevel`. 

87 

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

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

90 

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

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

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

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

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

96 :ref:`migrating_loop`. 

97 

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

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

100 

101 :param error_handler: Python callable which is called whenever 

102 compile or runtime exceptions occur. The callable is passed 

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

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

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

106 completes. Is used to provide custom error-rendering 

107 functions. 

108 

109 .. seealso:: 

110 

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

112 error handler function 

113 

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

115 the render phase of this template will be caught and 

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

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

118 runtime exceptions are propagated outwards. 

119 

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

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

122 preamble of all generated Python modules. See the example 

123 in :ref:`filtering_default_filters`. 

124 

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

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

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

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

129 

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

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

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

133 

134 .. versionadded:: 1.0.6 

135 

136 .. seealso:: 

137 

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

139 

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

141 be used in lieu of the coding comment. See 

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

143 details on source encoding. 

144 

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

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

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

148 :ref:`usage_templatelookup`. 

149 

150 :param module_directory: Filesystem location where generated 

151 Python module files will be placed. 

152 

153 :param module_filename: Overrides the filename of the generated 

154 Python module file. For advanced usage only. 

155 

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

157 module is written entirely. The callable is passed the 

158 encoded source content of the module and the destination 

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

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

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

162 writing function that mimics the default behavior would be: 

163 

164 .. sourcecode:: python 

165 

166 import tempfile 

167 import os 

168 import shutil 

169 

170 def module_writer(source, outputpath): 

171 (dest, name) = \\ 

172 tempfile.mkstemp( 

173 dir=os.path.dirname(outputpath) 

174 ) 

175 

176 os.write(dest, source) 

177 os.close(dest) 

178 shutil.move(name, outputpath) 

179 

180 from mako.template import Template 

181 mytemplate = Template( 

182 filename="index.html", 

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

184 module_writer=module_writer 

185 ) 

186 

187 The function is provided for unusual configurations where 

188 certain platform-specific permissions or other special 

189 steps are needed. 

190 

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

192 is called. 

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

194 

195 :param preprocessor: Python callable which will be passed 

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

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

198 code. 

199 

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

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

202 default. 

203 

204 .. versionadded:: 0.7.4 

205 

206 :param strict_undefined: Replaces the automatic usage of 

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

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

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

210 missing variables which include the name. 

211 

212 .. versionadded:: 0.3.6 

213 

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

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

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

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

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

219 file path of the generated Python module file, if 

220 ``module_directory`` is specified. 

221 

222 """ 

223 

224 lexer_cls = Lexer 

225 

226 def __init__( 

227 self, 

228 text=None, 

229 filename=None, 

230 uri=None, 

231 format_exceptions=False, 

232 error_handler=None, 

233 lookup=None, 

234 output_encoding=None, 

235 encoding_errors="strict", 

236 module_directory=None, 

237 cache_args=None, 

238 cache_impl="beaker", 

239 cache_enabled=True, 

240 cache_type=None, 

241 cache_dir=None, 

242 cache_url=None, 

243 module_filename=None, 

244 input_encoding=None, 

245 module_writer=None, 

246 default_filters=None, 

247 buffer_filters=(), 

248 strict_undefined=False, 

249 imports=None, 

250 future_imports=None, 

251 enable_loop=True, 

252 preprocessor=None, 

253 lexer_cls=None, 

254 include_error_handler=None, 

255 ): 

256 if uri: 

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

258 self.uri = uri 

259 elif filename: 

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

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

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

263 self.uri = path 

264 else: 

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

266 self.uri = self.module_id 

267 

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

269 # use posixpath, not os.path, to normalize; this is the same 

270 # module used by TemplateLookup to resolve the URI to a file, 

271 # and unlike ntpath it has no notion of a drive designator, so 

272 # a URI like "C:/../../secret.txt" retains its ".." segments 

273 # here rather than having them absorbed into a drive root. 

274 u_norm = posixpath.normpath(u_norm) 

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

276 raise exceptions.TemplateLookupException( 

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

278 "it cannot be relative outside " 

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

280 ) 

281 

282 self.input_encoding = input_encoding 

283 self.output_encoding = output_encoding 

284 self.encoding_errors = encoding_errors 

285 self.enable_loop = enable_loop 

286 self.strict_undefined = strict_undefined 

287 self.module_writer = module_writer 

288 

289 if default_filters is None: 

290 self.default_filters = ["str"] 

291 else: 

292 self.default_filters = default_filters 

293 self.buffer_filters = buffer_filters 

294 

295 self.imports = imports 

296 self.future_imports = future_imports 

297 self.preprocessor = preprocessor 

298 

299 if lexer_cls is not None: 

300 self.lexer_cls = lexer_cls 

301 

302 # if plain text, compile code in memory only 

303 if text is not None: 

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

305 self._code = code 

306 self._source = text 

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

308 elif filename is not None: 

309 # if template filename and a module directory, load 

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

311 if module_filename is not None: 

312 path = module_filename 

313 elif module_directory is not None: 

314 path = os.path.abspath( 

315 os.path.join( 

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

317 ) 

318 ) 

319 else: 

320 path = None 

321 module = self._compile_from_file(path, filename) 

322 else: 

323 raise exceptions.RuntimeException( 

324 "Template requires text or filename" 

325 ) 

326 

327 self.module = module 

328 self.filename = filename 

329 self.callable_ = self.module.render_body 

330 self.format_exceptions = format_exceptions 

331 self.error_handler = error_handler 

332 self.include_error_handler = include_error_handler 

333 self.lookup = lookup 

334 

335 self.module_directory = module_directory 

336 

337 self._setup_cache_args( 

338 cache_impl, 

339 cache_enabled, 

340 cache_args, 

341 cache_type, 

342 cache_dir, 

343 cache_url, 

344 ) 

345 

346 @util.memoized_property 

347 def reserved_names(self): 

348 if self.enable_loop: 

349 return codegen.RESERVED_NAMES 

350 else: 

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

352 

353 def _setup_cache_args( 

354 self, 

355 cache_impl, 

356 cache_enabled, 

357 cache_args, 

358 cache_type, 

359 cache_dir, 

360 cache_url, 

361 ): 

362 self.cache_impl = cache_impl 

363 self.cache_enabled = cache_enabled 

364 self.cache_args = cache_args or {} 

365 # transfer deprecated cache_* args 

366 if cache_type: 

367 self.cache_args["type"] = cache_type 

368 if cache_dir: 

369 self.cache_args["dir"] = cache_dir 

370 if cache_url: 

371 self.cache_args["url"] = cache_url 

372 

373 def _compile_from_file(self, path, filename): 

374 if path is not None: 

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

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

377 with _translate_module_warnings( 

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

379 ): 

380 if ( 

381 not os.path.exists(path) 

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

383 ): 

384 data = util.read_file(filename) 

385 with _drop_expression_warnings(): 

386 _compile_module_file( 

387 self, data, filename, path, self.module_writer 

388 ) 

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

390 if module._magic_number != codegen.MAGIC_NUMBER: 

391 data = util.read_file(filename) 

392 with _drop_expression_warnings(): 

393 _compile_module_file( 

394 self, data, filename, path, self.module_writer 

395 ) 

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

397 

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

399 else: 

400 # template filename and no module directory, compile code 

401 # in memory 

402 data = util.read_file(filename) 

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

404 self._source = None 

405 self._code = code 

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

407 return module 

408 

409 @property 

410 def source(self): 

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

412 

413 return _get_module_info_from_callable(self.callable_).source 

414 

415 @property 

416 def code(self): 

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

418 

419 return _get_module_info_from_callable(self.callable_).code 

420 

421 @util.memoized_property 

422 def cache(self): 

423 return cache.Cache(self) 

424 

425 @property 

426 def cache_dir(self): 

427 return self.cache_args["dir"] 

428 

429 @property 

430 def cache_url(self): 

431 return self.cache_args["url"] 

432 

433 @property 

434 def cache_type(self): 

435 return self.cache_args["type"] 

436 

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

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

439 

440 If the template specifies an output encoding, the string 

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

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

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

444 to the given data. Arguments that are explicitly declared 

445 by this template's internal rendering method are also 

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

447 

448 """ 

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

450 

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

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

453 

454 return runtime._render( 

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

456 ) 

457 

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

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

460 

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

462 

463 """ 

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

465 context._set_with_template(self) 

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

467 

468 def has_def(self, name): 

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

470 

471 def get_def(self, name): 

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

473 

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

475 

476 def list_defs(self): 

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

478 

479 .. versionadded:: 1.0.4 

480 

481 """ 

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

483 

484 def _get_def_callable(self, name): 

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

486 

487 @property 

488 def last_modified(self): 

489 return self.module._modified_time 

490 

491 

492class ModuleTemplate(Template): 

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

494 

495 e.g.:: 

496 

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

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

499 f.write(t.code) 

500 f.close() 

501 

502 import mymodule 

503 

504 t = ModuleTemplate(mymodule) 

505 print(t.render()) 

506 

507 """ 

508 

509 def __init__( 

510 self, 

511 module, 

512 module_filename=None, 

513 template=None, 

514 template_filename=None, 

515 module_source=None, 

516 template_source=None, 

517 output_encoding=None, 

518 encoding_errors="strict", 

519 format_exceptions=False, 

520 error_handler=None, 

521 lookup=None, 

522 cache_args=None, 

523 cache_impl="beaker", 

524 cache_enabled=True, 

525 cache_type=None, 

526 cache_dir=None, 

527 cache_url=None, 

528 include_error_handler=None, 

529 ): 

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

531 self.uri = module._template_uri 

532 self.input_encoding = module._source_encoding 

533 self.output_encoding = output_encoding 

534 self.encoding_errors = encoding_errors 

535 self.enable_loop = module._enable_loop 

536 

537 self.module = module 

538 self.filename = template_filename 

539 ModuleInfo( 

540 module, 

541 module_filename, 

542 self, 

543 template_filename, 

544 module_source, 

545 template_source, 

546 module._template_uri, 

547 ) 

548 

549 self.callable_ = self.module.render_body 

550 self.format_exceptions = format_exceptions 

551 self.error_handler = error_handler 

552 self.include_error_handler = include_error_handler 

553 self.lookup = lookup 

554 self._setup_cache_args( 

555 cache_impl, 

556 cache_enabled, 

557 cache_args, 

558 cache_type, 

559 cache_dir, 

560 cache_url, 

561 ) 

562 

563 

564class DefTemplate(Template): 

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

566 template.""" 

567 

568 def __init__(self, parent, callable_): 

569 self.parent = parent 

570 self.callable_ = callable_ 

571 self.output_encoding = parent.output_encoding 

572 self.module = parent.module 

573 self.encoding_errors = parent.encoding_errors 

574 self.format_exceptions = parent.format_exceptions 

575 self.error_handler = parent.error_handler 

576 self.include_error_handler = parent.include_error_handler 

577 self.enable_loop = parent.enable_loop 

578 self.lookup = parent.lookup 

579 

580 def get_def(self, name): 

581 return self.parent.get_def(name) 

582 

583 

584class ModuleInfo: 

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

586 memory, provides reverse lookups of template source, module 

587 source code based on a module's identifier. 

588 

589 """ 

590 

591 _modules = weakref.WeakValueDictionary() 

592 

593 def __init__( 

594 self, 

595 module, 

596 module_filename, 

597 template, 

598 template_filename, 

599 module_source, 

600 template_source, 

601 template_uri, 

602 ): 

603 self.module = module 

604 self.module_filename = module_filename 

605 self.template_filename = template_filename 

606 self.module_source = module_source 

607 self.template_source = template_source 

608 self.template_uri = template_uri 

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

610 if module_filename: 

611 self._modules[module_filename] = self 

612 

613 @classmethod 

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

615 source_map = re.search( 

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

617 ).group(1) 

618 source_map = json.loads(source_map) 

619 source_map["line_map"] = { 

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

621 } 

622 if full_line_map: 

623 f_line_map = source_map["full_line_map"] = [] 

624 line_map = source_map["line_map"] 

625 

626 curr_templ_line = 1 

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

628 if mod_line in line_map: 

629 curr_templ_line = line_map[mod_line] 

630 f_line_map.append(curr_templ_line) 

631 return source_map 

632 

633 @property 

634 def code(self): 

635 if self.module_source is not None: 

636 return self.module_source 

637 else: 

638 return util.read_python_file(self.module_filename) 

639 

640 @property 

641 def source(self): 

642 if self.template_source is None: 

643 data = util.read_file(self.template_filename) 

644 if self.module._source_encoding: 

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

646 else: 

647 return data 

648 

649 elif self.module._source_encoding and not isinstance( 

650 self.template_source, str 

651 ): 

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

653 else: 

654 return self.template_source 

655 

656 

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

658 lexer = template.lexer_cls( 

659 text, 

660 filename, 

661 input_encoding=template.input_encoding, 

662 preprocessor=template.preprocessor, 

663 ) 

664 node = lexer.parse() 

665 source = codegen.compile( 

666 node, 

667 template.uri, 

668 filename, 

669 default_filters=template.default_filters, 

670 buffer_filters=template.buffer_filters, 

671 imports=template.imports, 

672 future_imports=template.future_imports, 

673 source_encoding=lexer.encoding, 

674 generate_magic_comment=generate_magic_comment, 

675 strict_undefined=template.strict_undefined, 

676 enable_loop=template.enable_loop, 

677 reserved_names=template.reserved_names, 

678 ) 

679 return source, lexer 

680 

681 

682class _ModuleSourceLoader(abc.Loader): 

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

684 

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

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

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

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

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

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

691 that can produce the generated module source addresses both. 

692 

693 """ 

694 

695 def __init__(self, name, source): 

696 self.name = name 

697 self._source = source 

698 

699 def get_source(self, name): 

700 return self._source 

701 

702 def is_package(self, name): 

703 return False 

704 

705 

706@contextlib.contextmanager 

707def _show_warnings_as(locate): 

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

709 

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

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

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

713 

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

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

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

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

718 would be lost entirely. 

719 

720 """ 

721 

722 show_warning = warnings.showwarning 

723 

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

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

726 if location is None: 

727 return 

728 

729 if location != (filename, lineno): 

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

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

732 line = None 

733 filename, lineno = location 

734 

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

736 

737 warnings.showwarning = _show 

738 try: 

739 yield 

740 finally: 

741 warnings.showwarning = show_warning 

742 

743 

744def _drop_expression_warnings(): 

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

746 

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

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

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

750 translated. 

751 

752 """ 

753 

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

755 if filename != pyparser.EXPRESSION_FILENAME: 

756 return filename, lineno 

757 

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

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

760 # translated would be suppressed 

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

762 return None 

763 

764 return _show_warnings_as(_locate) 

765 

766 

767def _translate_module_warnings(get_source, module_id, filename): 

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

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

770 line map. 

771 

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

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

774 is one whose line cannot be translated. 

775 

776 """ 

777 

778 line_map = None 

779 

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

781 nonlocal line_map 

782 

783 if warning_filename != module_id: 

784 return warning_filename, lineno 

785 

786 if line_map is None: 

787 try: 

788 line_map = ModuleInfo.get_module_source_metadata( 

789 get_source(), full_line_map=True 

790 )["full_line_map"] 

791 except Exception: 

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

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

794 # is worth more than the translation is 

795 line_map = [] 

796 

797 try: 

798 translated = line_map[lineno - 1] 

799 except IndexError: 

800 return warning_filename, lineno 

801 else: 

802 return filename, translated 

803 

804 return _show_warnings_as(_locate) 

805 

806 

807def _compile_text(template, text, filename): 

808 identifier = template.module_id 

809 

810 cid = identifier 

811 module = types.ModuleType(cid) 

812 

813 with _drop_expression_warnings(): 

814 source, lexer = _compile( 

815 template, text, filename, generate_magic_comment=False 

816 ) 

817 

818 loader = _ModuleSourceLoader(cid, source) 

819 module.__loader__ = loader 

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

821 

822 with _translate_module_warnings( 

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

824 ): 

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

826 

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

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

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

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

831 # the module as one step 

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

833 

834 return (source, module) 

835 

836 

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

838 source, lexer = _compile( 

839 template, text, filename, generate_magic_comment=True 

840 ) 

841 

842 if isinstance(source, str): 

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

844 

845 if module_writer: 

846 module_writer(source, outputpath) 

847 else: 

848 # make tempfiles in the same location as the ultimate 

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

850 # avoiding synchronization issues. 

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

852 

853 os.write(dest, source) 

854 os.close(dest) 

855 shutil.move(name, outputpath) 

856 

857 

858def _get_module_info_from_callable(callable_): 

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

860 

861 

862def _get_module_info(filename): 

863 return ModuleInfo._modules[filename]