Version 1.0
Copyright © 2022 Lowell D. Thomas
Python APG
 … an ABNF Parser Generator
ini_file.py
Go to the documentation of this file.
1 ''' @file examples/ini_file/ini_file.py
2 @brief The ini file class for parsing an ini file into section/key/values.
3 '''
4 import sys
5 import os
6 from pprint import pprint
7 # add the current working directory to the path
8 # DO NOT MOVE THE FOLLOWING STATEMENT
9 # if using autopep8 formatter, for example, set argument '--ignore=E402'
10 sys.path.append(os.getcwd())
11 from apg_py.lib import utilities as utils
12 from apg_py.lib.parser import Parser
13 # from apg.lib.trace import Trace
14 from apg_py.lib.ast import Ast
15 import examples.ini_file.grammar as grammar
18 
19 # Note: The ABNF syntax for the ini file parser is in grammar.abnf.
20 # The grammar object, grammar.py was generated with
21 # python3 apg.py -i examples/ini_file/grammar.abnf
22 
23 
24 class IniFile:
25 
26  def __init__(self):
27  self.__have_file__have_file = False
28  self.__no_file__no_file = 'Must first call IniFile.get_ini_file(file_name).'
29 
30  def help(self):
31  display = '''IniFile is a class for parsing and retrieving named values from an ini formatted file.
32 This format is similar to, but has some variations from, the WikiPedia description at
33 https://en.wikipedia.org/wiki/INI_file.
34 
35 - Named values may be in an unnamed "global" section and/or in named sections.
36 - Each section consists of lines of key/value pairs.
37 - Section names are case sensitive and are alphanumeric with underscores.
38 - Section names are enclosed in square brackets and must begin in the first column of a line.
39 - Spaces are allowed, e.g. [ Section1 ]
40 - Keys are case sensitive and are alphanumeric with underscores.
41 - Values are multi-valued lists.
42 - Multiple occurrances of a section name will merge the two sections.
43 - Multiple occurrances of a key name within a section will merge the lists of values.
44 - Blank lines are ignored.
45 - Comments begin with a semicolon, ";", and continue to the end of the line.
46 - Values may be boolean, integers or strings.
47 - True booleans may be true or yes, case insensitive.
48 - False booleans may be false or no, case insensitive.
49 - Null values may be null or void, case insensitive.
50 - The keywords true, false, yes, no, null, void are reserved
51  and must be quoted if needed as strings.
52 - Strings with blanks characters must be single or double quoted.
53 - Escaped characters may be used within quoted or unquoted strings.
54 
55 Escaped characters:
56 \\\\ single back slash, x5C
57 \\" double quote, x22
58 \\# hash or number sign, x23
59 \\' single quote, x27
60 \\, comma, x2C
61 \\: colon, x3A
62 \\; semicolon, x3B
63 \\= equal sign, x3D
64 \\b blank or space, x20
65 \\t tab, x09
66 \\n line feed, x0A
67 \\r carriage return, x0D
68 \\xhh hexadecimal character code, xhh, h = 0-9A-F
69 \\uhhhh Unicode character code, xhhhh, h = 0-9A-F
70 
71 Example:
72 ; the global section
73 url = 'https://en.wikipedia.org/wiki/INI_file'
74 IP = '198.162.1.1'
75 other = null
76 [section1] ; start section A
77 numbers = 1, 2,3, 4
78 use_them = yes
79 [ SectionB ] ; start section B
80 hamburgers = '$1.99', '$2.99', '$3.99'
81 [ section1 ] ; continue with section A
82 numbers = 5,6 ; add some more numbers to the list
83 
84 '''
85  return display
86 
87  def parse_ini_file(self, fname):
88  fd = open(fname, 'r')
89  input = fd.read()
90  fd.close()
91  parser = Parser(grammar)
92  # Trace(parser, mode='dc')
93  parser.add_callbacks({'line-end': pcb.line_end})
94  parser.add_callbacks({'bad-section-line': pcb.bad_section_line})
95  parser.add_callbacks({'bad-value-line': pcb.bad_value_line})
96  parser.add_callbacks({'bad-blank-line': pcb.bad_blank_line})
97  ast = Ast(parser)
98  ast.add_callback('section-name', acb.section_name)
99  ast.add_callback('key-name', acb.key_name)
100  ast.add_callback('value', acb.value)
101  ast.add_callback('d-quoted-value', acb.d_value)
102  ast.add_callback('s-quoted-value', acb.s_value)
103  ast.add_callback('string', acb.string_value)
104  ast.add_callback('true', acb.true_value)
105  ast.add_callback('false', acb.false_value)
106  ast.add_callback('null', acb.null_value)
107  ast.add_callback('number', acb.number_value)
108  data = {}
109  data['line_no'] = 0
110  data['errors'] = []
111  result = parser.parse(utils.string_to_tuple(input), user_data=data)
112  if(not result.success):
113  # ABNF syntax is designed so that this should never happen
114  raise Exception('internal error - parser failed')
115  if(len(data['errors'])):
116  # display errors
117  pprint(data['errors'])
118  raise Exception('ini file syntax errors found')
119  self.__data__data = {}
120  self.__data__data['current_section'] = None
121  self.__data__data['current_key'] = None
122  self.__data__data['global'] = {}
123  self.__data__data['sections'] = {}
124  ast.translate(self.__data__data)
125  self.__have_file__have_file = True
126  # print()
127  # print('IniFile constructor')
128  # pprint(self.__data, sort_dicts=False)
129 
130  def get_keys(self):
131  '''Get a list of the global key names.
132  @returns Returns a list, possibly empty, of global key names.
133  '''
134  if(not self.__have_file__have_file):
135  raise Exception(self.__no_file__no_file)
136  keys = []
137  for key, value in self.__data__data['global'].items():
138  keys.append(key)
139  return keys
140 
141  def get_values(self, key):
142  '''Get the list of values for an global key name.
143  @param key The name of the key to get values for.
144  @returns Returns a list, possibly empty, of values.
145  '''
146  if(not self.__have_file__have_file):
147  raise Exception(self.__no_file__no_file)
148  values = []
149  key_values = self.__data__data['global'].get(key)
150  if(key_values):
151  for value in key_values:
152  values.append(value)
153  return values
154 
155  def get_sections(self):
156  '''Get a list of the section names.
157  @returns Returns a list, possibly empty, of section names.
158  '''
159  if(not self.__have_file__have_file):
160  raise Exception(self.__no_file__no_file)
161  sections = []
162  for key, value in self.__data__data['sections'].items():
163  sections.append(key)
164  return sections
165 
166  def get_section_keys(self, section):
167  '''Get a list of key names in the named section.
168  @param section The section name to find the key names in.
169  @returns Returns a list, possibly empty, of key names.
170  '''
171  if(not self.__have_file__have_file):
172  raise Exception(self.__no_file__no_file)
173  keys = []
174  if(self.__data__data['sections'].get(section)):
175  for key, value in self.__data__data['sections'][section].items():
176  keys.append(key)
177  return keys
178 
179  def get_section_values(self, section, key):
180  '''Get a list of values for the named key in the named section.
181  @param section The section name to find the key in.
182  @param key The key name to find the list of values for.
183  @returns Returns a list, possibly empty, of values.
184  '''
185  if(not self.__have_file__have_file):
186  raise Exception(self.__no_file__no_file)
187  values = []
188  if(self.__data__data['sections'].get(section)):
189  key_values = self.__data__data['sections'][section].get(key)
190  if(key_values):
191  for value in key_values:
192  values.append(value)
193  return values
A class for capturing the AST as the parser traverses the parse tree.
Definition: ast.py:69
The Parser class for parsing an APG grammar.
Definition: parser.py:60
def get_keys(self)
Get a list of the global key names.
Definition: ini_file.py:130
def get_values(self, key)
Get the list of values for an global key name.
Definition: ini_file.py:141
def get_section_values(self, section, key)
Get a list of values for the named key in the named section.
Definition: ini_file.py:179
def get_sections(self)
Get a list of the section names.
Definition: ini_file.py:155
def parse_ini_file(self, fname)
Definition: ini_file.py:87
def get_section_keys(self, section)
Get a list of key names in the named section.
Definition: ini_file.py:166
Python APG, Version 1.0, is licensed under the 2-Clause BSD License,
an Open Source Initiative Approved License.