mirror of
https://github.com/kennethreitz/tablib.git
synced 2026-06-05 15:00:19 +00:00
Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 930d38cf5a | |||
| 5e433c263d | |||
| 19ac9b9716 | |||
| 6feb59504a | |||
| 817eedd6f5 | |||
| 4d1c5a9996 | |||
| 520a1986d7 | |||
| 1ea793112c | |||
| 41a7a5d329 | |||
| c4edaa2ca8 | |||
| c612bb3dae | |||
| c223dfbdf1 | |||
| 49bd48b016 | |||
| c6d90bc825 | |||
| bcd0e37a65 | |||
| 8c92e878a3 | |||
| da2b011358 | |||
| a8b0bf4b5f | |||
| 6574d3e58b | |||
| 1020799828 | |||
| 333e73f892 | |||
| bfe70066b8 | |||
| fbfbe01b70 | |||
| 06a394ea5c | |||
| 9427decdb0 | |||
| fb59035f8d | |||
| 187d12cffc | |||
| eaa4de7793 | |||
| d479c5735a | |||
| 96668bb393 | |||
| b369baba40 | |||
| 25f846a78a | |||
| 22fe18239f | |||
| 149bafa97b | |||
| 9f7fec2379 | |||
| 762ac39e27 | |||
| 2a7aa959b3 | |||
| d85523b6a6 | |||
| 6407afba3e | |||
| 25a5bcea0c | |||
| 7aada68952 | |||
| 5ba92b0f6b | |||
| f58d4b67dc | |||
| a310ab7a09 | |||
| 7f2f925ddb | |||
| 3fc898e222 | |||
| de46f45e2e | |||
| 392eaac299 | |||
| 3a9c3944cf | |||
| 8c402da729 | |||
| 8feb6e8ddf | |||
| 9072b6ddae | |||
| 9f26c23eb5 | |||
| 8136f4b09e | |||
| 7e7ad73ddd | |||
| f889910629 | |||
| 969d9d957d | |||
| 86d84b555d | |||
| 66867527d2 | |||
| 7505d8d985 | |||
| d5515c17b8 | |||
| 07ac723971 | |||
| 5d7843ea59 | |||
| b5f0cf9d37 | |||
| a73bbe1645 | |||
| f1bdf43aab | |||
| 7623bfe7b0 | |||
| 59ccc0b422 | |||
| 99154aa6d6 | |||
| 65836d5ace | |||
| 4117503ed5 | |||
| dfa26a7d53 | |||
| 4f035caf1b | |||
| a9c7a5067d | |||
| 80cb42e8dd | |||
| 8d7e5732cd | |||
| 942dd3dadf | |||
| b1d282744c | |||
| 4c0c879d65 | |||
| cab63e02c8 | |||
| 63d025888a | |||
| 5a993ac281 | |||
| 666dd1d2c7 | |||
| ac1666e3ae | |||
| 5b7e817db2 | |||
| f9c168e4bc | |||
| 82f3d84c7d | |||
| 121cf46aec | |||
| 4bb4a05bcb | |||
| e52b8dd329 | |||
| 93fb89b8b6 | |||
| c01b66a16a | |||
| c3fa29a166 | |||
| 8d6a52aaf5 | |||
| 703b1da04c | |||
| 0e6bd079cc | |||
| 579dbf0cc0 | |||
| fbabb430ca | |||
| b8f923f8c5 | |||
| fbe6fe1612 | |||
| 17e90e71e5 |
@@ -10,4 +10,5 @@ Development Lead
|
||||
Patches and Suggestions
|
||||
```````````````````````
|
||||
|
||||
- A Lucky Someone
|
||||
- Luke Lee
|
||||
- Josh Ourisman
|
||||
+31
@@ -1,5 +1,36 @@
|
||||
History
|
||||
=======
|
||||
0.8.4 (2010-10-04)
|
||||
------------------
|
||||
|
||||
* Upated XLS output: Only wrap if '\n' in cell.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
0.8.1 (2010-09-28)
|
||||
------------------
|
||||
* Packaging Fix
|
||||
|
||||
|
||||
0.8.0 (2010-09-25)
|
||||
------------------
|
||||
* New format plugin system!
|
||||
* Imports! ELEGANT Imports!
|
||||
* Tests. Lots of tests.
|
||||
|
||||
|
||||
0.7.1 (2010-09-20)
|
||||
------------------
|
||||
|
||||
+54
-9
@@ -15,15 +15,27 @@ Tablib is a format-agnostic tabular dataset library, written in Python.
|
||||
|
||||
Output formats supported:
|
||||
|
||||
- Excel
|
||||
- JSON
|
||||
- YAML
|
||||
- CSV
|
||||
- Excel (Sets + Books)
|
||||
- JSON (Sets + Books)
|
||||
- YAML (Sets + Books)
|
||||
- CSV (Sets)
|
||||
|
||||
At this time, Tablib supports the **export** of it's powerful Dataset object instances into any of the above formats. Import is underway.
|
||||
Import formats supported:
|
||||
|
||||
- JSON (Sets + Books)
|
||||
- YAML (Sets + Books)
|
||||
- CSV (Sets)
|
||||
|
||||
Note that tablib *purposefully* excludes XML support. It always will.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
`tablib.Dataset()`
|
||||
A Dataset is a table of tabular data. It may or may not have a header row. They can be build and maniuplated as raw Python datatypes (Lists of tuples|dictonaries). Datasets can be imported from JSON, YAML, and CSV; they can be exported to Excel (XLS), JSON, YAML, and CSV.
|
||||
|
||||
`tablib.Databook()`
|
||||
A Databook is a set of Datasets. The most common form of a Databook is an Excel file with multiple spreadsheets. Databooks can be imported from JSON and YAML; they can be exported to Excel (XLS), JSON, and YAML.
|
||||
|
||||
Usage
|
||||
-----
|
||||
@@ -64,6 +76,9 @@ Easily delete rows: ::
|
||||
|
||||
>>> del data[1]
|
||||
|
||||
Exports
|
||||
-------
|
||||
|
||||
Drumroll please...........
|
||||
|
||||
JSON!
|
||||
@@ -109,8 +124,41 @@ EXCEL!
|
||||
>>> open('people.xls', 'wb').write(data.xls)
|
||||
|
||||
It's that easy.
|
||||
|
||||
Imports!
|
||||
--------
|
||||
|
||||
JSON
|
||||
++++
|
||||
|
||||
::
|
||||
|
||||
>>> data.json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]'
|
||||
>>> print data[0]
|
||||
('John', 'Adams', 90)
|
||||
|
||||
|
||||
YAML
|
||||
++++
|
||||
::
|
||||
|
||||
>>> data.yaml = '- {age: 90, first_name: John, last_name: Adams}'
|
||||
>>> print data[0]
|
||||
('John', 'Adams', 90)
|
||||
|
||||
CSV
|
||||
+++
|
||||
::
|
||||
|
||||
>>> data.yaml = 'age, first_name, last_name\n90, John, Adams'
|
||||
>>> print data[0]
|
||||
('John', 'Adams', 90)
|
||||
|
||||
|
||||
>>> print data.yaml
|
||||
- {age: 90, first_name: John, last_name: Adams}
|
||||
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
@@ -131,13 +179,10 @@ If you'd like to contribute, simply fork `the repository`_, commit your changes
|
||||
|
||||
Roadmap
|
||||
-------
|
||||
- Import datasets from CSV, JSON, YAML
|
||||
- Release CLI Interface
|
||||
- Auto-detect import format
|
||||
- Add possible other exports (SQL?)
|
||||
- Possibly plugin-ify format architecture
|
||||
- Ability to assign types to rows (set, regex=, &c.)
|
||||
- Plugin support
|
||||
|
||||
.. _`the repository`: http://github.com/kennethreitz/tablib
|
||||
.. _AUTHORS: http://github.com/kennethreitz/tablib/blob/master/AUTHORS
|
||||
|
||||
@@ -11,21 +11,20 @@ def publish():
|
||||
"""Publish to PyPi"""
|
||||
os.system("python setup.py sdist upload")
|
||||
|
||||
|
||||
if sys.argv[-1] == "publish":
|
||||
publish()
|
||||
sys.exit()
|
||||
|
||||
setup(
|
||||
name='tablib',
|
||||
version='0.7.1',
|
||||
version='0.8.4',
|
||||
description='Format agnostic tabular data library (XLS, JSON, YAML, CSV)',
|
||||
long_description=open('README.rst').read() + '\n\n' +
|
||||
open('HISTORY.rst').read(),
|
||||
author='Kenneth Reitz',
|
||||
author_email='me@kennethreitz.com',
|
||||
url='http://github.com/kennethreitz/tablib',
|
||||
packages=['tablib'],
|
||||
packages=['tablib', 'tablib.formats'],
|
||||
install_requires=['xlwt', 'simplejson', 'PyYAML'],
|
||||
license='MIT',
|
||||
classifiers=(
|
||||
|
||||
@@ -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()
|
||||
+8
-1
@@ -1 +1,8 @@
|
||||
from core import *
|
||||
""" Tablib.
|
||||
"""
|
||||
|
||||
from tablib.core import (
|
||||
Databook, Dataset, detect, import_set,
|
||||
InvalidDatasetType, InvalidDimensions, UnsupportedFormat
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
+130
-103
@@ -1,28 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# _____ ______ ______ _________
|
||||
# __ /_______ ____ /_ ___ /_ _____ ______ /
|
||||
# _ __/_ __ `/__ __ \__ __ \_ _ \_ __ /
|
||||
# / /_ / /_/ / _ /_/ /_ /_/ // __// /_/ /
|
||||
# \__/ \__,_/ /_.___/ /_.___/ \___/ \__,_/
|
||||
""" Tablib - Core Library.
|
||||
"""
|
||||
|
||||
from tablib.formats import FORMATS as formats
|
||||
|
||||
|
||||
import csv
|
||||
import cStringIO
|
||||
import random
|
||||
|
||||
import simplejson as json
|
||||
import xlwt
|
||||
import yaml
|
||||
|
||||
from helpers import *
|
||||
|
||||
|
||||
# __all__ = ['Dataset', 'DataBook']
|
||||
|
||||
__name__ = 'tablib'
|
||||
__version__ = '0.7.1'
|
||||
__build__ = 0x000701
|
||||
__title__ = 'tablib'
|
||||
__version__ = '0.8.4'
|
||||
__build__ = 0x000804
|
||||
__author__ = 'Kenneth Reitz'
|
||||
__license__ = 'MIT'
|
||||
__copyright__ = 'Copyright 2010 Kenneth Reitz'
|
||||
@@ -32,23 +18,25 @@ class Dataset(object):
|
||||
"""Epic Tabular-Dataset object. """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._data = None
|
||||
self._saved_file = None
|
||||
self._saved_format = None
|
||||
self._data = list(args)
|
||||
self.__headers = None
|
||||
|
||||
# ('title', index) tuples
|
||||
self._separators = []
|
||||
|
||||
try:
|
||||
self.headers = kwargs['headers']
|
||||
except KeyError, why:
|
||||
except KeyError:
|
||||
self.headers = None
|
||||
|
||||
try:
|
||||
self.title = kwargs['title']
|
||||
except KeyError, why:
|
||||
except KeyError:
|
||||
self.title = None
|
||||
|
||||
self._register_formats()
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self.height
|
||||
|
||||
@@ -79,6 +67,20 @@ class Dataset(object):
|
||||
except AttributeError:
|
||||
return '<dataset object>'
|
||||
|
||||
|
||||
@classmethod
|
||||
def _register_formats(cls):
|
||||
"""Adds format properties."""
|
||||
for fmt in formats:
|
||||
try:
|
||||
try:
|
||||
setattr(cls, fmt.title, property(fmt.export_set, fmt.import_set))
|
||||
except AttributeError:
|
||||
setattr(cls, fmt.title, property(fmt.export_set))
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def _validate(self, row=None, col=None, safety=False):
|
||||
"""Assures size of every row in dataset is of proper proportions."""
|
||||
@@ -113,6 +115,7 @@ class Dataset(object):
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
"""Returns the height of the Dataset."""
|
||||
@@ -124,12 +127,13 @@ class Dataset(object):
|
||||
"""Returns the width of the Dataset."""
|
||||
try:
|
||||
return len(self._data[0])
|
||||
except IndexError, why:
|
||||
except IndexError:
|
||||
try:
|
||||
return len(self.headers)
|
||||
except TypeError, e:
|
||||
except TypeError:
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
"""Headers property."""
|
||||
@@ -143,7 +147,7 @@ class Dataset(object):
|
||||
if collection:
|
||||
try:
|
||||
self.__headers = list(collection)
|
||||
except TypeError, why:
|
||||
except TypeError:
|
||||
raise TypeError
|
||||
else:
|
||||
self.__headers = None
|
||||
@@ -154,50 +158,38 @@ class Dataset(object):
|
||||
"""Returns python dict of Dataset."""
|
||||
return self._package()
|
||||
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
"""Returns JSON representation of Dataset."""
|
||||
return json.dumps(self.dict)
|
||||
|
||||
@property
|
||||
def yaml(self):
|
||||
"""Returns YAML representation of Dataset."""
|
||||
return yaml.dump(self.dict)
|
||||
|
||||
@property
|
||||
def csv(self):
|
||||
"""Returns CSV representation of Dataset."""
|
||||
stream = cStringIO.StringIO()
|
||||
_csv = csv.writer(stream)
|
||||
|
||||
for row in self._package(dicts=False):
|
||||
_csv.writerow(row)
|
||||
|
||||
return stream.getvalue()
|
||||
|
||||
@property
|
||||
def xls(self):
|
||||
"""Returns XLS representation of Dataset."""
|
||||
|
||||
wb = xlwt.Workbook(encoding='utf8')
|
||||
ws = wb.add_sheet(self.title if self.title else 'Tabbed Dataset')
|
||||
|
||||
for i, row in enumerate(self._package(dicts=False)):
|
||||
for j, col in enumerate(row):
|
||||
ws.write(i, j, col)
|
||||
|
||||
stream = cStringIO.StringIO()
|
||||
wb.save(stream)
|
||||
return stream.getvalue()
|
||||
|
||||
@dict.setter
|
||||
def dict(self, pickle):
|
||||
"""Returns python dict of Dataset."""
|
||||
if not len(pickle):
|
||||
return
|
||||
if isinstance(pickle[0], list):
|
||||
for row in pickle:
|
||||
self.append(row)
|
||||
elif isinstance(pickle[0], dict):
|
||||
self.headers = pickle[0].keys()
|
||||
for row in pickle:
|
||||
self.append(row.values())
|
||||
else:
|
||||
raise UnsupportedFormat
|
||||
|
||||
|
||||
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:
|
||||
@@ -215,13 +207,38 @@ class Dataset(object):
|
||||
self._data = [tuple([row]) for row in col]
|
||||
|
||||
|
||||
def insert(self, i, row=None, col=None):
|
||||
def insert_separator(self, index, text='-'):
|
||||
"""Adds a separator to Dataset at given index."""
|
||||
|
||||
sep = (index, text)
|
||||
self._separators.append(sep)
|
||||
|
||||
|
||||
def append_separator(self, text='-'):
|
||||
"""Adds a separator to Dataset."""
|
||||
|
||||
# change offsets if headers are or aren't defined
|
||||
if not self.headers:
|
||||
index = self.height if self.height else 0
|
||||
else:
|
||||
index = (self.height + 1) if self.height else 1
|
||||
|
||||
self.insert_separator(index, text)
|
||||
|
||||
|
||||
def insert(self, i, row=None):
|
||||
"""Inserts a row at given position in Dataset"""
|
||||
if row:
|
||||
self._validate(row)
|
||||
self._data.insert(i, tuple(row))
|
||||
elif col:
|
||||
pass
|
||||
|
||||
|
||||
def wipe(self):
|
||||
"""Erases all data from Dataset."""
|
||||
self._data = list()
|
||||
self.__headers = None
|
||||
|
||||
|
||||
class Databook(object):
|
||||
@@ -231,6 +248,7 @@ class Databook(object):
|
||||
|
||||
def __init__(self, sets=[]):
|
||||
self._datasets = sets
|
||||
self._register_formats()
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
@@ -240,15 +258,35 @@ class Databook(object):
|
||||
return '<databook object>'
|
||||
|
||||
|
||||
def wipe(self):
|
||||
"""Wipe book clean."""
|
||||
self._datasets = []
|
||||
|
||||
|
||||
@classmethod
|
||||
def _register_formats(cls):
|
||||
"""Adds format properties."""
|
||||
for fmt in formats:
|
||||
try:
|
||||
try:
|
||||
setattr(cls, fmt.title, property(fmt.export_book, fmt.import_book))
|
||||
except AttributeError:
|
||||
setattr(cls, fmt.title, property(fmt.export_book))
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def add_sheet(self, dataset):
|
||||
"""Add given dataset ."""
|
||||
"""Adds given dataset."""
|
||||
if type(dataset) is Dataset:
|
||||
self._datasets.append(dataset)
|
||||
else:
|
||||
raise InvalidDatasetType
|
||||
|
||||
|
||||
|
||||
def _package(self):
|
||||
"""Packages Databook for delivery."""
|
||||
collector = []
|
||||
for dset in self._datasets:
|
||||
collector.append(dict(
|
||||
@@ -263,40 +301,29 @@ class Databook(object):
|
||||
"""The number of the Datasets within DataBook."""
|
||||
return len(self._datasets)
|
||||
|
||||
@property
|
||||
def xls(self):
|
||||
"""Returns XLS representation of DataBook."""
|
||||
|
||||
def detect(stream):
|
||||
"""Return (format, stream) of given stream."""
|
||||
for fmt in formats:
|
||||
try:
|
||||
if fmt.detect(stream):
|
||||
return (fmt, stream)
|
||||
except AttributeError:
|
||||
pass
|
||||
return (None, stream)
|
||||
|
||||
|
||||
def import_set(stream):
|
||||
"""Return dataset of given stream."""
|
||||
(format, stream) = detect(stream)
|
||||
|
||||
wb = xlwt.Workbook(encoding='utf8')
|
||||
|
||||
for i, dset in enumerate(self._datasets):
|
||||
ws = wb.add_sheet(dset.title if dset.title else 'Sheet%s' % (i))
|
||||
|
||||
#for row in self._package(dicts=False):
|
||||
for i, row in enumerate(dset._package(dicts=False)):
|
||||
for j, col in enumerate(row):
|
||||
ws.write(i, j, col)
|
||||
|
||||
|
||||
stream = cStringIO.StringIO()
|
||||
wb.save(stream)
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
"""Returns JSON representation of Databook."""
|
||||
|
||||
return json.dumps(self._package())
|
||||
|
||||
|
||||
@property
|
||||
def yaml(self):
|
||||
"""Returns YAML representation of Databook."""
|
||||
|
||||
return yaml.dump(self._package())
|
||||
|
||||
try:
|
||||
data = Dataset()
|
||||
format.import_set(data, stream)
|
||||
return data
|
||||
|
||||
except AttributeError, e:
|
||||
return None
|
||||
|
||||
|
||||
class InvalidDatasetType(Exception):
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Tablib - formats
|
||||
"""
|
||||
|
||||
import _csv as csv
|
||||
import _json as json
|
||||
import _xls as xls
|
||||
import _yaml as yaml
|
||||
|
||||
FORMATS = (json, xls, yaml, csv)
|
||||
@@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Tablib - CSV Support.
|
||||
"""
|
||||
|
||||
import cStringIO
|
||||
import csv
|
||||
import os
|
||||
|
||||
import simplejson as json
|
||||
|
||||
import tablib
|
||||
|
||||
|
||||
title = 'csv'
|
||||
extentions = ('csv',)
|
||||
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns CSV representation of Dataset."""
|
||||
stream = cStringIO.StringIO()
|
||||
_csv = csv.writer(stream)
|
||||
|
||||
for row in dataset._package(dicts=False):
|
||||
_csv.writerow(row)
|
||||
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
def import_set(dset, in_stream, headers=True):
|
||||
"""Returns dataset from CSV stream."""
|
||||
|
||||
dset.wipe()
|
||||
|
||||
rows = csv.reader(in_stream.split())
|
||||
for i, row in enumerate(rows):
|
||||
|
||||
if (i == 0) and (headers):
|
||||
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
|
||||
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Tablib - JSON Support
|
||||
"""
|
||||
|
||||
import simplejson as json
|
||||
import tablib.core
|
||||
|
||||
title = 'json'
|
||||
extentions = ('json', 'jsn')
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns JSON representation of Dataset."""
|
||||
return json.dumps(dataset.dict)
|
||||
|
||||
|
||||
def export_book(databook):
|
||||
"""Returns JSON representation of Databook."""
|
||||
return json.dumps(databook._package())
|
||||
|
||||
|
||||
def import_set(dset, in_stream):
|
||||
"""Returns dataset from JSON stream."""
|
||||
|
||||
dset.wipe()
|
||||
dset.dict = json.loads(in_stream)
|
||||
|
||||
|
||||
def import_book(dbook, in_stream):
|
||||
"""Returns databook from JSON stream."""
|
||||
|
||||
dbook.wipe()
|
||||
for sheet in json.loads(in_stream):
|
||||
data = tablib.core.Dataset()
|
||||
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
|
||||
@@ -0,0 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Tablib - XLS Support.
|
||||
"""
|
||||
|
||||
import xlwt
|
||||
import cStringIO
|
||||
|
||||
|
||||
title = 'xls'
|
||||
extentions = ('xls',)
|
||||
|
||||
# special styles
|
||||
wrap = xlwt.easyxf("alignment: wrap on")
|
||||
bold = xlwt.easyxf("font: bold on")
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns XLS representation of Dataset."""
|
||||
|
||||
wb = xlwt.Workbook(encoding='utf8')
|
||||
ws = wb.add_sheet(dataset.title if dataset.title else 'Tabbed Dataset')
|
||||
|
||||
dset_sheet(dataset, ws)
|
||||
|
||||
stream = cStringIO.StringIO()
|
||||
wb.save(stream)
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
def export_book(databook):
|
||||
"""Returns XLS representation of DataBook."""
|
||||
|
||||
wb = xlwt.Workbook(encoding='utf8')
|
||||
|
||||
for i, dset in enumerate(databook._datasets):
|
||||
ws = wb.add_sheet(dset.title if dset.title else 'Sheet%s' % (i))
|
||||
|
||||
dset_sheet(dset, ws)
|
||||
|
||||
|
||||
stream = cStringIO.StringIO()
|
||||
wb.save(stream)
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
def dset_sheet(dataset, ws):
|
||||
"""Completes given worksheet from given Dataset."""
|
||||
_package = dataset._package(dicts=False)
|
||||
|
||||
for i, sep in enumerate(dataset._separators):
|
||||
_offset = i
|
||||
_package.insert((sep[0] + _offset), (sep[1],))
|
||||
|
||||
for i, row in enumerate(_package):
|
||||
for j, col in enumerate(row):
|
||||
|
||||
# bold headers
|
||||
if (i == 0) and dataset.headers:
|
||||
ws.write(i, j, col, bold)
|
||||
|
||||
# bold separators
|
||||
elif len(row) < dataset.width:
|
||||
ws.write(i, j, col, bold)
|
||||
|
||||
# wrap the rest
|
||||
else:
|
||||
if '\n' in col:
|
||||
ws.write(i, j, col, wrap)
|
||||
else:
|
||||
ws.write(i, j, col)
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Tablib - YAML Support.
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import tablib
|
||||
|
||||
|
||||
|
||||
title = 'yaml'
|
||||
extentions = ('yaml', 'yml')
|
||||
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns YAML representation of Dataset."""
|
||||
return yaml.dump(dataset.dict)
|
||||
|
||||
|
||||
def export_book(databook):
|
||||
"""Returns YAML representation of Databook."""
|
||||
return yaml.dump(databook._package())
|
||||
|
||||
|
||||
def import_set(dset, in_stream):
|
||||
"""Returns dataset from YAML stream."""
|
||||
|
||||
dset.wipe()
|
||||
dset.dict = yaml.load(in_stream)
|
||||
|
||||
|
||||
def import_book(dbook, in_stream):
|
||||
"""Returns databook from YAML stream."""
|
||||
|
||||
dbook.wipe()
|
||||
|
||||
for sheet in yaml.load(in_stream):
|
||||
data = tablib.core.Dataset()
|
||||
data.title = sheet['title']
|
||||
data.dict = sheet['data']
|
||||
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
|
||||
@@ -1,5 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Tablib - General Helpers.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
@@ -12,6 +15,19 @@ class Struct(object):
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key, None)
|
||||
|
||||
def dictionary(self):
|
||||
"""Returns dictionary representation of object."""
|
||||
return self.__dict__
|
||||
|
||||
def items(self):
|
||||
"""Returns items within object."""
|
||||
return self.__dict__.items()
|
||||
|
||||
def keys(self):
|
||||
"""Returns keys within object."""
|
||||
return self.__dict__.keys()
|
||||
|
||||
|
||||
|
||||
def piped():
|
||||
"""Returns piped input via stdin, else False."""
|
||||
|
||||
+157
-1
@@ -13,8 +13,10 @@ class TablibTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Create simple data set with headers."""
|
||||
global data
|
||||
|
||||
global data, book
|
||||
data = tablib.Dataset()
|
||||
book = tablib.Databook()
|
||||
|
||||
self.headers = ('first_name', 'last_name', 'gpa')
|
||||
self.john = ('John', 'Adams', 90)
|
||||
@@ -100,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."""
|
||||
@@ -181,6 +190,153 @@ class TablibTestCase(unittest.TestCase):
|
||||
data.csv
|
||||
data.xls
|
||||
|
||||
|
||||
def test_book_export_no_exceptions(self):
|
||||
"""Test that varoius exports don't error out."""
|
||||
|
||||
book = tablib.Databook()
|
||||
book.add_sheet(data)
|
||||
|
||||
book.json
|
||||
book.yaml
|
||||
book.xls
|
||||
|
||||
|
||||
def test_json_import_set(self):
|
||||
"""Generate and import JSON set serialization."""
|
||||
data.append(self.john)
|
||||
data.append(self.george)
|
||||
data.headers = self.headers
|
||||
|
||||
_json = data.json
|
||||
|
||||
data.json = _json
|
||||
|
||||
self.assertEqual(_json, data.json)
|
||||
|
||||
|
||||
def test_json_import_book(self):
|
||||
"""Generate and import JSON book serialization."""
|
||||
data.append(self.john)
|
||||
data.append(self.george)
|
||||
data.headers = self.headers
|
||||
|
||||
book.add_sheet(data)
|
||||
_json = book.json
|
||||
|
||||
book.json = _json
|
||||
|
||||
self.assertEqual(_json, book.json)
|
||||
|
||||
|
||||
def test_yaml_import_set(self):
|
||||
"""Generate and import YAML set serialization."""
|
||||
data.append(self.john)
|
||||
data.append(self.george)
|
||||
data.headers = self.headers
|
||||
|
||||
_yaml = data.yaml
|
||||
|
||||
data.yaml = _yaml
|
||||
|
||||
self.assertEqual(_yaml, data.yaml)
|
||||
|
||||
|
||||
def test_yaml_import_book(self):
|
||||
"""Generate and import YAML book serialization."""
|
||||
data.append(self.john)
|
||||
data.append(self.george)
|
||||
data.headers = self.headers
|
||||
|
||||
book.add_sheet(data)
|
||||
_yaml = book.yaml
|
||||
|
||||
book.yaml = _yaml
|
||||
|
||||
self.assertEqual(_yaml, book.yaml)
|
||||
|
||||
|
||||
def test_csv_import_set(self):
|
||||
"""Generate and import CSV set serialization."""
|
||||
data.append(self.john)
|
||||
data.append(self.george)
|
||||
data.headers = self.headers
|
||||
|
||||
_csv = data.csv
|
||||
|
||||
data.csv = _csv
|
||||
|
||||
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."""
|
||||
|
||||
new_row = (1, 2, 3)
|
||||
data.append(new_row)
|
||||
|
||||
# Verify width/data
|
||||
self.assertTrue(data.width == len(new_row))
|
||||
self.assertTrue(data[0] == new_row)
|
||||
|
||||
data.wipe()
|
||||
new_row = (1, 2, 3, 4)
|
||||
data.append(new_row)
|
||||
self.assertTrue(data.width == len(new_row))
|
||||
self.assertTrue(data[0] == new_row)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user