3 This module implements a very simple database on the flat ASCII files.
7 # Flad restriction error
8 class checking_error(Exception):
11 # Default key/value separator
17 Class to represent memory database.
18 FLAD database is a list of records,
19 where every record is a dictionary.
22 # Field and record separators are ALWAYS newline. It cannot be changed
23 # without extensive rewriting of this module
24 # field_sep = rec_sep = '\n'
26 def __init__(self, check_record_func = None, key_sep = def_keysep):
28 self.check_record_func = check_record_func
29 self.key_sep = key_sep
35 def check_record(self, record): # Method can be overriden in subclasses
36 if self.check_record_func:
37 if callable(self.check_record_func):
38 return self.check_record_func(self, record)
40 raise TypeError("non-callable restriction function")
45 def checking_error(self): # Method can be overriden in subclasses
49 def __setitem__(self, i, item):
50 if not self.check_record(item):
53 list.__setitem__(self, i, item)
56 def __setslice__(self, i, j, v_list):
60 if not self.check_record(item):
62 del copy_list[copy_list.index(item)]
63 list.__setslice__(self, i, j, copy_list)
66 def append(self, item):
67 if not self.check_record(item):
70 list.append(self, item)
73 def insert(self, i, item):
74 if not self.check_record(item):
77 list.insert(self, i, item)
80 def split_key(self, line):
82 Split input line to key/value pair and add the pair to dictionary
84 ###line = line.lstrip() # Do not rstrip - if empty value, this will remove space from key
86 line = line[:-1] # Chop
88 l = line.split(self.key_sep, 1) # Just split to key and reminder
92 def __parse_line(self, record, line): # Internal function
93 if line == '\n': # Empty line is record separator (but there cannot be more than 1 empty lines)
94 if record: # This helps to skip all empty lines
95 self.append(record) # Check and add the record to list
96 return 1 # Signal to stop filling the record and start a new one
99 key, value = self.split_key(line)
100 if key in record.keys(): # This have to be done with check_record
101 # but the record is not complete now,
102 # so it is not ready to be checked :(
103 # And, of course, two keys with the same name
104 # cannot be added to dictionary
105 raise KeyError("field key \"" + key + "\" already in record")
112 def create_new_record(self): # Method can be overriden in subclasses
116 def feed(self, record, line): # Method can be overriden in subclasses
118 if self.wait_comment:
119 if line.strip() == '':
120 self.comment = self.comment + '\n'
124 elif line.lstrip()[0] == '#':
125 self.comment = self.comment + line
129 self.wait_comment = 0
130 # Fallback to parsing
132 return self.__parse_line(record, line)
140 def load_file(self, f):
142 Load a database from file as a list of records.
143 Every record is a dictionary of key/value pairs.
144 The file is reading as whole - this is much faster, but require
148 if type(f) == type(''): # If f is string - use it as file's name
149 infile = open(f, 'r')
151 infile = f # else assume it is opened file (fileobject) or
152 # "compatible" object (must has readline() methods)
155 lines = infile.readlines()
158 if type(f) == type(''): # If f was opened - close it
161 record = self.create_new_record()
164 if self.feed(record, line):
165 record = self.create_new_record()
167 # Close record on EOF without empty line
169 self.feed(record, None)
172 def load_from_file(self, f):
174 Load a database from file as a list of records.
175 Every record is a dictionary of key/value pairs.
176 The file is reading line by line - this is much slower, but do not
177 require so much memory. (On systems with limited virtual memory,
178 like DOS, it is even faster - for big files)
181 if type(f) == type(''): # If f is string - use it as file's name
182 infile = open(f, 'r')
184 infile = f # else assume it is opened file (fileobject) or
185 # "compatible" object (must has readline() method)
187 record = self.create_new_record()
190 line = infile.readline()
193 if self.feed(record, line):
194 record = self.create_new_record()
196 line = infile.readline()
199 if type(f) == type(''): # If f was opened - close it
202 # Close record on EOF without empty line
204 self.feed(record, None)
207 def store_to_file(self, f):
208 if type(f) == type(''): # If f is string - use it as file's name
209 outfile = open(f, 'w')
211 outfile = f # else assume it is opened file (fileobject) or
212 # "compatible" object (must has write() method)
214 flush_record = 0 # Do not close record on 1st loop
216 if self.comment != '':
217 outfile.write(self.comment)
220 copy_rec = record.copy() # Make a copy to delete keys
223 outfile.write('\n') # Close record
225 flush_record = 1 # Set flag for all but 1st record
228 for key in list(copy_rec.keys()):
229 outfile.write(key + self.key_sep + copy_rec[key] + '\n')
232 if type(f) == type(''): # If f was opened - close it
236 def load_file(f, check_record_func = None):
238 Create a database object and load it from file
241 db = Flad(check_record_func)
247 def load_from_file(f, check_record_func = None):
249 Create a database object and load it from file
252 db = Flad(check_record_func)