Completed
Pull Request — master (#2520)
by
unknown
01:49
created

LineParser.__seperate_by_first_occurrence()   C

Complexity

Conditions 7

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
dl 0
loc 41
rs 5.5
c 0
b 0
f 0
1
from coala_utils.string_processing.StringConverter import StringConverter
2
from coala_utils.string_processing import unescape, convert_to_raw
3
4
5
class LineParser:
6
7
    def __init__(self,
8
                 key_value_delimiters=('=',),
9
                 comment_separators=('#',),
10
                 key_delimiters=(',', ' '),
11
                 section_name_surroundings=None,
12
                 section_override_delimiters=(".",)):
13
        """
14
        Creates a new line parser. Please note that no delimiter or separator
15
        may be an "o" or you may encounter undefined behaviour with the
16
        escapes.
17
18
        :param key_value_delimiters:        Delimiters that delimit a key from
19
                                            a value
20
        :param comment_separators:          Used to initiate a comment
21
        :param key_delimiters:              Delimiters between several keys
22
        :param section_name_surroundings:   Dictionary, e.g. {"[", "]"} means a
23
                                            section name is surrounded by [].
24
                                            If None, {"[": "]"} is used as
25
                                            default.
26
        :param section_override_delimiters: Delimiter for a section override.
27
                                            E.g. "." would mean that
28
                                            section.key is a possible key that
29
                                            puts the key into the section
30
                                            "section" despite of the current
31
                                            section.
32
        """
33
        section_name_surroundings = section_name_surroundings or {"[": "]"}
34
35
        self.key_value_delimiters = key_value_delimiters
36
        self.comment_separators = comment_separators
37
        self.key_delimiters = key_delimiters
38
        self.section_name_surroundings = section_name_surroundings
39
        self.section_override_delimiters = section_override_delimiters
40
41
    def parse(self, line):
42
        """
43
        Note that every value in the returned touple *besides the value* is
44
        unescaped. This is so since the value is meant to be put into a Setting
45
        later thus the escapes may be needed there.
46
47
        :param line: the line to parse
48
        :return:     section_name (empty string if it's no section name),
49
                     [(section_override, key), ...], value, comment
50
        """
51
        line, comment = self.__separate_by_first_occurrence(
52
            line,
53
            self.comment_separators)
54
        comment = unescape(comment)
55
        if line == "":
56
            return '', [], '', comment
57
58
        section_name = unescape(self.__get_section_name(line))
59
        if section_name != '':
60
            return section_name, [], '', comment
61
62
        # Escapes in value might be needed by the bears
63
        keys, value = self.__extract_keys_and_value(line)
64
65
        # Add all the delimiters that stored as tuples
66
        all_delimiters = self.key_value_delimiters
67
        all_delimiters += self.key_delimiters
68
        all_delimiters += self.comment_separators
69
        all_delimiters += self.section_override_delimiters
70
        all_delimiters = "".join(all_delimiters)
71
72
        # Add all keys and values in section_name_surroundings, which is
73
        # stored as a dict
74
        all_delimiters += "".join(self.section_name_surroundings.keys())
75
        all_delimiters += "".join(self.section_name_surroundings.values())
76
77
        value = convert_to_raw(value, all_delimiters)
78
79
        key_touples = []
80
        for key in keys:
81
            key = convert_to_raw(key, all_delimiters)
82
            section, key = self.__separate_by_first_occurrence(
83
                key,
84
                self.section_override_delimiters,
85
                True,
86
                True)
87
            key_touples.append((unescape(section), unescape(key)))
88
89
        return '', key_touples, value, comment
90
91
    @staticmethod
92
    def __separate_by_first_occurrence(string,
93
                                       delimiters,
94
                                       strip_delim=False,
95
                                       return_second_part_nonempty=False):
96
        """
97
        separates a string by the first of all given delimiters. Any whitespace
98
        characters will be stripped away from the parts.
99
100
        :param string:                      The string to separate.
101
        :param delimiters:                  The delimiters.
102
        :param strip_delim:                 Strips the delimiter from the
103
                                            result if true.
104
        :param return_second_part_nonempty: If no delimiter is found and this
105
                                            is true the contents of the string
106
                                            will be returned in the second part
107
                                            of the touple instead of the first
108
                                            one.
109
        :return:                            (first_part, second_part)
110
        """
111
        temp_string = string.replace("\\\\", "oo")
112
        i = temp_string.find("\\")
113
        while i != -1:
114
            temp_string = temp_string[:i] + "oo" + temp_string[i+2:]
115
            i = temp_string.find("\\", i+2)
116
117
        delim_pos = len(string)
118
        used_delim = ""
119
        for delim in delimiters:
120
            pos = temp_string.find(delim)
121
            if 0 <= pos < delim_pos:
122
                delim_pos = pos
123
                used_delim = delim
124
125
        if return_second_part_nonempty and delim_pos == len(string):
126
            return "", string.strip(" \n")
127
128
        return (
129
            string[:delim_pos].strip(" \n"),
130
            string[delim_pos + (len(used_delim) if strip_delim else 0):].strip(
131
                " \n"))
132
133
    def __get_section_name(self, line):
134
        for begin, end in self.section_name_surroundings.items():
135
            if (line[0:len(begin)] == begin and
136
                    line[len(line) - len(end):len(line)] == end):
137
                return line[len(begin):len(line) - len(end)].strip(" \n")
138
139
        return ''
140
141
    def __extract_keys_and_value(self, line):
142
        key_part, value = self.__separate_by_first_occurrence(
143
            line,
144
            self.key_value_delimiters,
145
            True,
146
            True)
147
        keys = list(StringConverter(
148
            key_part,
149
            list_delimiters=self.key_delimiters).__iter__(
150
            remove_backslashes=False))
151
152
        return keys, value
153