3 This module implements a very simple database on the flat ASCII files.
5 Written by Broytman. Copyright (C) 1997-2005 PhiloSoft Design
10 from UserList import UserList
13 # Flad restriction error
14 checking_error = "flad.checking_error"
16 # Default key/value separator
22 Class to represent memory database.
23 FLAD database is a list of records,
24 where every record is a dictionary.
27 # Field and record separators are ALWAYS newline. It cannot be changed
28 # without extensive rewriting of this module
29 # field_sep = rec_sep = '\n'
31 def __init__(self, check_record_func = None, key_sep = def_keysep):
32 UserList.__init__(self)
33 self.check_record_func = check_record_func
34 self.key_sep = key_sep
40 def check_record(self, record): # Method can be overriden in subclasses
41 if self.check_record_func:
42 if callable(self.check_record_func):
43 return self.check_record_func(self, record)
45 raise TypeError, "non-callable restriction function"
50 def checking_error(self): # Method can be overriden in subclasses
54 def __setitem__(self, i, item):
55 if not self.check_record(item):
58 UserList.__setitem__(self, i, item)
61 def __setslice__(self, i, j, list):
65 if not self.check_record(item):
67 del copy_list[copy_list.index(item)]
68 UserList.__setslice__(self, i, j, copy_list)
71 def append(self, item):
72 if not self.check_record(item):
75 UserList.append(self, item)
78 def insert(self, i, item):
79 if not self.check_record(item):
82 UserList.insert(self, i, item)
85 def split_key(self, line):
87 Split input line to key/value pair and add the pair to dictionary
89 ###line = string.lstrip(line) # Do not rstrip - if empty value, this will remove space from key
91 line = line[:-1] # Chop
93 l = string.split(line, self.key_sep, 1) # Just split to key and reminder
97 def __parse_line(self, record, line): # Internal function
98 if line == '\n': # Empty line is record separator (but there cannot be more than 1 empty lines)
99 if record: # This helps to skip all empty lines
100 self.append(record) # Check and add the record to list
101 return 1 # Signal to stop filling the record and start a new one
104 key, value = self.split_key(line)
105 if key in record.keys(): # This have to be done with check_record
106 # but the record is not complete now,
107 # so it is not ready to be checked :(
108 # And, of course, two keys with the same name
109 # cannot be added to dictionary
110 raise KeyError, "field key \"" + key + "\" already in record"
117 def create_new_record(self): # Method can be overriden in subclasses
121 def feed(self, record, line): # Method can be overriden in subclasses
123 if self.wait_comment:
124 if string.strip(line) == '':
125 self.comment = self.comment + '\n'
129 elif string.lstrip(line)[0] == '#':
130 self.comment = self.comment + line
134 self.wait_comment = 0
135 # Fallback to parsing
137 return self.__parse_line(record, line)
145 def load_file(self, f):
147 Load a database from file as a list of records.
148 Every record is a dictionary of key/value pairs.
149 The file is reading as whole - this is much faster, but require
153 if type(f) == type(''): # If f is string - use it as file's name
154 infile = open(f, 'r')
156 infile = f # else assume it is opened file (fileobject) or
157 # "compatible" object (must has readline() methods)
160 lines = infile.readlines()
163 if type(f) == type(''): # If f was opened - close it
166 record = self.create_new_record()
169 if self.feed(record, line):
170 record = self.create_new_record()
172 # Close record on EOF without empty line
174 self.feed(record, None)
177 def load_from_file(self, f):
179 Load a database from file as a list of records.
180 Every record is a dictionary of key/value pairs.
181 The file is reading line by line - this is much slower, but do not
182 require so much memory. (On systems with limited virtual memory,
183 like DOS, it is even faster - for big files)
186 if type(f) == type(''): # If f is string - use it as file's name
187 infile = open(f, 'r')
189 infile = f # else assume it is opened file (fileobject) or
190 # "compatible" object (must has readline() method)
192 record = self.create_new_record()
195 line = infile.readline()
198 if self.feed(record, line):
199 record = self.create_new_record()
201 line = infile.readline()
204 if type(f) == type(''): # If f was opened - close it
207 # Close record on EOF without empty line
209 self.feed(record, None)
212 def store_to_file(self, f):
213 if type(f) == type(''): # If f is string - use it as file's name
214 outfile = open(f, 'w')
216 outfile = f # else assume it is opened file (fileobject) or
217 # "compatible" object (must has write() method)
219 flush_record = 0 # Do not close record on 1st loop
221 if self.comment <> '':
222 outfile.write(self.comment)
225 copy_rec = record.copy() # Make a copy to delete keys
228 outfile.write('\n') # Close record
230 flush_record = 1 # Set flag for all but 1st record
233 for key in copy_rec.keys():
234 outfile.write(key + self.key_sep + copy_rec[key] + '\n')
237 if type(f) == type(''): # If f was opened - close it
241 def load_file(f, check_record_func = None):
243 Create a database object and load it from file
246 db = Flad(check_record_func)
252 def load_from_file(f, check_record_func = None):
254 Create a database object and load it from file
257 db = Flad(check_record_func)