Commits

Juan BC committed 18c7b85

add tag

  • Participants
  • Parent commits f02bc4b
  • Tags 0.1

Comments (0)

Files changed (5)

+
+syntax: regexp
+^\.project$
+syntax: regexp
+^\.pydevproject$
+syntax: regexp
+^.*[.](pyc|pyo)$
+syntax: regexp
+^python/dist$
+syntax: regexp
+^python/build$
+syntax: regexp
+^.*/?_ign_
+syntax: regexp
+^.*(.)orig$
+syntax: regexp
+^.*(.)pdf$
+syntax: regexp
+^\.settings$
+syntax: regexp
+^\build$
+syntax: regexp
+^\dist$
+syntax: regexp
+^.*(.)egg-info$
+#!/usr/bin/env python
+#-*-coding:utf-8-*-
+
+# Copyright (C) - 2011 Juan B Cabral <jbc dot develop at gmail dot com>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Also add information on how to contact you by electronic and paper mail.
+
+
+#===============================================================================
+# DOC
+#===============================================================================
+
+"""Cool way to deal with 'headed' CSV 
+
+Rules of the parser
+    1 - First row is a list of column names.
+    2 - All rows have the same numbers of columns.
+    3 - Empty column names are ignored.
+    4 - Columns without name are ignored.
+    
+Example:
+
+    +-------+-------+----+-------+----+
+    | name0 | name1 |    | name3 |    |
+    +-------+-------+----+-------+----+
+    | v0    |       | v2 | v3    | v4 |
+    +-------+-------+----+-------+----+
+    
+Equivalent:
+
+    +-------+-------+-------+
+    | name0 | name1 | name3 |
+    +-------+-------+-------+
+    | v0    |       | v2    |
+    +-------+-------+-------+
+
+Use:
+
+    >>> import csvcool
+    >>> cool = csvcool.read(open("path/to/csv"))
+    >>> cool[0]["name0"]
+    v0
+    >>> cool[0]["name0"] = 1
+    >>> cool[0]["name0"]
+    1
+    >>> csvcool.write(cool, open("path/to/csv", "w"))
+
+
+"""
+
+#===============================================================================
+# META
+#===============================================================================
+
+__version__ = "0.1"
+__license__ = "GPL3"
+__author__ = "JBC"
+__email__ = "jbc dot develop at gmail dot com"
+__url__ = "http://bitbucket.org/leliel12/csvcool"
+__date__ = "2011-05-22"
+
+
+#===============================================================================
+# IMPORTS
+#===============================================================================
+
+import csv
+
+
+#===============================================================================
+# CLASS FROZEN ROW
+#===============================================================================
+
+class FrozenRow(object):
+    """Esentialy is a "key" fixed dict"""
+    
+    def __init__(self, **kwargs):
+        """Creates a new instances of a FrozenRow"""
+        self._d = kwargs
+
+    def __eq__(self, obj):
+        """x.__eq__(obj) <==> x==obj"""
+        return isinstance(obj, self.__class__) and self._d == obj._d 
+
+    def __contains__(self, key):
+        """x.__contains__(key) <==> key in x"""
+        return key in self._d
+
+    def __getitem__(self, key):
+        """x.__getitem__(key) <==> x[key]"""
+        return self._d[key]
+    
+    def __setitem__(self, key, value):
+        """x.__setitem__(key, value) <==> x[key]=y (only if key in D)"""
+        if key not in sel._d:
+            raise KeyError(key)
+        self._d[key] = value
+    
+    def __len__(self):
+        """x.__len__() <==> len(x)"""
+        return len(self._d)
+        
+    def __iter__(self):
+        """x.__iter__() <==> iter(x)"""
+        return iter(self._d)
+        
+    def __repr__(self):
+        """x.__repr__() <==> repr(x)"""
+        return "<%s: %s>" % (self.__class__.__name__, repr(self._d))
+        
+    def items(self):
+        """ D.items() -> list of D's (key, value) pairs, as 2-tuples"""
+        return self._d.items()
+        
+    def keys(self):
+        """D.keys() -> list of D's keys"""
+        return self._d.keys()
+        
+    def values(self):
+        """D.values() -> list of D's values"""
+        return self._d.values()
+        
+    def get(self, key, default=None):
+        """D.get(key[,default]) -> D[key] if key in D, else default. 
+        default = None."""
+        return self._d.get(key, default)
+        
+
+#===============================================================================
+# CLASS CSV COOL
+#===============================================================================
+
+class CSVCool(object):
+    """The CSVCool object is the coolest way to manipulate CSV files."""
+    
+    def __init__(self, keys, rows):
+        """Creates the new instances of the CSVCool
+        
+        @param keys: The first row of the CSV file
+        @param rows: All rows of the CSV file except the first one
+        """
+        self._columnnames = keys
+        self._rows = []
+        for row in rows:
+            kdata = zip(keys, row)
+            row = FrozenRow(**dict(kdata))
+            self._rows.append(row)
+
+    def __repr__(self):
+        """x.__repr__() <==> repr(x)"""
+        return "%s (%i columns x %i rows) at %s" % (self.__class__.__name__,
+                                                     len(self.columnnames),
+                                                     len(self.rows),
+                                                     hex(id(self)))
+    def __eq__(self, obj):
+        """x.__eq__(obj) <==> x == obj"""
+        return isinstance(obj, self.__class__) \
+               and self.columnnames == obj.columnnames \
+               and self.rows == obj.rows
+        
+        
+    def __ne__(self, obj):
+        """x.__ne__(obj) <==> x != obj"""
+        return not (self == obj)
+    
+    def __iter__(self):
+        """x.__iter__() <==> iter(x)"""
+        return iter(self._rows)
+
+    def __getitem__(self, idx):
+        """x.__getitem__(idx) <==> x[idx]"""
+        return self._rows[idx]
+    
+    def __len__(self):
+        """x.__len__() <==> len(x)"""
+        return len(self._rows) 
+    
+    def __contains__(self, row):
+        """x.__contains__(row) <==> row in x
+        
+        row can be any iterable with columns order or a frozenrow.
+        
+        """
+        if not isinstance(row, FrozenRow):
+            if len(row) != len(self.columnnames):
+                return False
+            row = FrozenRow(**dict(zip(self._columnnames, row)))
+        return row in self._rows
+
+    def wrap(self, iterable):
+        """Convert an iterable in FrozenRow instance of this CSVCool
+        instance.
+        
+        """
+        return FrozenRow(**dict(zip(self._columnnames, iterable)))
+        
+    def cut(self, *columns):
+        """Retrieves a new CSVCool instance only with a named columns.
+        
+        @params *columns: all columns to be preserved
+        
+        Example:
+        
+            original
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+                | v0    |       | v2    |
+                +-------+-------+-------+
+            
+            cuted = original.cut("name0", "name3")
+            
+                +-------+-------+
+                | name0 | name2 |
+                +-------+-------+
+                | v0    | v2    |
+                +-------+-------+
+        
+        """
+        keys = [k for k in self._columnnames if k in columns]
+        rows = []
+        for r in self._rows:
+            rows.append([r[k] for k in keys])
+        return CSVCool(keys, rows)
+
+    def filter(self, *conditions):
+        """Retrieves a new CSVCool instance only with rows who match all the
+        callable conditions passed as parameter.
+        
+        @params *conditions: callables to filter rows
+        
+        Example:
+        
+            original
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+                | v0    |       | v2    |
+                +-------+-------+-------+
+                | v0    |   v1  | v2    |
+                +-------+-------+-------+
+            
+            filtered = original.filter(lambda r: r["name1"] == "v1",
+                                       lambda r: r["name0"] == "v0")
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+                | v0    |   v1  | v2    |
+                +-------+-------+-------+
+        
+        """
+        keys = self._columnnames
+        rows = []
+        for row in self._rows:
+            if all((c(row) for c in conditions)):
+                rows.append([row[k] for k in keys])
+        return CSVCool(keys, rows)
+
+    def addcolumn(self, name, idx=None, default=None):
+        """Append a column.
+        
+        @param name: The name of the new column.
+        @param idx: The position of the new column (append if None).
+        @param default: The vale by default ot the column (None by default).
+        
+        Example:
+        
+            original
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+                | v0    |       | v2    |
+                +-------+-------+-------+
+                
+            >>> original.addcolumn("name1.5", 2, "v1.5")
+            
+                +-------+-------+---------+-------+
+                | name0 | name1 | name1.5 | name2 |
+                +-------+-------+---------+-------+
+                | v0    |       |  v1.5   | v2    |
+                +-------+-------+---------+-------+
+            
+        """
+        if name in self._columnnames:
+            msg = "name '%s' already exists" % name
+            raise KeyError(msg)
+        if idx != None:
+            self._columnnames.insert(idx, name)
+        else:
+            self._columnnames.append(name)
+        oldrows = self._rows
+        self._rows = []
+        for row in oldrows:
+            row = FrozenRow(**dict(row.items() + [(name, default)]))
+            self._rows.append(row)
+
+    def removecolumn(self, name):
+        """Remove a column.
+        
+        @param name: The name of the column to be removed.
+        
+        Example:
+        
+            original
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+                | v0    |       | v2    |
+                +-------+-------+-------+
+                
+            >>> original.removecolumn("name1")
+            
+                +-------+-------+
+                | name0 | name2 |
+                +-------+-------+
+                | v0    | v2    |
+                +-------+-------+
+                
+        """
+        self._columnnames.remove(name)
+        for idx, row in enumerate(self):
+            nrow = dict((k, v) for k, v in row.items() if k != name)
+            self.removerow(row)
+            self.addrow(nrow, idx)
+            
+            
+
+    def addrow(self, row, idx=None):
+        """Add a new row, if the values given it's not enough the cells are 
+        filled with None.
+        
+        @param row: Iterable with row values or a compatible FrozenRow instance.
+        @param idx: The position of the new row (append if None).
+        
+        Example:
+        
+            original
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+                | v0    |       | v2    |
+                +-------+-------+-------+
+                
+            >>> original.addrow([w1,w2], idx=0)
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+                | w1    | w2    | None  |
+                +-------+-------+-------+                
+                | v0    |       | v2    |
+                +-------+-------+-------+
+                
+        """
+        if isinstance(row, FrozenRow):
+            
+            if sorted(row.keys()) != sorted(self._columnnames):
+                raise ValueError("Incompatible FrozenRow instance")
+        else:
+            row = list(row)
+            while len(row) < len(self._columnnames): row.append(None)
+            row = self.wrap(row)
+        
+        if idx != None:
+            self._rows.insert(idx, row)
+        else:
+            self._rows.append(row)
+            
+    def count(self, row):
+        if not isinstance(row, FrozenRow):
+            row = self.wrap(row)
+        return self._rows.count(row)
+
+    def index(self, row, start=None, stop=None):
+        """Return first index of row. 
+        Raises ValueError if the value is not present.
+        
+        Example:
+        
+            >>> csvcool.index(original.poprow(25))
+            25
+        
+        """
+        start = start if start != None else 0
+        stop = stop if stop != None else len(self._rows)
+        if not isinstance(row, FrozenRow):
+            row = self.wrap(row)
+        return self._rows.index(row, start, stop)
+    
+    def removerow(self, row):
+        """remove first occurrence of value.
+        Raises ValueError if the value is not present.
+        
+        @param row
+
+        """
+        if not isinstance(row, FrozenRow):
+            row = self.wrap(row)
+        self._rows.remove(row)
+        
+    def poprow(self, idx=None):
+        """Return a row of a given index. 
+        
+        
+        @param idx: index of a row
+        
+        Example:
+        
+            original
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+                | v0    |       | v2    |
+                +-------+-------+-------+
+            
+            >>> original.pop(0)
+            <FrozenRow: {'name0': 'v0', 'name1': '', 'name2': 'v2'} at 0x0000000>
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+        
+        """
+        return self._rows.pop(idx) if idx != None else self._rows.pop()
+
+    def column(self, columnname):
+        """Retrieves al values from a column
+        
+        @param columnname: Name of a column
+        
+        Example:
+            
+            original
+            
+                +-------+-------+-------+
+                | name0 | name1 | name2 |
+                +-------+-------+-------+
+                | w1    | w2    | None  |
+                +-------+-------+-------+                
+                | v0    |       | v2    |
+                +-------+-------+-------+
+            
+            >>> original.column("name2")
+            (None, v2)
+            
+        """
+        c = []
+        for r in self:
+            c.append(r[columnname])
+        return tuple(c)
+
+    @property
+    def columnnames(self):
+        return tuple(self._columnnames)
+        
+    @property
+    def rows(self):
+        return tuple(self._rows)
+
+
+#===============================================================================
+# FUNCTIONS
+#===============================================================================
+
+def read(stream, **kwargs):
+    """Read a CSVCool instance into from file-like stream who contains csv data.
+
+    @param stream: A File like object.
+    @param **kwargs: kwargs for csv reader method.
+    
+    """
+    src = stream.read()
+    if "dialect" not in kwargs:
+        kwargs["dialect"] = csv.Sniffer().sniff(src)
+    keys = None
+    rows = []
+    for idx, row in enumerate(csv.reader(src.splitlines(), **kwargs)):
+        if idx == 0:
+            keys = [k for k in row if k.strip()]
+        else:
+            rows.append(row)
+    return CSVCool(keys, rows)
+
+
+def write(csvcool, stream, **kwargs):
+    """Write a CSVCool instance into file-like stream.
+    
+    @param csvcool: CSVCool instance.
+    @param stream: A File like object.
+    @param **kwargs: kwargs for csv writer method.
+    
+    """
+    rows = []
+    rows.append(csvcool.columnnames)
+    for row in csvcool:
+        rows.append([row[k] for k in csvcool.columnnames])
+    csv_writer = csv.writer(stream, **kwargs)
+    csv_writer.writerows(rows)
+
+
+#===============================================================================
+# MAIN
+#===============================================================================
+
+if __name__ == "__main__":
+    print(__doc__)
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c11"
+DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+    'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
+    'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
+    'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
+    'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
+    'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
+    'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
+    'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
+    'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
+    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+    'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+    'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+    'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+    'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
+    'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
+    'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
+    'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
+}
+
+import sys, os
+try: from hashlib import md5
+except ImportError: from md5 import md5
+
+def _validate_md5(egg_name, data):
+    if egg_name in md5_data:
+        digest = md5(data).hexdigest()
+        if digest != md5_data[egg_name]:
+            print >> sys.stderr, (
+                "md5 validation of %s failed!  (Possible download problem?)"
+                % egg_name
+            )
+            sys.exit(2)
+    return data
+
+def use_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    download_delay=15
+):
+    """Automatically find/download setuptools and make it available on sys.path
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end with
+    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
+    it is not already available.  If `download_delay` is specified, it should
+    be the number of seconds that will be paused before initiating a download,
+    should one be required.  If an older version of setuptools is installed,
+    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    an attempt to abort the calling script.
+    """
+    was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+    def do_download():
+        egg = download_setuptools(version, download_base, to_dir, download_delay)
+        sys.path.insert(0, egg)
+        import setuptools; setuptools.bootstrap_install_from = egg
+    try:
+        import pkg_resources
+    except ImportError:
+        return do_download()       
+    try:
+        pkg_resources.require("setuptools>=" + version); return
+    except pkg_resources.VersionConflict, e:
+        if was_imported:
+            print >> sys.stderr, (
+            "The required version of setuptools (>=%s) is not available, and\n"
+            "can't be installed while this script is running. Please install\n"
+            " a more recent version first, using 'easy_install -U setuptools'."
+            "\n\n(Currently using %r)"
+            ) % (version, e.args[0])
+            sys.exit(2)
+        else:
+            del pkg_resources, sys.modules['pkg_resources']    # reload ok
+            return do_download()
+    except pkg_resources.DistributionNotFound:
+        return do_download()
+
+def download_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    delay=15
+):
+    """Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download attempt.
+    """
+    import urllib2, shutil
+    egg_name = "setuptools-%s-py%s.egg" % (version, sys.version[:3])
+    url = download_base + egg_name
+    saveto = os.path.join(to_dir, egg_name)
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            from distutils import log
+            if delay:
+                log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help).  I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+   %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+                    version, download_base, delay, url
+                ); from time import sleep; sleep(delay)
+            log.warn("Downloading %s", url)
+            src = urllib2.urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = _validate_md5(egg_name, src.read())
+            dst = open(saveto, "wb"); dst.write(data)
+        finally:
+            if src: src.close()
+            if dst: dst.close()
+    return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+    """Install or upgrade setuptools and EasyInstall"""
+    try:
+        import setuptools
+    except ImportError:
+        egg = None
+        try:
+            egg = download_setuptools(version, delay=0)
+            sys.path.insert(0, egg)
+            from setuptools.command.easy_install import main
+            return main(list(argv) + [egg])   # we're done here
+        finally:
+            if egg and os.path.exists(egg):
+                os.unlink(egg)
+    else:
+        if setuptools.__version__ == '0.0.1':
+            print >> sys.stderr, (
+            "You have an obsolete version of setuptools installed.  Please\n"
+            "remove it from your system entirely before rerunning this script."
+            )
+            sys.exit(2)
+
+    req = "setuptools>=" + version
+    import pkg_resources
+    try:
+        pkg_resources.require(req)
+    except pkg_resources.VersionConflict:
+        try:
+            from setuptools.command.easy_install import main
+        except ImportError:
+            from easy_install import main
+        main(list(argv) + [download_setuptools(delay=0)])
+        sys.exit(0) # try to force an exit
+    else:
+        if argv:
+            from setuptools.command.easy_install import main
+            main(argv)
+        else:
+            print "Setuptools version", version, "or greater has been installed."
+            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+    """Update our built-in md5 registry"""
+
+    import re
+
+    for name in filenames:
+        base = os.path.basename(name)
+        f = open(name, 'rb')
+        md5_data[base] = md5(f.read()).hexdigest()
+        f.close()
+
+    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data.sort()
+    repl = "".join(data)
+
+    import inspect
+    srcfile = inspect.getsourcefile(sys.modules[__name__])
+    f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+    match = re.search("\nmd5_data = {\n([^}]+)}", src)
+    if not match:
+        print >> sys.stderr, "Internal error!"
+        sys.exit(2)
+
+    src = src[:match.start(1)] + repl + src[match.end(1):]
+    f = open(srcfile, 'w')
+    f.write(src)
+    f.close()
+
+
+if __name__ == '__main__':
+    if len(sys.argv) > 2 and sys.argv[1] == '--md5update':
+        update_md5(sys.argv[2:])
+    else:
+        main(sys.argv[1:])
+
+
+
+
+
+
+#!/usr/bin/env python
+#-*-coding:utf-8-*-
+
+# Copyright (C) - 2011 Juan B Cabral <jbc dot develop at gmail dot com>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Also add information on how to contact you by electronic and paper mail.
+
+
+#===============================================================================
+# DOCS
+#===============================================================================
+
+"""Setup for csvcool (http://bitbucket.org/leliel12/csvcool)"""
+
+
+#===============================================================================
+# IMPORTS
+#===============================================================================
+
+from ez_setup import use_setuptools
+use_setuptools()
+
+from setuptools import setup
+
+import csvcool
+
+
+#===============================================================================
+# SETUP
+#===============================================================================
+
+download_url = "%s/get/%s.tar.gz#egg=%s-%s" % (csvcool.__url__,
+                                               csvcool.__version__,
+                                               csvcool.__name__,
+                                               csvcool.__version__)
+ 
+setup(name=csvcool.__name__,
+      version=csvcool.__version__,
+      description=csvcool.__doc__.splitlines()[0],
+      long_description=csvcool.__doc__,
+      author=csvcool.__author__,
+      author_email=csvcool.__email__,
+      url=csvcool.__url__,
+      download_url=download_url,
+      license="GPL3",
+      keywords="csv",
+      classifiers=[
+                   "Development Status :: 4 - Beta",
+                   "Topic :: Utilities",
+                   "Operating System :: OS Independent",
+                   "Programming Language :: Python :: 2",
+                   ],
+      py_modules = ['csvcool'],
+)
+
+
+#===============================================================================
+# MAIN
+#===============================================================================
+
+if __name__ == '__main__':
+    print(__doc__)
+#!/usr/bin/env python
+#-*-coding:utf-8-*-
+
+# Copyright (C) - 2011 Juan B Cabral <jbc dot develop at gmail dot com>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Also add information on how to contact you by electronic and paper mail.
+
+
+#===============================================================================
+# DOCS
+#===============================================================================
+
+"""Test suite fot csvcool"""
+
+
+#===============================================================================
+# IMPORTS
+#===============================================================================
+
+import unittest
+import cStringIO
+import time
+import random
+
+import csvcool
+
+
+#===============================================================================
+# CONF CONSTANTS
+#===============================================================================
+
+CSV_CELLS = (
+    ("col_0" , "col_1" , "col_2" , "col_3" , "col_4"),
+    ("val_00", "val_01", ""      , "val_03", "val_04"),
+    ("val_10", "val_11", "val_12", "val_13", "val_14", "val_15"),
+)
+
+
+#===============================================================================
+# INTERNAL CONSTANS AND FUNCTIONS
+#===============================================================================
+
+def to_csv_code(cells):
+    strlines = []
+    for line in cells:
+        strline = []
+        for cell in line:
+            strline.append("\"%s\"" % cell)
+        strlines.append(", ".join(strline))
+    return "\n".join(strlines)
+
+CSV_HEADERS = CSV_CELLS[0]
+
+CSV_ROWS = CSV_CELLS[1:]
+
+CSV_CODE = to_csv_code(CSV_CELLS)
+
+
+#===============================================================================
+# TEST
+#===============================================================================
+
+class CoolTest(unittest.TestCase):
+    
+    def setUp(self):
+        self.cool = csvcool.read(cStringIO.StringIO(CSV_CODE))
+        
+    def test_sizes(self):
+        acepted_size = len(self.cool.columnnames)
+        for row in self.cool:
+            self.assertEquals(len(row), acepted_size)
+
+    def test_column_names(self):
+        cnames = self.cool.columnnames
+        for cname in cnames:
+            self.assertTrue(cname in CSV_HEADERS)
+            for row in self.cool:
+                self.assertTrue(cname in row)
+        
+    def test_cut(self):
+        for _ in range(100):
+            cnames = [cn for cn in CSV_HEADERS if random.randint(0, 1)]
+            unused_cnames = [cn for cn in CSV_HEADERS if cn not in cnames]
+            random.shuffle(cnames)
+            cuted = self.cool.cut(*cnames)
+            for cname in cnames:
+                self.assertTrue(cname in cuted.columnnames)
+                for row in cuted:
+                    self.assertTrue(cname in row)
+            for cname in unused_cnames:
+                self.assertTrue(cname not in cuted.columnnames)
+                for row in cuted:
+                    self.assertTrue(cname not in row)
+        
+    def test_filter(self):
+        for _ in range(100):
+            filters = []
+            rows_to_accept = []
+            for row in self.cool:
+                if random.randint(0, 1):
+                    idx = random.randint(0, len(row) - 1)
+                    cname = random.choice(CSV_HEADERS)
+                    fnc = lambda r: r[cname] == row[cname]
+                    filters.append(fnc)
+                    filtered = self.cool.filter(*filters)
+                    for frow in filtered:
+                        self.assertEquals(frow, row)
+                        
+    def test_add_remove_column(self):
+        names_old = self.cool.columnnames
+        names_new = []
+        for name in range(100):
+            name = str(name)
+            names_new.append(name)
+            idx = random.randint(0, len(self.cool.columnnames) - 1) \
+                  if random.randint(0, 1) \
+                  else None
+            default = str(random.random()) if random.randint(0, 1) else None
+            self.cool.addcolumn(name, idx, default)
+            try:
+                self.cool.addcolumn(name, idx, default)
+            except KeyError as err:
+                pass
+            else:
+                self.fail("name '%s' already exists" % name)
+            self.assertTrue(name in self.cool.columnnames)
+            self.assertEquals(self.cool.columnnames.index(name),
+                              idx if idx != None else len(self.cool.columnnames) - 1)
+            for row in self.cool:
+                self.assertEquals(row[name], default)
+        for name in names_new:
+            self.cool.removecolumn(name)        
+            for row in self.cool:
+                self.assertTrue(name not in row)
+        self.assertEquals(names_old, self.cool.columnnames)
+                
+    def test_add_pop_add_and_remove_row(self):
+        old_rows = list(self.cool)
+        new_rows = []
+        for _ in range(100):
+            row = [str(random.random()) 
+                   for _ in range(random.randint(0, len(self.cool.columnnames) + random.randint(1, 10)))]
+            idx = random.randint(0, len(self.cool) - 1) \
+                  if random.randint(0, 1) \
+                  else None
+            self.cool.addrow(row, idx)
+            row = list(row)
+            while len(row) < len(self.cool.columnnames): row.append(None)
+            while len(row) > len(self.cool.columnnames): row.pop()
+            
+            new_rows.append(row)
+            
+            self.assertTrue(row in self.cool)
+            idxs = set()
+            try:
+                start = 0
+                while True:
+                    idxs.add(self.cool.index(row, start))
+                    start = self.cool.index(row, start) + 1
+            except ValueError: 
+                pass
+            self.assertTrue((idx if idx != None else len(self.cool) - 1) in idxs)
+            
+        for row in new_rows:
+            total = self.cool.count(row)
+            idxs = []
+            for cnt in range(total):
+                idx = self.cool.index(row)
+                idxs.append(idx)
+                self.cool.addrow(self.cool.poprow(idx), idx)
+                self.cool.poprow(idx), idx
+                self.assertEquals(self.cool.count(row), total - (cnt + 1))
+            for idx in idxs:
+                self.cool.addrow(row, idx)
+            self.assertEquals(total, self.cool.count(row))
+                
+        for row in new_rows:
+            total = self.cool.count(row)
+            for cnt in range(total):
+                self.cool.removerow(row)
+                self.assertEquals(self.cool.count(row), total - (cnt + 1))
+            
+        self.assertEquals(old_rows, list(self.cool))
+            
+    def test_columns(self):
+        for cname in self.cool.columnnames:
+            column = self.cool.column(cname)
+            for idx, row in enumerate(self.cool):
+                self.assertEquals(row[cname], column[idx])
+            
+    def test_io(self):
+        out = cStringIO.StringIO()
+        csvcool.write(self.cool, out)
+        out.seek(0)
+        ncool = csvcool.read(out)
+        self.assertEqual(ncool, self.cool)
+
+
+#===============================================================================
+# MAIN
+#===============================================================================
+
+if __name__ == "__main__":
+    unittest.main()