Compare commits

..

31 Commits

Author SHA1 Message Date
Iuri de Silvio 6afe716d64 Version bump: 0.11.4 2017-01-23 19:10:36 -02:00
Iuri de Silvio 3d44bdec40 Merge pull request #269 from kammala/master
Fixed classifiers in setup.py
2017-01-10 10:14:11 -02:00
kammala 319505817a Fixed classifiers in setup.py
moved classifiers from tuple to list(this allow to use setup.py upload command in python >= 3.5)
2017-01-10 12:20:08 +03:00
kennethreitz 6cb9a69746 Merge pull request #266 from wenzhihong2003/master
remove file must be close it.
2017-01-05 12:51:01 -05:00
tomwen bb1354b61f remove file must be close it.
in windows if you don't close template file, remove it will raise

WindowsError: [Error 32]
2016-12-30 10:21:43 +08:00
Iuri de Silvio ddc4bd30f2 Merge pull request #234 from BrianPainter/master
if the object is a decimal, return the string representation of it.
2016-12-18 19:10:18 -02:00
Iuri de Silvio 52e547daf9 Merge pull request #259 from dyve/master
Fix #260 date and datetime export to JSON
2016-12-18 19:09:26 -02:00
Iuri de Silvio 7f0b7a0a22 Merge pull request #263 from andriisoldatenko/develop
Remove LOCALE from str regular expression
2016-12-18 19:08:41 -02:00
Andrii Soldatenko ddac443732 Added py36 to tox.ini 2016-12-18 17:04:28 +02:00
Andrii Soldatenko e13f4d0aba Added py35 to tox.ini 2016-12-18 16:54:22 +02:00
Andrii Soldatenko 54f9041f2c Remove LOCALE from str regular expression 2016-12-18 16:44:18 +02:00
Dylan Verheul 91d3299280 Fix date and datetime export to JSON in Python versions with a json package
Python without a json package will use omnijson and fail on date and datetime objects.
Added unit tests.
2016-11-30 12:32:47 +01:00
kennethreitz f59abe84be Merge pull request #239 from ErwinJunge/dataset-title-in-docs
Put Dataset.title in the documentation
2016-05-21 15:13:30 -04:00
Erwin Junge cf23f2344f Put Dataset.title in the documentation 2016-05-20 16:13:22 +02:00
kennethreitz e16bb38c48 Merge pull request #238 from sushrutrathi/code_changes
changes in code refactoring
2016-05-03 21:30:25 -04:00
Sushrut Rathi 71ca275dd1 changes in code refactoring 2016-05-03 13:47:25 +05:30
kennethreitz 75bbfbbaf4 Merge pull request #233 from ScorpionResponse/html_book_test
Add HTML format to the book_export test and fix the format to work properly
2016-04-10 18:12:37 -04:00
Iuri de Silvio b35d505621 Merge pull request #236 from candy0427/master
Update README.rst
2016-03-24 10:18:34 -03:00
CandyLikeSmile cd491c062c Update README.rst 2016-03-24 14:19:59 +08:00
Brian Painter 9fdb72cc5c if the object is a decimal, return the string representation of it. 2016-03-23 08:21:36 -04:00
kennethreitz a5b1f7987e Merge pull request #232 from chimeno/patch-1
[docs] Update variable name in tuto
2016-03-18 14:37:19 -04:00
Paul Moss 8cf6770a76 Add HTML format to the book_export test and fix the format to work properly 2016-03-18 18:17:19 +00:00
Daniel Chimeno 5fa3d2f886 [docs] Update variable name in tuto
The tutorial has been using the 'data' variable, but in this case it's using 'd'.
This change that.
2016-03-18 09:22:59 +01:00
kennethreitz d4c66c7a4e Merge pull request #229 from pmlandwehr/patch-1
python 3 fix: map filter to ifilter
2016-02-28 00:20:37 -05:00
Peter M. Landwehr af17586581 python 3 fix: map filter to ifilter 2016-02-27 21:14:13 -08:00
kennethreitz 23d21f00f3 Update HISTORY.rst 2016-02-25 12:59:18 -05:00
kennethreitz 7ee924b5a6 Merge pull request #228 from tomchristie/print-dataset-with-no-headers
Fixed textual representation for Dataset with no headers
2016-02-25 12:58:35 -05:00
Tom Christie d720beadac Fixed __unicode__/__str__ for dataset with no headers 2016-02-25 13:28:29 +00:00
kennethreitz ee9666a146 Merge pull request #225 from tusharmakkar08/master
PEP-8 standards followed
2016-02-22 09:17:34 -05:00
tusharmakkar08 77a9e25795 Reverted back yaml3 for ci failure 2016-02-22 17:05:06 +05:30
tusharmakkar08 d515724817 PEP-8 standards followed 2016-02-22 16:36:26 +05:30
25 changed files with 344 additions and 321 deletions
+3
View File
@@ -26,3 +26,6 @@ junit-py27.xml
# tox noise
.tox
# pyenv noise
.python-version
+2
View File
@@ -32,3 +32,5 @@ Patches and Suggestions
- Rabin Nankhwa
- Marco Dallagiacoma
- Mathias Loesch
- Tushar Makkar
- Andrii Soldatenko
+11
View File
@@ -1,6 +1,17 @@
History
-------
0.11.4 (2017-01-23)
+++++++++++++++++++
- Use built-in `json` package if available
- Support Python 3.5+ in classifiers
** Bugfixes **
- Fixed textual representation for Dataset with no headers
- Handle decimal types
0.11.3 (2016-02-16)
+++++++++++++++++++
+1 -1
View File
@@ -1,7 +1,7 @@
Tablib: format-agnostic tabular dataset library
===============================================
.. image:: https://travis-ci.org/kennethreitz/tablib.svg?branch=develop
.. image:: https://travis-ci.org/kennethreitz/tablib.svg?branch=master
:target: https://travis-ci.org/kennethreitz/tablib
::
+2 -2
View File
@@ -158,9 +158,9 @@ To do so, we access the :class:`Dataset` as if it were a standard Python diction
You can also access the column using its index. ::
>>> d.headers
>>> data.headers
['Last Name', 'First Name', 'Age']
>>> d.get_col(1)
>>> data.get_col(1)
['Kenneth', 'Bessie']
Let's find the average age. ::
+4 -2
View File
@@ -73,7 +73,7 @@ setup(
url='http://python-tablib.org',
packages=packages,
license='MIT',
classifiers=(
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Natural Language :: English',
@@ -87,6 +87,8 @@ setup(
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
),
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
],
tests_require=['pytest'],
)
+1
View File
@@ -34,6 +34,7 @@ if is_py3:
from io import StringIO
# py3 mappings
ifilter = filter
unicode = str
bytes = bytes
basestring = str
+7 -4
View File
@@ -18,8 +18,8 @@ from tablib.compat import OrderedDict, unicode
__title__ = 'tablib'
__version__ = '0.11.3'
__build__ = 0x001103
__version__ = '0.11.4'
__build__ = 0x001104
__author__ = 'Kenneth Reitz'
__license__ = 'MIT'
__copyright__ = 'Copyright 2016 Kenneth Reitz'
@@ -143,6 +143,7 @@ class Dataset(object):
:param \*args: (optional) list of rows to populate Dataset
:param headers: (optional) list strings for Dataset header row
:param title: (optional) string to use as title of the Dataset
.. admonition:: Format Attributes Definition
@@ -223,7 +224,8 @@ class Dataset(object):
result = []
# Add unicode representation of headers.
result.append([unicode(h) for h in self.__headers])
if self.__headers:
result.append([unicode(h) for h in self.__headers])
# Add unicode representation of rows.
result.extend(list(map(unicode, row)) for row in self._data)
@@ -232,7 +234,8 @@ class Dataset(object):
field_lens = list(map(max, zip(*lens)))
# delimiter between header and data
result.insert(1, ['-' * length for length in field_lens])
if self.__headers:
result.insert(1, ['-' * length for length in field_lens])
format_string = '|'.join('{%s:%s}' % item for item in enumerate(field_lens))
+1
View File
@@ -55,6 +55,7 @@ def export_set(dataset):
else:
stream = StringIO(dbf_stream.read())
dbf_stream.close()
os.close(temp_file)
os.remove(temp_uri)
return stream.getvalue()
+9 -6
View File
@@ -31,7 +31,7 @@ def export_set(dataset):
page.table.open()
if dataset.headers is not None:
new_header = [item if item is not None else '' for item in dataset.headers]
new_header = [item if item is not None else '' for item in dataset.headers]
page.thead.open()
headers = markup.oneliner.th(new_header)
@@ -39,7 +39,7 @@ def export_set(dataset):
page.thead.close()
for row in dataset:
new_row = [item if item is not None else '' for item in row]
new_row = [item if item is not None else '' for item in row]
html_row = markup.oneliner.td(new_row)
page.tr(html_row)
@@ -58,10 +58,13 @@ def export_book(databook):
stream = StringIO()
# Allow unicode characters in output
wrapper = codecs.getwriter("utf8")(stream)
for i, dset in enumerate(databook._datasets):
title = (dset.title if dset.title else 'Set %s' % (i))
stream.write('<%s>%s</%s>\n' % (BOOK_ENDINGS, title, BOOK_ENDINGS))
stream.write(dset.html)
stream.write('\n')
wrapper.write('<%s>%s</%s>\n' % (BOOK_ENDINGS, title, BOOK_ENDINGS))
wrapper.write(dset.html)
wrapper.write('\n')
return stream.getvalue()
return stream.getvalue().decode('utf-8')
+12 -3
View File
@@ -2,11 +2,14 @@
""" Tablib - JSON Support
"""
import decimal
import tablib
import sys
from tablib.packages import omnijson as json
try:
import json
except ImportError:
from tablib.packages import omnijson as json
title = 'json'
@@ -14,7 +17,13 @@ extensions = ('json', 'jsn')
def date_handler(obj):
return obj.isoformat() if hasattr(obj, 'isoformat') else obj
if isinstance(obj, decimal.Decimal):
return str(obj)
elif hasattr(obj, 'isoformat'):
return obj.isoformat()
else:
return obj
# return obj.isoformat() if hasattr(obj, 'isoformat') else obj
def export_set(dataset):
+1 -4
View File
@@ -13,16 +13,12 @@ except ImportError:
else:
import tablib.packages.yaml as yaml
import tablib
title = 'yaml'
extensions = ('yaml', 'yml')
def export_set(dataset):
"""Returns YAML representation of Dataset."""
@@ -52,6 +48,7 @@ def import_book(dbook, in_stream):
data.dict = sheet['data']
dbook.add_sheet(data)
def detect(stream):
"""Returns True if given stream is valid YAML."""
try:
+28 -23
View File
@@ -63,9 +63,10 @@ __author__ = "Jeff Kunce <kuncej@mail.conservation.state.mo.us>"
__all__ = ["Dbf"]
from . import header
from .import record
from . import record
from utils import INVALID_VALUE
class Dbf(object):
"""DBF accessor.
@@ -82,13 +83,13 @@ class Dbf(object):
"""
__slots__ = ("name", "header", "stream",
"_changed", "_new", "_ignore_errors")
"_changed", "_new", "_ignore_errors")
HeaderClass = header.DbfHeader
RecordClass = record.DbfRecord
INVALID_VALUE = INVALID_VALUE
## initialization and creation helpers
# initialization and creation helpers
def __init__(self, f, readOnly=False, new=False, ignoreErrors=False):
"""Initialize instance.
@@ -137,7 +138,7 @@ class Dbf(object):
self._new = bool(new)
self._changed = False
## properties
# properties
closed = property(lambda self: self.stream.closed)
recordCount = property(lambda self: self.header.recordCount)
@@ -149,6 +150,7 @@ class Dbf(object):
def ignoreErrors(self, value):
"""Update `ignoreErrors` flag on the header object and self"""
self.header.ignoreErrors = self._ignore_errors = bool(value)
ignoreErrors = property(
lambda self: self._ignore_errors,
ignoreErrors,
@@ -159,7 +161,7 @@ class Dbf(object):
""")
## protected methods
# protected methods
def _fixIndex(self, index):
"""Return fixed index.
@@ -185,7 +187,7 @@ class Dbf(object):
raise IndexError("Record index out of range")
return index
## iterface methods
# iterface methods
def close(self):
self.flush()
@@ -226,9 +228,9 @@ class Dbf(object):
self.header.addField(*defs)
else:
raise TypeError("At least one record was added, "
"structure can't be changed")
"structure can't be changed")
## 'magic' methods (representation and sequence interface)
# 'magic' methods (representation and sequence interface)
def __repr__(self):
return "Dbf stream '%s'\n" % self.stream + repr(self.header)
@@ -248,19 +250,20 @@ class Dbf(object):
self._changed = True
self._new = False
#def __del__(self):
# """Flush stream upon deletion of the object."""
# self.flush()
# def __del__(self):
# """Flush stream upon deletion of the object."""
# self.flush()
def demoRead(filename):
def demo_read(filename):
_dbf = Dbf(filename, True)
for _rec in _dbf:
print
print(repr(_rec))
_dbf.close()
def demoCreate(filename):
def demo_create(filename):
_dbf = Dbf(filename, new=True)
_dbf.addField(
("NAME", "C", 15),
@@ -269,10 +272,10 @@ def demoCreate(filename):
("BIRTHDATE", "D"),
)
for (_n, _s, _i, _b) in (
("John", "Miller", "YC", (1981, 1, 2)),
("Andy", "Larkin", "AL", (1982, 3, 4)),
("Bill", "Clinth", "", (1983, 5, 6)),
("Bobb", "McNail", "", (1984, 7, 8)),
("John", "Miller", "YC", (1981, 1, 2)),
("Andy", "Larkin", "AL", (1982, 3, 4)),
("Bill", "Clinth", "", (1983, 5, 6)),
("Bobb", "McNail", "", (1984, 7, 8)),
):
_rec = _dbf.newRecord()
_rec["NAME"] = _n
@@ -283,10 +286,12 @@ def demoCreate(filename):
print(repr(_dbf))
_dbf.close()
if (__name__=='__main__'):
import sys
_name = len(sys.argv) > 1 and sys.argv[1] or "county.dbf"
demoCreate(_name)
demoRead(_name)
# vim: set et sw=4 sts=4 :
if __name__ == '__main__':
import sys
_name = len(sys.argv) > 1 and sys.argv[1] or "county.dbf"
demo_create(_name)
demo_read(_name)
# vim: set et sw=4 sts=4 :
+16 -15
View File
@@ -29,6 +29,7 @@ from fields import *
from header import *
from record import *
class _FieldDefinition(object):
"""Field definition.
@@ -151,28 +152,28 @@ class dbf_new(object):
_dbfh.write(stream)
if (__name__=='__main__'):
if __name__ == '__main__':
# create a new DBF-File
dbfn=dbf_new()
dbfn.add_field("name",'C',80)
dbfn.add_field("price",'N',10,2)
dbfn.add_field("date",'D',8)
dbfn = dbf_new()
dbfn.add_field("name", 'C', 80)
dbfn.add_field("price", 'N', 10, 2)
dbfn.add_field("date", 'D', 8)
dbfn.write("tst.dbf")
# test new dbf
print "*** created tst.dbf: ***"
dbft = Dbf('tst.dbf', readOnly=0)
print repr(dbft)
# add a record
rec=DbfRecord(dbft)
rec['name']='something'
rec['price']=10.5
rec['date']=(2000,1,12)
rec = DbfRecord(dbft)
rec['name'] = 'something'
rec['price'] = 10.5
rec['date'] = (2000, 1, 12)
rec.store()
# add another record
rec=DbfRecord(dbft)
rec['name']='foo and bar'
rec['price']=12234
rec['date']=(1992,7,15)
rec = DbfRecord(dbft)
rec['name'] = 'foo and bar'
rec['price'] = 12234
rec['date'] = (1992, 7, 15)
rec.store()
# show the records
@@ -181,8 +182,8 @@ if (__name__=='__main__'):
for i1 in range(len(dbft)):
rec = dbft[i1]
for fldName in dbft.fieldNames:
print '%s:\t %s'%(fldName, rec[fldName])
print '%s:\t %s' % (fldName, rec[fldName])
print
dbft.close()
# vim: set et sts=4 sw=4 :
# vim: set et sts=4 sw=4 :
+24 -19
View File
@@ -66,6 +66,7 @@ from . import header
from . import record
from .utils import INVALID_VALUE
class Dbf(object):
"""DBF accessor.
@@ -82,13 +83,13 @@ class Dbf(object):
"""
__slots__ = ("name", "header", "stream",
"_changed", "_new", "_ignore_errors")
"_changed", "_new", "_ignore_errors")
HeaderClass = header.DbfHeader
RecordClass = record.DbfRecord
INVALID_VALUE = INVALID_VALUE
## initialization and creation helpers
# initialization and creation helpers
def __init__(self, f, readOnly=False, new=False, ignoreErrors=False):
"""Initialize instance.
@@ -137,7 +138,7 @@ class Dbf(object):
self._new = bool(new)
self._changed = False
## properties
# properties
closed = property(lambda self: self.stream.closed)
recordCount = property(lambda self: self.header.recordCount)
@@ -149,6 +150,7 @@ class Dbf(object):
def ignoreErrors(self, value):
"""Update `ignoreErrors` flag on the header object and self"""
self.header.ignoreErrors = self._ignore_errors = bool(value)
ignoreErrors = property(
lambda self: self._ignore_errors,
ignoreErrors,
@@ -159,7 +161,7 @@ class Dbf(object):
""")
## protected methods
# protected methods
def _fixIndex(self, index):
"""Return fixed index.
@@ -185,7 +187,7 @@ class Dbf(object):
raise IndexError("Record index out of range")
return index
## iterface methods
# iterface methods
def close(self):
self.flush()
@@ -227,9 +229,9 @@ class Dbf(object):
self.header.addField(*defs)
else:
raise TypeError("At least one record was added, "
"structure can't be changed")
"structure can't be changed")
## 'magic' methods (representation and sequence interface)
# 'magic' methods (representation and sequence interface)
def __repr__(self):
return "Dbf stream '%s'\n" % self.stream + repr(self.header)
@@ -249,19 +251,20 @@ class Dbf(object):
self._changed = True
self._new = False
#def __del__(self):
# """Flush stream upon deletion of the object."""
# self.flush()
# def __del__(self):
# """Flush stream upon deletion of the object."""
# self.flush()
def demoRead(filename):
def demo_read(filename):
_dbf = Dbf(filename, True)
for _rec in _dbf:
print()
print(repr(_rec))
_dbf.close()
def demoCreate(filename):
def demo_create(filename):
_dbf = Dbf(filename, new=True)
_dbf.addField(
("NAME", "C", 15),
@@ -270,10 +273,10 @@ def demoCreate(filename):
("BIRTHDATE", "D"),
)
for (_n, _s, _i, _b) in (
("John", "Miller", "YC", (1981, 1, 2)),
("Andy", "Larkin", "AL", (1982, 3, 4)),
("Bill", "Clinth", "", (1983, 5, 6)),
("Bobb", "McNail", "", (1984, 7, 8)),
("John", "Miller", "YC", (1981, 1, 2)),
("Andy", "Larkin", "AL", (1982, 3, 4)),
("Bill", "Clinth", "", (1983, 5, 6)),
("Bobb", "McNail", "", (1984, 7, 8)),
):
_rec = _dbf.newRecord()
_rec["NAME"] = _n
@@ -284,10 +287,12 @@ def demoCreate(filename):
print(repr(_dbf))
_dbf.close()
if (__name__=='__main__'):
if __name__ == '__main__':
import sys
_name = len(sys.argv) > 1 and sys.argv[1] or "county.dbf"
demoCreate(_name)
demoRead(_name)
demo_create(_name)
demo_read(_name)
# vim: set et sw=4 sts=4 :
+15 -14
View File
@@ -29,6 +29,7 @@ from .fields import *
from .header import *
from .record import *
class _FieldDefinition(object):
"""Field definition.
@@ -145,28 +146,28 @@ class dbf_new(object):
_dbfStream.close()
if (__name__=='__main__'):
if __name__ == '__main__':
# create a new DBF-File
dbfn=dbf_new()
dbfn.add_field("name",'C',80)
dbfn.add_field("price",'N',10,2)
dbfn.add_field("date",'D',8)
dbfn = dbf_new()
dbfn.add_field("name", 'C', 80)
dbfn.add_field("price", 'N', 10, 2)
dbfn.add_field("date", 'D', 8)
dbfn.write("tst.dbf")
# test new dbf
print("*** created tst.dbf: ***")
dbft = Dbf('tst.dbf', readOnly=0)
print(repr(dbft))
# add a record
rec=DbfRecord(dbft)
rec['name']='something'
rec['price']=10.5
rec['date']=(2000,1,12)
rec = DbfRecord(dbft)
rec['name'] = 'something'
rec['price'] = 10.5
rec['date'] = (2000, 1, 12)
rec.store()
# add another record
rec=DbfRecord(dbft)
rec['name']='foo and bar'
rec['price']=12234
rec['date']=(1992,7,15)
rec = DbfRecord(dbft)
rec['name'] = 'foo and bar'
rec['price'] = 12234
rec['date'] = (1992, 7, 15)
rec.store()
# show the records
@@ -175,7 +176,7 @@ if (__name__=='__main__'):
for i1 in range(len(dbft)):
rec = dbft[i1]
for fldName in dbft.fieldNames:
print('%s:\t %s'%(fldName, rec[fldName]))
print('%s:\t %s' % (fldName, rec[fldName]))
print()
dbft.close()
+2 -4
View File
@@ -71,10 +71,8 @@ class element:
for key, value in kwargs.iteritems( ):
if value is not None: # when value is None that means stuff like <... checked>
key = key.strip('_') # strip this so class_ will mean class, etc.
if key == 'http_equiv': # special cases, maybe change _ to - overall?
key = 'http-equiv'
elif key == 'accept_charset':
key = 'accept-charset'
if key in ['http_equiv', 'accept_charset']:
key.replace('_','-')
out = u"%s %s=\"%s\"" % ( out, key, escape( value ) )
else:
out = u"%s %s" % ( out, key )
+2 -2
View File
@@ -4,7 +4,7 @@ import sys
from antlr import EOF, CommonToken as Tok, TokenStream, TokenStreamException
import struct
import ExcelFormulaParser
from re import compile as recompile, match, LOCALE, UNICODE, IGNORECASE, VERBOSE
from re import compile as recompile, match, UNICODE, IGNORECASE, VERBOSE
int_const_pattern = r"\d+\b"
@@ -51,7 +51,7 @@ pattern_type_tuples = (
_re = recompile(
'(' + ')|('.join([i[0] for i in pattern_type_tuples]) + ')',
VERBOSE+LOCALE+IGNORECASE)
VERBOSE+IGNORECASE)
_toktype = [None] + [i[1] for i in pattern_type_tuples]
# need dummy at start because re.MatchObject.lastindex counts from 1
+2 -2
View File
@@ -2,7 +2,7 @@ import sys
from .antlr import EOF, CommonToken as Tok, TokenStream, TokenStreamException
import struct
from . import ExcelFormulaParser
from re import compile as recompile, match, LOCALE, UNICODE, IGNORECASE, VERBOSE
from re import compile as recompile, match, UNICODE, IGNORECASE, VERBOSE
int_const_pattern = r"\d+\b"
@@ -49,7 +49,7 @@ pattern_type_tuples = (
_re = recompile(
'(' + ')|('.join([i[0] for i in pattern_type_tuples]) + ')',
VERBOSE+LOCALE+IGNORECASE)
VERBOSE+IGNORECASE)
_toktype = [None] + [i[1] for i in pattern_type_tuples]
# need dummy at start because re.MatchObject.lastindex counts from 1
+19 -20
View File
@@ -1,15 +1,16 @@
__all__ = ['Composer', 'ComposerError']
from error import MarkedYAMLError
from events import *
from nodes import *
from events import StreamEndEvent, StreamStartEvent, AliasEvent, SequenceEndEvent, SequenceStartEvent, MappingEndEvent,\
MappingStartEvent, ScalarEvent
from nodes import MappingNode, ScalarNode, SequenceNode
class ComposerError(MarkedYAMLError):
pass
class Composer(object):
class Composer(object):
def __init__(self):
self.anchors = {}
@@ -39,8 +40,8 @@ class Composer(object):
if not self.check_event(StreamEndEvent):
event = self.get_event()
raise ComposerError("expected a single document in the stream",
document.start_mark, "but found another document",
event.start_mark)
document.start_mark, "but found another document",
event.start_mark)
# Drop the STREAM-END event.
self.get_event()
@@ -66,15 +67,14 @@ class Composer(object):
anchor = event.anchor
if anchor not in self.anchors:
raise ComposerError(None, None, "found undefined alias %r"
% anchor.encode('utf-8'), event.start_mark)
% anchor.encode('utf-8'), event.start_mark)
return self.anchors[anchor]
event = self.peek_event()
anchor = event.anchor
if anchor is not None:
if anchor in self.anchors:
if anchor is not None and anchor in self.anchors:
raise ComposerError("found duplicate anchor %r; first occurence"
% anchor.encode('utf-8'), self.anchors[anchor].start_mark,
"second occurence", event.start_mark)
% anchor.encode('utf-8'), self.anchors[anchor].start_mark,
"second occurence", event.start_mark)
self.descend_resolver(parent, index)
if self.check_event(ScalarEvent):
node = self.compose_scalar_node(anchor)
@@ -91,7 +91,7 @@ class Composer(object):
if tag is None or tag == u'!':
tag = self.resolve(ScalarNode, event.value, event.implicit)
node = ScalarNode(tag, event.value,
event.start_mark, event.end_mark, style=event.style)
event.start_mark, event.end_mark, style=event.style)
if anchor is not None:
self.anchors[anchor] = node
return node
@@ -102,8 +102,8 @@ class Composer(object):
if tag is None or tag == u'!':
tag = self.resolve(SequenceNode, None, start_event.implicit)
node = SequenceNode(tag, [],
start_event.start_mark, None,
flow_style=start_event.flow_style)
start_event.start_mark, None,
flow_style=start_event.flow_style)
if anchor is not None:
self.anchors[anchor] = node
index = 0
@@ -120,20 +120,19 @@ class Composer(object):
if tag is None or tag == u'!':
tag = self.resolve(MappingNode, None, start_event.implicit)
node = MappingNode(tag, [],
start_event.start_mark, None,
flow_style=start_event.flow_style)
start_event.start_mark, None,
flow_style=start_event.flow_style)
if anchor is not None:
self.anchors[anchor] = node
while not self.check_event(MappingEndEvent):
#key_event = self.peek_event()
# key_event = self.peek_event()
item_key = self.compose_node(node, None)
#if item_key in node.value:
# if item_key in node.value:
# raise ComposerError("while composing a mapping", start_event.start_mark,
# "found duplicate key", key_event.start_mark)
item_value = self.compose_node(node, item_key)
#node.value[item_key] = item_value
# node.value[item_key] = item_value
node.value.append((item_key, item_value))
end_event = self.get_event()
node.end_mark = end_event.end_mark
return node
+101 -94
View File
@@ -1,6 +1,5 @@
__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
'ConstructorError']
'ConstructorError']
from error import *
from nodes import *
@@ -12,13 +11,17 @@ try:
except NameError:
from sets import Set as set
import binascii, re, sys, types
import binascii
import re
import sys
import types
class ConstructorError(MarkedYAMLError):
pass
class BaseConstructor(object):
class BaseConstructor(object):
yaml_constructors = {}
yaml_multi_constructors = {}
@@ -65,7 +68,7 @@ class BaseConstructor(object):
return self.constructed_objects[node]
if node in self.recursive_objects:
raise ConstructorError(None, None,
"found unconstructable recursive node", node.start_mark)
"found unconstructable recursive node", node.start_mark)
self.recursive_objects[node] = None
constructor = None
tag_suffix = None
@@ -110,23 +113,23 @@ class BaseConstructor(object):
def construct_scalar(self, node):
if not isinstance(node, ScalarNode):
raise ConstructorError(None, None,
"expected a scalar node, but found %s" % node.id,
node.start_mark)
"expected a scalar node, but found %s" % node.id,
node.start_mark)
return node.value
def construct_sequence(self, node, deep=False):
if not isinstance(node, SequenceNode):
raise ConstructorError(None, None,
"expected a sequence node, but found %s" % node.id,
node.start_mark)
"expected a sequence node, but found %s" % node.id,
node.start_mark)
return [self.construct_object(child, deep=deep)
for child in node.value]
def construct_mapping(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
"expected a mapping node, but found %s" % node.id,
node.start_mark)
mapping = {}
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
@@ -134,7 +137,7 @@ class BaseConstructor(object):
hash(key)
except TypeError, exc:
raise ConstructorError("while constructing a mapping", node.start_mark,
"found unacceptable key (%s)" % exc, key_node.start_mark)
"found unacceptable key (%s)" % exc, key_node.start_mark)
value = self.construct_object(value_node, deep=deep)
mapping[key] = value
return mapping
@@ -142,8 +145,8 @@ class BaseConstructor(object):
def construct_pairs(self, node, deep=False):
if not isinstance(node, MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
"expected a mapping node, but found %s" % node.id,
node.start_mark)
pairs = []
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
@@ -155,16 +158,18 @@ class BaseConstructor(object):
if not 'yaml_constructors' in cls.__dict__:
cls.yaml_constructors = cls.yaml_constructors.copy()
cls.yaml_constructors[tag] = constructor
add_constructor = classmethod(add_constructor)
def add_multi_constructor(cls, tag_prefix, multi_constructor):
if not 'yaml_multi_constructors' in cls.__dict__:
cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
cls.yaml_multi_constructors[tag_prefix] = multi_constructor
add_multi_constructor = classmethod(add_multi_constructor)
class SafeConstructor(BaseConstructor):
class SafeConstructor(BaseConstructor):
def construct_scalar(self, node):
if isinstance(node, MappingNode):
for key_node, value_node in node.value:
@@ -187,9 +192,9 @@ class SafeConstructor(BaseConstructor):
for subnode in value_node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing a mapping",
node.start_mark,
"expected a mapping for merging, but found %s"
% subnode.id, subnode.start_mark)
node.start_mark,
"expected a mapping for merging, but found %s"
% subnode.id, subnode.start_mark)
self.flatten_mapping(subnode)
submerge.append(subnode.value)
submerge.reverse()
@@ -197,8 +202,8 @@ class SafeConstructor(BaseConstructor):
merge.extend(value)
else:
raise ConstructorError("while constructing a mapping", node.start_mark,
"expected a mapping or list of mappings for merging, but found %s"
% value_node.id, value_node.start_mark)
"expected a mapping or list of mappings for merging, but found %s"
% value_node.id, value_node.start_mark)
elif key_node.tag == u'tag:yaml.org,2002:value':
key_node.tag = u'tag:yaml.org,2002:str'
index += 1
@@ -217,12 +222,12 @@ class SafeConstructor(BaseConstructor):
return None
bool_values = {
u'yes': True,
u'no': False,
u'true': True,
u'false': False,
u'on': True,
u'off': False,
u'yes': True,
u'no': False,
u'true': True,
u'false': False,
u'on': True,
u'off': False,
}
def construct_yaml_bool(self, node):
@@ -240,27 +245,27 @@ class SafeConstructor(BaseConstructor):
if value == '0':
return 0
elif value.startswith('0b'):
return sign*int(value[2:], 2)
return sign * int(value[2:], 2)
elif value.startswith('0x'):
return sign*int(value[2:], 16)
return sign * int(value[2:], 16)
elif value[0] == '0':
return sign*int(value, 8)
return sign * int(value, 8)
elif ':' in value:
digits = [int(part) for part in value.split(':')]
digits.reverse()
base = 1
value = 0
for digit in digits:
value += digit*base
value += digit * base
base *= 60
return sign*value
return sign * value
else:
return sign*int(value)
return sign * int(value)
inf_value = 1e300
while inf_value != inf_value*inf_value:
while inf_value != inf_value * inf_value:
inf_value *= inf_value
nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99).
nan_value = -inf_value / inf_value # Trying to make a quiet NaN (like C99).
def construct_yaml_float(self, node):
value = str(self.construct_scalar(node))
@@ -271,7 +276,7 @@ class SafeConstructor(BaseConstructor):
if value[0] in '+-':
value = value[1:]
if value == '.inf':
return sign*self.inf_value
return sign * self.inf_value
elif value == '.nan':
return self.nan_value
elif ':' in value:
@@ -280,11 +285,11 @@ class SafeConstructor(BaseConstructor):
base = 1
value = 0.0
for digit in digits:
value += digit*base
value += digit * base
base *= 60
return sign*value
return sign * value
else:
return sign*float(value)
return sign * float(value)
def construct_yaml_binary(self, node):
value = self.construct_scalar(node)
@@ -292,10 +297,10 @@ class SafeConstructor(BaseConstructor):
return str(value).decode('base64')
except (binascii.Error, UnicodeEncodeError), exc:
raise ConstructorError(None, None,
"failed to decode base64 data: %s" % exc, node.start_mark)
"failed to decode base64 data: %s" % exc, node.start_mark)
timestamp_regexp = re.compile(
ur'''^(?P<year>[0-9][0-9][0-9][0-9])
ur'''^(?P<year>[0-9][0-9][0-9][0-9])
-(?P<month>[0-9][0-9]?)
-(?P<day>[0-9][0-9]?)
(?:(?:[Tt]|[ \t]+)
@@ -343,16 +348,16 @@ class SafeConstructor(BaseConstructor):
yield omap
if not isinstance(node, SequenceNode):
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a sequence, but found %s" % node.id, node.start_mark)
"expected a sequence, but found %s" % node.id, node.start_mark)
for subnode in node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
if len(subnode.value) != 1:
raise ConstructorError("while constructing an ordered map", node.start_mark,
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
key_node, value_node = subnode.value[0]
key = self.construct_object(key_node)
value = self.construct_object(value_node)
@@ -364,16 +369,16 @@ class SafeConstructor(BaseConstructor):
yield pairs
if not isinstance(node, SequenceNode):
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a sequence, but found %s" % node.id, node.start_mark)
"expected a sequence, but found %s" % node.id, node.start_mark)
for subnode in node.value:
if not isinstance(subnode, MappingNode):
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
"expected a mapping of length 1, but found %s" % subnode.id,
subnode.start_mark)
if len(subnode.value) != 1:
raise ConstructorError("while constructing pairs", node.start_mark,
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
"expected a single mapping item, but found %d items" % len(subnode.value),
subnode.start_mark)
key_node, value_node = subnode.value[0]
key = self.construct_object(key_node)
value = self.construct_object(value_node)
@@ -415,62 +420,63 @@ class SafeConstructor(BaseConstructor):
def construct_undefined(self, node):
raise ConstructorError(None, None,
"could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
node.start_mark)
"could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
node.start_mark)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:null',
SafeConstructor.construct_yaml_null)
u'tag:yaml.org,2002:null',
SafeConstructor.construct_yaml_null)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:bool',
SafeConstructor.construct_yaml_bool)
u'tag:yaml.org,2002:bool',
SafeConstructor.construct_yaml_bool)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:int',
SafeConstructor.construct_yaml_int)
u'tag:yaml.org,2002:int',
SafeConstructor.construct_yaml_int)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:float',
SafeConstructor.construct_yaml_float)
u'tag:yaml.org,2002:float',
SafeConstructor.construct_yaml_float)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:binary',
SafeConstructor.construct_yaml_binary)
u'tag:yaml.org,2002:binary',
SafeConstructor.construct_yaml_binary)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:timestamp',
SafeConstructor.construct_yaml_timestamp)
u'tag:yaml.org,2002:timestamp',
SafeConstructor.construct_yaml_timestamp)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:omap',
SafeConstructor.construct_yaml_omap)
u'tag:yaml.org,2002:omap',
SafeConstructor.construct_yaml_omap)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:pairs',
SafeConstructor.construct_yaml_pairs)
u'tag:yaml.org,2002:pairs',
SafeConstructor.construct_yaml_pairs)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:set',
SafeConstructor.construct_yaml_set)
u'tag:yaml.org,2002:set',
SafeConstructor.construct_yaml_set)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:str',
SafeConstructor.construct_yaml_str)
u'tag:yaml.org,2002:str',
SafeConstructor.construct_yaml_str)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:seq',
SafeConstructor.construct_yaml_seq)
u'tag:yaml.org,2002:seq',
SafeConstructor.construct_yaml_seq)
SafeConstructor.add_constructor(
u'tag:yaml.org,2002:map',
SafeConstructor.construct_yaml_map)
u'tag:yaml.org,2002:map',
SafeConstructor.construct_yaml_map)
SafeConstructor.add_constructor(None,
SafeConstructor.construct_undefined)
SafeConstructor.construct_undefined)
class Constructor(SafeConstructor):
def construct_python_str(self, node):
return self.construct_scalar(node).encode('utf-8')
@@ -481,7 +487,7 @@ class Constructor(SafeConstructor):
return long(self.construct_yaml_int(node))
def construct_python_complex(self, node):
return complex(self.construct_scalar(node))
return complex(self.construct_scalar(node))
def construct_python_tuple(self, node):
return tuple(self.construct_sequence(node))
@@ -489,21 +495,21 @@ class Constructor(SafeConstructor):
def find_python_module(self, name, mark):
if not name:
raise ConstructorError("while constructing a Python module", mark,
"expected non-empty name appended to the tag", mark)
"expected non-empty name appended to the tag", mark)
try:
__import__(name)
except ImportError, exc:
raise ConstructorError("while constructing a Python module", mark,
"cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
"cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
return sys.modules[name]
def find_python_name(self, name, mark):
if not name:
raise ConstructorError("while constructing a Python object", mark,
"expected non-empty name appended to the tag", mark)
"expected non-empty name appended to the tag", mark)
if u'.' in name:
# Python 2.4 only
#module_name, object_name = name.rsplit('.', 1)
# module_name, object_name = name.rsplit('.', 1)
items = name.split('.')
object_name = items.pop()
module_name = '.'.join(items)
@@ -514,40 +520,41 @@ class Constructor(SafeConstructor):
__import__(module_name)
except ImportError, exc:
raise ConstructorError("while constructing a Python object", mark,
"cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
"cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
module = sys.modules[module_name]
if not hasattr(module, object_name):
raise ConstructorError("while constructing a Python object", mark,
"cannot find %r in the module %r" % (object_name.encode('utf-8'),
module.__name__), mark)
"cannot find %r in the module %r" % (object_name.encode('utf-8'),
module.__name__), mark)
return getattr(module, object_name)
def construct_python_name(self, suffix, node):
value = self.construct_scalar(node)
if value:
raise ConstructorError("while constructing a Python name", node.start_mark,
"expected the empty value, but found %r" % value.encode('utf-8'),
node.start_mark)
"expected the empty value, but found %r" % value.encode('utf-8'),
node.start_mark)
return self.find_python_name(suffix, node.start_mark)
def construct_python_module(self, suffix, node):
value = self.construct_scalar(node)
if value:
raise ConstructorError("while constructing a Python module", node.start_mark,
"expected the empty value, but found %r" % value.encode('utf-8'),
node.start_mark)
"expected the empty value, but found %r" % value.encode('utf-8'),
node.start_mark)
return self.find_python_module(suffix, node.start_mark)
class classobj: pass
class classobj:
pass
def make_python_instance(self, suffix, node,
args=None, kwds=None, newobj=False):
args=None, kwds=None, newobj=False):
if not args:
args = []
if not kwds:
kwds = {}
cls = self.find_python_name(suffix, node.start_mark)
if newobj and isinstance(cls, type(self.classobj)) \
if newobj and isinstance(cls, type(self.classobj)) \
and not args and not kwds:
instance = self.classobj()
instance.__class__ = cls
@@ -618,6 +625,7 @@ class Constructor(SafeConstructor):
def construct_python_object_new(self, suffix, node):
return self.construct_python_object_apply(suffix, node, newobj=True)
Constructor.add_constructor(
u'tag:yaml.org,2002:python/none',
Constructor.construct_yaml_null)
@@ -681,4 +689,3 @@ Constructor.add_multi_constructor(
Constructor.add_multi_constructor(
u'tag:yaml.org,2002:python/object/new:',
Constructor.construct_python_object_new)
+15 -16
View File
@@ -1,15 +1,15 @@
__all__ = ['Composer', 'ComposerError']
from .error import MarkedYAMLError
from .events import *
from .nodes import *
class ComposerError(MarkedYAMLError):
pass
class Composer:
class Composer:
def __init__(self):
self.anchors = {}
@@ -39,8 +39,8 @@ class Composer:
if not self.check_event(StreamEndEvent):
event = self.get_event()
raise ComposerError("expected a single document in the stream",
document.start_mark, "but found another document",
event.start_mark)
document.start_mark, "but found another document",
event.start_mark)
# Drop the STREAM-END event.
self.get_event()
@@ -66,15 +66,15 @@ class Composer:
anchor = event.anchor
if anchor not in self.anchors:
raise ComposerError(None, None, "found undefined alias %r"
% anchor, event.start_mark)
% anchor, event.start_mark)
return self.anchors[anchor]
event = self.peek_event()
anchor = event.anchor
if anchor is not None:
if anchor in self.anchors:
raise ComposerError("found duplicate anchor %r; first occurence"
% anchor, self.anchors[anchor].start_mark,
"second occurence", event.start_mark)
% anchor, self.anchors[anchor].start_mark,
"second occurence", event.start_mark)
self.descend_resolver(parent, index)
if self.check_event(ScalarEvent):
node = self.compose_scalar_node(anchor)
@@ -91,7 +91,7 @@ class Composer:
if tag is None or tag == '!':
tag = self.resolve(ScalarNode, event.value, event.implicit)
node = ScalarNode(tag, event.value,
event.start_mark, event.end_mark, style=event.style)
event.start_mark, event.end_mark, style=event.style)
if anchor is not None:
self.anchors[anchor] = node
return node
@@ -102,8 +102,8 @@ class Composer:
if tag is None or tag == '!':
tag = self.resolve(SequenceNode, None, start_event.implicit)
node = SequenceNode(tag, [],
start_event.start_mark, None,
flow_style=start_event.flow_style)
start_event.start_mark, None,
flow_style=start_event.flow_style)
if anchor is not None:
self.anchors[anchor] = node
index = 0
@@ -120,20 +120,19 @@ class Composer:
if tag is None or tag == '!':
tag = self.resolve(MappingNode, None, start_event.implicit)
node = MappingNode(tag, [],
start_event.start_mark, None,
flow_style=start_event.flow_style)
start_event.start_mark, None,
flow_style=start_event.flow_style)
if anchor is not None:
self.anchors[anchor] = node
while not self.check_event(MappingEndEvent):
#key_event = self.peek_event()
# key_event = self.peek_event()
item_key = self.compose_node(node, None)
#if item_key in node.value:
# if item_key in node.value:
# raise ComposerError("while composing a mapping", start_event.start_mark,
# "found duplicate key", key_event.start_mark)
item_value = self.compose_node(node, item_key)
#node.value[item_key] = item_value
# node.value[item_key] = item_value
node.value.append((item_key, item_value))
end_event = self.get_event()
node.end_mark = end_event.end_mark
return node
-1
View File
@@ -683,4 +683,3 @@ Constructor.add_multi_constructor(
Constructor.add_multi_constructor(
'tag:yaml.org,2002:python/object/new:',
Constructor.construct_python_object_new)
+65 -88
View File
@@ -6,14 +6,14 @@ import json
import unittest
import sys
import os
import datetime
import tablib
from tablib.compat import markup, unicode, is_py3
from tablib.core import Row
class TablibTestCase(unittest.TestCase):
"""Tablib test cases."""
@@ -35,12 +35,10 @@ class TablibTestCase(unittest.TestCase):
self.founders.append(self.george)
self.founders.append(self.tom)
def tearDown(self):
"""Teardown."""
pass
def test_empty_append(self):
"""Verify append() correctly adds tuple with no headers."""
new_row = (1, 2, 3)
@@ -50,7 +48,6 @@ class TablibTestCase(unittest.TestCase):
self.assertTrue(data.width == len(new_row))
self.assertTrue(data[0] == new_row)
def test_empty_append_with_headers(self):
"""Verify append() correctly detects mismatch of number of
headers and data.
@@ -72,7 +69,6 @@ class TablibTestCase(unittest.TestCase):
self.assertRaises(tablib.InvalidDimensions, set_header_callable)
def test_add_column(self):
"""Verify adding column works with/without headers."""
@@ -93,7 +89,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(data['age'], new_col)
def test_add_column_no_data_no_headers(self):
"""Verify adding new column with no headers."""
@@ -105,7 +100,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(data.width, 1)
self.assertEqual(data.height, len(new_col))
def test_add_column_with_header_ignored(self):
"""Verify append_col() ignores the header if data.headers has
not previously been set
@@ -120,14 +114,13 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(data.height, len(new_col))
self.assertEqual(data.headers, None)
def test_add_column_with_header_and_headers_only_exist(self):
"""Verify append_col() with header correctly detects mismatch when
headers exist but there is no existing row data
"""
data.headers = ['first_name']
#no data
# no data
new_col = ('allen')
@@ -136,7 +129,6 @@ class TablibTestCase(unittest.TestCase):
self.assertRaises(tablib.InvalidDimensions, append_col_callable)
def test_add_column_with_header_and_data_exists(self):
"""Verify append_col() works when headers and rows exists"""
@@ -152,7 +144,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(data['age'], new_col)
self.assertEqual(len(data.headers), len(self.headers) + 1)
def test_add_callable_column(self):
"""Verify adding column with values specified as callable."""
@@ -160,19 +151,17 @@ class TablibTestCase(unittest.TestCase):
self.founders.append_col(new_col, header='first_again')
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.john[0], self.george[0], self.tom[0]])
self.assertEqual(self.founders['last_name'],
[self.john[1], self.george[1], self.tom[1]])
[self.john[1], self.george[1], self.tom[1]])
self.assertEqual(self.founders['gpa'],
[self.john[2], self.george[2], self.tom[2]])
[self.john[2], self.george[2], self.tom[2]])
def test_get_col(self):
"""Verify getting columns by index"""
@@ -189,7 +178,6 @@ class TablibTestCase(unittest.TestCase):
self.founders.get_col(list(self.headers).index('gpa')),
[self.john[2], self.george[2], self.tom[2]])
def test_data_slicing(self):
"""Verify slicing by data."""
@@ -206,7 +194,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(self.founders[1:3], [self.george, self.tom])
self.assertEqual(self.founders[2:], [self.tom])
def test_row_slicing(self):
"""Verify Row's __getslice__ method. Issue #184."""
@@ -218,7 +205,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(john[0:2], list(self.john[0:2]))
self.assertEqual(john[0:-1], list(self.john[0:-1]))
def test_delete(self):
"""Verify deleting from dataset works."""
@@ -258,7 +244,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(csv, self.founders.csv)
def test_tsv_export(self):
"""Verify exporting dataset object as TSV."""
@@ -276,7 +261,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(tsv, self.founders.tsv)
def test_html_export(self):
"""HTML export"""
@@ -288,7 +272,6 @@ class TablibTestCase(unittest.TestCase):
html.thead.close()
for founder in self.founders:
html.tr(markup.oneliner.td(founder))
html.table.close()
@@ -296,7 +279,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(html, self.founders.html)
def test_html_export_none_value(self):
"""HTML export"""
@@ -304,10 +286,10 @@ class TablibTestCase(unittest.TestCase):
html.table.open()
html.thead.open()
html.tr(markup.oneliner.th(['foo','', 'bar']))
html.tr(markup.oneliner.th(['foo', '', 'bar']))
html.thead.close()
html.tr(markup.oneliner.td(['foo','', 'bar']))
html.tr(markup.oneliner.td(['foo', '', 'bar']))
html.table.close()
html = str(html)
@@ -317,7 +299,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(html, d.html)
def test_latex_export(self):
"""LaTeX export"""
@@ -341,17 +322,14 @@ class TablibTestCase(unittest.TestCase):
output = self.founders.latex
self.assertEqual(output, expected)
def test_latex_export_empty_dataset(self):
self.assertTrue(tablib.Dataset().latex is not None)
def test_latex_export_no_headers(self):
d = tablib.Dataset()
d.append(('one', 'two', 'three'))
self.assertTrue('one' in d.latex)
def test_latex_export_caption(self):
d = tablib.Dataset()
d.append(('one', 'two', 'three'))
@@ -360,7 +338,6 @@ class TablibTestCase(unittest.TestCase):
d.title = 'Title'
self.assertTrue('\\caption{Title}' in d.latex)
def test_latex_export_none_values(self):
headers = ['foo', None, 'bar']
d = tablib.Dataset(['foo', None, 'bar'], headers=headers)
@@ -368,7 +345,6 @@ class TablibTestCase(unittest.TestCase):
self.assertTrue('foo' in output)
self.assertFalse('None' in output)
def test_latex_escaping(self):
d = tablib.Dataset(['~', '^'])
output = d.latex
@@ -378,6 +354,15 @@ class TablibTestCase(unittest.TestCase):
self.assertFalse('^' in output)
self.assertTrue('textasciicircum' in output)
def test_str_no_columns(self):
d = tablib.Dataset(['a', 1], ['b', 2], ['c', 3])
output = '%s' % d
self.assertEqual(output.splitlines(), [
'a|1',
'b|2',
'c|3'
])
def test_unicode_append(self):
"""Passes in a single unicode character and exports."""
@@ -385,8 +370,7 @@ class TablibTestCase(unittest.TestCase):
if is_py3:
new_row = ('å', 'é')
else:
exec("new_row = (u'å', u'é')")
exec ("new_row = (u'å', u'é')")
data.append(new_row)
@@ -400,6 +384,25 @@ class TablibTestCase(unittest.TestCase):
data.html
data.latex
def test_datetime_append(self):
"""Passes in a single datetime and a single date and exports."""
new_row = (
datetime.datetime.now(),
datetime.datetime.today(),
)
data.append(new_row)
data.json
data.yaml
data.csv
data.tsv
data.xls
data.xlsx
data.ods
data.html
data.latex
def test_book_export_no_exceptions(self):
"""Test that various exports don't error out."""
@@ -412,7 +415,7 @@ class TablibTestCase(unittest.TestCase):
book.xls
book.xlsx
book.ods
book.html
def test_json_import_set(self):
"""Generate and import JSON set serialization."""
@@ -426,7 +429,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(json.loads(_json), json.loads(data.json))
def test_json_import_book(self):
"""Generate and import JSON book serialization."""
data.append(self.john)
@@ -440,7 +442,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(json.loads(_json), json.loads(book.json))
def test_yaml_import_set(self):
"""Generate and import YAML set serialization."""
data.append(self.john)
@@ -453,7 +454,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(_yaml, data.yaml)
def test_yaml_import_book(self):
"""Generate and import YAML book serialization."""
data.append(self.john)
@@ -467,7 +467,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(_yaml, book.yaml)
def test_csv_import_set(self):
"""Generate and import CSV set serialization."""
data.append(self.john)
@@ -518,7 +517,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(_csv, data.get_csv(delimiter=';'))
def test_csv_import_set_with_newlines(self):
"""Generate and import CSV set serialization when row values have
newlines."""
@@ -533,7 +531,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(_csv, data.csv)
def test_tsv_import_set(self):
"""Generate and import TSV set serialization."""
data.append(self.john)
@@ -546,7 +543,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(_tsv, data.tsv)
def test_dbf_import_set(self):
data.append(self.john)
data.append(self.george)
@@ -555,7 +551,7 @@ class TablibTestCase(unittest.TestCase):
_dbf = data.dbf
data.dbf = _dbf
#self.assertEqual(_dbf, data.dbf)
# self.assertEqual(_dbf, data.dbf)
try:
self.assertEqual(_dbf, data.dbf)
except AssertionError:
@@ -576,13 +572,13 @@ class TablibTestCase(unittest.TestCase):
data.headers = self.headers
_regression_dbf = (b'\x03r\x06\x06\x03\x00\x00\x00\x81\x00\xab\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00FIRST_NAME\x00C\x00\x00\x00\x00P\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LAST_NAME\x00\x00C\x00'
b'\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00GPA\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\x00\n'
b'\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r'
)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00FIRST_NAME\x00C\x00\x00\x00\x00P\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LAST_NAME\x00\x00C\x00'
b'\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00GPA\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\x00\n'
b'\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r'
)
_regression_dbf += b' John' + (b' ' * 75)
_regression_dbf += b' Adams' + (b' ' * 74)
_regression_dbf += b' 90.0000000'
@@ -596,8 +592,8 @@ class TablibTestCase(unittest.TestCase):
if is_py3:
# If in python3, decode regression string to binary.
#_regression_dbf = bytes(_regression_dbf, 'utf-8')
#_regression_dbf = _regression_dbf.replace(b'\n', b'\r')
# _regression_dbf = bytes(_regression_dbf, 'utf-8')
# _regression_dbf = _regression_dbf.replace(b'\n', b'\r')
pass
try:
@@ -606,23 +602,23 @@ class TablibTestCase(unittest.TestCase):
index = 0
found_so_far = ''
for reg_char, data_char in zip(_regression_dbf, data.dbf):
#found_so_far += chr(data_char)
# found_so_far += chr(data_char)
if reg_char != data_char and index not in [1, 2, 3]:
raise AssertionError(
'Failing at char %s: %s vs %s (found %s)' % (
index, reg_char, data_char, found_so_far))
index, reg_char, data_char, found_so_far))
index += 1
def test_dbf_format_detect(self):
"""Test the DBF format detection."""
_dbf = (b'\x03r\x06\x03\x03\x00\x00\x00\x81\x00\xab\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00FIRST_NAME\x00C\x00\x00\x00\x00P\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LAST_NAME\x00\x00C\x00'
b'\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00GPA\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\x00\n'
b'\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r'
)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00FIRST_NAME\x00C\x00\x00\x00\x00P\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LAST_NAME\x00\x00C\x00'
b'\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00GPA\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\x00\n'
b'\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r'
)
_dbf += b' John' + (b' ' * 75)
_dbf += b' Adams' + (b' ' * 74)
_dbf += b' 90.0000000'
@@ -664,7 +660,6 @@ class TablibTestCase(unittest.TestCase):
self.assertTrue(tablib.formats.csv.detect(_csv))
self.assertFalse(tablib.formats.csv.detect(_bunk))
def test_tsv_format_detect(self):
"""Test TSV format detection."""
@@ -680,7 +675,6 @@ class TablibTestCase(unittest.TestCase):
self.assertTrue(tablib.formats.tsv.detect(_tsv))
self.assertFalse(tablib.formats.tsv.detect(_bunk))
def test_json_format_detect(self):
"""Test JSON format detection."""
@@ -692,7 +686,6 @@ class TablibTestCase(unittest.TestCase):
self.assertTrue(tablib.formats.json.detect(_json))
self.assertFalse(tablib.formats.json.detect(_bunk))
def test_yaml_format_detect(self):
"""Test YAML format detection."""
@@ -706,7 +699,6 @@ class TablibTestCase(unittest.TestCase):
self.assertFalse(tablib.formats.yaml.detect(_bunk))
self.assertFalse(tablib.formats.yaml.detect(_tsv))
def test_auto_format_detect(self):
"""Test auto format detection."""
@@ -722,7 +714,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(tablib.detect_format(_json), 'json')
self.assertEqual(tablib.detect_format(_bunk), None)
def test_transpose(self):
"""Transpose a dataset."""
@@ -731,11 +722,11 @@ class TablibTestCase(unittest.TestCase):
second_row = transposed_founders[1]
self.assertEqual(transposed_founders.headers,
["first_name","John", "George", "Thomas"])
["first_name", "John", "George", "Thomas"])
self.assertEqual(first_row,
("last_name","Adams", "Washington", "Jefferson"))
("last_name", "Adams", "Washington", "Jefferson"))
self.assertEqual(second_row,
("gpa",90, 67, 50))
("gpa", 90, 67, 50))
def test_transpose_multiple_headers(self):
@@ -746,7 +737,6 @@ class TablibTestCase(unittest.TestCase):
data.append(('John', 'Tyler', 71))
self.assertEqual(data.transpose().transpose().dict, data.dict)
def test_row_stacking(self):
"""Row stacking."""
@@ -758,12 +748,10 @@ class TablibTestCase(unittest.TestCase):
row_stacked = self.founders.stack(to_join)
for column in row_stacked.headers:
original_data = self.founders[column]
expected_data = original_data + original_data
self.assertEqual(row_stacked[column], expected_data)
def test_column_stacking(self):
"""Column stacking"""
@@ -775,14 +763,12 @@ class TablibTestCase(unittest.TestCase):
column_stacked = self.founders.stack_cols(to_join)
for index, row in enumerate(column_stacked):
original_data = self.founders[index]
expected_data = original_data + original_data
self.assertEqual(row, expected_data)
self.assertEqual(column_stacked[0],
("John", "Adams", 90, "John", "Adams", 90))
("John", "Adams", 90, "John", "Adams", 90))
def test_sorting(self):
"""Sort columns."""
@@ -801,7 +787,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(second_row, expected_second)
self.assertEqual(third_row, expected_third)
def test_remove_duplicates(self):
"""Unique Rows."""
@@ -820,7 +805,6 @@ class TablibTestCase(unittest.TestCase):
self.assertEqual(self.founders[2], self.tom)
self.assertEqual(self.founders.height, 3)
def test_wipe(self):
"""Purge a dataset."""
@@ -837,12 +821,11 @@ class TablibTestCase(unittest.TestCase):
self.assertTrue(data.width == len(new_row))
self.assertTrue(data[0] == new_row)
def test_subset(self):
"""Create a subset of a dataset"""
rows = (0, 2)
columns = ('first_name','gpa')
columns = ('first_name', 'gpa')
data.headers = self.headers
@@ -850,14 +833,13 @@ class TablibTestCase(unittest.TestCase):
data.append(self.george)
data.append(self.tom)
#Verify data is truncated
# Verify data is truncated
subset = data.subset(rows=rows, cols=columns)
self.assertEqual(type(subset), tablib.Dataset)
self.assertEqual(subset.headers, list(columns))
self.assertEqual(subset._data[0].list, ['John', 90])
self.assertEqual(subset._data[1].list, ['Thomas', 50])
def test_formatters(self):
"""Confirm formatters are being triggered."""
@@ -877,8 +859,7 @@ class TablibTestCase(unittest.TestCase):
if sys.version_info[0] > 2:
data.append(['\xfc', '\xfd'])
else:
exec("data.append([u'\xfc', u'\xfd'])")
exec ("data.append([u'\xfc', u'\xfd'])")
data.csv
@@ -895,7 +876,6 @@ class TablibTestCase(unittest.TestCase):
csv_first_name = data[headers[0]]
self.assertEqual(orig_first_name, csv_first_name)
def test_csv_column_delete(self):
"""Build up a CSV and test deleting a column"""
@@ -929,7 +909,6 @@ class TablibTestCase(unittest.TestCase):
self.founders.append(('Old', 'Man', 100500))
self.assertEqual('first_name|last_name |gpa ', unicode(self.founders).split('\n')[0])
def test_databook_add_sheet_accepts_only_dataset_instances(self):
class NotDataset(object):
def append(self, item):
@@ -940,7 +919,6 @@ class TablibTestCase(unittest.TestCase):
self.assertRaises(tablib.InvalidDatasetType, book.add_sheet, dataset)
def test_databook_add_sheet_accepts_dataset_subclasses(self):
class DatasetSubclass(tablib.Dataset):
pass
@@ -955,7 +933,6 @@ class TablibTestCase(unittest.TestCase):
except tablib.InvalidDatasetType:
self.fail("Subclass of tablib.Dataset should be accepted by Databook.add_sheet")
def test_csv_formatter_support_kwargs(self):
"""Test CSV import and export with formatter configuration."""
data.append(self.john)
+1 -1
View File
@@ -4,7 +4,7 @@
# and then run "tox" from this directory.
[tox]
envlist = py26, py27, py32, py33, py34, pypy
envlist = py26, py27, py32, py33, py34, py35, py36, pypy
[testenv]
commands = python setup.py test