• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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