]> git.phdru.name Git - m_lib.git/commitdiff
Import m_lib/flad
authorOleg Broytman <phd@phdru.name>
Sat, 30 Nov 2013 22:08:50 +0000 (02:08 +0400)
committerOleg Broytman <phd@phdru.name>
Sat, 30 Nov 2013 22:11:10 +0000 (02:11 +0400)
19 files changed:
MANIFEST.in [new file with mode: 0644]
README-flad.txt [new file with mode: 0644]
m_lib/flad/__init__.py [new file with mode: 0644]
m_lib/flad/flad.py [new file with mode: 0644]
m_lib/flad/fladc.py [new file with mode: 0644]
m_lib/flad/fladm.py [new file with mode: 0644]
m_lib/flad/fladw.py [new file with mode: 0644]
m_lib/flad/test/__init__.py [new file with mode: 0644]
m_lib/flad/test/comment.txt [new file with mode: 0644]
m_lib/flad/test/test.cfg [new file with mode: 0644]
m_lib/flad/test/test.txt [new file with mode: 0644]
m_lib/flad/test/test1.py [new file with mode: 0755]
m_lib/flad/test/test2.py [new file with mode: 0755]
m_lib/flad/test/test3.py [new file with mode: 0755]
m_lib/flad/test/test4.py [new file with mode: 0755]
m_lib/flad/test/test5.py [new file with mode: 0755]
m_lib/flad/test/test6.py [new file with mode: 0755]
m_lib/flad/test/test7.py [new file with mode: 0755]
setup.py

diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644 (file)
index 0000000..4fc8ecb
--- /dev/null
@@ -0,0 +1,2 @@
+include MANIFEST.in
+include m_lib/flad/test/test.cfg m_lib/flad/test/test.txt m_lib/flad/test/comment.txt
diff --git a/README-flad.txt b/README-flad.txt
new file mode 100644 (file)
index 0000000..07591cf
--- /dev/null
@@ -0,0 +1,70 @@
+
+                 Flat ASCII Database and config files modules
+
+   FLAD is family of modules for manipulating Flat ASCII Databases.
+Flat ASCII Database is just a text file with strucrured information. For
+example, flad/fladm modules operates on the following files:
+
+   # Global comment/header for the entire file
+   # It is possible to insert empty line after header
+
+   Name: Orion
+   Type: URL
+   URL: http://www.orion.web/
+
+   Name: NRSC
+   Type: Med domain
+   Domain type: N/A
+
+   Well, I hope you get the idea. The database is just a list of records;
+records are key/value pairs separated by key_sep (flad/fladm/fladc default is
+": ", fladw default is "=", but this can be easyly changed); records are
+usually separated by empty line (fladw use different syntax).
+
+COPYRIGHT and LEGAL ISSUES
+   The software is copyrighted and free. All  programs  copyrighted by
+PhiloSoft Design. Programs are provided "as-is", without any kind of
+warranty.
+   #include "disclaimer.h"
+
+-------------------------------- flad --------------------------------
+
+   flad.py defines the base object, Flad. The object is real FLADatabase,
+and it serves as parent for most FLAD objects. This object provides
+framework for checking (during append/insert operations) every record. The
+module itself is not using it, but fladm (see below) make use of the
+feature.
+   The database is a list (UserList, actually) of records, where every
+record is just a dictionary mapping keys to values. Keys and values are
+just strings (this is not enforced; loading from file create a dictionaries
+of string, after loading user can add/change values; but storing to file
+routines are expecting string values again).
+
+-------------------------------- fladm -------------------------------
+
+   fladm.py defines the object Flad_WithMustKeys. This is inherently FLAD
+with restriction. User should define two set of keys - keys that must be in
+every record, and keys that can be. If there are no "must" keys - "other"
+keys don't matter. If there are "must" keys, but not "other" keys - every
+"must" key must be in every record, but any record can contain any key. If
+there are both "must" keys and "other" keys - every record must contain all
+"must" keys and some or all of "other" keys, but not other. To create
+database with only "must" keys, make "other" keys empty list - []!
+
+-------------------------------- fladc -------------------------------
+
+   fladc.py defines the object Flad_Conf. This is FLAD object to manipulate
+config files. Config file is just a FLAD file with EXACTLY one record - one
+dictionary of properties, that can be used to query and store properties.
+The resulting dictionary can be saved as FLAD file.
+
+-------------------------------- fladw -------------------------------
+
+   fladw.py defines object Flad_WIni to retrieve, manipulate and store
+WIN.INI-like files.
+   General rules for the object is the same - there are routines to load it
+from file and store back, but algorithms are quite different. Records are
+sections; sections are separated by section names - [section_name], e.g.
+   Every record in Flad_WIni is tuple
+(section_name_string, [list_of_comments_and_keys], {dict_of_key-to-value_mapping}).
+   There are procedures to add/remove sections, add/del/change/query key values.
diff --git a/m_lib/flad/__init__.py b/m_lib/flad/__init__.py
new file mode 100644 (file)
index 0000000..89dcb8f
--- /dev/null
@@ -0,0 +1 @@
+"Broytman Flat ASCII Database for Python, Copyright (C) 1996-2001 PhiloSoft Design"
diff --git a/m_lib/flad/flad.py b/m_lib/flad/flad.py
new file mode 100644 (file)
index 0000000..05703c1
--- /dev/null
@@ -0,0 +1,260 @@
+"""
+   Flat ASCII Database.
+   This module implements a very simple database on the flat ASCII files.
+
+   Written by Broytman. Copyright (C) 1997-2005 PhiloSoft Design
+"""
+
+
+import string
+from UserList import UserList
+
+
+# Flad restriction error
+checking_error = "flad.checking_error"
+
+# Default key/value separator
+def_keysep = ": "
+
+
+class Flad(UserList):
+   """
+      Class to represent memory database.
+      FLAD database is a list of records,
+      where every record is a dictionary.
+   """
+
+   # Field and record separators are ALWAYS newline. It cannot be changed
+   #    without extensive rewriting of this module
+   # field_sep = rec_sep = '\n'
+
+   def __init__(self, check_record_func = None, key_sep = def_keysep):
+      UserList.__init__(self)
+      self.check_record_func = check_record_func
+      self.key_sep = key_sep
+
+      self.comment = ''
+      self.wait_comment = 1
+
+
+   def check_record(self, record): # Method can be overriden in subclasses
+      if self.check_record_func:
+         if callable(self.check_record_func):
+            return self.check_record_func(self, record)
+         else:
+            raise TypeError, "non-callable restriction function"
+      else:
+         return 1
+
+
+   def checking_error(self): # Method can be overriden in subclasses
+      raise checking_error
+
+
+   def __setitem__(self, i, item):
+      if not self.check_record(item):
+         self.checking_error()
+      else:
+         UserList.__setitem__(self, i, item)
+
+
+   def __setslice__(self, i, j, list):
+      if list:
+         copy_list = list[:]
+         for item in list:
+            if not self.check_record(item):
+               self.checking_error()
+               del copy_list[copy_list.index(item)]
+         UserList.__setslice__(self, i, j, copy_list)
+
+
+   def append(self, item):
+      if not self.check_record(item):
+         self.checking_error()
+      else:
+         UserList.append(self, item)
+
+
+   def insert(self, i, item):
+      if not self.check_record(item):
+         self.checking_error()
+      else:
+         UserList.insert(self, i, item)
+
+
+   def split_key(self, line):
+      """
+         Split input line to key/value pair and add the pair to dictionary
+      """
+      ###line = string.lstrip(line) # Do not rstrip - if empty value, this will remove space from key
+      if line[-1] == '\n':
+         line = line[:-1] # Chop
+
+      l = string.split(line, self.key_sep, 1) # Just split to key and reminder
+      return tuple(l)
+
+
+   def __parse_line(self, record, line): # Internal function
+      if line == '\n': # Empty line is record separator (but there cannot be more than 1 empty lines)
+         if record: # This helps to skip all empty lines
+            self.append(record) # Check and add the record to list
+            return 1 # Signal to stop filling the record and start a new one
+
+      else:
+         key, value = self.split_key(line)
+         if key in record.keys(): # This have to be done with check_record
+                                  # but the record is not complete now,
+                                  # so it is not ready to be checked :(
+                                  # And, of course, two keys with the same name
+                                  # cannot be added to dictionary
+            raise KeyError, "field key \"" + key + "\" already in record"
+
+         record[key] = value
+
+      return 0
+
+
+   def create_new_record(self): # Method can be overriden in subclasses
+      return {}
+
+
+   def feed(self, record, line): # Method can be overriden in subclasses
+      if line:
+         if self.wait_comment:
+            if string.strip(line) == '':
+               self.comment = self.comment + '\n'
+               self.wait_string = 0
+               return 0
+
+            elif string.lstrip(line)[0] == '#':
+               self.comment = self.comment + line
+               return 0
+
+            else:
+               self.wait_comment = 0
+               # Fallback to parsing
+
+         return self.__parse_line(record, line)
+
+      else:
+         self.append(record)
+
+      return 0
+
+
+   def load_file(self, f):
+      """
+         Load a database from file as a list of records.
+         Every record is a dictionary of key/value pairs.
+         The file is reading as whole - this is much faster, but require
+         more memory.
+      """
+
+      if type(f) == type(''): # If f is string - use it as file's name
+         infile = open(f, 'r')
+      else:
+         infile = f           # else assume it is opened file (fileobject) or
+                              # "compatible" object (must has readline() methods)
+
+      try:
+         lines = infile.readlines()
+
+      finally:
+         if type(f) == type(''): # If f was opened - close it
+            infile.close()
+
+      record = self.create_new_record()
+
+      for line in lines:
+         if self.feed(record, line):
+            record = self.create_new_record()
+
+      # Close record on EOF without empty line
+      if record:
+         self.feed(record, None)
+
+
+   def load_from_file(self, f):
+      """
+         Load a database from file as a list of records.
+         Every record is a dictionary of key/value pairs.
+         The file is reading line by line - this is much slower, but do not
+         require so much memory. (On systems with limited virtual memory,
+         like DOS, it is even faster - for big files)
+      """
+
+      if type(f) == type(''): # If f is string - use it as file's name
+         infile = open(f, 'r')
+      else:
+         infile = f           # else assume it is opened file (fileobject) or
+                              # "compatible" object (must has readline() method)
+
+      record = self.create_new_record()
+
+      try:
+         line = infile.readline()
+
+         while line:
+            if self.feed(record, line):
+               record = self.create_new_record()
+
+            line = infile.readline()
+
+      finally:
+         if type(f) == type(''): # If f was opened - close it
+            infile.close()
+
+      # Close record on EOF without empty line
+      if record:
+         self.feed(record, None)
+
+
+   def store_to_file(self, f):
+      if type(f) == type(''): # If f is string - use it as file's name
+         outfile = open(f, 'w')
+      else:
+         outfile = f          # else assume it is opened file (fileobject) or
+                              # "compatible" object (must has write() method)
+
+      flush_record = 0 # Do not close record on 1st loop
+
+      if self.comment <> '':
+         outfile.write(self.comment)
+
+      for record in self:
+         copy_rec = record.copy() # Make a copy to delete keys
+
+         if flush_record:
+            outfile.write('\n') # Close record
+         else:
+            flush_record = 1    # Set flag for all but 1st record
+
+         if copy_rec:
+            for key in copy_rec.keys():
+               outfile.write(key + self.key_sep + copy_rec[key] + '\n')
+               del copy_rec[key]
+
+      if type(f) == type(''): # If f was opened - close it
+         outfile.close()
+
+
+def load_file(f, check_record_func = None):
+   """
+      Create a database object and load it from file
+   """
+
+   db = Flad(check_record_func)
+   db.load_file(f)
+
+   return db
+
+
+def load_from_file(f, check_record_func = None):
+   """
+      Create a database object and load it from file
+   """
+
+   db = Flad(check_record_func)
+   db.load_from_file(f)
+
+   return db
diff --git a/m_lib/flad/fladc.py b/m_lib/flad/fladc.py
new file mode 100644 (file)
index 0000000..bbde040
--- /dev/null
@@ -0,0 +1,84 @@
+"""
+   Flat ASCII Database to implement VERY simple config files.
+
+   Written by Broytman. Copyright (C) 1997-2005 PhiloSoft Design
+"""
+
+
+import flad, fladm
+
+
+error = "fladc.error" # Too many records
+
+
+class Flad_Conf(dict):
+   """
+      FLAD config is just FLAD Database with exactly ONE record.
+      Flad_Conf objects are just UserDicts.
+   """
+   def __init__(self, must_keys = None, other_keys = None):
+      dict.__init__(self)
+
+      self.must_keys = must_keys
+      self.other_keys = other_keys
+
+
+   def __make_db(self):
+      if self.must_keys:
+         db = fladm.Flad_WithMustKeys(check_record, self.must_keys, self.other_keys)
+      else:
+         db = flad.Flad()
+
+      return db
+
+
+   def load_file(self, f):
+      db = self.__make_db()
+      db.load_file(f)
+
+      if len(db) <> 1:
+         raise error, "incorrect number of records in config file `%s'; expected 1, got %d" % (str(f), len(db))
+
+      self.data = db[0]
+
+
+   def load_from_file(self, f):
+      db = self.__make_db()
+      db.load_from_file(f)
+
+      if len(db) <> 1:
+         raise error, "incorrect number of records in config file `%s'; expected 1, got %d" % (str(f), len(db))
+
+      self.data = db[0]
+
+
+   def store_to_file(self, f):
+      db = self.__make_db()
+      db.append(self.data)
+      db.store_to_file(f)
+
+
+def check_record(data, record): # Only allow append 1 record
+   return len(data) == 0
+
+
+def load_file(f, must_keys = None, other_keys = None):
+   """
+      Create a database object and load it from file
+   """
+
+   db = Flad_Conf(must_keys, other_keys)
+   db.load_file(f)
+
+   return db
+
+
+def load_from_file(f, must_keys = None, other_keys = None):
+   """
+      Create a database object and load it from file
+   """
+
+   db = Flad_Conf(must_keys, other_keys)
+   db.load_from_file(f)
+
+   return db
diff --git a/m_lib/flad/fladm.py b/m_lib/flad/fladm.py
new file mode 100644 (file)
index 0000000..9e835bc
--- /dev/null
@@ -0,0 +1,104 @@
+"""
+   Flat ASCII Database with "must" keys
+
+   Written by Broytman. Copyright (C) 1997-2005 PhiloSoft Design
+"""
+
+
+from flad import Flad, def_keysep
+
+
+class Flad_WithMustKeys(Flad):
+   """
+      Database with two lists of keys - keys that must be in every record,
+      and keys that allowed to be in some records.
+   """
+
+   def __init__(self, check_record_func = None, must_keys = None, other_keys = None):
+      Flad.__init__(self, check_record_func)
+      self.must_keys = must_keys   # Save keys lists to store...
+      self.other_keys = other_keys #... desired sequence of keys
+
+
+   def store_to_file(self, f):
+      if type(f) == type(''): # If f is string - use it as file's name
+         outfile = open(f, 'w')
+      else:
+         outfile = f          # else assume it is opened file (fileobject) or
+                              # "compatible" object (must has write() method)
+
+      flush_record = 0 # Do not close record on 1st loop
+
+      for record in self:
+         copy_rec = record.copy() # Make a copy to delete keys
+
+         if flush_record:
+            outfile.write('\n') # Close record
+         else:
+            flush_record = 1    # Set flag for all but 1st record
+
+         if self.must_keys:
+            for key in self.must_keys:
+               outfile.write(key + def_keysep + copy_rec[key] + '\n')
+               del copy_rec[key]
+
+         if self.other_keys:
+            for key in self.other_keys:
+               if copy_rec.has_key(key):
+                  outfile.write(key + def_keysep + copy_rec[key] + '\n')
+                  del copy_rec[key]
+
+         if copy_rec:
+            for key in copy_rec.keys():
+               outfile.write(key + def_keysep + copy_rec[key] + '\n')
+               del copy_rec[key]
+
+      if type(f) == type(''): # If f was open - close it
+         outfile.close()
+
+
+def check_record(data, record):
+   """
+      Check record for consistency and append it to list of records
+   """
+   must_keys = data.must_keys
+   other_keys  = data.other_keys
+
+   if must_keys:
+      copy_must = must_keys[:] # Make a copy
+   else:
+      copy_must = None
+
+   for key in record.keys(): # Check every key
+      if must_keys and (key in must_keys):
+         del copy_must[copy_must.index(key)] # Remove the key from copied list
+      elif (must_keys and (key not in must_keys) and (other_keys and (key not in other_keys))) or (other_keys and (key not in other_keys)):
+         raise KeyError, "field key \"" + key + "\" is not in list of allowed keys"
+
+   if copy_must: # If there is at least one key - it is an error:
+                      # not all "must" keys are in record
+      raise KeyError, "not all \"must\" keys are in record; keys: " + str(copy_must)
+
+   return 1
+
+
+def load_file(f, check_record_func = None, must_keys = None, other_keys = None):
+   """
+      Create a database object and load it from file
+   """
+
+   db = Flad_WithMustKeys(check_record_func, must_keys, other_keys)
+   db.load_file(f)
+
+   return db
+
+
+def load_from_file(f, check_record_func = None, must_keys = None, other_keys = None):
+   """
+      Create a database object and load it from file
+   """
+
+   db = Flad_WithMustKeys(check_record_func, must_keys, other_keys)
+   db.load_from_file(f)
+
+   return db
diff --git a/m_lib/flad/fladw.py b/m_lib/flad/fladw.py
new file mode 100644 (file)
index 0000000..fe48b00
--- /dev/null
@@ -0,0 +1,211 @@
+"""
+   Flat ASCII Database to load WIN.INI-like files.
+
+   Written by Broytman. Copyright (C) 1997-2005 PhiloSoft Design
+"""
+
+
+import string, re
+import flad
+
+
+error = "fladw.error"
+section_error = "fladw.section_error"
+
+
+re_section = re.compile("^ *\[(.+)\] *$")
+
+
+class Flad_WIni(flad.Flad):
+   """
+      FLAD database is a list of records, where every record is
+      a tuple (section_name, keys, section_dictionary).
+      Sounds similary to Flad? But it is Flad! The only difference is that
+      Flad_WIni has section names and keys (i.e. list of keys and comments
+      in every section to preserve comments and desired order of keys).
+   """
+   def __init__(self):
+      flad.Flad.__init__(self, key_sep = '=')
+      self.first_section = 1
+
+
+   def __parse_line(self, record, line): # Internal function
+      match = re_section.match(line) # Record separator is section name
+      if match:
+         return match.group(1) # Signal to stop filling the record (section) and start a new one
+
+      if self.first_section:
+         if string.strip(line) <> '':
+            raise error, "non-empty line before 1st section"
+
+      elif (string.strip(line) == '') or (string.lstrip(line)[0] == ';') : # Empty line or comment
+         record[0].append(line)
+
+      else:
+         key, value = self.split_key(line)
+         if key in record[1].keys(): # This have to be done with check_record
+                                  # but the record is not complete now,
+                                  # so it is not ready to be checked :(
+                                  # And, of course, two keys with the same name
+                                  # cannot be added to dictionary
+            raise KeyError, "field key \"" + key + "\" already in record"
+
+         record[0].append(key)
+         record[1][key] = value
+
+      return 0
+
+
+   def create_new_record(self):
+      return ([], {})
+
+
+   def feed(self, record, line):
+      if line:
+         section = self.__parse_line(record, line)
+         if section:
+            if not self.first_section:
+               self.append((self.section, record[0], record[1]))
+               self.section = section
+
+               return 1 # Section filled - create new section
+            else:
+               self.first_section = 0
+               self.section = section
+
+         else:
+            if self.first_section and (string.strip(line) <> ''):
+               raise error, "non-empty line before 1st section"
+            # else: line had been appended to section in __parse_line()
+
+      else: # This called after last line of the source file
+         self.append((self.section, record[0], record[1]))
+         del self.section, self.first_section
+
+         # Now remove last empty line in every section
+         for record in self:
+            klist = record[1]
+            if klist:
+               l = len(klist) - 1
+               if string.strip(klist[l]) == '':
+                  del klist[l]
+
+      return 0
+
+
+   def store_to_file(self, f):
+      if type(f) == type(''): # If f is string - use it as file's name
+         outfile = open(f, 'w')
+      else:
+         outfile = f          # else assume it is opened file (fileobject) or
+                              # "compatible" object (must has write() method)
+
+      flush_section = 0 # Do not close 1st section
+
+      for record in self:
+         if flush_section:
+            outfile.write('\n') # Close section
+         else:
+            flush_section = 1    # Set flag for all but 1st section
+
+         outfile.write('[' + record[0] + ']\n') # Section
+
+         if record[1]:
+            for key in record[1]:
+               if string.strip(key) == '' or string.lstrip(key)[0] == ';' :
+                  outfile.write(key)
+               else:
+                  outfile.write(key + self.key_sep + record[2][key] + '\n')
+
+      if type(f) == type(''): # If f was opened - close it
+         outfile.close()
+
+
+   def find_section(self, section):
+      for i in range(0, len(self)):
+         record = self[i]
+         if record[0] == section:
+            return i
+
+      return -1
+
+
+   def add_section(self, section):
+      rec_no = self.find_section(section)
+      if rec_no >= 0:
+         raise section_error, "section [%s] already exists" % section
+
+      self.append((section, [], {}))
+
+
+   def del_section(self, section):
+      rec_no = self.find_section(section)
+      if rec_no < 0:
+         raise section_error, "section [%s] does not exists" % section
+
+      del self[rec_no]
+
+
+   def set_keyvalue(self, section, key, value):
+      rec_no = self.find_section(section)
+      if rec_no < 0:
+         record = (section, [key], {key: value})
+         self.append(record)
+
+      else:
+         record = self[rec_no]
+         if key not in record[1]:
+            record[1].append(key)
+         record[2][key] = value
+
+
+   def get_keyvalue(self, section, key):
+      rec_no = self.find_section(section)
+      if rec_no < 0:
+         raise section_error, "section [%s] does not exists" % section
+
+      record = self[rec_no]
+      if key not in record[1]:
+         raise KeyError, "section [%s] does not has `%s' key" % (section, key)
+
+      return record[2][key]
+
+
+   def get_keydefault(self, section, key, default):
+      rec_no = self.find_section(section)
+      if rec_no < 0:
+         return default
+
+      record = self[rec_no]
+      if key not in record[1]:
+         return default
+
+      return record[2][key]
+
+
+   def del_key(self, section, key):
+      rec_no = self.find_section(section)
+      if rec_no < 0:
+         raise section_error, "section [%s] does not exists" % section
+
+      record = self[rec_no]
+      if key not in record[1]:
+         raise KeyError, "section [%s] does not has `%s' key" % (section, key)
+
+      klist = record[1]
+      del klist[klist.index(key)]
+      del record[2][key]
+
+
+def load_file(f):
+   db = Flad_WIni()
+   db.load_file(f)
+
+   return db
+
+
+def load_from_file(f):
+   db = Flad_WIni()
+   db.load_from_file(f)
+
+   return db
diff --git a/m_lib/flad/test/__init__.py b/m_lib/flad/test/__init__.py
new file mode 100644 (file)
index 0000000..6f91d98
--- /dev/null
@@ -0,0 +1 @@
+"Broytman Flat ASCII Database for Python - Test, Copyright (C) 1996-2001 PhiloSoft Design"
diff --git a/m_lib/flad/test/comment.txt b/m_lib/flad/test/comment.txt
new file mode 100644 (file)
index 0000000..c80687e
--- /dev/null
@@ -0,0 +1,10 @@
+# This is test FLAD (Flat ASCII Database)
+
+Type: Public URL
+Name: Test 1
+
+Type: Public Dir
+Name: Test 2
+
+Type: Private Dir
+Name: Test 3
diff --git a/m_lib/flad/test/test.cfg b/m_lib/flad/test/test.cfg
new file mode 100644 (file)
index 0000000..72174ac
--- /dev/null
@@ -0,0 +1,2 @@
+Type: Public URL
+Name: Test 1
\ No newline at end of file
diff --git a/m_lib/flad/test/test.txt b/m_lib/flad/test/test.txt
new file mode 100644 (file)
index 0000000..d4117cf
--- /dev/null
@@ -0,0 +1,8 @@
+Type: Public URL
+Name: Test 1
+
+Type: Public Dir
+Name: Test 2
+
+Type: Private Dir
+Name: Test 3
diff --git a/m_lib/flad/test/test1.py b/m_lib/flad/test/test1.py
new file mode 100755 (executable)
index 0000000..5ac6217
--- /dev/null
@@ -0,0 +1,55 @@
+#! /usr/bin/env python
+
+
+from m_lib.flad import fladm
+
+
+def test():
+   print
+   print "Test 1:",
+   fladm.load_from_file("test.txt", fladm.check_record, None, None)
+   print "Ok"
+
+   print "Test 2:",
+   fladm.load_from_file("test.txt", fladm.check_record, ["Type"], None)
+   print "Ok"
+
+   print "Test 3:",
+   fladm.load_from_file("test.txt", fladm.check_record, ["Type", "Name"], None)
+   print "Ok"
+
+   print "Test 4:",
+   fladm.load_from_file("test.txt", fladm.check_record, ["Type"], ["Name"])
+   print "Ok"
+
+   print "Test 5:",
+   try: # Note! This must raise KeyError - "Name" key is not listed
+      fladm.load_from_file("test.txt", fladm.check_record, ["Type"], [""])
+   except KeyError:
+      print "Ok"
+   else:
+      print "Error!"
+
+   print "Test 6:",
+   fladm.load_from_file("test.txt", fladm.check_record, None, ["Type", "Name"])
+   print "Ok"
+
+   print "Test 7:",
+   try: # Note! This must raise KeyError - "Error" key is listed in must field
+      fladm.load_from_file("test.txt", fladm.check_record, ["Error"], ["Type"])
+   except KeyError:
+      print "Ok"
+   else:
+      print "Error!"
+
+   print "Test 8:",
+   datalist = fladm.load_from_file("test.txt", fladm.check_record, None, ["Type", "Name", "Error"])
+   print "Ok"
+
+   print "\nLast but not test: just printing loaded list"
+   print datalist
+   print
+
+
+if __name__ == "__main__":
+   test()
diff --git a/m_lib/flad/test/test2.py b/m_lib/flad/test/test2.py
new file mode 100755 (executable)
index 0000000..15035ee
--- /dev/null
@@ -0,0 +1,20 @@
+#! /usr/bin/env python
+
+
+from m_lib.flad import flad
+
+
+def test():
+   print "Test 1:",
+   datalist = flad.load_from_file("test.txt")
+   datalist.store_to_file("test2.o1")
+   print "Ok"
+
+   print "Test 2:",
+   datalist = flad.load_from_file("comment.txt")
+   datalist.store_to_file("test2.o2")
+   print "Ok"
+
+
+if __name__ == "__main__":
+   test()
diff --git a/m_lib/flad/test/test3.py b/m_lib/flad/test/test3.py
new file mode 100755 (executable)
index 0000000..cf0cf3e
--- /dev/null
@@ -0,0 +1,15 @@
+#! /usr/bin/env python
+
+
+from m_lib.flad import fladm
+
+
+def test():
+   print "Test:",
+   datalist = fladm.load_file("test.txt", fladm.check_record, ["Type"], ["Name"])
+   datalist.store_to_file("test3.out")
+   print "Ok"
+
+
+if __name__ == "__main__":
+   test()
diff --git a/m_lib/flad/test/test4.py b/m_lib/flad/test/test4.py
new file mode 100755 (executable)
index 0000000..8103ed9
--- /dev/null
@@ -0,0 +1,16 @@
+#! /usr/bin/env python
+
+
+from m_lib.flad import fladc
+
+
+def test():
+   print "Test:",
+   conf = fladc.load_file("test.cfg")
+   print "Ok"
+
+   print "Property 'Type' is", conf["Type"]
+
+
+if __name__ == "__main__":
+   test()
diff --git a/m_lib/flad/test/test5.py b/m_lib/flad/test/test5.py
new file mode 100755 (executable)
index 0000000..45b70aa
--- /dev/null
@@ -0,0 +1,19 @@
+#! /usr/bin/env python
+
+
+from m_lib.flad import fladc
+
+
+def test():
+   print "Test:",
+
+   try: # Note! This must raise fladc.error - too many records in the file
+      conf = fladc.load_file("test.txt")
+   except fladc.error:
+      print "Ok"
+   else:
+      print "Error!"
+
+
+if __name__ == "__main__":
+   test()
diff --git a/m_lib/flad/test/test6.py b/m_lib/flad/test/test6.py
new file mode 100755 (executable)
index 0000000..ae1131b
--- /dev/null
@@ -0,0 +1,15 @@
+#! /usr/bin/env python
+
+
+from m_lib.flad import fladc
+
+
+def test():
+   print "Test:",
+   conf = fladc.load_file("test.cfg", ["Type", "Name"])
+   conf.store_to_file("test6.out")
+   print "Ok"
+
+
+if __name__ == "__main__":
+   test()
diff --git a/m_lib/flad/test/test7.py b/m_lib/flad/test/test7.py
new file mode 100755 (executable)
index 0000000..4e75952
--- /dev/null
@@ -0,0 +1,17 @@
+#! /usr/bin/env python
+
+
+from m_lib.flad import fladw
+
+
+def test():
+   print "Test:",
+   ini = fladw.load_file("C:\\WINDOWS\\WIN.INI")
+   ini.store_to_file("test7.out")
+   print "Ok"
+
+   print "windows/BorderWidth =", ini.get_keyvalue("windows", "BorderWidth")
+
+
+if __name__ == "__main__":
+   test()
index ac13e3b12b071a2499f9f19a8112ceca5ece7c84..670cbc9f28c49d2b535a39b7256c423261140141 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -3,14 +3,21 @@
 from distutils.core import setup
 
 setup(name = "m_lib",
-   version = "2.0",
-   description = "Broytman Library for Python",
-   long_description = "Broytman Library for Python, Copyright (C) 1996-2013 PhiloSoft Design",
-   author = "Oleg Broytman",
-   author_email = "phd@phdru.name",
-   url = "http://phdru.name/Software/Python/#m_lib",
-   license = "GPL",
-   platforms = "All",
-   packages = ["m_lib", "m_lib.clock", "m_lib.lazy",
-      "m_lib.net", "m_lib.net.ftp", "m_lib.net.www", "m_lib.rus"]
+    version = "2.0",
+    description = "Broytman Library for Python",
+    long_description = "Broytman Library for Python, Copyright (C) 1996-2013 PhiloSoft Design",
+    author = "Oleg Broytman",
+    author_email = "phd@phdru.name",
+    url = "http://phdru.name/Software/Python/#m_lib",
+    license = "GPL",
+    platforms = "All",
+    packages = ["m_lib", "m_lib.clock",
+        "m_lib.flad", "m_lib.flad.test", "m_lib.lazy",
+        "m_lib.net", "m_lib.net.ftp", "m_lib.net.www", "m_lib.rus",
+        ],
+    data_files = [("%s/m_lib/flad/test" % python_lib, [
+        "m_lib/flad/test/test.cfg",
+        "m_lib/flad/test/test.txt",
+        "m_lib/flad/test/comment.txt",
+    ])]
 )