LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
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
30import os
31import re
32import sys
33
34
35class 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
169def _colorize(text, category):
170 text = Color(text, category)
171 return str(text)
172
173
174def 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 (
206 "__new__",
207 "__set__",
208 "__setattr__",
209 "execfile",
210 "wrapper",
211 ) or os.path.split(frame.filename)[1] in ("argparse.py", "argumentParser.py"):
212 if not verbose:
213 continue
214
215 line = []
216 if writeSourceLine:
217 line.append(
218 [
219 "%s" % ("%s:%d" % (frame.filename, frame.lineno)),
220 "FILE",
221 ]
222 )
223
224 line.append(
225 [
226 frame.content,
227 "TEXT",
228 ]
229 )
230 if False:
231 line.append(
232 [
233 frame.function,
234 "FUNCTION_NAME",
235 ]
236 )
237
238 output.append(line)
239
240 outputs.append([value, output])
241
242 if outputs:
243 # Find the maximum widths of the value and file:lineNo fields.
244 if writeSourceLine:
245 sourceLengths = []
246 for value, output in outputs:
247 sourceLengths.append(max([len(x[0][0]) for x in output]))
248 sourceLength = max(sourceLengths)
249
250 valueLength = len(prefix) + max([len(str(value)) for value, output in outputs])
251
252 # Generate the config history content.
253 msg = []
254 fullname = "%s.%s" % (config._name, name) if config._name is not None else name
255 msg.append(_colorize(re.sub(r"^root\.", "", fullname), "NAME"))
256 for value, output in outputs:
257 line = prefix + _colorize("%-*s" % (valueLength, value), "VALUE") + " "
258 for i, vt in enumerate(output):
259 if writeSourceLine:
260 vt[0][0] = "%-*s" % (sourceLength, vt[0][0])
261
262 output[i] = " ".join([_colorize(v, t) for v, t in vt])
263
264 line += ("\n%*s" % (valueLength + 1, "")).join(output)
265 msg.append(line)
266
267 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