diff options
| -rw-r--r-- | AUTHORS | 3 | ||||
| -rw-r--r-- | HISTORY.rst | 9 | ||||
| -rw-r--r-- | setup.py | 2 | ||||
| -rwxr-xr-x | tabbed | 14 | ||||
| -rw-r--r-- | tablib/__init__.py | 4 | ||||
| -rw-r--r-- | tablib/cli.py | 84 | ||||
| -rw-r--r-- | tablib/core.py | 17 | ||||
| -rw-r--r-- | tablib/formats/__init__.py | 2 | ||||
| -rw-r--r-- | tablib/formats/_csv.py | 9 | ||||
| -rw-r--r-- | tablib/formats/_json.py | 9 | ||||
| -rw-r--r-- | tablib/formats/_yaml.py | 13 | ||||
| -rwxr-xr-x | test_tablib.py | 59 |
12 files changed, 214 insertions, 11 deletions
@@ -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. @@ -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(), @@ -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.""" |
