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"""Configurations for TensorFlow Debugger (TFDBG) command-line interfaces.""" 16from __future__ import absolute_import 17from __future__ import division 18from __future__ import print_function 19 20import collections 21import json 22import os 23 24from tensorflow.python.debug.cli import debugger_cli_common 25from tensorflow.python.platform import gfile 26 27RL = debugger_cli_common.RichLine 28 29 30class CLIConfig(object): 31 """Client-facing configurations for TFDBG command-line interfaces.""" 32 33 _CONFIG_FILE_NAME = ".tfdbg_config" 34 35 _DEFAULT_CONFIG = [ 36 ("graph_recursion_depth", 20), 37 ("mouse_mode", True), 38 ] 39 40 def __init__(self, config_file_path=None): 41 self._config_file_path = (config_file_path or 42 self._default_config_file_path()) 43 self._config = collections.OrderedDict(self._DEFAULT_CONFIG) 44 if gfile.Exists(self._config_file_path): 45 config = self._load_from_file() 46 for key, value in config.items(): 47 self._config[key] = value 48 self._save_to_file() 49 50 self._set_callbacks = {} 51 52 def get(self, property_name): 53 if property_name not in self._config: 54 raise KeyError("%s is not a valid property name." % property_name) 55 return self._config[property_name] 56 57 def set(self, property_name, property_val): 58 """Set the value of a property. 59 60 Supports limitd property value types: `bool`, `int` and `str`. 61 62 Args: 63 property_name: Name of the property. 64 property_val: Value of the property. If the property has `bool` type and 65 this argument has `str` type, the `str` value will be parsed as a `bool` 66 67 Raises: 68 ValueError: if a `str` property_value fails to be parsed as a `bool`. 69 KeyError: if `property_name` is an invalid property name. 70 """ 71 if property_name not in self._config: 72 raise KeyError("%s is not a valid property name." % property_name) 73 74 orig_val = self._config[property_name] 75 if isinstance(orig_val, bool): 76 if isinstance(property_val, str): 77 if property_val.lower() in ("1", "true", "t", "yes", "y", "on"): 78 property_val = True 79 elif property_val.lower() in ("0", "false", "f", "no", "n", "off"): 80 property_val = False 81 else: 82 raise ValueError( 83 "Invalid string value for bool type: %s" % property_val) 84 else: 85 property_val = bool(property_val) 86 elif isinstance(orig_val, int): 87 property_val = int(property_val) 88 elif isinstance(orig_val, str): 89 property_val = str(property_val) 90 else: 91 raise TypeError("Unsupported property type: %s" % type(orig_val)) 92 self._config[property_name] = property_val 93 self._save_to_file() 94 95 # Invoke set-callback. 96 if property_name in self._set_callbacks: 97 self._set_callbacks[property_name](self._config) 98 99 def set_callback(self, property_name, callback): 100 """Set a set-callback for given property. 101 102 Args: 103 property_name: Name of the property. 104 callback: The callback as a `callable` of signature: 105 def cbk(config): 106 where config is the config after it is set to the new value. 107 The callback is invoked each time the set() method is called with the 108 matching property_name. 109 110 Raises: 111 KeyError: If property_name does not exist. 112 TypeError: If `callback` is not callable. 113 """ 114 if property_name not in self._config: 115 raise KeyError("%s is not a valid property name." % property_name) 116 if not callable(callback): 117 raise TypeError("The callback object provided is not callable.") 118 self._set_callbacks[property_name] = callback 119 120 def _default_config_file_path(self): 121 return os.path.join(os.path.expanduser("~"), self._CONFIG_FILE_NAME) 122 123 def _save_to_file(self): 124 try: 125 with gfile.Open(self._config_file_path, "w") as config_file: 126 json.dump(self._config, config_file) 127 except IOError: 128 pass 129 130 def summarize(self, highlight=None): 131 """Get a text summary of the config. 132 133 Args: 134 highlight: A property name to highlight in the output. 135 136 Returns: 137 A `RichTextLines` output. 138 """ 139 lines = [RL("Command-line configuration:", "bold"), RL("")] 140 for name, val in self._config.items(): 141 highlight_attr = "bold" if name == highlight else None 142 line = RL(" ") 143 line += RL(name, ["underline", highlight_attr]) 144 line += RL(": ") 145 line += RL(str(val), font_attr=highlight_attr) 146 lines.append(line) 147 return debugger_cli_common.rich_text_lines_from_rich_line_list(lines) 148 149 def _load_from_file(self): 150 try: 151 with gfile.Open(self._config_file_path, "r") as config_file: 152 config_dict = json.load(config_file) 153 config = collections.OrderedDict() 154 for key in sorted(config_dict.keys()): 155 config[key] = config_dict[key] 156 return config 157 except (IOError, ValueError): 158 # The reading of the config file may fail due to IO issues or file 159 # corruption. We do not want tfdbg to error out just because of that. 160 return dict() 161