Revision 5b69bc7c
b/lib/utils.py | ||
---|---|---|
1897 | 1897 |
return resu |
1898 | 1898 |
|
1899 | 1899 |
|
1900 |
def UnescapeAndSplit(text, sep=","): |
|
1901 |
"""Split and unescape a string based on a given separator. |
|
1902 |
|
|
1903 |
This function splits a string based on a separator where the |
|
1904 |
separator itself can be escape in order to be an element of the |
|
1905 |
elements. The escaping rules are (assuming coma being the |
|
1906 |
separator): |
|
1907 |
- a plain , separates the elements |
|
1908 |
- a sequence \\\\, (double backslash plus comma) is handled as a |
|
1909 |
backslash plus a separator comma |
|
1910 |
- a sequence \, (backslash plus comma) is handled as a |
|
1911 |
non-separator comma |
|
1912 |
|
|
1913 |
@type text: string |
|
1914 |
@param text: the string to split |
|
1915 |
@type sep: string |
|
1916 |
@param text: the separator |
|
1917 |
@rtype: string |
|
1918 |
@return: a list of strings |
|
1919 |
|
|
1920 |
""" |
|
1921 |
# we split the list by sep (with no escaping at this stage) |
|
1922 |
slist = text.split(sep) |
|
1923 |
# next, we revisit the elements and if any of them ended with an odd |
|
1924 |
# number of backslashes, then we join it with the next |
|
1925 |
rlist = [] |
|
1926 |
while slist: |
|
1927 |
e1 = slist.pop(0) |
|
1928 |
if e1.endswith("\\"): |
|
1929 |
num_b = len(e1) - len(e1.rstrip("\\")) |
|
1930 |
if num_b % 2 == 1: |
|
1931 |
e2 = slist.pop(0) |
|
1932 |
# here the backslashes remain (all), and will be reduced in |
|
1933 |
# the next step |
|
1934 |
rlist.append(e1 + sep + e2) |
|
1935 |
continue |
|
1936 |
rlist.append(e1) |
|
1937 |
# finally, replace backslash-something with something |
|
1938 |
rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist] |
|
1939 |
return rlist |
|
1940 |
|
|
1941 |
|
|
1900 | 1942 |
def CommaJoin(names): |
1901 | 1943 |
"""Nicely join a set of identifiers. |
1902 | 1944 |
|
b/test/ganeti.utils_unittest.py | ||
---|---|---|
45 | 45 |
ParseUnit, AddAuthorizedKey, RemoveAuthorizedKey, \ |
46 | 46 |
ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \ |
47 | 47 |
SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree, OwnIpAddress, \ |
48 |
TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime |
|
48 |
TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime, \ |
|
49 |
UnescapeAndSplit |
|
49 | 50 |
|
50 | 51 |
from ganeti.errors import LockError, UnitParseError, GenericError, \ |
51 | 52 |
ProgrammerError |
... | ... | |
1053 | 1054 |
FormatTime(int(time.time())) |
1054 | 1055 |
|
1055 | 1056 |
|
1057 |
class TestUnescapeAndSplit(unittest.TestCase): |
|
1058 |
"""Testing case for UnescapeAndSplit""" |
|
1059 |
|
|
1060 |
def setUp(self): |
|
1061 |
# testing more that one separator for regexp safety |
|
1062 |
self._seps = [",", "+", "."] |
|
1063 |
|
|
1064 |
def testSimple(self): |
|
1065 |
a = ["a", "b", "c", "d"] |
|
1066 |
for sep in self._seps: |
|
1067 |
self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), a) |
|
1068 |
|
|
1069 |
def testEscape(self): |
|
1070 |
for sep in self._seps: |
|
1071 |
a = ["a", "b\\" + sep + "c", "d"] |
|
1072 |
b = ["a", "b" + sep + "c", "d"] |
|
1073 |
self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b) |
|
1074 |
|
|
1075 |
def testDoubleEscape(self): |
|
1076 |
for sep in self._seps: |
|
1077 |
a = ["a", "b\\\\", "c", "d"] |
|
1078 |
b = ["a", "b\\", "c", "d"] |
|
1079 |
self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b) |
|
1080 |
|
|
1081 |
def testThreeEscape(self): |
|
1082 |
for sep in self._seps: |
|
1083 |
a = ["a", "b\\\\\\" + sep + "c", "d"] |
|
1084 |
b = ["a", "b\\" + sep + "c", "d"] |
|
1085 |
self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b) |
|
1086 |
|
|
1087 |
|
|
1056 | 1088 |
if __name__ == '__main__': |
1057 | 1089 |
testutils.GanetiTestProgram() |
Also available in: Unified diff