comparison env/lib/python3.7/site-packages/click/utils.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 import os
2 import sys
3
4 from ._compat import _default_text_stderr
5 from ._compat import _default_text_stdout
6 from ._compat import auto_wrap_for_ansi
7 from ._compat import binary_streams
8 from ._compat import filename_to_ui
9 from ._compat import get_filesystem_encoding
10 from ._compat import get_streerror
11 from ._compat import is_bytes
12 from ._compat import open_stream
13 from ._compat import PY2
14 from ._compat import should_strip_ansi
15 from ._compat import string_types
16 from ._compat import strip_ansi
17 from ._compat import text_streams
18 from ._compat import text_type
19 from ._compat import WIN
20 from .globals import resolve_color_default
21
22 if not PY2:
23 from ._compat import _find_binary_writer
24 elif WIN:
25 from ._winconsole import _get_windows_argv
26 from ._winconsole import _hash_py_argv
27 from ._winconsole import _initial_argv_hash
28
29 echo_native_types = string_types + (bytes, bytearray)
30
31
32 def _posixify(name):
33 return "-".join(name.split()).lower()
34
35
36 def safecall(func):
37 """Wraps a function so that it swallows exceptions."""
38
39 def wrapper(*args, **kwargs):
40 try:
41 return func(*args, **kwargs)
42 except Exception:
43 pass
44
45 return wrapper
46
47
48 def make_str(value):
49 """Converts a value into a valid string."""
50 if isinstance(value, bytes):
51 try:
52 return value.decode(get_filesystem_encoding())
53 except UnicodeError:
54 return value.decode("utf-8", "replace")
55 return text_type(value)
56
57
58 def make_default_short_help(help, max_length=45):
59 """Return a condensed version of help string."""
60 words = help.split()
61 total_length = 0
62 result = []
63 done = False
64
65 for word in words:
66 if word[-1:] == ".":
67 done = True
68 new_length = 1 + len(word) if result else len(word)
69 if total_length + new_length > max_length:
70 result.append("...")
71 done = True
72 else:
73 if result:
74 result.append(" ")
75 result.append(word)
76 if done:
77 break
78 total_length += new_length
79
80 return "".join(result)
81
82
83 class LazyFile(object):
84 """A lazy file works like a regular file but it does not fully open
85 the file but it does perform some basic checks early to see if the
86 filename parameter does make sense. This is useful for safely opening
87 files for writing.
88 """
89
90 def __init__(
91 self, filename, mode="r", encoding=None, errors="strict", atomic=False
92 ):
93 self.name = filename
94 self.mode = mode
95 self.encoding = encoding
96 self.errors = errors
97 self.atomic = atomic
98
99 if filename == "-":
100 self._f, self.should_close = open_stream(filename, mode, encoding, errors)
101 else:
102 if "r" in mode:
103 # Open and close the file in case we're opening it for
104 # reading so that we can catch at least some errors in
105 # some cases early.
106 open(filename, mode).close()
107 self._f = None
108 self.should_close = True
109
110 def __getattr__(self, name):
111 return getattr(self.open(), name)
112
113 def __repr__(self):
114 if self._f is not None:
115 return repr(self._f)
116 return "<unopened file '{}' {}>".format(self.name, self.mode)
117
118 def open(self):
119 """Opens the file if it's not yet open. This call might fail with
120 a :exc:`FileError`. Not handling this error will produce an error
121 that Click shows.
122 """
123 if self._f is not None:
124 return self._f
125 try:
126 rv, self.should_close = open_stream(
127 self.name, self.mode, self.encoding, self.errors, atomic=self.atomic
128 )
129 except (IOError, OSError) as e: # noqa: E402
130 from .exceptions import FileError
131
132 raise FileError(self.name, hint=get_streerror(e))
133 self._f = rv
134 return rv
135
136 def close(self):
137 """Closes the underlying file, no matter what."""
138 if self._f is not None:
139 self._f.close()
140
141 def close_intelligently(self):
142 """This function only closes the file if it was opened by the lazy
143 file wrapper. For instance this will never close stdin.
144 """
145 if self.should_close:
146 self.close()
147
148 def __enter__(self):
149 return self
150
151 def __exit__(self, exc_type, exc_value, tb):
152 self.close_intelligently()
153
154 def __iter__(self):
155 self.open()
156 return iter(self._f)
157
158
159 class KeepOpenFile(object):
160 def __init__(self, file):
161 self._file = file
162
163 def __getattr__(self, name):
164 return getattr(self._file, name)
165
166 def __enter__(self):
167 return self
168
169 def __exit__(self, exc_type, exc_value, tb):
170 pass
171
172 def __repr__(self):
173 return repr(self._file)
174
175 def __iter__(self):
176 return iter(self._file)
177
178
179 def echo(message=None, file=None, nl=True, err=False, color=None):
180 """Prints a message plus a newline to the given file or stdout. On
181 first sight, this looks like the print function, but it has improved
182 support for handling Unicode and binary data that does not fail no
183 matter how badly configured the system is.
184
185 Primarily it means that you can print binary data as well as Unicode
186 data on both 2.x and 3.x to the given file in the most appropriate way
187 possible. This is a very carefree function in that it will try its
188 best to not fail. As of Click 6.0 this includes support for unicode
189 output on the Windows console.
190
191 In addition to that, if `colorama`_ is installed, the echo function will
192 also support clever handling of ANSI codes. Essentially it will then
193 do the following:
194
195 - add transparent handling of ANSI color codes on Windows.
196 - hide ANSI codes automatically if the destination file is not a
197 terminal.
198
199 .. _colorama: https://pypi.org/project/colorama/
200
201 .. versionchanged:: 6.0
202 As of Click 6.0 the echo function will properly support unicode
203 output on the windows console. Not that click does not modify
204 the interpreter in any way which means that `sys.stdout` or the
205 print statement or function will still not provide unicode support.
206
207 .. versionchanged:: 2.0
208 Starting with version 2.0 of Click, the echo function will work
209 with colorama if it's installed.
210
211 .. versionadded:: 3.0
212 The `err` parameter was added.
213
214 .. versionchanged:: 4.0
215 Added the `color` flag.
216
217 :param message: the message to print
218 :param file: the file to write to (defaults to ``stdout``)
219 :param err: if set to true the file defaults to ``stderr`` instead of
220 ``stdout``. This is faster and easier than calling
221 :func:`get_text_stderr` yourself.
222 :param nl: if set to `True` (the default) a newline is printed afterwards.
223 :param color: controls if the terminal supports ANSI colors or not. The
224 default is autodetection.
225 """
226 if file is None:
227 if err:
228 file = _default_text_stderr()
229 else:
230 file = _default_text_stdout()
231
232 # Convert non bytes/text into the native string type.
233 if message is not None and not isinstance(message, echo_native_types):
234 message = text_type(message)
235
236 if nl:
237 message = message or u""
238 if isinstance(message, text_type):
239 message += u"\n"
240 else:
241 message += b"\n"
242
243 # If there is a message, and we're in Python 3, and the value looks
244 # like bytes, we manually need to find the binary stream and write the
245 # message in there. This is done separately so that most stream
246 # types will work as you would expect. Eg: you can write to StringIO
247 # for other cases.
248 if message and not PY2 and is_bytes(message):
249 binary_file = _find_binary_writer(file)
250 if binary_file is not None:
251 file.flush()
252 binary_file.write(message)
253 binary_file.flush()
254 return
255
256 # ANSI-style support. If there is no message or we are dealing with
257 # bytes nothing is happening. If we are connected to a file we want
258 # to strip colors. If we are on windows we either wrap the stream
259 # to strip the color or we use the colorama support to translate the
260 # ansi codes to API calls.
261 if message and not is_bytes(message):
262 color = resolve_color_default(color)
263 if should_strip_ansi(file, color):
264 message = strip_ansi(message)
265 elif WIN:
266 if auto_wrap_for_ansi is not None:
267 file = auto_wrap_for_ansi(file)
268 elif not color:
269 message = strip_ansi(message)
270
271 if message:
272 file.write(message)
273 file.flush()
274
275
276 def get_binary_stream(name):
277 """Returns a system stream for byte processing. This essentially
278 returns the stream from the sys module with the given name but it
279 solves some compatibility issues between different Python versions.
280 Primarily this function is necessary for getting binary streams on
281 Python 3.
282
283 :param name: the name of the stream to open. Valid names are ``'stdin'``,
284 ``'stdout'`` and ``'stderr'``
285 """
286 opener = binary_streams.get(name)
287 if opener is None:
288 raise TypeError("Unknown standard stream '{}'".format(name))
289 return opener()
290
291
292 def get_text_stream(name, encoding=None, errors="strict"):
293 """Returns a system stream for text processing. This usually returns
294 a wrapped stream around a binary stream returned from
295 :func:`get_binary_stream` but it also can take shortcuts on Python 3
296 for already correctly configured streams.
297
298 :param name: the name of the stream to open. Valid names are ``'stdin'``,
299 ``'stdout'`` and ``'stderr'``
300 :param encoding: overrides the detected default encoding.
301 :param errors: overrides the default error mode.
302 """
303 opener = text_streams.get(name)
304 if opener is None:
305 raise TypeError("Unknown standard stream '{}'".format(name))
306 return opener(encoding, errors)
307
308
309 def open_file(
310 filename, mode="r", encoding=None, errors="strict", lazy=False, atomic=False
311 ):
312 """This is similar to how the :class:`File` works but for manual
313 usage. Files are opened non lazy by default. This can open regular
314 files as well as stdin/stdout if ``'-'`` is passed.
315
316 If stdin/stdout is returned the stream is wrapped so that the context
317 manager will not close the stream accidentally. This makes it possible
318 to always use the function like this without having to worry to
319 accidentally close a standard stream::
320
321 with open_file(filename) as f:
322 ...
323
324 .. versionadded:: 3.0
325
326 :param filename: the name of the file to open (or ``'-'`` for stdin/stdout).
327 :param mode: the mode in which to open the file.
328 :param encoding: the encoding to use.
329 :param errors: the error handling for this file.
330 :param lazy: can be flipped to true to open the file lazily.
331 :param atomic: in atomic mode writes go into a temporary file and it's
332 moved on close.
333 """
334 if lazy:
335 return LazyFile(filename, mode, encoding, errors, atomic=atomic)
336 f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)
337 if not should_close:
338 f = KeepOpenFile(f)
339 return f
340
341
342 def get_os_args():
343 """This returns the argument part of sys.argv in the most appropriate
344 form for processing. What this means is that this return value is in
345 a format that works for Click to process but does not necessarily
346 correspond well to what's actually standard for the interpreter.
347
348 On most environments the return value is ``sys.argv[:1]`` unchanged.
349 However if you are on Windows and running Python 2 the return value
350 will actually be a list of unicode strings instead because the
351 default behavior on that platform otherwise will not be able to
352 carry all possible values that sys.argv can have.
353
354 .. versionadded:: 6.0
355 """
356 # We can only extract the unicode argv if sys.argv has not been
357 # changed since the startup of the application.
358 if PY2 and WIN and _initial_argv_hash == _hash_py_argv():
359 return _get_windows_argv()
360 return sys.argv[1:]
361
362
363 def format_filename(filename, shorten=False):
364 """Formats a filename for user display. The main purpose of this
365 function is to ensure that the filename can be displayed at all. This
366 will decode the filename to unicode if necessary in a way that it will
367 not fail. Optionally, it can shorten the filename to not include the
368 full path to the filename.
369
370 :param filename: formats a filename for UI display. This will also convert
371 the filename into unicode without failing.
372 :param shorten: this optionally shortens the filename to strip of the
373 path that leads up to it.
374 """
375 if shorten:
376 filename = os.path.basename(filename)
377 return filename_to_ui(filename)
378
379
380 def get_app_dir(app_name, roaming=True, force_posix=False):
381 r"""Returns the config folder for the application. The default behavior
382 is to return whatever is most appropriate for the operating system.
383
384 To give you an idea, for an app called ``"Foo Bar"``, something like
385 the following folders could be returned:
386
387 Mac OS X:
388 ``~/Library/Application Support/Foo Bar``
389 Mac OS X (POSIX):
390 ``~/.foo-bar``
391 Unix:
392 ``~/.config/foo-bar``
393 Unix (POSIX):
394 ``~/.foo-bar``
395 Win XP (roaming):
396 ``C:\Documents and Settings\<user>\Local Settings\Application Data\Foo Bar``
397 Win XP (not roaming):
398 ``C:\Documents and Settings\<user>\Application Data\Foo Bar``
399 Win 7 (roaming):
400 ``C:\Users\<user>\AppData\Roaming\Foo Bar``
401 Win 7 (not roaming):
402 ``C:\Users\<user>\AppData\Local\Foo Bar``
403
404 .. versionadded:: 2.0
405
406 :param app_name: the application name. This should be properly capitalized
407 and can contain whitespace.
408 :param roaming: controls if the folder should be roaming or not on Windows.
409 Has no affect otherwise.
410 :param force_posix: if this is set to `True` then on any POSIX system the
411 folder will be stored in the home folder with a leading
412 dot instead of the XDG config home or darwin's
413 application support folder.
414 """
415 if WIN:
416 key = "APPDATA" if roaming else "LOCALAPPDATA"
417 folder = os.environ.get(key)
418 if folder is None:
419 folder = os.path.expanduser("~")
420 return os.path.join(folder, app_name)
421 if force_posix:
422 return os.path.join(os.path.expanduser("~/.{}".format(_posixify(app_name))))
423 if sys.platform == "darwin":
424 return os.path.join(
425 os.path.expanduser("~/Library/Application Support"), app_name
426 )
427 return os.path.join(
428 os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")),
429 _posixify(app_name),
430 )
431
432
433 class PacifyFlushWrapper(object):
434 """This wrapper is used to catch and suppress BrokenPipeErrors resulting
435 from ``.flush()`` being called on broken pipe during the shutdown/final-GC
436 of the Python interpreter. Notably ``.flush()`` is always called on
437 ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any
438 other cleanup code, and the case where the underlying file is not a broken
439 pipe, all calls and attributes are proxied.
440 """
441
442 def __init__(self, wrapped):
443 self.wrapped = wrapped
444
445 def flush(self):
446 try:
447 self.wrapped.flush()
448 except IOError as e:
449 import errno
450
451 if e.errno != errno.EPIPE:
452 raise
453
454 def __getattr__(self, attr):
455 return getattr(self.wrapped, attr)