Compare commits

...

104 Commits

Author SHA1 Message Date
Kenneth Reitz 6407afba3e typo fix. 2010-09-28 08:46:31 -04:00
Kenneth Reitz 7aada68952 Merge branch 'hotfix/8.0.1' 2010-09-28 08:37:43 -04:00
Kenneth Reitz 5ba92b0f6b Packaging fix.
Version bump.
2010-09-28 08:37:32 -04:00
Kenneth Reitz 9f26c23eb5 Merge branch 'release/0.8.0' 2010-09-25 17:18:51 -04:00
Kenneth Reitz 8136f4b09e Updated history for v0.8.0. 2010-09-25 17:18:48 -04:00
Kenneth Reitz 7e7ad73ddd Merge branch 'release/0.8.0' into develop 2010-09-25 17:13:25 -04:00
Kenneth Reitz f889910629 Big documentation update. 2010-09-25 17:12:50 -04:00
Kenneth Reitz 969d9d957d Version Bump (to v0.8.0) 2010-09-25 16:59:27 -04:00
Kenneth Reitz 86d84b555d Import cleanup. 2010-09-25 16:53:33 -04:00
Kenneth Reitz 66867527d2 Format import cleanups. 2010-09-25 16:51:09 -04:00
Kenneth Reitz 7505d8d985 Adding docstrings for pylint coverage. 2010-09-25 16:49:21 -04:00
Kenneth Reitz d5515c17b8 Removed useless imports. 2010-09-25 16:47:04 -04:00
Kenneth Reitz 07ac723971 Readme update for imports. 2010-09-25 16:46:52 -04:00
Kenneth Reitz 5d7843ea59 Merge branch 'feature/imports' into develop 2010-09-25 15:57:30 -04:00
Kenneth Reitz b5f0cf9d37 Tests elegant book imports. 2010-09-25 15:56:43 -04:00
Kenneth Reitz a73bbe1645 Elegant databook importers. 2010-09-25 15:56:20 -04:00
Kenneth Reitz f1bdf43aab Book wiper. 2010-09-25 15:50:06 -04:00
Kenneth Reitz 7623bfe7b0 Updated tests for set imports. 2010-09-25 15:40:05 -04:00
Kenneth Reitz 59ccc0b422 YAML input support. 2010-09-25 15:39:09 -04:00
Kenneth Reitz 99154aa6d6 Merge branches 'feature/import-seamless' and 'feature/imports' into feature/imports 2010-09-25 15:24:59 -04:00
Kenneth Reitz 65836d5ace Updated elegant imports for instance properties.
Data wipes.
2010-09-25 15:24:16 -04:00
Kenneth Reitz 4117503ed5 Elegant imports in place! 2010-09-25 15:23:01 -04:00
Kenneth Reitz dfa26a7d53 Typos. 2010-09-25 10:49:06 -04:00
Kenneth Reitz 4f035caf1b Added dataset wipe. 2010-09-25 10:40:59 -04:00
Kenneth Reitz a9c7a5067d Added dataset wipe. 2010-09-25 06:22:40 -04:00
Kenneth Reitz 80cb42e8dd Archaic imports in place! 2010-09-25 06:20:34 -04:00
Kenneth Reitz 8d7e5732cd Typo. 2010-09-25 05:59:02 -04:00
Kenneth Reitz 942dd3dadf Added tablib core docstring placeholder. 2010-09-25 05:58:40 -04:00
Kenneth Reitz b1d282744c Docstring updates. 2010-09-25 05:57:42 -04:00
Kenneth Reitz 4c0c879d65 Updated tests. 2010-09-25 05:53:19 -04:00
Kenneth Reitz cab63e02c8 Module namespace change. 2010-09-25 05:53:13 -04:00
Kenneth Reitz 63d025888a Added format importers. 2010-09-25 05:49:21 -04:00
Kenneth Reitz 5a993ac281 Working on it. 2010-09-25 05:49:14 -04:00
Kenneth Reitz 666dd1d2c7 Pylint preps. 2010-09-25 05:17:03 -04:00
Kenneth Reitz ac1666e3ae removing garbage 2010-09-25 05:14:07 -04:00
Kenneth Reitz 5b7e817db2 Only CSV Left. 2010-09-25 05:11:57 -04:00
Kenneth Reitz f9c168e4bc Added coverage bin. 2010-09-25 05:08:35 -04:00
Kenneth Reitz 82f3d84c7d Added docstring. 2010-09-25 05:06:04 -04:00
Kenneth Reitz 121cf46aec Corrected always-false condition. 2010-09-25 05:04:51 -04:00
Kenneth Reitz 4bb4a05bcb Longer varnames for pylint. 2010-09-25 05:02:58 -04:00
Kenneth Reitz e52b8dd329 Added methods to struct for pylint. 2010-09-25 05:01:05 -04:00
Kenneth Reitz 93fb89b8b6 Cleanup * imports. 2010-09-25 04:58:24 -04:00
Kenneth Reitz c01b66a16a Moving that back. 2010-09-25 04:53:20 -04:00
Kenneth Reitz c3fa29a166 Added public method for pylint. 2010-09-25 04:51:56 -04:00
Kenneth Reitz 8d6a52aaf5 Cleanups for pylint. 2010-09-25 04:49:31 -04:00
Kenneth Reitz 703b1da04c General cleanups for pylint. 2010-09-25 04:45:22 -04:00
Kenneth Reitz 0e6bd079cc Improved docstring. 2010-09-25 04:43:45 -04:00
Kenneth Reitz 579dbf0cc0 Added docstring.
Removed unneeded import.
2010-09-25 04:43:39 -04:00
Kenneth Reitz fbabb430ca small setup.py fix 2010-09-25 04:04:36 -04:00
Kenneth Reitz b8f923f8c5 added Luke Lee to Authors 2010-09-25 04:03:01 -04:00
Kenneth Reitz fbe6fe1612 fix old push 2010-09-25 02:55:21 -04:00
Kenneth Reitz 17e90e71e5 test 2010-09-25 02:54:43 -04:00
Kenneth Reitz dc21825f34 Merge branch 'release/0.7.1' 2010-09-20 21:39:47 -04:00
Kenneth Reitz 7364995eaa Version bump (v0.7.1) 2010-09-20 21:39:27 -04:00
Kenneth Reitz 3407170b99 Updated TODO. 2010-09-20 21:37:32 -04:00
Kenneth Reitz dd13744c92 Documentation update for properties. 2010-09-20 21:37:08 -04:00
Kenneth Reitz 31e4c39762 Updated tests for reverted methods. 2010-09-20 21:34:01 -04:00
Kenneth Reitz 4fc70957ac Reverted methods back to properties. 2010-09-20 21:33:48 -04:00
Kenneth Reitz 7f17ccf445 Merge branch 'hotfix/dict' into develop 2010-09-20 14:37:36 -04:00
Kenneth Reitz fbcc3b60af Merge branch 'hotfix/dict' 2010-09-20 14:37:26 -04:00
Kenneth Reitz 9b3268f0ad Whoops. 2010-09-20 14:37:10 -04:00
Kenneth Reitz f386ef8ac8 Merge branch 'feature/unicode' into develop 2010-09-20 14:18:55 -04:00
Kenneth Reitz e8f5e023c4 Version bump (v0.7.0). 2010-09-20 14:18:31 -04:00
Kenneth Reitz 81445aeec8 Updated readme to reflect property to method changes. 2010-09-20 14:05:15 -04:00
Kenneth Reitz f94a236122 Changed export properties to methods. 2010-09-20 14:04:02 -04:00
Kenneth Reitz bfbb7c626f Moved from cStringIO to StringIO. More stable. 2010-09-20 12:50:10 -04:00
Kenneth Reitz be0f77f9ee Merge branch 'release/0.6.4' into develop 2010-09-20 09:21:51 -04:00
Kenneth Reitz 3b44349090 Version bump (0.6.4). 2010-09-20 09:21:02 -04:00
Kenneth Reitz 04a16afa58 Chmox. 2010-09-20 09:14:20 -04:00
Kenneth Reitz a8632125dc Merge branch 'master' into dev 2010-09-20 09:07:31 -04:00
Kenneth Reitz ccf2ebcde2 Version bump (v0.6.4) 2010-09-20 08:57:49 -04:00
Kenneth Reitz 2c60ce9233 String decoding to avoid unicode collisions for XLS output. 2010-09-19 23:51:48 -04:00
Kenneth Reitz 649c7e8bb7 Removed unneeded tuple_check. 2010-09-19 23:31:05 -04:00
Kenneth Reitz 2d3dc5ef71 PEP257. 2010-09-19 23:26:37 -04:00
Kenneth Reitz efc516f366 PEP8. 2010-09-19 23:23:03 -04:00
Kenneth Reitz b2a51fd941 Merge branch 'durden' into develop 2010-09-19 23:13:29 -04:00
Luke Lee d54d70bc22 Added test for csv export 2010-09-19 17:04:14 -05:00
Luke Lee 391ad61bef Improved del test
- Added testing for data set width/height
2010-09-19 16:41:23 -05:00
Luke Lee 99a45814d1 Added tests del functionality 2010-09-19 16:36:17 -05:00
Luke Lee fad3546614 Added docstrings 2010-09-19 16:25:18 -05:00
Luke Lee 7ba2849829 Misc. PEP8 whitespace celeanup 2010-09-19 16:16:31 -05:00
Luke Lee 7ec0f2ef07 Attempt at merging upstream develop branch
- Kept the slicing tests in tact by leaving their setup info. in the main setup
- Moved around some of the test methods to organize them a bit by functionality
2010-09-19 16:14:27 -05:00
Luke Lee bd470684a4 Ignore file update
- Update ignoring of python leftovers
- Added vi noise
2010-09-19 16:06:47 -05:00
Kenneth Reitz dbcea81c17 Inline docs. 2010-09-16 00:59:58 -04:00
Kenneth Reitz 49dc4a249e Removed useless is_string function. 2010-09-15 23:46:56 -04:00
Kenneth Reitz 7cd82f956f Version Bump. 2010-09-15 23:46:40 -04:00
Kenneth Reitz 13c3e537fd reamde update 2010-09-14 00:09:04 -04:00
Kenneth Reitz f913853cae Merge branch 'release/0.6.3' 2010-09-14 00:07:19 -04:00
Kenneth Reitz ea1de420a3 Merge branch 'release/0.6.3' 2010-09-14 00:02:38 -04:00
Kenneth Reitz d0c8df95a3 Version bump. v0.6.3. 2010-09-14 00:02:14 -04:00
Kenneth Reitz bb4e97f8aa Updated readme for column additions. 2010-09-14 00:01:59 -04:00
Kenneth Reitz ffaeb64639 Merge branch 'feature/add-cols' into develop 2010-09-13 23:56:08 -04:00
Kenneth Reitz f31ec562b4 Extensively testing 2010-09-13 23:55:17 -04:00
Kenneth Reitz 68d7204b2d Added data.append(col=[]) support. 2010-09-13 23:25:49 -04:00
Luke Lee 52db1ddc3e Fixed typo in test from previous commit 2010-09-13 21:27:35 -05:00
Luke Lee 4755020dd7 Added extra row to base data set
- Testing with 3 rows is a bit more interesting
2010-09-13 21:26:15 -05:00
Luke Lee 5468dd7e67 Added test for slicing data elements 2010-09-13 21:23:20 -05:00
Luke Lee 8673710ddb Refactored creation of data set into setUp
- Broke out tuples for more robust comparisions
2010-09-13 21:08:31 -05:00
Luke Lee f01cf184d4 Added simple test for slicing by headers 2010-09-13 21:03:29 -05:00
Luke Lee 1482ca4a19 Adding docstrings 2010-09-13 20:32:36 -05:00
Luke Lee 93c6c39581 Misc. pep8 cleanups including spaces after ',' and blank line organization 2010-09-13 20:23:31 -05:00
Kenneth Reitz a0cb44cc43 Made Struct really powerful. 2010-09-13 20:03:46 -04:00
Kenneth Reitz b2cd061773 Updated Roadmap 2010-09-13 18:13:20 -04:00
Kenneth Reitz 876b849950 mend 2010-09-13 17:44:28 -04:00
14 changed files with 695 additions and 153 deletions
+4 -2
View File
@@ -4,8 +4,8 @@ dist/*
MANIFEST
# python skin
.pyc
.pyo
*.pyc
*.pyo
# osx noise
.DS_Store
@@ -15,3 +15,5 @@ profile
.idea
.idea/*
# vi noise
*.swp
+1 -1
View File
@@ -10,4 +10,4 @@ Development Lead
Patches and Suggestions
```````````````````````
- A Lucky Someone
- Luke Lee
+40
View File
@@ -1,6 +1,46 @@
History
=======
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)
------------------
* Reverting methods back to properties.
* Windows bug compenated in documentation.
0.7.0 (2010-09-20)
------------------
* Renamed DataBook Databook for consistiency.
* Export properties changed to methods (XLS filename / StringIO bug).
* Optional Dataset.xls(path='filename') support (for writing on windows).
* Added utf-8 on the worksheet level.
0.6.4 (2010-09-19)
------------------
* Updated unicode export for XLS.
* More exhaustive unit tests.
0.6.3 (2010-09-14)
------------------
* Added Dataset.append() support for columns.
0.6.2 (2010-09-13)
------------------
* Fixed Dataset.append() error on empty dataset.
+67 -18
View File
@@ -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
-----
@@ -31,11 +43,11 @@ Usage
Populate fresh data files: ::
headers = ('first_name', 'last_name', 'gpa')
headers = ('first_name', 'last_name')
data = [
('John', 'Adams', 90),
('George', 'Washington', 67)
('John', 'Adams'),
('George', 'Washington')
]
data = tablib.Dataset(*data, headers=headers)
@@ -43,7 +55,11 @@ Populate fresh data files: ::
Intelligently add new rows: ::
>>> data.append(('Henry', 'Ford', 83))
>>> data.append(('Henry', 'Ford'))
Intelligently add new columns: ::
>>> data.append(col=('age', 90, 67, 83))
Slice rows: ::
@@ -60,6 +76,9 @@ Easily delete rows: ::
>>> del data[1]
Exports
-------
Drumroll please...........
JSON!
@@ -102,11 +121,44 @@ EXCEL!
++++++
::
>>> open('people.xls').write(data.xls)
>>> 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
------------
@@ -122,18 +174,15 @@ Or, if you absolutely must: ::
Contribute
----------
If you'd like to contribute, simply fork `the repository`_, commit your changes, and send a pull request. Make sure you add yourself to AUTHORS_.
If you'd like to contribute, simply fork `the repository`_, commit your changes to the **develop** branch (or branch off of it), and send a pull request. Make sure you add yourself to AUTHORS_.
Roadmap
-------
- Add ability to add/remove full columns
- Import datasets from CSV, JSON, YAML
- Release CLI Interface
- Auto-detect import format
- Add possible other exports (SQL?)
- Possibly plugin-ify format architecture
- Plugin support
- Ability to assign types to rows (set, regex=, &c.)
.. _`the repository`: http://github.com/kennethreitz/tablib
.. _AUTHORS: http://github.com/kennethreitz/tablib/blob/master/AUTHORS
.. _AUTHORS: http://github.com/kennethreitz/tablib/blob/master/AUTHORS
+2 -3
View File
@@ -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.6.2',
version='0.8.1',
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=(
+8 -1
View File
@@ -1 +1,8 @@
from core import *
""" Tablib.
"""
from tablib.core import (
Databook, Dataset, InvalidDatasetType,
InvalidDimensions, UnsupportedFormat
)
+120 -108
View File
@@ -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.6.1'
__build__ = 0x000601
__title__ = 'tablib'
__version__ = '0.8.1'
__build__ = 0x000801
__author__ = 'Kenneth Reitz'
__license__ = 'MIT'
__copyright__ = 'Copyright 2010 Kenneth Reitz'
@@ -32,29 +18,27 @@ 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
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
def __getitem__(self, key):
if is_string(key):
if isinstance(key, basestring):
if key in self.headers:
pos = self.headers.index(key) # get 'key' index from each data
return [row[pos] for row in self._data]
@@ -79,11 +63,30 @@ 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, safety=False):
def _validate(self, row=None, col=None, safety=False):
"""Assures size of every row in dataset is of proper proportions."""
if row:
is_valid = (len(row) == self.width) if self.width else True
elif col:
if self.headers:
is_valid = (len(col) - 1) == self.height
else:
is_valid = (len(col) == self.height) if self.height else True
else:
is_valid = all((len(x)== self.width for x in self._data))
@@ -108,6 +111,7 @@ class Dataset(object):
return data
@property
def height(self):
"""Returns the height of the Dataset."""
@@ -119,86 +123,101 @@ 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."""
return self.__headers
@headers.setter
def headers(self, collection):
"""Validating headers setter."""
self._validate(collection)
self.__headers = collection
if collection:
try:
self.__headers = list(collection)
except TypeError:
raise TypeError
else:
self.__headers = None
@property
def dict(self):
"""Returns python dict of Dataset."""
return self._package()
@property
def json(self):
"""Returns JSON representation of Dataset."""
return json.dumps(self.dict)
@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
@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."""
stream = cStringIO.StringIO()
wb = xlwt.Workbook()
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, str(col))
wb.save(stream)
return stream.getvalue()
def append(self, row):
def append(self, row=None, col=None):
"""Adds a row to the end of Dataset"""
self._validate(row)
self._data.append(tuple(row))
if row:
self._validate(row)
self._data.append(tuple(row))
elif col:
self._validate(col=col)
if self.headers:
# pop the first item off, add to headers
self.headers.append(col[0])
col = col[1:]
if self.height and self.width:
for i, row in enumerate(self._data):
_row = list(row)
_row.append(col[i])
self._data[i] = tuple(_row)
else:
self._data = [tuple([row]) for row in col]
def index(self, i, row):
def insert(self, i, row=None, col=None):
"""Inserts a row at given position in Dataset"""
self._validate(row)
self._data.insert(i, tuple(row))
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):
class Databook(object):
"""A book of Dataset objects.
Currently, this exists only for XLS workbook support.
"""
def __init__(self, sets=[]):
self._datasets = sets
self._register_formats()
def __repr__(self):
try:
@@ -206,14 +225,34 @@ class DataBook(object):
except AttributeError:
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(
@@ -222,48 +261,21 @@ class DataBook(object):
))
return collector
@property
def size(self):
"""The number of the Datasets within DataBook."""
return len(self._datasets)
@property
def xls(self):
"""Returns XLS representation of DataBook."""
stream = cStringIO.StringIO()
wb = xlwt.Workbook()
for dset in self._datasets:
ws = wb.add_sheet(dset.title if dset.title else 'Tabbed Dataset %s' % (int(random.random() * 100000000)))
#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, str(col))
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())
class InvalidDatasetType(Exception):
"Only Datasets can be added to a DataBook"
class InvalidDimensions(Exception):
"Invalid size"
class UnsupportedFormat(NotImplementedError):
"Format is not supported"
+11
View File
@@ -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 = (csv, json, xls, yaml)
+42
View File
@@ -0,0 +1,42 @@
# -*- 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)
+38
View File
@@ -0,0 +1,38 @@
# -*- 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)
+45
View File
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
""" Tablib - XLS Support.
"""
import xlwt
import cStringIO
title = 'xls'
extentions = ('xls',)
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')
for i, row in enumerate(dataset._package(dicts=False)):
for j, col in enumerate(row):
ws.write(i, j, col)
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))
#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()
+42
View File
@@ -0,0 +1,42 @@
# -*- 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)
+19 -7
View File
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
""" Tablib - General Helpers.
"""
import sys
@@ -10,16 +13,25 @@ class Struct(object):
self.__dict__.update(entries)
def __getitem__(self, key):
return getattr(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"""
"""Returns piped input via stdin, else False."""
with sys.stdin as stdin:
# TTY is only way to detect if stdin contains data
return stdin.read() if not stdin.isatty() else None
def is_string(obj):
"""Tests if an object is a string"""
return True if type(obj).__name__ == 'str' else False
Regular → Executable
+256 -13
View File
@@ -1,40 +1,283 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Tests for tablib."""
import unittest
import tablib
class TablibTestCase(unittest.TestCase):
"""Tablib test cases."""
def setUp(self):
pass
"""Create simple data set with headers."""
global data, book
data = tablib.Dataset()
book = tablib.Databook()
self.headers = ('first_name', 'last_name', 'gpa')
self.john = ('John', 'Adams', 90)
self.george = ('George', 'Washington', 67)
self.tom = ('Thomas', 'Jefferson', 50)
self.founders = tablib.Dataset(headers=self.headers)
self.founders.append(self.john)
self.founders.append(self.george)
self.founders.append(self.tom)
def tearDown(self):
"""Teardown."""
pass
def test_empty_append(self):
data = tablib.Dataset()
new_row = (1,2,3)
"""Verify append() correctly adds tuple with no headers."""
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)
def test_empty_append_with_headers(self):
data = tablib.Dataset()
"""Verify append() correctly detects mismatch of number of
headers and data.
"""
data.headers = ['first', 'second']
new_row = (1,2,3,4)
new_row = (1, 2, 3, 4)
self.assertRaises(tablib.InvalidDimensions, data.append, new_row)
def test_add_column(self):
"""Verify adding column works with/without headers."""
data.append(['kenneth'])
data.append(['bessie'])
new_col = ['reitz', 'monke']
data.append(col=new_col)
self.assertEquals(data[0], ('kenneth', 'reitz'))
self.assertEquals(data.width, 2)
# With Headers
data.headers = ('fname', 'lname')
new_col = ['age', 21, 22]
data.append(col=new_col)
self.assertEquals(data[new_col[0]], new_col[1:])
def test_add_column_no_data_no_headers(self):
"""Verify adding new column with no headers."""
new_col = ('reitz', 'monke')
data.append(col=new_col)
self.assertEquals(data[0], tuple([new_col[0]]))
self.assertEquals(data.width, 1)
self.assertEquals(data.height, len(new_col))
def test_add_column_no_data_with_headers(self):
"""Verify adding new column with headers."""
data.headers = ('first', 'last')
new_col = ('age',)
data.append(col=new_col)
self.assertEquals(len(data.headers), 3)
self.assertEquals(data.width, 3)
new_col = ('foo', 'bar')
self.assertRaises(tablib.InvalidDimensions, data.append, col=new_col)
def test_header_slicing(self):
"""Verify slicing by headers."""
self.assertEqual(self.founders['first_name'],
[self.john[0], self.george[0], self.tom[0]])
self.assertEqual(self.founders['last_name'],
[self.john[1], self.george[1], self.tom[1]])
self.assertEqual(self.founders['gpa'],
[self.john[2], self.george[2], self.tom[2]])
def test_data_slicing(self):
"""Verify slicing by data."""
# Slice individual rows
self.assertEqual(self.founders[0], self.john)
self.assertEqual(self.founders[:1], [self.john])
self.assertEqual(self.founders[1:2], [self.george])
self.assertEqual(self.founders[-1], self.tom)
self.assertEqual(self.founders[3:], [])
# Slice multiple rows
self.assertEqual(self.founders[:], [self.john, self.george, self.tom])
self.assertEqual(self.founders[0:2], [self.john, self.george])
self.assertEqual(self.founders[1:3], [self.george, self.tom])
self.assertEqual(self.founders[2:], [self.tom])
def test_delete(self):
"""Verify deleting from dataset works."""
# Delete from front of object
del self.founders[0]
self.assertEqual(self.founders[:], [self.george, self.tom])
# Verify dimensions, width should NOT change
self.assertEqual(self.founders.height, 2)
self.assertEqual(self.founders.width, 3)
# Delete from back of object
del self.founders[1]
self.assertEqual(self.founders[:], [self.george])
# Verify dimensions, width should NOT change
self.assertEqual(self.founders.height, 1)
self.assertEqual(self.founders.width, 3)
# Delete from invalid index
self.assertRaises(IndexError, self.founders.__delitem__, 3)
def test_csv_export(self):
"""Verify exporting dataset object as CSV."""
# Build up the csv string with headers first, followed by each row
csv = ''
for col in self.headers:
csv += col + ','
csv = csv.strip(',') + '\r\n'
for founder in self.founders:
for col in founder:
csv += str(col) + ','
csv = csv.strip(',') + '\r\n'
self.assertEqual(csv, self.founders.csv)
def test_unicode_append(self):
"""Passes in a single unicode charecter and exports."""
new_row = ('å', 'é')
data.append(new_row)
data.json
data.yaml
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_adding_header with (self):
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_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()
unittest.main()