1# Copyright 2019 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"""Utilities used to capture Python idioms.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import print_function 20 21 22def ld(v): 23 """Load variable operator.""" 24 if isinstance(v, Undefined): 25 return v.read() 26 return v 27 28 29def ldu(load_v, name): 30 """Load variable operator that returns Undefined when failing to evaluate. 31 32 Note: the name ("load or return undefined") is abbreviated to minimize 33 the amount of clutter in generated code. 34 35 This variant of `ld` is useful when loading symbols that may be undefined at 36 runtime, such as composite symbols, and whether they are defined or not cannot 37 be determined statically. For example `d['a']` is undefined when `d` is an 38 empty dict. 39 40 Args: 41 load_v: Lambda that executes the actual read. 42 name: Human-readable name of the symbol being read. 43 Returns: 44 Either the value of the symbol, or Undefined, if the symbol is not fully 45 defined. 46 """ 47 try: 48 # TODO(mdan): Use locals()/globals() here. 49 return load_v() 50 except (KeyError, AttributeError, NameError): 51 return Undefined(name) 52 53 54class Undefined(object): 55 """Represents an undefined symbol in Python. 56 57 This is used to reify undefined symbols, which is required to use the 58 functional form of loops. 59 Example: 60 61 while n > 0: 62 n = n - 1 63 s = n 64 return s # Runtime error if n == 0 65 66 This is valid Python code and will not result in an error as long as n 67 is positive. The use of this class is to stay as close to Python semantics 68 as possible for staged code of this nature. 69 70 Converted version of the above showing the possible usage of this class: 71 72 s = Undefined('s') 73 init_state = (s,) 74 s = while_loop(cond, body, init_state) 75 return s # s is an instance of Undefined if the loop never runs 76 77 Attributes: 78 symbol_name: Text, identifier for the undefined symbol 79 """ 80 81 __slots__ = ('symbol_name',) 82 83 def __init__(self, symbol_name): 84 self.symbol_name = symbol_name 85 86 def read(self): 87 raise UnboundLocalError("'{}' is used before assignment".format( 88 self.symbol_name)) 89 90 def __repr__(self): 91 return self.symbol_name 92 93 def __getattribute__(self, name): 94 try: 95 # If it's an existing attribute, return it. 96 return object.__getattribute__(self, name) 97 except AttributeError: 98 # Otherwise return Undefined. 99 return self 100 101 def __getitem__(self, i): 102 return self 103 104 105# TODO(mdan): Refactor as a RetVal object, aggregating the value and do_return. 106class UndefinedReturnValue(object): 107 """Represents a return value that is undefined.""" 108 pass 109