1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
#
# coding=utf-8
"""Shared utility functions"""
import collections
import os
from typing import Any, List, Optional, Union
from . import constants
def strip_ansi(text: str) -> str:
"""Strip ANSI escape codes from a string.
:param text: string which may contain ANSI escape codes
:return: the same string with any ANSI escape codes removed
"""
return constants.ANSI_ESCAPE_RE.sub('', text)
def strip_quotes(arg: str) -> str:
""" Strip outer quotes from a string.
Applies to both single and double quotes.
:param arg: string to strip outer quotes from
:return: same string with potentially outer quotes stripped
"""
if len(arg) > 1 and arg[0] == arg[-1] and arg[0] in constants.QUOTES:
arg = arg[1:-1]
return arg
def namedtuple_with_defaults(typename: str, field_names: Union[str, List[str]],
default_values: collections.Iterable=()):
"""
Convenience function for defining a namedtuple with default values
From: https://stackoverflow.com/questions/11351032/namedtuple-and-default-values-for-optional-keyword-arguments
Examples:
>>> Node = namedtuple_with_defaults('Node', 'val left right')
>>> Node()
Node(val=None, left=None, right=None)
>>> Node = namedtuple_with_defaults('Node', 'val left right', [1, 2, 3])
>>> Node()
Node(val=1, left=2, right=3)
>>> Node = namedtuple_with_defaults('Node', 'val left right', {'right':7})
>>> Node()
Node(val=None, left=None, right=7)
>>> Node(4)
Node(val=4, left=None, right=7)
"""
T = collections.namedtuple(typename, field_names)
T.__new__.__defaults__ = (None,) * len(T._fields)
if isinstance(default_values, collections.Mapping):
prototype = T(**default_values)
else:
prototype = T(*default_values)
T.__new__.__defaults__ = tuple(prototype)
return T
def cast(current: Any, new: str) -> Any:
"""Tries to force a new value into the same type as the current when trying to set the value for a parameter.
:param current: current value for the parameter, type varies
:param new: str - new value
:return: new value with same type as current, or the current value if there was an error casting
"""
typ = type(current)
if typ == bool:
try:
return bool(int(new))
except (ValueError, TypeError):
pass
try:
new = new.lower()
except AttributeError:
pass
if (new == 'on') or (new[0] in ('y', 't')):
return True
if (new == 'off') or (new[0] in ('n', 'f')):
return False
else:
try:
return typ(new)
except (ValueError, TypeError):
pass
print("Problem setting parameter (now %s) to %s; incorrect type?" % (current, new))
return current
def which(editor: str) -> Optional[str]:
"""Find the full path of a given editor.
Return the full path of the given editor, or None if the editor can
not be found.
:param editor: filename of the editor to check, ie 'notepad.exe' or 'vi'
:return: a full path or None
"""
import subprocess
try:
editor_path = subprocess.check_output(['which', editor], stderr=subprocess.STDOUT).strip()
editor_path = editor_path.decode()
except subprocess.CalledProcessError:
editor_path = None
return editor_path
def is_text_file(file_path: str) -> bool:
"""Returns if a file contains only ASCII or UTF-8 encoded text
:param file_path: path to the file being checked
:return: True if the file is a text file, False if it is binary.
"""
import codecs
expanded_path = os.path.abspath(os.path.expanduser(file_path.strip()))
valid_text_file = False
# Check if the file is ASCII
try:
with codecs.open(expanded_path, encoding='ascii', errors='strict') as f:
# Make sure the file has at least one line of text
# noinspection PyUnusedLocal
if sum(1 for line in f) > 0:
valid_text_file = True
except OSError: # pragma: no cover
pass
except UnicodeDecodeError:
# The file is not ASCII. Check if it is UTF-8.
try:
with codecs.open(expanded_path, encoding='utf-8', errors='strict') as f:
# Make sure the file has at least one line of text
# noinspection PyUnusedLocal
if sum(1 for line in f) > 0:
valid_text_file = True
except OSError: # pragma: no cover
pass
except UnicodeDecodeError:
# Not UTF-8
pass
return valid_text_file
|