test_tablib.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """Tests for Tablib."""
  4. import json
  5. import unittest
  6. import sys
  7. import datetime
  8. import tablib
  9. from tablib.compat import markup, unicode, is_py3
  10. from tablib.core import Row
  11. class TablibTestCase(unittest.TestCase):
  12. """Tablib test cases."""
  13. def setUp(self):
  14. """Create simple data set with headers."""
  15. global data, book
  16. data = tablib.Dataset()
  17. book = tablib.Databook()
  18. self.headers = ('first_name', 'last_name', 'gpa')
  19. self.john = ('John', 'Adams', 90)
  20. self.george = ('George', 'Washington', 67)
  21. self.tom = ('Thomas', 'Jefferson', 50)
  22. self.founders = tablib.Dataset(headers=self.headers, title='Founders')
  23. self.founders.append(self.john)
  24. self.founders.append(self.george)
  25. self.founders.append(self.tom)
  26. def tearDown(self):
  27. """Teardown."""
  28. pass
  29. def test_empty_append(self):
  30. """Verify append() correctly adds tuple with no headers."""
  31. new_row = (1, 2, 3)
  32. data.append(new_row)
  33. # Verify width/data
  34. self.assertTrue(data.width == len(new_row))
  35. self.assertTrue(data[0] == new_row)
  36. def test_empty_append_with_headers(self):
  37. """Verify append() correctly detects mismatch of number of
  38. headers and data.
  39. """
  40. data.headers = ['first', 'second']
  41. new_row = (1, 2, 3, 4)
  42. self.assertRaises(tablib.InvalidDimensions, data.append, new_row)
  43. def test_set_headers_with_incorrect_dimension(self):
  44. """Verify headers correctly detects mismatch of number of
  45. headers and data.
  46. """
  47. data.append(self.john)
  48. def set_header_callable():
  49. data.headers = ['first_name']
  50. self.assertRaises(tablib.InvalidDimensions, set_header_callable)
  51. def test_add_column(self):
  52. """Verify adding column works with/without headers."""
  53. data.append(['kenneth'])
  54. data.append(['bessie'])
  55. new_col = ['reitz', 'monke']
  56. data.append_col(new_col)
  57. self.assertEqual(data[0], ('kenneth', 'reitz'))
  58. self.assertEqual(data.width, 2)
  59. # With Headers
  60. data.headers = ('fname', 'lname')
  61. new_col = [21, 22]
  62. data.append_col(new_col, header='age')
  63. self.assertEqual(data['age'], new_col)
  64. def test_add_column_no_data_no_headers(self):
  65. """Verify adding new column with no headers."""
  66. new_col = ('reitz', 'monke')
  67. data.append_col(new_col)
  68. self.assertEqual(data[0], tuple([new_col[0]]))
  69. self.assertEqual(data.width, 1)
  70. self.assertEqual(data.height, len(new_col))
  71. def test_add_column_with_header_ignored(self):
  72. """Verify append_col() ignores the header if data.headers has
  73. not previously been set
  74. """
  75. new_col = ('reitz', 'monke')
  76. data.append_col(new_col, header='first_name')
  77. self.assertEqual(data[0], tuple([new_col[0]]))
  78. self.assertEqual(data.width, 1)
  79. self.assertEqual(data.height, len(new_col))
  80. self.assertEqual(data.headers, None)
  81. def test_add_column_with_header_and_headers_only_exist(self):
  82. """Verify append_col() with header correctly detects mismatch when
  83. headers exist but there is no existing row data
  84. """
  85. data.headers = ['first_name']
  86. # no data
  87. new_col = ('allen')
  88. def append_col_callable():
  89. data.append_col(new_col, header='middle_name')
  90. self.assertRaises(tablib.InvalidDimensions, append_col_callable)
  91. def test_add_column_with_header_and_data_exists(self):
  92. """Verify append_col() works when headers and rows exists"""
  93. data.headers = self.headers
  94. data.append(self.john)
  95. new_col = [10];
  96. data.append_col(new_col, header='age')
  97. self.assertEqual(data.height, 1)
  98. self.assertEqual(data.width, len(self.john) + 1)
  99. self.assertEqual(data['age'], new_col)
  100. self.assertEqual(len(data.headers), len(self.headers) + 1)
  101. def test_add_callable_column(self):
  102. """Verify adding column with values specified as callable."""
  103. new_col = lambda x: x[0]
  104. self.founders.append_col(new_col, header='first_again')
  105. def test_header_slicing(self):
  106. """Verify slicing by headers."""
  107. self.assertEqual(self.founders['first_name'],
  108. [self.john[0], self.george[0], self.tom[0]])
  109. self.assertEqual(self.founders['last_name'],
  110. [self.john[1], self.george[1], self.tom[1]])
  111. self.assertEqual(self.founders['gpa'],
  112. [self.john[2], self.george[2], self.tom[2]])
  113. def test_get_col(self):
  114. """Verify getting columns by index"""
  115. self.assertEqual(
  116. self.founders.get_col(list(self.headers).index('first_name')),
  117. [self.john[0], self.george[0], self.tom[0]])
  118. self.assertEqual(
  119. self.founders.get_col(list(self.headers).index('last_name')),
  120. [self.john[1], self.george[1], self.tom[1]])
  121. self.assertEqual(
  122. self.founders.get_col(list(self.headers).index('gpa')),
  123. [self.john[2], self.george[2], self.tom[2]])
  124. def test_data_slicing(self):
  125. """Verify slicing by data."""
  126. # Slice individual rows
  127. self.assertEqual(self.founders[0], self.john)
  128. self.assertEqual(self.founders[:1], [self.john])
  129. self.assertEqual(self.founders[1:2], [self.george])
  130. self.assertEqual(self.founders[-1], self.tom)
  131. self.assertEqual(self.founders[3:], [])
  132. # Slice multiple rows
  133. self.assertEqual(self.founders[:], [self.john, self.george, self.tom])
  134. self.assertEqual(self.founders[0:2], [self.john, self.george])
  135. self.assertEqual(self.founders[1:3], [self.george, self.tom])
  136. self.assertEqual(self.founders[2:], [self.tom])
  137. def test_row_slicing(self):
  138. """Verify Row's __getslice__ method. Issue #184."""
  139. john = Row(self.john)
  140. self.assertEqual(john[:], list(self.john[:]))
  141. self.assertEqual(john[0:], list(self.john[0:]))
  142. self.assertEqual(john[:2], list(self.john[:2]))
  143. self.assertEqual(john[0:2], list(self.john[0:2]))
  144. self.assertEqual(john[0:-1], list(self.john[0:-1]))
  145. def test_delete(self):
  146. """Verify deleting from dataset works."""
  147. # Delete from front of object
  148. del self.founders[0]
  149. self.assertEqual(self.founders[:], [self.george, self.tom])
  150. # Verify dimensions, width should NOT change
  151. self.assertEqual(self.founders.height, 2)
  152. self.assertEqual(self.founders.width, 3)
  153. # Delete from back of object
  154. del self.founders[1]
  155. self.assertEqual(self.founders[:], [self.george])
  156. # Verify dimensions, width should NOT change
  157. self.assertEqual(self.founders.height, 1)
  158. self.assertEqual(self.founders.width, 3)
  159. # Delete from invalid index
  160. self.assertRaises(IndexError, self.founders.__delitem__, 3)
  161. def test_csv_export(self):
  162. """Verify exporting dataset object as CSV."""
  163. # Build up the csv string with headers first, followed by each row
  164. csv = ''
  165. for col in self.headers:
  166. csv += col + ','
  167. csv = csv.strip(',') + '\r\n'
  168. for founder in self.founders:
  169. for col in founder:
  170. csv += str(col) + ','
  171. csv = csv.strip(',') + '\r\n'
  172. self.assertEqual(csv, self.founders.csv)
  173. def test_tsv_export(self):
  174. """Verify exporting dataset object as TSV."""
  175. # Build up the tsv string with headers first, followed by each row
  176. tsv = ''
  177. for col in self.headers:
  178. tsv += col + '\t'
  179. tsv = tsv.strip('\t') + '\r\n'
  180. for founder in self.founders:
  181. for col in founder:
  182. tsv += str(col) + '\t'
  183. tsv = tsv.strip('\t') + '\r\n'
  184. self.assertEqual(tsv, self.founders.tsv)
  185. def test_html_export(self):
  186. """HTML export"""
  187. html = markup.page()
  188. html.table.open()
  189. html.thead.open()
  190. html.tr(markup.oneliner.th(self.founders.headers))
  191. html.thead.close()
  192. for founder in self.founders:
  193. html.tr(markup.oneliner.td(founder))
  194. html.table.close()
  195. html = str(html)
  196. self.assertEqual(html, self.founders.html)
  197. def test_html_export_none_value(self):
  198. """HTML export"""
  199. html = markup.page()
  200. html.table.open()
  201. html.thead.open()
  202. html.tr(markup.oneliner.th(['foo', '', 'bar']))
  203. html.thead.close()
  204. html.tr(markup.oneliner.td(['foo', '', 'bar']))
  205. html.table.close()
  206. html = str(html)
  207. headers = ['foo', None, 'bar'];
  208. d = tablib.Dataset(['foo', None, 'bar'], headers=headers)
  209. self.assertEqual(html, d.html)
  210. def test_latex_export(self):
  211. """LaTeX export"""
  212. expected = """\
  213. % Note: add \\usepackage{booktabs} to your preamble
  214. %
  215. \\begin{table}[!htbp]
  216. \\centering
  217. \\caption{Founders}
  218. \\begin{tabular}{lrr}
  219. \\toprule
  220. first\\_name & last\\_name & gpa \\\\
  221. \\cmidrule(r){1-1} \\cmidrule(lr){2-2} \\cmidrule(l){3-3}
  222. John & Adams & 90 \\\\
  223. George & Washington & 67 \\\\
  224. Thomas & Jefferson & 50 \\\\
  225. \\bottomrule
  226. \\end{tabular}
  227. \\end{table}
  228. """
  229. output = self.founders.latex
  230. self.assertEqual(output, expected)
  231. def test_latex_export_empty_dataset(self):
  232. self.assertTrue(tablib.Dataset().latex is not None)
  233. def test_latex_export_no_headers(self):
  234. d = tablib.Dataset()
  235. d.append(('one', 'two', 'three'))
  236. self.assertTrue('one' in d.latex)
  237. def test_latex_export_caption(self):
  238. d = tablib.Dataset()
  239. d.append(('one', 'two', 'three'))
  240. self.assertFalse('caption' in d.latex)
  241. d.title = 'Title'
  242. self.assertTrue('\\caption{Title}' in d.latex)
  243. def test_latex_export_none_values(self):
  244. headers = ['foo', None, 'bar']
  245. d = tablib.Dataset(['foo', None, 'bar'], headers=headers)
  246. output = d.latex
  247. self.assertTrue('foo' in output)
  248. self.assertFalse('None' in output)
  249. def test_latex_escaping(self):
  250. d = tablib.Dataset(['~', '^'])
  251. output = d.latex
  252. self.assertFalse('~' in output)
  253. self.assertTrue('textasciitilde' in output)
  254. self.assertFalse('^' in output)
  255. self.assertTrue('textasciicircum' in output)
  256. def test_str_no_columns(self):
  257. d = tablib.Dataset(['a', 1], ['b', 2], ['c', 3])
  258. output = '%s' % d
  259. self.assertEqual(output.splitlines(), [
  260. 'a|1',
  261. 'b|2',
  262. 'c|3'
  263. ])
  264. def test_unicode_append(self):
  265. """Passes in a single unicode character and exports."""
  266. if is_py3:
  267. new_row = ('å', 'é')
  268. else:
  269. exec ("new_row = (u'å', u'é')")
  270. data.append(new_row)
  271. data.json
  272. data.yaml
  273. data.csv
  274. data.tsv
  275. data.xls
  276. data.xlsx
  277. data.ods
  278. data.html
  279. data.latex
  280. data.df
  281. def test_datetime_append(self):
  282. """Passes in a single datetime and a single date and exports."""
  283. new_row = (
  284. datetime.datetime.now(),
  285. datetime.datetime.today(),
  286. )
  287. data.append(new_row)
  288. data.json
  289. data.yaml
  290. data.csv
  291. data.tsv
  292. data.xls
  293. data.xlsx
  294. data.ods
  295. data.html
  296. data.latex
  297. def test_book_export_no_exceptions(self):
  298. """Test that various exports don't error out."""
  299. book = tablib.Databook()
  300. book.add_sheet(data)
  301. book.json
  302. book.yaml
  303. book.xls
  304. book.xlsx
  305. book.ods
  306. book.html
  307. def test_json_import_set(self):
  308. """Generate and import JSON set serialization."""
  309. data.append(self.john)
  310. data.append(self.george)
  311. data.headers = self.headers
  312. _json = data.json
  313. data.json = _json
  314. self.assertEqual(json.loads(_json), json.loads(data.json))
  315. def test_json_import_book(self):
  316. """Generate and import JSON book serialization."""
  317. data.append(self.john)
  318. data.append(self.george)
  319. data.headers = self.headers
  320. book.add_sheet(data)
  321. _json = book.json
  322. book.json = _json
  323. self.assertEqual(json.loads(_json), json.loads(book.json))
  324. def test_yaml_import_set(self):
  325. """Generate and import YAML set serialization."""
  326. data.append(self.john)
  327. data.append(self.george)
  328. data.headers = self.headers
  329. _yaml = data.yaml
  330. data.yaml = _yaml
  331. self.assertEqual(_yaml, data.yaml)
  332. def test_yaml_import_book(self):
  333. """Generate and import YAML book serialization."""
  334. data.append(self.john)
  335. data.append(self.george)
  336. data.headers = self.headers
  337. book.add_sheet(data)
  338. _yaml = book.yaml
  339. book.yaml = _yaml
  340. self.assertEqual(_yaml, book.yaml)
  341. def test_csv_import_set(self):
  342. """Generate and import CSV set serialization."""
  343. data.append(self.john)
  344. data.append(self.george)
  345. data.headers = self.headers
  346. _csv = data.csv
  347. data.csv = _csv
  348. self.assertEqual(_csv, data.csv)
  349. def test_csv_import_set_semicolons(self):
  350. """Test for proper output with semicolon separated CSV."""
  351. data.append(self.john)
  352. data.append(self.george)
  353. data.headers = self.headers
  354. _csv = data.get_csv(delimiter=';')
  355. data.set_csv(_csv, delimiter=';')
  356. self.assertEqual(_csv, data.get_csv(delimiter=';'))
  357. def test_csv_import_set_with_spaces(self):
  358. """Generate and import CSV set serialization when row values have
  359. spaces."""
  360. data.append(('Bill Gates', 'Microsoft'))
  361. data.append(('Steve Jobs', 'Apple'))
  362. data.headers = ('Name', 'Company')
  363. _csv = data.csv
  364. data.csv = _csv
  365. self.assertEqual(_csv, data.csv)
  366. def test_csv_import_set_semicolon_with_spaces(self):
  367. """Generate and import semicolon separated CSV set serialization when row values have
  368. spaces."""
  369. data.append(('Bill Gates', 'Microsoft'))
  370. data.append(('Steve Jobs', 'Apple'))
  371. data.headers = ('Name', 'Company')
  372. _csv = data.get_csv(delimiter=';')
  373. data.set_csv(_csv, delimiter=';')
  374. self.assertEqual(_csv, data.get_csv(delimiter=';'))
  375. def test_csv_import_set_with_newlines(self):
  376. """Generate and import CSV set serialization when row values have
  377. newlines."""
  378. data.append(('Markdown\n=======',
  379. 'A cool language\n\nwith paragraphs'))
  380. data.append(('reStructedText\n==============',
  381. 'Another cool language\n\nwith paragraphs'))
  382. data.headers = ('title', 'body')
  383. _csv = data.csv
  384. data.csv = _csv
  385. self.assertEqual(_csv, data.csv)
  386. def test_tsv_import_set(self):
  387. """Generate and import TSV set serialization."""
  388. data.append(self.john)
  389. data.append(self.george)
  390. data.headers = self.headers
  391. _tsv = data.tsv
  392. data.tsv = _tsv
  393. self.assertEqual(_tsv, data.tsv)
  394. def test_dbf_import_set(self):
  395. data.append(self.john)
  396. data.append(self.george)
  397. data.headers = self.headers
  398. _dbf = data.dbf
  399. data.dbf = _dbf
  400. # self.assertEqual(_dbf, data.dbf)
  401. try:
  402. self.assertEqual(_dbf, data.dbf)
  403. except AssertionError:
  404. index = 0
  405. so_far = ''
  406. for reg_char, data_char in zip(_dbf, data.dbf):
  407. so_far += chr(data_char)
  408. if reg_char != data_char and index not in [1, 2, 3]:
  409. raise AssertionError('Failing at char %s: %s vs %s %s' % (
  410. index, reg_char, data_char, so_far))
  411. index += 1
  412. def test_dbf_export_set(self):
  413. """Test DBF import."""
  414. data.append(self.john)
  415. data.append(self.george)
  416. data.append(self.tom)
  417. data.headers = self.headers
  418. _regression_dbf = (b'\x03r\x06\x06\x03\x00\x00\x00\x81\x00\xab\x00\x00'
  419. b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  420. b'\x00\x00\x00FIRST_NAME\x00C\x00\x00\x00\x00P\x00\x00\x00\x00\x00'
  421. b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LAST_NAME\x00\x00C\x00'
  422. b'\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  423. b'\x00\x00GPA\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\x00\n'
  424. b'\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r'
  425. )
  426. _regression_dbf += b' John' + (b' ' * 75)
  427. _regression_dbf += b' Adams' + (b' ' * 74)
  428. _regression_dbf += b' 90.0000000'
  429. _regression_dbf += b' George' + (b' ' * 73)
  430. _regression_dbf += b' Washington' + (b' ' * 69)
  431. _regression_dbf += b' 67.0000000'
  432. _regression_dbf += b' Thomas' + (b' ' * 73)
  433. _regression_dbf += b' Jefferson' + (b' ' * 70)
  434. _regression_dbf += b' 50.0000000'
  435. _regression_dbf += b'\x1a'
  436. if is_py3:
  437. # If in python3, decode regression string to binary.
  438. # _regression_dbf = bytes(_regression_dbf, 'utf-8')
  439. # _regression_dbf = _regression_dbf.replace(b'\n', b'\r')
  440. pass
  441. try:
  442. self.assertEqual(_regression_dbf, data.dbf)
  443. except AssertionError:
  444. index = 0
  445. found_so_far = ''
  446. for reg_char, data_char in zip(_regression_dbf, data.dbf):
  447. # found_so_far += chr(data_char)
  448. if reg_char != data_char and index not in [1, 2, 3]:
  449. raise AssertionError(
  450. 'Failing at char %s: %s vs %s (found %s)' % (
  451. index, reg_char, data_char, found_so_far))
  452. index += 1
  453. def test_dbf_format_detect(self):
  454. """Test the DBF format detection."""
  455. _dbf = (b'\x03r\x06\x03\x03\x00\x00\x00\x81\x00\xab\x00\x00'
  456. b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  457. b'\x00\x00\x00FIRST_NAME\x00C\x00\x00\x00\x00P\x00\x00\x00\x00\x00'
  458. b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LAST_NAME\x00\x00C\x00'
  459. b'\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  460. b'\x00\x00GPA\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\x00\n'
  461. b'\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r'
  462. )
  463. _dbf += b' John' + (b' ' * 75)
  464. _dbf += b' Adams' + (b' ' * 74)
  465. _dbf += b' 90.0000000'
  466. _dbf += b' George' + (b' ' * 73)
  467. _dbf += b' Washington' + (b' ' * 69)
  468. _dbf += b' 67.0000000'
  469. _dbf += b' Thomas' + (b' ' * 73)
  470. _dbf += b' Jefferson' + (b' ' * 70)
  471. _dbf += b' 50.0000000'
  472. _dbf += b'\x1a'
  473. _yaml = '- {age: 90, first_name: John, last_name: Adams}'
  474. _tsv = 'foo\tbar'
  475. _csv = '1,2,3\n4,5,6\n7,8,9\n'
  476. _json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]'
  477. _bunk = (
  478. '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
  479. )
  480. self.assertTrue(tablib.formats.dbf.detect(_dbf))
  481. self.assertFalse(tablib.formats.dbf.detect(_yaml))
  482. self.assertFalse(tablib.formats.dbf.detect(_tsv))
  483. self.assertFalse(tablib.formats.dbf.detect(_csv))
  484. self.assertFalse(tablib.formats.dbf.detect(_json))
  485. self.assertFalse(tablib.formats.dbf.detect(_bunk))
  486. def test_csv_format_detect(self):
  487. """Test CSV format detection."""
  488. _csv = (
  489. '1,2,3\n'
  490. '4,5,6\n'
  491. '7,8,9\n'
  492. )
  493. _bunk = (
  494. '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
  495. )
  496. self.assertTrue(tablib.formats.csv.detect(_csv))
  497. self.assertFalse(tablib.formats.csv.detect(_bunk))
  498. def test_tsv_format_detect(self):
  499. """Test TSV format detection."""
  500. _tsv = (
  501. '1\t2\t3\n'
  502. '4\t5\t6\n'
  503. '7\t8\t9\n'
  504. )
  505. _bunk = (
  506. '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
  507. )
  508. self.assertTrue(tablib.formats.tsv.detect(_tsv))
  509. self.assertFalse(tablib.formats.tsv.detect(_bunk))
  510. def test_json_format_detect(self):
  511. """Test JSON format detection."""
  512. _json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]'
  513. _bunk = (
  514. '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
  515. )
  516. self.assertTrue(tablib.formats.json.detect(_json))
  517. self.assertFalse(tablib.formats.json.detect(_bunk))
  518. def test_yaml_format_detect(self):
  519. """Test YAML format detection."""
  520. _yaml = '- {age: 90, first_name: John, last_name: Adams}'
  521. _tsv = 'foo\tbar'
  522. _bunk = (
  523. '¡¡¡¡¡¡---///\n\n\n¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
  524. )
  525. self.assertTrue(tablib.formats.yaml.detect(_yaml))
  526. self.assertFalse(tablib.formats.yaml.detect(_bunk))
  527. self.assertFalse(tablib.formats.yaml.detect(_tsv))
  528. def test_auto_format_detect(self):
  529. """Test auto format detection."""
  530. _yaml = '- {age: 90, first_name: John, last_name: Adams}'
  531. _json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]'
  532. _csv = '1,2,3\n4,5,6\n7,8,9\n'
  533. _tsv = '1\t2\t3\n4\t5\t6\n7\t8\t9\n'
  534. _bunk = '¡¡¡¡¡¡---///\n\n\n¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶'
  535. self.assertEqual(tablib.detect_format(_yaml), 'yaml')
  536. self.assertEqual(tablib.detect_format(_csv), 'csv')
  537. self.assertEqual(tablib.detect_format(_tsv), 'tsv')
  538. self.assertEqual(tablib.detect_format(_json), 'json')
  539. self.assertEqual(tablib.detect_format(_bunk), None)
  540. def test_transpose(self):
  541. """Transpose a dataset."""
  542. transposed_founders = self.founders.transpose()
  543. first_row = transposed_founders[0]
  544. second_row = transposed_founders[1]
  545. self.assertEqual(transposed_founders.headers,
  546. ["first_name", "John", "George", "Thomas"])
  547. self.assertEqual(first_row,
  548. ("last_name", "Adams", "Washington", "Jefferson"))
  549. self.assertEqual(second_row,
  550. ("gpa", 90, 67, 50))
  551. def test_transpose_multiple_headers(self):
  552. data = tablib.Dataset()
  553. data.headers = ("first_name", "last_name", "age")
  554. data.append(('John', 'Adams', 90))
  555. data.append(('George', 'Washington', 67))
  556. data.append(('John', 'Tyler', 71))
  557. self.assertEqual(data.transpose().transpose().dict, data.dict)
  558. def test_row_stacking(self):
  559. """Row stacking."""
  560. to_join = tablib.Dataset(headers=self.founders.headers)
  561. for row in self.founders:
  562. to_join.append(row=row)
  563. row_stacked = self.founders.stack(to_join)
  564. for column in row_stacked.headers:
  565. original_data = self.founders[column]
  566. expected_data = original_data + original_data
  567. self.assertEqual(row_stacked[column], expected_data)
  568. def test_column_stacking(self):
  569. """Column stacking"""
  570. to_join = tablib.Dataset(headers=self.founders.headers)
  571. for row in self.founders:
  572. to_join.append(row=row)
  573. column_stacked = self.founders.stack_cols(to_join)
  574. for index, row in enumerate(column_stacked):
  575. original_data = self.founders[index]
  576. expected_data = original_data + original_data
  577. self.assertEqual(row, expected_data)
  578. self.assertEqual(column_stacked[0],
  579. ("John", "Adams", 90, "John", "Adams", 90))
  580. def test_sorting(self):
  581. """Sort columns."""
  582. sorted_data = self.founders.sort(col="first_name")
  583. self.assertEqual(sorted_data.title, 'Founders')
  584. first_row = sorted_data[0]
  585. second_row = sorted_data[2]
  586. third_row = sorted_data[1]
  587. expected_first = self.founders[1]
  588. expected_second = self.founders[2]
  589. expected_third = self.founders[0]
  590. self.assertEqual(first_row, expected_first)
  591. self.assertEqual(second_row, expected_second)
  592. self.assertEqual(third_row, expected_third)
  593. def test_remove_duplicates(self):
  594. """Unique Rows."""
  595. self.founders.append(self.john)
  596. self.founders.append(self.george)
  597. self.founders.append(self.tom)
  598. self.assertEqual(self.founders[0], self.founders[3])
  599. self.assertEqual(self.founders[1], self.founders[4])
  600. self.assertEqual(self.founders[2], self.founders[5])
  601. self.assertEqual(self.founders.height, 6)
  602. self.founders.remove_duplicates()
  603. self.assertEqual(self.founders[0], self.john)
  604. self.assertEqual(self.founders[1], self.george)
  605. self.assertEqual(self.founders[2], self.tom)
  606. self.assertEqual(self.founders.height, 3)
  607. def test_wipe(self):
  608. """Purge a dataset."""
  609. new_row = (1, 2, 3)
  610. data.append(new_row)
  611. # Verify width/data
  612. self.assertTrue(data.width == len(new_row))
  613. self.assertTrue(data[0] == new_row)
  614. data.wipe()
  615. new_row = (1, 2, 3, 4)
  616. data.append(new_row)
  617. self.assertTrue(data.width == len(new_row))
  618. self.assertTrue(data[0] == new_row)
  619. def test_subset(self):
  620. """Create a subset of a dataset"""
  621. rows = (0, 2)
  622. columns = ('first_name', 'gpa')
  623. data.headers = self.headers
  624. data.append(self.john)
  625. data.append(self.george)
  626. data.append(self.tom)
  627. # Verify data is truncated
  628. subset = data.subset(rows=rows, cols=columns)
  629. self.assertEqual(type(subset), tablib.Dataset)
  630. self.assertEqual(subset.headers, list(columns))
  631. self.assertEqual(subset._data[0].list, ['John', 90])
  632. self.assertEqual(subset._data[1].list, ['Thomas', 50])
  633. def test_formatters(self):
  634. """Confirm formatters are being triggered."""
  635. def _formatter(cell_value):
  636. return str(cell_value).upper()
  637. self.founders.add_formatter('last_name', _formatter)
  638. for name in [r['last_name'] for r in self.founders.dict]:
  639. self.assertTrue(name.isupper())
  640. def test_unicode_csv(self):
  641. """Check if unicode in csv export doesn't raise."""
  642. data = tablib.Dataset()
  643. if sys.version_info[0] > 2:
  644. data.append(['\xfc', '\xfd'])
  645. else:
  646. exec ("data.append([u'\xfc', u'\xfd'])")
  647. data.csv
  648. def test_csv_column_select(self):
  649. """Build up a CSV and test selecting a column"""
  650. data = tablib.Dataset()
  651. data.csv = self.founders.csv
  652. headers = data.headers
  653. self.assertTrue(isinstance(headers[0], unicode))
  654. orig_first_name = self.founders[self.headers[0]]
  655. csv_first_name = data[headers[0]]
  656. self.assertEqual(orig_first_name, csv_first_name)
  657. def test_csv_column_delete(self):
  658. """Build up a CSV and test deleting a column"""
  659. data = tablib.Dataset()
  660. data.csv = self.founders.csv
  661. target_header = data.headers[0]
  662. self.assertTrue(isinstance(target_header, unicode))
  663. del data[target_header]
  664. self.assertTrue(target_header not in data.headers)
  665. def test_csv_column_sort(self):
  666. """Build up a CSV and test sorting a column by name"""
  667. data = tablib.Dataset()
  668. data.csv = self.founders.csv
  669. orig_target_header = self.founders.headers[1]
  670. target_header = data.headers[1]
  671. self.founders.sort(orig_target_header)
  672. data.sort(target_header)
  673. self.assertEqual(self.founders[orig_target_header], data[target_header])
  674. def test_unicode_renders_markdown_table(self):
  675. # add another entry to test right field width for
  676. # integer
  677. self.founders.append(('Old', 'Man', 100500))
  678. self.assertEqual('first_name|last_name |gpa ', unicode(self.founders).split('\n')[0])
  679. def test_databook_add_sheet_accepts_only_dataset_instances(self):
  680. class NotDataset(object):
  681. def append(self, item):
  682. pass
  683. dataset = NotDataset()
  684. dataset.append(self.john)
  685. self.assertRaises(tablib.InvalidDatasetType, book.add_sheet, dataset)
  686. def test_databook_add_sheet_accepts_dataset_subclasses(self):
  687. class DatasetSubclass(tablib.Dataset):
  688. pass
  689. # just checking if subclass of tablib.Dataset can be added to Databook
  690. dataset = DatasetSubclass()
  691. dataset.append(self.john)
  692. dataset.append(self.tom)
  693. try:
  694. book.add_sheet(dataset)
  695. except tablib.InvalidDatasetType:
  696. self.fail("Subclass of tablib.Dataset should be accepted by Databook.add_sheet")
  697. def test_csv_formatter_support_kwargs(self):
  698. """Test CSV import and export with formatter configuration."""
  699. data.append(self.john)
  700. data.append(self.george)
  701. data.headers = self.headers
  702. expected = 'first_name;last_name;gpa\nJohn;Adams;90\nGeorge;Washington;67\n'
  703. kwargs = dict(delimiter=';', lineterminator='\n')
  704. _csv = data.export('csv', **kwargs)
  705. self.assertEqual(expected, _csv)
  706. # the import works but consider default delimiter=','
  707. d1 = tablib.import_set(_csv, format="csv")
  708. self.assertEqual(1, len(d1.headers))
  709. d2 = tablib.import_set(_csv, format="csv", **kwargs)
  710. self.assertEqual(3, len(d2.headers))
  711. def test_databook_formatter_support_kwargs(self):
  712. """Test XLSX export with formatter configuration."""
  713. self.founders.export('xlsx', freeze_panes=False)
  714. def test_databook_formatter_with_new_lines(self):
  715. """Test XLSX export with new line in content."""
  716. self.founders.append(('First\nSecond', 'Name', 42))
  717. self.founders.export('xlsx')
  718. if __name__ == '__main__':
  719. unittest.main()