]> git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/terminal.py
update ceph source to reef 18.2.0
[ceph.git] / ceph / src / ceph-volume / ceph_volume / terminal.py
1 import logging
2 import sys
3
4
5 terminal_logger = logging.getLogger('terminal')
6
7
8 class colorize(str):
9 """
10 Pretty simple to use::
11
12 colorize.make('foo').bold
13 colorize.make('foo').green
14 colorize.make('foo').yellow
15 colorize.make('foo').red
16 colorize.make('foo').blue
17
18 Otherwise you could go the long way (for example if you are
19 testing this class)::
20
21 string = colorize('foo')
22 string._set_attributes()
23 string.red
24
25 """
26
27 def __init__(self, string):
28 self.appends = ''
29 self.prepends = ''
30 self.isatty = sys.__stderr__.isatty()
31
32 def _set_attributes(self):
33 """
34 Sets the attributes here because the str class does not
35 allow to pass in anything other than a string to the constructor
36 so we can't really mess with the other attributes.
37 """
38 for k, v in self.__colors__.items():
39 setattr(self, k, self.make_color(v))
40
41 def make_color(self, color):
42 if not self.isatty:
43 return self
44 return color + self + '\033[0m' + self.appends
45
46 @property
47 def __colors__(self):
48 return dict(
49 blue='\033[34m',
50 green='\033[92m',
51 yellow='\033[33m',
52 red='\033[91m',
53 bold='\033[1m',
54 ends='\033[0m'
55 )
56
57 @classmethod
58 def make(cls, string):
59 """
60 A helper method to return itself and workaround the fact that
61 the str object doesn't allow extra arguments passed in to the
62 constructor
63 """
64 obj = cls(string)
65 obj._set_attributes()
66 return obj
67
68 #
69 # Common string manipulations
70 #
71 yellow = lambda x: colorize.make(x).yellow # noqa
72 blue = lambda x: colorize.make(x).blue # noqa
73 green = lambda x: colorize.make(x).green # noqa
74 red = lambda x: colorize.make(x).red # noqa
75 bold = lambda x: colorize.make(x).bold # noqa
76 red_arrow = red('--> ')
77 blue_arrow = blue('--> ')
78 green_arrow = green('--> ')
79 yellow_arrow = yellow('--> ')
80
81
82 class _Write(object):
83
84 def __init__(self, _writer=None, prefix='', suffix='', flush=False):
85 # we can't set sys.stderr as the default for _writer. otherwise
86 # pytest's capturing gets confused
87 self._writer = _writer or sys.stderr
88 self.suffix = suffix
89 self.prefix = prefix
90 self.flush = flush
91
92 def bold(self, string):
93 self.write(bold(string))
94
95 def raw(self, string):
96 if not string.endswith('\n'):
97 string = '%s\n' % string
98 self.write(string)
99
100 def write(self, line):
101 entry = self.prefix + line + self.suffix
102
103 try:
104 self._writer.write(entry)
105 if self.flush:
106 self._writer.flush()
107 except (UnicodeDecodeError, UnicodeEncodeError):
108 try:
109 terminal_logger.info(entry.strip('\n'))
110 except (AttributeError, TypeError):
111 terminal_logger.info(entry)
112
113
114 def stdout(msg):
115 return _Write(prefix=blue(' stdout: ')).raw(msg)
116
117
118 def stderr(msg):
119 return _Write(prefix=yellow(' stderr: ')).raw(msg)
120
121
122 def write(msg):
123 return _Write().raw(msg)
124
125
126 def error(msg):
127 return _Write(prefix=red_arrow).raw(msg)
128
129
130 def info(msg):
131 return _Write(prefix=blue_arrow).raw(msg)
132
133
134 def debug(msg):
135 return _Write(prefix=blue_arrow).raw(msg)
136
137
138 def warning(msg):
139 return _Write(prefix=yellow_arrow).raw(msg)
140
141
142 def success(msg):
143 return _Write(prefix=green_arrow).raw(msg)
144
145
146 class MultiLogger(object):
147 """
148 Proxy class to be able to report on both logger instances and terminal
149 messages avoiding the issue of having to call them both separately
150
151 Initialize it in the same way a logger object::
152
153 logger = terminal.MultiLogger(__name__)
154 """
155
156 def __init__(self, name):
157 self.logger = logging.getLogger(name)
158
159 def _make_record(self, msg, *args):
160 if len(str(args)):
161 try:
162 return msg % args
163 except TypeError:
164 self.logger.exception('unable to produce log record: %s' % msg)
165 return msg
166
167 def warning(self, msg, *args):
168 record = self._make_record(msg, *args)
169 warning(record)
170 self.logger.warning(record)
171
172 def debug(self, msg, *args):
173 record = self._make_record(msg, *args)
174 debug(record)
175 self.logger.debug(record)
176
177 def info(self, msg, *args):
178 record = self._make_record(msg, *args)
179 info(record)
180 self.logger.info(record)
181
182 def error(self, msg, *args):
183 record = self._make_record(msg, *args)
184 error(record)
185 self.logger.error(record)
186
187
188 def dispatch(mapper, argv=None):
189 argv = argv or sys.argv
190 for count, arg in enumerate(argv, 1):
191 if arg in mapper.keys():
192 instance = mapper.get(arg)(argv[count:])
193 if hasattr(instance, 'main'):
194 instance.main()
195 raise SystemExit(0)
196
197
198 def subhelp(mapper):
199 """
200 Look at every value of every key in the mapper and will output any
201 ``class.help`` possible to return it as a string that will be sent to
202 stderr.
203 """
204 help_text_lines = []
205 for key, value in mapper.items():
206 try:
207 help_text = value.help
208 except AttributeError:
209 continue
210 help_text_lines.append("%-24s %s" % (key, help_text))
211
212 if help_text_lines:
213 return "Available subcommands:\n\n%s" % '\n'.join(help_text_lines)
214 return ''