LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
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