comparison venv/lib/python2.7/site-packages/click/formatting.py @ 0:d67268158946 draft

planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author bcclaywell
date Mon, 12 Oct 2015 17:43:33 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d67268158946
1 from contextlib import contextmanager
2 from .termui import get_terminal_size
3 from .parser import split_opt
4 from ._compat import term_len
5
6
7 def measure_table(rows):
8 widths = {}
9 for row in rows:
10 for idx, col in enumerate(row):
11 widths[idx] = max(widths.get(idx, 0), term_len(col))
12 return tuple(y for x, y in sorted(widths.items()))
13
14
15 def iter_rows(rows, col_count):
16 for row in rows:
17 row = tuple(row)
18 yield row + ('',) * (col_count - len(row))
19
20
21 def wrap_text(text, width=78, initial_indent='', subsequent_indent='',
22 preserve_paragraphs=False):
23 """A helper function that intelligently wraps text. By default, it
24 assumes that it operates on a single paragraph of text but if the
25 `preserve_paragraphs` parameter is provided it will intelligently
26 handle paragraphs (defined by two empty lines).
27
28 If paragraphs are handled, a paragraph can be prefixed with an empty
29 line containing the ``\\b`` character (``\\x08``) to indicate that
30 no rewrapping should happen in that block.
31
32 :param text: the text that should be rewrapped.
33 :param width: the maximum width for the text.
34 :param initial_indent: the initial indent that should be placed on the
35 first line as a string.
36 :param subsequent_indent: the indent string that should be placed on
37 each consecutive line.
38 :param preserve_paragraphs: if this flag is set then the wrapping will
39 intelligently handle paragraphs.
40 """
41 from ._textwrap import TextWrapper
42 text = text.expandtabs()
43 wrapper = TextWrapper(width, initial_indent=initial_indent,
44 subsequent_indent=subsequent_indent,
45 replace_whitespace=False)
46 if not preserve_paragraphs:
47 return wrapper.fill(text)
48
49 p = []
50 buf = []
51 indent = None
52
53 def _flush_par():
54 if not buf:
55 return
56 if buf[0].strip() == '\b':
57 p.append((indent or 0, True, '\n'.join(buf[1:])))
58 else:
59 p.append((indent or 0, False, ' '.join(buf)))
60 del buf[:]
61
62 for line in text.splitlines():
63 if not line:
64 _flush_par()
65 indent = None
66 else:
67 if indent is None:
68 orig_len = term_len(line)
69 line = line.lstrip()
70 indent = orig_len - term_len(line)
71 buf.append(line)
72 _flush_par()
73
74 rv = []
75 for indent, raw, text in p:
76 with wrapper.extra_indent(' ' * indent):
77 if raw:
78 rv.append(wrapper.indent_only(text))
79 else:
80 rv.append(wrapper.fill(text))
81
82 return '\n\n'.join(rv)
83
84
85 class HelpFormatter(object):
86 """This class helps with formatting text-based help pages. It's
87 usually just needed for very special internal cases, but it's also
88 exposed so that developers can write their own fancy outputs.
89
90 At present, it always writes into memory.
91
92 :param indent_increment: the additional increment for each level.
93 :param width: the width for the text. This defaults to the terminal
94 width clamped to a maximum of 78.
95 """
96
97 def __init__(self, indent_increment=2, width=None, max_width=None):
98 self.indent_increment = indent_increment
99 if max_width is None:
100 max_width = 80
101 if width is None:
102 width = max(min(get_terminal_size()[0], max_width) - 2, 50)
103 self.width = width
104 self.current_indent = 0
105 self.buffer = []
106
107 def write(self, string):
108 """Writes a unicode string into the internal buffer."""
109 self.buffer.append(string)
110
111 def indent(self):
112 """Increases the indentation."""
113 self.current_indent += self.indent_increment
114
115 def dedent(self):
116 """Decreases the indentation."""
117 self.current_indent -= self.indent_increment
118
119 def write_usage(self, prog, args='', prefix='Usage: '):
120 """Writes a usage line into the buffer.
121
122 :param prog: the program name.
123 :param args: whitespace separated list of arguments.
124 :param prefix: the prefix for the first line.
125 """
126 usage_prefix = '%*s%s ' % (self.current_indent, prefix, prog)
127 text_width = self.width - self.current_indent
128
129 if text_width >= (term_len(usage_prefix) + 20):
130 # The arguments will fit to the right of the prefix.
131 indent = ' ' * term_len(usage_prefix)
132 self.write(wrap_text(args, text_width,
133 initial_indent=usage_prefix,
134 subsequent_indent=indent))
135 else:
136 # The prefix is too long, put the arguments on the next line.
137 self.write(usage_prefix)
138 self.write('\n')
139 indent = ' ' * (max(self.current_indent, term_len(prefix)) + 4)
140 self.write(wrap_text(args, text_width,
141 initial_indent=indent,
142 subsequent_indent=indent))
143
144 self.write('\n')
145
146 def write_heading(self, heading):
147 """Writes a heading into the buffer."""
148 self.write('%*s%s:\n' % (self.current_indent, '', heading))
149
150 def write_paragraph(self):
151 """Writes a paragraph into the buffer."""
152 if self.buffer:
153 self.write('\n')
154
155 def write_text(self, text):
156 """Writes re-indented text into the buffer. This rewraps and
157 preserves paragraphs.
158 """
159 text_width = max(self.width - self.current_indent, 11)
160 indent = ' ' * self.current_indent
161 self.write(wrap_text(text, text_width,
162 initial_indent=indent,
163 subsequent_indent=indent,
164 preserve_paragraphs=True))
165 self.write('\n')
166
167 def write_dl(self, rows, col_max=30, col_spacing=2):
168 """Writes a definition list into the buffer. This is how options
169 and commands are usually formatted.
170
171 :param rows: a list of two item tuples for the terms and values.
172 :param col_max: the maximum width of the first column.
173 :param col_spacing: the number of spaces between the first and
174 second column.
175 """
176 rows = list(rows)
177 widths = measure_table(rows)
178 if len(widths) != 2:
179 raise TypeError('Expected two columns for definition list')
180
181 first_col = min(widths[0], col_max) + col_spacing
182
183 for first, second in iter_rows(rows, len(widths)):
184 self.write('%*s%s' % (self.current_indent, '', first))
185 if not second:
186 self.write('\n')
187 continue
188 if term_len(first) <= first_col - col_spacing:
189 self.write(' ' * (first_col - term_len(first)))
190 else:
191 self.write('\n')
192 self.write(' ' * (first_col + self.current_indent))
193
194 text_width = max(self.width - first_col - 2, 10)
195 lines = iter(wrap_text(second, text_width).splitlines())
196 if lines:
197 self.write(next(lines) + '\n')
198 for line in lines:
199 self.write('%*s%s\n' % (
200 first_col + self.current_indent, '', line))
201 else:
202 self.write('\n')
203
204 @contextmanager
205 def section(self, name):
206 """Helpful context manager that writes a paragraph, a heading,
207 and the indents.
208
209 :param name: the section name that is written as heading.
210 """
211 self.write_paragraph()
212 self.write_heading(name)
213 self.indent()
214 try:
215 yield
216 finally:
217 self.dedent()
218
219 @contextmanager
220 def indentation(self):
221 """A context manager that increases the indentation."""
222 self.indent()
223 try:
224 yield
225 finally:
226 self.dedent()
227
228 def getvalue(self):
229 """Returns the buffer contents."""
230 return ''.join(self.buffer)
231
232
233 def join_options(options):
234 """Given a list of option strings this joins them in the most appropriate
235 way and returns them in the form ``(formatted_string,
236 any_prefix_is_slash)`` where the second item in the tuple is a flag that
237 indicates if any of the option prefixes was a slash.
238 """
239 rv = []
240 any_prefix_is_slash = False
241 for opt in options:
242 prefix = split_opt(opt)[0]
243 if prefix == '/':
244 any_prefix_is_slash = True
245 rv.append((len(prefix), opt))
246
247 rv.sort(key=lambda x: x[0])
248
249 rv = ', '.join(x[1] for x in rv)
250 return rv, any_prefix_is_slash