lsst.pex.config  15.0-1-gae1598d+17
history.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 from __future__ import print_function
23 from builtins import str
24 from builtins import object
25 
26 import os
27 import re
28 import sys
29 
30 # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
31 
32 
33 class Color(object):
34  """Control whether strings should be coloured
35 
36  The usual usage is `Color(string, category)` which returns a string that
37  may be printed; categories are given by the keys of Color.categories
38 
39  Color.colorize() may be used to set or retrieve whether the user wants
40  colour; it always returns False when sys.stdout is not attached to a
41  terminal.
42  """
43 
44  categories = dict(
45  NAME="blue",
46  VALUE="yellow",
47  FILE="green",
48  TEXT="red",
49  FUNCTION_NAME="blue",
50  )
51 
52  colors = {
53  "black": 0,
54  "red": 1,
55  "green": 2,
56  "yellow": 3,
57  "blue": 4,
58  "magenta": 5,
59  "cyan": 6,
60  "white": 7,
61  }
62 
63  _colorize = True
64 
65  def __init__(self, text, category):
66  """Return a string that should display as coloured on a conformant terminal"""
67  try:
68  color = Color.categories[category]
69  except KeyError:
70  raise RuntimeError("Unknown category: %s" % category)
71 
72  self.rawText = str(text)
73  x = color.lower().split(";")
74  self.color, bold = x.pop(0), False
75  if x:
76  props = x.pop(0)
77  if props in ("bold",):
78  bold = True
79 
80  try:
81  self._code = "%s" % (30 + Color.colors[self.color])
82  except KeyError:
83  raise RuntimeError("Unknown colour: %s" % self.color)
84 
85  if bold:
86  self._code += ";1"
87 
88  @staticmethod
89  def colorize(val=None):
90  """Should I colour strings? With an argument, set the value
91 
92  The value is usually a bool, but it may be a dict which is used
93  to modify Color.categories
94 
95  N.b. only strings written to a terminal are colourized
96  """
97 
98  if val is not None:
99  Color._colorize = val
100 
101  if isinstance(val, dict):
102  unknown = []
103  for k in val:
104  if k in Color.categories:
105  if val[k] in Color.colors:
106  Color.categories[k] = val[k]
107  else:
108  print("Unknown colour %s for category %s" % (val[k], k), file=sys.stderr)
109  else:
110  unknown.append(k)
111 
112  if unknown:
113  print("Unknown colourizing category: %s" % " ".join(unknown), file=sys.stderr)
114 
115  return Color._colorize if sys.stdout.isatty() else False
116 
117  def __str__(self):
118  if not self.colorize():
119  return self.rawText
120 
121  base = "\033["
122 
123  prefix = base + self._code + "m"
124  suffix = base + "m"
125 
126  return prefix + self.rawText + suffix
127 
128 
129 def _colorize(text, category):
130  text = Color(text, category)
131  return str(text)
132 
133 
134 def format(config, name=None, writeSourceLine=True, prefix="", verbose=False):
135  """Format the history record for config.name"""
136 
137  if name is None:
138  for i, name in enumerate(config.history.keys()):
139  if i > 0:
140  print()
141  print(format(config, name))
142 
143  outputs = []
144  for value, stack, label in config.history[name]:
145  output = []
146  for frame in stack:
147  if frame.function in ("__new__", "__set__", "__setattr__", "execfile", "wrapper") or \
148  os.path.split(frame.filename)[1] in ("argparse.py", "argumentParser.py"):
149  if not verbose:
150  continue
151 
152  line = []
153  if writeSourceLine:
154  line.append(["%s" % ("%s:%d" % (frame.filename, frame.lineno)), "FILE", ])
155 
156  line.append([frame.content, "TEXT", ])
157  if False:
158  line.append([frame.function, "FUNCTION_NAME", ])
159 
160  output.append(line)
161 
162  outputs.append([value, output])
163  #
164  # Find the maximum widths of the value and file:lineNo fields
165  #
166  if writeSourceLine:
167  sourceLengths = []
168  for value, output in outputs:
169  sourceLengths.append(max([len(x[0][0]) for x in output]))
170  sourceLength = max(sourceLengths)
171 
172  valueLength = len(prefix) + max([len(str(value)) for value, output in outputs])
173  #
174  # actually generate the config history
175  #
176  msg = []
177  fullname = "%s.%s" % (config._name, name) if config._name is not None else name
178  msg.append(_colorize(re.sub(r"^root\.", "", fullname), "NAME"))
179  for value, output in outputs:
180  line = prefix + _colorize("%-*s" % (valueLength, value), "VALUE") + " "
181  for i, vt in enumerate(output):
182  if writeSourceLine:
183  vt[0][0] = "%-*s" % (sourceLength, vt[0][0])
184 
185  output[i] = " ".join([_colorize(v, t) for v, t in vt])
186 
187  line += ("\n%*s" % (valueLength + 1, "")).join(output)
188  msg.append(line)
189 
190  return "\n".join(msg)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:134
def colorize(val=None)
Definition: history.py:89
def __init__(self, text, category)
Definition: history.py:65