3 This module implements a very simple database on the flat ASCII files.
10 # Flad restriction error
11 checking_error = "flad.checking_error"
13 # Default key/value separator
19 Class to represent memory database.
20 FLAD database is a list of records,
21 where every record is a dictionary.
24 # Field and record separators are ALWAYS newline. It cannot be changed
25 # without extensive rewriting of this module
26 # field_sep = rec_sep = '\n'
28 def __init__(self, check_record_func = None, key_sep = def_keysep):
30 self.check_record_func = check_record_func
31 self.key_sep = key_sep
37 def check_record(self, record): # Method can be overriden in subclasses
38 if self.check_record_func:
39 if callable(self.check_record_func):
40 return self.check_record_func(self, record)
42 raise TypeError("non-callable restriction function")
47 def checking_error(self): # Method can be overriden in subclasses
51 def __setitem__(self, i, item):
52 if not self.check_record(item):
55 list.__setitem__(self, i, item)
58 def __setslice__(self, i, j, v_list):
62 if not self.check_record(item):
64 del copy_list[copy_list.index(item)]
65 list.__setslice__(self, i, j, copy_list)
68 def append(self, item):
69 if not self.check_record(item):
72 list.append(self, item)
75 def insert(self, i, item):
76 if not self.check_record(item):
79 list.insert(self, i, item)
82 def split_key(self, line):
84 Split input line to key/value pair and add the pair to dictionary
86 ###line = string.lstrip(line) # Do not rstrip - if empty value, this will remove space from key
88 line = line[:-1] # Chop
90 l = line.split(self.key_sep, 1) # Just split to key and reminder
94 def __parse_line(self, record, line): # Internal function
95 if line == '\n': # Empty line is record separator (but there cannot be more than 1 empty lines)
96 if record: # This helps to skip all empty lines
97 self.append(record) # Check and add the record to list
98 return 1 # Signal to stop filling the record and start a new one
101 key, value = self.split_key(line)
102 if key in record.keys(): # This have to be done with check_record
103 # but the record is not complete now,
104 # so it is not ready to be checked :(
105 # And, of course, two keys with the same name
106 # cannot be added to dictionary
107 raise KeyError("field key \"" + key + "\" already in record")
114 def create_new_record(self): # Method can be overriden in subclasses
118 def feed(self, record, line): # Method can be overriden in subclasses
120 if self.wait_comment:
121 if string.strip(line) == '':
122 self.comment = self.comment + '\n'
126 elif string.lstrip(line)[0] == '#':
127 self.comment = self.comment + line
131 self.wait_comment = 0
132 # Fallback to parsing
134 return self.__parse_line(record, line)
142 def load_file(self, f):
144 Load a database from file as a list of records.
145 Every record is a dictionary of key/value pairs.
146 The file is reading as whole - this is much faster, but require
150 if type(f) == type(''): # If f is string - use it as file's name
151 infile = open(f, 'r')
153 infile = f # else assume it is opened file (fileobject) or
154 # "compatible" object (must has readline() methods)
157 lines = infile.readlines()
160 if type(f) == type(''): # If f was opened - close it
163 record = self.create_new_record()
166 if self.feed(record, line):
167 record = self.create_new_record()
169 # Close record on EOF without empty line
171 self.feed(record, None)
174 def load_from_file(self, f):
176 Load a database from file as a list of records.
177 Every record is a dictionary of key/value pairs.
178 The file is reading line by line - this is much slower, but do not
179 require so much memory. (On systems with limited virtual memory,
180 like DOS, it is even faster - for big files)
183 if type(f) == type(''): # If f is string - use it as file's name
184 infile = open(f, 'r')
186 infile = f # else assume it is opened file (fileobject) or
187 # "compatible" object (must has readline() method)
189 record = self.create_new_record()
192 line = infile.readline()
195 if self.feed(record, line):
196 record = self.create_new_record()
198 line = infile.readline()
201 if type(f) == type(''): # If f was opened - close it
204 # Close record on EOF without empty line
206 self.feed(record, None)
209 def store_to_file(self, f):
210 if type(f) == type(''): # If f is string - use it as file's name
211 outfile = open(f, 'w')
213 outfile = f # else assume it is opened file (fileobject) or
214 # "compatible" object (must has write() method)
216 flush_record = 0 # Do not close record on 1st loop
218 if self.comment != '':
219 outfile.write(self.comment)
222 copy_rec = record.copy() # Make a copy to delete keys
225 outfile.write('\n') # Close record
227 flush_record = 1 # Set flag for all but 1st record
230 for key in copy_rec.keys():
231 outfile.write(key + self.key_sep + copy_rec[key] + '\n')
234 if type(f) == type(''): # If f was opened - close it
238 def load_file(f, check_record_func = None):
240 Create a database object and load it from file
243 db = Flad(check_record_func)
249 def load_from_file(f, check_record_func = None):
251 Create a database object and load it from file
254 db = Flad(check_record_func)