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
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
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
7"""Provides the Template class, a facade for parsing, generating and executing
8template strings, as well as template runtime operations."""
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
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
34class Template:
35 r"""Represents a compiled template.
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.
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.
47 :param text: textual template source. This argument is mutually
48 exclusive versus the ``filename`` parameter.
50 :param filename: filename of the source template. This argument is
51 mutually exclusive versus the ``text`` parameter.
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.
61 :param cache_args: Dictionary of cache configuration arguments that
62 will be passed to the :class:`.CacheImpl`. See :ref:`caching_toplevel`.
64 :param cache_dir:
66 .. deprecated:: 0.6
67 Use the ``'dir'`` argument in the ``cache_args`` dictionary.
68 See :ref:`caching_toplevel`.
70 :param cache_enabled: Boolean flag which enables caching of this
71 template. See :ref:`caching_toplevel`.
73 :param cache_impl: String name of a :class:`.CacheImpl` caching
74 implementation to use. Defaults to ``'beaker'``.
76 :param cache_type:
78 .. deprecated:: 0.6
79 Use the ``'type'`` argument in the ``cache_args`` dictionary.
80 See :ref:`caching_toplevel`.
82 :param cache_url:
84 .. deprecated:: 0.6
85 Use the ``'url'`` argument in the ``cache_args`` dictionary.
86 See :ref:`caching_toplevel`.
88 :param default_filters: List of string filter names that will
89 be applied to all expressions. See :ref:`filtering_default_filters`.
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`.
98 :param encoding_errors: Error parameter passed to ``encode()`` when
99 string encoding is performed. See :ref:`usage_unicode`.
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.
109 .. seealso::
111 :paramref:`.Template.include_error_handler` - include-specific
112 error handler function
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.
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`.
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``.
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.
134 .. versionadded:: 1.0.6
136 .. seealso::
138 :paramref:`.Template.error_handler` - top-level error handler function
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.
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`.
150 :param module_directory: Filesystem location where generated
151 Python module files will be placed.
153 :param module_filename: Overrides the filename of the generated
154 Python module file. For advanced usage only.
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:
164 .. sourcecode:: python
166 import tempfile
167 import os
168 import shutil
170 def module_writer(source, outputpath):
171 (dest, name) = \\
172 tempfile.mkstemp(
173 dir=os.path.dirname(outputpath)
174 )
176 os.write(dest, source)
177 os.close(dest)
178 shutil.move(name, outputpath)
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 )
187 The function is provided for unusual configurations where
188 certain platform-specific permissions or other special
189 steps are needed.
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`.
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.
200 :param lexer_cls: A :class:`.Lexer` class used to parse
201 the template. The :class:`.Lexer` class is used by
202 default.
204 .. versionadded:: 0.7.4
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.
212 .. versionadded:: 0.3.6
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.
222 """
224 lexer_cls = Lexer
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
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 )
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
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
295 self.imports = imports
296 self.future_imports = future_imports
297 self.preprocessor = preprocessor
299 if lexer_cls is not None:
300 self.lexer_cls = lexer_cls
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 )
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
335 self.module_directory = module_directory
337 self._setup_cache_args(
338 cache_impl,
339 cache_enabled,
340 cache_args,
341 cache_type,
342 cache_dir,
343 cache_url,
344 )
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"])
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
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)
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
409 @property
410 def source(self):
411 """Return the template source code for this :class:`.Template`."""
413 return _get_module_info_from_callable(self.callable_).source
415 @property
416 def code(self):
417 """Return the module source code for this :class:`.Template`."""
419 return _get_module_info_from_callable(self.callable_).code
421 @util.memoized_property
422 def cache(self):
423 return cache.Cache(self)
425 @property
426 def cache_dir(self):
427 return self.cache_args["dir"]
429 @property
430 def cache_url(self):
431 return self.cache_args["url"]
433 @property
434 def cache_type(self):
435 return self.cache_args["type"]
437 def render(self, *args, **data):
438 """Render the output of this template as a string.
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.
448 """
449 return runtime._render(self, self.callable_, args, data)
451 def render_unicode(self, *args, **data):
452 """Render the output of this template as a unicode object."""
454 return runtime._render(
455 self, self.callable_, args, data, as_unicode=True
456 )
458 def render_context(self, context, *args, **kwargs):
459 """Render this :class:`.Template` with the given context.
461 The data is written to the context's buffer.
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)
468 def has_def(self, name):
469 return hasattr(self.module, "render_%s" % name)
471 def get_def(self, name):
472 """Return a def of this template as a :class:`.DefTemplate`."""
474 return DefTemplate(self, getattr(self.module, "render_%s" % name))
476 def list_defs(self):
477 """return a list of defs in the template.
479 .. versionadded:: 1.0.4
481 """
482 return [i[7:] for i in dir(self.module) if i[:7] == "render_"]
484 def _get_def_callable(self, name):
485 return getattr(self.module, "render_%s" % name)
487 @property
488 def last_modified(self):
489 return self.module._modified_time
492class ModuleTemplate(Template):
493 """A Template which is constructed given an existing Python module.
495 e.g.::
497 t = Template("this is a template")
498 f = file("mymodule.py", "w")
499 f.write(t.code)
500 f.close()
502 import mymodule
504 t = ModuleTemplate(mymodule)
505 print(t.render())
507 """
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
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 )
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 )
564class DefTemplate(Template):
565 """A :class:`.Template` which represents a callable def in a parent
566 template."""
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
580 def get_def(self, name):
581 return self.parent.get_def(name)
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.
589 """
591 _modules = weakref.WeakValueDictionary()
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
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"]
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
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)
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
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
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
682class _ModuleSourceLoader(abc.Loader):
683 """Provide the generated source of an in-memory template module.
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.
693 """
695 def __init__(self, name, source):
696 self.name = name
697 self._source = source
699 def get_source(self, name):
700 return self._source
702 def is_package(self, name):
703 return False
706@contextlib.contextmanager
707def _show_warnings_as(locate):
708 """Replace the warnings display hook for the duration of the block.
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.
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.
720 """
722 show_warning = warnings.showwarning
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
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
735 show_warning(message, category, filename, lineno, file, line)
737 warnings.showwarning = _show
738 try:
739 yield
740 finally:
741 warnings.showwarning = show_warning
744def _drop_expression_warnings():
745 """Drop warnings raised while individual expressions are parsed.
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.
752 """
754 def _locate(message, category, filename, lineno):
755 if filename != pyparser.EXPRESSION_FILENAME:
756 return filename, lineno
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
764 return _show_warnings_as(_locate)
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.
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.
776 """
778 line_map = None
780 def _locate(message, category, warning_filename, lineno):
781 nonlocal line_map
783 if warning_filename != module_id:
784 return warning_filename, lineno
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 = []
797 try:
798 translated = line_map[lineno - 1]
799 except IndexError:
800 return warning_filename, lineno
801 else:
802 return filename, translated
804 return _show_warnings_as(_locate)
807def _compile_text(template, text, filename):
808 identifier = template.module_id
810 cid = identifier
811 module = types.ModuleType(cid)
813 with _drop_expression_warnings():
814 source, lexer = _compile(
815 template, text, filename, generate_magic_comment=False
816 )
818 loader = _ModuleSourceLoader(cid, source)
819 module.__loader__ = loader
820 module.__spec__ = machinery.ModuleSpec(cid, loader, origin=cid)
822 with _translate_module_warnings(
823 lambda: source, cid, filename or template.uri
824 ):
825 code = compile(source, cid, "exec")
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__)
834 return (source, module)
837def _compile_module_file(template, text, filename, outputpath, module_writer):
838 source, lexer = _compile(
839 template, text, filename, generate_magic_comment=True
840 )
842 if isinstance(source, str):
843 source = source.encode(lexer.encoding or "ascii")
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))
853 os.write(dest, source)
854 os.close(dest)
855 shutil.move(name, outputpath)
858def _get_module_info_from_callable(callable_):
859 return _get_module_info(callable_.__globals__["__name__"])
862def _get_module_info(filename):
863 return ModuleInfo._modules[filename]