LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
history.py
Go to the documentation of this file.
1 # This file is part of pex_config.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (http://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This software is dual licensed under the GNU General Public License and also
10 # under a 3-clause BSD license. Recipients may choose which of these licenses
11 # to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12 # respectively. If you choose the GPL option then the following text applies
13 # (but note that there is still no warranty even if you opt for BSD instead):
14 #
15 # This program is free software: you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License as published by
17 # the Free Software Foundation, either version 3 of the License, or
18 # (at your option) any later version.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 
28 __all__ = ('Color', 'format')
29 
30 import os
31 import re
32 import sys
33 
34 
35 class Color:
36  """A controller that determines whether strings should be colored.
37 
38  Parameters
39  ----------
40  text : `str`
41  Text content to print to a terminal.
42  category : `str`
43  Semantic category of the ``text``. See `categories` for possible
44  values.
45 
46  Raises
47  ------
48  RuntimeError
49  Raised when the ``category`` is not a key of ``Color.categories``.
50 
51  Notes
52  -----
53  The usual usage is ``Color(string, category)`` which returns a string that
54  may be printed; categories are given by the keys of `Color.categories`.
55 
56  `Color.colorize` may be used to set or retrieve whether the user wants
57  color. It always returns `False` when `sys.stdout` is not attached to a
58  terminal.
59  """
60 
61  categories = dict(
62  NAME="blue",
63  VALUE="yellow",
64  FILE="green",
65  TEXT="red",
66  FUNCTION_NAME="blue",
67  )
68  """Mapping of semantic labels to color names (`dict`).
69 
70  Notes
71  -----
72  The default categories are:
73 
74  - ``'NAME'``
75  - ``'VALUE'``
76  - ``'FILE'``
77  - ``'TEXT'``
78  - ``'FUNCTION_NAME'``
79  """
80 
81  colors = {
82  "black": 0,
83  "red": 1,
84  "green": 2,
85  "yellow": 3,
86  "blue": 4,
87  "magenta": 5,
88  "cyan": 6,
89  "white": 7,
90  }
91  """Mapping of color names to terminal color codes (`dict`).
92  """
93 
94  _colorize = True
95 
96  def __init__(self, text, category):
97  try:
98  color = Color.categories[category]
99  except KeyError:
100  raise RuntimeError("Unknown category: %s" % category)
101 
102  self.rawTextrawText = str(text)
103  x = color.lower().split(";")
104  self.color, bold = x.pop(0), False
105  if x:
106  props = x.pop(0)
107  if props in ("bold",):
108  bold = True
109 
110  try:
111  self._code_code = "%s" % (30 + Color.colors[self.color])
112  except KeyError:
113  raise RuntimeError("Unknown colour: %s" % self.color)
114 
115  if bold:
116  self._code_code += ";1"
117 
118  @staticmethod
119  def colorize(val=None):
120  """Get or set whether the string should be colorized.
121 
122  Parameters
123  ----------
124  val : `bool` or `dict`, optional
125  The value is usually a bool, but it may be a dict which is used
126  to modify Color.categories
127 
128  Returns
129  -------
130  shouldColorize : `bool`
131  If `True`, the string should be colorized. A string **will not** be
132  colorized if standard output or standard error are not attached to
133  a terminal or if the ``val`` argument was `False`.
134 
135  Only strings written to a terminal are colorized.
136  """
137 
138  if val is not None:
139  Color._colorize = val
140 
141  if isinstance(val, dict):
142  unknown = []
143  for k in val:
144  if k in Color.categories:
145  if val[k] in Color.colors:
146  Color.categories[k] = val[k]
147  else:
148  print("Unknown colour %s for category %s" % (val[k], k), file=sys.stderr)
149  else:
150  unknown.append(k)
151 
152  if unknown:
153  print("Unknown colourizing category: %s" % " ".join(unknown), file=sys.stderr)
154 
155  return Color._colorize if sys.stdout.isatty() else False
156 
157  def __str__(self):
158  if not self.colorizecolorize():
159  return self.rawTextrawText
160 
161  base = "\033["
162 
163  prefix = base + self._code_code + "m"
164  suffix = base + "m"
165 
166  return prefix + self.rawTextrawText + suffix
167 
168 
169 def _colorize(text, category):
170  text = Color(text, category)
171  return str(text)
172 
173 
174 def format(config, name=None, writeSourceLine=True, prefix="", verbose=False):
175  """Format the history record for a configuration, or a specific
176  configuration field.
177 
178  Parameters
179  ----------
180  config : `lsst.pex.config.Config`
181  A configuration instance.
182  name : `str`, optional
183  The name of a configuration field to specifically format the history
184  for. Otherwise the history of all configuration fields is printed.
185  writeSourceLine : `bool`, optional
186  If `True`, prefix each printout line with the code filename and line
187  number where the configuration event occurred. Default is `True`.
188  prefix : `str`, optional
189  A prefix for to add to each printout line. This prefix occurs first,
190  even before any source line. The default is an empty string.
191  verbose : `bool`, optional
192  Default is `False`.
193  """
194 
195  if name is None:
196  for i, name in enumerate(config.history.keys()):
197  if i > 0:
198  print()
199  print(format(config, name))
200 
201  outputs = []
202  for value, stack, label in config.history.get(name, []):
203  output = []
204  for frame in stack:
205  if frame.function in ("__new__", "__set__", "__setattr__", "execfile", "wrapper") or \
206  os.path.split(frame.filename)[1] in ("argparse.py", "argumentParser.py"):
207  if not verbose:
208  continue
209 
210  line = []
211  if writeSourceLine:
212  line.append(["%s" % ("%s:%d" % (frame.filename, frame.lineno)), "FILE", ])
213 
214  line.append([frame.content, "TEXT", ])
215  if False:
216  line.append([frame.function, "FUNCTION_NAME", ])
217 
218  output.append(line)
219 
220  outputs.append([value, output])
221 
222  if outputs:
223  # Find the maximum widths of the value and file:lineNo fields.
224  if writeSourceLine:
225  sourceLengths = []
226  for value, output in outputs:
227  sourceLengths.append(max([len(x[0][0]) for x in output]))
228  sourceLength = max(sourceLengths)
229 
230  valueLength = len(prefix) + max([len(str(value)) for value, output in outputs])
231 
232  # Generate the config history content.
233  msg = []
234  fullname = "%s.%s" % (config._name, name) if config._name is not None else name
235  msg.append(_colorize(re.sub(r"^root\.", "", fullname), "NAME"))
236  for value, output in outputs:
237  line = prefix + _colorize("%-*s" % (valueLength, value), "VALUE") + " "
238  for i, vt in enumerate(output):
239  if writeSourceLine:
240  vt[0][0] = "%-*s" % (sourceLength, vt[0][0])
241 
242  output[i] = " ".join([_colorize(v, t) for v, t in vt])
243 
244  line += ("\n%*s" % (valueLength + 1, "")).join(output)
245  msg.append(line)
246 
247  return "\n".join(msg)
int max
def __init__(self, text, category)
Definition: history.py:96
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174