3 This module implements a very simple database on the flat ASCII files.
7 # Flad restriction error
8 checking_error = "flad.checking_error"
10 # Default key/value separator
16 Class to represent memory database.
17 FLAD database is a list of records,
18 where every record is a dictionary.
21 # Field and record separators are ALWAYS newline. It cannot be changed
22 # without extensive rewriting of this module
23 # field_sep = rec_sep = '\n'
25 def __init__(self, check_record_func = None, key_sep = def_keysep):
27 self.check_record_func = check_record_func
28 self.key_sep = key_sep
34 def check_record(self, record): # Method can be overriden in subclasses
35 if self.check_record_func:
36 if callable(self.check_record_func):
37 return self.check_record_func(self, record)
39 raise TypeError("non-callable restriction function")
44 def checking_error(self): # Method can be overriden in subclasses
48 def __setitem__(self, i, item):
49 if not self.check_record(item):
52 list.__setitem__(self, i, item)
55 def __setslice__(self, i, j, v_list):
59 if not self.check_record(item):
61 del copy_list[copy_list.index(item)]
62 list.__setslice__(self, i, j, copy_list)
65 def append(self, item):
66 if not self.check_record(item):
69 list.append(self, item)
72 def insert(self, i, item):
73 if not self.check_record(item):
76 list.insert(self, i, item)
79 def split_key(self, line):
81 Split input line to key/value pair and add the pair to dictionary
83 ###line = line.lstrip() # Do not rstrip - if empty value, this will remove space from key
85 line = line[:-1] # Chop
87 l = line.split(self.key_sep, 1) # Just split to key and reminder
91 def __parse_line(self, record, line): # Internal function
92 if line == '\n': # Empty line is record separator (but there cannot be more than 1 empty lines)
93 if record: # This helps to skip all empty lines
94 self.append(record) # Check and add the record to list
95 return 1 # Signal to stop filling the record and start a new one
98 key, value = self.split_key(line)
99 if key in record.keys(): # This have to be done with check_record
100 # but the record is not complete now,
101 # so it is not ready to be checked :(
102 # And, of course, two keys with the same name
103 # cannot be added to dictionary
104 raise KeyError("field key \"" + key + "\" already in record")
111 def create_new_record(self): # Method can be overriden in subclasses
115 def feed(self, record, line): # Method can be overriden in subclasses
117 if self.wait_comment:
118 if line.strip() == '':
119 self.comment = self.comment + '\n'
123 elif line.lstrip()[0] == '#':
124 self.comment = self.comment + line
128 self.wait_comment = 0
129 # Fallback to parsing
131 return self.__parse_line(record, line)
139 def load_file(self, f):
141 Load a database from file as a list of records.
142 Every record is a dictionary of key/value pairs.
143 The file is reading as whole - this is much faster, but require
147 if type(f) == type(''): # If f is string - use it as file's name
148 infile = open(f, 'r')
150 infile = f # else assume it is opened file (fileobject) or
151 # "compatible" object (must has readline() methods)
154 lines = infile.readlines()
157 if type(f) == type(''): # If f was opened - close it
160 record = self.create_new_record()
163 if self.feed(record, line):
164 record = self.create_new_record()
166 # Close record on EOF without empty line
168 self.feed(record, None)
171 def load_from_file(self, f):
173 Load a database from file as a list of records.
174 Every record is a dictionary of key/value pairs.
175 The file is reading line by line - this is much slower, but do not
176 require so much memory. (On systems with limited virtual memory,
177 like DOS, it is even faster - for big files)
180 if type(f) == type(''): # If f is string - use it as file's name
181 infile = open(f, 'r')
183 infile = f # else assume it is opened file (fileobject) or
184 # "compatible" object (must has readline() method)
186 record = self.create_new_record()
189 line = infile.readline()
192 if self.feed(record, line):
193 record = self.create_new_record()
195 line = infile.readline()
198 if type(f) == type(''): # If f was opened - close it
201 # Close record on EOF without empty line
203 self.feed(record, None)
206 def store_to_file(self, f):
207 if type(f) == type(''): # If f is string - use it as file's name
208 outfile = open(f, 'w')
210 outfile = f # else assume it is opened file (fileobject) or
211 # "compatible" object (must has write() method)
213 flush_record = 0 # Do not close record on 1st loop
215 if self.comment != '':
216 outfile.write(self.comment)
219 copy_rec = record.copy() # Make a copy to delete keys
222 outfile.write('\n') # Close record
224 flush_record = 1 # Set flag for all but 1st record
227 for key in copy_rec.keys():
228 outfile.write(key + self.key_sep + copy_rec[key] + '\n')
231 if type(f) == type(''): # If f was opened - close it
235 def load_file(f, check_record_func = None):
237 Create a database object and load it from file
240 db = Flad(check_record_func)
246 def load_from_file(f, check_record_func = None):
248 Create a database object and load it from file
251 db = Flad(check_record_func)