LSSTApplications  19.0.0-14-gb0260a2+64befe7c1c,20.0.0+0b340dd7c4,20.0.0+1540ce6389,20.0.0+7c6b12c2f9,20.0.0+ae956f52c5,20.0.0+be870186d9,20.0.0+e2e26847c2,20.0.0-1-g04bee24+7c6b12c2f9,20.0.0-1-g10df615+fbe45b1aff,20.0.0-1-g253301a+7c6b12c2f9,20.0.0-1-g2b7511a+46a6078777,20.0.0-1-g4d801e7+9d50d6c21a,20.0.0-1-g5b95a8c+8ebfc75efb,20.0.0-1-gb058bd0+46a6078777,20.0.0-1-gb88604f+acecce4127,20.0.0-1-gc96f8cb+016e330d09,20.0.0-1-gedffbd8+83814edeba,20.0.0-10-g0891cd99+a5f14e9acb,20.0.0-10-g9a20bd332+576ca7b471,20.0.0-17-gcdbda88+0e26bfb1e6,20.0.0-2-g4dae9ad+016e330d09,20.0.0-2-g61b8584+85c46248f3,20.0.0-2-gb780d76+f45b7d88f4,20.0.0-21-g9bbb7f7+016e330d09,20.0.0-22-gc512666+f30f3883a7,20.0.0-23-g8900aa8+68630f7098,20.0.0-3-g1653f94+85c46248f3,20.0.0-3-g4cc78c6+63636aeed8,20.0.0-3-g750bffe+e05f822de9,20.0.0-3-gbd60e8c+ff10c6d78d,20.0.0-32-g15a0e07c+a4c9d0309b,20.0.0-4-g97dc21a+68630f7098,20.0.0-4-gfea843c+f45b7d88f4,20.0.0-5-g357b56b+f45b7d88f4,20.0.0-6-g9a5b7a1+b0f8c2be93,20.0.0-60-gad89340+e4dd172200,20.0.0-7-gcda7bf1+673816f70c,w.2020.43
LSSTDataManagementBasePackage
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.rawText = 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 = "%s" % (30 + Color.colors[self.color])
112  except KeyError:
113  raise RuntimeError("Unknown colour: %s" % self.color)
114 
115  if bold:
116  self._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.colorize():
159  return self.rawText
160 
161  base = "\033["
162 
163  prefix = base + self._code + "m"
164  suffix = base + "m"
165 
166  return prefix + self.rawText + 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[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  # Find the maximum widths of the value and file:lineNo fields.
223  if writeSourceLine:
224  sourceLengths = []
225  for value, output in outputs:
226  sourceLengths.append(max([len(x[0][0]) for x in output]))
227  sourceLength = max(sourceLengths)
228 
229  valueLength = len(prefix) + max([len(str(value)) for value, output in outputs])
230 
231  # Generate the config history content.
232  msg = []
233  fullname = "%s.%s" % (config._name, name) if config._name is not None else name
234  msg.append(_colorize(re.sub(r"^root\.", "", fullname), "NAME"))
235  for value, output in outputs:
236  line = prefix + _colorize("%-*s" % (valueLength, value), "VALUE") + " "
237  for i, vt in enumerate(output):
238  if writeSourceLine:
239  vt[0][0] = "%-*s" % (sourceLength, vt[0][0])
240 
241  output[i] = " ".join([_colorize(v, t) for v, t in vt])
242 
243  line += ("\n%*s" % (valueLength + 1, "")).join(output)
244  msg.append(line)
245 
246  return "\n".join(msg)
lsst.pex.config.history.Color._code
_code
Definition: history.py:111
lsst.pex.config.history.Color.__str__
def __str__(self)
Definition: history.py:157
lsst.pex.config.history.Color.rawText
rawText
Definition: history.py:102
lsst.pex.config.history.format
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174
lsst.pex.config.history.Color.colorize
def colorize(val=None)
Definition: history.py:119
lsst.pex.config.history.Color
Definition: history.py:35
max
int max
Definition: BoundedField.cc:104
lsst.pex.config.history.Color.__init__
def __init__(self, text, category)
Definition: history.py:96