Esempio n. 1
0
class LineParserTest(unittest.TestCase):

    def setUp(self):
        self.uut = LineParser(comment_separators=('#', ';'))

    def test_empty_line(self):
        self.check_data_set('')
        self.check_data_set('\n \n \n')

    def test_comment_parsing(self):
        self.check_data_set('# comment only$§\n',
                            output_comment='# comment only$§')
        self.check_data_set('   ; comment only  \n',
                            output_comment='; comment only')
        self.check_data_set('   ; \\comment only  \n',
                            output_comment='; comment only')
        self.check_data_set('#', output_comment='#')

    def test_section_override(self):
        self.check_data_set(r'a.b, \a\.\b\ c=',
                            output_keys=[('a', 'b'), ('', r'\a.\b c')])

    def test_escaping(self):
        self.check_data_set("hello = world\ # yes here's a space",
                            output_keys=[('', 'hello')],
                            output_value='world\\ ',
                            output_comment="# yes here's a space")

    def test_multi_value_parsing(self):
        self.check_data_set(
            'a, b\\ \\=, section.c= = :()&/ \\\\#heres a comment \n',
            output_section='',
            output_keys=[('', 'a'), ('', 'b ='), ('section', 'c')],
            output_value='= :()&/ \\\\',
            output_comment='#heres a comment')

    def test_multi_line_parsing(self):
        self.check_data_set(' a,b,d another value ',
                            output_value='a,b,d another value')
        self.check_data_set(' a,b,d\\= another value ',
                            output_value='a,b,d\\= another value')

    def test_section_name_parsing(self):
        self.check_data_set(' [   a section name   ]      # with comment   \n',
                            'a section name',
                            output_comment='# with comment')
        self.check_data_set(' [   a section name]   ]         \n',
                            'a section name]')
        self.check_data_set(' [   a section name\\]   ]         \n',
                            'a section name]')
        self.check_data_set(' [   a section name\\;   ]         \n',
                            'a section name;')

        self.uut.section_name_surroundings['Section:'] = ''
        self.check_data_set('[  sec]; thats a normal section',
                            output_section='sec',
                            output_comment='; thats a normal section')
        self.check_data_set('  Section:  sEc]\\\\; thats a new section',
                            output_section='sEc]\\',
                            output_comment='; thats a new section')
        self.check_data_set('  Section:  sec]\\\\\\\\; thats a new section',
                            output_section='sec]\\\\',
                            output_comment='; thats a new section')
        self.check_data_set('  Section:  sec]\\\\\\; thats a new section',
                            output_section='sec]\\; thats a new section')

    def test_append_value_parsing(self):
        self.check_data_set('a += b',
                            output_keys=[('', 'a')],
                            output_value='b',
                            output_append=True)
        self.check_data_set('a = b',
                            output_keys=[('', 'a')],
                            output_value='b')
        self.check_data_set('a \\+\\= b',
                            output_value='a \\+\\= b')

    def check_data_set(self,
                       line,
                       output_section='',
                       output_keys=None,
                       output_value='',
                       output_append=False,
                       output_comment=''):
        output_keys = output_keys or []

        section_name, keys, value, append, comment = self.uut._parse(line)

        self.assertEqual(section_name, output_section)
        self.assertEqual(keys, output_keys)
        self.assertEqual(value, output_value)
        self.assertEqual(append, output_append)
        self.assertEqual(comment, output_comment)

    def test_deprecation(self):
        logger = logging.getLogger()

        with self.assertLogs(logger, 'WARNING') as cm:
            self.uut.parse('')

        self.assertRegex(cm.output[0], 'WARNING:root:The parse method of '
                                       'LineParser is deprecated\.*')
