866 If s is not a valid boolean, returns None. |
866 If s is not a valid boolean, returns None. |
867 """ |
867 """ |
868 return _booleans.get(s.lower(), None) |
868 return _booleans.get(s.lower(), None) |
869 |
869 |
870 |
870 |
|
871 def parselist(value): |
|
872 """parse a configuration value as a list of comma/space separated strings |
|
873 |
|
874 >>> parselist(b'this,is "a small" ,test') |
|
875 ['this', 'is', 'a small', 'test'] |
|
876 """ |
|
877 |
|
878 def _parse_plain(parts, s, offset): |
|
879 whitespace = False |
|
880 while offset < len(s) and ( |
|
881 s[offset : offset + 1].isspace() or s[offset : offset + 1] == b',' |
|
882 ): |
|
883 whitespace = True |
|
884 offset += 1 |
|
885 if offset >= len(s): |
|
886 return None, parts, offset |
|
887 if whitespace: |
|
888 parts.append(b'') |
|
889 if s[offset : offset + 1] == b'"' and not parts[-1]: |
|
890 return _parse_quote, parts, offset + 1 |
|
891 elif s[offset : offset + 1] == b'"' and parts[-1][-1:] == b'\\': |
|
892 parts[-1] = parts[-1][:-1] + s[offset : offset + 1] |
|
893 return _parse_plain, parts, offset + 1 |
|
894 parts[-1] += s[offset : offset + 1] |
|
895 return _parse_plain, parts, offset + 1 |
|
896 |
|
897 def _parse_quote(parts, s, offset): |
|
898 if offset < len(s) and s[offset : offset + 1] == b'"': # "" |
|
899 parts.append(b'') |
|
900 offset += 1 |
|
901 while offset < len(s) and ( |
|
902 s[offset : offset + 1].isspace() |
|
903 or s[offset : offset + 1] == b',' |
|
904 ): |
|
905 offset += 1 |
|
906 return _parse_plain, parts, offset |
|
907 |
|
908 while offset < len(s) and s[offset : offset + 1] != b'"': |
|
909 if ( |
|
910 s[offset : offset + 1] == b'\\' |
|
911 and offset + 1 < len(s) |
|
912 and s[offset + 1 : offset + 2] == b'"' |
|
913 ): |
|
914 offset += 1 |
|
915 parts[-1] += b'"' |
|
916 else: |
|
917 parts[-1] += s[offset : offset + 1] |
|
918 offset += 1 |
|
919 |
|
920 if offset >= len(s): |
|
921 real_parts = _configlist(parts[-1]) |
|
922 if not real_parts: |
|
923 parts[-1] = b'"' |
|
924 else: |
|
925 real_parts[0] = b'"' + real_parts[0] |
|
926 parts = parts[:-1] |
|
927 parts.extend(real_parts) |
|
928 return None, parts, offset |
|
929 |
|
930 offset += 1 |
|
931 while offset < len(s) and s[offset : offset + 1] in [b' ', b',']: |
|
932 offset += 1 |
|
933 |
|
934 if offset < len(s): |
|
935 if offset + 1 == len(s) and s[offset : offset + 1] == b'"': |
|
936 parts[-1] += b'"' |
|
937 offset += 1 |
|
938 else: |
|
939 parts.append(b'') |
|
940 else: |
|
941 return None, parts, offset |
|
942 |
|
943 return _parse_plain, parts, offset |
|
944 |
|
945 def _configlist(s): |
|
946 s = s.rstrip(b' ,') |
|
947 if not s: |
|
948 return [] |
|
949 parser, parts, offset = _parse_plain, [b''], 0 |
|
950 while parser: |
|
951 parser, parts, offset = parser(parts, s, offset) |
|
952 return parts |
|
953 |
|
954 if value is not None and isinstance(value, bytes): |
|
955 result = _configlist(value.lstrip(b' ,\n')) |
|
956 else: |
|
957 result = value |
|
958 return result or [] |
|
959 |
|
960 |
871 def evalpythonliteral(s): |
961 def evalpythonliteral(s): |
872 """Evaluate a string containing a Python literal expression""" |
962 """Evaluate a string containing a Python literal expression""" |
873 # We could backport our tokenizer hack to rewrite '' to u'' if we want |
963 # We could backport our tokenizer hack to rewrite '' to u'' if we want |
874 if pycompat.ispy3: |
964 if pycompat.ispy3: |
875 return ast.literal_eval(s.decode('latin1')) |
965 return ast.literal_eval(s.decode('latin1')) |