LSSTApplications  17.0+11,17.0+34,17.0+56,17.0+57,17.0+59,17.0+7,17.0-1-g377950a+33,17.0.1-1-g114240f+2,17.0.1-1-g4d4fbc4+28,17.0.1-1-g55520dc+49,17.0.1-1-g5f4ed7e+52,17.0.1-1-g6dd7d69+17,17.0.1-1-g8de6c91+11,17.0.1-1-gb9095d2+7,17.0.1-1-ge9fec5e+5,17.0.1-1-gf4e0155+55,17.0.1-1-gfc65f5f+50,17.0.1-1-gfc6fb1f+20,17.0.1-10-g87f9f3f+1,17.0.1-11-ge9de802+16,17.0.1-16-ga14f7d5c+4,17.0.1-17-gc79d625+1,17.0.1-17-gdae4c4a+8,17.0.1-2-g26618f5+29,17.0.1-2-g54f2ebc+9,17.0.1-2-gf403422+1,17.0.1-20-g2ca2f74+6,17.0.1-23-gf3eadeb7+1,17.0.1-3-g7e86b59+39,17.0.1-3-gb5ca14a,17.0.1-3-gd08d533+40,17.0.1-30-g596af8797,17.0.1-4-g59d126d+4,17.0.1-4-gc69c472+5,17.0.1-6-g5afd9b9+4,17.0.1-7-g35889ee+1,17.0.1-7-gc7c8782+18,17.0.1-9-gc4bbfb2+3,w.2019.22
LSSTDataManagementBasePackage
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 
23 __all__ = ('Color', 'format')
24 
25 import os
26 import re
27 import sys
28 
29 
30 class Color:
31  """A controller that determines whether strings should be colored.
32 
33  Parameters
34  ----------
35  text : `str`
36  Text content to print to a terminal.
37  category : `str`
38  Semantic category of the ``text``. See `categories` for possible values.
39 
40  Raises
41  ------
42  RuntimeError
43  Raised when the ``category`` is not a key of ``Color.categories``.
44 
45  Notes
46  -----
47  The usual usage is ``Color(string, category)`` which returns a string that
48  may be printed; categories are given by the keys of `Color.categories`.
49 
50  `Color.colorize` may be used to set or retrieve whether the user wants
51  color. It always returns `False` when `sys.stdout` is not attached to a
52  terminal.
53  """
54 
55  categories = dict(
56  NAME="blue",
57  VALUE="yellow",
58  FILE="green",
59  TEXT="red",
60  FUNCTION_NAME="blue",
61  )
62  """Mapping of semantic labels to color names (`dict`).
63 
64  Notes
65  -----
66  The default categories are:
67 
68  - ``'NAME'``
69  - ``'VALUE'``
70  - ``'FILE'``
71  - ``'TEXT'``
72  - ``'FUNCTION_NAME'``
73  """
74 
75  colors = {
76  "black": 0,
77  "red": 1,
78  "green": 2,
79  "yellow": 3,
80  "blue": 4,
81  "magenta": 5,
82  "cyan": 6,
83  "white": 7,
84  }
85  """Mapping of color names to terminal color codes (`dict`).
86  """
87 
88  _colorize = True
89 
90  def __init__(self, text, category):
91  try:
92  color = Color.categories[category]
93  except KeyError:
94  raise RuntimeError("Unknown category: %s" % category)
95 
96  self.rawText = str(text)
97  x = color.lower().split(";")
98  self.color, bold = x.pop(0), False
99  if x:
100  props = x.pop(0)
101  if props in ("bold",):
102  bold = True
103 
104  try:
105  self._code = "%s" % (30 + Color.colors[self.color])
106  except KeyError:
107  raise RuntimeError("Unknown colour: %s" % self.color)
108 
109  if bold:
110  self._code += ";1"
111 
112  @staticmethod
113  def colorize(val=None):
114  """Get or set whether the string should be colorized.
115 
116  Parameters
117  ----------
118  val : `bool` or `dict`, optional
119  The value is usually a bool, but it may be a dict which is used
120  to modify Color.categories
121 
122  Returns
123  -------
124  shouldColorize : `bool`
125  If `True`, the string should be colorized. A string **will not** be
126  colorized if standard output or standard error are not attached to
127  a terminal or if the ``val`` argument was `False`.
128 
129  Only strings written to a terminal are colorized.
130  """
131 
132  if val is not None:
133  Color._colorize = val
134 
135  if isinstance(val, dict):
136  unknown = []
137  for k in val:
138  if k in Color.categories:
139  if val[k] in Color.colors:
140  Color.categories[k] = val[k]
141  else:
142  print("Unknown colour %s for category %s" % (val[k], k), file=sys.stderr)
143  else:
144  unknown.append(k)
145 
146  if unknown:
147  print("Unknown colourizing category: %s" % " ".join(unknown), file=sys.stderr)
148 
149  return Color._colorize if sys.stdout.isatty() else False
150 
151  def __str__(self):
152  if not self.colorize():
153  return self.rawText
154 
155  base = "\033["
156 
157  prefix = base + self._code + "m"
158  suffix = base + "m"
159 
160  return prefix + self.rawText + suffix
161 
162 
163 def _colorize(text, category):
164  text = Color(text, category)
165  return str(text)
166 
167 
168 def format(config, name=None, writeSourceLine=True, prefix="", verbose=False):
169  """Format the history record for a configuration, or a specific
170  configuration field.
171 
172  Parameters
173  ----------
174  config : `lsst.pex.config.Config`
175  A configuration instance.
176  name : `str`, optional
177  The name of a configuration field to specifically format the history
178  for. Otherwise the history of all configuration fields is printed.
179  writeSourceLine : `bool`, optional
180  If `True`, prefix each printout line with the code filename and line
181  number where the configuration event occurred. Default is `True`.
182  prefix : `str`, optional
183  A prefix for to add to each printout line. This prefix occurs first,
184  even before any source line. The default is an empty string.
185  verbose : `bool`, optional
186  Default is `False`.
187  """
188 
189  if name is None:
190  for i, name in enumerate(config.history.keys()):
191  if i > 0:
192  print()
193  print(format(config, name))
194 
195  outputs = []
196  for value, stack, label in config.history[name]:
197  output = []
198  for frame in stack:
199  if frame.function in ("__new__", "__set__", "__setattr__", "execfile", "wrapper") or \
200  os.path.split(frame.filename)[1] in ("argparse.py", "argumentParser.py"):
201  if not verbose:
202  continue
203 
204  line = []
205  if writeSourceLine:
206  line.append(["%s" % ("%s:%d" % (frame.filename, frame.lineno)), "FILE", ])
207 
208  line.append([frame.content, "TEXT", ])
209  if False:
210  line.append([frame.function, "FUNCTION_NAME", ])
211 
212  output.append(line)
213 
214  outputs.append([value, output])
215 
216  # Find the maximum widths of the value and file:lineNo fields.
217  if writeSourceLine:
218  sourceLengths = []
219  for value, output in outputs:
220  sourceLengths.append(max([len(x[0][0]) for x in output]))
221  sourceLength = max(sourceLengths)
222 
223  valueLength = len(prefix) + max([len(str(value)) for value, output in outputs])
224 
225  # Generate the config history content.
226  msg = []
227  fullname = "%s.%s" % (config._name, name) if config._name is not None else name
228  msg.append(_colorize(re.sub(r"^root\.", "", fullname), "NAME"))
229  for value, output in outputs:
230  line = prefix + _colorize("%-*s" % (valueLength, value), "VALUE") + " "
231  for i, vt in enumerate(output):
232  if writeSourceLine:
233  vt[0][0] = "%-*s" % (sourceLength, vt[0][0])
234 
235  output[i] = " ".join([_colorize(v, t) for v, t in vt])
236 
237  line += ("\n%*s" % (valueLength + 1, "")).join(output)
238  msg.append(line)
239 
240  return "\n".join(msg)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:168
int max
def __init__(self, text, category)
Definition: history.py:90