Esempio n. 2
0
class ConfParser:
    def __init__(self,
                 key_value_delimiters=('=', ),
                 comment_seperators=('#', ),
                 key_delimiters=(',', ' '),
                 section_name_surroundings=MappingProxyType({'[': ']'}),
                 remove_empty_iter_elements=True,
                 key_value_append_delimiters=('+=', )):
        self.line_parser = LineParser(
            key_value_delimiters,
            comment_seperators,
            key_delimiters,
            section_name_surroundings,
            key_value_append_delimiters=key_value_append_delimiters)

        self.__remove_empty_iter_elements = remove_empty_iter_elements

        # Declare it
        self.sections = None
        self.__rand_helper = None
        self.__init_sections()

    def parse(self, input_data, overwrite=False):
        """
        Parses the input and adds the new data to the existing.

        :param input_data: The filename to parse from.
        :param overwrite:  If True, wipes all existing Settings inside this
                           instance and adds only the newly parsed ones. If
                           False, adds the newly parsed data to the existing
                           one (and overwrites already existing keys with the
                           newly parsed values).
        :return:           A dictionary with (lowercase) section names as keys
                           and their Setting objects as values.
        """
        if os.path.isdir(input_data):
            input_data = os.path.join(input_data, Constants.default_coafile)

        with open(input_data, 'r', encoding='utf-8') as _file:
            lines = _file.readlines()

        if overwrite:
            self.__init_sections()

        self.__parse_lines(lines, input_data)

        return self.sections

    def get_section(self, name, create_if_not_exists=False):
        key = self.__refine_key(name)
        sec = self.sections.get(key, None)
        if sec is not None:
            return sec

        if not create_if_not_exists:
            raise IndexError

        retval = self.sections[key] = Section(str(name))
        return retval

    @staticmethod
    def __refine_key(key):
        return str(key).lower().strip()

    def __add_comment(self, section, comment, origin):
        key = 'comment' + str(self.__rand_helper)
        self.__rand_helper += 1
        section.append(
            Setting(
                key,
                comment,
                origin,
                remove_empty_iter_elements=self.__remove_empty_iter_elements))

    def __parse_lines(self, lines, origin):
        current_section_name = 'default'
        current_section = self.get_section(current_section_name)
        current_keys = []
        no_section = True

        for line in lines:
            (section_name, keys, value, append,
             comment) = self.line_parser._parse(line)

            if comment != '':
                self.__add_comment(current_section, comment, origin)

            if section_name != '':
                no_section = False
                current_section_name = section_name
                current_section = self.get_section(current_section_name, True)
                current_keys = []
                continue

            if comment == '' and keys == [] and value == '':
                self.__add_comment(current_section, '', origin)
                continue

            if keys != []:
                current_keys = keys

            for section_override, key in current_keys:
                if no_section:
                    logging.warning('A setting does not have a section.'
                                    'This is a deprecated feature please '
                                    'put this setting in a section defined'
                                    ' with `[<your-section-name]` in a '
                                    'configuration file.')
                if key == '':
                    continue

                if section_override == '':
                    current_section.add_or_create_setting(
                        Setting(
                            key,
                            value,
                            origin,
                            to_append=append,
                            # Start ignoring PEP8Bear, PycodestyleBear*
                            # they fail to resolve this
                            remove_empty_iter_elements=self.
                            __remove_empty_iter_elements),
                        # Stop ignoring
                        allow_appending=(keys == []))
                else:
                    self.get_section(
                        section_override, True
                    ).add_or_create_setting(
                        Setting(
                            key,
                            value,
                            origin,
                            to_append=append,
                            # Start ignoring PEP8Bear, PycodestyleBear*
                            # they fail to resolve this
                            remove_empty_iter_elements=self.
                            __remove_empty_iter_elements),
                        # Stop ignoring
                        allow_appending=(keys == []))

    def __init_sections(self):
        self.sections = OrderedDict()
        self.sections['default'] = Section('Default')
        self.__rand_helper = 0
