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