summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS3
-rw-r--r--HISTORY.rst9
-rw-r--r--setup.py2
-rwxr-xr-xtabbed14
-rw-r--r--tablib/__init__.py4
-rw-r--r--tablib/cli.py84
-rw-r--r--tablib/core.py17
-rw-r--r--tablib/formats/__init__.py2
-rw-r--r--tablib/formats/_csv.py9
-rw-r--r--tablib/formats/_json.py9
-rw-r--r--tablib/formats/_yaml.py13
-rwxr-xr-xtest_tablib.py59
12 files changed, 214 insertions, 11 deletions
diff --git a/AUTHORS b/AUTHORS
index 3d48743..fa467c3 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -10,4 +10,5 @@ Development Lead
Patches and Suggestions
```````````````````````
-- Luke Lee \ No newline at end of file
+- Luke Lee
+- Josh Ourisman \ No newline at end of file
diff --git a/HISTORY.rst b/HISTORY.rst
index d380034..350cb6f 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,7 +1,14 @@
History
=======
-0.8.2 (2010-09-28)
+0.8.3 (2010-10-04)
+------------------
+
+* Ability to append new column passing a callable
+ as the value that will be applied to every row.
+
+
+0.8.2 (2010-10-04)
------------------
* Added alignment wrapping to written cells.
* Added separator support to XLS.
diff --git a/setup.py b/setup.py
index bea4609..a297169 100644
--- a/setup.py
+++ b/setup.py
@@ -17,7 +17,7 @@ if sys.argv[-1] == "publish":
setup(
name='tablib',
- version='0.8.2',
+ version='0.8.3',
description='Format agnostic tabular data library (XLS, JSON, YAML, CSV)',
long_description=open('README.rst').read() + '\n\n' +
open('HISTORY.rst').read(),
diff --git a/tabbed b/tabbed
new file mode 100755
index 0000000..c30ec36
--- /dev/null
+++ b/tabbed
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Tabbed -- CLI for Tablib
+Copyright (c) 2010 Kenneth Reitz. MIT License.
+"""
+
+import tablib.cli
+
+
+if __name__ == '__main__':
+
+ tablib.cli.start() \ No newline at end of file
diff --git a/tablib/__init__.py b/tablib/__init__.py
index fadd8dd..3f23850 100644
--- a/tablib/__init__.py
+++ b/tablib/__init__.py
@@ -2,7 +2,7 @@
"""
from tablib.core import (
- Databook, Dataset, InvalidDatasetType,
- InvalidDimensions, UnsupportedFormat
+ Databook, Dataset, detect, import_set,
+ InvalidDatasetType, InvalidDimensions, UnsupportedFormat
)
diff --git a/tablib/cli.py b/tablib/cli.py
new file mode 100644
index 0000000..6d773c9
--- /dev/null
+++ b/tablib/cli.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+""" Tabbed CLI Inteface Application
+"""
+
+import io
+import sys
+
+import argue
+
+import tablib
+from helpers import Struct, piped
+
+
+
+FORMATS = [fmt.title for fmt in tablib.formats.FORMATS]
+
+opts = []
+
+opts.append(('v', 'version', False, 'Report tabbed version'))
+
+for format in FORMATS:
+ opts.append(('', format, False, 'Output to %s' % (format.upper())))
+
+
+
+@argue.command(options=opts, usage='[FILE] [--FORMAT | FILE]')
+def start(in_file=None, out_file=None, **opts):
+ """Covertly convert dataset formats"""
+
+ opts = Struct(**opts)
+
+ if opts.version:
+ print('Tabbed, Ver. %s' % tablib.core.__version__)
+ sys.exit(0)
+
+ stdin = piped()
+
+ if stdin:
+ data = tablib.import_set(stdin)
+
+ elif in_file:
+
+ try:
+ in_stream =- io.open(in_file, 'r').read()
+ except Exception, e:
+ print(' %s cannot be read.' % in_file)
+ sys.exit(65)
+
+ try:
+ tablib.import_set(in_stream)
+ except Exception, e:
+ raise e
+ print('Import format not supported.')
+ sys.exit(65)
+ else:
+ print('Please provide input.')
+ sys.exit(65)
+
+
+ _formats_sum = sum(opts[f] for f in FORMATS)
+
+ # Multiple output formats given
+ if _formats_sum > 1:
+ print('Please specify a single output format.')
+ sys.exit(64)
+
+ # No output formats given
+ elif _formats_sum < 1:
+ print('Please specify an output format.')
+ sys.exit(64)
+
+
+ # fetch options.formats list
+ # if sum(()) > 1
+ # log only one data format please
+ # if sum of formats == 0, specity format
+
+ # look for filename
+
+ # print opts.__dict__
+ # print in_file
+ # print out_file \ No newline at end of file
diff --git a/tablib/core.py b/tablib/core.py
index e7b622c..69e7549 100644
--- a/tablib/core.py
+++ b/tablib/core.py
@@ -7,8 +7,8 @@ from tablib.formats import FORMATS as formats
__title__ = 'tablib'
-__version__ = '0.8.1'
-__build__ = 0x000801
+__version__ = '0.8.3'
+__build__ = 0x000803
__author__ = 'Kenneth Reitz'
__license__ = 'MIT'
__copyright__ = 'Copyright 2010 Kenneth Reitz'
@@ -177,10 +177,19 @@ class Dataset(object):
def append(self, row=None, col=None):
"""Adds a row to the end of Dataset"""
- if row:
+ if row is not None:
self._validate(row)
self._data.append(tuple(row))
- elif col:
+ elif col is not None:
+ col = list(col)
+ if self.headers:
+ header = [col.pop(0)]
+ else:
+ header = []
+ if len(col) == 1 and callable(col[0]):
+ col = map(col[0], self._data)
+ col = tuple(header + col)
+
self._validate(col=col)
if self.headers:
diff --git a/tablib/formats/__init__.py b/tablib/formats/__init__.py
index b22a959..69eada7 100644
--- a/tablib/formats/__init__.py
+++ b/tablib/formats/__init__.py
@@ -8,4 +8,4 @@ import _json as json
import _xls as xls
import _yaml as yaml
-FORMATS = (csv, json, xls, yaml)
+FORMATS = (json, xls, yaml, csv)
diff --git a/tablib/formats/_csv.py b/tablib/formats/_csv.py
index 8b19da7..27d2e0d 100644
--- a/tablib/formats/_csv.py
+++ b/tablib/formats/_csv.py
@@ -40,3 +40,12 @@ def import_set(dset, in_stream, headers=True):
dset.headers = row
else:
dset.append(row)
+
+
+def detect(stream):
+ """Returns True if given stream is valid CSV."""
+ try:
+ rows = dialect = csv.Sniffer().sniff(stream)
+ return True
+ except csv.Error:
+ return False \ No newline at end of file
diff --git a/tablib/formats/_json.py b/tablib/formats/_json.py
index 1f92b58..f7c88ee 100644
--- a/tablib/formats/_json.py
+++ b/tablib/formats/_json.py
@@ -36,3 +36,12 @@ def import_book(dbook, in_stream):
data.title = sheet['title']
data.dict = sheet['data']
dbook.add_sheet(data)
+
+
+def detect(stream):
+ """Returns True if given stream is valid JSON."""
+ try:
+ json.loads(stream)
+ return True
+ except json.decoder.JSONDecodeError:
+ return False \ No newline at end of file
diff --git a/tablib/formats/_yaml.py b/tablib/formats/_yaml.py
index 4cac8aa..57d63d7 100644
--- a/tablib/formats/_yaml.py
+++ b/tablib/formats/_yaml.py
@@ -39,4 +39,15 @@ def import_book(dbook, in_stream):
data = tablib.core.Dataset()
data.title = sheet['title']
data.dict = sheet['data']
- dbook.add_sheet(data) \ No newline at end of file
+ dbook.add_sheet(data)
+
+def detect(stream):
+ """Returns True if given stream is valid YAML."""
+ try:
+ _yaml = yaml.load(stream)
+ if isinstance(_yaml, (list, tuple, dict)):
+ return True
+ else:
+ return False
+ except yaml.parser.ParserError:
+ return False \ No newline at end of file
diff --git a/test_tablib.py b/test_tablib.py
index 67b693d..ebad061 100755
--- a/test_tablib.py
+++ b/test_tablib.py
@@ -102,6 +102,13 @@ class TablibTestCase(unittest.TestCase):
self.assertRaises(tablib.InvalidDimensions, data.append, col=new_col)
+ def test_add_callable_column(self):
+ """Verify adding column with values specified as callable."""
+ new_col = ['first_again', lambda x: x[0]]
+ self.founders.append(col=new_col)
+
+ self.assertTrue(map(lambda x: x[0] == x[-1], self.founders))
+
def test_header_slicing(self):
"""Verify slicing by headers."""
@@ -261,6 +268,58 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(_csv, data.csv)
+ def test_csv_format_detect(self):
+ """Test CSV format detection."""
+
+ _csv = (
+ '1,2,3\n'
+ '4,5,6\n'
+ '7,8,9\n'
+ )
+ _bunk = (
+ '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
+ )
+
+ self.assertTrue(tablib.formats.csv.detect(_csv))
+ self.assertFalse(tablib.formats.csv.detect(_bunk))
+
+ def test_json_format_detect(self):
+ """Test JSON format detection."""
+
+ _json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]'
+ _bunk = (
+ '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
+ )
+
+ self.assertTrue(tablib.formats.json.detect(_json))
+ self.assertFalse(tablib.formats.json.detect(_bunk))
+
+
+ def test_yaml_format_detect(self):
+ """Test YAML format detection."""
+
+ _yaml = '- {age: 90, first_name: John, last_name: Adams}'
+ _bunk = (
+ '¡¡¡¡¡¡---///\n\n\n¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
+ )
+
+ self.assertTrue(tablib.formats.yaml.detect(_yaml))
+ self.assertFalse(tablib.formats.yaml.detect(_bunk))
+
+
+ def test_auto_format_detect(self):
+ """Test auto format detection."""
+
+ _yaml = '- {age: 90, first_name: John, last_name: Adams}'
+ _json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]'
+ _csv = '1,2,3\n4,5,6\n7,8,9\n'
+ _bunk = '¡¡¡¡¡¡---///\n\n\n¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
+
+ self.assertEqual(tablib.detect(_yaml)[0], tablib.formats.yaml)
+ self.assertEqual(tablib.detect(_csv)[0], tablib.formats.csv)
+ self.assertEqual(tablib.detect(_json)[0], tablib.formats.json)
+ self.assertEqual(tablib.detect(_bunk)[0], None)
+
def test_wipe(self):
"""Purge a dataset."""