Esempio n. 3
0
class ConfParser:

    def __init__(self,
                 key_value_delimiters=('=',),
                 comment_seperators=('#',),
                 key_delimiters=(',', ' '),
                 section_name_surroundings=MappingProxyType({'[': ']'}),
                 remove_empty_iter_elements=True,
                 key_value_append_delimiters=('+=',)):
        self.line_parser = LineParser(
            key_value_delimiters,
            comment_seperators,
            key_delimiters,
            section_name_surroundings,
            key_value_append_delimiters=key_value_append_delimiters)

        self.__remove_empty_iter_elements = remove_empty_iter_elements

        # Declare it
        self.sections = None
        self.__rand_helper = None
        self.__init_sections()

    def parse(self, input_data, overwrite=False):
        """
        Parses the input and adds the new data to the existing.

        :param input_data: The filename to parse from.
        :param overwrite:  If True, wipes all existing Settings inside this
                           instance and adds only the newly parsed ones. If
                           False, adds the newly parsed data to the existing
                           one (and overwrites already existing keys with the
                           newly parsed values).
        :return:           A dictionary with (lowercase) section names as keys
                           and their Setting objects as values.
        """
        if os.path.isdir(input_data):
            input_data = os.path.join(input_data, Constants.default_coafile)

        with open(input_data, 'r', encoding='utf-8') as _file:
            lines = _file.readlines()

        if overwrite:
            self.__init_sections()

        self.__parse_lines(lines, input_data)

        return self.sections

    def get_section(self, name, create_if_not_exists=False):
        key = self.__refine_key(name)
        sec = self.sections.get(key, None)
        if sec is not None:
            return sec

        if not create_if_not_exists:
            raise IndexError

        retval = self.sections[key] = Section(str(name))
        return retval

    @staticmethod
    def __refine_key(key):
        return str(key).lower().strip()

    def __add_comment(self, section, comment, origin):
        key = 'comment' + str(self.__rand_helper)
        self.__rand_helper += 1
        section.append(Setting(
            key,
            comment,
            origin,
            remove_empty_iter_elements=self.__remove_empty_iter_elements))

    def __parse_lines(self, lines, origin):
        current_section_name = 'default'
        current_section = self.get_section(current_section_name)
        current_keys = []
        no_section = True

        for line in lines:
            (section_name,
             keys,
             value,
             append,
             comment) = self.line_parser._parse(line)

            if comment != '':
                self.__add_comment(current_section, comment, origin)

            if section_name != '':
                no_section = False
                current_section_name = section_name
                current_section = self.get_section(current_section_name, True)
                current_keys = []
                continue

            if comment == '' and keys == [] and value == '':
                self.__add_comment(current_section, '', origin)
                continue

            if keys != []:
                current_keys = keys

            for section_override, key in current_keys:
                if no_section:
                    logging.warning('A setting does not have a section.'
                                    'This is a deprecated feature please '
                                    'put this setting in a section defined'
                                    ' with `[<your-section-name]` in a '
                                    'configuration file.')
                if key == '':
                    continue

                if section_override == '':
                    current_section.add_or_create_setting(
                        Setting(key,
                                value,
                                origin,
                                to_append=append,
                                # Start ignoring PEP8Bear, PycodestyleBear*
                                # they fail to resolve this
                                remove_empty_iter_elements=
                                self.__remove_empty_iter_elements),
                                # Stop ignoring
                        allow_appending=(keys == []))
                else:
                    self.get_section(
                        section_override,
                        True).add_or_create_setting(
                            Setting(key,
                                    value,
                                    origin,
                                    to_append=append,
                                    # Start ignoring PEP8Bear, PycodestyleBear*
                                    # they fail to resolve this
                                    remove_empty_iter_elements=
                                    self.__remove_empty_iter_elements),
                                    # Stop ignoring
                            allow_appending=(keys == []))

    def __init_sections(self):
        self.sections = OrderedDict()
        self.sections['default'] = Section('Default')
        self.__rand_helper = 0
