1# Copyright 2017 The TensorFlow Authors. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# ============================================================================== 15"""Converting AST to code and Python entities. 16 17Adapted from Tangent. 18""" 19 20import atexit 21import errno 22import importlib 23import os 24import sys 25import tempfile 26 27from tensorflow.python.autograph.pyct import origin_info 28from tensorflow.python.autograph.pyct import parser 29 30 31def _remove_file(file_name): 32 """Remove a file, if it exists.""" 33 try: 34 os.remove(file_name) 35 except OSError as e: 36 if e.errno == errno.ENOENT: 37 # The file disappeared. Ignore this. Temporary files might get 38 # cleaned up, especially if they reside in /tmp. 39 pass 40 else: 41 raise 42 43 44def load_source(source, delete_on_exit): 45 """Loads the given source code as a Python module.""" 46 with tempfile.NamedTemporaryFile( 47 mode='w', 48 suffix='.py', 49 prefix='__autograph_generated_file', 50 delete=False, 51 encoding='utf-8') as f: 52 module_name = os.path.basename(f.name[:-3]) 53 file_name = f.name 54 f.write(source) 55 56 if delete_on_exit: 57 atexit.register(lambda: _remove_file(file_name)) 58 59 spec = importlib.util.spec_from_file_location(module_name, file_name) 60 module = importlib.util.module_from_spec(spec) 61 spec.loader.exec_module(module) 62 # TODO(mdan): Use our own garbage-collected cache instead of sys.modules. 63 sys.modules[module_name] = module 64 return module, file_name 65 66 67def load_ast(nodes, 68 indentation=' ', 69 include_source_map=False, 70 delete_on_exit=True): 71 """Loads the given AST as a Python module. 72 73 Compiling the AST code this way ensures that the source code is readable by 74 e.g. `pdb` or `inspect`. 75 76 Args: 77 nodes: Union[ast.AST, Iterable[ast.AST]], the code to compile, as an AST 78 object. 79 indentation: Text, the string to use for indentation. 80 include_source_map: bool, whether return a source map. 81 delete_on_exit: bool, whether to delete the temporary file used for 82 compilation on exit. 83 84 Returns: 85 Tuple[module, Text, Dict[LineLocation, OriginInfo]], containing: 86 the module containing the unparsed nodes, the source code corresponding to 87 nodes, and the source map. Is include_source_map is False, the source map 88 will be None. 89 """ 90 if not isinstance(nodes, (list, tuple)): 91 nodes = (nodes,) 92 93 source = parser.unparse(nodes, indentation=indentation) 94 module, _ = load_source(source, delete_on_exit) 95 96 if include_source_map: 97 source_map = origin_info.create_source_map(nodes, source, module.__file__) 98 else: 99 source_map = None 100 101 # TODO(mdan): Return a structured object. 102 return module, source, source_map 103