code: def smart_autograph(f: Callable[..., T]) -> Callable[..., T]:
      """Wraps `f` such that in graph mode it uses autograph but not in eager.
  
      Whilst wrapping `f` in autograph is (intended to be) semantics preserving,
      some things (e.g. breakpoints) are not preserved. Using `smart_autograph`
      users can write code with eager syntax, add breakpoints and debug it as you
      might expect and still be compatible with code that uses
      `@tf.function(autograph=False)`.
  
          >>> @smart_autograph
          ... def f(x):
          ...   if x > 0:
          ...     y = x * x
          ...   else:
          ...     y = -x
          ...   return y
  
          >>> f = tf.function(f, autograph=False)
          >>> f(tf.constant(2))
          <tf.Tensor: ... numpy=4>
  
      Args:
        f: A function to wrap conditionally in `tf.autograph`.
  
      Returns:
        A wrapper for `f` that dispatches to the original or autograph version of f.
      """
      cache = []
  
      @_synchronized
      def f_autograph():
          if not cache:
              cache.append(tf.autograph.to_graph(f))
          return cache[0]
  
      @functools.wraps(f)
      def smart_autograph_wrapper(*args, **kwargs):
          if tf.executing_eagerly():
              return f(*args, **kwargs)
          else:
              return f_autograph()(*args, **kwargs)
  
      return smart_autograph_wrapper
  
imports: import collections.abc
  import functools
  import inspect
  import re
  import threading
  from typing import Any, Callable, Dict, Generic, Optional, Sequence, Tuple, TypeVar, Union
  from absl import logging
  from sonnet.src import initializers
  import tabulate
  import tensorflow
label_desc: Use image node_id 8 for calling a global function with example usage: smart_autograph(f) and returns: smart_autograph_wrapper, cache, f, f_autograph