Esempio n. 4
0
class LineParserTest(unittest.TestCase):
    def setUp(self):
        self.uut = LineParser(comment_separators=('#', ';'))

    def test_empty_line(self):
        with mock.patch('re.match', false_mock):
            import re
            self.check_data_set('')
            self.check_data_set('\n \n \n')

    def test_comment_parsing(self):
        logger = logging.getLogger()

        with mock.patch('re.match', false_mock):
            import re
            self.check_data_set('# comment only$§\n',
                                output_comment='# comment only$§')
            self.check_data_set('   ; comment only  \n',
                                output_comment='; comment only')
            self.check_data_set('   ; \\comment only  \n',
                                output_comment='; comment only')
            self.assertEqual(re.match.called, True)

        self.check_data_set('#', output_comment='#')
        with self.assertLogs(logger, 'WARNING') as warn:
            self.check_data_set('##\n', output_comment='##')
        self.assertEqual(len(warn.output), 1)
        self.assertEqual(
            warn.output[0], 'WARNING:root:This comment does ' +
            'not have whitespace before or ' + 'after # in: ' + repr('##') +
            '. If you didn\'t mean to make ' +
            'a comment, use a backslash for ' + 'escaping.')

        with mock.patch('re.match', true_mock):
            with self.assertLogs(logger, 'WARNING') as warn:
                self.check_data_set('#A\n', output_comment='#A')
            self.assertEqual(
                warn.output[0], 'WARNING:root:This comment does ' +
                'not have whitespace before or ' + 'after # in: ' +
                repr('#A') + '. If you didn\'t mean to make ' +
                'a comment, use a backslash for ' + 'escaping.')

    def test_section_override(self):
        self.check_data_set(r'a.b, \a\.\b\ c=',
                            output_keys=[('a', 'b'), ('', r'\a.\b c')])

    def test_escaping(self):
        self.check_data_set("hello = world\ # yes here's a space",
                            output_keys=[('', 'hello')],
                            output_value='world\\ ',
                            output_comment="# yes here's a space")

    def test_multi_value_parsing(self):
        self.check_data_set(
            'a, b\\ \\=, section.c= = :()&/ \\\\#heres a comment \n',
            output_section='',
            output_keys=[('', 'a'), ('', 'b ='), ('section', 'c')],
            output_value='= :()&/ \\\\',
            output_comment='#heres a comment')

    def test_multi_line_parsing(self):
        self.check_data_set(' a,b,d another value ',
                            output_value='a,b,d another value')
        self.check_data_set(' a,b,d\\= another value ',
                            output_value='a,b,d\\= another value')

    def test_section_name_parsing(self):
        self.check_data_set(' [   a section name   ]      # with comment   \n',
                            'a section name',
                            output_comment='# with comment')
        self.check_data_set(' [   a section name]   ]         \n',
                            'a section name]')
        self.check_data_set(' [   a section name\\]   ]         \n',
                            'a section name]')
        self.check_data_set(' [   a section name\\;   ]         \n',
                            'a section name;')

        self.uut.section_name_surroundings['Section:'] = ''
        self.check_data_set('[  sec]; thats a normal section',
                            output_section='sec',
                            output_comment='; thats a normal section')
        self.check_data_set('  Section:  sEc]\\\\; thats a new section',
                            output_section='sEc]\\',
                            output_comment='; thats a new section')
        self.check_data_set('  Section:  sec]\\\\\\\\; thats a new section',
                            output_section='sec]\\\\',
                            output_comment='; thats a new section')
        self.check_data_set('  Section:  sec]\\\\\\; thats a new section',
                            output_section='sec]\\; thats a new section')

    def test_append_value_parsing(self):
        self.check_data_set('a += b',
                            output_keys=[('', 'a')],
                            output_value='b',
                            output_append=True)
        self.check_data_set('a = b', output_keys=[('', 'a')], output_value='b')
        self.check_data_set('a \\+\\= b', output_value='a \\+\\= b')

    def check_data_set(self,
                       line,
                       output_section='',
                       output_keys=None,
                       output_value='',
                       output_append=False,
                       output_comment=''):
        output_keys = output_keys or []

        section_name, keys, value, append, comment = self.uut._parse(line)

        self.assertEqual(section_name, output_section)
        self.assertEqual(keys, output_keys)
        self.assertEqual(value, output_value)
        self.assertEqual(append, output_append)
        self.assertEqual(comment, output_comment)

    def test_deprecation(self):
        logger = logging.getLogger()

        with self.assertLogs(logger, 'WARNING') as cm:
            self.uut.parse('')

        self.assertRegex(
            cm.output[0], 'WARNING:root:The parse method of '
            'LineParser is deprecated\.*')