Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/humanfriendly/cli.py @ 2:6af9afd405e9 draft
"planemo upload commit 0a63dd5f4d38a1f6944587f52a8cd79874177fc1"
author | shellac |
---|---|
date | Thu, 14 May 2020 14:56:58 -0400 |
parents | 26e78fe6e8c4 |
children |
comparison
equal
deleted
inserted
replaced
1:75ca89e9b81c | 2:6af9afd405e9 |
---|---|
1 # Human friendly input/output in Python. | |
2 # | |
3 # Author: Peter Odding <peter@peterodding.com> | |
4 # Last Change: March 1, 2020 | |
5 # URL: https://humanfriendly.readthedocs.io | |
6 | |
7 """ | |
8 Usage: humanfriendly [OPTIONS] | |
9 | |
10 Human friendly input/output (text formatting) on the command | |
11 line based on the Python package with the same name. | |
12 | |
13 Supported options: | |
14 | |
15 -c, --run-command | |
16 | |
17 Execute an external command (given as the positional arguments) and render | |
18 a spinner and timer while the command is running. The exit status of the | |
19 command is propagated. | |
20 | |
21 --format-table | |
22 | |
23 Read tabular data from standard input (each line is a row and each | |
24 whitespace separated field is a column), format the data as a table and | |
25 print the resulting table to standard output. See also the --delimiter | |
26 option. | |
27 | |
28 -d, --delimiter=VALUE | |
29 | |
30 Change the delimiter used by --format-table to VALUE (a string). By default | |
31 all whitespace is treated as a delimiter. | |
32 | |
33 -l, --format-length=LENGTH | |
34 | |
35 Convert a length count (given as the integer or float LENGTH) into a human | |
36 readable string and print that string to standard output. | |
37 | |
38 -n, --format-number=VALUE | |
39 | |
40 Format a number (given as the integer or floating point number VALUE) with | |
41 thousands separators and two decimal places (if needed) and print the | |
42 formatted number to standard output. | |
43 | |
44 -s, --format-size=BYTES | |
45 | |
46 Convert a byte count (given as the integer BYTES) into a human readable | |
47 string and print that string to standard output. | |
48 | |
49 -b, --binary | |
50 | |
51 Change the output of -s, --format-size to use binary multiples of bytes | |
52 (base-2) instead of the default decimal multiples of bytes (base-10). | |
53 | |
54 -t, --format-timespan=SECONDS | |
55 | |
56 Convert a number of seconds (given as the floating point number SECONDS) | |
57 into a human readable timespan and print that string to standard output. | |
58 | |
59 --parse-length=VALUE | |
60 | |
61 Parse a human readable length (given as the string VALUE) and print the | |
62 number of metres to standard output. | |
63 | |
64 --parse-size=VALUE | |
65 | |
66 Parse a human readable data size (given as the string VALUE) and print the | |
67 number of bytes to standard output. | |
68 | |
69 --demo | |
70 | |
71 Demonstrate changing the style and color of the terminal font using ANSI | |
72 escape sequences. | |
73 | |
74 -h, --help | |
75 | |
76 Show this message and exit. | |
77 """ | |
78 | |
79 # Standard library modules. | |
80 import functools | |
81 import getopt | |
82 import pipes | |
83 import subprocess | |
84 import sys | |
85 | |
86 # Modules included in our package. | |
87 from humanfriendly import ( | |
88 Timer, | |
89 format_length, | |
90 format_number, | |
91 format_size, | |
92 format_timespan, | |
93 parse_length, | |
94 parse_size, | |
95 ) | |
96 from humanfriendly.tables import format_pretty_table, format_smart_table | |
97 from humanfriendly.terminal import ( | |
98 ANSI_COLOR_CODES, | |
99 ANSI_TEXT_STYLES, | |
100 HIGHLIGHT_COLOR, | |
101 ansi_strip, | |
102 ansi_wrap, | |
103 enable_ansi_support, | |
104 find_terminal_size, | |
105 output, | |
106 usage, | |
107 warning, | |
108 ) | |
109 from humanfriendly.terminal.spinners import Spinner | |
110 | |
111 # Public identifiers that require documentation. | |
112 __all__ = ( | |
113 'demonstrate_256_colors', | |
114 'demonstrate_ansi_formatting', | |
115 'main', | |
116 'print_formatted_length', | |
117 'print_formatted_number', | |
118 'print_formatted_size', | |
119 'print_formatted_table', | |
120 'print_formatted_timespan', | |
121 'print_parsed_length', | |
122 'print_parsed_size', | |
123 'run_command', | |
124 ) | |
125 | |
126 | |
127 def main(): | |
128 """Command line interface for the ``humanfriendly`` program.""" | |
129 enable_ansi_support() | |
130 try: | |
131 options, arguments = getopt.getopt(sys.argv[1:], 'cd:l:n:s:bt:h', [ | |
132 'run-command', 'format-table', 'delimiter=', 'format-length=', | |
133 'format-number=', 'format-size=', 'binary', 'format-timespan=', | |
134 'parse-length=', 'parse-size=', 'demo', 'help', | |
135 ]) | |
136 except Exception as e: | |
137 warning("Error: %s", e) | |
138 sys.exit(1) | |
139 actions = [] | |
140 delimiter = None | |
141 should_format_table = False | |
142 binary = any(o in ('-b', '--binary') for o, v in options) | |
143 for option, value in options: | |
144 if option in ('-d', '--delimiter'): | |
145 delimiter = value | |
146 elif option == '--parse-size': | |
147 actions.append(functools.partial(print_parsed_size, value)) | |
148 elif option == '--parse-length': | |
149 actions.append(functools.partial(print_parsed_length, value)) | |
150 elif option in ('-c', '--run-command'): | |
151 actions.append(functools.partial(run_command, arguments)) | |
152 elif option in ('-l', '--format-length'): | |
153 actions.append(functools.partial(print_formatted_length, value)) | |
154 elif option in ('-n', '--format-number'): | |
155 actions.append(functools.partial(print_formatted_number, value)) | |
156 elif option in ('-s', '--format-size'): | |
157 actions.append(functools.partial(print_formatted_size, value, binary)) | |
158 elif option == '--format-table': | |
159 should_format_table = True | |
160 elif option in ('-t', '--format-timespan'): | |
161 actions.append(functools.partial(print_formatted_timespan, value)) | |
162 elif option == '--demo': | |
163 actions.append(demonstrate_ansi_formatting) | |
164 elif option in ('-h', '--help'): | |
165 usage(__doc__) | |
166 return | |
167 if should_format_table: | |
168 actions.append(functools.partial(print_formatted_table, delimiter)) | |
169 if not actions: | |
170 usage(__doc__) | |
171 return | |
172 for partial in actions: | |
173 partial() | |
174 | |
175 | |
176 def run_command(command_line): | |
177 """Run an external command and show a spinner while the command is running.""" | |
178 timer = Timer() | |
179 spinner_label = "Waiting for command: %s" % " ".join(map(pipes.quote, command_line)) | |
180 with Spinner(label=spinner_label, timer=timer) as spinner: | |
181 process = subprocess.Popen(command_line) | |
182 while True: | |
183 spinner.step() | |
184 spinner.sleep() | |
185 if process.poll() is not None: | |
186 break | |
187 sys.exit(process.returncode) | |
188 | |
189 | |
190 def print_formatted_length(value): | |
191 """Print a human readable length.""" | |
192 if '.' in value: | |
193 output(format_length(float(value))) | |
194 else: | |
195 output(format_length(int(value))) | |
196 | |
197 | |
198 def print_formatted_number(value): | |
199 """Print large numbers in a human readable format.""" | |
200 output(format_number(float(value))) | |
201 | |
202 | |
203 def print_formatted_size(value, binary): | |
204 """Print a human readable size.""" | |
205 output(format_size(int(value), binary=binary)) | |
206 | |
207 | |
208 def print_formatted_table(delimiter): | |
209 """Read tabular data from standard input and print a table.""" | |
210 data = [] | |
211 for line in sys.stdin: | |
212 line = line.rstrip() | |
213 data.append(line.split(delimiter)) | |
214 output(format_pretty_table(data)) | |
215 | |
216 | |
217 def print_formatted_timespan(value): | |
218 """Print a human readable timespan.""" | |
219 output(format_timespan(float(value))) | |
220 | |
221 | |
222 def print_parsed_length(value): | |
223 """Parse a human readable length and print the number of metres.""" | |
224 output(parse_length(value)) | |
225 | |
226 | |
227 def print_parsed_size(value): | |
228 """Parse a human readable data size and print the number of bytes.""" | |
229 output(parse_size(value)) | |
230 | |
231 | |
232 def demonstrate_ansi_formatting(): | |
233 """Demonstrate the use of ANSI escape sequences.""" | |
234 # First we demonstrate the supported text styles. | |
235 output('%s', ansi_wrap('Text styles:', bold=True)) | |
236 styles = ['normal', 'bright'] | |
237 styles.extend(ANSI_TEXT_STYLES.keys()) | |
238 for style_name in sorted(styles): | |
239 options = dict(color=HIGHLIGHT_COLOR) | |
240 if style_name != 'normal': | |
241 options[style_name] = True | |
242 style_label = style_name.replace('_', ' ').capitalize() | |
243 output(' - %s', ansi_wrap(style_label, **options)) | |
244 # Now we demonstrate named foreground and background colors. | |
245 for color_type, color_label in (('color', 'Foreground colors'), | |
246 ('background', 'Background colors')): | |
247 intensities = [ | |
248 ('normal', dict()), | |
249 ('bright', dict(bright=True)), | |
250 ] | |
251 if color_type != 'background': | |
252 intensities.insert(0, ('faint', dict(faint=True))) | |
253 output('\n%s' % ansi_wrap('%s:' % color_label, bold=True)) | |
254 output(format_smart_table([ | |
255 [color_name] + [ | |
256 ansi_wrap( | |
257 'XXXXXX' if color_type != 'background' else (' ' * 6), | |
258 **dict(list(kw.items()) + [(color_type, color_name)]) | |
259 ) for label, kw in intensities | |
260 ] for color_name in sorted(ANSI_COLOR_CODES.keys()) | |
261 ], column_names=['Color'] + [ | |
262 label.capitalize() for label, kw in intensities | |
263 ])) | |
264 # Demonstrate support for 256 colors as well. | |
265 demonstrate_256_colors(0, 7, 'standard colors') | |
266 demonstrate_256_colors(8, 15, 'high-intensity colors') | |
267 demonstrate_256_colors(16, 231, '216 colors') | |
268 demonstrate_256_colors(232, 255, 'gray scale colors') | |
269 | |
270 | |
271 def demonstrate_256_colors(i, j, group=None): | |
272 """Demonstrate 256 color mode support.""" | |
273 # Generate the label. | |
274 label = '256 color mode' | |
275 if group: | |
276 label += ' (%s)' % group | |
277 output('\n' + ansi_wrap('%s:' % label, bold=True)) | |
278 # Generate a simple rendering of the colors in the requested range and | |
279 # check if it will fit on a single line (given the terminal's width). | |
280 single_line = ''.join(' ' + ansi_wrap(str(n), color=n) for n in range(i, j + 1)) | |
281 lines, columns = find_terminal_size() | |
282 if columns >= len(ansi_strip(single_line)): | |
283 output(single_line) | |
284 else: | |
285 # Generate a more complex rendering of the colors that will nicely wrap | |
286 # over multiple lines without using too many lines. | |
287 width = len(str(j)) + 1 | |
288 colors_per_line = int(columns / width) | |
289 colors = [ansi_wrap(str(n).rjust(width), color=n) for n in range(i, j + 1)] | |
290 blocks = [colors[n:n + colors_per_line] for n in range(0, len(colors), colors_per_line)] | |
291 output('\n'.join(''.join(b) for b in blocks)) |