csvsimple
Very basic CSV utility for Python.
csvsimple.py
00001 ## @package base
00002 #  This file contains the implementation of the basic CSV class.
00003 
00004 import re
00005 import collections
00006 import sys
00007 
00008 if sys.version_info < (3, 0):
00009     raise RuntimeError("Module CSV requires Python 3.0 or greater!")
00010 
00011 __all__ = ['Csv']
00012 
00013 
00014 ## This class handles CSV data. 
00015 #
00016 #  This class "partially" implements the following interfaces:
00017 #  <ul>
00018 #     <li>Sequence.</li>
00019 #     <li>Mutable Mapping.</li>
00020 #  </ul>
00021 #
00022 #  <pre>
00023 #  <code>
00024 #     csv = Csv(['id', 'first name', 'last name'])
00025 #     csv.add([1, 'John', 'Carter'])
00026 #     csv.add([2, 'John', 'Dupond'])
00027 #     csv.add([3, 'John', 'XXXX'])
00028 #     print ("%d" % len(csv))
00029 #     record = csv[1]
00030 #     del self.csv[1]
00031 #
00032 #     # Print all first names.
00033 #     for record in csv: print (csv.getValue(record, 'first name'))
00034 #
00035 #     # Get all columns' names.
00036 #     v = []
00037 #     for c in csv.keys(): v.append(c)
00038 #
00039 #     # name: one column's name.
00040 #     # value: all values for this column.
00041 #     v = []
00042 #     for name, value in csv.items(): v.append([name, value])
00043 #
00044 #     # v[0]: all value for column "id"
00045 #     # v[1]: all value for column "first name"
00046 #     # v[2]: all value for column "last name"
00047 #     v = []
00048 #     for value in csv.values(): v.append(value)
00049 #  </code>
00050 #  </pre>
00051 #
00052 #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00053 
00054 class Csv:
00055         
00056         # -----------------------------------------------------------------
00057         # Class' methods.
00058         # -----------------------------------------------------------------
00059         
00060         ## Default records' formater.
00061         #  @param in_record Record to convert into string.
00062         #  @param in_header List of columns' names that defines the CSV's structure.
00063         #  @return The method returns a string that represents the given record.
00064         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00065         @staticmethod
00066         def __formater(in_record, in_header):
00067                 r = []
00068                 for i in range(0, len(in_header)):
00069                         name  = in_header[i]
00070                         value = in_record[i]
00071                         s     = "%s: %s" % (name.rjust(20), value)
00072                         r.append(s)
00073                 return "\n".join(r)
00074                 
00075         
00076         ## Return the default records' formater.
00077         #  @return The method returns the default records' formater.
00078         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00079         @staticmethod
00080         def getDefaultFormater():
00081                 return Csv.__formater;
00082         
00083         # -----------------------------------------------------------------
00084         # Class' attributes.
00085         # -----------------------------------------------------------------
00086 
00087         ## Triggers selection via simple equality. This value is used by the method select().   
00088         EQUALITY = 1
00089         ## Triggers selection via pattern matching. This value is used by the method select().  
00090         MATCH    = 2
00091         ## Triggers selection via function execution. This value is used by the method select().        
00092         EXECUTE  = 3
00093                 
00094         # -----------------------------------------------------------------
00095         # Constructor.
00096         # -----------------------------------------------------------------
00097 
00098         ## Create a CSV container.
00099         #  @param in_header This list contains the names of the columns that define the CSV structure.
00100         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00101         def __init__(self, in_header):
00102                 # Check for duplicated columns' names.
00103                 c = collections.Counter(in_header)
00104                 for column, count in c.items():
00105                         if count > 1: raise RuntimeError('Duplicated columns "%s"!' % column)
00106                         
00107                 # Create instance's attributes.
00108                 self.__positions = {}                           # Association between columns' names and values' positions.
00109                 self.__header    = in_header                    # Names of columns.
00110                 self.__count     = len(in_header)               # Number of columns.
00111                 self.__records   = []                           # List of list.
00112                 self.__index     = 0                            # Used for the iterator.
00113                 self.__format    = Csv.getDefaultFormater()         # Default values for string conversion.
00114 
00115                 # Build the association between columns' names and values' positions.
00116                 pos = 0         
00117                 for name in in_header:
00118                         self.__positions[name] = pos
00119                         pos += 1
00120 
00121         # -----------------------------------------------------------------
00122         # Public instance's methods.
00123         # -----------------------------------------------------------------
00124 
00125         ## Add a record to the CSV container.
00126         #  @param in_record This list contains the values to add.
00127         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00128         def add(self, in_record):
00129                 # Make sure that the number of values is correct.
00130                 if not len(in_record) == self.__count: raise RuntimeError('Invalid number of values for record (found %d, expected %d)' % (len(in_record), self.__count))
00131                 self.__records.append(in_record)
00132 
00133         ## Clear the CVS container.     
00134         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00135         def void(self): self.__records = []
00136 
00137 
00138         ## Select records fro the CSV container.
00139         #  @param in_criterias This dictionary contains selections' criterions.
00140         #         <ul>
00141         #             <li>Dictionary's key : the name of a column.</li>
00142         #             <li>Dictionary's value: this value can be : a simple value, a regular expression or a functioin.
00143         #                 The type of value depends on the value of parameter "in_action".</li>
00144         #         </ul>
00145         #         If this parameter is not specified, then the method returns all the records in the container.
00146         #  @param in_action This parameter defines the selection's method. 
00147         #         Three methods are available :
00148         #         <ul>
00149         #             <li>Csv.EQUALITY (1): Simple equality.
00150         #                 Dictionary's values must be simple values.</li>
00151         #             <li>Csv.MATCH (2): Pattern matching.
00152         #                 Dictionary's values must be regular expressions.</li>
00153         #             <li>Csv.EXECUTE (3): Execution of code.
00154         #                 Dictionary's values must be functions.
00155         #                 Function's signature is: def myFuntion(in_value)</li>
00156         #         </ul>
00157         #  @return The method returns a lust of records.
00158         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00159         def select(self, in_criterias=None, in_action=1):
00160                 if in_criterias is None: return self.__records;
00161                 res = []
00162                 for record in self.__records:
00163                         r = True
00164                         for key, value in in_criterias.items():
00165                                 if not key in self.__header: raise RuntimeError('Unexpected name (%s) for criteria!' % key)
00166                                 record_value = record[self.__position(key)]
00167                                 
00168                                 # Simple comparaison.
00169                                 if in_action == Csv.EQUALITY:
00170                                         if not value == record_value:
00171                                                 r = False
00172                                                 break
00173                                 else:
00174                                         # Matching.
00175                                         if in_action == Csv.MATCH:
00176                                                 reg = re.compile(value)
00177                                                 if not re.match(reg, record_value):
00178                                                         r = False
00179                                                         break
00180                                         else:
00181                                                 # Verify through function's execution.
00182                                                 if in_action == Csv.EXECUTE:
00183                                                         if not value(record_value):
00184                                                                 r = False
00185                                                                 break
00186                                                 else:
00187                                                         raise RuntimeError('Invalid select method (%d)!' % in_action)
00188                         if r: res.append(record)
00189                 return res
00190 
00191         ## Set a function used to print records.
00192         #  @param in_formatter Function with the following signature: def myFunction(in_record, in_header)
00193         #         This function must return a string.
00194         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00195         def setFormater(self, in_formatter):
00196                 self.__format = in_formatter
00197         
00198         ## Return the function used to print records.
00199         #  @return Te method returns the function used to print records.
00200         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples       
00201         def getFormater(self):
00202                 return self.__format
00203 
00204         ## This method returns a list of strings. Each string represents a record.
00205         #  @return The method returns a list of strings. Each string represents a record.
00206         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00207         def strs(self):
00208                 result = []
00209                 for record in self.__records:
00210                         # print ("==> %s" % self.__format(record, self.__header))
00211                         result.append(self.__format(record, self.__header))
00212                 return result
00213         
00214         ## Get the value of a given column, for a given record.
00215         #  @param in_record The record.
00216         #  @param in_column_name Name of the column.
00217         #  @return The method returns the value for the given column.
00218         #  @remark See example on https://github.com/denis-beurive/csvsimple/tree/master/examples
00219         def getValue(self, in_record, in_column_name):
00220                 return in_record[self.__position(in_column_name)]
00221                 
00222         # -----------------------------------------------------------------
00223         # Sequence's interface.
00224         # -----------------------------------------------------------------
00225                                 
00226         def __iter__(self): return self
00227 
00228         def __next__(self):
00229                 if self.__index < len(self.__records):
00230                         self.__index += 1
00231                         return self.__records[self.__index-1]
00232                 else:
00233                         self.__index = 0
00234                         raise StopIteration
00235 
00236         def __len__(self): return len(self.__records)
00237         
00238         def __getitem__(self, in_index):
00239                 return self.__records[in_index]
00240         
00241         def __setitem__(self, in_index, in_value):
00242                 self.__records[in_index] = in_value     
00243         
00244         def __delitem__(self, in_index):
00245                 del self.__records[in_index]
00246 
00247         # -----------------------------------------------------------------
00248         # Mapping's interface
00249         # -----------------------------------------------------------------
00250 
00251         def keys(self):
00252                 return self.__header
00253         
00254         def items(self):
00255                 v = []
00256                 for name in self.__header:
00257                         r = []
00258                         for record in self.__records: r.append(record[self.__position(name)])
00259                         v.append([name, r])
00260                 return v        
00261                         
00262         def values(self):
00263                 v = []
00264                 for name in self.__header:
00265                         r = []
00266                         for record in self.__records: r.append(record[self.__position(name)])
00267                         v.append(r)
00268                 return v        
00269 
00270         # -----------------------------------------------------------------
00271         # Other methods
00272         # -----------------------------------------------------------------
00273 
00274         def __str__(self):
00275                 return "\n".join(self.strs())
00276 
00277         # -----------------------------------------------------------------
00278         # Private instance's methods.
00279         # -----------------------------------------------------------------
00280 
00281         def __position(self, in_name):          
00282                 return self.__positions[in_name]
00283         
00284