Compare commits

...

531 Commits

Author SHA1 Message Date
Claude Paroz 993af5b0b4 Add release date for 1.0.0 2020-01-13 19:08:47 +01:00
Hugo van Kemenade 0accb4c437 Add project_urls metadata for programmatic use 2020-01-11 15:00:51 +01:00
Claude Paroz 0821716983 Refs #401 - Fixed some flake8 errors 2020-01-11 11:57:53 +01:00
Claude Paroz 660990b6b0 Fixes #440 -Normalize stream inputs as IO streams 2020-01-11 11:30:16 +01:00
Claude Paroz 6152d995f0 Tablib docs isn't the place to debate GPL vs MIT licensing 2019-12-31 14:08:08 +01:00
Claude Paroz 0ea6d706a9 Refs #293 - Ensured Dataset can be pickled/unpickled without damages 2019-12-30 16:23:38 +01:00
Hugo van Kemenade 00d8ab0b37 Remove unnecessary MANIFEST.in (#439)
* This MANIFEST.in unnecessary with setuptools_scm

https://github.com/pypa/setuptools_scm/blob/master/README.rst#file-finders-hook-makes-most-of-manifestin-unnecessary

* No manifest to check
2019-12-11 10:51:21 +01:00
Hugo van Kemenade 06c2326dc0 Refactor error raising to remove duplication 2019-12-10 10:55:30 +01:00
Daniel Santos fa30ea858d Implement feature that allows to export tabular data suited to a… (#437) 2019-12-10 01:04:03 +02:00
Hugo van Kemenade 4de2e17984 README: Add more badges (#435) 2019-12-02 11:10:54 +02:00
Hugo van Kemenade 52b64757b7 Remove unused Pipfile (#436) 2019-12-02 11:10:41 +02:00
Joseph Herlant 5ff4a55ae6 Force default_flow_style for pyyaml safe_dump
This is to keep behavior of pre-5.1 pyyaml.
2019-11-24 20:43:12 +01:00
Claude Paroz ce7d887adc Documented csv import/export options from standard lib (#431) 2019-11-14 18:08:51 +02:00
Hugo van Kemenade 57a535f577 Fix NameError: name '_get_column_widths' is not defined (#433)
* Fix NameError: name '_get_column_widths' is not defined

* Also test ReSTFormat.export_set
2019-11-12 10:53:20 +02:00
Claude Paroz 357a5594c5 Admonitions must have a title 2019-11-11 21:25:56 +01:00
Claude Paroz f61b8d8926 Fixes #422 - Allow ability to lazy-load external modules (#430) 2019-11-11 21:46:28 +02:00
Hugo van Kemenade 22a193dafb No __cmp__ or cmp in Python 3 (#429)
* No __cmp__ or cmp in Python 3

* Add rich comparisons

* Simplify using total_ordering decorator
2019-11-11 12:06:25 +02:00
Hugo van Kemenade b539e96697 Update testing: add docs + lint jobs; use pre-commit for linting (#426)
* Move docs and lint to their own [3.8] build job for more parallelism

* No codecov for docs or lint

* Move isort into pre-commit

* Add some handy linters to pre-commit

* Add rst-backticks linter and fix the errors

* Add pyupgrade and add upgrades

* Test docs and lint on GitHub Actions

* Xenial is default
2019-11-10 21:09:18 +02:00
Claude Paroz 626a062747 Fixes #421 - Make all dependencies optional
Thanks Hugo van Kemenade for the review.
2019-11-10 18:00:31 +01:00
Claude Paroz 9d2f7d6999 Point README to the documentation 2019-11-08 17:31:25 +01:00
Claude Paroz a9d9671b7f Moved format documentation from code to docs (#420) 2019-11-06 22:37:01 +02:00
Claude Paroz f1046cd13e Refs #256 - Implement class-based formats
This allows to extend Tablib with new formats far more easily.
2019-11-02 17:44:05 +01:00
Claude Paroz d21bd10908 Revert " Implement feature new format: Cli. Generate adapter for tabulate. This close issue #340"
This reverts commit c26159d48f.
The patch was NOT ready to be merged.
2019-10-30 14:24:07 +01:00
Daniel Santos c26159d48f Implement feature new format: Cli. Generate adapter for tabulate. This close issue #340
* Implement feature new format: Cli. Generate adapter for  tabulate. This close issue #340

* Write respective tests.

* Correct name Clase Base Test

* Implement missing class method to export cli.

* Remove property headers in method export book Cli.

* Remove cli from list to test Iterable data books.
2019-10-30 14:13:39 +01:00
Daniel Santos 34fe72305e Add missing extraline. 2019-10-29 22:13:57 +01:00
Daniel Santos d94420d968 Elucidate the use of filters (and, or). 2019-10-29 22:08:31 +01:00
Daniel Santos 51a720b21c Merge pull request #416 from xdanielsb/doc-formats
Update doc, clarify the use and scope of the flag headers.
2019-10-28 16:53:02 +01:00
Daniel 20f51d0bc1 Update doc, apply requested changes in headers flag doc. 2019-10-28 16:45:26 +01:00
Daniel 87d15a1529 Update doc, clarify the use and scope of the flag headers. 2019-10-27 21:00:21 +01:00
Hugo van Kemenade 08a6759520 Fixes #202 - Keep error content when importing xls files (#415)
Fixes #202 - Keep error content when importing xls files
2019-10-22 22:49:10 +03:00
Claude Paroz 205403d377 Fixes #202 - Keep error content when importing xls files 2019-10-22 20:48:45 +02:00
Claude Paroz 9858539c87 Add known third parties to isort 2019-10-22 14:19:20 +02:00
Hugo van Kemenade 201d8d9910 Merge pull request #412 from claudep/isort
Display isort errors
2019-10-22 14:06:31 +03:00
Claude Paroz fede4a4f13 Display isort errors 2019-10-22 12:29:55 +02:00
Hugo a76933edd5 Refs #401 - Sort imports with isort 2019-10-22 11:59:19 +02:00
Hugo 3197e59b25 Add pyproject.toml as per PEP 518 2019-10-22 11:52:20 +02:00
Claude Paroz 1f000f2f2c Removed unused imports 2019-10-20 12:23:05 +02:00
Hugo van Kemenade 7879fef65a Remove Python 2 code (#410)
Remove Python 2 code
2019-10-20 12:58:52 +03:00
Hugo b8bff1190e Don't omit tests from coverage https://nedbatchelder.com/blog/201908/dont_omit_tests_from_coverage.html 2019-10-20 12:36:20 +03:00
Hugo d77aba6210 Add more tests 2019-10-20 12:32:00 +03:00
Hugo bf6e5c2e78 100% Row test coverage 2019-10-20 12:27:51 +03:00
Hugo 088b916bab __unicode__ not used in Python 3 2019-10-20 12:04:33 +03:00
Hugo e4ac50260e __getslice__ is deprecated since Python 2.0 and it is not available in Python 3
https://docs.python.org/2/reference/datamodel.html#object.__getslice__
2019-10-19 20:01:07 +03:00
Hugo 7347d07624 Upgrade Python syntax with pyupgrade --py3-plus 2019-10-19 19:25:34 +03:00
Hugo c9027b446c Drop support fo Python 2.7 2019-10-19 19:24:03 +03:00
Hugo van Kemenade 825de0193b Test Windows, macOS and Linux on GitHub Actions (#409)
Test Windows, macOS and Linux on GitHub Actions
2019-10-19 19:20:42 +03:00
Claude Paroz 78b483d39e Fixes #368 - Avoid crashing when exporting empty string in ReST 2019-10-19 18:00:47 +02:00
Claude Paroz 8f09789d40 [csv] Fixes #342 - Feed only 1k of content to csv.Sniffer
Thanks Rivo Laks for the suggestion.
2019-10-19 17:37:56 +02:00
Hugo e0e75ed43c Test Windows, macOS and Linux on GitHub Actions 2019-10-19 18:25:02 +03:00
Peyman Salehi bdc84255a8 Fix some linting errors 2019-10-19 16:57:14 +02:00
Peyman Salehi b3c7145c40 Drop python 2 support
Remove support python 2 from doc, requirements.txt and config
Replace unicode with str
Remove dbfpy folder and rename dbfpy3 to dbfpy
Remove compat file and remove python2 packages from dependency
2019-10-19 16:30:57 +02:00
Claude Paroz 44f43516a5 Refs #250 - Test that commas embedded in quoted strings can be imported 2019-10-19 15:05:46 +02:00
Hugo van Kemenade e0a40577fd Update docs (#406)
Update docs
2019-10-19 16:02:55 +03:00
Hugo 0329eb6168 Update docs 2019-10-19 15:33:33 +03:00
Hugo van Kemenade 4c3dc847b0 Add release checklist (#403)
Add release checklist
2019-10-19 15:00:14 +03:00
Hugo 89fbd54b00 Fix check-manifest 2019-10-19 14:55:31 +03:00
Hugo van Kemenade 067dc769dc Fix typos (#404)
Fix typos
2019-10-19 14:54:25 +03:00
Hugo 1726c1cf37 Add release checklist 2019-10-19 14:45:55 +03:00
Hugo debe77e432 Fix typos 2019-10-19 13:56:34 +03:00
Hugo van Kemenade 0e022d89e5 Merge pull request #402 from hugovk/release-prep
Prepare for release
2019-10-19 13:24:35 +03:00
Hugo van Kemenade a40852d1c6 Add release date 2019-10-19 13:11:45 +03:00
Hugo 9b9fb0aa8a Prepare for release 2019-10-18 23:25:50 +03:00
Hugo van Kemenade ca1aa3ad30 Add support for Python 3.8 (#399)
Add support for Python 3.8
2019-10-18 22:11:19 +03:00
Jannis Leidel 2cfde95fe2 Fix PDF documentation generation. 2019-10-18 21:05:17 +02:00
Hugo 5b94682df3 Add support for Python 3.8 2019-10-18 20:46:23 +03:00
Jannis Leidel f6bf14afd2 Add project release config and cleanup project setup. (#398)
* Add project release config and use Travis build stages.

Refs #378.

* Restructure project to use src/ and tests/ directories.

* Fix testing.

* Remove eggs.

* More fixes.

- isort and flake8 config
- manifest template update
- tox ini extension
- docs build fixes
- docs content fixes

* Docs and license cleanup.
2019-10-18 15:57:13 +02:00
Hugo f3d02aa3b0 Test with pytest and send coverage to Codecov 2019-10-05 23:21:40 +02:00
Claude Paroz ca8dbcf9be Refs #108 - Test and improve format autodetection
Autodetection was added for the odf format.
2019-10-04 23:40:24 +02:00
Claude Paroz 4418535030 Refs #288 - Add string starting with '0' to xlsx round trip test 2019-10-04 21:34:33 +02:00
Claude Paroz 5bd896b954 Refs #304 - Test separator exporting 2019-10-04 21:18:30 +02:00
Claude Paroz af414b69d7 Factorized exporting in all formats in tests 2019-10-04 21:13:29 +02:00
Claude Paroz 91062672b5 Fixed #373 - Properly detect xlsx format 2019-10-04 20:46:31 +02:00
Claude Paroz 34334e72a1 Refs #314 - Add datetime to xlsx round trip test 2019-10-04 19:57:04 +02:00
Claude Paroz 5595bb7993 Refs #380 - Removed mention of the develop branch in docs 2019-10-04 19:52:14 +02:00
Claude Paroz 5fde5259d9 Fixes #314 - Delegate type coercion to openpyxl
Thanks Cristiano Lopes for the initial patch.
2019-10-04 19:45:42 +02:00
Peyman Salehi 591e8f7448 Fix missing comma in setup.py 2019-10-04 19:44:15 +02:00
Claude Paroz 4dfe2c2f89 Fixes #388 - Pipfile.lock is not needed for us 2019-10-04 15:45:15 +02:00
Claude Paroz 91608895d6 Fixes #376 - Add missing HISTORY entries 2019-10-04 10:45:05 +02:00
Hugo van Kemenade 0d36390254 Remove call for financial help 2019-10-04 10:28:47 +02:00
Claude Paroz 8ea082ce60 Fixes #274 - Fix Databook.load() params ordering 2019-10-04 09:36:42 +02:00
Claude Paroz a0df54ca22 Reorganized test cases by format 2019-10-03 22:59:12 +02:00
Claude Paroz 0e06b7e328 Refs #322 - Open dbf file in binary mode in docs 2019-10-03 22:24:23 +02:00
Claude Paroz 8aeb5e5158 io.BytesIO is also available in Python 2.7 2019-10-03 21:14:22 +02:00
Claude Paroz a0d19a56cb Made blank lines PEP-8 compatible 2019-10-03 20:54:10 +02:00
Claude Paroz 8cc024e61b Refs #273 - Replaced vendored markup lib by dependency 2019-10-03 20:42:55 +02:00
Claude Paroz a7c40a0881 Updated some links and favour https 2019-10-03 20:27:10 +02:00
Claude Paroz 20de7fad98 Replaced python-tablib.org by tablib.readthedocs.io 2019-10-03 20:16:00 +02:00
Kiran Subbaraman 4969a71f7f Nose link corrected
It now points to https://github.com/nose-devs/nose
2019-10-03 18:58:37 +02:00
Hugo 743776371a Test on Python 3.8 beta 2019-10-03 11:29:10 +02:00
Claude Paroz 326d07c2ed Updated AUTHORS file and alphabetized list 2019-10-03 11:27:40 +02:00
Hugo 2f6ea8c644 Update MANIFEST.in 2019-10-03 11:24:05 +02:00
Hugo 8aaed50cc8 Refs #378 Add Jazzband Contributing Guidelines 2019-10-03 11:24:05 +02:00
Ran Benita a21b276d9c Avoid DeprecationWarning due to invalid escape in docstring
Will become a SyntaxError in Python 3.8:
https://bugs.python.org/issue32912
2019-10-03 11:15:34 +02:00
Hugo van Kemenade e8838b5ce6 Converted README/HISTORY to Markdown format 2019-10-03 11:13:13 +02:00
Hugo van Kemenade 923711d99a Add support for Python 3.7 and drop 3.4 2019-10-03 09:32:43 +02:00
Claude Paroz aac129db66 Refs #378 - Added the Jazzband badge to the README 2019-10-03 09:13:22 +02:00
Claude Paroz d9df89f5da Refs #378 - Updated Travis and GitHub links 2019-10-03 09:10:46 +02:00
schopenhauerzhang 22bb20c74b delete ; 2019-10-03 08:45:56 +02:00
Frost Ming d25d24a9bb Merge pull request #337 from ZuluPro/stream
Added stream to CSV
2019-06-28 09:02:43 +08:00
Anthony Monthe 513bba2c20 Added CSV stream test 2019-06-27 23:19:06 +01:00
kennethreitz 2b9ce02e3c Merge pull request #364 from s-pace/doc/update
[doc website] Add a nice search experience
2019-04-22 22:21:34 -04:00
s-pace f9f28d3d86 feat: add search to the main introduction page 2019-04-22 22:40:48 +02:00
s-pace 0cb50bb008 feat: add search to every documentation pages 2019-04-22 22:40:35 +02:00
Anthony Monthe f55f56ae1d Added stream to CSV 2019-03-30 19:09:12 +00:00
Timo Furrer 0937c9f9ec Merge pull request #358 from claudep/byedistutils
Removed distutils fallback
2019-03-17 16:15:22 +01:00
Timo Furrer 25a66f95ac Merge pull request #356 from claudep/xlsx_read_only
Open xlsx workbooks in read-only mode
2019-03-11 12:12:12 +01:00
Parth Shandilya 6ab511f8c0 Merge pull request #361 from jdufresne/pin
Unpin transient dependencies in requirements.txt
2019-03-10 23:52:05 +05:30
Jon Dufresne 64816258e6 Unpin transient dependencies in requirements.txt
The project is expected to work with the all versions of dependencies as
specified by dependency ranges, not just a single pinned version. Stop
overspecifying them.
2019-03-09 10:13:39 -08:00
Timo Furrer 41cbaa04b9 Merge pull request #359 from claudep/backports.csv
Limit backports.csv install to Python 2
2019-03-09 16:40:24 +01:00
Claude Paroz c136940801 Limit backports.csv install to Python 2 2019-03-09 10:06:33 +01:00
Claude Paroz cf03ecfe25 Removed distutils fallback
As of https://github.com/kennethreitz/setup.py/blob/master/setup.py
2019-03-09 09:57:19 +01:00
Claude Paroz 193b840da2 Open xlsx workbooks in read-only mode
Refs #316
2019-03-09 09:26:10 +01:00
Timo Furrer 733d77ad1e release: 0.13.0 2019-03-08 12:17:07 +00:00
Timo Furrer 3abd7e8c53 Merge pull request #351 from jdufresne/isinstance
Merge multiple isinstance() calls to one
2019-03-03 16:43:10 +01:00
Timo Furrer 0be9e6a74b Merge pull request #353 from jdufresne/pypy
Remove pypy from tox.ini
2019-03-03 16:42:34 +01:00
Timo Furrer ecd0afbcec Merge pull request #248 from jean/master
Editing while reading: punctuation, markup, linebreaks
2019-03-03 16:41:47 +01:00
Jean Jordaan addaa090ef Merge branch 'master' into master 2019-03-03 13:29:21 +07:00
Jon Dufresne f7b3fd4601 Remove pypy from tox.ini
The platform is not tested on Travis and it fails to run with:

    Processing ./.tox/dist/tablib-0.12.1.zip
    Collecting odfpy (from tablib==0.12.1)
    Collecting openpyxl>=2.4.0 (from tablib==0.12.1)
    Collecting backports.csv (from tablib==0.12.1)
      Using cached https://files.pythonhosted.org/packages/71/f7/5db9136de67021a6dce4eefbe50d46aa043e59ebb11c83d4ecfeb47b686e/backports.csv-1.0.6-py2.py3-none-any.whl
    Collecting xlrd (from tablib==0.12.1)
      Using cached https://files.pythonhosted.org/packages/b0/16/63576a1a001752e34bf8ea62e367997530dc553b689356b9879339cf45a4/xlrd-1.2.0-py2.py3-none-any.whl
    Collecting xlwt (from tablib==0.12.1)
      Using cached https://files.pythonhosted.org/packages/44/48/def306413b25c3d01753603b1a222a011b8621aed27cd7f89cbc27e6b0f4/xlwt-1.3.0-py2.py3-none-any.whl
    Collecting pyyaml (from tablib==0.12.1)
    Collecting pandas (from tablib==0.12.1)
      Using cached https://files.pythonhosted.org/packages/81/fd/b1f17f7dc914047cd1df9d6813b944ee446973baafe8106e4458bfb68884/pandas-0.24.1.tar.gz
        Complete output from command python setup.py egg_info:
        Traceback (most recent call last):
          File "<module>", line 1, in <module>
          File "/tmp/pip-install-F5lmAg/pandas/setup.py", line 732, in <module>
            ext_modules=maybe_cythonize(extensions, compiler_directives=directives),
          File "/tmp/pip-install-F5lmAg/pandas/setup.py", line 475, in maybe_cythonize
            numpy_incl = pkg_resources.resource_filename('numpy', 'core/include')
          File "tablib/.tox/pypy/site-packages/pkg_resources/__init__.py", line 1144, in resource_filename
            return get_provider(package_or_requirement).get_resource_filename(
          File "tablib/.tox/pypy/site-packages/pkg_resources/__init__.py", line 361, in get_provider
            __import__(moduleOrReq)
        ImportError: No module named numpy

        ----------------------------------------
    Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-F5lmAg/pandas/
2019-03-02 08:56:06 -08:00
Parth Shandilya 79dc77de49 Merge pull request #352 from jdufresne/ws
Trim trailing white space throughout the project
2019-03-02 22:18:07 +05:30
Jon Dufresne b057cdf05e Trim trailing white space throughout the project
Many editors clean up trailing white space on save. By removing it all
in one go, it helps keep future diffs cleaner by avoiding spurious white
space changes on unrelated lines.
2019-03-02 08:42:53 -08:00
Jon Dufresne fc2f3c07c8 Merge multiple isinstance() calls to one 2019-03-02 08:38:03 -08:00
Timo Furrer a10327a283 Merge pull request #350 from browniebroke/bugfix/invalid-ascii-csv
Import ascii characters not valid with unicode literals - updated
2019-03-02 15:06:21 +01:00
Bruno Alla e0de42ef06 Add backports.csv to requirements.txt 2019-03-02 10:44:38 -03:00
Bruno Alla f757ab84d1 Merge branch 'master' into bugfix/invalid-ascii-csv
# Conflicts:
#	setup.py
#	tablib/compat.py
#	test_tablib.py
2019-03-02 10:41:07 -03:00
Timo Furrer dc24fda415 Merge pull request #333 from hudgeon/master
Updated xlsx format to remove reference to openpyxl's deprecated get_active_worksheet
2019-03-02 13:03:30 +01:00
Timo Furrer 3ba8d529fc Merge pull request #348 from mloesch/jira
Add Jira table export
2019-03-02 12:16:00 +01:00
Timo Furrer a8bdb4b28f Merge pull request #338 from lepuchi/hotfix/csv-new-line
Handle case where there is an empty line in CSV
2019-03-02 12:12:30 +01:00
Timo Furrer 1aaf235751 Merge pull request #344 from jdufresne/tox-pandas
Include pandas dependency when testing with tox
2019-03-02 12:04:35 +01:00
Parth Shandilya 36ec60d5dd Merge pull request #343 from jdufresne/od
Remove vendored ordereddict package
2019-03-02 00:08:40 +05:30
Parth Shandilya babcbfd949 Merge pull request #339 from thombashi/replace_deprecated_method
Replace a deprecated method call
2019-03-02 00:07:39 +05:30
Parth Shandilya 29b2c08da0 Merge pull request #346 from jdufresne/compat
Remove unused compat entries
2019-03-01 23:59:27 +05:30
Parth Shandilya 862a681263 Merge pull request #345 from jdufresne/cache
Enable pip cache in Travis CI
2019-03-01 23:58:22 +05:30
Mathias Loesch 102073c426 Add Jira table export 2019-01-23 22:34:45 +01:00
Jon Dufresne 499ce52304 Remove unused compat entries
Organize both the Python2 & Python3 sections in the same order so they
are easier to compare.

Removed:

- basestring
- ifilter
- bytes
2019-01-01 10:40:29 -08:00
Jon Dufresne c650b67e06 Enable pip cache in Travis CI
Reduce load on PyPI servers and slightly speed up builds.

For more information, see:

https://docs.travis-ci.com/user/caching/#pip-cache
2019-01-01 10:32:08 -08:00
Jon Dufresne 3e4d6fb5aa Include pandas dependency when testing with tox
Allows all tests to pass.

As pandas is defined as an 'extra', use tox's 'extras' feature. This
requires tox 2.4+, so document that as well.

https://tox.readthedocs.io/en/latest/config.html#conf-extras
2019-01-01 10:28:29 -08:00
Jon Dufresne dd2ba714d3 Remove vendored ordereddict package
Now that Python 2.6 support has been dropped, can remove the vendored
ordereddict package. Use the stdlib collections.OrderedDict instead.
2019-01-01 10:02:13 -08:00
Tsuyoshi Hombashi a28a057559 Replace a deprecated method call
Workbook.remove_sheet method deprecated since openpyxl 2.4.0
2018-10-06 19:19:09 +09:00
lepuchi d38549ef1e only add row if it exists 2018-10-02 23:26:19 +05:30
kennethreitz 5a359ba4de Update README.rst 2018-09-17 08:14:12 -04:00
kennethreitz 359007444c Update README.rst 2018-09-17 08:13:48 -04:00
Maciej "RooTer" Urbański 4f8949417e ujson presence no longer breaks tablib (resolves #297) (#311) 2018-09-12 16:15:20 -03:00
Bruno Soares 3d5943a8a4 Fix: Circular reference detected error (#332)
* Rename function name

* Add uuid handler on json dumps

* Add myself to authors
2018-09-12 15:49:46 -03:00
Norman Hooper 38486231cc reStructuredText (#336)
* median for Python 2

* More compat

* Support reStructuredText

* Tests
2018-09-12 15:27:10 -03:00
Claude Paroz 75f1bafd69 Removed Python 3.3 support (#310) 2018-09-12 15:24:37 -03:00
Iuri de Silvio 4749760e6f Typo: OSD -> ODS
Fix #330
2018-09-12 15:22:06 -03:00
Gregory Bataille ac3cf67620 fix(): remove openpyxl warning by properly accessing cells (#296) 2018-09-12 08:34:55 -03:00
DougHudgeon f812c29275 Add instructions for handling csv line endings in Windows in Python 3 2018-06-26 10:33:21 +10:00
DougHudgeon 4c5d0b1a45 Instructions for opening Excel workbook and reading the first sheet 2018-06-25 14:25:50 +10:00
DougHudgeon 61063e2b09 Updated xlsx format to use openpyxl's .active property 2018-06-25 14:17:34 +10:00
kennethreitz 4c300e65a5 update install instructions
Signed-off-by: Kenneth Reitz <me@kennethreitz.org>
2017-09-01 15:42:51 -04:00
kennethreitz edbb16ec97 next version 2017-09-01 15:37:00 -04:00
kennethreitz dec5cea722 Merge pull request #307 from audiolion/make-pandas-optional
Make pandas optional
2017-09-01 13:49:44 -04:00
Ryan Castner 38183938dc Change how travis installs to get all test dependencies 2017-09-01 13:33:28 -04:00
Ryan Castner 7f1db4023f Raise NotImplementedError if pandas is not installed 2017-09-01 13:21:21 -04:00
Ryan Castner b09fface1b Make pandas an optional install 2017-09-01 13:20:54 -04:00
kennethreitz 69edb9def3 Update index.rst 2017-08-28 01:14:36 -04:00
kennethreitz ec54918f4a Update tutorial.rst 2017-08-28 01:06:43 -04:00
kennethreitz ab6633549f Update index.rst 2017-08-28 01:04:16 -04:00
kennethreitz 56005d8022 Update README.rst 2017-08-28 01:02:49 -04:00
kennethreitz 36fa7ef097 update docs
Signed-off-by: Kenneth Reitz <me@kennethreitz.org>
2017-08-27 03:56:14 -04:00
kennethreitz bb0abc863e bunk requirements file
Signed-off-by: Kenneth Reitz <me@kennethreitz.org>
2017-08-27 03:49:29 -04:00
kennethreitz 58f6eefe01 Merge branch 'master' of github.com:kennethreitz/tablib 2017-08-27 03:48:10 -04:00
kennethreitz e4726cb85c update docs
Signed-off-by: Kenneth Reitz <me@kennethreitz.org>
2017-08-27 03:48:01 -04:00
kennethreitz 412e690289 Update README.rst 2017-08-27 03:42:15 -04:00
kennethreitz 44e797d70e Update README.rst 2017-08-27 03:41:53 -04:00
kennethreitz 34c14aca18 Update README.rst 2017-08-27 03:41:26 -04:00
kennethreitz 7c318adde4 Update README.rst 2017-08-27 03:41:01 -04:00
kennethreitz 5dd74c0104 drop 2.6 2017-08-27 03:29:44 -04:00
kennethreitz a50ff92ff2 only require pandas if python isn't 2.6
Signed-off-by: Kenneth Reitz <me@kennethreitz.org>
2017-08-27 03:26:21 -04:00
kennethreitz 383d4b9c4e Merge pull request #301 from jasonamyers/feature/dataframes
Adding initial DataFrames Support
2017-08-27 03:22:42 -04:00
Jason Myers 00e2ffa2ef Adding initial DataFrames Support
Signed-off-by: Jason Myers <jason@jasonamyers.com>
2017-08-26 20:43:35 -05:00
kennethreitz a3cd2c9cff history 2017-06-13 12:31:42 -04:00
kennethreitz d89d243a30 v0.11.5 2017-06-13 12:30:27 -04:00
kennethreitz 69abfc3ada use safe load 2017-06-13 12:29:55 -04:00
Bruno Alla 80e72cfa27 Fix unicode encode errors on Python 2 -- Fixes #215
Switch csv library to backports.csv as the implementation
is closer to the python 3 one. Add a test case covering the
problem.

Run tests with unicode_literals from future

Fix unicode encode errors with unicode characters

- Use `backports.csv` instead of `unicodecsv`
- Use StringIO instead of cStringIO
- Clean-up some Python 2 specific code
2017-05-02 17:33:14 +01:00
Nicolas Appriou 05bd0d1d42 fix python interpreter supported version in doc (#286) 2017-04-19 11:02:55 -03:00
Claude Paroz 62807734bd Replaced vendored odfpy by a dependency (#280)
Refs #273.
2017-02-26 19:05:01 -03:00
yarko c5c2dffe42 correct example (#276)
map() is a function in python2, and iterator in python3+;

In any case - map is inefficient compared to either comprehensions (most efficient), or simple loops (close second).
SInce in this case, data.append() returns nothing, use a simple look.
It is clearer, more efficient, and works with both python2 and python3
2017-02-24 09:39:53 -03:00
Claude Paroz 46102d4be7 Replaced vendored omnijson by the standard lib version (#279)
Refs #273.
2017-02-24 09:38:07 -03:00
Claude Paroz 44e9e24fec Replaced vendored pyyaml by a dependency (#278) 2017-02-20 19:41:38 -03:00
Claude Paroz 0ca5520bbc Replaced vendored xlrd/xlwt by dependencies (#277)
Refs #273.
2017-02-20 17:29:22 -03:00
Claude Paroz e66eb4a189 Replaced vendored openpyxl by a dependency (#221)
It is time to make it happen.

* Dropped Python 3.2 support

Recent dependencies are dropping Python 3.2 too.

* Replaced vendored openpyxl by a dependency

Thanks Tommy Anthony for the initial patch.
2017-02-20 12:41:33 -03:00
kennethreitz 0e720d78ca Merge pull request #272 from founders4schools/unicodecsv
Replaced vendored unicodecsv by a dependency
2017-01-24 23:48:54 -05:00
Iuri de Silvio 6afe716d64 Version bump: 0.11.4 2017-01-23 19:10:36 -02:00
Bruno Alla 76cbf9fadf Read version in setup.py without importing tablib 2017-01-15 14:59:49 +00:00
Bruno Alla a93f93a458 Replaced vendored unicodecsv by a dependency
Using https://pypi.python.org/pypi/unicodecsv/0.14.1
2017-01-15 14:47:14 +00: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
Jean Jordaan cd67a63b43 Fix typo in label, add missing newline before directive
Label: s/peed/speed/
2016-08-01 11:13:25 +07:00
Jean Jordaan 19b3d6d06a Change the blind reference mit to an URL
:ref:`MIT Licensed <mit>` had no target in the docs, so change it to a
canonical URL.
2016-08-01 11:11:01 +07:00
Jean Jordaan 59090d33a8 Missed some tabs. 2016-07-31 18:23:21 +07:00
Jean Jordaan a4f974287b Editing while reading: punctuation, markup, linebreaks
I fixed some extra commas, missing apostrophes, and typos;
added some linebreaks between sentences for very long lines;
added explicit markup for console blocks,
got rid of some tabs,
fixed indentation of an admonition, and some more small tweaks.

This supersedes https://github.com/kennethreitz/tablib/pull/84
2016-07-31 18:15:12 +07: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
kennethreitz 2814fbc381 v0.11.3 2016-02-16 08:49:28 -05:00
kennethreitz 9ca1d4ec54 Merge pull request #220 from kennethreitz/master
Master
2016-02-16 08:46:37 -05:00
kennethreitz abbb4e32d8 update footer in docs 2016-02-16 08:29:17 -05:00
kennethreitz f6e757d569 v0.11.2 2016-02-16 08:27:59 -05:00
kennethreitz 9ba0451843 Merge pull request #219 from timofurrer/bugfix/export-only
Fix export only formats
2016-02-16 08:17:56 -05:00
Timo Furrer d99db57d75 Fix export only formats
Formats like LaTeX could have never been exported because
`setattr(cls, set_%s % fmt.title, fmt.import_set)` always failed
for export-only formats and with that the exception was caught in the
outer try/except and the format tuple was set to (None, None) with
`cls._formats[fmt.title] = (None, None)`
2016-02-15 19:29:46 -08:00
kennethreitz 2299c00883 Merge pull request #216 from cmhofer/develop
Error: frzn_col_idx not defined
2016-02-08 18:27:39 -05:00
Claudio Mike Hofer 5ba6f5d91a frzn_col_idx not defined
As in #53 already solved. Freeze panes set to A2 again.
2016-02-08 17:30:33 +01:00
kennethreitz bbdf5f11ab v0.11.1, fix packaging error 2016-02-07 13:46:03 -05:00
kennethreitz 851ba25702 Update README.rst 2016-02-07 11:00:14 -05:00
kennethreitz 039272b274 docs cleanup 2016-02-07 10:56:29 -05:00
kennethreitz d6a7832e60 v0.11.0 2016-02-07 10:53:25 -05:00
kennethreitz e51c4faec7 smarter detect_format function 2016-02-07 10:43:38 -05:00
kennethreitz f7fc3244ee updated history 2016-02-07 10:43:19 -05:00
kennethreitz 53d69bd3ea fix __unicode__ 2016-02-07 08:09:10 -05:00
kennethreitz fcc9700d11 Fix for transpose().transpose() with duplicate keys
#199
2016-02-07 07:29:08 -05:00
kennethreitz 1ec9c18a66 $ make test 2016-02-07 07:09:34 -05:00
kennethreitz 99c28fa560 Merge pull request #206 from kontza/develop
Two 'raise AttributeError' converted to Python 3 -friendly format.
2016-02-07 07:09:12 -05:00
kennethreitz fa7fb579fd Merge pull request #193 from jhermann/patch-1
Formats .tsv and .html are implemented by now
2016-02-07 07:03:25 -05:00
kennethreitz be24de19dc Merge remote-tracking branch 'origin/develop' into develop 2016-02-07 07:01:46 -05:00
kennethreitz 1d4f4b68ca cleanup 2016-02-07 07:01:13 -05:00
kennethreitz 8debeb26ac Merge branch 'develop' into import_export
# Conflicts:
#	tablib/core.py
#	tablib/formats/_csv.py
#	tablib/formats/_xlsx.py
2016-02-07 07:00:55 -05:00
kennethreitz 38e1ee6c3d Merge pull request #186 from hdzierz/develop
Added a mechanism to avoid datetime.datetime issues when serializing dat...
2016-02-07 06:44:20 -05:00
kennethreitz a774789252 /s/unique/remove_duplicates
#182
2016-02-07 06:40:46 -05:00
kennethreitz 995eabad37 Merge pull request #182 from cherepski/develop
Adding ability to unique all rows in a dataset.
2016-02-07 06:38:58 -05:00
kennethreitz d90358bf69 Merge branch 'develop' of https://github.com/rabinnankhwa/tablib into develop
# Conflicts:
#	AUTHORS
2016-02-07 06:36:12 -05:00
kennethreitz c5920249de python 3.2 is terrible 2016-02-07 06:32:10 -05:00
kennethreitz 9b6a73c97c fixed stuipid test 2016-02-07 06:29:07 -05:00
kennethreitz 679bd115b6 Merge branch 'develop' of https://github.com/papisz/tablib into develop 2016-02-07 06:09:55 -05:00
kennethreitz 32cbc36fc1 Merge branch 'latex-export' of https://github.com/mloesch/tablib into develop 2016-02-07 06:08:23 -05:00
kennethreitz 8bded88559 update development guide 2016-02-07 06:01:56 -05:00
kennethreitz f8f57a467e updates to install guide 2016-02-07 05:56:19 -05:00
kennethreitz a11a993955 fix documentation 2016-02-07 05:52:45 -05:00
kennethreitz 25894f2948 remove bunk file 2016-02-07 05:47:28 -05:00
kennethreitz 591b89693e remove TODO.rst 2016-02-07 05:46:45 -05:00
kennethreitz 85d9c2497e --universal 2016-02-07 05:46:20 -05:00
kennethreitz eaf52b691e Merge pull request #204 from dallagi/notabs
Replace tabs with whitespaces
2016-01-27 17:19:37 -05:00
kennethreitz 6f53c5d2b9 Merge pull request #209 from jdharms/develop
Small documentation fix in Dataset class
2016-01-27 17:17:38 -05:00
kennethreitz 90ee799576 Merge pull request #208 from stclair/develop
Fix XLSX import
2016-01-27 17:16:22 -05:00
Iuri de Silvio c02a21ccd2 Merge pull request #213 from go8ose/develop
Add section on importing to tutorial.
2016-01-20 10:48:06 -02:00
Geoff Crompton fa045ca114 Add section on importing to tutorial. 2016-01-18 12:13:15 +11:00
Daniel Harms 65703550c3 Small documentation fix in Dataset class 2015-11-10 14:15:37 -05:00
Wes 1fcb98f9ae Fix XLSX import
Calling import_set on an XLSX file was throwing a TypeError from
Openpyxl. Openpyxl Reader load_workbook requires a file-like object as the first
argument. This commit fixes the error by passing in a file-like object
instead of a string.
2015-11-09 06:45:28 -07:00
Rumpu-Jussi e2d45ecff7 More Python 3 -friendly formatting. 2015-10-27 14:46:43 +02:00
Rumpu-Jussi 47d92277cc More Python 3 -friendly formatting. 2015-10-27 14:45:55 +02:00
Rumpu-Jussi fdd74b5b0c More Python 3 -friendly formatting. 2015-10-27 14:44:07 +02:00
Rumpu-Jussi de052f0fac Two 'raise AttributeError' converted to Python 3 -friendly format. 2015-10-27 14:33:44 +02:00
Marco Dalla G 2f3acf5af4 Added myself to authors, as indicated in README 2015-10-07 11:31:26 +03:00
Marco Dalla G c4e8755cd2 Replaced tabs with whitespaces 2015-10-07 11:25:56 +03:00
Mathias Loesch 79dc4524a0 Added LaTeX table export format 2015-06-04 09:26:35 +02:00
Iuri de Silvio a785d77901 Merge pull request #194 from tommyanthony/develop
Fixed a compatibility bug for Python 3
2015-05-27 13:19:36 -03:00
Thomas Anthony b3485ec942 Fixed a compatibility bug for Python 3 by adding xrange to
compat.py.

The code in tablib/formats/_xls.py used xrange in parsing excel spreadsheets.
xrange is not a builtin for Python 3, so I've added
	xrange = range
in compat.py and imported it in tablib/formats/_xls.py.
2015-05-26 20:06:42 -07:00
Jürgen Hermann 28b358c9da Formats .tsv and .html are implemented by now
Removed mentioning of "wanted" formats that exist.
2015-04-08 15:59:55 +02:00
Iuri de Silvio 24657520e9 Merge pull request #189 from tsroten/issue_184
Fixes Row slicing. Fixes #184.
2015-04-05 20:05:31 -03:00
Iuri de Silvio 66d9e50984 New import/export interface with dataset and databook import_ and export methods
and overloaded `import_set` and `import_book` functions.
2015-04-05 19:51:56 -03:00
Thomas Roten 541fba6786 Fixes Row slicing. Fixes #184. 2015-03-28 16:14:27 -04:00
Helge bc6398ffb0 Added a mechanism to avoid datetime.datetime issues when serializing data 2015-03-02 15:06:31 +13:00
Kevin Cherepski dca7bc9a7d Adding ability to unique all rows in a dataset. 2015-02-04 11:53:14 -05:00
Iuri de Silvio 2fbda0f43d Merge pull request #176 from sramana/develop
Fix import errors when installed from source
2014-11-15 16:28:57 -02:00
Ramana Varanasi e350f9428b Fix import errors when installed from source 2014-11-10 16:03:10 +05:30
Iuri de Silvio 68dba0a77d Merge pull request #173 from amarandon/develop
Fix JSON import example
2014-10-04 11:26:55 -03:00
Alex Marandon 028be03c2c Fix JSON import example
The example was triggering this error:

    JSONError: Expecting property name: line 1 column 3 (char 2)

This is because JSON property names should be wrapped in double
quotes.

While at it, I've fixed the typo in "last_name"
2014-10-03 09:17:38 +02:00
Iuri de Silvio e1d65ba3c8 Merge pull request #172 from thibault/patch-1
Minor typo correction
2014-09-26 16:35:29 -03:00
Thibault J. e4cb3bcd9b Minor typo correction
Requests -> Tablib
2014-09-23 11:46:05 +02:00
Iuri de Silvio bf9510e0c7 Merge pull request #170 from phargogh/dbf_docs_repair
Cleaning up DBF API documentation
2014-09-06 09:54:21 -03:00
James Douglass 82ae3ca507 Cleaning up DBF documentation
Fixing indentation issues (off by one space), which caused problems
with the sphinx rendering of the DBF docstring and otherwise cleaning
up the sphinx docstring.
2014-09-05 14:56:33 -07:00
rabinnankhwa 5fbdd56fba filter row and column values 2014-08-31 00:12:44 +05:45
rabinnankhwa f187cef5f4 adding support for creating subset of a dataset. 2014-08-30 23:52:35 +05:45
rabinnankhwa 87892d7266 used get method of dictionary instead of exception handling 2014-08-30 08:56:17 +05:45
rabinnankhwa 20e2ce5ba0 __getslice__ method of Row classcorrected 2014-08-30 08:26:08 +05:45
Iuri de Silvio 48e576954d Merge pull request #153 from phargogh/dbf-support
Support for dBase (DBF) files
2014-08-26 08:24:36 -03:00
James Douglass a21f8187f8 Adding DBF support.
Squashing two squashes.

Adding DBF support

Adding the DBFpy python package

The DBFpy package provides basic dbf support for python.  Still need to
write an interface format file for tablib.

Adding DBF format and imports in compat.py

Adding DBF format to formats.__init__

DBF format had not been committed to formats.__init__, so I’m adding it.

Adding a dbf import test

Adding at test to check whether a DBF can be created properly and
compare it against a regression binary string.

Adding an import_set test (and renaming another)

Adding an import_set test that conforms with the other import_set tests
for other formats.  I’m also adding an export_set function.

Fixing system site-packages import

Importing dbfpy from tab lib.packages instead of system site packages.

Fixing a syntaxError in dbfpy/dbfnew.py

Fixing an issue with ending field definitions

DBFPY, when writing a DBF, terminates the field definitions with a
newline character.  When importing a DBF from a stream, however, DBFPY
was looking only for the \x0D character rather than the newline.  Now
we consider both cases.

Adding a test for dbf format detection

Adding DBF filetype detection tests

Adding tests for YAML, JSON, TSV, CSV using the DBF detection function.

Handling extra exceptions in dbf detection

Adding exception handling for struct.error, an exception that DBFPY
raises when trying to unpack a TSV table.  Since it’s not a DBF file,
we know it’s not a DBF and return False.

Fixing an issue with the DBF set exporting test

The DBF set export test needed a bit enabled (probably the writeable
bit?) before the test would match the regression output.

Updating dbf interface

Updating the int/float class/type checking in the dbf format file.
This allows for python2 and python3 compatibility.

Tweaking dbfpy to work with python3

Altering a couple of imports.

Updating dbf tests for binary data compatibility

Making regression strings binary and improving debug messages for dbf
assertion errors.

Improving file handling for python 2 and 3

Updating DBF file handling for both python 2 and 3 in the _dbf
interface.

Adding a (seemingly) functional dbfpy for python3

I’ve made dbfpy python3 compatible!  Tests appear to pass.
A significant change was made to the format detection test whereby I
made the input string a binary (bytes) string.  If the string is not a
bytes string by the time we try to detect the format, we try to decode
the string as utf-8 (which admittedly might not be the safest thing to
do) and try to decode anyways.

Updating imports for tablib dbf interface

Now importing python2 or python3 versions as appropriate.

Updating dbf package references in compat.py

Cleaning up debugging print statements

Updating stream handling in dbf interface

Factoring the open() call out of the py3 conditional and removing the
temp file before returning the stream value.

Adding dbfpy3 init.py

I had apparently missed the dbfpy3 init file when committing dbfpy3.

Adding dbfpy and dbfpy3 to setup.py's package list

Switching test order of formats

Putting dbf format testing ahead of TSV.  In some of my tests with
numeric DBF files, I encountered an issue where the ASCII horizontal
tab character (0x09) would appear in a numeric DBF.  Because of the
order of tabular format imports, though, format detection would
recognize it as a TSV and not as a DBF.

Adding my name to AUTHORS.

Adding a DBF property to tab lib core

Documentation includes examples on how to explicitly load a DBF
straight from a file and how to load a DBF from a binary string.  Also,
how to write the binary data to a file.

Adding DBF format notes to README

Adding exclamation point to DBF section title

Matching formatting of XLS section

Updating setup.py to match current dev state

Setup.py had been updated since I forked the tablib repo, so I’m
updating setup.py to match its current structure while still
maintaining DBF compatibility.

Fixed callable collumn test

the test was sending a list instead of a function

CORE CONTRIBUTORS

🍰 @iurisilvio

v0.10.0

WHEELS

3.3, 3.4

makefile for WHEELS

v0.10.0 history

ALL

Separate py2 and py3 packages to avoid installation errors. Fix #151

Running travis and tox with python 3.4.

Adding DBF support

Adding the DBFpy python package

The DBFpy package provides basic dbf support for python.  Still need to
write an interface format file for tablib.

Adding DBF format and imports in compat.py

Adding DBF format to formats.__init__

DBF format had not been committed to formats.__init__, so I’m adding it.

Adding a dbf import test

Adding at test to check whether a DBF can be created properly and
compare it against a regression binary string.

Adding an import_set test (and renaming another)

Adding an import_set test that conforms with the other import_set tests
for other formats.  I’m also adding an export_set function.

Fixing system site-packages import

Importing dbfpy from tab lib.packages instead of system site packages.

Fixing a syntaxError in dbfpy/dbfnew.py

Fixing an issue with ending field definitions

DBFPY, when writing a DBF, terminates the field definitions with a
newline character.  When importing a DBF from a stream, however, DBFPY
was looking only for the \x0D character rather than the newline.  Now
we consider both cases.

Adding a test for dbf format detection

Adding DBF filetype detection tests

Adding tests for YAML, JSON, TSV, CSV using the DBF detection function.

Handling extra exceptions in dbf detection

Adding exception handling for struct.error, an exception that DBFPY
raises when trying to unpack a TSV table.  Since it’s not a DBF file,
we know it’s not a DBF and return False.

Fixing an issue with the DBF set exporting test

The DBF set export test needed a bit enabled (probably the writeable
bit?) before the test would match the regression output.

Updating dbf interface

Updating the int/float class/type checking in the dbf format file.
This allows for python2 and python3 compatibility.

Tweaking dbfpy to work with python3

Altering a couple of imports.

Updating dbf tests for binary data compatibility

Making regression strings binary and improving debug messages for dbf
assertion errors.

Improving file handling for python 2 and 3

Updating DBF file handling for both python 2 and 3 in the _dbf
interface.

Adding a (seemingly) functional dbfpy for python3

I’ve made dbfpy python3 compatible!  Tests appear to pass.
A significant change was made to the format detection test whereby I
made the input string a binary (bytes) string.  If the string is not a
bytes string by the time we try to detect the format, we try to decode
the string as utf-8 (which admittedly might not be the safest thing to
do) and try to decode anyways.

Updating imports for tablib dbf interface

Now importing python2 or python3 versions as appropriate.

Updating dbf package references in compat.py

Cleaning up debugging print statements

Updating stream handling in dbf interface

Factoring the open() call out of the py3 conditional and removing the
temp file before returning the stream value.

Adding dbfpy3 init.py

I had apparently missed the dbfpy3 init file when committing dbfpy3.

Adding dbfpy and dbfpy3 to setup.py's package list

Switching test order of formats

Putting dbf format testing ahead of TSV.  In some of my tests with
numeric DBF files, I encountered an issue where the ASCII horizontal
tab character (0x09) would appear in a numeric DBF.  Because of the
order of tabular format imports, though, format detection would
recognize it as a TSV and not as a DBF.

Adding my name to AUTHORS.

Adding a DBF property to tab lib core

Documentation includes examples on how to explicitly load a DBF
straight from a file and how to load a DBF from a binary string.  Also,
how to write the binary data to a file.

Adding DBF format notes to README

Adding exclamation point to DBF section title

Matching formatting of XLS section

Updating setup.py to match current dev state

Setup.py had been updated since I forked the tablib repo, so I’m
updating setup.py to match its current structure while still
maintaining DBF compatibility.

Fixed callable collumn test

the test was sending a list instead of a function

CORE CONTRIBUTORS

🍰 @iurisilvio

v0.10.0

WHEELS

3.3, 3.4

makefile for WHEELS

v0.10.0 history

ALL

Separate py2 and py3 packages to avoid installation errors. Fix #151

Running travis and tox with python 3.4.
2014-08-21 22:06:42 -07:00
Iuri de Silvio 8479df725e Fix some http schemes to follow page scheme. 2014-08-10 11:47:13 -03:00
Iuri de Silvio 333deb2311 Merge pull request #160 from ustun/patch-1
Typo
2014-07-30 08:55:06 -03:00
Ustun Ozgur 0b714f21e1 Typo 2014-07-30 14:46:50 +03:00
Iuri de Silvio ae730b00b1 Merge pull request #154 from fusionbox/freeze-panes
Only freeze the headers row, not the headers columns
2014-06-26 14:00:12 -03:00
Iuri de Silvio 84e8b0384f Merge pull request #155 from fusionbox/update-unicodecsv
Update the vendored unicodecsv to fix None handling
2014-06-24 22:42:56 -03:00
Gavin Wahl 7a2842a8af Update the vendored unicodecsv to fix None handling
The old version of unicodecsv incorrectly (according
https://docs.python.org/2/library/csv.html#csv.writer) encoding None
values as the string 'None', instead of the string '' as the python
documentation specifies.

The newest version of unicodecsv has fixed this.

Fixes #121
2014-06-24 15:22:12 -06:00
Gavin Wahl 954bbdccf3 Only freeze the headers row, not the headers columns
Fixes #53
2014-06-16 15:31:00 -06:00
Iuri de Silvio 7acaa8460d Running travis and tox with python 3.4. 2014-05-27 21:18:14 -03:00
Iuri de Silvio 84e7e251ae Separate py2 and py3 packages to avoid installation errors. Fix #151 2014-05-27 19:25:15 -03:00
Kenneth Reitz dc868eff31 ALL 2014-05-27 12:52:57 -04:00
Kenneth Reitz 43356e908c v0.10.0 history 2014-05-27 12:52:43 -04:00
Kenneth Reitz f7acc19523 makefile for WHEELS 2014-05-27 12:51:51 -04:00
Kenneth Reitz c5972db8f0 Merge branch 'develop' 2014-05-27 12:51:30 -04:00
Kenneth Reitz 1cc051f3e8 .org 2014-05-27 12:49:23 -04:00
Kenneth Reitz 3da155ce0d 3.3, 3.4 2014-05-27 12:49:11 -04:00
Kenneth Reitz 9a34cf0980 WHEELS 2014-05-27 12:49:07 -04:00
Kenneth Reitz 434f66b4eb v0.10.0 2014-05-27 12:48:00 -04:00
Kenneth Reitz d056916c53 CORE CONTRIBUTORS
🍰 @iurisilvio
2014-05-27 12:47:54 -04:00
Iuri de Silvio cf5239f097 Merge pull request #150 from brad/csv-newlines
Allow csv fields to have multiple lines.
2014-05-02 11:25:30 -03:00
Brad Pitcher 49d8cb816f allow csv fields to have multiple lines 2014-05-01 08:12:39 -07:00
Iuri de Silvio fbd277ff2e Merge pull request #149 from brad/tests_fix
Load json to dict to workaround random dictionary hashes. Fix #147
2014-05-01 09:17:34 -03:00
Brad Pitcher 6f4572fa56 load json to dict to workaround random dictionary hashes 2014-04-30 16:27:20 -07:00
Iuri de Silvio 453fc8614c Add NOTICE and tests files to manifest
Tests are code.

The NOTICE file is about third-party licenses and are important too.
2014-04-23 15:01:31 -03:00
Iuri de Silvio 01cf58e431 Add travis badge to readme 2014-04-23 11:25:23 -03:00
Iuri de Silvio f6cd89c76c Fix DeprecationWarnings: assertEquals -> assertEqual 2014-04-19 15:36:00 -03:00
Iuri de Silvio 1e0f30e8a6 Add py33 to travis matrix 2014-04-19 15:26:00 -03:00
Iuri de Silvio 569d35bfca Exit with error when python setup.py test fails 2014-04-19 15:25:43 -03:00
Iuri de Silvio d40cdfbcd0 Merge pull request #146 from kennethreitz/fix/unicode_append
Fix test_unicode_append
2014-04-19 14:51:06 -03:00
Iuri de Silvio 86bbaf9bea Merge pull request #141 from fcurella/develop
added missing yaml3 module to setup.py
2014-04-19 14:48:19 -03:00
Iuri de Silvio 0ed01d85b9 Fix test_unicode_append 2014-04-19 12:41:21 -03:00
Iuri de Silvio fc4cc7fa14 Merge pull request #144 from aleasoluciones/develop
Remove `extend` from first example to make it simple.
2014-04-14 09:06:52 -03:00
papisz 70716fdd21 CSV custom delimiter support 2014-04-09 22:35:56 +02:00
Guillermo Pascual 1146ec2341 Update docs 2014-04-08 10:13:04 +02:00
Flavio Curella 1a7d597745 added missing package to setup.py 2014-03-10 12:56:33 -05:00
kennethreitz 56b627a561 Merge pull request #137 from gisce/fix_xlsx_detect_test
Use InvalidFileException to fix the test
2014-01-23 10:51:59 -08:00
Eduard Carreras 98e182bed2 Use InvalidFileException to fix the test 2014-01-23 18:15:46 +01:00
Iuri de Silvio c8a5563309 Maintain dataset title after sort. 2014-01-11 13:45:45 -02:00
Thomas Coopman c225a64d68 don't use ExcelWriter with databook 2014-01-11 12:56:11 -02:00
kennethreitz d611d5a14f Merge pull request #117 from iurisilvio/patch-1
Fix typo: avalable -> available
2014-01-08 11:48:28 -08:00
kennethreitz 45121ddd65 Merge pull request #63 from jsdalton/fix_unicode_error_in_html_output
Fix unicode error in html output
2014-01-08 11:47:26 -08:00
kennethreitz c74357cb20 Merge pull request #76 from djv/develop
xls and xlsx import support
2014-01-08 11:46:50 -08:00
kennethreitz 939b0af551 Merge pull request #110 from djrobstep/develop
Fix for a broken YAML test and tsv autodetection
2014-01-08 11:44:30 -08:00
kennethreitz 9c2018653f Merge pull request #111 from dec0dedab0de/develop
using readlines() in _tsv.py fixes a small bug.
2014-01-08 11:44:20 -08:00
kennethreitz 2bc6122ee8 Merge pull request #113 from dec0dedab0de/master
Fixed callable column test
2014-01-08 11:44:06 -08:00
kennethreitz 7f0748aac9 Merge pull request #116 from kachick/fix-tsv-typo
Fix some typos in TSV test comment
2014-01-08 11:43:50 -08:00
kennethreitz 41a5c67159 Merge pull request #119 from iurisilvio/empty_sheet
Remove XLSX empty sheet on export_book
2014-01-08 11:43:35 -08:00
kennethreitz 3efefcc8da Merge pull request #127 from medecau/develop
test python 3.3
2014-01-08 11:43:15 -08:00
kennethreitz d19de6025b Merge pull request #131 from fusionbox/quotes
remove extraneous quote marks
2014-01-08 11:41:15 -08:00
kennethreitz 65ba937c0d Merge pull request #129 from lexual/dataset_typo_fix
fix misspelling. hundres -> hundreds.
2014-01-08 11:41:05 -08:00
kennethreitz 79a2bb888f Merge pull request #135 from overthink/doc-fixes
Fix funny typo, refs to tablib.org
2014-01-08 11:40:59 -08:00
kennethreitz 25eacaf6f0 Merge pull request #130 from lndbrg/patch-1
Add pass to json property.
2014-01-08 11:17:23 -08:00
Mark Feeney c2a9af7fb3 Fix funny typo, refs to tablib.org 2013-11-27 12:38:55 -05:00
Gavin Wahl 3b06f3760d remove extraneous quote marks 2013-11-13 13:01:27 -07:00
Olle Lundberg e7ee3195a7 Add pass to json property.
To conform to the code for the other properties.
2013-11-11 21:57:17 +01:00
lexual 5bd2e3df52 fix misspelling. hundres -> hundreds. 2013-11-08 19:03:53 +11:00
Pedro Rodrigues 837b3f83e6 test python 3.3 2013-10-27 18:57:26 +00:00
kennethreitz ff8f23edd5 Merge pull request #98 from pfctdayelise/fixtests
Remove wrong/unused import so tests will actually run
2013-10-16 23:48:37 -07:00
Iuri de Silvio 5ffcfd56f2 Remove XLSX empty sheet on export_book 2013-09-16 10:28:50 -03:00
Iuri de Silvio 955c24c974 Fix typo: avalable -> available 2013-09-15 15:13:29 -03:00
Kenichi Kamiya 192a5efabb Fix some typos in TSV test comment 2013-08-31 21:04:57 +09:00
James Patrick Robinson Jr 1aafc7e2f4 Fixed callable collumn test
the test was sending a list instead of a function
2013-08-28 14:03:58 -04:00
James Patrick Robinson Jr 9e45b95d12 Removed import of openpyxl all together
It's not needed for any of these tests, but if it were we would
need to check for the python version to import the right one.
2013-08-28 11:40:37 -04:00
James Patrick Robinson Jr d8f0a018ae safe_load is not working for book
yaml.safe_load() was not working for import_book,
changed it to use yaml.load() instead.
2013-08-28 11:24:56 -04:00
James Patrick Robinson Jr 7545f3726e changed import to reflect vendorized openpyxl 2013-08-28 09:45:30 -04:00
James Patrick Robinson Jr 85e2bd73fc put the install back in 2013-08-27 17:34:06 -04:00
James Patrick Robinson Jr 37033903c5 Merge branch 'master' into develop 2013-08-27 17:20:31 -04:00
James Patrick Robinson Jr 02c38c2520 edited travis to match master 2013-08-27 17:14:25 -04:00
James Patrick Robinson Jr 26748deb9f changed split('\r\n') to splitlines() 2013-08-27 11:57:43 -04:00
Robert Lechte 63f6cea132 Fixed tsv auto format detection. 2013-08-28 02:07:06 +12:00
Robert Lechte 1b035f9774 Changed yaml dumping to use safe_dump, for consistency with loading. 2013-08-28 01:58:30 +12:00
Kenneth Reitz 2c14486c33 @alex 2013-08-25 17:45:48 -04:00
Kenneth Reitz 8bc69c9d85 Merge pull request #109 from alex/patch-1
Write the example file reliably in the readme
2013-08-25 13:26:47 -07:00
Alex Gaynor d36a2cbd42 Write the example file reliably in the readme
The previous way doesn't work on PyPy or Jython, and emits warnings in recent python3s.
2013-08-25 12:11:46 -07:00
Brianna Laugher 1ab0eb3fae Remove wrong/unused import 2013-04-10 17:45:42 +10:00
Kenneth Reitz cd71e1a5b1 Merge pull request #94 from techniq/patch-1
Update CI docs (Jenkins->Travis)
2013-03-06 10:02:20 -08:00
Sean Lynch 47f79a7ca1 Update CI docs (Jenkins->Travis) 2013-01-20 23:22:56 -05:00
Kenneth Reitz 9f38efe413 Merge pull request #68 from msabramo/python3
Improve Python 3 compatibility
2012-11-15 18:56:50 -08:00
Kenneth Reitz 5d98239a7e Merge pull request #81 from weirdcanada/frozen-frame-fix
Frozen frame fix
2012-11-15 18:50:22 -08:00
Kenneth Reitz a3f0d02633 Merge pull request #89 from PiPeep/patch-1
Update url for pip vs easy_install in docs/install
2012-11-15 18:03:53 -08:00
Benjamin Woodruff b29007a0df Update url for pip vs easy_install in docs/install
The page referred to in the pip documentation has been moved. It
discusses the features that pip offers over easy_install.
2012-10-31 21:23:49 -03:00
Kenneth Reitz e75c3c1a66 Merge pull request #88 from pfmoore/develop
Remove __init__ from slots in ExcelFormula.py for Python 3.3 compatibility
2012-09-22 09:53:49 -07:00
Paul Moore 47cebbc328 Remove __init__ from slots in ExcelFormula.py for Python 3.3 compatibility 2012-09-21 23:35:24 +01:00
Aaron Levin e4c39524f7 another try at committing 2012-08-01 11:51:23 -04:00
Aaron Levin c88c794314 Fixed Frozen Frame issue in xlsx export 2012-08-01 11:45:12 -04:00
Kenneth Reitz 752443f077 Merge pull request #78 from waywardmonkeys/spelling
Fix typos.
2012-06-08 20:12:34 -07:00
Bruce Mitchener 7c0507bcce Fix typos. 2012-06-08 14:10:43 +07:00
Kenneth Reitz 652ac85549 Merge pull request #77 from rbonvall/fix-typos
Fix typos
2012-06-07 17:07:11 -07:00
Roberto Bonvallet 05ea3c35fc s/Jeckyl/Jekyll/ 2012-06-07 12:05:22 -04:00
Roberto Bonvallet d5fada7e1d s/ebpub/epub/ 2012-06-07 12:04:22 -04:00
Roberto Bonvallet 511c58d4e1 s/reqeust/request/ 2012-06-07 12:03:45 -04:00
Kenneth Reitz c469360a0e new domain 2012-06-05 11:19:56 +02:00
Daniel Velkov 97b4401b18 xls and xlsx import support 2012-06-01 11:11:15 -07:00
Kenneth Reitz 40e0f41b4c Merge pull request #72 from xando/develop
import_book method for xls format implemented
2012-05-16 12:13:57 -07:00
xando 39435727ba XLS import_book method implemented. 2012-05-16 17:22:14 +01:00
xando eda9d5af03 Generic method import_book (similar to import_set) to import data into Databook model. 2012-05-16 17:22:14 +01:00
Marc Abramowitz 15435047c6 Add myself to AUTHORS 2012-05-15 07:20:04 -07:00
Marc Abramowitz a3781e3c89 Changes for Python 3 compatibility, including vendorizing xlrd3 2012-05-15 07:19:15 -07:00
Marc Abramowitz 6a825a8a39 NOTICE: Add license info for xlrd3 and xlwt3 2012-05-15 07:18:15 -07:00
Marc Abramowitz 6a449d497a Add support for tox 2012-05-14 22:24:36 -07:00
Marc Abramowitz d807c60346 Tweak setup.py for py.test (pytest?) 2012-05-14 17:14:46 -07:00
Jim Dalton 71603662b1 Make sure codecs module loaded for all versions of Python 2012-05-10 11:29:41 -07:00
Jim Dalton 21c11b9911 Fix UnicodeError in HTML output
* Alter `test_unicode_append` so that actual unicode characters outside the ASCII bytestring range are tested.

 * Make sure output of `render` in markup.py is unicode

 * Add wrapper around output of `export_set` in _html.py so that unicode characters are output.
2012-05-10 11:14:17 -07:00
Kenneth Reitz e8c923d712 Merge pull request #58 from jqb/develop
Support for Dataset subclassing
2012-04-20 06:17:05 -07:00
Kenneth Reitz bc581c08df Update NOTICE 2012-04-20 10:16:28 -03:00
Kenneth Reitz 4f9c9d09ec ODFPy license
(which seems to be missing a copyright for some reason)
2012-04-20 10:15:36 -03:00
Kenneth Reitz 63e8a7172d Merge pull request #61 from bmihelac/patch-1
tablib.org domain expired
2012-04-04 05:41:25 -07:00
Bojan Mihelac 45e0af9f0e tablib.org domain expired 2012-04-04 15:35:16 +03:00
Kuba Janoszek fa6f5b3af3 Databook.add_sheet test for not Dataset subclass added. 2012-03-13 00:21:32 +01:00
Kuba Janoszek 0528e0a500 AUTHORS updated 2012-03-13 00:14:51 +01:00
Kuba Janoszek 8e83734985 Databook.add_sheet accepts Dataset subclasses 2012-03-13 00:05:24 +01:00
Kenneth Reitz 783eccc67d skip install 2012-02-23 06:31:50 -05:00
Kenneth Reitz 7236415f42 travis 2012-02-23 06:20:57 -05:00
Kenneth Reitz c0a3c3ea1e travis test 2012-02-23 06:17:01 -05:00
Jan Brauer 14bd964fb1 Fix #50 - Catch YAML ScannerError 2012-01-29 17:18:30 +01:00
Kenneth Reitz 6bfc6634ba index update 2012-01-28 01:23:54 -05:00
mellort 54affad292 ref #48. makes Dataset more like a duck with extend() 2012-01-28 01:17:15 -05:00
Kenneth Reitz 7c963a0f4d SOPA 2012-01-18 11:24:18 -05:00
Kenneth Reitz 02f27f15c5 Merge pull request #47 from VanL/develop
Add detect function in _xls. Update yaml, csv, and tsv detection functio...
2012-01-05 21:37:51 -08:00
VanL 9c65515e7a Add detect function in _xls. Update yaml, csv, and tsv detection functions to catch other errors when faced with invalid input. 2012-01-06 00:12:06 +00:00
Kenneth Reitz c87a954a9e Merge pull request #43 from svetlyak40wt/develop
Render table in Markdown format on unicode(dataset). Closes #41.
2011-12-24 23:05:03 -08:00
Kenneth Reitz 42e40ed0ab use yaml safe_load (thanks @toastdriven) 2011-11-02 02:35:59 -03:00
Alexander Artemenko 23ab6c4724 Render table in Markdown format on unicode(dataset). Closes #41. 2011-10-16 11:00:06 +04:00
Kenneth Reitz 32a09ccd6a Edited AUTHORS via GitHub 2011-08-31 02:16:16 -03:00
Kenneth Reitz 81a7f79b3d Merge pull request #37 from jfriedly/patch-1
Fixed a few typos.
2011-08-30 22:15:49 -07:00
Joel Friedly 05c9b33003 Fixed a few typos. 2011-08-25 23:33:29 -03:00
Kenneth Reitz ec7273d02d that wasn't right. 2011-08-15 23:29:19 -04:00
Kenneth Reitz 19ee1997b5 really need to use testing branches.. 2011-08-15 22:49:14 -04:00
Kenneth Reitz f01d65c2e9 I don't remember merging that.. 2011-08-15 22:45:35 -04:00
Kenneth Reitz 9778a96351 tuples didn't have index method in the past.
…why?
2011-08-15 22:43:12 -04:00
Kenneth Reitz 906138b138 a column w/ no length could work 2011-08-11 00:47:23 -04:00
Mike Waldner 43c68b396f Fixing magic number in test 2011-08-10 20:05:17 -04:00
Mike Waldner d611233c80 Throwing InvalidDimensions when append_col with header is called but only headers exists
Related #33
2011-08-10 19:52:06 -04:00
Kenneth Reitz 3d02b866ce Merge branch 'append_col_docs' of https://github.com/mawaldne/tablib into develop 2011-08-09 21:48:32 -04:00
Mike Waldner 887ee2fbac Adding documentation changes for append_col
Related #21
2011-08-09 20:52:09 -04:00
Kenneth Reitz bfd211854a Added Mike Waldner to Authors.
#34
2011-08-08 06:48:34 -04:00
Kenneth Reitz bc75911500 Merge branch 'html_None_fix' of https://github.com/mawaldne/tablib into develop 2011-08-08 06:47:47 -04:00
Mike Waldner a2b4e4c6ba Replace None with empty string before creating td 2011-08-07 19:19:54 -04:00
Kenneth Reitz fde6f11763 Merge branch 'feature/xls-import' of https://github.com/xdissent/tablib into develop 2011-07-14 15:16:01 -04:00
Kenneth Reitz 33a83316df Merge branch 'fix_pickle_bug_2' of https://github.com/cswegger/tablib into develop 2011-07-14 15:15:42 -04:00
Greg Thornton f6d7888d9e Added xls detection. 2011-07-14 13:47:07 -05:00
Greg Thornton c19e2f2c5b Added xlrd license to NOTICE. 2011-07-14 13:11:33 -05:00
Greg Thornton eaa2b9b8ea Added XLS import support 2011-07-14 13:08:06 -05:00
Luca Beltrame 2f8083bda6 Fix also __slots__ to ensure proper unpickling 2011-07-14 10:28:12 +02:00
Luca Beltrame 2c5a9af76e Fix pickling (again). Unit tests still pass. 2011-07-14 09:36:35 +02:00
Mark Walling e74a8f41cc Created get_col method with tests and tutorial.rst update
Useful when you have multiple columns with the same header
2011-07-11 17:26:21 -04:00
Kenneth Reitz cd5aa4fc06 toxless 2011-07-04 14:36:08 -04:00
Kenneth Reitz 1d460bac40 setup.py changes 2011-07-04 14:27:42 -04:00
Kenneth Reitz 4a3fde37a3 tox cleanups 2011-07-04 14:05:48 -04:00
Kenneth Reitz 62ad123ad8 updated history 2011-07-04 05:49:41 -04:00
Kenneth Reitz fefc7b4d1f Merge branch 'unicodeheaders' of https://github.com/mwalling/tablib into develop 2011-07-04 05:48:37 -04:00
Mark Walling 6313437a27 Added support for detecting unicode column headers
Also added tests!

Fix for kennethreitz#26
2011-07-01 17:53:38 -04:00
Kenneth Reitz 23a5bb1443 yay 2011-06-30 23:00:26 -04:00
Mark Walling 864f29cc4b Updated some docstrings in core.py
* Binary warning for CSV output, because if you don't, Excel gets upset when Python translates \r\n to \r\n\r\n
 * Cleaned up what looked like a couple of copy paste errors
2011-06-30 22:38:57 -04:00
Kenneth Reitz c136b794a7 Merge branch 'develop' 2011-06-30 16:29:10 -04:00
Kenneth Reitz d254c2d2b0 dynamic columns bugfix for @mwalling :) 2011-06-30 16:28:56 -04:00
Kenneth Reitz 9b235150cf v0.9.10 (packaging fix) 2011-06-23 06:46:24 -04:00
Kenneth Reitz 9f3e6eeaa1 oops 2011-06-23 05:37:09 -04:00
Kenneth Reitz 51728f954f Merge codeplane.com:kennethreitz/tablib into develop 2011-06-22 13:34:22 -04:00
Kenneth Reitz 2949b7c656 A change. 2011-06-22 13:27:24 -04:00
Kenneth Reitz 07d243bbc9 testing GitHub for Mac 2011-06-22 13:16:09 -04:00
Kenneth Reitz bf3484e606 release date 2011-06-21 23:04:42 -04:00
Kenneth Reitz 9b2ab6fae9 Merge branch 'release/0.9.9' 2011-06-21 23:01:46 -04:00
Kenneth Reitz 7a3d55daab test cleanups 2011-06-21 22:58:14 -04:00
Kenneth Reitz eec0595c5c new column methods in tutorial 2011-06-21 20:35:18 -04:00
Kenneth Reitz 0c7c248b96 installation updates 2011-06-21 20:32:44 -04:00
Kenneth Reitz 0d14f7f2b9 Jenkins 2011-06-21 20:28:56 -04:00
Kenneth Reitz d5f713024d setup.py fixes 2011-06-21 20:26:05 -04:00
Kenneth Reitz 415bc819e7 __version__ 2011-06-21 20:17:05 -04:00
Kenneth Reitz 974258094e tablib version in docs 2011-06-21 20:15:47 -04:00
Kenneth Reitz ab16f69be6 big history update 2011-06-21 20:08:28 -04:00
Kenneth Reitz 28d9af852a 0.9.9 2011-06-21 20:04:48 -04:00
Kenneth Reitz 39c6ea6503 lpop/rpop 2011-06-21 20:03:50 -04:00
Kenneth Reitz 39b66ad8e9 add row pop 2011-06-21 20:02:12 -04:00
Kenneth Reitz 004b3da680 Major API Changes
Related #21
2011-06-21 19:42:56 -04:00
Kenneth Reitz d4923533eb style fixes 2011-06-21 19:07:24 -04:00
Kenneth Reitz 29e0b76910 bettter setup.py pattern 2011-06-21 19:04:03 -04:00
Kenneth Reitz 4f54de2630 Stick w/ utf-8. Easy enough to modify.
Related: #18.
2011-06-21 19:00:27 -04:00
Kenneth Reitz 1f0d68ee79 utf-8-sig encoding for csv/tsv (for excel).
Fixes #18.
2011-06-21 18:56:44 -04:00
Kenneth Reitz cae8fa1276 ujson 2011-06-21 18:52:01 -04:00
Kenneth Reitz 4c0a20a7b9 staying with MIT License, for now. 2011-06-21 18:51:54 -04:00
Kenneth Reitz 6c1fa87138 tox cleanup 2011-06-21 01:26:16 -04:00
Kenneth Reitz 0e30255836 bugfix 2011-06-20 12:57:24 -04:00
Kenneth Reitz 1156d5a220 NOTICE update 2011-06-20 12:56:39 -04:00
Kenneth Reitz 83b71967b9 integrate omnijson 2011-06-20 12:55:43 -04:00
Kenneth Reitz 4dab48cd76 add omnijson 2011-06-20 12:55:37 -04:00
Kenneth Reitz 5324526329 remove anyjson 2011-06-20 12:55:30 -04:00
Kenneth Reitz 1dfcd42233 whitespace 2011-06-05 18:50:36 -04:00
Kenneth Reitz f162b19bd6 todo cleanup 2011-06-05 18:43:08 -04:00
Kenneth Reitz 707164e459 fixes #17 2011-05-25 12:12:04 -04:00
Kenneth Reitz 42f0a285c3 gaug.es 2011-05-24 18:30:14 -04:00
Kenneth Reitz d111cc7cc7 testimonial cleanup 2011-05-24 17:19:13 -04:00
Kenneth Reitz 25fe211a22 fix setup packages 2011-05-23 11:20:10 -04:00
Kenneth Reitz 4b675494c4 Merge branch 'feature/apache' into develop 2011-05-22 20:10:33 -04:00
Kenneth Reitz a196b9a5dd readme update 2011-05-22 20:10:14 -04:00
Kenneth Reitz 5ba56c2bb3 Turn off OrderedDict for yaml.
Fixes #12.
2011-05-22 19:52:24 -04:00
Kenneth Reitz 36fbdda492 setup.py improvements
closes #5
2011-05-22 19:43:29 -04:00
Kenneth Reitz 273d2729ee Apache v2 2011-05-22 19:36:38 -04:00
Kenneth Reitz 3036bc9e52 abandon 2011-05-22 15:45:34 -04:00
Kenneth Reitz b9c74eacc8 lower case 2011-05-22 15:41:10 -04:00
Kenneth Reitz 805ccfae34 mention formats 2011-05-22 15:39:28 -04:00
Kenneth Reitz fddc018394 datestamp 2011-05-22 15:34:27 -04:00
Kenneth Reitz 2477100062 Merge branch 'release/0.9.8' into develop 2011-05-22 15:34:05 -04:00
Kenneth Reitz 983b979fda Merge branch 'release/0.9.8' 2011-05-22 15:33:49 -04:00
Kenneth Reitz 3edb45bac7 version bump (v0.9.8)! 2011-05-22 15:33:40 -04:00
Kenneth Reitz 29d626fa1f 2.x bytesio fix 2011-05-22 15:29:11 -04:00
Kenneth Reitz 1f22fc7321 BytesIO 2011-05-22 15:22:22 -04:00
Kenneth Reitz 8631f60f8d Python3 ods fix 2011-05-22 15:13:48 -04:00
Kenneth Reitz 65873b6112 BytesIO 2011-05-22 15:13:40 -04:00
Kenneth Reitz 56e44bd45c csv compatibility 2011-05-22 15:06:52 -04:00
Kenneth Reitz 87e65fd3e7 Merge pull request #14 from f4nt/tablib
---

This should provide basic support for OpenDocument spreadsheets. I didnt have py2.7 installed to test with in tox, but 2.5, 2.6, and py3k passed all tests with tox for me. Lemme know if you see any issues I may have glossed over.

Conflicts:
	tablib/compat.py
2011-05-22 14:07:17 -04:00
Kenneth Reitz ffbc3b122d pass 2011-05-22 14:04:47 -04:00
Kenneth Reitz 9d71603dad Installation link 2011-05-19 12:20:17 -04:00
Mark Rogers cceb41af98 ods support 2011-05-18 16:12:42 -05:00
Kenneth Reitz 60ffa898fd removed meh testimonial 2011-05-16 04:15:34 -04:00
Kenneth Reitz a4a211b5a6 theme update 2011-05-16 02:18:03 -04:00
Kenneth Reitz c9766a48b0 docs update 2011-05-16 02:08:37 -04:00
Kenneth Reitz 6975685b89 theme update 2011-05-15 19:53:18 -04:00
Kenneth Reitz e920244a1b testimonials 2011-05-15 17:08:26 -04:00
Kenneth Reitz ea63779baf syntax fix 2011-05-15 13:29:54 -04:00
Kenneth Reitz d826f6d0ae Orgs 2011-05-15 13:28:15 -04:00
Kenneth Reitz f6fa3f2abc change (c) attribution 2011-05-15 13:28:10 -04:00
Mark Rogers eed6df45e0 Bolding still doesn't work :( 2011-05-15 09:00:47 -05:00
Mark Rogers cb4c67767a py3k tests now pass 2011-05-15 08:35:29 -05:00
Mark Rogers 1e21fee70e start of the py3k port of odfpy 2011-05-14 16:44:23 -05:00
Mark Rogers 420dd36ab8 Tidied up a bit, renamed _odf to _ods like it should have been. Bold not working yet :( 2011-05-14 16:13:17 -05:00
Mark Rogers 9a05770899 proof of concept works. Onto styling and tidying 2011-05-14 14:52:16 -05:00
Mark Rogers 8e055f1c57 adding odfpy to packages 2011-05-14 14:32:03 -05:00
Kenneth Reitz 239e33aaed subtle format cleanups 2011-05-14 10:10:02 -04:00
Kenneth Reitz bf4fdea187 fewer 2/3 mappings 2011-05-14 10:06:54 -04:00
Kenneth Reitz 03086052ed Merge pull request #11 from cswegger/tablib
---

This change applies the same unicode CSV fix for TSV files, since all its done in the exporter is changing a few parameters of the CSV module.

All unit tests are still passing after this change.
2011-05-14 10:02:29 -04:00
Kenneth Reitz 2128473938 license/support update 2011-05-13 21:15:09 -04:00
Kenneth Reitz 74c64d66a9 pypy-1.5 2011-05-13 20:51:54 -04:00
Kenneth Reitz a4e77f22c4 .orig? geeze.. 2011-05-13 01:47:16 -04:00
Kenneth Reitz 2e03046a07 docs fix 2011-05-13 01:46:37 -04:00
Kenneth Reitz 06a7b4cd4e new roadmap 2011-05-13 01:42:42 -04:00
Kenneth Reitz 6a70b84166 docs on xlsx 2011-05-13 01:34:24 -04:00
Kenneth Reitz 77d9fe8b41 Merge branch 'develop' 2011-05-13 01:27:44 -04:00
Kenneth Reitz 64cb547e0a missing modules 2011-05-13 01:27:38 -04:00
262 changed files with 6246 additions and 52606 deletions
+10
View File
@@ -0,0 +1,10 @@
# .coveragerc to control coverage.py
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma:
pragma: no cover
# Don't complain if non-runnable code isn't run:
if __name__ == .__main__.:
+10
View File
@@ -0,0 +1,10 @@
[![Jazzband](https://jazzband.co/static/img/jazzband.svg)](https://jazzband.co/)
This is a [Jazzband](https://jazzband.co/) project. By contributing you agree to abide
by the [Contributor Code of Conduct](https://jazzband.co/about/conduct) and follow the
[guidelines](https://jazzband.co/about/guidelines).
If you'd like to contribute, simply fork
[the repository](https://github.com/jazzband/tablib), commit your changes to a feature
branch, and send a pull request to `master`. Make sure you add yourself to
[AUTHORS](https://github.com/jazzband/tablib/blob/master/AUTHORS).
+30
View File
@@ -0,0 +1,30 @@
name: Docs and lint
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8]
env:
- TOXENV: docs
- TOXENV: lint
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade tox
- name: Tox
run: tox
env: ${{ matrix.env }}
+33
View File
@@ -0,0 +1,33 @@
name: Test
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: [3.5, 3.6, 3.7]
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade tox
python -m pip install -e .
- name: Tox tests
shell: bash
# Drop the dot: py3.7-tests -> py37-tests
run: |
tox -e py`echo ${{ matrix.python-version }} | tr -d .`-tests
+16 -1
View File
@@ -22,4 +22,19 @@ coverage.xml
nosetests.xml
junit-py25.xml
junit-py26.xml
junit-py27.xml
junit-py27.xml
# tox noise
.tox
# pyenv noise
.python-version
tablib.egg-info/*
# Coverage
.coverage
htmlcov
# setuptools noise
.eggs
*.egg-info
+25
View File
@@ -0,0 +1,25 @@
repos:
- repo: https://github.com/asottile/pyupgrade
rev: v1.25.2
hooks:
- id: pyupgrade
args: ["--py3-plus"]
- repo: https://github.com/pre-commit/mirrors-isort
rev: v4.3.21
hooks:
- id: isort
additional_dependencies: [toml]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.4.2
hooks:
- id: python-check-blanket-noqa
- id: rst-backticks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.4.0
hooks:
- id: check-merge-conflict
- id: check-toml
- id: check-yaml
+38
View File
@@ -0,0 +1,38 @@
language: python
cache:
pip: true
directories:
- $HOME/.cache/pre-commit
matrix:
fast_finish: true
include:
- python: 3.8
env: TOXENV=docs
- python: 3.8
env: TOXENV=lint
- python: 3.8
- python: 3.7
- python: 3.6
install: travis_retry pip install tox-travis
script: tox
after_success:
- |
if [[ "$TOXENV" != "docs" && "$TOXENV" != "lint" ]]; then
bash <(curl -s https://codecov.io/bash)
fi
deploy:
provider: pypi
user: jazzband
server: https://jazzband.co/projects/tablib/upload
distributions: sdist bdist_wheel
password:
secure: svV4fYtodwW+iTyFOm5ISEfhVwcA+6vTskD3x6peznc40TdMV9Ek8nT3Q/NB4lCbXoUw2qR4H6uhLCjesnv/VvVk/qbitCyD8ySlgwOV5n7NzJs8lC8EYaHSjGQjatTwJAokfGVYkPawkI7HXDqtDggLUQBK+Ag8HDW+XBSbQIU=
on:
tags: true
repo: jazzband/tablib
python: 3.7
+30 -16
View File
@@ -1,18 +1,32 @@
Tablib is written and maintained by Kenneth Reitz and
various contributors:
Tablib was originally written by Kenneth Reitz and is now maintained
by the Jazzband GitHub team.
Development Lead
````````````````
Here is a list of passed and present much-appreciated contributors:
- Kenneth Reitz <_@kennethreitz.com>
Patches and Suggestions
```````````````````````
- Luke Lee
- Josh Ourisman
- Luca Beltrame
- Benjamin Wohlwend
- Erik Youngren
- Mark Rogers
Alex Gaynor
Andrii Soldatenko
Benjamin Wohlwend
Bruno Soares
Claude Paroz
Daniel Santos
Erik Youngren
Hugo van Kemenade
Iuri de Silvio
Jakub Janoszek
James Douglass
Joel Friedly
Josh Ourisman
Kenneth Reitz
Luca Beltrame
Luke Lee
Marc Abramowitz
Marco Dallagiacoma
Mark Rogers
Mark Walling
Mathias Loesch
Mike Waldner
Peyman Salehi
Rabin Nankhwa
Tommy Anthony
Tsuyoshi Hombashi
Tushar Makkar
-14
View File
@@ -1,14 +0,0 @@
Where possible, please follow PEP8 with regard to coding style. Sometimes the line
length restriction is too hard to follow, so don't bend over backwards there.
Triple-quotes should always be """, single quotes are ' unless using "
would result in less escaping within the string.
All modules, functions, and methods should be well documented reStructuredText for
Sphinx AutoDoc.
All functionality should be available in pure Python. Optional C (via Cython)
implementations may be written for performance reasons, but should never
replace the Python implementation.
Lastly, don't take yourself too seriously :)
+294
View File
@@ -0,0 +1,294 @@
# History
## 1.0.0 (2020-01-13)
### Breaking changes
- Dropped Python 2 support
- Dependencies are now all optional. To install `tablib` as before with all
possible supported formats, run `pip install tablib[all]`
### Improvements
- Formats can now be dynamically registered through the
`tablib.formats.registry.refister` API (#256).
- Tablib methods expecting data input (`detect_format`, `import_set`,
`Dataset.load`, `Databook.load`) now accepts file-like objects in addition
to raw strings and bytestrings (#440).
### Bugfixes
- Fixed a crash when exporting an empty string with the ReST format (#368)
- Error cells from imported .xls files contain now the error string (#202)
## 0.14.0 (2019-10-19)
### Deprecations
- The 0.14.x series will be the last to support Python 2
### Breaking changes
- Dropped Python 3.4 support
### Improvements
- Added Python 3.7 and 3.8 support
- The project is now maintained by the Jazzband team, https://jazzband.co
- Improved format autodetection and added autodetection for the odf format.
- Added search to all documentation pages
- Open xlsx workbooks in read-only mode (#316)
- Unpin requirements
- Only install backports.csv on Python 2
### Bugfixes
- Fixed `DataBook().load` parameter ordering (first stream, then format).
- Fixed a regression for xlsx exports where non-string values were forced to
strings (#314)
- Fixed xlsx format detection (which was often detected as `xls` format)
## 0.13.0 (2019-03-08)
- Added reStructuredText output capability (#336)
- Added Jira output capability
- Stopped calling openpyxl deprecated methods (accessing cells, removing sheets)
(openpyxl minimal version is now 2.4.0)
- Fixed a circular dependency issue in JSON output (#332)
- Fixed Unicode error for the CSV export on Python 2 (#215)
- Removed usage of optional `ujson` (#311)
- Dropped Python 3.3 support
## 0.12.1 (2017-09-01)
- Favor `Dataset.export(<format>)` over `Dataset.<format>` syntax in docs
- Make Panda dependency optional
## 0.12.0 (2017-08-27)
- Add initial Panda DataFrame support
- Dropped Python 2.6 support
## 0.11.5 (2017-06-13)
- Use `yaml.safe_load` for importing yaml.
## 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)
- Release fix.
## 0.11.2 (2016-02-16)
### Bugfixes
- Fix export only formats.
- Fix for xlsx output.
## 0.11.1 (2016-02-07)
### Bugfixes
- Fixed packaging error on Python 3.
## 0.11.0 (2016-02-07)
### New Formats!
- Added LaTeX table export format (`Dataset.latex`).
- Support for dBase (DBF) files (`Dataset.dbf`).
### Improvements
- New import/export interface (`Dataset.export()`, `Dataset.load()`).
- CSV custom delimiter support (`Dataset.export('csv', delimiter='$')`).
- Adding ability to remove duplicates to all rows in a dataset (`Dataset.remove_duplicates()`).
- Added a mechanism to avoid `datetime.datetime` issues when serializing data.
- New `detect_format()` function (mostly for internal use).
- Update the vendored unicodecsv to fix `None` handling.
- Only freeze the headers row, not the headers columns (xls).
### Breaking Changes
- `detect()` function removed.
### Bugfixes
- Fix XLSX import.
- Bugfix for `Dataset.transpose().transpose()`.
## 0.10.0 (2014-05-27)
* Unicode Column Headers
* ALL the bugfixes!
## 0.9.11 (2011-06-30)
* Bugfixes
## 0.9.10 (2011-06-22)
* Bugfixes
## 0.9.9 (2011-06-21)
* Dataset API Changes
* `stack_rows` => `stack`, `stack_columns` => `stack_cols`
* column operations have their own methods now (`append_col`, `insert_col`)
* List-style `pop()`
* Redis-style `rpush`, `lpush`, `rpop`, `lpop`, `rpush_col`, and `lpush_col`
## 0.9.8 (2011-05-22)
* OpenDocument Spreadsheet support (.ods)
* Full Unicode TSV support
## 0.9.7 (2011-05-12)
* Full XLSX Support!
* Pickling Bugfix
* Compat Module
## 0.9.6 (2011-05-12)
* `seperators` renamed to `separators`
* Full unicode CSV support
## 0.9.5 (2011-03-24)
* Python 3.1, Python 3.2 Support (same code base!)
* Formatter callback support
* Various bug fixes
## 0.9.4 (2011-02-18)
* Python 2.5 Support!
* Tox Testing for 2.5, 2.6, 2.7
* AnyJSON Integrated
* OrderedDict support
* Caved to community pressure (spaces)
## 0.9.3 (2011-01-31)
* Databook duplication leak fix.
* HTML Table output.
* Added column sorting.
## 0.9.2 (2010-11-17)
* Transpose method added to Datasets.
* New frozen top row in Excel output.
* Pickling support for Datasets and Rows.
* Support for row/column stacking.
## 0.9.1 (2010-11-04)
* Minor reference shadowing bugfix.
## 0.9.0 (2010-11-04)
* Massive documentation update!
* Tablib.org!
* Row tagging and Dataset filtering!
* Column insert/delete support
* Column append API change (header required)
* Internal Changes (Row object and use thereof)
## 0.8.5 (2010-10-06)
* New import system. All dependencies attempt to load from site-packages,
then fallback on tenderized modules.
## 0.8.4 (2010-10-04)
* Updated XLS output: Only wrap if '\\n' in cell.
## 0.8.3 (2010-10-04)
* Ability to append new column passing a callable
as the value that will be applied to every row.
## 0.8.2 (2010-10-04)
* Added alignment wrapping to written cells.
* Added separator support to XLS.
## 0.8.1 (2010-09-28)
* Packaging Fix
## 0.8.0 (2010-09-25)
* New format plugin system!
* Imports! ELEGANT Imports!
* Tests. Lots of tests.
## 0.7.1 (2010-09-20)
* Reverting methods back to properties.
* Windows bug compensated in documentation.
## 0.7.0 (2010-09-20)
* Renamed DataBook Databook for consistency.
* 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.
* Updated Dataset.headers property w/ validation.
* Added Testing Fixtures.
## 0.6.1 (2010-09-12)
* Packaging hotfixes.
## 0.6.0 (2010-09-11)
* Public Release.
* Export Support for XLS, JSON, YAML, and CSV.
* DataBook Export for XLS, JSON, and YAML.
* Python Dict Property Support.
-160
View File
@@ -1,160 +0,0 @@
History
-------
0.9.7 (2011-05-12)
++++++++++++++++++
* Full XLSX Support!
* Pickling Bugfix
* Compat Module
0.9.6 (2011-05-12)
++++++++++++++++++
* ``seperators`` renamed to ``separators``
* Full unicode CSV support
0.9.5 (2011-03-24)
++++++++++++++++++
* Python 3.1, Python 3.2 Support (same code base!)
* Formatter callback support
* Various bug fixes
0.9.4 (2011-02-18)
++++++++++++++++++
* Python 2.5 Support!
* Tox Testing for 2.5, 2.6, 2.7
* AnyJSON Integrated
* OrderedDict support
* Caved to community pressure (spaces)
0.9.3 (2011-01-31)
++++++++++++++++++
* Databook duplication leak fix.
* HTML Table output.
* Added column sorting.
0.9.2 (2010-11-17)
++++++++++++++++++
* Transpose method added to Datasets.
* New frozen top row in Excel output.
* Pickling support for Datasets and Rows.
* Support for row/column stacking.
0.9.1 (2010-11-04)
++++++++++++++++++
* Minor reference shadowing bugfix.
0.9.0 (2010-11-04)
++++++++++++++++++
* Massive documentation update!
* Tablib.org!
* Row tagging and Dataset filtering!
* Column insert/delete support
* Column append API change (header required)
* Internal Changes (Row object and use thereof)
0.8.5 (2010-10-06)
++++++++++++++++++
* New import system. All dependencies attempt to load from site-packages,
then fallback on tenderized modules.
0.8.4 (2010-10-04)
++++++++++++++++++
* Updated XLS output: Only wrap if '\\n' in cell.
0.8.3 (2010-10-04)
++++++++++++++++++
* Ability to append new column passing a callable
as the value that will be applied to every row.
0.8.2 (2010-10-04)
++++++++++++++++++
* Added alignment wrapping to written cells.
* Added separator support to XLS.
0.8.1 (2010-09-28)
++++++++++++++++++
* Packaging Fix
0.8.0 (2010-09-25)
++++++++++++++++++
* New format plugin system!
* Imports! ELEGANT Imports!
* Tests. Lots of tests.
0.7.1 (2010-09-20)
++++++++++++++++++
* Reverting methods back to properties.
* Windows bug compensated in documentation.
0.7.0 (2010-09-20)
++++++++++++++++++
* Renamed DataBook Databook for consistency.
* 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.
* Updated Dataset.headers property w/ validation.
* Added Testing Fixtures.
0.6.1 (2010-09-12)
++++++++++++++++++
* Packaging hotfixes.
0.6.0 (2010-09-11)
++++++++++++++++++
* Public Release.
* Export Support for XLS, JSON, YAML, and CSV.
* DataBook Export for XLS, JSON, and YAML.
* Python Dict Property Support.
+3 -2
View File
@@ -1,4 +1,5 @@
Copyright (c) 2011 Kenneth Reitz.
Copyright 2016 Kenneth Reitz
Copyright 2019 Jazzband
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -16,4 +17,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
THE SOFTWARE.
-1
View File
@@ -1 +0,0 @@
include HISTORY.rst README.rst LICENSE AUTHORS
-209
View File
@@ -1,209 +0,0 @@
Tablib includes some vendorized python libraries: ordereddict, pyyaml,
simplejson, unicodecsv, and xlwt.
Markup License
==============
Markup is in the public domain.
OrderedDict License
===================
Copyright (c) 2009 Raymond Hettinger
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
PyYAML License
==============
Copyright (c) 2006 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
AnyJSON License
==================
This software is licensed under the ``New BSD License``:
Copyright (c) 2009, by the authors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the authors nor the names of its contributors may be used
to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
UnicodeCSV License
==================
Copyright 2010 Jeremy Dunck. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY JEREMY DUNCK ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JEREMY DUNCK OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of Jeremy Dunck.
XLWT License
============
Portions copyright © 2007, Stephen John Machin, Lingfo Pty Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. None of the names of Stephen John Machin, Lingfo Pty Ltd and any
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
"""
"""
Copyright (C) 2005 Roman V. Kiseliov
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. All advertising materials mentioning features or use of this
software must display the following acknowledgment:
"This product includes software developed by
Roman V. Kiseliov <roman@kiseliov.ru>."
4. Redistributions of any form whatsoever must retain the following
acknowledgment:
"This product includes software developed by
Roman V. Kiseliov <roman@kiseliov.ru>."
THIS SOFTWARE IS PROVIDED BY Roman V. Kiseliov ``AS IS'' AND ANY
EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Roman V. Kiseliov OR
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
Roman V. Kiseliov
Russia
Kursk
Libknecht St., 4
+7(0712)56-09-83
<roman@kiseliov.ru>
Subject: pyExcelerator
+45
View File
@@ -0,0 +1,45 @@
# Tablib: format-agnostic tabular dataset library
[![Jazzband](https://jazzband.co/static/img/badge.svg)](https://jazzband.co/)
[![PyPI version](https://img.shields.io/pypi/v/tablib.svg)](https://pypi.org/project/tablib/)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/tablib.svg)](https://pypi.org/project/tablib/)
[![PyPI downloads](https://img.shields.io/pypi/dm/tablib.svg)](https://pypistats.org/packages/tablib)
[![Travus CI status](https://img.shields.io/travis/jazzband/tablib/master?label=Travis%20CI&logo=travis)](https://travis-ci.org/jazzband/tablib)
[![GitHub Actions status](https://github.com/jazzband/tablib/workflows/Test/badge.svg)](https://github.com/jazzband/tablib/actions)
[![codecov](https://codecov.io/gh/jazzband/tablib/branch/master/graph/badge.svg)](https://codecov.io/gh/jazzband/tablib)
[![GitHub](https://img.shields.io/github/license/jazzband/tablib.svg)](LICENSE)
_____ ______ ___________ ______
__ /_______ ____ /_ ___ /___(_)___ /_
_ __/_ __ `/__ __ \__ / __ / __ __ \
/ /_ / /_/ / _ /_/ /_ / _ / _ /_/ /
\__/ \__,_/ /_.___/ /_/ /_/ /_.___/
Tablib is a format-agnostic tabular dataset library, written in Python.
Output formats supported:
- Excel (Sets + Books)
- JSON (Sets + Books)
- YAML (Sets + Books)
- Pandas DataFrames (Sets)
- HTML (Sets)
- Jira (Sets)
- TSV (Sets)
- ODS (Sets)
- CSV (Sets)
- DBF (Sets)
Note that tablib *purposefully* excludes XML support. It always will. (Note: This is a
joke. Pull requests are welcome.)
Tablib documentation is graciously hosted on https://tablib.readthedocs.io
It is also available in the ``docs`` directory of the source distribution.
Make sure to check out [Tablib on PyPI](https://pypi.org/project/tablib/)!
## Contribute
Please see the [contributing guide](https://github.com/jazzband/tablib/blob/master/.github/CONTRIBUTING.md).
-155
View File
@@ -1,155 +0,0 @@
Tablib: format-agnostic tabular dataset library
===============================================
::
_____ ______ ___________ ______
__ /_______ ____ /_ ___ /___(_)___ /_
_ __/_ __ `/__ __ \__ / __ / __ __ \
/ /_ / /_/ / _ /_/ /_ / _ / _ /_/ /
\__/ \__,_/ /_.___/ /_/ /_/ /_.___/
Tablib is a format-agnostic tabular dataset library, written in Python.
Output formats supported:
- Excel (Sets + Books)
- JSON (Sets + Books)
- YAML (Sets + Books)
- HTML (Sets)
- TSV (Sets)
- CSV (Sets)
Note that tablib *purposefully* excludes XML support. It always will. (Note: This is a joke. Pull requests are welcome.)
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 manipulated as raw Python datatypes (Lists of tuples|dictionaries). 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
-----
Populate fresh data files: ::
headers = ('first_name', 'last_name')
data = [
('John', 'Adams'),
('George', 'Washington')
]
data = tablib.Dataset(*data, headers=headers)
Intelligently add new rows: ::
>>> data.append(('Henry', 'Ford'))
Intelligently add new columns: ::
>>> data.append(col=(90, 67, 83), header='age')
Slice rows: ::
>>> print data[:2]
[('John', 'Adams', 90), ('George', 'Washington', 67)]
Slice columns by header: ::
>>> print data['first_name']
['John', 'George', 'Henry']
Easily delete rows: ::
>>> del data[1]
Exports
-------
Drumroll please...........
JSON!
+++++
::
>>> print data.json
[
{
"last_name": "Adams",
"age": 90,
"first_name": "John"
},
{
"last_name": "Ford",
"age": 83,
"first_name": "Henry"
}
]
YAML!
+++++
::
>>> print data.yaml
- {age: 90, first_name: John, last_name: Adams}
- {age: 83, first_name: Henry, last_name: Ford}
CSV...
++++++
::
>>> print data.csv
first_name,last_name,age
John,Adams,90
Henry,Ford,83
EXCEL!
++++++
::
>>> open('people.xls', 'wb').write(data.xls)
It's that easy.
Installation
------------
To install tablib, simply: ::
$ pip install tablib
Or, if you absolutely must: ::
$ easy_install tablib
Contribute
----------
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
-------
v1.0.0:
- Add hooks system
- Tablib.ext namespace
- Better 2.x/3.x handling (currently internal codebase fork)
- Width detection on XLS out
.. _`the repository`: http://github.com/kennethreitz/tablib
.. _AUTHORS: http://github.com/kennethreitz/tablib/blob/master/AUTHORS
+29
View File
@@ -0,0 +1,29 @@
# Release checklist
Jazzband guidelines: https://jazzband.co/about/releases
* [ ] Get master to the appropriate code release state.
[Travis CI](https://travis-ci.org/jazzband/tablib)
should pass on master.
[![Build Status](https://travis-ci.org/jazzband/tablib.svg?branch=master)](https://travis-ci.org/jazzband/tablib)
* [ ] Check [HISTORY.md](https://github.com/jazzband/tablib/blob/master/HISTORY.md),
update version number and release date
* [ ] Tag with version number and push tag, for example:
```bash
git tag -a v0.14.0 -m v0.14.0
git push --tags
```
* [ ] Once Travis CI has built and uploaded distributions, check files at
[Jazzband](https://jazzband.co/projects/tablib) and release to
[PyPI](https://pypi.org/pypi/tablib)
* [ ] Check installation:
```bash
pip uninstall -y tablib && pip install -U tablib
```
* [ ] Create new GitHub release: https://github.com/jazzband/tablib/releases/new
* Tag: Pick existing tag "v0.14.0"
-9
View File
@@ -1,9 +0,0 @@
* Hooks System
- pre/post-append
- pre/post-import
- pre/post-export
* Add Tablib.ext namespace
* Fix 2.x/3.x handling (currently internal codebase fork)
* Make CSV write more customizable.
* Width detection for XLS output
* Documentation Improvements
+11
View File
@@ -0,0 +1,11 @@
<h3><a href="https://tablib.readthedocs.io">About Tablib</a></h3>
<p>
Tablib is an MIT Licensed format-agnostic tabular dataset library, written in Python. It allows you to import, export, and manipulate tabular data sets. Advanced features include, segregation, dynamic columns, tags & filtering, and seamless format import & export.
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://tablib.readthedocs.io">The Tablib Website</a></li>
<li><a href="https://pypi.org/project/tablib">Tablib @ PyPI</a></li>
<li><a href="https://github.com/jazzband/tablib">Tablib @ GitHub</a></li>
<li><a href="https://github.com/jazzband/tablib/issues">Issue Tracker</a></li>
</ul>
+4
View File
@@ -0,0 +1,4 @@
<h3><a href="https://tablib.readthedocs.io">About Tablib</a></h3>
<p>
Tablib is an MIT Licensed format-agnostic tabular dataset library, written in Python. It allows you to import, export, and manipulate tabular data sets. Advanced features include, segregation, dynamic columns, tags & filtering, and seamless format import & export.
</p>
-3
View File
@@ -1,3 +0,0 @@
*.pyc
*.pyo
.DS_Store
-45
View File
@@ -1,45 +0,0 @@
Modifications:
Copyright (c) 2011 Kenneth Reitz.
Original Project:
Copyright (c) 2010 by Armin Ronacher.
Some rights reserved.
Redistribution and use in source and binary forms of the theme, with or
without modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
We kindly ask you to only use these themes in an unmodified manner just
for Flask and Flask-related products, not for unrelated projects. If you
like the visual style and want to use it for your own projects, please
consider making some larger changes to the themes (such as changing
font faces, sizes, colors or margins).
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-25
View File
@@ -1,25 +0,0 @@
krTheme Sphinx Style
====================
This repository contains sphinx styles Kenneth Reitz uses in most of
his projects. It is a drivative of Mitsuhiko's themes for Flask and Flask related
projects. To use this style in your Sphinx documentation, follow
this guide:
1. put this folder as _themes into your docs folder. Alternatively
you can also use git submodules to check out the contents there.
2. add this to your conf.py: ::
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'flask'
The following themes exist:
**kr**
the standard flask documentation theme for large projects
**kr_small**
small one-page theme. Intended to be used by very small addon libraries.
-86
View File
@@ -1,86 +0,0 @@
# flasky extensions. flasky pygments style based on tango style
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
class FlaskyStyle(Style):
background_color = "#f8f8f8"
default_style = ""
styles = {
# No corresponding class for the following:
#Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'
Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'
Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'
Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords
Punctuation: "bold #000000", # class: 'p'
# because special names such as Name.Class, Name.Function, etc.
# are not recognized as such later in the parsing, we choose them
# to look the same as ordinary variables.
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
Number: "#990000", # class: 'm'
Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'
String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'
Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
}
-31
View File
@@ -1,31 +0,0 @@
{%- extends "basic/layout.html" %}
{%- block extrahead %}
{{ super() }}
{% if theme_touch_icon %}
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %}
<link media="only screen and (max-device-width: 480px)" href="{{
pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
{% endblock %}
{%- block relbar2 %}{% endblock %}
{%- block footer %}
<div class="footer">
&copy; Copyright {{ copyright }}.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-8742933-9']);
_gaq.push(['_setDomainName', 'none']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
{%- endblock %}
-19
View File
@@ -1,19 +0,0 @@
<h3>Related Topics</h3>
<ul>
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
{%- for parent in parents %}
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
{%- endfor %}
{%- if prev %}
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
}}">{{ prev.title }}</a></li>
{%- endif %}
{%- if next %}
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
}}">{{ next.title }}</a></li>
{%- endif %}
{%- for parent in parents %}
</ul></li>
{%- endfor %}
</ul></li>
</ul>
-430
View File
@@ -1,430 +0,0 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz.
* :license: Flask Design License, see LICENSE for details.
*/
{% set page_width = '940px' %}
{% set sidebar_width = '220px' %}
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro';
font-size: 17px;
background-color: white;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: {{ page_width }};
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 {{ sidebar_width }};
}
div.sphinxsidebar {
width: {{ sidebar_width }};
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 0 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 14px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0 0 20px 0;
margin: 0;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Garamond', 'Georgia', serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Georgia', serif;
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition tt.xref, div.admonition a tt {
border-bottom: 1px solid #fafafa;
}
dd div.admonition {
margin-left: -60px;
padding-left: 60px;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
background: #fdfdfd;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td.label {
width: 0px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #eee;
padding: 7px 30px;
margin: 15px -30px;
line-height: 1.3em;
}
dl pre, blockquote pre, li pre {
margin-left: -60px;
padding-left: 60px;
}
dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid white;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt {
background: #EEE;
}
@media screen and (max-width: 600px) {
div.sphinxsidebar {
display: none;
}
div.documentwrapper {
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.bodywrapper {
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
ul {
margin-left: 0;
}
.document {
width: auto;
}
.bodywrapper {
margin: 0;
}
.footer {
width: auto;
}
}
-70
View File
@@ -1,70 +0,0 @@
/*
* small_flask.css_t
* ~~~~~~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: white;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: white;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.related {
display: block;
margin: 0;
padding: 10px 0 20px 0;
}
div.related ul,
div.related ul li {
margin: 0;
padding: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
-7
View File
@@ -1,7 +0,0 @@
[theme]
inherit = basic
stylesheet = flasky.css
pygments_style = flask_theme_support.FlaskyStyle
[options]
touch_icon =
-22
View File
@@ -1,22 +0,0 @@
{% extends "basic/layout.html" %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class=indexwrapper>
{% endif %}
{% endblock %}
{% block footer %}
{% if pagename == 'index' %}
</div>
{% endif %}
{% endblock %}
{# do not display relbars #}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}
{% if theme_github_fork %}
<a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
{% endif %}
{% endblock %}
{% block sidebar1 %}{% endblock %}
{% block sidebar2 %}{% endblock %}
-287
View File
@@ -1,287 +0,0 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- flasky theme based on nature theme.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 17px;
color: #000;
background: white;
margin: 0;
padding: 0;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 40px auto 0 auto;
width: 700px;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
text-align: right;
color: #888;
padding: 10px;
font-size: 14px;
width: 650px;
margin: 0 auto 40px auto;
}
div.footer a {
color: #888;
text-decoration: underline;
}
div.related {
line-height: 32px;
color: #888;
}
div.related ul {
padding: 0 0 0 10px;
}
div.related a {
color: #444;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body {
padding-bottom: 40px; /* saved for footer */
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: white;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight{
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.85em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td {
padding: 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
pre {
padding: 0;
margin: 15px -30px;
padding: 8px;
line-height: 1.3em;
padding: 7px 30px;
background: #eee;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
}
dl pre {
margin-left: -60px;
padding-left: 60px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
}
a:hover tt {
background: #EEE;
}
-10
View File
@@ -1,10 +0,0 @@
[theme]
inherit = basic
stylesheet = flasky.css
nosidebar = true
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
github_fork = ''
+2 -2
View File
@@ -36,7 +36,7 @@ Functions
---------
.. autofunction:: detect
.. autofunction:: detect_format
.. autofunction:: import_set
@@ -61,4 +61,4 @@ Exceptions
You're trying to add something that doesn't quite taste right.
Now, go start some :ref:`Tablib Development <development>`.
Now, go start some :ref:`Tablib Development <development>`.
+26 -30
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Tablib documentation build configuration file, created by
# sphinx-quickstart on Tue Oct 5 15:25:21 2010.
@@ -10,22 +9,24 @@
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
from pkg_resources import get_distribution
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
import tablib
# sys.path.insert(0, os.path.abspath('..'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage',
'sphinx.ext.viewcode', 'sphinx.ext.intersphinx'
]
intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -40,17 +41,18 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'Tablib'
copyright = u'2011, Kenneth Reitz. Styles (modified) &copy; Armin Ronacher'
project = 'Tablib'
copyright = '2019 Jazzband'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.9.7'
# The full version, including alpha/beta/rc tags.
release = version
release = get_distribution('tablib').version
# The short X.Y version.
version = '.'.join(release.split('.')[:2])
# for example take major/minor
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -81,7 +83,7 @@ add_function_parentheses = True
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'flask_theme_support.FlaskyStyle'
# pygments_style = ''
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
@@ -91,7 +93,7 @@ pygments_style = 'flask_theme_support.FlaskyStyle'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -120,7 +122,7 @@ html_theme = 'default'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['static']
# html_static_path = ['static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
@@ -131,7 +133,11 @@ html_static_path = ['static']
html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
'sourcelink.html', 'searchbox.html']
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
@@ -178,24 +184,18 @@ htmlhelp_basename = 'Tablibdoc'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Tablib.tex', u'Tablib Documentation',
u'Kenneth Reitz', 'manual'),
('index', 'Tablib.tex', 'Tablib Documentation',
'Jazzband', 'manual'),
]
latex_use_modindex = False
latex_elements = {
'fontpkg': r'\usepackage{mathpazo}',
'papersize': 'a4paper',
'pointsize': '12pt',
'preamble': r'\usepackage{krstyle}'
}
latex_use_parts = True
latex_additional_files = ['krstyle.sty']
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
@@ -225,10 +225,6 @@ latex_additional_files = ['krstyle.sty']
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'tablib', u'Tablib Documentation',
[u'Kenneth Reitz'], 1)
('index', 'tablib', 'Tablib Documentation',
['Jazzband'], 1)
]
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'kr'
+82 -114
View File
@@ -5,14 +5,10 @@ Development
Tablib is under active development, and contributors are welcome.
If you have a feature request, suggestion, or bug report, please open a new issue on GitHub_. To submit patches, please send a pull request on GitHub_.
If you have a feature request, suggestion, or bug report, please open a new
issue on GitHub_. To submit patches, please send a pull request on GitHub_.
If you'd like to contribute, there's plenty to do. Here's a short todo list.
.. include:: ../TODO.rst
.. _GitHub: http://github.com/kennethreitz/tablib/
.. _GitHub: https://github.com/jazzband/tablib/
@@ -42,41 +38,38 @@ Source Control
--------------
Tablib source is controlled with Git_, the lean, mean, distributed source control machine.
Tablib source is controlled with Git_, the lean, mean, distributed source
control machine.
The repository is publicly accessable.
The repository is publicly accessible.
``git clone git://github.com/kennethreitz/tablib.git``
The project is hosted both on **GitHub** and **git.kennethreitz.com**.
GitHub:
http://github.com/kennethreitz/tablib
"Mirror":
http://git.kennethreitz.com/projects/tablib
.. code-block:: console
git clone git://github.com/jazzband/tablib.git
The project is hosted on **GitHub**.
GitHub:
https://github.com/jazzband/tablib
Git Branch Structure
++++++++++++++++++++
Feature / Hotfix / Release branches follow a `Successful Git Branching Model`_ . Git-flow_ is a great tool for managing the repository. I highly recommend it.
Feature / Hotfix / Release branches follow a `Successful Git Branching Model`_ .
Git-flow_ is a great tool for managing the repository. I highly recommend it.
``develop``
The "next release" branch. Likely unstable.
``master``
Current production release (|version|) on PyPi.
``gh-pages``
Current release of http://tablib.org.
Each release is tagged.
When submitting patches, please place your feature/change in its own branch prior to opening a pull reqeust on GitHub_.
When submitting patches, please place your feature/change in its own branch prior to opening a pull request on GitHub_.
.. _Git: http://git-scm.org
.. _`Successful Git Branching Model`: http://nvie.com/posts/a-successful-git-branching-model/
.. _git-flow: http://github.com/nvie/gitflow
.. _Git: https://git-scm.org
.. _`Successful Git Branching Model`: https://nvie.com/posts/a-successful-git-branching-model/
.. _git-flow: https://github.com/nvie/gitflow
.. _newformats:
@@ -87,57 +80,66 @@ Adding New Formats
Tablib welcomes new format additions! Format suggestions include:
* Tab Separated Values
* MySQL Dump
* HTML Table
Coding by Convention
++++++++++++++++++++
Tablib features a micro-framework for adding format support. The easiest way to understand it is to use it. So, let's define our own format, named *xxx*.
Tablib features a micro-framework for adding format support.
The easiest way to understand it is to use it.
So, let's define our own format, named *xxx*.
1. Write a new format interface.
From version 1.0, Tablib formats are class-based and can be dynamically
registered.
:class:`tablib.core` follows a simple pattern for automatically utilizing your format throughout Tablib. Function names are crucial.
Example **tablib/formats/_xxx.py**: ::
1. Write your custom format class::
class MyXXXFormatClass:
title = 'xxx'
def export_set(dset):
@classmethod
def export_set(cls, dset):
....
# returns string representation of given dataset
def export_book(dbook):
@classmethod
def export_book(cls, dbook):
....
# returns string representation of given databook
def import_set(dset, in_stream):
@classmethod
def import_set(cls, dset, in_stream):
...
# populates given Dataset with given datastream
def import_book(dbook, in_stream):
@classmethod
def import_book(cls, dbook, in_stream):
...
# returns Databook instance
def detect(stream):
@classmethod
def detect(cls, stream):
...
# returns True if given stream is parsable as xxx
.. admonition:: Excluding Support
.. admonition:: Excluding Support
If the format excludes support for an import/export mechanism (*e.g.*
:class:`csv <tablib.Dataset.csv>` excludes
:class:`Databook <tablib.Databook>` support),
simply don't define the respective class methods.
Appropriate errors will be raised.
If the format excludes support for an import/export mechanism (*eg.* :class:`csv <tablib.Dataset.csv>` excludes :class:`Databook <tablib.Databook>` support), simply don't define the respective functions. Appropriate errors will be raised.
2. Register your class::
2.
from tablib.formats import registry
Add your new format module to the :class:`tablib.formats.avalable` tuple.
registry.register('xxx', MyXXXFormatClass())
3.
Add a mock property to the :class:`Dataset <tablib.Dataset>` class with verbose `reStructured Text`_ docstring. This alleviates IDE confusion, and allows for pretty auto-generated Sphinx_ documentation.
4. Write respective :ref:`tests <testing>`.
3. From then on, you should be able to use your new custom format as if it were
a built-in Tablib format, e.g. using ``dataset.export('xxx')`` will use the
``MyXXXFormatClass.export_set`` method.
.. _testing:
@@ -145,49 +147,32 @@ Tablib features a micro-framework for adding format support. The easiest way to
Testing Tablib
--------------
Testing is crucial to Tablib's stability. This stable project is used in production by many companies and developers, so it is important to be certain that every version released is fully operational. When developing a new feature for Tablib, be sure to write proper tests for it as well.
Testing is crucial to Tablib's stability.
This stable project is used in production by many companies and developers,
so it is important to be certain that every version released is fully operational.
When developing a new feature for Tablib, be sure to write proper tests for it as well.
When developing a feature for Tablib, the easiest way to test your changes for potential issues is to simply run the test suite directly. ::
When developing a feature for Tablib,
the easiest way to test your changes for potential issues is to simply run the test suite directly.
$ ./test_tablib.py
.. code-block:: console
`Hudson CI`_, amongst other tools, supports Java's xUnit testing report format. Nose_ allows us to generate our own xUnit reports.
Installing nose is simple. ::
$ pip install nose
Once installed, we can generate our xUnit report with a single command. ::
$ nosetests test_tablib.py --with-xunit
This will generate a **nosetests.xml** file, which can then be analyzed.
.. _Nose: http://somethingaboutorange.com/mrl/projects/nose/
.. _hudson:
$ tox
----------------------
Continuous Integration
----------------------
Every commit made to the **develop** branch is automatically tested and inspected upon receipt with `Hudson CI`_. If you have access to the main repository and broke the build, you will receive an email accordingly.
Every pull request is automatically tested and inspected upon receipt with `Travis CI`_.
If you broke the build, you will receive an email accordingly.
Anyone may view the build status and history at any time.
http://ci.kennethreitz.com/
If you are trustworthy and plan to contribute to tablib on a regular basis, please contact `Kenneth Reitz`_ to get an account on the Hudson Server.
https://travis-ci.org/jazzband/tablib
Additional reports will also be included here in the future, including :pep:`8` checks and stress reports for extremely large datasets.
.. _`Hudson CI`: http://hudson.dev.java.net
.. _`Kenneth Reitz`: http://kennethreitz.com/contact-me/
.. _`Travis CI`: https://travis-ci.org/
.. _docs:
@@ -196,51 +181,34 @@ Additional reports will also be included here in the future, including :pep:`8`
Building the Docs
-----------------
Documentation is written in the powerful, flexible, and standard Python documentation format, `reStructured Text`_.
Documentation builds are powered by the powerful Pocoo project, Sphinx_. The :ref:`API Documentation <api>` is mostly documented inline throughout the module.
Documentation is written in the powerful, flexible,
and standard Python documentation format, `reStructured Text`_.
Documentation builds are powered by the powerful Pocoo project, Sphinx_.
The :ref:`API Documentation <api>` is mostly documented inline throughout the module.
The Docs live in ``tablib/docs``. In order to build them, you will first need to install Sphinx. ::
The Docs live in ``tablib/docs``.
In order to build them, you will first need to install Sphinx.
$ pip install sphinx
.. code-block:: console
Then, to build an HTML version of the docs, simply run the following from the **docs** directory: ::
$ make html
Your ``docs/_build/html`` directory will then contain an HTML representation of the documentation, ready for publication on most web servers.
You can also generate the documentation in **ebpub**, **latex**, **json**, *&c* similarly.
.. admonition:: GitHub Pages
To push the documentation up to `GitHub Pages`_, you will first need to run `sphinx-to-github`_ against your ``docs/_build/html`` directory.
GitHub Pages are powered by an HTML generation system called Jeckyl_, which is configured to ignore files and folders that begin with "``_``" (*ie.* **_static**).
$ pip install sphinx
Then, to build an HTML version of the docs, simply run the following from the ``docs`` directory:
.. code-block:: console
$ make html
and `sphinx-to-github`_. ::
Your ``docs/_build/html`` directory will then contain an HTML representation of the documentation,
ready for publication on most web servers.
Installing sphinx-to-github is simple. ::
$ pip install sphinx-to-github
Running it against the docs is even simpler. ::
$ sphinx-to-github _build/html
Move the resulting files to the **gh-pages** branch of your repository, and push it up to GitHub.
You can also generate the documentation in **epub**, **latex**, **json**, *&c* similarly.
.. _`reStructured Text`: http://docutils.sourceforge.net/rst.html
.. _Sphinx: http://sphinx.pocoo.org
.. _`GitHub Pages`: http://pages.github.com
.. _Jeckyl: http://github.com/mojombo/jekyll
.. _`sphinx-to-github`: http://github.com/michaeljones/sphinx-to-github
.. _`GitHub Pages`: https://pages.github.com
----------
Make sure to check out the :ref:`API Documentation <api>`.
Make sure to check out the :ref:`API Documentation <api>`.
+229
View File
@@ -0,0 +1,229 @@
.. _formats:
=======
Formats
=======
Tablib supports a wide variety of different tabular formats, both for input and
output. Moreover, you can :ref:`register your own formats <newformats>`.
cli
===
The ``cli`` format is currently export-only. The exports produce a representation
table suited to a terminal.
When exporting to a CLI you can pass the table format with the ``tablefmt``
parameter, the supported formats are::
>>> import tabulate
>>> list(tabulate._table_formats)
['simple', 'plain', 'grid', 'fancy_grid', 'github', 'pipe', 'orgtbl',
'jira', 'presto', 'psql', 'rst', 'mediawiki', 'moinmoin', 'youtrack',
'html', 'latex', 'latex_raw', 'latex_booktabs', 'tsv', 'textile']
For example::
dataset.export("cli", tablefmt="github")
dataset.export("cli", tablefmt="grid")
This format is optional, install Tablib with ``pip install tablib[cli]`` to
make the format available.
csv
===
When you import CSV data, you can specify if the first line of your data source
is headers with the ``headers`` boolean parameter (defaults to ``True``)::
import tablib
tablib.import_set(your_data_stream, format='csv', headers=False)
When exporting with the ``csv`` format, the top row will contain headers, if
they have been set. Otherwise, the top row will contain the first row of the
dataset.
When importing a CSV data source or exporting a dataset as CSV, you can pass any
parameter supported by the :py:func:`csv.reader` and :py:func:`csv.writer`
functions. For example::
tablib.import_set(your_data_stream, format='csv', dialect='unix')
dataset.export('csv', delimiter=' ', quotechar='|')
.. admonition:: Line endings
Exporting uses \\r\\n line endings by default so, make sure to include
``newline=''`` otherwise you will get a blank line between each row
when you open the file in Excel::
with open('output.csv', 'w', newline='') as f:
f.write(dataset.export('csv'))
If you do not do this, and you export the file on Windows, your
CSV file will open in Excel with a blank line between each row.
dbf
===
Import/export using the dBASE_ format.
.. admonition:: Binary Warning
The ``dbf`` format contains binary data, so make sure to write in binary
mode::
with open('output.dbf', 'wb') as f:
f.write(dataset.export('dbf')
.. _dBASE: https://en.wikipedia.org/wiki/DBase
df (DataFrame)
==============
Import/export using the pandas_ DataFrame format. This format is optional,
install Tablib with ``pip install tablib[pandas]`` to make the format available.
.. _pandas: https://pandas.pydata.org/
html
====
The ``html`` format is currently export-only. The exports produce an HTML page
with the data in a ``<table>``. If headers have been set, they will be used as
table headers.
This format is optional, install Tablib with ``pip install tablib[html]`` to
make the format available.
jira
====
The ``jira`` format is currently export-only. Exports format the dataset
according to the Jira table syntax::
||heading 1||heading 2||heading 3||
|col A1|col A2|col A3|
|col B1|col B2|col B3|
json
====
Import/export using the JSON_ format. If headers have been set, a JSON list of
objects will be returned. If no headers have been set, a JSON list of lists
(rows) will be returned instead.
Import assumes (for now) that headers exist.
.. _JSON: http://json.org/
latex
=====
Import/export using the LaTeX_ format. This format is export-only.
If a title has been set, it will be exported as the table caption.
.. _LaTeX: https://www.latex-project.org/
ods
===
Export data in OpenDocument Spreadsheet format. The ``ods`` format is currently
export-only.
This format is optional, install Tablib with ``pip install tablib[ods]`` to
make the format available.
.. admonition:: Binary Warning
:class:`Dataset.ods` contains binary data, so make sure to write in binary mode::
with open('output.ods', 'wb') as f:
f.write(data.ods)
rst
===
Export data as a reStructuredText_ table representation of a dataset. The
``rst`` format is export-only.
Exporting returns a simple table if the text in the first column is never
wrapped, otherwise returns a grid table::
>>> from tablib import Dataset
>>> bits = ((0, 0), (1, 0), (0, 1), (1, 1))
>>> data = Dataset()
>>> data.headers = ['A', 'B', 'A and B']
>>> for a, b in bits:
... data.append([bool(a), bool(b), bool(a * b)])
>>> table = data.export('rst')
>>> table.split('\\n') == [
... '===== ===== =====',
... ' A B A and',
... ' B ',
... '===== ===== =====',
... 'False False False',
... 'True False False',
... 'False True False',
... 'True True True ',
... '===== ===== =====',
... ]
True
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
tsv
===
A variant of the csv_ format with tabulators as fields separators.
xls
===
Import/export data in Legacy Excel Spreadsheet representation.
This format is optional, install Tablib with ``pip install tablib[xls]`` to
make the format available.
.. note::
XLS files are limited to a maximum of 65,000 rows. Use xlsx_ to avoid this
limitation.
.. admonition:: Binary Warning
The ``xls`` file format is binary, so make sure to write in binary mode::
with open('output.xls', 'wb') as f:
f.write(data.export('xls'))
xlsx
====
Import/export data in Excel 07+ Spreadsheet representation.
This format is optional, install Tablib with ``pip install tablib[xlsx]`` to
make the format available.
.. admonition:: Binary Warning
The ``xlsx`` file format is binary, so make sure to write in binary mode::
with open('output.xlsx', 'wb') as f:
f.write(data.export('xlsx'))
yaml
====
Import/export data in the YAML_ format.
When exporting, if headers have been set, a YAML list of objects will be
returned. If no headers have been set, a YAML list of lists (rows) will be
returned instead.
Import assumes (for now) that headers exist.
This format is optional, install Tablib with ``pip install tablib[yaml]`` to
make the format available.
.. _YAML: https://yaml.org
+66 -9
View File
@@ -1,30 +1,82 @@
.. Tablib documentation master file, created by
sphinx-quickstart on Tue Oct 5 15:25:21 2010.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
contain the root ``toctree`` directive.
Tablib: Pythonic Tabular Datasets
Tablib: Pythonic Tabular Datasets
=================================
Release |version|.
Release v\ |version|. (:ref:`Installation <install>`)
.. Contents:
..
..
.. .. toctree::
.. :maxdepth: 2
..
..
.. Indices and tables
.. ==================
..
..
.. * :ref:`genindex`
.. * :ref:`modindex`
.. * :ref:`search`
Tablib is an :ref:`MIT Licensed <mit>` format-agnostic tabular dataset library, written in Python. It allows you to import, export, and manipulate tabular data sets. Advanced features include, segregation, dynamic columns, tags & filtering, and seamless format import & export.
Tablib is an `MIT Licensed <https://mit-license.org/>`_ format-agnostic tabular dataset library, written in Python.
It allows you to import, export, and manipulate tabular data sets.
Advanced features include segregation, dynamic columns, tags & filtering,
and seamless format import & export.
::
>>> data = tablib.Dataset(headers=['First Name', 'Last Name', 'Age'])
>>> for i in [('Kenneth', 'Reitz', 22), ('Bessie', 'Monke', 21)]:
... data.append(i)
>>> print(data.export('json'))
[{"Last Name": "Reitz", "First Name": "Kenneth", "Age": 22}, {"Last Name": "Monke", "First Name": "Bessie", "Age": 21}]
>>> print(data.export('yaml'))
- {Age: 22, First Name: Kenneth, Last Name: Reitz}
- {Age: 21, First Name: Bessie, Last Name: Monke}
>>> data.export('xlsx')
<redacted binary data>
>>> data.export('df')
First Name Last Name Age
0 Kenneth Reitz 22
1 Bessie Monke 21
Testimonials
------------
`National Geographic <https://www.nationalgeographic.com/>`_,
`Digg, Inc <https://digg.com/>`_,
`Northrop Grumman <https://www.northropgrumman.com/>`_,
`Discovery Channel <https://dsc.discovery.com/>`_,
and `The Sunlight Foundation <https://sunlightfoundation.com/>`_ use Tablib internally.
**Greg Thorton**
Tablib by @kennethreitz saved my life.
I had to consolidate like 5 huge poorly maintained lists of domains and data.
It was a breeze!
**Dave Coutts**
It's turning into one of my most used modules of 2010.
You really hit a sweet spot for managing tabular data with a minimal amount of code and effort.
**Joshua Ourisman**
Tablib has made it so much easier to deal with the inevitable 'I want an Excel file!' requests from clients...
**Brad Montgomery**
I think you nailed the "Python Zen" with tablib.
Thanks again for an awesome lib!
I recommend you start with :ref:`Installation <install>`.
User's Guide
------------
@@ -46,6 +98,11 @@ This part of the documentation, which is mostly prose, begins with some backgrou
tutorial
.. toctree::
:maxdepth: 2
formats
.. toctree::
:maxdepth: 2
@@ -61,4 +118,4 @@ method, this part of the documentation is for you.
.. toctree::
:maxdepth: 2
api
api
+42 -35
View File
@@ -1,8 +1,9 @@
.. _install:
Installation
============
This part of the documentation covers the installation of Tablib. The first step to using any software package is getting it properly installed. Please read this section carefully, or you may miss out on some nice :ref:`speed enhancements <peed-extentions>`.
This part of the documentation covers the installation of Tablib. The first step to using any software package is getting it properly installed.
.. _installing:
@@ -11,68 +12,74 @@ This part of the documentation covers the installation of Tablib. The first step
Installing Tablib
-----------------
To install Tablib, it only takes one simple command. ::
Distribute & Pip
----------------
$ pip install tablib
Of course, the recommended way to install Tablib is with `pip <https://pip.pypa.io>`_:
Or, if you must: ::
.. code-block:: console
$ easy_install tablib
But, you really shouldn't do that.
$ pip install tablib
You can also choose to install more dependencies to have more import/export
formats available:
.. code-block:: console
$ pip install tablib[xlsx]
Or all possible formats:
.. code-block:: console
$ pip install tablib[all]
which is equivalent to:
.. code-block:: console
$ pip install tablib[html, pandas, ods, xls, xlsx, yaml]
-------------------
Download the Source
-------------------
You can also install tablib from source. The latest release (|version|) is available from GitHub.
You can also install Tablib from source.
The latest release (|version|) is available from GitHub.
* tarball_
* zipball_
.. _
Once you have a copy of the source, you can embed it in your Python package, or install it into your site-packages easily. ::
Once you have a copy of the source,
you can embed it in your Python package,
or install it into your site-packages easily.
.. code-block:: console
$ python setup.py install
To download the full source history from Git, see :ref:`Source Control <scm>`.
.. _tarball: http://github.com/kennethreitz/tablib/tarball/master
.. _zipball: http://github.com/kennethreitz/tablib/zipball/master
.. _speed-extentions:
Speed Extentions
----------------
.. versionadded:: 0.8.5
Tablib is partially dependent on the **pyyaml**, **simplejson**, and **xlwt** modules. To reduce installation issues, fully integrated versions of all required libraries are included in Tablib.
However, if performance is important to you (and it should be), you can install **pyyaml** with C extentions from PyPi. ::
$ pip install PyYAML
If you're using Python 2.5, you should also install the **simplejson** module (pip will do this for you). If you're using Python 2.6+, the built-in **json** module is already optimized and in use. ::
$ pip install simplejson
.. _tarball: https://github.com/jazzband/tablib/tarball/master
.. _zipball: https://github.com/jazzband/tablib/zipball/master
.. _updates:
Staying Updated
---------------
The latest version of Tablib will always be available here:
The latest version of Tablib will always be available here:
* PyPi: http://pypi.python.org/pypi/tablib/
* GitHub: http://github.com/kennethreitz/tablib/
* PyPI: https://pypi.org/project/tablib/
* GitHub: https://github.com/jazzband/tablib/
When a new version is available, upgrading is simple. ::
When a new version is available, upgrading is simple::
$ pip install tablib --upgrade
$ pip install tablib --upgrade
Now, go get a :ref:`Quick Start <quickstart>`.
Now, go get a :ref:`Quick Start <quickstart>`.
+13 -34
View File
@@ -3,16 +3,18 @@
Introduction
============
This part of the documentation covers all the interfaces of Tablib.
Tablib is a format-agnostic tabular dataset library, written in Python. It allows you to Pythonically import, export, and manipulate tabular data sets. Advanced features include, segregation, dynamic columns, tags / filtering, and seamless format import/export.
This part of the documentation covers all the interfaces of Tablib.
Tablib is a format-agnostic tabular dataset library, written in Python.
It allows you to Pythonically import, export, and manipulate tabular data sets.
Advanced features include segregation, dynamic columns, tags/filtering, and
seamless format import/export.
Philosphy
---------
Philosophy
----------
Tablib was developed with a few :pep:`20` idioms in mind.
#. Beautiful is better than ugly.
#. Explicit is better than implicit.
#. Simple is better than complex.
@@ -21,27 +23,14 @@ Tablib was developed with a few :pep:`20` idioms in mind.
All contributions to Tablib should keep these important rules in mind.
.. _mit:
MIT License
-----------
A large number of open source projects you find today are `GPL Licensed`_. While the GPL has its time and place, it should most certainly not be your go-to license for your next open source project.
A project that is released as GPL cannot be used in any commercial product without the product itself also being offered as open source. The MIT and BSD licenses are great alternatives to the GPL that allow your open-source software to be used in proprietary, closed-source software.
Tablib is released under terms of `The MIT License`_.
.. _`GPL Licensed`: http://www.opensource.org/licenses/gpl-license.php
.. _`The MIT License`: http://www.opensource.org/licenses/mit-license.php
.. _license:
Tablib License
--------------
Copyright (c) 2011 Kenneth Reitz.
Tablib is released under terms of `The MIT License`_.
Copyright 2017 Kenneth Reitz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -61,24 +50,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
.. _`The MIT License`: https://opensource.org/licenses/mit-license.php
.. _pythonsupport:
Pythons Supported
-----------------
At this time, the following Python platforms are officially supported:
Python 3.5+ is officially supported.
* cPython 2.5
* cPython 2.6
* cPython 2.7
* cPython 3.1
* cPython 3.2
* PyPy-c 1.4
Now, go :ref:`install Tablib <install>`.
Support for other Pythons will be rolled out soon.
Now, go :ref:`Install Tablib <install>`.
+107 -45
View File
@@ -5,10 +5,10 @@ Quickstart
==========
.. module:: tablib
Eager to get started? This page gives a good introduction in how to get started with Tablib. This assumes you already have Tablib installed. If you do not, head over to the :ref:`Installation <install>` section.
Eager to get started?
This page gives a good introduction in how to get started with Tablib.
This assumes you already have Tablib installed.
If you do not, head over to the :ref:`Installation <install>` section.
First, make sure that:
@@ -16,7 +16,7 @@ First, make sure that:
* Tablib is :ref:`up-to-date <updates>`
Lets gets started with some simple use cases and examples.
Let's get started with some simple use cases and examples.
@@ -35,7 +35,8 @@ You can now start filling this :class:`Dataset <tablib.Dataset>` object with dat
.. admonition:: Example Context
From here on out, if you see ``data``, assume that it's a fresh :class:`Dataset <tablib.Dataset>` object.
From here on out, if you see ``data``, assume that it's a fresh
:class:`Dataset <tablib.Dataset>` object.
@@ -56,7 +57,7 @@ Let's say you want to collect a simple list of names. ::
# add names to Dataset
data.append([fname, lname])
You can get a nice, Pythonic view of the dataset at any time with :class:`Dataset.dict`.
You can get a nice, Pythonic view of the dataset at any time with :class:`Dataset.dict`::
>>> data.dict
[('Kenneth', 'Reitz'), ('Bessie', 'Monke')]
@@ -68,14 +69,16 @@ Adding Headers
--------------
It's time enhance our :class:`Dataset` by giving our columns some titles. To do so, set :class:`Dataset.headers`. ::
It's time to enhance our :class:`Dataset` by giving our columns some titles.
To do so, set :class:`Dataset.headers`. ::
data.headers = ['First Name', 'Last Name']
Now our data looks a little different. ::
>>> data.dict
[{'Last Name': 'Reitz', 'First Name': 'Kenneth'}, {'Last Name': 'Monke', 'First Name': 'Bessie'}]
[{'Last Name': 'Reitz', 'First Name': 'Kenneth'},
{'Last Name': 'Monke', 'First Name': 'Bessie'}]
@@ -87,16 +90,34 @@ Adding Columns
Now that we have a basic :class:`Dataset` in place, let's add a column of **ages** to it. ::
data.append(col=[22, 20], header='Age')
data.append_col([22, 20], header='Age')
Let's view the data now. ::
>>> data.dict
[{'Last Name': 'Reitz', 'First Name': 'Kenneth', 'Age': 22}, {'Last Name': 'Monke', 'First Name': 'Bessie', 'Age': 20}]
[{'Last Name': 'Reitz', 'First Name': 'Kenneth', 'Age': 22},
{'Last Name': 'Monke', 'First Name': 'Bessie', 'Age': 20}]
It's that easy.
--------------
Importing Data
--------------
Creating a :class:`tablib.Dataset` object by importing a pre-existing file is simple. ::
with open('data.csv', 'r') as fh:
imported_data = Dataset().load(fh)
This detects what sort of data is being passed in, and uses an appropriate formatter to do the import. So you can import from a variety of different file types.
.. admonition:: Source without headers
When the format is :class:`csv <Dataset.csv>`, :class:`tsv <Dataset.tsv>`, :class:`dbf <Dataset.dbf>`, :class:`xls <Dataset.xls>` or :class:`xlsx <Dataset.xlsx>`, and the data source does not have headers, the import should be done as follows ::
with open('data.csv', 'r') as fh:
imported_data = Dataset().load(fh, headers=False)
--------------
Exporting Data
--------------
@@ -105,28 +126,36 @@ Tablib's killer feature is the ability to export your :class:`Dataset` objects i
**Comma-Separated Values** ::
>>> data.csv
>>> data.export('csv')
Last Name,First Name,Age
Reitz,Kenneth,22
Monke,Bessie,20
**JavaScript Object Notation** ::
>>> data.json
>>> data.export('json')
[{"Last Name": "Reitz", "First Name": "Kenneth", "Age": 22}, {"Last Name": "Monke", "First Name": "Bessie", "Age": 20}]
**YAML Ain't Markup Language** ::
>>> data.yaml
>>> data.export('yaml')
- {Age: 22, First Name: Kenneth, Last Name: Reitz}
- {Age: 20, First Name: Bessie, Last Name: Monke}
**Microsoft Excel** ::
>>> data.xls
<censored binary data>
>>> data.export('xls')
<redacted binary data>
**Pandas DataFrame** ::
>>> data.export('df')
First Name Last Name Age
0 Kenneth Reitz 22
1 Bessie Monke 21
------------------------
@@ -140,12 +169,20 @@ You can slice and dice your data, just like a standard Python list. ::
('Kenneth', 'Reitz', 22)
If we had a set of data consisting of thousands of rows, it could be useful to get a list of values in a column.
If we had a set of data consisting of thousands of rows,
it could be useful to get a list of values in a column.
To do so, we access the :class:`Dataset` as if it were a standard Python dictionary. ::
>>> data['First Name']
['Kenneth', 'Bessie']
You can also access the column using its index. ::
>>> data.headers
['Last Name', 'First Name', 'Age']
>>> data.get_col(1)
['Kenneth', 'Bessie']
Let's find the average age. ::
>>> ages = data['Age']
@@ -158,11 +195,11 @@ Let's find the average age. ::
Removing Rows & Columns
-----------------------
It's easier than you could imagine. ::
It's easier than you could imagine. Delete a column::
>>> del data['Col Name']
::
Delete a range of rows::
>>> del data[0:12]
@@ -171,8 +208,7 @@ It's easier than you could imagine. ::
Advanced Usage
==============
This part of the documentation services to give you an idea that are otherwise hard to extract from the :ref:`API Documentation <api>`
This part of the documentation services to give you an idea that are otherwise hard to extract from the :ref:`API Documentation <api>`.
And now for something completely different.
@@ -185,9 +221,11 @@ Dynamic Columns
.. versionadded:: 0.8.3
Thanks to Josh Ourisman, Tablib now supports adding dynamic columns. A dynamic column is a single callable object (*ie.* a function).
Thanks to Josh Ourisman, Tablib now supports adding dynamic columns.
A dynamic column is a single callable object (*e.g.* a function).
Let's add a dynamic column to our :class:`Dataset` object. In this example, we have a function that generates a random grade for our students. ::
Let's add a dynamic column to our :class:`Dataset` object.
In this example, we have a function that generates a random grade for our students. ::
import random
@@ -195,11 +233,11 @@ Let's add a dynamic column to our :class:`Dataset` object. In this example, we h
"""Returns a random integer for entry."""
return (random.randint(60,100)/100.0)
data.append(col=[random_grade], header='Grade')
data.append_col(random_grade, header='Grade')
Let's have a look at our data. ::
>>> data.yaml
>>> data.export('yaml')
- {Age: 22, First Name: Kenneth, Grade: 0.6, Last Name: Reitz}
- {Age: 20, First Name: Bessie, Grade: 0.75, Last Name: Monke}
@@ -209,7 +247,8 @@ Let's remove that column. ::
>>> del data['Grade']
When you add a dynamic column, the first argument that is passed in to the given callable is the current data row. You can use this to perform calculations against your data row.
When you add a dynamic column, the first argument that is passed in to the given callable is the current data row.
You can use this to perform calculations against your data row.
For example, we can use the data available in the row to guess the gender of a student. ::
@@ -229,7 +268,7 @@ For example, we can use the data available in the row to guess the gender of a s
Adding this function to our dataset as a dynamic column would result in: ::
>>> data.yaml
>>> data.export('yaml')
- {Age: 22, First Name: Kenneth, Gender: Male, Last Name: Reitz}
- {Age: 20, First Name: Bessie, Gender: Female, Last Name: Monke}
@@ -243,9 +282,11 @@ Filtering Datasets with Tags
.. versionadded:: 0.9.0
When constructing a :class:`Dataset` object, you can add tags to rows by specifying the ``tags`` parameter.
This allows you to filter your :class:`Dataset` later. This can be useful so separate rows of data based on
arbitrary criteria (*e.g.* origin) that you don't want to include in your :class:`Dataset`.
When constructing a :class:`Dataset` object,
you can add tags to rows by specifying the ``tags`` parameter.
This allows you to filter your :class:`Dataset` later.
This can be useful to separate rows of data based on arbitrary criteria
(*e.g.* origin) that you don't want to include in your :class:`Dataset`.
Let's tag some students. ::
@@ -253,34 +294,56 @@ Let's tag some students. ::
students.headers = ['first', 'last']
students.append(['Kenneth', 'Reitz'], tags=['male', 'technical'])
students.append(['Bessie', 'Monke'], tags=['female', 'creative'])
students.rpush(['Kenneth', 'Reitz'], tags=['male', 'technical'])
students.rpush(['Daniel', 'Dupont'], tags=['male', 'creative' ])
students.rpush(['Bessie', 'Monke'], tags=['female', 'creative'])
Now that we have extra meta-data on our rows, we can use easily filter our :class:`Dataset`. Let's just see Male students. ::
Now that we have extra meta-data on our rows, we can easily filter our :class:`Dataset`. Let's just see Female students. ::
>>> students.filter(['female']).yaml
- {first: Bessie, Last: Monke}
>>> data.filter(['male']).yaml
- {first: Kenneth, Last: Reitz}
By default, when you pass a list of tags you get filter type or. ::
>>> students.filter(['female', 'creative']).yaml
- {first: Daniel, Last: Dupont}
- {first: Bessie, Last: Monke}
Using chaining you can get a filter type and. ::
>>> students.filter(['female']).filter(['creative']).yaml
- {first: Bessie, Last: Monke}
It's that simple. The original :class:`Dataset` is untouched.
Open an Excel Workbook and read first sheet
-------------------------------------------
Open an Excel 2007 and later workbook with a single sheet (or a workbook with multiple sheets but you just want the first sheet). ::
data = tablib.Dataset()
with open('my_excel_file.xlsx', 'rb') as fh:
data.load(fh, 'xlsx')
print(data)
Excel Workbook With Multiple Sheets
------------------------------------
When dealing with a large number of :class:`Datasets <Dataset>` in spreadsheet format, it's quite common to group multiple spreadsheets into a single Excel file, known as a Workbook. Tablib makes it extremely easy to build workbooks with the handy, :class:`Databook` class.
When dealing with a large number of :class:`Datasets <Dataset>` in spreadsheet format,
it's quite common to group multiple spreadsheets into a single Excel file, known as a Workbook.
Tablib makes it extremely easy to build workbooks with the handy :class:`Databook` class.
Let's say we have 3 different :class:`Datasets <Dataset>`.
All we have to do is add them to a :class:`Databook` object... ::
Let's say we have 3 different :class:`Datasets <Dataset>`. All we have to do is add then to a :class:`Databook` object... ::
book = tablib.Databook([data1, data2, data3])
book = tablib.Databook((data1, data2, data3))
... and export to Excel just like :class:`Datasets <Dataset>`. ::
with open('students.xls', 'wb') as f:
f.write(book.xls)
f.write(book.export('xls'))
The resulting **students.xls** file will contain a separate spreadsheet for each :class:`Dataset` object in the :class:`Databook`.
The resulting ``students.xls`` file will contain a separate spreadsheet for each :class:`Dataset` object in the :class:`Databook`.
.. admonition:: Binary Warning
@@ -295,9 +358,8 @@ Separators
.. versionadded:: 0.8.2
When, it's often useful to create a blank row containing information on the upcoming data. So,
When constructing a spreadsheet,
it's often useful to create a blank row containing information on the upcoming data. So,
::
@@ -329,7 +391,7 @@ When, it's often useful to create a blank row containing information on the upco
# Write spreadsheet to disk
with open('grades.xls', 'wb') as f:
f.write(tests.xls)
f.write(tests.export('xls'))
The resulting **tests.xls** will have the following layout:
+7
View File
@@ -0,0 +1,7 @@
[tool.isort]
force_grid_wrap = 0
include_trailing_comma = true
known_third_party = ["MarkupPy", "odf", "openpyxl", "pkg_resources", "setuptools", "tablib", "xlrd", "xlwt", "yaml"]
line_length = 88
multi_line_output = 3
use_parentheses = true
+4
View File
@@ -0,0 +1,4 @@
[pytest]
norecursedirs = .git .*
addopts = -rsxX --showlocals --tb=native --cov=tablib --cov=tests --cov-report xml --cov-report term --cov-report html
python_paths = .
Regular → Executable
+36 -41
View File
@@ -1,55 +1,50 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
from distutils.core import setup
def publish():
"""Publish to PyPi"""
os.system("python setup.py sdist upload")
if sys.argv[-1] == "publish":
publish()
sys.exit()
required = []
if sys.version_info[:2] < (2,6):
required.append('simplejson')
from setuptools import find_packages, setup
setup(
name='tablib',
version='0.9.7',
use_scm_version=True,
setup_requires=['setuptools_scm'],
description='Format agnostic tabular data library (XLS, JSON, YAML, CSV)',
long_description=open('README.rst').read() + '\n\n' +
open('HISTORY.rst').read(),
long_description=(
open('README.md').read() + '\n\n' + open('HISTORY.md').read()
),
long_description_content_type="text/markdown",
author='Kenneth Reitz',
author_email='me@kennethreitz.com',
url='http://tablib.org',
packages= [
'tablib', 'tablib.formats',
'tablib.packages',
'tablib.packages.xlwt',
'tablib.packages.openpyxl',
'tablib.packages.yaml',
'tablib.packages.unicodecsv'
],
install_requires=required,
author_email='me@kennethreitz.org',
maintainer='Jazzband',
maintainer_email='roadies@jazzband.co',
url='https://tablib.readthedocs.io',
project_urls={
"Documentation": "https://tablib.readthedocs.io",
"Source": "https://github.com/jazzband/tablib",
},
packages=find_packages(where="src"),
package_dir={"": "src"},
license='MIT',
classifiers=(
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Natural Language :: English',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.0',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
),
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
],
python_requires='>=3.5',
extras_require={
'all': ['markuppy', 'odfpy', 'openpyxl>=2.4.0', 'pandas', 'pyyaml', 'tabulate', 'xlrd', 'xlwt'],
'cli': ['tabulate'],
'html': ['markuppy'],
'ods': ['odfpy'],
'pandas': ['pandas'],
'xls': ['xlrd', 'xlwt'],
'xlsx': ['openpyxl>=2.4.0'],
'yaml': ['pyyaml'],
},
)
+18
View File
@@ -0,0 +1,18 @@
""" Tablib. """
from pkg_resources import DistributionNotFound, get_distribution
from tablib.core import ( # noqa: F401
Databook,
Dataset,
InvalidDatasetType,
InvalidDimensions,
UnsupportedFormat,
detect_format,
import_book,
import_set,
)
try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
# package is not installed
__version__ = None
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
class InvalidDatasetType(Exception):
"Only Datasets can be added to a DataBook"
class InvalidDimensions(Exception):
"Invalid size"
class InvalidDatasetIndex(Exception):
"Outside of Dataset size"
class HeadersNeeded(Exception):
"Header parameter must be given when appending a column in this Dataset."
class UnsupportedFormat(NotImplementedError):
"Format is not supported"
+135
View File
@@ -0,0 +1,135 @@
""" Tablib - formats
"""
from collections import OrderedDict
from functools import partialmethod
from importlib import import_module
from importlib.util import find_spec
from tablib.exceptions import UnsupportedFormat
from tablib.utils import normalize_input
from ._csv import CSVFormat
from ._json import JSONFormat
from ._tsv import TSVFormat
uninstalled_format_messages = {
"cli": {"package_name": "tabulate package", "extras_name": "cli"},
"df": {"package_name": "pandas package", "extras_name": "pandas"},
"html": {"package_name": "MarkupPy package", "extras_name": "html"},
"ods": {"package_name": "odfpy package", "extras_name": "ods"},
"xls": {"package_name": "odfpy and xlwt packages", "extras_name": "ods"},
"xlsx": {"package_name": "openpyxl package", "extras_name": "xlsx"},
"yaml": {"package_name": "pyyaml package", "extras_name": "yaml"},
}
def load_format_class(dotted_path):
try:
module_path, class_name = dotted_path.rsplit('.', 1)
return getattr(import_module(module_path), class_name)
except (ValueError, AttributeError) as err:
raise ImportError("Unable to load format class '{}' ({})".format(dotted_path, err))
class FormatDescriptorBase:
def __init__(self, key, format_or_path):
self.key = key
self._format_path = None
if isinstance(format_or_path, str):
self._format = None
self._format_path = format_or_path
else:
self._format = format_or_path
def ensure_format_loaded(self):
if self._format is None:
self._format = load_format_class(self._format_path)
class ImportExportBookDescriptor(FormatDescriptorBase):
def __get__(self, obj, cls, **kwargs):
self.ensure_format_loaded()
return self._format.export_book(obj, **kwargs)
def __set__(self, obj, val):
self.ensure_format_loaded()
return self._format.import_book(obj, normalize_input(val))
class ImportExportSetDescriptor(FormatDescriptorBase):
def __get__(self, obj, cls, **kwargs):
self.ensure_format_loaded()
return self._format.export_set(obj, **kwargs)
def __set__(self, obj, val):
self.ensure_format_loaded()
return self._format.import_set(obj, normalize_input(val))
class Registry:
_formats = OrderedDict()
def register(self, key, format_or_path):
from tablib.core import Databook, Dataset
# Create Databook.<format> read or read/write properties
setattr(Databook, key, ImportExportBookDescriptor(key, format_or_path))
# Create Dataset.<format> read or read/write properties,
# and Dataset.get_<format>/set_<format> methods.
setattr(Dataset, key, ImportExportSetDescriptor(key, format_or_path))
try:
setattr(Dataset, 'get_%s' % key, partialmethod(Dataset._get_in_format, key))
setattr(Dataset, 'set_%s' % key, partialmethod(Dataset._set_in_format, key))
except AttributeError:
setattr(Dataset, 'get_%s' % key, partialmethod(Dataset._get_in_format, key))
self._formats[key] = format_or_path
def register_builtins(self):
# Registration ordering matters for autodetection.
self.register('json', JSONFormat())
# xlsx before as xls (xlrd) can also read xlsx
if find_spec('openpyxl'):
self.register('xlsx', 'tablib.formats._xlsx.XLSXFormat')
if find_spec('xlrd') and find_spec('xlwt'):
self.register('xls', 'tablib.formats._xls.XLSFormat')
if find_spec('yaml'):
self.register('yaml', 'tablib.formats._yaml.YAMLFormat')
self.register('csv', CSVFormat())
self.register('tsv', TSVFormat())
if find_spec('odf'):
self.register('ods', 'tablib.formats._ods.ODSFormat')
self.register('dbf', 'tablib.formats._dbf.DBFFormat')
if find_spec('MarkupPy'):
self.register('html', 'tablib.formats._html.HTMLFormat')
self.register('jira', 'tablib.formats._jira.JIRAFormat')
self.register('latex', 'tablib.formats._latex.LATEXFormat')
if find_spec('pandas'):
self.register('df', 'tablib.formats._df.DataFrameFormat')
self.register('rst', 'tablib.formats._rst.ReSTFormat')
if find_spec('tabulate'):
self.register('cli', 'tablib.formats._cli.CLIFormat')
def formats(self):
for key, frm in self._formats.items():
if isinstance(frm, str):
self._formats[key] = load_format_class(frm)
yield self._formats[key]
def get_format(self, key):
if key not in self._formats:
if key in uninstalled_format_messages:
raise UnsupportedFormat(
"The '{key}' format is not available. You may want to install the "
"{package_name} (or `pip install tablib[{extras_name}]`).".format(
**uninstalled_format_messages[key], key=key
)
)
raise UnsupportedFormat("Tablib has no format '%s' or it is not registered." % key)
if isinstance(self._formats[key], str):
self._formats[key] = load_format_class(self._formats[key])
return self._formats[key]
registry = Registry()
+20
View File
@@ -0,0 +1,20 @@
"""Tablib - Command-line Interface table export support.
Generates a representation for CLI from the dataset.
Wrapper for tabulate library.
"""
from tabulate import tabulate as Tabulate
class CLIFormat:
""" Class responsible to export to CLI Format """
title = 'cli'
DEFAULT_FMT = 'plain'
@classmethod
def export_set(cls, dataset, **kwargs):
"""Returns CLI representation of a Dataset."""
if dataset.headers:
kwargs.setdefault('headers', dataset.headers)
kwargs.setdefault('tablefmt', cls.DEFAULT_FMT)
return Tabulate(dataset, **kwargs)
+58
View File
@@ -0,0 +1,58 @@
""" Tablib - *SV Support.
"""
import csv
from io import StringIO
class CSVFormat:
title = 'csv'
extensions = ('csv',)
DEFAULT_DELIMITER = ','
@classmethod
def export_stream_set(cls, dataset, **kwargs):
"""Returns CSV representation of Dataset as file-like."""
stream = StringIO()
kwargs.setdefault('delimiter', cls.DEFAULT_DELIMITER)
_csv = csv.writer(stream, **kwargs)
for row in dataset._package(dicts=False):
_csv.writerow(row)
stream.seek(0)
return stream
@classmethod
def export_set(cls, dataset, **kwargs):
"""Returns CSV representation of Dataset."""
stream = cls.export_stream_set(dataset, **kwargs)
return stream.getvalue()
@classmethod
def import_set(cls, dset, in_stream, headers=True, **kwargs):
"""Returns dataset from CSV stream."""
dset.wipe()
kwargs.setdefault('delimiter', cls.DEFAULT_DELIMITER)
rows = csv.reader(in_stream, **kwargs)
for i, row in enumerate(rows):
if (i == 0) and (headers):
dset.headers = row
elif row:
dset.append(row)
@classmethod
def detect(cls, stream, delimiter=None):
"""Returns True if given stream is valid CSV."""
try:
csv.Sniffer().sniff(stream.read(1024), delimiters=delimiter or cls.DEFAULT_DELIMITER)
return True
except Exception:
return False
+66
View File
@@ -0,0 +1,66 @@
""" Tablib - DBF Support.
"""
import io
import os
import tempfile
from tablib.packages.dbfpy import dbf, dbfnew
from tablib.packages.dbfpy import record as dbfrecord
class DBFFormat:
title = 'dbf'
extensions = ('csv',)
DEFAULT_ENCODING = 'utf-8'
@classmethod
def export_set(cls, dataset):
"""Returns DBF representation of a Dataset"""
new_dbf = dbfnew.dbf_new()
temp_file, temp_uri = tempfile.mkstemp()
# create the appropriate fields based on the contents of the first row
first_row = dataset[0]
for fieldname, field_value in zip(dataset.headers, first_row):
if type(field_value) in [int, float]:
new_dbf.add_field(fieldname, 'N', 10, 8)
else:
new_dbf.add_field(fieldname, 'C', 80)
new_dbf.write(temp_uri)
dbf_file = dbf.Dbf(temp_uri, readOnly=0)
for row in dataset:
record = dbfrecord.DbfRecord(dbf_file)
for fieldname, field_value in zip(dataset.headers, row):
record[fieldname] = field_value
record.store()
dbf_file.close()
dbf_stream = open(temp_uri, 'rb')
stream = io.BytesIO(dbf_stream.read())
dbf_stream.close()
os.close(temp_file)
os.remove(temp_uri)
return stream.getvalue()
@classmethod
def import_set(cls, dset, in_stream, headers=True):
"""Returns a dataset from a DBF stream."""
dset.wipe()
_dbf = dbf.Dbf(in_stream)
dset.headers = _dbf.fieldNames
for record in range(_dbf.recordCount):
row = [_dbf[record][f] for f in _dbf.fieldNames]
dset.append(row)
@classmethod
def detect(cls, stream):
"""Returns True if the given stream is valid DBF"""
try:
_dbf = dbf.Dbf(stream, readOnly=True)
return True
except Exception:
return False
+41
View File
@@ -0,0 +1,41 @@
""" Tablib - DataFrame Support.
"""
try:
from pandas import DataFrame
except ImportError:
DataFrame = None
class DataFrameFormat:
title = 'df'
extensions = ('df',)
@classmethod
def detect(cls, stream):
"""Returns True if given stream is a DataFrame."""
if DataFrame is None:
return False
elif isinstance(stream, DataFrame):
return True
try:
DataFrame(stream.read())
return True
except ValueError:
return False
@classmethod
def export_set(cls, dset, index=None):
"""Returns DataFrame representation of DataBook."""
if DataFrame is None:
raise NotImplementedError(
'DataFrame Format requires `pandas` to be installed.'
' Try `pip install tablib[pandas]`.')
dataframe = DataFrame(dset.dict, columns=dset.headers)
return dataframe
@classmethod
def import_set(cls, dset, in_stream):
"""Returns dataset from DataFrame."""
dset.wipe()
dset.dict = in_stream.to_dict(orient='records')
+62
View File
@@ -0,0 +1,62 @@
""" Tablib - HTML export support.
"""
import codecs
from io import BytesIO
from MarkupPy import markup
class HTMLFormat:
BOOK_ENDINGS = 'h3'
title = 'html'
extensions = ('html', )
@classmethod
def export_set(cls, dataset):
"""HTML representation of a Dataset."""
stream = BytesIO()
page = markup.page()
page.table.open()
if dataset.headers is not None:
new_header = [item if item is not None else '' for item in dataset.headers]
page.thead.open()
headers = markup.oneliner.th(new_header)
page.tr(headers)
page.thead.close()
for row in dataset:
new_row = [item if item is not None else '' for item in row]
html_row = markup.oneliner.td(new_row)
page.tr(html_row)
page.table.close()
# Allow unicode characters in output
wrapper = codecs.getwriter("utf8")(stream)
wrapper.writelines(str(page))
return stream.getvalue().decode('utf-8')
@classmethod
def export_book(cls, databook):
"""HTML representation of a Databook."""
stream = BytesIO()
# 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))
wrapper.write('<{}>{}</{}>\n'.format(cls.BOOK_ENDINGS, title, cls.BOOK_ENDINGS))
wrapper.write(dset.html)
wrapper.write('\n')
return stream.getvalue().decode('utf-8')
+40
View File
@@ -0,0 +1,40 @@
"""Tablib - Jira table export support.
Generates a Jira table from the dataset.
"""
class JIRAFormat:
title = 'jira'
@classmethod
def export_set(cls, dataset):
"""Formats the dataset according to the Jira table syntax:
||heading 1||heading 2||heading 3||
|col A1|col A2|col A3|
|col B1|col B2|col B3|
:param dataset: dataset to serialize
:type dataset: tablib.core.Dataset
"""
header = cls._get_header(dataset.headers) if dataset.headers else ''
body = cls._get_body(dataset)
return '{}\n{}'.format(header, body) if header else body
@classmethod
def _get_body(cls, dataset):
return '\n'.join([cls._serialize_row(row) for row in dataset])
@classmethod
def _get_header(cls, headers):
return cls._serialize_row(headers, delimiter='||')
@classmethod
def _serialize_row(cls, row, delimiter='|'):
return '{}{}{}'.format(
delimiter,
delimiter.join([str(item) if item else ' ' for item in row]),
delimiter
)
+58
View File
@@ -0,0 +1,58 @@
""" Tablib - JSON Support
"""
import decimal
import json
from uuid import UUID
import tablib
def serialize_objects_handler(obj):
if isinstance(obj, (decimal.Decimal, UUID)):
return str(obj)
elif hasattr(obj, 'isoformat'):
return obj.isoformat()
else:
return obj
class JSONFormat:
title = 'json'
extensions = ('json', 'jsn')
@classmethod
def export_set(cls, dataset):
"""Returns JSON representation of Dataset."""
return json.dumps(dataset.dict, default=serialize_objects_handler)
@classmethod
def export_book(cls, databook):
"""Returns JSON representation of Databook."""
return json.dumps(databook._package(), default=serialize_objects_handler)
@classmethod
def import_set(cls, dset, in_stream):
"""Returns dataset from JSON stream."""
dset.wipe()
dset.dict = json.load(in_stream)
@classmethod
def import_book(cls, dbook, in_stream):
"""Returns databook from JSON stream."""
dbook.wipe()
for sheet in json.load(in_stream):
data = tablib.Dataset()
data.title = sheet['title']
data.dict = sheet['data']
dbook.add_sheet(data)
@classmethod
def detect(cls, stream):
"""Returns True if given stream is valid JSON."""
try:
json.load(stream)
return True
except (TypeError, ValueError):
return False
+132
View File
@@ -0,0 +1,132 @@
"""Tablib - LaTeX table export support.
Generates a LaTeX booktabs-style table from the dataset.
"""
import re
class LATEXFormat:
title = 'latex'
extensions = ('tex',)
TABLE_TEMPLATE = """\
%% Note: add \\usepackage{booktabs} to your preamble
%%
\\begin{table}[!htbp]
\\centering
%(CAPTION)s
\\begin{tabular}{%(COLSPEC)s}
\\toprule
%(HEADER)s
%(MIDRULE)s
%(BODY)s
\\bottomrule
\\end{tabular}
\\end{table}
"""
TEX_RESERVED_SYMBOLS_MAP = dict([
('\\', '\\textbackslash{}'),
('{', '\\{'),
('}', '\\}'),
('$', '\\$'),
('&', '\\&'),
('#', '\\#'),
('^', '\\textasciicircum{}'),
('_', '\\_'),
('~', '\\textasciitilde{}'),
('%', '\\%'),
])
TEX_RESERVED_SYMBOLS_RE = re.compile(
'(%s)' % '|'.join(map(re.escape, TEX_RESERVED_SYMBOLS_MAP.keys())))
@classmethod
def export_set(cls, dataset):
"""Returns LaTeX representation of dataset
:param dataset: dataset to serialize
:type dataset: tablib.core.Dataset
"""
caption = '\\caption{%s}' % dataset.title if dataset.title else '%'
colspec = cls._colspec(dataset.width)
header = cls._serialize_row(dataset.headers) if dataset.headers else ''
midrule = cls._midrule(dataset.width)
body = '\n'.join([cls._serialize_row(row) for row in dataset])
return cls.TABLE_TEMPLATE % dict(CAPTION=caption, COLSPEC=colspec,
HEADER=header, MIDRULE=midrule, BODY=body)
@classmethod
def _colspec(cls, dataset_width):
"""Generates the column specification for the LaTeX `tabular` environment
based on the dataset width.
The first column is justified to the left, all further columns are aligned
to the right.
.. note:: This is only a heuristic and most probably has to be fine-tuned
post export. Column alignment should depend on the data type, e.g., textual
content should usually be aligned to the left while numeric content almost
always should be aligned to the right.
:param dataset_width: width of the dataset
"""
spec = 'l'
for _ in range(1, dataset_width):
spec += 'r'
return spec
@classmethod
def _midrule(cls, dataset_width):
"""Generates the table `midrule`, which may be composed of several
`cmidrules`.
:param dataset_width: width of the dataset to serialize
"""
if not dataset_width or dataset_width == 1:
return '\\midrule'
return ' '.join([cls._cmidrule(colindex, dataset_width) for colindex in
range(1, dataset_width + 1)])
@classmethod
def _cmidrule(cls, colindex, dataset_width):
"""Generates the `cmidrule` for a single column with appropriate trimming
based on the column position.
:param colindex: Column index
:param dataset_width: width of the dataset
"""
rule = '\\cmidrule(%s){%d-%d}'
if colindex == 1:
# Rule of first column is trimmed on the right
return rule % ('r', colindex, colindex)
if colindex == dataset_width:
# Rule of last column is trimmed on the left
return rule % ('l', colindex, colindex)
# Inner columns are trimmed on the left and right
return rule % ('lr', colindex, colindex)
@classmethod
def _serialize_row(cls, row):
"""Returns string representation of a single row.
:param row: single dataset row
"""
new_row = [cls._escape_tex_reserved_symbols(str(item)) if item else ''
for item in row]
return 6 * ' ' + ' & '.join(new_row) + ' \\\\'
@classmethod
def _escape_tex_reserved_symbols(cls, input):
"""Escapes all TeX reserved symbols ('_', '~', etc.) in a string.
:param input: String to escape
"""
def replace(match):
return cls.TEX_RESERVED_SYMBOLS_MAP[match.group()]
return cls.TEX_RESERVED_SYMBOLS_RE.sub(replace, input)
+105
View File
@@ -0,0 +1,105 @@
""" Tablib - ODF Support.
"""
from io import BytesIO
from odf import opendocument, style, table, text
bold = style.Style(name="bold", family="paragraph")
bold.addElement(style.TextProperties(fontweight="bold", fontweightasian="bold", fontweightcomplex="bold"))
class ODSFormat:
title = 'ods'
extensions = ('ods',)
@classmethod
def export_set(cls, dataset):
"""Returns ODF representation of Dataset."""
wb = opendocument.OpenDocumentSpreadsheet()
wb.automaticstyles.addElement(bold)
ws = table.Table(name=dataset.title if dataset.title else 'Tablib Dataset')
wb.spreadsheet.addElement(ws)
cls.dset_sheet(dataset, ws)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
@classmethod
def export_book(cls, databook):
"""Returns ODF representation of DataBook."""
wb = opendocument.OpenDocumentSpreadsheet()
wb.automaticstyles.addElement(bold)
for i, dset in enumerate(databook._datasets):
ws = table.Table(name=dset.title if dset.title else 'Sheet%s' % (i))
wb.spreadsheet.addElement(ws)
cls.dset_sheet(dset, ws)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
@classmethod
def dset_sheet(cls, dataset, ws):
"""Completes given worksheet from given Dataset."""
_package = dataset._package(dicts=False)
for i, sep in enumerate(dataset._separators):
_offset = i
_package.insert((sep[0] + _offset), (sep[1],))
for i, row in enumerate(_package):
row_number = i + 1
odf_row = table.TableRow(stylename=bold, defaultcellstylename='bold')
for j, col in enumerate(row):
try:
col = str(col, errors='ignore')
except TypeError:
# col is already str
pass
ws.addElement(table.TableColumn())
# bold headers
if (row_number == 1) and dataset.headers:
odf_row.setAttribute('stylename', bold)
ws.addElement(odf_row)
cell = table.TableCell()
p = text.P()
p.addElement(text.Span(text=col, stylename=bold))
cell.addElement(p)
odf_row.addElement(cell)
# wrap the rest
else:
try:
if '\n' in col:
ws.addElement(odf_row)
cell = table.TableCell()
cell.addElement(text.P(text=col))
odf_row.addElement(cell)
else:
ws.addElement(odf_row)
cell = table.TableCell()
cell.addElement(text.P(text=col))
odf_row.addElement(cell)
except TypeError:
ws.addElement(odf_row)
cell = table.TableCell()
cell.addElement(text.P(text=col))
odf_row.addElement(cell)
@classmethod
def detect(cls, stream):
if isinstance(stream, bytes):
# load expects a file-like object.
stream = BytesIO(stream)
try:
opendocument.load(stream)
return True
except Exception:
return False
+265
View File
@@ -0,0 +1,265 @@
""" Tablib - reStructuredText Support
"""
from itertools import zip_longest
from statistics import median
from textwrap import TextWrapper
JUSTIFY_LEFT = 'left'
JUSTIFY_CENTER = 'center'
JUSTIFY_RIGHT = 'right'
JUSTIFY_VALUES = (JUSTIFY_LEFT, JUSTIFY_CENTER, JUSTIFY_RIGHT)
def to_str(value):
if isinstance(value, bytes):
return value.decode('utf-8')
return str(value)
def _max_word_len(text):
"""
Return the length of the longest word in `text`.
>>> _max_word_len('Python Module for Tabular Datasets')
8
"""
return max(len(word) for word in text.split()) if text else 0
class ReSTFormat:
title = 'rst'
extensions = ('rst',)
MAX_TABLE_WIDTH = 80 # Roughly. It may be wider to avoid breaking words.
@classmethod
def _get_column_string_lengths(cls, dataset):
"""
Returns a list of string lengths of each column, and a list of
maximum word lengths.
"""
if dataset.headers:
column_lengths = [[len(h)] for h in dataset.headers]
word_lens = [_max_word_len(h) for h in dataset.headers]
else:
column_lengths = [[] for _ in range(dataset.width)]
word_lens = [0 for _ in range(dataset.width)]
for row in dataset.dict:
values = iter(row.values() if hasattr(row, 'values') else row)
for i, val in enumerate(values):
text = to_str(val)
column_lengths[i].append(len(text))
word_lens[i] = max(word_lens[i], _max_word_len(text))
return column_lengths, word_lens
@classmethod
def _row_to_lines(cls, values, widths, wrapper, sep='|', justify=JUSTIFY_LEFT):
"""
Returns a table row of wrapped values as a list of lines
"""
if justify not in JUSTIFY_VALUES:
raise ValueError('Value of "justify" must be one of "{}"'.format(
'", "'.join(JUSTIFY_VALUES)
))
if justify == JUSTIFY_LEFT:
just = lambda text, width: text.ljust(width)
elif justify == JUSTIFY_CENTER:
just = lambda text, width: text.center(width)
else:
just = lambda text, width: text.rjust(width)
lpad = sep + ' ' if sep else ''
rpad = ' ' + sep if sep else ''
pad = ' ' + sep + ' '
cells = []
for value, width in zip(values, widths):
wrapper.width = width
text = to_str(value)
cell = wrapper.wrap(text)
cells.append(cell)
lines = zip_longest(*cells, fillvalue='')
lines = (
(just(cell_line, widths[i]) for i, cell_line in enumerate(line))
for line in lines
)
lines = [''.join((lpad, pad.join(line), rpad)) for line in lines]
return lines
@classmethod
def _get_column_widths(cls, dataset, max_table_width=MAX_TABLE_WIDTH, pad_len=3):
"""
Returns a list of column widths proportional to the median length
of the text in their cells.
"""
str_lens, word_lens = cls._get_column_string_lengths(dataset)
median_lens = [int(median(lens)) for lens in str_lens]
total = sum(median_lens)
if total > max_table_width - (pad_len * len(median_lens)):
column_widths = (max_table_width * l // total for l in median_lens)
else:
column_widths = (l for l in median_lens)
# Allow for separator and padding:
column_widths = (w - pad_len if w > pad_len else w for w in column_widths)
# Rather widen table than break words:
column_widths = [max(w, l) for w, l in zip(column_widths, word_lens)]
return column_widths
@classmethod
def export_set_as_simple_table(cls, dataset, column_widths=None):
"""
Returns reStructuredText grid table representation of dataset.
"""
lines = []
wrapper = TextWrapper()
if column_widths is None:
column_widths = cls._get_column_widths(dataset, pad_len=2)
border = ' '.join(['=' * w for w in column_widths])
lines.append(border)
if dataset.headers:
lines.extend(cls._row_to_lines(
dataset.headers,
column_widths,
wrapper,
sep='',
justify=JUSTIFY_CENTER,
))
lines.append(border)
for row in dataset.dict:
values = iter(row.values() if hasattr(row, 'values') else row)
lines.extend(cls._row_to_lines(values, column_widths, wrapper, ''))
lines.append(border)
return '\n'.join(lines)
@classmethod
def export_set_as_grid_table(cls, dataset, column_widths=None):
"""
Returns reStructuredText grid table representation of dataset.
>>> from tablib import Dataset
>>> from tablib.formats import registry
>>> bits = ((0, 0), (1, 0), (0, 1), (1, 1))
>>> data = Dataset()
>>> data.headers = ['A', 'B', 'A and B']
>>> for a, b in bits:
... data.append([bool(a), bool(b), bool(a * b)])
>>> rst = registry.get_format('rst')
>>> print(rst.export_set(data, force_grid=True))
+-------+-------+-------+
| A | B | A and |
| | | B |
+=======+=======+=======+
| False | False | False |
+-------+-------+-------+
| True | False | False |
+-------+-------+-------+
| False | True | False |
+-------+-------+-------+
| True | True | True |
+-------+-------+-------+
"""
lines = []
wrapper = TextWrapper()
if column_widths is None:
column_widths = cls._get_column_widths(dataset)
header_sep = '+=' + '=+='.join(['=' * w for w in column_widths]) + '=+'
row_sep = '+-' + '-+-'.join(['-' * w for w in column_widths]) + '-+'
lines.append(row_sep)
if dataset.headers:
lines.extend(cls._row_to_lines(
dataset.headers,
column_widths,
wrapper,
justify=JUSTIFY_CENTER,
))
lines.append(header_sep)
for row in dataset.dict:
values = iter(row.values() if hasattr(row, 'values') else row)
lines.extend(cls._row_to_lines(values, column_widths, wrapper))
lines.append(row_sep)
return '\n'.join(lines)
@classmethod
def _use_simple_table(cls, head0, col0, width0):
"""
Use a simple table if the text in the first column is never wrapped
>>> from tablib.formats import registry
>>> rst = registry.get_format('rst')
>>> rst._use_simple_table('menu', ['egg', 'bacon'], 10)
True
>>> rst._use_simple_table(None, ['lobster thermidor', 'spam'], 10)
False
"""
if head0 is not None:
head0 = to_str(head0)
if len(head0) > width0:
return False
for cell in col0:
cell = to_str(cell)
if len(cell) > width0:
return False
return True
@classmethod
def export_set(cls, dataset, **kwargs):
"""
Returns reStructuredText table representation of dataset.
Returns a simple table if the text in the first column is never
wrapped, otherwise returns a grid table.
>>> from tablib import Dataset
>>> bits = ((0, 0), (1, 0), (0, 1), (1, 1))
>>> data = Dataset()
>>> data.headers = ['A', 'B', 'A and B']
>>> for a, b in bits:
... data.append([bool(a), bool(b), bool(a * b)])
>>> table = data.rst
>>> table.split('\\n') == [
... '===== ===== =====',
... ' A B A and',
... ' B ',
... '===== ===== =====',
... 'False False False',
... 'True False False',
... 'False True False',
... 'True True True ',
... '===== ===== =====',
... ]
True
"""
if not dataset.dict:
return ''
force_grid = kwargs.get('force_grid', False)
max_table_width = kwargs.get('max_table_width', cls.MAX_TABLE_WIDTH)
column_widths = cls._get_column_widths(dataset, max_table_width)
use_simple_table = cls._use_simple_table(
dataset.headers[0] if dataset.headers else None,
dataset.get_col(0),
column_widths[0],
)
if use_simple_table and not force_grid:
return cls.export_set_as_simple_table(dataset, column_widths)
else:
return cls.export_set_as_grid_table(dataset, column_widths)
@classmethod
def export_book(cls, databook):
"""
reStructuredText representation of a Databook.
Tables are separated by a blank line. All tables use the grid
format.
"""
return '\n\n'.join(cls.export_set(dataset, force_grid=True)
for dataset in databook._datasets)
+11
View File
@@ -0,0 +1,11 @@
""" Tablib - TSV (Tab Separated Values) Support.
"""
from ._csv import CSVFormat
class TSVFormat(CSVFormat):
title = 'tsv'
extensions = ('tsv',)
DEFAULT_DELIMITER = '\t'
+138
View File
@@ -0,0 +1,138 @@
""" Tablib - XLS Support.
"""
from io import BytesIO
import tablib
import xlrd
import xlwt
# special styles
wrap = xlwt.easyxf("alignment: wrap on")
bold = xlwt.easyxf("font: bold on")
class XLSFormat:
title = 'xls'
extensions = ('xls',)
@classmethod
def detect(cls, stream):
"""Returns True if given stream is a readable excel file."""
try:
xlrd.open_workbook(file_contents=stream)
return True
except Exception:
pass
try:
xlrd.open_workbook(file_contents=stream.read())
return True
except Exception:
pass
try:
xlrd.open_workbook(filename=stream)
return True
except Exception:
return False
@classmethod
def export_set(cls, dataset):
"""Returns XLS representation of Dataset."""
wb = xlwt.Workbook(encoding='utf8')
ws = wb.add_sheet(dataset.title if dataset.title else 'Tablib Dataset')
cls.dset_sheet(dataset, ws)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
@classmethod
def export_book(cls, 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))
cls.dset_sheet(dset, ws)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
@classmethod
def import_set(cls, dset, in_stream, headers=True):
"""Returns databook from XLS stream."""
dset.wipe()
xls_book = xlrd.open_workbook(file_contents=in_stream.read())
sheet = xls_book.sheet_by_index(0)
dset.title = sheet.name
for i in range(sheet.nrows):
if i == 0 and headers:
dset.headers = sheet.row_values(0)
else:
dset.append([
val if typ != xlrd.XL_CELL_ERROR else xlrd.error_text_from_code[val]
for val, typ in zip(sheet.row_values(i), sheet.row_types(i))
])
@classmethod
def import_book(cls, dbook, in_stream, headers=True):
"""Returns databook from XLS stream."""
dbook.wipe()
xls_book = xlrd.open_workbook(file_contents=in_stream)
for sheet in xls_book.sheets():
data = tablib.Dataset()
data.title = sheet.name
for i in range(sheet.nrows):
if i == 0 and headers:
data.headers = sheet.row_values(0)
else:
data.append(sheet.row_values(i))
dbook.add_sheet(data)
@classmethod
def dset_sheet(cls, dataset, ws):
"""Completes given worksheet from given Dataset."""
_package = dataset._package(dicts=False)
for i, sep in enumerate(dataset._separators):
_offset = i
_package.insert((sep[0] + _offset), (sep[1],))
for i, row in enumerate(_package):
for j, col in enumerate(row):
# bold headers
if (i == 0) and dataset.headers:
ws.write(i, j, col, bold)
# frozen header row
ws.panes_frozen = True
ws.horz_split_pos = 1
# bold separators
elif len(row) < dataset.width:
ws.write(i, j, col, bold)
# wrap the rest
else:
try:
if '\n' in col:
ws.write(i, j, col, wrap)
else:
ws.write(i, j, col)
except TypeError:
ws.write(i, j, col)
+137
View File
@@ -0,0 +1,137 @@
""" Tablib - XLSX Support.
"""
from io import BytesIO
import openpyxl
import tablib
Workbook = openpyxl.workbook.Workbook
ExcelWriter = openpyxl.writer.excel.ExcelWriter
get_column_letter = openpyxl.utils.get_column_letter
class XLSXFormat:
title = 'xlsx'
extensions = ('xlsx',)
@classmethod
def detect(cls, stream):
"""Returns True if given stream is a readable excel file."""
try:
openpyxl.reader.excel.load_workbook(stream, read_only=True)
return True
except Exception:
return False
@classmethod
def export_set(cls, dataset, freeze_panes=True):
"""Returns XLSX representation of Dataset."""
wb = Workbook()
ws = wb.worksheets[0]
ws.title = dataset.title if dataset.title else 'Tablib Dataset'
cls.dset_sheet(dataset, ws, freeze_panes=freeze_panes)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
@classmethod
def export_book(cls, databook, freeze_panes=True):
"""Returns XLSX representation of DataBook."""
wb = Workbook()
for sheet in wb.worksheets:
wb.remove(sheet)
for i, dset in enumerate(databook._datasets):
ws = wb.create_sheet()
ws.title = dset.title if dset.title else 'Sheet%s' % (i)
cls.dset_sheet(dset, ws, freeze_panes=freeze_panes)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
@classmethod
def import_set(cls, dset, in_stream, headers=True):
"""Returns databook from XLS stream."""
dset.wipe()
xls_book = openpyxl.reader.excel.load_workbook(in_stream, read_only=True)
sheet = xls_book.active
dset.title = sheet.title
for i, row in enumerate(sheet.rows):
row_vals = [c.value for c in row]
if (i == 0) and (headers):
dset.headers = row_vals
else:
dset.append(row_vals)
@classmethod
def import_book(cls, dbook, in_stream, headers=True):
"""Returns databook from XLS stream."""
dbook.wipe()
xls_book = openpyxl.reader.excel.load_workbook(in_stream, read_only=True)
for sheet in xls_book.worksheets:
data = tablib.Dataset()
data.title = sheet.title
for i, row in enumerate(sheet.rows):
row_vals = [c.value for c in row]
if (i == 0) and (headers):
data.headers = row_vals
else:
data.append(row_vals)
dbook.add_sheet(data)
@classmethod
def dset_sheet(cls, dataset, ws, freeze_panes=True):
"""Completes given worksheet from given Dataset."""
_package = dataset._package(dicts=False)
for i, sep in enumerate(dataset._separators):
_offset = i
_package.insert((sep[0] + _offset), (sep[1],))
bold = openpyxl.styles.Font(bold=True)
wrap_text = openpyxl.styles.Alignment(wrap_text=True)
for i, row in enumerate(_package):
row_number = i + 1
for j, col in enumerate(row):
col_idx = get_column_letter(j + 1)
cell = ws['{}{}'.format(col_idx, row_number)]
# bold headers
if (row_number == 1) and dataset.headers:
cell.font = bold
if freeze_panes:
# Export Freeze only after first Line
ws.freeze_panes = 'A2'
# bold separators
elif len(row) < dataset.width:
cell.font = bold
# wrap the rest
else:
try:
str_col_value = str(col)
except TypeError:
str_col_value = ''
if '\n' in str_col_value:
cell.alignment = wrap_text
try:
cell.value = col
except (ValueError, TypeError):
cell.value = str(col)
+53
View File
@@ -0,0 +1,53 @@
""" Tablib - YAML Support.
"""
import tablib
import yaml
class YAMLFormat:
title = 'yaml'
extensions = ('yaml', 'yml')
@classmethod
def export_set(cls, dataset):
"""Returns YAML representation of Dataset."""
return yaml.safe_dump(dataset._package(ordered=False), default_flow_style=None)
@classmethod
def export_book(cls, databook):
"""Returns YAML representation of Databook."""
return yaml.safe_dump(databook._package(ordered=False), default_flow_style=None)
@classmethod
def import_set(cls, dset, in_stream):
"""Returns dataset from YAML stream."""
dset.wipe()
dset.dict = yaml.safe_load(in_stream)
@classmethod
def import_book(cls, dbook, in_stream):
"""Returns databook from YAML stream."""
dbook.wipe()
for sheet in yaml.safe_load(in_stream):
data = tablib.Dataset()
data.title = sheet['title']
data.dict = sheet['data']
dbook.add_sheet(data)
@classmethod
def detect(cls, stream):
"""Returns True if given stream is valid YAML."""
try:
_yaml = yaml.safe_load(stream)
if isinstance(_yaml, (list, tuple, dict)):
return True
else:
return False
except (yaml.parser.ParserError, yaml.reader.ReaderError,
yaml.scanner.ScannerError):
return False
+297
View File
@@ -0,0 +1,297 @@
#! /usr/bin/env python
"""DBF accessing helpers.
FIXME: more documentation needed
Examples:
Create new table, setup structure, add records:
dbf = Dbf(filename, new=True)
dbf.addField(
("NAME", "C", 15),
("SURNAME", "C", 25),
("INITIALS", "C", 10),
("BIRTHDATE", "D"),
)
for (n, s, i, b) in (
("John", "Miller", "YC", (1980, 10, 11)),
("Andy", "Larkin", "", (1980, 4, 11)),
):
rec = dbf.newRecord()
rec["NAME"] = n
rec["SURNAME"] = s
rec["INITIALS"] = i
rec["BIRTHDATE"] = b
rec.store()
dbf.close()
Open existed dbf, read some data:
dbf = Dbf(filename, True)
for rec in dbf:
for fldName in dbf.fieldNames:
print('%s:\t %s (%s)' % (fldName, rec[fldName],
type(rec[fldName])))
print()
dbf.close()
"""
"""History (most recent first):
11-feb-2007 [als] export INVALID_VALUE;
Dbf: added .ignoreErrors, .INVALID_VALUE
04-jul-2006 [als] added export declaration
20-dec-2005 [yc] removed fromStream and newDbf methods:
use argument of __init__ call must be used instead;
added class fields pointing to the header and
record classes.
17-dec-2005 [yc] split to several modules; reimplemented
13-dec-2005 [yc] adapted to the changes of the `strutil` module.
13-sep-2002 [als] support FoxPro Timestamp datatype
15-nov-1999 [jjk] documentation updates, add demo
24-aug-1998 [jjk] add some encodeValue methods (not tested), other tweaks
08-jun-1998 [jjk] fix problems, add more features
20-feb-1998 [jjk] fix problems, add more features
19-feb-1998 [jjk] add create/write capabilities
18-feb-1998 [jjk] from dbfload.py
"""
__version__ = "$Revision: 1.7 $"[11:-2]
__date__ = "$Date: 2007/02/11 09:23:13 $"[7:-2]
__author__ = "Jeff Kunce <kuncej@mail.conservation.state.mo.us>"
__all__ = ["Dbf"]
from . import header, record
from .utils import INVALID_VALUE
class Dbf:
"""DBF accessor.
FIXME:
docs and examples needed (dont' forget to tell
about problems adding new fields on the fly)
Implementation notes:
``_new`` field is used to indicate whether this is
a new data table. `addField` could be used only for
the new tables! If at least one record was appended
to the table it's structure couldn't be changed.
"""
__slots__ = ("name", "header", "stream",
"_changed", "_new", "_ignore_errors")
HeaderClass = header.DbfHeader
RecordClass = record.DbfRecord
INVALID_VALUE = INVALID_VALUE
# initialization and creation helpers
def __init__(self, f, readOnly=False, new=False, ignoreErrors=False):
"""Initialize instance.
Arguments:
f:
Filename or file-like object.
new:
True if new data table must be created. Assume
data table exists if this argument is False.
readOnly:
if ``f`` argument is a string file will
be opend in read-only mode; in other cases
this argument is ignored. This argument is ignored
even if ``new`` argument is True.
headerObj:
`header.DbfHeader` instance or None. If this argument
is None, new empty header will be used with the
all fields set by default.
ignoreErrors:
if set, failing field value conversion will return
``INVALID_VALUE`` instead of raising conversion error.
"""
if isinstance(f, str):
# a filename
self.name = f
if new:
# new table (table file must be
# created or opened and truncated)
self.stream = open(f, "w+b")
else:
# table file must exist
self.stream = open(f, ("r+b", "rb")[bool(readOnly)])
else:
# a stream
self.name = getattr(f, "name", "")
self.stream = f
if new:
# if this is a new table, header will be empty
self.header = self.HeaderClass()
else:
# or instantiated using stream
self.header = self.HeaderClass.fromStream(self.stream)
self.ignoreErrors = ignoreErrors
self._new = bool(new)
self._changed = False
# properties
closed = property(lambda self: self.stream.closed)
recordCount = property(lambda self: self.header.recordCount)
fieldNames = property(
lambda self: [_fld.name for _fld in self.header.fields])
fieldDefs = property(lambda self: self.header.fields)
changed = property(lambda self: self._changed or self.header.changed)
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,
doc="""Error processing mode for DBF field value conversion
if set, failing field value conversion will return
``INVALID_VALUE`` instead of raising conversion error.
""")
# protected methods
def _fixIndex(self, index):
"""Return fixed index.
This method fails if index isn't a numeric object
(long or int). Or index isn't in a valid range
(less or equal to the number of records in the db).
If ``index`` is a negative number, it will be
treated as a negative indexes for list objects.
Return:
Return value is numeric object maning valid index.
"""
if not isinstance(index, int):
raise TypeError("Index must be a numeric object")
if index < 0:
# index from the right side
# fix it to the left-side index
index += len(self) + 1
if index >= len(self):
raise IndexError("Record index out of range")
return index
# interface methods
def close(self):
self.flush()
self.stream.close()
def flush(self):
"""Flush data to the associated stream."""
if self.changed:
self.header.setCurrentDate()
self.header.write(self.stream)
self.stream.flush()
self._changed = False
def indexOfFieldName(self, name):
"""Index of field named ``name``."""
# FIXME: move this to header class
names = [f.name for f in self.header.fields]
return names.index(name.upper())
def newRecord(self):
"""Return new record, which belong to this table."""
return self.RecordClass(self)
def append(self, record):
"""Append ``record`` to the database."""
record.index = self.header.recordCount
record._write()
self.header.recordCount += 1
self._changed = True
self._new = False
def addField(self, *defs):
"""Add field definitions.
For more information see `header.DbfHeader.addField`.
"""
if self._new:
self.header.addField(*defs)
else:
raise TypeError("At least one record was added, "
"structure can't be changed")
# 'magic' methods (representation and sequence interface)
def __repr__(self):
return "Dbf stream '%s'\n" % self.stream + repr(self.header)
def __len__(self):
"""Return number of records."""
return self.recordCount
def __getitem__(self, index):
"""Return `DbfRecord` instance."""
return self.RecordClass.fromStream(self, self._fixIndex(index))
def __setitem__(self, index, record):
"""Write `DbfRecord` instance to the stream."""
record.index = self._fixIndex(index)
record._write()
self._changed = True
self._new = False
# def __del__(self):
# """Flush stream upon deletion of the object."""
# self.flush()
def demo_read(filename):
_dbf = Dbf(filename, True)
for _rec in _dbf:
print()
print(repr(_rec))
_dbf.close()
def demo_create(filename):
_dbf = Dbf(filename, new=True)
_dbf.addField(
("NAME", "C", 15),
("SURNAME", "C", 25),
("INITIALS", "C", 10),
("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)),
):
_rec = _dbf.newRecord()
_rec["NAME"] = _n
_rec["SURNAME"] = _s
_rec["INITIALS"] = _i
_rec["BIRTHDATE"] = _b
_rec.store()
print(repr(_dbf))
_dbf.close()
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 :
+183
View File
@@ -0,0 +1,183 @@
#!/usr/bin/python
""".DBF creation helpers.
Note: this is a legacy interface. New code should use Dbf class
for table creation (see examples in dbf.py)
TODO:
- handle Memo fields.
- check length of the fields according to the
`http://www.clicketyclick.dk/databases/xbase/format/data_types.html`
"""
"""History (most recent first)
04-jul-2006 [als] added export declaration;
updated for dbfpy 2.0
15-dec-2005 [yc] define dbf_new.__slots__
14-dec-2005 [yc] added vim modeline; retab'd; added doc-strings;
dbf_new now is a new class (inherited from object)
??-jun-2000 [--] added by Hans Fiby
"""
__version__ = "$Revision: 1.4 $"[11:-2]
__date__ = "$Date: 2006/07/04 08:18:18 $"[7:-2]
__all__ = ["dbf_new"]
from .dbf import *
from .fields import *
from .header import *
from .record import *
class _FieldDefinition:
"""Field definition.
This is a simple structure, which contains ``name``, ``type``,
``len``, ``dec`` and ``cls`` fields.
Objects also implement get/setitem magic functions, so fields
could be accessed via sequence interface, where 'name' has
index 0, 'type' index 1, 'len' index 2, 'dec' index 3 and
'cls' could be located at index 4.
"""
__slots__ = "name", "type", "len", "dec", "cls"
# WARNING: be attentive - dictionaries are mutable!
FLD_TYPES = {
# type: (cls, len)
"C": (DbfCharacterFieldDef, None),
"N": (DbfNumericFieldDef, None),
"L": (DbfLogicalFieldDef, 1),
# FIXME: support memos
# "M": (DbfMemoFieldDef),
"D": (DbfDateFieldDef, 8),
# FIXME: I'm not sure length should be 14 characters!
# but temporary I use it, cuz date is 8 characters
# and time 6 (hhmmss)
"T": (DbfDateTimeFieldDef, 14),
}
def __init__(self, name, type, len=None, dec=0):
_cls, _len = self.FLD_TYPES[type]
if _len is None:
if len is None:
raise ValueError("Field length must be defined")
_len = len
self.name = name
self.type = type
self.len = _len
self.dec = dec
self.cls = _cls
def getDbfField(self):
"Return `DbfFieldDef` instance from the current definition."
return self.cls(self.name, self.len, self.dec)
def appendToHeader(self, dbfh):
"""Create a `DbfFieldDef` instance and append it to the dbf header.
Arguments:
dbfh: `DbfHeader` instance.
"""
_dbff = self.getDbfField()
dbfh.addField(_dbff)
class dbf_new:
"""New .DBF creation helper.
Example Usage:
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")
Note:
This module cannot handle Memo-fields,
they are special.
"""
__slots__ = ("fields",)
FieldDefinitionClass = _FieldDefinition
def __init__(self):
self.fields = []
def add_field(self, name, typ, len, dec=0):
"""Add field definition.
Arguments:
name:
field name (str object). field name must not
contain ASCII NULs and it's length shouldn't
exceed 10 characters.
typ:
type of the field. this must be a single character
from the "CNLMDT" set meaning character, numeric,
logical, memo, date and date/time respectively.
len:
length of the field. this argument is used only for
the character and numeric fields. all other fields
have fixed length.
FIXME: use None as a default for this argument?
dec:
decimal precision. used only for the numric fields.
"""
self.fields.append(self.FieldDefinitionClass(name, typ, len, dec))
def write(self, filename):
"""Create empty .DBF file using current structure."""
_dbfh = DbfHeader()
_dbfh.setCurrentDate()
for _fldDef in self.fields:
_fldDef.appendToHeader(_dbfh)
_dbfStream = open(filename, "wb")
_dbfh.write(_dbfStream)
_dbfStream.close()
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.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.store()
# add another record
rec = DbfRecord(dbft)
rec['name'] = 'foo and bar'
rec['price'] = 12234
rec['date'] = (1992, 7, 15)
rec.store()
# show the records
print("*** inserted 2 records into tst.dbf: ***")
print(repr(dbft))
for i1 in range(len(dbft)):
rec = dbft[i1]
for fldName in dbft.fieldNames:
print('{}:\t {}'.format(fldName, rec[fldName]))
print()
dbft.close()
# vim: set et sts=4 sw=4 :
+475
View File
@@ -0,0 +1,475 @@
"""DBF fields definitions.
TODO:
- make memos work
"""
"""History (most recent first):
26-may-2009 [als] DbfNumericFieldDef.decodeValue: strip zero bytes
05-feb-2009 [als] DbfDateFieldDef.encodeValue: empty arg produces empty date
16-sep-2008 [als] DbfNumericFieldDef decoding looks for decimal point
in the value to select float or integer return type
13-mar-2008 [als] check field name length in constructor
11-feb-2007 [als] handle value conversion errors
10-feb-2007 [als] DbfFieldDef: added .rawFromRecord()
01-dec-2006 [als] Timestamp columns use None for empty values
31-oct-2006 [als] support field types 'F' (float), 'I' (integer)
and 'Y' (currency);
automate export and registration of field classes
04-jul-2006 [als] added export declaration
10-mar-2006 [als] decode empty values for Date and Logical fields;
show field name in errors
10-mar-2006 [als] fix Numeric value decoding: according to spec,
value always is string representation of the number;
ensure that encoded Numeric value fits into the field
20-dec-2005 [yc] use field names in upper case
15-dec-2005 [yc] field definitions moved from `dbf`.
"""
__version__ = "$Revision: 1.14 $"[11:-2]
__date__ = "$Date: 2009/05/26 05:16:51 $"[7:-2]
__all__ = ["lookupFor"] # field classes added at the end of the module
import datetime
import struct
import sys
from functools import total_ordering
from . import utils
# abstract definitions
@total_ordering
class DbfFieldDef:
"""Abstract field definition.
Child classes must override ``type`` class attribute to provide datatype
information of the field definition. For more info about types visit
`http://www.clicketyclick.dk/databases/xbase/format/data_types.html`
Also child classes must override ``defaultValue`` field to provide
default value for the field value.
If child class has fixed length ``length`` class attribute must be
overridden and set to the valid value. None value means, that field
isn't of fixed length.
Note: ``name`` field must not be changed after instantiation.
"""
__slots__ = ("name", "decimalCount", "start", "end", "ignoreErrors")
# length of the field, None in case of variable-length field,
# or a number if this field is a fixed-length field
length = None
# field type. for more information about fields types visit
# `http://www.clicketyclick.dk/databases/xbase/format/data_types.html`
# must be overridden in child classes
typeCode = None
# default value for the field. this field must be
# overridden in child classes
defaultValue = None
def __init__(self, name, length=None, decimalCount=None,
start=None, stop=None, ignoreErrors=False):
"""Initialize instance."""
assert self.typeCode is not None, "Type code must be overridden"
assert self.defaultValue is not None, "Default value must be overridden"
# fix arguments
if len(name) > 10:
raise ValueError("Field name \"%s\" is too long" % name)
name = str(name).upper()
if self.__class__.length is None:
if length is None:
raise ValueError("[%s] Length isn't specified" % name)
length = int(length)
if length <= 0:
raise ValueError("[%s] Length must be a positive integer" % name)
else:
length = self.length
if decimalCount is None:
decimalCount = 0
# set fields
self.name = name
# FIXME: validate length according to the specification at
# http://www.clicketyclick.dk/databases/xbase/format/data_types.html
self.length = length
self.decimalCount = decimalCount
self.ignoreErrors = ignoreErrors
self.start = start
self.end = stop
def __eq__(self, other):
return repr(self) == repr(other)
def __ne__(self, other):
return repr(self) != repr(other)
def __lt__(self, other):
return repr(self) < repr(other)
def __hash__(self):
return hash(self.name)
def fromString(cls, string, start, ignoreErrors=False):
"""Decode dbf field definition from the string data.
Arguments:
string:
a string, dbf definition is decoded from. length of
the string must be 32 bytes.
start:
position in the database file.
ignoreErrors:
initial error processing mode for the new field (boolean)
"""
assert len(string) == 32
_length = string[16]
return cls(utils.unzfill(string)[:11].decode('utf-8'), _length,
string[17], start, start + _length, ignoreErrors=ignoreErrors)
fromString = classmethod(fromString)
def toString(self):
"""Return encoded field definition.
Return:
Return value is a string object containing encoded
definition of this field.
"""
_name = self.name.ljust(11, '\0')
return (
_name +
self.typeCode +
# data address
chr(0) * 4 +
chr(self.length) +
chr(self.decimalCount) +
chr(0) * 14
)
def __repr__(self):
return "%-10s %1s %3d %3d" % self.fieldInfo()
def fieldInfo(self):
"""Return field information.
Return:
Return value is a (name, type, length, decimals) tuple.
"""
return (self.name, self.typeCode, self.length, self.decimalCount)
def rawFromRecord(self, record):
"""Return a "raw" field value from the record string."""
return record[self.start:self.end]
def decodeFromRecord(self, record):
"""Return decoded field value from the record string."""
try:
return self.decodeValue(self.rawFromRecord(record))
except Exception:
if self.ignoreErrors:
return utils.INVALID_VALUE
else:
raise
def decodeValue(self, value):
"""Return decoded value from string value.
This method shouldn't be used publicly. It's called from the
`decodeFromRecord` method.
This is an abstract method and it must be overridden in child classes.
"""
raise NotImplementedError
def encodeValue(self, value):
"""Return str object containing encoded field value.
This is an abstract method and it must be overridden in child classes.
"""
raise NotImplementedError
# real classes
class DbfCharacterFieldDef(DbfFieldDef):
"""Definition of the character field."""
typeCode = "C"
defaultValue = b''
def decodeValue(self, value):
"""Return string object.
Return value is a ``value`` argument with stripped right spaces.
"""
return value.rstrip(b' ').decode('utf-8')
def encodeValue(self, value):
"""Return raw data string encoded from a ``value``."""
return str(value)[:self.length].ljust(self.length)
class DbfNumericFieldDef(DbfFieldDef):
"""Definition of the numeric field."""
typeCode = "N"
# XXX: now I'm not sure it was a good idea to make a class field
# `defaultValue` instead of a generic method as it was implemented
# previously -- it's ok with all types except number, cuz
# if self.decimalCount is 0, we should return 0 and 0.0 otherwise.
defaultValue = 0
def decodeValue(self, value):
"""Return a number decoded from ``value``.
If decimals is zero, value will be decoded as an integer;
or as a float otherwise.
Return:
Return value is a int (long) or float instance.
"""
value = value.strip(b' \0')
if b'.' in value:
# a float (has decimal separator)
return float(value)
elif value:
# must be an integer
return int(value)
else:
return 0
def encodeValue(self, value):
"""Return string containing encoded ``value``."""
_rv = ("%*.*f" % (self.length, self.decimalCount, value))
if len(_rv) > self.length:
_ppos = _rv.find(".")
if 0 <= _ppos <= self.length:
_rv = _rv[:self.length]
else:
raise ValueError("[%s] Numeric overflow: %s (field width: %i)"
% (self.name, _rv, self.length))
return _rv
class DbfFloatFieldDef(DbfNumericFieldDef):
"""Definition of the float field - same as numeric."""
typeCode = "F"
class DbfIntegerFieldDef(DbfFieldDef):
"""Definition of the integer field."""
typeCode = "I"
length = 4
defaultValue = 0
def decodeValue(self, value):
"""Return an integer number decoded from ``value``."""
return struct.unpack("<i", value)[0]
def encodeValue(self, value):
"""Return string containing encoded ``value``."""
return struct.pack("<i", int(value))
class DbfCurrencyFieldDef(DbfFieldDef):
"""Definition of the currency field."""
typeCode = "Y"
length = 8
defaultValue = 0.0
def decodeValue(self, value):
"""Return float number decoded from ``value``."""
return struct.unpack("<q", value)[0] / 10000.
def encodeValue(self, value):
"""Return string containing encoded ``value``."""
return struct.pack("<q", round(value * 10000))
class DbfLogicalFieldDef(DbfFieldDef):
"""Definition of the logical field."""
typeCode = "L"
defaultValue = -1
length = 1
def decodeValue(self, value):
"""Return True, False or -1 decoded from ``value``."""
# Note: value always is 1-char string
if value == "?":
return -1
if value in "NnFf ":
return False
if value in "YyTt":
return True
raise ValueError("[{}] Invalid logical value {!r}".format(self.name, value))
def encodeValue(self, value):
"""Return a character from the "TF?" set.
Return:
Return value is "T" if ``value`` is True
"?" if value is -1 or False otherwise.
"""
if value is True:
return "T"
if value == -1:
return "?"
return "F"
class DbfMemoFieldDef(DbfFieldDef):
"""Definition of the memo field.
Note: memos aren't currently completely supported.
"""
typeCode = "M"
defaultValue = " " * 10
length = 10
def decodeValue(self, value):
"""Return int .dbt block number decoded from the string object."""
# return int(value)
raise NotImplementedError
def encodeValue(self, value):
"""Return raw data string encoded from a ``value``.
Note: this is an internal method.
"""
# return str(value)[:self.length].ljust(self.length)
raise NotImplementedError
class DbfDateFieldDef(DbfFieldDef):
"""Definition of the date field."""
typeCode = "D"
defaultValue = utils.classproperty(lambda cls: datetime.date.today())
# "yyyymmdd" gives us 8 characters
length = 8
def decodeValue(self, value):
"""Return a ``datetime.date`` instance decoded from ``value``."""
if value.strip():
return utils.getDate(value)
else:
return None
def encodeValue(self, value):
"""Return a string-encoded value.
``value`` argument should be a value suitable for the
`utils.getDate` call.
Return:
Return value is a string in format "yyyymmdd".
"""
if value:
return utils.getDate(value).strftime("%Y%m%d")
else:
return " " * self.length
class DbfDateTimeFieldDef(DbfFieldDef):
"""Definition of the timestamp field."""
# a difference between JDN (Julian Day Number)
# and GDN (Gregorian Day Number). note, that GDN < JDN
JDN_GDN_DIFF = 1721425
typeCode = "T"
defaultValue = utils.classproperty(lambda cls: datetime.datetime.now())
# two 32-bits integers representing JDN and amount of
# milliseconds respectively gives us 8 bytes.
# note, that values must be encoded in LE byteorder.
length = 8
def decodeValue(self, value):
"""Return a `datetime.datetime` instance."""
assert len(value) == self.length
# LE byteorder
_jdn, _msecs = struct.unpack("<2I", value)
if _jdn >= 1:
_rv = datetime.datetime.fromordinal(_jdn - self.JDN_GDN_DIFF)
_rv += datetime.timedelta(0, _msecs / 1000.0)
else:
# empty date
_rv = None
return _rv
def encodeValue(self, value):
"""Return a string-encoded ``value``."""
if value:
value = utils.getDateTime(value)
# LE byteorder
_rv = struct.pack("<2I", value.toordinal() + self.JDN_GDN_DIFF,
(value.hour * 3600 + value.minute * 60 + value.second) * 1000)
else:
_rv = "\0" * self.length
assert len(_rv) == self.length
return _rv
_fieldsRegistry = {}
def registerField(fieldCls):
"""Register field definition class.
``fieldCls`` should be subclass of the `DbfFieldDef`.
Use `lookupFor` to retrieve field definition class
by the type code.
"""
assert fieldCls.typeCode is not None, "Type code isn't defined"
# XXX: use fieldCls.typeCode.upper()? in case of any decign
# don't forget to look to the same comment in ``lookupFor`` method
_fieldsRegistry[fieldCls.typeCode] = fieldCls
def lookupFor(typeCode):
"""Return field definition class for the given type code.
``typeCode`` must be a single character. That type should be
previously registered.
Use `registerField` to register new field class.
Return:
Return value is a subclass of the `DbfFieldDef`.
"""
# XXX: use typeCode.upper()? in case of any decign don't
# forget to look to the same comment in ``registerField``
return _fieldsRegistry[chr(typeCode)]
# register generic types
for (_name, _val) in list(globals().items()):
if isinstance(_val, type) and issubclass(_val, DbfFieldDef) \
and (_name != "DbfFieldDef"):
__all__.append(_name)
registerField(_val)
del _name, _val
# vim: et sts=4 sw=4 :
+270
View File
@@ -0,0 +1,270 @@
"""DBF header definition.
TODO:
- handle encoding of the character fields
(encoding information stored in the DBF header)
"""
"""History (most recent first):
16-sep-2010 [als] fromStream: fix century of the last update field
11-feb-2007 [als] added .ignoreErrors
10-feb-2007 [als] added __getitem__: return field definitions
by field name or field number (zero-based)
04-jul-2006 [als] added export declaration
15-dec-2005 [yc] created
"""
__version__ = "$Revision: 1.6 $"[11:-2]
__date__ = "$Date: 2010/09/16 05:06:39 $"[7:-2]
__all__ = ["DbfHeader"]
import datetime
import io
import struct
import sys
from . import fields
from .utils import getDate
class DbfHeader:
"""Dbf header definition.
For more information about dbf header format visit
`http://www.clicketyclick.dk/databases/xbase/format/dbf.html#DBF_STRUCT`
Examples:
Create an empty dbf header and add some field definitions:
dbfh = DbfHeader()
dbfh.addField(("name", "C", 10))
dbfh.addField(("date", "D"))
dbfh.addField(DbfNumericFieldDef("price", 5, 2))
Create a dbf header with field definitions:
dbfh = DbfHeader([
("name", "C", 10),
("date", "D"),
DbfNumericFieldDef("price", 5, 2),
])
"""
__slots__ = ("signature", "fields", "lastUpdate", "recordLength",
"recordCount", "headerLength", "changed", "_ignore_errors")
# instance construction and initialization methods
def __init__(self, fields=None, headerLength=0, recordLength=0,
recordCount=0, signature=0x03, lastUpdate=None, ignoreErrors=False):
"""Initialize instance.
Arguments:
fields:
a list of field definitions;
recordLength:
size of the records;
headerLength:
size of the header;
recordCount:
number of records stored in DBF;
signature:
version number (aka signature). using 0x03 as a default meaning
"File without DBT". for more information about this field visit
``http://www.clicketyclick.dk/databases/xbase/format/dbf.html#DBF_NOTE_1_TARGET``
lastUpdate:
date of the DBF's update. this could be a string ('yymmdd' or
'yyyymmdd'), timestamp (int or float), datetime/date value,
a sequence (assuming (yyyy, mm, dd, ...)) or an object having
callable ``ticks`` field.
ignoreErrors:
error processing mode for DBF fields (boolean)
"""
self.signature = signature
if fields is None:
self.fields = []
else:
self.fields = list(fields)
self.lastUpdate = getDate(lastUpdate)
self.recordLength = recordLength
self.headerLength = headerLength
self.recordCount = recordCount
self.ignoreErrors = ignoreErrors
# XXX: I'm not sure this is safe to
# initialize `self.changed` in this way
self.changed = bool(self.fields)
# @classmethod
def fromString(cls, string):
"""Return header instance from the string object."""
return cls.fromStream(io.StringIO(str(string)))
fromString = classmethod(fromString)
# @classmethod
def fromStream(cls, stream):
"""Return header object from the stream."""
stream.seek(0)
first_32 = stream.read(32)
if type(first_32) != bytes:
_data = bytes(first_32, sys.getfilesystemencoding())
_data = first_32
(_cnt, _hdrLen, _recLen) = struct.unpack("<I2H", _data[4:12])
# reserved = _data[12:32]
_year = _data[1]
if _year < 80:
# dBase II started at 1980. It is quite unlikely
# that actual last update date is before that year.
_year += 2000
else:
_year += 1900
# create header object
_obj = cls(None, _hdrLen, _recLen, _cnt, _data[0],
(_year, _data[2], _data[3]))
# append field definitions
# position 0 is for the deletion flag
_pos = 1
_data = stream.read(1)
while _data != b'\r':
_data += stream.read(31)
_fld = fields.lookupFor(_data[11]).fromString(_data, _pos)
_obj._addField(_fld)
_pos = _fld.end
_data = stream.read(1)
return _obj
fromStream = classmethod(fromStream)
# properties
year = property(lambda self: self.lastUpdate.year)
month = property(lambda self: self.lastUpdate.month)
day = property(lambda self: self.lastUpdate.day)
def ignoreErrors(self, value):
"""Update `ignoreErrors` flag on self and all fields"""
self._ignore_errors = value = bool(value)
for _field in self.fields:
_field.ignoreErrors = value
ignoreErrors = property(
lambda self: self._ignore_errors,
ignoreErrors,
doc="""Error processing mode for DBF field value conversion
if set, failing field value conversion will return
``INVALID_VALUE`` instead of raising conversion error.
""")
# object representation
def __repr__(self):
_rv = """\
Version (signature): 0x%02x
Last update: %s
Header length: %d
Record length: %d
Record count: %d
FieldName Type Len Dec
""" % (self.signature, self.lastUpdate, self.headerLength,
self.recordLength, self.recordCount)
_rv += "\n".join(
["%10s %4s %3s %3s" % _fld.fieldInfo() for _fld in self.fields]
)
return _rv
# internal methods
def _addField(self, *defs):
"""Internal variant of the `addField` method.
This method doesn't set `self.changed` field to True.
Return value is a length of the appended records.
Note: this method doesn't modify ``recordLength`` and
``headerLength`` fields. Use `addField` instead of this
method if you don't exactly know what you're doing.
"""
# insure we have dbf.DbfFieldDef instances first (instantiation
# from the tuple could raise an error, in such a case I don't
# wanna add any of the definitions -- all will be ignored)
_defs = []
_recordLength = 0
for _def in defs:
if isinstance(_def, fields.DbfFieldDef):
_obj = _def
else:
(_name, _type, _len, _dec) = (tuple(_def) + (None,) * 4)[:4]
_cls = fields.lookupFor(_type)
_obj = _cls(_name, _len, _dec, ignoreErrors=self._ignore_errors)
_recordLength += _obj.length
_defs.append(_obj)
# and now extend field definitions and
# update record length
self.fields += _defs
return _recordLength
# interface methods
def addField(self, *defs):
"""Add field definition to the header.
Examples:
dbfh.addField(
("name", "C", 20),
dbf.DbfCharacterFieldDef("surname", 20),
dbf.DbfDateFieldDef("birthdate"),
("member", "L"),
)
dbfh.addField(("price", "N", 5, 2))
dbfh.addField(dbf.DbfNumericFieldDef("origprice", 5, 2))
"""
_oldLen = self.recordLength
self.recordLength += self._addField(*defs)
if not _oldLen:
self.recordLength += 1
# XXX: may be just use:
# self.recordeLength += self._addField(*defs) + bool(not _oldLen)
# recalculate headerLength
self.headerLength = 32 + (32 * len(self.fields)) + 1
self.changed = True
def write(self, stream):
"""Encode and write header to the stream."""
stream.seek(0)
stream.write(self.toString())
fields = [_fld.toString() for _fld in self.fields]
stream.write(''.join(fields).encode(sys.getfilesystemencoding()))
stream.write(b'\x0D') # cr at end of all header data
self.changed = False
def toString(self):
"""Returned 32 chars length string with encoded header."""
return struct.pack("<4BI2H",
self.signature,
self.year - 1900,
self.month,
self.day,
self.recordCount,
self.headerLength,
self.recordLength) + (b'\x00' * 20)
# TODO: figure out if bytes(utf-8) is correct here.
def setCurrentDate(self):
"""Update ``self.lastUpdate`` field with current date value."""
self.lastUpdate = datetime.date.today()
def __getitem__(self, item):
"""Return a field definition by numeric index or name string"""
if isinstance(item, str):
_name = item.upper()
for _field in self.fields:
if _field.name == _name:
return _field
else:
raise KeyError(item)
else:
# item must be field index
return self.fields[item]
# vim: et sts=4 sw=4 :
+267
View File
@@ -0,0 +1,267 @@
"""DBF record definition.
"""
"""History (most recent first):
11-feb-2007 [als] __repr__: added special case for invalid field values
10-feb-2007 [als] added .rawFromStream()
30-oct-2006 [als] fix record length in .fromStream()
04-jul-2006 [als] added export declaration
20-dec-2005 [yc] DbfRecord.write() -> DbfRecord._write();
added delete() method.
16-dec-2005 [yc] record definition moved from `dbf`.
"""
__version__ = "$Revision: 1.7 $"[11:-2]
__date__ = "$Date: 2007/02/11 09:05:49 $"[7:-2]
__all__ = ["DbfRecord"]
import sys
from . import utils
class DbfRecord:
"""DBF record.
Instances of this class shouldn't be created manually,
use `dbf.Dbf.newRecord` instead.
Class implements mapping/sequence interface, so
fields could be accessed via their names or indexes
(names is a preferred way to access fields).
Hint:
Use `store` method to save modified record.
Examples:
Add new record to the database:
db = Dbf(filename)
rec = db.newRecord()
rec["FIELD1"] = value1
rec["FIELD2"] = value2
rec.store()
Or the same, but modify existed
(second in this case) record:
db = Dbf(filename)
rec = db[2]
rec["FIELD1"] = value1
rec["FIELD2"] = value2
rec.store()
"""
__slots__ = "dbf", "index", "deleted", "fieldData"
# creation and initialization
def __init__(self, dbf, index=None, deleted=False, data=None):
"""Instance initialization.
Arguments:
dbf:
A `Dbf.Dbf` instance this record belonogs to.
index:
An integer record index or None. If this value is
None, record will be appended to the DBF.
deleted:
Boolean flag indicating whether this record
is a deleted record.
data:
A sequence or None. This is a data of the fields.
If this argument is None, default values will be used.
"""
self.dbf = dbf
# XXX: I'm not sure ``index`` is necessary
self.index = index
self.deleted = deleted
if data is None:
self.fieldData = [_fd.defaultValue for _fd in dbf.header.fields]
else:
self.fieldData = list(data)
# XXX: validate self.index before calculating position?
position = property(lambda self: self.dbf.header.headerLength + \
self.index * self.dbf.header.recordLength)
def rawFromStream(cls, dbf, index):
"""Return raw record contents read from the stream.
Arguments:
dbf:
A `Dbf.Dbf` instance containing the record.
index:
Index of the record in the records' container.
This argument can't be None in this call.
Return value is a string containing record data in DBF format.
"""
# XXX: may be write smth assuming, that current stream
# position is the required one? it could save some
# time required to calculate where to seek in the file
dbf.stream.seek(dbf.header.headerLength +
index * dbf.header.recordLength)
return dbf.stream.read(dbf.header.recordLength)
rawFromStream = classmethod(rawFromStream)
def fromStream(cls, dbf, index):
"""Return a record read from the stream.
Arguments:
dbf:
A `Dbf.Dbf` instance new record should belong to.
index:
Index of the record in the records' container.
This argument can't be None in this call.
Return value is an instance of the current class.
"""
return cls.fromString(dbf, cls.rawFromStream(dbf, index), index)
fromStream = classmethod(fromStream)
def fromString(cls, dbf, string, index=None):
"""Return record read from the string object.
Arguments:
dbf:
A `Dbf.Dbf` instance new record should belong to.
string:
A string new record should be created from.
index:
Index of the record in the container. If this
argument is None, record will be appended.
Return value is an instance of the current class.
"""
return cls(dbf, index, string[0]=="*",
[_fd.decodeFromRecord(string) for _fd in dbf.header.fields])
fromString = classmethod(fromString)
# object representation
def __repr__(self):
_template = "%%%ds: %%s (%%s)" % max([len(_fld)
for _fld in self.dbf.fieldNames])
_rv = []
for _fld in self.dbf.fieldNames:
_val = self[_fld]
if _val is utils.INVALID_VALUE:
_rv.append(_template %
(_fld, "None", "value cannot be decoded"))
else:
_rv.append(_template % (_fld, _val, type(_val)))
return "\n".join(_rv)
# protected methods
def _write(self):
"""Write data to the dbf stream.
Note:
This isn't a public method, it's better to
use 'store' instead publicly.
Be design ``_write`` method should be called
only from the `Dbf` instance.
"""
self._validateIndex(False)
self.dbf.stream.seek(self.position)
self.dbf.stream.write(bytes(self.toString(),
sys.getfilesystemencoding()))
# FIXME: may be move this write somewhere else?
# why we should check this condition for each record?
if self.index == len(self.dbf):
# this is the last record,
# we should write SUB (ASCII 26)
self.dbf.stream.write(b"\x1A")
# utility methods
def _validateIndex(self, allowUndefined=True, checkRange=False):
"""Valid ``self.index`` value.
If ``allowUndefined`` argument is True functions does nothing
in case of ``self.index`` pointing to None object.
"""
if self.index is None:
if not allowUndefined:
raise ValueError("Index is undefined")
elif self.index < 0:
raise ValueError("Index can't be negative (%s)" % self.index)
elif checkRange and self.index <= self.dbf.header.recordCount:
raise ValueError("There are only %d records in the DBF" %
self.dbf.header.recordCount)
# interface methods
def store(self):
"""Store current record in the DBF.
If ``self.index`` is None, this record will be appended to the
records of the DBF this records belongs to; or replaced otherwise.
"""
self._validateIndex()
if self.index is None:
self.index = len(self.dbf)
self.dbf.append(self)
else:
self.dbf[self.index] = self
def delete(self):
"""Mark method as deleted."""
self.deleted = True
def toString(self):
"""Return string packed record values."""
# for (_def, _dat) in zip(self.dbf.header.fields, self.fieldData):
#
return "".join([" *"[self.deleted]] + [
_def.encodeValue(_dat)
for (_def, _dat) in zip(self.dbf.header.fields, self.fieldData)
])
def asList(self):
"""Return a flat list of fields.
Note:
Change of the list's values won't change
real values stored in this object.
"""
return self.fieldData[:]
def asDict(self):
"""Return a dictionary of fields.
Note:
Change of the dicts's values won't change
real values stored in this object.
"""
return dict([_i for _i in zip(self.dbf.fieldNames, self.fieldData)])
def __getitem__(self, key):
"""Return value by field name or field index."""
if isinstance(key, int):
# integer index of the field
return self.fieldData[key]
# assuming string field name
return self.fieldData[self.dbf.indexOfFieldName(key)]
def __setitem__(self, key, value):
"""Set field value by integer index of the field or string name."""
if isinstance(key, int):
# integer index of the field
return self.fieldData[key]
# assuming string field name
self.fieldData[self.dbf.indexOfFieldName(key)] = value
# vim: et sts=4 sw=4 :
+168
View File
@@ -0,0 +1,168 @@
"""String utilities.
TODO:
- allow strings in getDateTime routine;
"""
"""History (most recent first):
11-feb-2007 [als] added INVALID_VALUE
10-feb-2007 [als] allow date strings padded with spaces instead of zeroes
20-dec-2005 [yc] handle long objects in getDate/getDateTime
16-dec-2005 [yc] created from ``strutil`` module.
"""
__version__ = "$Revision: 1.4 $"[11:-2]
__date__ = "$Date: 2007/02/11 08:57:17 $"[7:-2]
import datetime
import time
def unzfill(str):
"""Return a string without ASCII NULs.
This function searchers for the first NUL (ASCII 0) occurrence
and truncates string till that position.
"""
try:
return str[:str.index(b'\0')]
except ValueError:
return str
def getDate(date=None):
"""Return `datetime.date` instance.
Type of the ``date`` argument could be one of the following:
None:
use current date value;
datetime.date:
this value will be returned;
datetime.datetime:
the result of the date.date() will be returned;
string:
assuming "%Y%m%d" or "%y%m%dd" format;
number:
assuming it's a timestamp (returned for example
by the time.time() call;
sequence:
assuming (year, month, day, ...) sequence;
Additionally, if ``date`` has callable ``ticks`` attribute,
it will be used and result of the called would be treated
as a timestamp value.
"""
if date is None:
# use current value
return datetime.date.today()
if isinstance(date, datetime.date):
return date
if isinstance(date, datetime.datetime):
return date.date()
if isinstance(date, (int, float)):
# date is a timestamp
return datetime.date.fromtimestamp(date)
if isinstance(date, str):
date = date.replace(" ", "0")
if len(date) == 6:
# yymmdd
return datetime.date(*time.strptime(date, "%y%m%d")[:3])
# yyyymmdd
return datetime.date(*time.strptime(date, "%Y%m%d")[:3])
if hasattr(date, "__getitem__"):
# a sequence (assuming date/time tuple)
return datetime.date(*date[:3])
return datetime.date.fromtimestamp(date.ticks())
def getDateTime(value=None):
"""Return `datetime.datetime` instance.
Type of the ``value`` argument could be one of the following:
None:
use current date value;
datetime.date:
result will be converted to the `datetime.datetime` instance
using midnight;
datetime.datetime:
``value`` will be returned as is;
string:
*** CURRENTLY NOT SUPPORTED ***;
number:
assuming it's a timestamp (returned for example
by the time.time() call;
sequence:
assuming (year, month, day, ...) sequence;
Additionally, if ``value`` has callable ``ticks`` attribute,
it will be used and result of the called would be treated
as a timestamp value.
"""
if value is None:
# use current value
return datetime.datetime.today()
if isinstance(value, datetime.datetime):
return value
if isinstance(value, datetime.date):
return datetime.datetime.fromordinal(value.toordinal())
if isinstance(value, (int, float)):
# value is a timestamp
return datetime.datetime.fromtimestamp(value)
if isinstance(value, str):
raise NotImplementedError("Strings aren't currently implemented")
if hasattr(value, "__getitem__"):
# a sequence (assuming date/time tuple)
return datetime.datetime(*tuple(value)[:6])
return datetime.datetime.fromtimestamp(value.ticks())
class classproperty(property):
"""Works in the same way as a ``property``, but for the classes."""
def __get__(self, obj, cls):
return self.fget(cls)
class _InvalidValue:
"""Value returned from DBF records when field validation fails
The value is not equal to anything except for itself
and equal to all empty values: None, 0, empty string etc.
In other words, invalid value is equal to None and not equal
to None at the same time.
This value yields zero upon explicit conversion to a number type,
empty string for string types, and False for boolean.
"""
def __eq__(self, other):
return not other
def __ne__(self, other):
return not (other is self)
def __bool__(self):
return False
def __int__(self):
return 0
__long__ = __int__
def __float__(self):
return 0.0
def __str__(self):
return ""
def __repr__(self):
return "<INVALID>"
# invalid value is a constant singleton
INVALID_VALUE = _InvalidValue()
# vim: set et sts=4 sw=4 :
+13
View File
@@ -0,0 +1,13 @@
from io import BytesIO, StringIO
def normalize_input(stream):
"""
Accept either a str/bytes stream or a file-like object and always return a
file-like object.
"""
if isinstance(stream, str):
return StringIO(stream)
elif isinstance(stream, bytes):
return BytesIO(stream)
return stream
-8
View File
@@ -1,8 +0,0 @@
""" Tablib.
"""
from tablib.core import (
Databook, Dataset, detect, import_set,
InvalidDatasetType, InvalidDimensions, UnsupportedFormat
)
-49
View File
@@ -1,49 +0,0 @@
# -*- coding: utf-8 -*-
"""
tablib.compat
~~~~~~~~~~~~~
Tablib compatiblity module.
"""
import sys
is_py3 = (sys.version_info[0] > 2)
try:
from collections import OrderedDict
except ImportError:
from tablib.packages.ordereddict import OrderedDict
if is_py3:
from io import BytesIO
import tablib.packages.xlwt3 as xlwt
from tablib.packages import markup3 as markup
from tablib.packages import openpyxl3 as openpyxl
# py3 mappings
ifilter = filter
xrange = range
unicode = str
bytes = bytes
basestring = str
else:
from cStringIO import StringIO as BytesIO
import tablib.packages.xlwt as xlwt
from tablib.packages import markup
from itertools import ifilter
from tablib.packages import openpyxl
# py2 mappings
xrange = xrange
unicode = unicode
bytes = str
basestring = basestring
-14
View File
@@ -1,14 +0,0 @@
# -*- coding: utf-8 -*-
""" Tablib - formats
"""
from . import _csv as csv
from . import _json as json
from . import _xls as xls
from . import _yaml as yaml
from . import _tsv as tsv
from . import _html as html
from . import _xlsx as xlsx
available = (json, xls, yaml, csv, tsv, html, xlsx)
-70
View File
@@ -1,70 +0,0 @@
# -*- coding: utf-8 -*-
""" Tablib - CSV Support.
"""
import sys
if sys.version_info[0] > 2:
is_py3 = True
from io import StringIO
import csv
else:
is_py3 = False
from cStringIO import StringIO
import tablib.packages.unicodecsv as csv
import os
import tablib
title = 'csv'
extentions = ('csv',)
DEFAULT_ENCODING = 'utf-8'
def export_set(dataset):
"""Returns CSV representation of Dataset."""
stream = StringIO()
if is_py3:
_csv = csv.writer(stream)
else:
_csv = csv.writer(stream, encoding=DEFAULT_ENCODING)
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()
if is_py3:
rows = csv.reader(in_stream.splitlines())
else:
rows = csv.reader(in_stream.splitlines(), encoding=DEFAULT_ENCODING)
for i, row in enumerate(rows):
if (i == 0) and (headers):
dset.headers = row
else:
dset.append(row)
def detect(stream):
"""Returns True if given stream is valid CSV."""
try:
rows = dialect = csv.Sniffer().sniff(stream)
return True
except csv.Error:
return False
-60
View File
@@ -1,60 +0,0 @@
# -*- coding: utf-8 -*-
""" Tablib - HTML export support.
"""
import sys
if sys.version_info[0] > 2:
from io import StringIO
from tablib.packages import markup3 as markup
else:
from cStringIO import StringIO
from tablib.packages import markup
import tablib
BOOK_ENDINGS = 'h3'
title = 'html'
extentions = ('html', )
def export_set(dataset):
"""HTML representation of a Dataset."""
stream = StringIO()
page = markup.page()
page.table.open()
if dataset.headers is not None:
page.thead.open()
headers = markup.oneliner.th(dataset.headers)
page.tr(headers)
page.thead.close()
for row in dataset:
html_row = markup.oneliner.td(row)
page.tr(html_row)
page.table.close()
stream.writelines(str(page))
return stream.getvalue()
def export_book(databook):
"""HTML representation of a Databook."""
stream = StringIO()
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')
return stream.getvalue()
-54
View File
@@ -1,54 +0,0 @@
# -*- coding: utf-8 -*-
""" Tablib - JSON Support
"""
import tablib
import sys
if sys.version_info[:2] > (2, 5):
from tablib.packages import anyjson
else:
from tablib.packages import anyjson25 as anyjson
title = 'json'
extentions = ('json', 'jsn')
def export_set(dataset):
"""Returns JSON representation of Dataset."""
return anyjson.serialize(dataset.dict)
def export_book(databook):
"""Returns JSON representation of Databook."""
return anyjson.serialize(databook._package())
def import_set(dset, in_stream):
"""Returns dataset from JSON stream."""
dset.wipe()
dset.dict = anyjson.deserialize(in_stream)
def import_book(dbook, in_stream):
"""Returns databook from JSON stream."""
dbook.wipe()
for sheet in anyjson.deserialize(in_stream):
data = tablib.Dataset()
data.title = sheet['title']
data.dict = sheet['data']
dbook.add_sheet(data)
def detect(stream):
"""Returns True if given stream is valid JSON."""
try:
anyjson.deserialize(stream)
return True
except ValueError:
return False
-56
View File
@@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
""" Tablib - TSV (Tab Separated Values) Support.
"""
import sys
if sys.version_info[0] > 2:
from io import StringIO
else:
from cStringIO import StringIO
import csv
import os
import tablib
title = 'tsv'
extentions = ('tsv',)
def export_set(dataset):
"""Returns a TSV representation of Dataset."""
stream = StringIO()
_tsv = csv.writer(stream, delimiter='\t')
for row in dataset._package(dicts=False):
_tsv.writerow(row)
return stream.getvalue()
def import_set(dset, in_stream, headers=True):
"""Returns dataset from TSV stream."""
dset.wipe()
rows = csv.reader(in_stream.split('\r\n'), delimiter='\t')
for i, row in enumerate(rows):
# Skip empty rows
if not row:
continue
if (i == 0) and (headers):
dset.headers = row
else:
dset.append(row)
def detect(stream):
"""Returns True if given stream is valid TSV."""
try:
rows = dialect = csv.Sniffer().sniff(stream, delimiters='\t')
return True
except csv.Error:
return False
-82
View File
@@ -1,82 +0,0 @@
# -*- coding: utf-8 -*-
""" Tablib - XLS Support.
"""
import sys
from tablib.compat import BytesIO, xlwt
title = 'xls'
extentions = ('xls',)
# special styles
wrap = xlwt.easyxf("alignment: wrap on")
bold = xlwt.easyxf("font: bold on")
def export_set(dataset):
"""Returns XLS representation of Dataset."""
wb = xlwt.Workbook(encoding='utf8')
ws = wb.add_sheet(dataset.title if dataset.title else 'Tablib Dataset')
dset_sheet(dataset, ws)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
def export_book(databook):
"""Returns XLS representation of DataBook."""
wb = xlwt.Workbook(encoding='utf8')
for i, dset in enumerate(databook._datasets):
ws = wb.add_sheet(dset.title if dset.title else 'Sheet%s' % (i))
dset_sheet(dset, ws)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
def dset_sheet(dataset, ws):
"""Completes given worksheet from given Dataset."""
_package = dataset._package(dicts=False)
for i, sep in enumerate(dataset._separators):
_offset = i
_package.insert((sep[0] + _offset), (sep[1],))
for i, row in enumerate(_package):
for j, col in enumerate(row):
# bold headers
if (i == 0) and dataset.headers:
ws.write(i, j, col, bold)
# frozen header row
ws.panes_frozen = True
ws.horz_split_pos = 1
# bold separators
elif len(row) < dataset.width:
ws.write(i, j, col, bold)
# wrap the rest
else:
try:
if '\n' in col:
ws.write(i, j, col, wrap)
else:
ws.write(i, j, col)
except TypeError:
ws.write(i, j, col)
-101
View File
@@ -1,101 +0,0 @@
# -*- coding: utf-8 -*-
""" Tablib - XLSX Support.
"""
import sys
if sys.version_info[0] > 2:
from io import BytesIO
else:
from cStringIO import StringIO as BytesIO
from tablib.compat import openpyxl
Workbook = openpyxl.workbook.Workbook
ExcelWriter = openpyxl.writer.excel.ExcelWriter
get_column_letter = openpyxl.cell.get_column_letter
from tablib.compat import unicode
title = 'xlsx'
extentions = ('xlsx',)
def export_set(dataset):
"""Returns XLSX representation of Dataset."""
wb = Workbook()
ws = wb.worksheets[0]
ws.title = dataset.title if dataset.title else 'Tablib Dataset'
dset_sheet(dataset, ws)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
def export_book(databook):
"""Returns XLSX representation of DataBook."""
wb = Workbook()
ew = ExcelWriter(workbook = wb)
for i, dset in enumerate(databook._datasets):
ws = wb.create_sheet()
ws.title = dset.title if dset.title else 'Sheet%s' % (i)
dset_sheet(dset, ws)
stream = BytesIO()
ew.save(stream)
return stream.getvalue()
def dset_sheet(dataset, ws):
"""Completes given worksheet from given Dataset."""
_package = dataset._package(dicts=False)
for i, sep in enumerate(dataset._separators):
_offset = i
_package.insert((sep[0] + _offset), (sep[1],))
for i, row in enumerate(_package):
row_number = i + 1
for j, col in enumerate(row):
col_idx = get_column_letter(j + 1)
# bold headers
if (row_number == 1) and dataset.headers:
# ws.cell('%s%s'%(col_idx, row_number)).value = unicode(
# '%s' % col, errors='ignore')
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(col)
style = ws.get_style('%s%s' % (col_idx, row_number))
style.font.bold = True
ws.freeze_panes = '%s%s' % (col_idx, row_number)
# bold separators
elif len(row) < dataset.width:
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(
'%s' % col, errors='ignore')
style = ws.get_style('%s%s' % (col_idx, row_number))
style.font.bold = True
# wrap the rest
else:
try:
if '\n' in col:
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(
'%s' % col, errors='ignore')
style = ws.get_style('%s%s' % (col_idx, row_number))
style.alignment.wrap_text
else:
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(
'%s' % col, errors='ignore')
except TypeError:
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(col)
-63
View File
@@ -1,63 +0,0 @@
# -*- coding: utf-8 -*-
""" Tablib - YAML Support.
"""
import sys
try:
import yaml
except ImportError:
if sys.version_info[0] > 2:
import tablib.packages.yaml3 as yaml
else:
import tablib.packages.yaml as 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.Dataset()
data.title = sheet['title']
data.dict = sheet['data']
dbook.add_sheet(data)
def detect(stream):
"""Returns True if given stream is valid YAML."""
try:
_yaml = yaml.load(stream)
if isinstance(_yaml, (list, tuple, dict)):
return True
else:
return False
except yaml.parser.ParserError:
return False
-117
View File
@@ -1,117 +0,0 @@
"""
Wraps the best available JSON implementation available in a common interface
"""
__version__ = "0.2.0"
__author__ = "Rune Halvorsen <runefh@gmail.com>"
__homepage__ = "http://bitbucket.org/runeh/anyjson/"
__docformat__ = "restructuredtext"
"""
.. function:: serialize(obj)
Serialize the object to JSON.
.. function:: deserialize(str)
Deserialize JSON-encoded object to a Python object.
.. function:: force_implementation(name)
Load a specific json module. This is useful for testing and not much else
.. attribute:: implementation
The json implementation object. This is probably not useful to you,
except to get the name of the implementation in use. The name is
available through `implementation.name`.
"""
import sys
implementation = None
"""
.. data:: _modules
List of known json modules, and the names of their serialize/unserialize
methods, as well as the exception they throw. Exception can be either
an exception class or a string.
"""
_modules = [("cjson", "encode", "EncodeError", "decode", "DecodeError"),
("jsonlib2", "write", "WriteError", "read", "ReadError"),
("jsonlib", "write", "WriteError", "read", "ReadError"),
("simplejson", "dumps", TypeError, "loads", ValueError),
("json", "dumps", TypeError, "loads", ValueError),
("django.utils.simplejson", "dumps", TypeError, "loads",
ValueError)]
_fields = ("modname", "encoder", "encerror", "decoder", "decerror")
class _JsonImplementation(object):
"""Incapsulates a JSON implementation"""
def __init__(self, modspec):
modinfo = dict(list(zip(_fields, modspec)))
# No try block. We want importerror to end up at caller
module = self._attempt_load(modinfo["modname"])
self.implementation = modinfo["modname"]
self._encode = getattr(module, modinfo["encoder"])
self._decode = getattr(module, modinfo["decoder"])
self._encode_error = modinfo["encerror"]
self._decode_error = modinfo["decerror"]
if isinstance(modinfo["encerror"], str):
self._encode_error = getattr(module, modinfo["encerror"])
if isinstance(modinfo["decerror"], str):
self._decode_error = getattr(module, modinfo["decerror"])
self.name = modinfo["modname"]
def _attempt_load(self, modname):
"""Attempt to load module name modname, returning it on success,
throwing ImportError if module couldn't be imported"""
__import__(modname)
return sys.modules[modname]
def serialize(self, data):
"""Serialize the datastructure to json. Returns a string. Raises
TypeError if the object could not be serialized."""
try:
return self._encode(data)
except self._encode_error as exc:
raise TypeError(*exc.args)
def deserialize(self, s):
"""deserialize the string to python data types. Raises
ValueError if the string vould not be parsed."""
try:
return self._decode(s)
except self._decode_error as exc:
raise ValueError(*exc.args)
def force_implementation(modname):
"""Forces anyjson to use a specific json module if it's available"""
global implementation
for name, spec in [(e[0], e) for e in _modules]:
if name == modname:
implementation = _JsonImplementation(spec)
return
raise ImportError("No module named: %s" % modname)
for modspec in _modules:
try:
implementation = _JsonImplementation(modspec)
break
except ImportError:
pass
else:
raise ImportError("No supported JSON module found")
serialize = lambda value: implementation.serialize(value)
deserialize = lambda value: implementation.deserialize(value)
-118
View File
@@ -1,118 +0,0 @@
u"""
Wraps the best available JSON implementation available in a common interface
"""
__version__ = u"0.2.0"
__author__ = u"Rune Halvorsen <runefh@gmail.com>"
__homepage__ = u"http://bitbucket.org/runeh/anyjson/"
__docformat__ = u"restructuredtext"
u"""
.. function:: serialize(obj)
Serialize the object to JSON.
.. function:: deserialize(str)
Deserialize JSON-encoded object to a Python object.
.. function:: force_implementation(name)
Load a specific json module. This is useful for testing and not much else
.. attribute:: implementation
The json implementation object. This is probably not useful to you,
except to get the name of the implementation in use. The name is
available through `implementation.name`.
"""
import sys
from itertools import izip
implementation = None
u"""
.. data:: _modules
List of known json modules, and the names of their serialize/unserialize
methods, as well as the exception they throw. Exception can be either
an exception class or a string.
"""
_modules = [(u"cjson", u"encode", u"EncodeError", u"decode", u"DecodeError"),
(u"jsonlib2", u"write", u"WriteError", u"read", u"ReadError"),
(u"jsonlib", u"write", u"WriteError", u"read", u"ReadError"),
(u"simplejson", u"dumps", TypeError, u"loads", ValueError),
(u"json", u"dumps", TypeError, u"loads", ValueError),
(u"django.utils.simplejson", u"dumps", TypeError, u"loads",
ValueError)]
_fields = (u"modname", u"encoder", u"encerror", u"decoder", u"decerror")
class _JsonImplementation(object):
u"""Incapsulates a JSON implementation"""
def __init__(self, modspec):
modinfo = dict(list(izip(_fields, modspec)))
# No try block. We want importerror to end up at caller
module = self._attempt_load(modinfo[u"modname"])
self.implementation = modinfo[u"modname"]
self._encode = getattr(module, modinfo[u"encoder"])
self._decode = getattr(module, modinfo[u"decoder"])
self._encode_error = modinfo[u"encerror"]
self._decode_error = modinfo[u"decerror"]
if isinstance(modinfo[u"encerror"], unicode):
self._encode_error = getattr(module, modinfo[u"encerror"])
if isinstance(modinfo[u"decerror"], unicode):
self._decode_error = getattr(module, modinfo[u"decerror"])
self.name = modinfo[u"modname"]
def _attempt_load(self, modname):
u"""Attempt to load module name modname, returning it on success,
throwing ImportError if module couldn't be imported"""
__import__(modname)
return sys.modules[modname]
def serialize(self, data):
u"""Serialize the datastructure to json. Returns a string. Raises
TypeError if the object could not be serialized."""
try:
return self._encode(data)
except self._encode_error, exc:
raise TypeError(*exc.args)
def deserialize(self, s):
u"""deserialize the string to python data types. Raises
ValueError if the string vould not be parsed."""
try:
return self._decode(s)
except self._decode_error, exc:
raise ValueError(*exc.args)
def force_implementation(modname):
u"""Forces anyjson to use a specific json module if it's available"""
global implementation
for name, spec in [(e[0], e) for e in _modules]:
if name == modname:
implementation = _JsonImplementation(spec)
return
raise ImportError(u"No module named: %s" % modname)
for modspec in _modules:
try:
implementation = _JsonImplementation(modspec)
break
except ImportError:
pass
else:
raise ImportError(u"No supported JSON module found")
serialize = lambda value: implementation.serialize(value)
deserialize = lambda value: implementation.deserialize(value)
-484
View File
@@ -1,484 +0,0 @@
# This code is in the public domain, it comes
# with absolutely no warranty and you can do
# absolutely whatever you want with it.
__date__ = '17 May 2007'
__version__ = '1.7'
__doc__= """
This is markup.py - a Python module that attempts to
make it easier to generate HTML/XML from a Python program
in an intuitive, lightweight, customizable and pythonic way.
The code is in the public domain.
Version: %s as of %s.
Documentation and further info is at http://markup.sourceforge.net/
Please send bug reports, feature requests, enhancement
ideas or questions to nogradi at gmail dot com.
Installation: drop markup.py somewhere into your Python path.
""" % ( __version__, __date__ )
import string
class element:
"""This class handles the addition of a new element."""
def __init__( self, tag, case='lower', parent=None ):
self.parent = parent
if case == 'lower':
self.tag = tag.lower( )
else:
self.tag = tag.upper( )
def __call__( self, *args, **kwargs ):
if len( args ) > 1:
raise ArgumentError( self.tag )
# if class_ was defined in parent it should be added to every element
if self.parent is not None and self.parent.class_ is not None:
if 'class_' not in kwargs:
kwargs['class_'] = self.parent.class_
if self.parent is None and len( args ) == 1:
x = [ self.render( self.tag, False, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ]
return '\n'.join( x )
elif self.parent is None and len( args ) == 0:
x = [ self.render( self.tag, True, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ]
return '\n'.join( x )
if self.tag in self.parent.twotags:
for myarg, mydict in _argsdicts( args, kwargs ):
self.render( self.tag, False, myarg, mydict )
elif self.tag in self.parent.onetags:
if len( args ) == 0:
for myarg, mydict in _argsdicts( args, kwargs ):
self.render( self.tag, True, myarg, mydict ) # here myarg is always None, because len( args ) = 0
else:
raise ClosingError( self.tag )
elif self.parent.mode == 'strict_html' and self.tag in self.parent.deptags:
raise DeprecationError( self.tag )
else:
raise InvalidElementError( self.tag, self.parent.mode )
def render( self, tag, single, between, kwargs ):
"""Append the actual tags to content."""
out = "<%s" % tag
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'
out = "%s %s=\"%s\"" % ( out, key, escape( value ) )
else:
out = "%s %s" % ( out, key )
if between is not None:
out = "%s>%s</%s>" % ( out, between, tag )
else:
if single:
out = "%s />" % out
else:
out = "%s>" % out
if self.parent is not None:
self.parent.content.append( out )
else:
return out
def close( self ):
"""Append a closing tag unless element has only opening tag."""
if self.tag in self.parent.twotags:
self.parent.content.append( "</%s>" % self.tag )
elif self.tag in self.parent.onetags:
raise ClosingError( self.tag )
elif self.parent.mode == 'strict_html' and self.tag in self.parent.deptags:
raise DeprecationError( self.tag )
def open( self, **kwargs ):
"""Append an opening tag."""
if self.tag in self.parent.twotags or self.tag in self.parent.onetags:
self.render( self.tag, False, None, kwargs )
elif self.mode == 'strict_html' and self.tag in self.parent.deptags:
raise DeprecationError( self.tag )
class page:
"""This is our main class representing a document. Elements are added
as attributes of an instance of this class."""
def __init__( self, mode='strict_html', case='lower', onetags=None, twotags=None, separator='\n', class_=None ):
"""Stuff that effects the whole document.
mode -- 'strict_html' for HTML 4.01 (default)
'html' alias for 'strict_html'
'loose_html' to allow some deprecated elements
'xml' to allow arbitrary elements
case -- 'lower' element names will be printed in lower case (default)
'upper' they will be printed in upper case
onetags -- list or tuple of valid elements with opening tags only
twotags -- list or tuple of valid elements with both opening and closing tags
these two keyword arguments may be used to select
the set of valid elements in 'xml' mode
invalid elements will raise appropriate exceptions
separator -- string to place between added elements, defaults to newline
class_ -- a class that will be added to every element if defined"""
valid_onetags = [ "AREA", "BASE", "BR", "COL", "FRAME", "HR", "IMG", "INPUT", "LINK", "META", "PARAM" ]
valid_twotags = [ "A", "ABBR", "ACRONYM", "ADDRESS", "B", "BDO", "BIG", "BLOCKQUOTE", "BODY", "BUTTON",
"CAPTION", "CITE", "CODE", "COLGROUP", "DD", "DEL", "DFN", "DIV", "DL", "DT", "EM", "FIELDSET",
"FORM", "FRAMESET", "H1", "H2", "H3", "H4", "H5", "H6", "HEAD", "HTML", "I", "IFRAME", "INS",
"KBD", "LABEL", "LEGEND", "LI", "MAP", "NOFRAMES", "NOSCRIPT", "OBJECT", "OL", "OPTGROUP",
"OPTION", "P", "PRE", "Q", "SAMP", "SCRIPT", "SELECT", "SMALL", "SPAN", "STRONG", "STYLE",
"SUB", "SUP", "TABLE", "TBODY", "TD", "TEXTAREA", "TFOOT", "TH", "THEAD", "TITLE", "TR",
"TT", "UL", "VAR" ]
deprecated_onetags = [ "BASEFONT", "ISINDEX" ]
deprecated_twotags = [ "APPLET", "CENTER", "DIR", "FONT", "MENU", "S", "STRIKE", "U" ]
self.header = [ ]
self.content = [ ]
self.footer = [ ]
self.case = case
self.separator = separator
# init( ) sets it to True so we know that </body></html> has to be printed at the end
self._full = False
self.class_= class_
if mode == 'strict_html' or mode == 'html':
self.onetags = valid_onetags
self.onetags += map( string.lower, self.onetags )
self.twotags = valid_twotags
self.twotags += map( string.lower, self.twotags )
self.deptags = deprecated_onetags + deprecated_twotags
self.deptags += map( string.lower, self.deptags )
self.mode = 'strict_html'
elif mode == 'loose_html':
self.onetags = valid_onetags + deprecated_onetags
self.onetags += map( string.lower, self.onetags )
self.twotags = valid_twotags + deprecated_twotags
self.twotags += map( string.lower, self.twotags )
self.mode = mode
elif mode == 'xml':
if onetags and twotags:
self.onetags = onetags
self.twotags = twotags
elif ( onetags and not twotags ) or ( twotags and not onetags ):
raise CustomizationError( )
else:
self.onetags = russell( )
self.twotags = russell( )
self.mode = mode
else:
raise ModeError( mode )
def __getattr__( self, attr ):
if attr.startswith("__") and attr.endswith("__"):
raise AttributeError, attr
return element( attr, case=self.case, parent=self )
def __str__( self ):
if self._full and ( self.mode == 'strict_html' or self.mode == 'loose_html' ):
end = [ '</body>', '</html>' ]
else:
end = [ ]
return self.separator.join( self.header + self.content + self.footer + end )
def __call__( self, escape=False ):
"""Return the document as a string.
escape -- False print normally
True replace < and > by &lt; and &gt;
the default escape sequences in most browsers"""
if escape:
return _escape( self.__str__( ) )
else:
return self.__str__( )
def add( self, text ):
"""This is an alias to addcontent."""
self.addcontent( text )
def addfooter( self, text ):
"""Add some text to the bottom of the document"""
self.footer.append( text )
def addheader( self, text ):
"""Add some text to the top of the document"""
self.header.append( text )
def addcontent( self, text ):
"""Add some text to the main part of the document"""
self.content.append( text )
def init( self, lang='en', css=None, metainfo=None, title=None, header=None,
footer=None, charset=None, encoding=None, doctype=None, bodyattrs=None, script=None ):
"""This method is used for complete documents with appropriate
doctype, encoding, title, etc information. For an HTML/XML snippet
omit this method.
lang -- language, usually a two character string, will appear
as <html lang='en'> in html mode (ignored in xml mode)
css -- Cascading Style Sheet filename as a string or a list of
strings for multiple css files (ignored in xml mode)
metainfo -- a dictionary in the form { 'name':'content' } to be inserted
into meta element(s) as <meta name='name' content='content'>
(ignored in xml mode)
bodyattrs --a dictionary in the form { 'key':'value', ... } which will be added
as attributes of the <body> element as <body key='value' ... >
(ignored in xml mode)
script -- dictionary containing src:type pairs, <script type='text/type' src=src></script>
title -- the title of the document as a string to be inserted into
a title element as <title>my title</title> (ignored in xml mode)
header -- some text to be inserted right after the <body> element
(ignored in xml mode)
footer -- some text to be inserted right before the </body> element
(ignored in xml mode)
charset -- a string defining the character set, will be inserted into a
<meta http-equiv='Content-Type' content='text/html; charset=myset'>
element (ignored in xml mode)
encoding -- a string defining the encoding, will be put into to first line of
the document as <?xml version='1.0' encoding='myencoding' ?> in
xml mode (ignored in html mode)
doctype -- the document type string, defaults to
<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>
in html mode (ignored in xml mode)"""
self._full = True
if self.mode == 'strict_html' or self.mode == 'loose_html':
if doctype is None:
doctype = "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>"
self.header.append( doctype )
self.html( lang=lang )
self.head( )
if charset is not None:
self.meta( http_equiv='Content-Type', content="text/html; charset=%s" % charset )
if metainfo is not None:
self.metainfo( metainfo )
if css is not None:
self.css( css )
if title is not None:
self.title( title )
if script is not None:
self.scripts( script )
self.head.close()
if bodyattrs is not None:
self.body( **bodyattrs )
else:
self.body( )
if header is not None:
self.content.append( header )
if footer is not None:
self.footer.append( footer )
elif self.mode == 'xml':
if doctype is None:
if encoding is not None:
doctype = "<?xml version='1.0' encoding='%s' ?>" % encoding
else:
doctype = "<?xml version='1.0' ?>"
self.header.append( doctype )
def css( self, filelist ):
"""This convenience function is only useful for html.
It adds css stylesheet(s) to the document via the <link> element."""
if isinstance( filelist, basestring ):
self.link( href=filelist, rel='stylesheet', type='text/css', media='all' )
else:
for file in filelist:
self.link( href=file, rel='stylesheet', type='text/css', media='all' )
def metainfo( self, mydict ):
"""This convenience function is only useful for html.
It adds meta information via the <meta> element, the argument is
a dictionary of the form { 'name':'content' }."""
if isinstance( mydict, dict ):
for name, content in mydict.iteritems( ):
self.meta( name=name, content=content )
else:
raise TypeError, "Metainfo should be called with a dictionary argument of name:content pairs."
def scripts( self, mydict ):
"""Only useful in html, mydict is dictionary of src:type pairs will
be rendered as <script type='text/type' src=src></script>"""
if isinstance( mydict, dict ):
for src, type in mydict.iteritems( ):
self.script( '', src=src, type='text/%s' % type )
else:
raise TypeError, "Script should be given a dictionary of src:type pairs."
class _oneliner:
"""An instance of oneliner returns a string corresponding to one element.
This class can be used to write 'oneliners' that return a string
immediately so there is no need to instantiate the page class."""
def __init__( self, case='lower' ):
self.case = case
def __getattr__( self, attr ):
if attr.startswith("__") and attr.endswith("__"):
raise AttributeError, attr
return element( attr, case=self.case, parent=None )
oneliner = _oneliner( case='lower' )
upper_oneliner = _oneliner( case='upper' )
def _argsdicts( args, mydict ):
"""A utility generator that pads argument list and dictionary values, will only be called with len( args ) = 0, 1."""
if len( args ) == 0:
args = None,
elif len( args ) == 1:
args = _totuple( args[0] )
else:
raise Exception, "We should have never gotten here."
mykeys = mydict.keys( )
myvalues = map( _totuple, mydict.values( ) )
maxlength = max( map( len, [ args ] + myvalues ) )
for i in xrange( maxlength ):
thisdict = { }
for key, value in zip( mykeys, myvalues ):
try:
thisdict[ key ] = value[i]
except IndexError:
thisdict[ key ] = value[-1]
try:
thisarg = args[i]
except IndexError:
thisarg = args[-1]
yield thisarg, thisdict
def _totuple( x ):
"""Utility stuff to convert string, int, float, None or anything to a usable tuple."""
if isinstance( x, basestring ):
out = x,
elif isinstance( x, ( int, float ) ):
out = str( x ),
elif x is None:
out = None,
else:
out = tuple( x )
return out
def escape( text, newline=False ):
"""Escape special html characters."""
if isinstance( text, basestring ):
if '&' in text:
text = text.replace( '&', '&amp;' )
if '>' in text:
text = text.replace( '>', '&gt;' )
if '<' in text:
text = text.replace( '<', '&lt;' )
if '\"' in text:
text = text.replace( '\"', '&quot;' )
if '\'' in text:
text = text.replace( '\'', '&quot;' )
if newline:
if '\n' in text:
text = text.replace( '\n', '<br>' )
return text
_escape = escape
def unescape( text ):
"""Inverse of escape."""
if isinstance( text, basestring ):
if '&amp;' in text:
text = text.replace( '&amp;', '&' )
if '&gt;' in text:
text = text.replace( '&gt;', '>' )
if '&lt;' in text:
text = text.replace( '&lt;', '<' )
if '&quot;' in text:
text = text.replace( '&quot;', '\"' )
return text
class dummy:
"""A dummy class for attaching attributes."""
pass
doctype = dummy( )
doctype.frameset = "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/html4/frameset.dtd'>"
doctype.strict = "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>"
doctype.loose = "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>"
class russell:
"""A dummy class that contains anything."""
def __contains__( self, item ):
return True
class MarkupError( Exception ):
"""All our exceptions subclass this."""
def __str__( self ):
return self.message
class ClosingError( MarkupError ):
def __init__( self, tag ):
self.message = "The element '%s' does not accept non-keyword arguments (has no closing tag)." % tag
class OpeningError( MarkupError ):
def __init__( self, tag ):
self.message = "The element '%s' can not be opened." % tag
class ArgumentError( MarkupError ):
def __init__( self, tag ):
self.message = "The element '%s' was called with more than one non-keyword argument." % tag
class InvalidElementError( MarkupError ):
def __init__( self, tag, mode ):
self.message = "The element '%s' is not valid for your mode '%s'." % ( tag, mode )
class DeprecationError( MarkupError ):
def __init__( self, tag ):
self.message = "The element '%s' is deprecated, instantiate markup.page with mode='loose_html' to allow it." % tag
class ModeError( MarkupError ):
def __init__( self, mode ):
self.message = "Mode '%s' is invalid, possible values: strict_html, loose_html, xml." % mode
class CustomizationError( MarkupError ):
def __init__( self ):
self.message = "If you customize the allowed elements, you must define both types 'onetags' and 'twotags'."
if __name__ == '__main__':
print __doc__
-484
View File
@@ -1,484 +0,0 @@
# This code is in the public domain, it comes
# with absolutely no warranty and you can do
# absolutely whatever you want with it.
__date__ = '17 May 2007'
__version__ = '1.7'
__doc__= """
This is markup.py - a Python module that attempts to
make it easier to generate HTML/XML from a Python program
in an intuitive, lightweight, customizable and pythonic way.
The code is in the public domain.
Version: %s as of %s.
Documentation and further info is at http://markup.sourceforge.net/
Please send bug reports, feature requests, enhancement
ideas or questions to nogradi at gmail dot com.
Installation: drop markup.py somewhere into your Python path.
""" % ( __version__, __date__ )
import string
class element:
"""This class handles the addition of a new element."""
def __init__( self, tag, case='lower', parent=None ):
self.parent = parent
if case == 'lower':
self.tag = tag.lower( )
else:
self.tag = tag.upper( )
def __call__( self, *args, **kwargs ):
if len( args ) > 1:
raise ArgumentError( self.tag )
# if class_ was defined in parent it should be added to every element
if self.parent is not None and self.parent.class_ is not None:
if 'class_' not in kwargs:
kwargs['class_'] = self.parent.class_
if self.parent is None and len( args ) == 1:
x = [ self.render( self.tag, False, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ]
return '\n'.join( x )
elif self.parent is None and len( args ) == 0:
x = [ self.render( self.tag, True, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ]
return '\n'.join( x )
if self.tag in self.parent.twotags:
for myarg, mydict in _argsdicts( args, kwargs ):
self.render( self.tag, False, myarg, mydict )
elif self.tag in self.parent.onetags:
if len( args ) == 0:
for myarg, mydict in _argsdicts( args, kwargs ):
self.render( self.tag, True, myarg, mydict ) # here myarg is always None, because len( args ) = 0
else:
raise ClosingError( self.tag )
elif self.parent.mode == 'strict_html' and self.tag in self.parent.deptags:
raise DeprecationError( self.tag )
else:
raise InvalidElementError( self.tag, self.parent.mode )
def render( self, tag, single, between, kwargs ):
"""Append the actual tags to content."""
out = "<%s" % tag
for key, value in kwargs.items( ):
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'
out = "%s %s=\"%s\"" % ( out, key, escape( value ) )
else:
out = "%s %s" % ( out, key )
if between is not None:
out = "%s>%s</%s>" % ( out, between, tag )
else:
if single:
out = "%s />" % out
else:
out = "%s>" % out
if self.parent is not None:
self.parent.content.append( out )
else:
return out
def close( self ):
"""Append a closing tag unless element has only opening tag."""
if self.tag in self.parent.twotags:
self.parent.content.append( "</%s>" % self.tag )
elif self.tag in self.parent.onetags:
raise ClosingError( self.tag )
elif self.parent.mode == 'strict_html' and self.tag in self.parent.deptags:
raise DeprecationError( self.tag )
def open( self, **kwargs ):
"""Append an opening tag."""
if self.tag in self.parent.twotags or self.tag in self.parent.onetags:
self.render( self.tag, False, None, kwargs )
elif self.mode == 'strict_html' and self.tag in self.parent.deptags:
raise DeprecationError( self.tag )
class page:
"""This is our main class representing a document. Elements are added
as attributes of an instance of this class."""
def __init__( self, mode='strict_html', case='lower', onetags=None, twotags=None, separator='\n', class_=None ):
"""Stuff that effects the whole document.
mode -- 'strict_html' for HTML 4.01 (default)
'html' alias for 'strict_html'
'loose_html' to allow some deprecated elements
'xml' to allow arbitrary elements
case -- 'lower' element names will be printed in lower case (default)
'upper' they will be printed in upper case
onetags -- list or tuple of valid elements with opening tags only
twotags -- list or tuple of valid elements with both opening and closing tags
these two keyword arguments may be used to select
the set of valid elements in 'xml' mode
invalid elements will raise appropriate exceptions
separator -- string to place between added elements, defaults to newline
class_ -- a class that will be added to every element if defined"""
valid_onetags = [ "AREA", "BASE", "BR", "COL", "FRAME", "HR", "IMG", "INPUT", "LINK", "META", "PARAM" ]
valid_twotags = [ "A", "ABBR", "ACRONYM", "ADDRESS", "B", "BDO", "BIG", "BLOCKQUOTE", "BODY", "BUTTON",
"CAPTION", "CITE", "CODE", "COLGROUP", "DD", "DEL", "DFN", "DIV", "DL", "DT", "EM", "FIELDSET",
"FORM", "FRAMESET", "H1", "H2", "H3", "H4", "H5", "H6", "HEAD", "HTML", "I", "IFRAME", "INS",
"KBD", "LABEL", "LEGEND", "LI", "MAP", "NOFRAMES", "NOSCRIPT", "OBJECT", "OL", "OPTGROUP",
"OPTION", "P", "PRE", "Q", "SAMP", "SCRIPT", "SELECT", "SMALL", "SPAN", "STRONG", "STYLE",
"SUB", "SUP", "TABLE", "TBODY", "TD", "TEXTAREA", "TFOOT", "TH", "THEAD", "TITLE", "TR",
"TT", "UL", "VAR" ]
deprecated_onetags = [ "BASEFONT", "ISINDEX" ]
deprecated_twotags = [ "APPLET", "CENTER", "DIR", "FONT", "MENU", "S", "STRIKE", "U" ]
self.header = [ ]
self.content = [ ]
self.footer = [ ]
self.case = case
self.separator = separator
# init( ) sets it to True so we know that </body></html> has to be printed at the end
self._full = False
self.class_= class_
if mode == 'strict_html' or mode == 'html':
self.onetags = valid_onetags
self.onetags += list(map( str.lower, self.onetags ))
self.twotags = valid_twotags
self.twotags += list(map( str.lower, self.twotags ))
self.deptags = deprecated_onetags + deprecated_twotags
self.deptags += list(map( str.lower, self.deptags ))
self.mode = 'strict_html'
elif mode == 'loose_html':
self.onetags = valid_onetags + deprecated_onetags
self.onetags += list(map( str.lower, self.onetags ))
self.twotags = valid_twotags + deprecated_twotags
self.twotags += list(map( str.lower, self.twotags ))
self.mode = mode
elif mode == 'xml':
if onetags and twotags:
self.onetags = onetags
self.twotags = twotags
elif ( onetags and not twotags ) or ( twotags and not onetags ):
raise CustomizationError( )
else:
self.onetags = russell( )
self.twotags = russell( )
self.mode = mode
else:
raise ModeError( mode )
def __getattr__( self, attr ):
if attr.startswith("__") and attr.endswith("__"):
raise AttributeError(attr)
return element( attr, case=self.case, parent=self )
def __str__( self ):
if self._full and ( self.mode == 'strict_html' or self.mode == 'loose_html' ):
end = [ '</body>', '</html>' ]
else:
end = [ ]
return self.separator.join( self.header + self.content + self.footer + end )
def __call__( self, escape=False ):
"""Return the document as a string.
escape -- False print normally
True replace < and > by &lt; and &gt;
the default escape sequences in most browsers"""
if escape:
return _escape( self.__str__( ) )
else:
return self.__str__( )
def add( self, text ):
"""This is an alias to addcontent."""
self.addcontent( text )
def addfooter( self, text ):
"""Add some text to the bottom of the document"""
self.footer.append( text )
def addheader( self, text ):
"""Add some text to the top of the document"""
self.header.append( text )
def addcontent( self, text ):
"""Add some text to the main part of the document"""
self.content.append( text )
def init( self, lang='en', css=None, metainfo=None, title=None, header=None,
footer=None, charset=None, encoding=None, doctype=None, bodyattrs=None, script=None ):
"""This method is used for complete documents with appropriate
doctype, encoding, title, etc information. For an HTML/XML snippet
omit this method.
lang -- language, usually a two character string, will appear
as <html lang='en'> in html mode (ignored in xml mode)
css -- Cascading Style Sheet filename as a string or a list of
strings for multiple css files (ignored in xml mode)
metainfo -- a dictionary in the form { 'name':'content' } to be inserted
into meta element(s) as <meta name='name' content='content'>
(ignored in xml mode)
bodyattrs --a dictionary in the form { 'key':'value', ... } which will be added
as attributes of the <body> element as <body key='value' ... >
(ignored in xml mode)
script -- dictionary containing src:type pairs, <script type='text/type' src=src></script>
title -- the title of the document as a string to be inserted into
a title element as <title>my title</title> (ignored in xml mode)
header -- some text to be inserted right after the <body> element
(ignored in xml mode)
footer -- some text to be inserted right before the </body> element
(ignored in xml mode)
charset -- a string defining the character set, will be inserted into a
<meta http-equiv='Content-Type' content='text/html; charset=myset'>
element (ignored in xml mode)
encoding -- a string defining the encoding, will be put into to first line of
the document as <?xml version='1.0' encoding='myencoding' ?> in
xml mode (ignored in html mode)
doctype -- the document type string, defaults to
<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>
in html mode (ignored in xml mode)"""
self._full = True
if self.mode == 'strict_html' or self.mode == 'loose_html':
if doctype is None:
doctype = "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>"
self.header.append( doctype )
self.html( lang=lang )
self.head( )
if charset is not None:
self.meta( http_equiv='Content-Type', content="text/html; charset=%s" % charset )
if metainfo is not None:
self.metainfo( metainfo )
if css is not None:
self.css( css )
if title is not None:
self.title( title )
if script is not None:
self.scripts( script )
self.head.close()
if bodyattrs is not None:
self.body( **bodyattrs )
else:
self.body( )
if header is not None:
self.content.append( header )
if footer is not None:
self.footer.append( footer )
elif self.mode == 'xml':
if doctype is None:
if encoding is not None:
doctype = "<?xml version='1.0' encoding='%s' ?>" % encoding
else:
doctype = "<?xml version='1.0' ?>"
self.header.append( doctype )
def css( self, filelist ):
"""This convenience function is only useful for html.
It adds css stylesheet(s) to the document via the <link> element."""
if isinstance( filelist, str ):
self.link( href=filelist, rel='stylesheet', type='text/css', media='all' )
else:
for file in filelist:
self.link( href=file, rel='stylesheet', type='text/css', media='all' )
def metainfo( self, mydict ):
"""This convenience function is only useful for html.
It adds meta information via the <meta> element, the argument is
a dictionary of the form { 'name':'content' }."""
if isinstance( mydict, dict ):
for name, content in mydict.items( ):
self.meta( name=name, content=content )
else:
raise TypeError("Metainfo should be called with a dictionary argument of name:content pairs.")
def scripts( self, mydict ):
"""Only useful in html, mydict is dictionary of src:type pairs will
be rendered as <script type='text/type' src=src></script>"""
if isinstance( mydict, dict ):
for src, type in mydict.items( ):
self.script( '', src=src, type='text/%s' % type )
else:
raise TypeError("Script should be given a dictionary of src:type pairs.")
class _oneliner:
"""An instance of oneliner returns a string corresponding to one element.
This class can be used to write 'oneliners' that return a string
immediately so there is no need to instantiate the page class."""
def __init__( self, case='lower' ):
self.case = case
def __getattr__( self, attr ):
if attr.startswith("__") and attr.endswith("__"):
raise AttributeError(attr)
return element( attr, case=self.case, parent=None )
oneliner = _oneliner( case='lower' )
upper_oneliner = _oneliner( case='upper' )
def _argsdicts( args, mydict ):
"""A utility generator that pads argument list and dictionary values, will only be called with len( args ) = 0, 1."""
if len( args ) == 0:
args = None,
elif len( args ) == 1:
args = _totuple( args[0] )
else:
raise Exception("We should have never gotten here.")
mykeys = list(mydict.keys( ))
myvalues = list(map( _totuple, list(mydict.values( )) ))
maxlength = max( list(map( len, [ args ] + myvalues )) )
for i in range( maxlength ):
thisdict = { }
for key, value in zip( mykeys, myvalues ):
try:
thisdict[ key ] = value[i]
except IndexError:
thisdict[ key ] = value[-1]
try:
thisarg = args[i]
except IndexError:
thisarg = args[-1]
yield thisarg, thisdict
def _totuple( x ):
"""Utility stuff to convert string, int, float, None or anything to a usable tuple."""
if isinstance( x, str ):
out = x,
elif isinstance( x, ( int, float ) ):
out = str( x ),
elif x is None:
out = None,
else:
out = tuple( x )
return out
def escape( text, newline=False ):
"""Escape special html characters."""
if isinstance( text, str ):
if '&' in text:
text = text.replace( '&', '&amp;' )
if '>' in text:
text = text.replace( '>', '&gt;' )
if '<' in text:
text = text.replace( '<', '&lt;' )
if '\"' in text:
text = text.replace( '\"', '&quot;' )
if '\'' in text:
text = text.replace( '\'', '&quot;' )
if newline:
if '\n' in text:
text = text.replace( '\n', '<br>' )
return text
_escape = escape
def unescape( text ):
"""Inverse of escape."""
if isinstance( text, str ):
if '&amp;' in text:
text = text.replace( '&amp;', '&' )
if '&gt;' in text:
text = text.replace( '&gt;', '>' )
if '&lt;' in text:
text = text.replace( '&lt;', '<' )
if '&quot;' in text:
text = text.replace( '&quot;', '\"' )
return text
class dummy:
"""A dummy class for attaching attributes."""
pass
doctype = dummy( )
doctype.frameset = "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/html4/frameset.dtd'>"
doctype.strict = "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>"
doctype.loose = "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>"
class russell:
"""A dummy class that contains anything."""
def __contains__( self, item ):
return True
class MarkupError( Exception ):
"""All our exceptions subclass this."""
def __str__( self ):
return self.message
class ClosingError( MarkupError ):
def __init__( self, tag ):
self.message = "The element '%s' does not accept non-keyword arguments (has no closing tag)." % tag
class OpeningError( MarkupError ):
def __init__( self, tag ):
self.message = "The element '%s' can not be opened." % tag
class ArgumentError( MarkupError ):
def __init__( self, tag ):
self.message = "The element '%s' was called with more than one non-keyword argument." % tag
class InvalidElementError( MarkupError ):
def __init__( self, tag, mode ):
self.message = "The element '%s' is not valid for your mode '%s'." % ( tag, mode )
class DeprecationError( MarkupError ):
def __init__( self, tag ):
self.message = "The element '%s' is deprecated, instantiate markup.page with mode='loose_html' to allow it." % tag
class ModeError( MarkupError ):
def __init__( self, mode ):
self.message = "Mode '%s' is invalid, possible values: strict_html, loose_html, xml." % mode
class CustomizationError( MarkupError ):
def __init__( self ):
self.message = "If you customize the allowed elements, you must define both types 'onetags' and 'twotags'."
if __name__ == '__main__':
print(__doc__)
-53
View File
@@ -1,53 +0,0 @@
# file openpyxl/__init__.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Imports for the openpyxl package."""
# package imports
from . import cell
from . import namedrange
from . import style
from . import workbook
from . import worksheet
from . import reader
from . import shared
from . import writer
# constants
__major__ = 1 # for major interface/format changes
__minor__ = 5 # for minor interface/format changes
__release__ = 2 # for tweaks, bug-fixes, or development
__version__ = '%d.%d.%d' % (__major__, __minor__, __release__)
__author__ = 'Eric Gazoni'
__license__ = 'MIT/Expat'
__author_email__ = 'eric.gazoni@gmail.com'
__maintainer_email__ = 'openpyxl-users@googlegroups.com'
__url__ = 'http://bitbucket.org/ericgazoni/openpyxl/wiki/Home'
__downloadUrl__ = "http://bitbucket.org/ericgazoni/openpyxl/downloads"
__all__ = ('reader', 'shared', 'writer',)
-384
View File
@@ -1,384 +0,0 @@
# file openpyxl/cell.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Manage individual cells in a spreadsheet.
The Cell class is required to know its value and type, display options,
and any other features of an Excel cell. Utilities for referencing
cells using Excel's 'A1' column/row nomenclature are also provided.
"""
__docformat__ = "restructuredtext en"
# Python stdlib imports
import datetime
import re
# package imports
from .shared.date_time import SharedDate
from .shared.exc import CellCoordinatesException, \
ColumnStringIndexException, DataTypeException
from .style import NumberFormat
# constants
COORD_RE = re.compile('^[$]?([A-Z]+)[$]?(\d+)$')
ABSOLUTE_RE = re.compile('^[$]?([A-Z]+)[$]?(\d+)(:[$]?([A-Z]+)[$]?(\d+))?$')
def coordinate_from_string(coord_string):
"""Convert a coordinate string like 'B12' to a tuple ('B', 12)"""
match = COORD_RE.match(coord_string.upper())
if not match:
msg = 'Invalid cell coordinates (%s)' % coord_string
raise CellCoordinatesException(msg)
column, row = match.groups()
return (column, int(row))
def absolute_coordinate(coord_string):
"""Convert a coordinate to an absolute coordinate string (B12 -> $B$12)"""
parts = ABSOLUTE_RE.match(coord_string).groups()
if all(parts[-2:]):
return '$%s$%s:$%s$%s' % (parts[0], parts[1], parts[3], parts[4])
else:
return '$%s$%s' % (parts[0], parts[1])
def column_index_from_string(column, fast = False):
"""Convert a column letter into a column number (e.g. B -> 2)
Excel only supports 1-3 letter column names from A -> ZZZ, so we
restrict our column names to 1-3 characters, each in the range A-Z.
.. note::
Fast mode is faster but does not check that all letters are capitals between A and Z
"""
column = column.upper()
clen = len(column)
if not fast and not all('A' <= char <= 'Z' for char in column):
msg = 'Column string must contain only characters A-Z: got %s' % column
raise ColumnStringIndexException(msg)
if clen == 1:
return ord(column[0]) - 64
elif clen == 2:
return ((1 + (ord(column[0]) - 65)) * 26) + (ord(column[1]) - 64)
elif clen == 3:
return ((1 + (ord(column[0]) - 65)) * 676) + ((1 + (ord(column[1]) - 65)) * 26) + (ord(column[2]) - 64)
elif clen > 3:
raise ColumnStringIndexException('Column string index can not be longer than 3 characters')
else:
raise ColumnStringIndexException('Column string index can not be empty')
def get_column_letter(col_idx):
"""Convert a column number into a column letter (3 -> 'C')
Right shift the column col_idx by 26 to find column letters in reverse
order. These numbers are 1-based, and can be converted to ASCII
ordinals by adding 64.
"""
# these indicies corrospond to A -> ZZZ and include all allowed
# columns
if not 1 <= col_idx <= 18278:
msg = 'Column index out of bounds: %s' % col_idx
raise ColumnStringIndexException(msg)
ordinals = []
temp = col_idx
while temp:
quotient, remainder = divmod(temp, 26)
# check for exact division and borrow if needed
if remainder == 0:
quotient -= 1
remainder = 26
ordinals.append(remainder + 64)
temp = quotient
ordinals.reverse()
return ''.join([chr(ordinal) for ordinal in ordinals])
class Cell(object):
"""Describes cell associated properties.
Properties of interest include style, type, value, and address.
"""
__slots__ = ('column',
'row',
'_value',
'_data_type',
'parent',
'xf_index',
'_hyperlink_rel')
ERROR_CODES = {'#NULL!': 0,
'#DIV/0!': 1,
'#VALUE!': 2,
'#REF!': 3,
'#NAME?': 4,
'#NUM!': 5,
'#N/A': 6}
TYPE_STRING = 's'
TYPE_FORMULA = 'f'
TYPE_NUMERIC = 'n'
TYPE_BOOL = 'b'
TYPE_NULL = 's'
TYPE_INLINE = 'inlineStr'
TYPE_ERROR = 'e'
VALID_TYPES = [TYPE_STRING, TYPE_FORMULA, TYPE_NUMERIC, TYPE_BOOL,
TYPE_NULL, TYPE_INLINE, TYPE_ERROR]
RE_PATTERNS = {
'percentage': re.compile('^\-?[0-9]*\.?[0-9]*\s?\%$'),
'time': re.compile('^(\d|[0-1]\d|2[0-3]):[0-5]\d(:[0-5]\d)?$'),
'numeric': re.compile('^\-?([0-9]+\\.?[0-9]*|[0-9]*\\.?[0-9]+)((E|e)\-?[0-9]+)?$'), }
def __init__(self, worksheet, column, row, value = None):
self.column = column.upper()
self.row = row
# _value is the stored value, while value is the displayed value
self._value = None
self._hyperlink_rel = None
self._data_type = self.TYPE_NULL
if value:
self.value = value
self.parent = worksheet
self.xf_index = 0
def __repr__(self):
return "<Cell %s.%s>" % (self.parent.title, self.get_coordinate())
def check_string(self, value):
"""Check string coding, length, and line break character"""
# convert to unicode string
value = unicode(value)
# string must never be longer than 32,767 characters
# truncate if necessary
value = value[:32767]
# we require that newline is represented as "\n" in core,
# not as "\r\n" or "\r"
value = value.replace('\r\n', '\n')
return value
def check_numeric(self, value):
"""Cast value to int or float if necessary"""
if not isinstance(value, (int, float)):
try:
value = int(value)
except ValueError:
value = float(value)
return value
def set_value_explicit(self, value = None, data_type = TYPE_STRING):
"""Coerce values according to their explicit type"""
type_coercion_map = {
self.TYPE_INLINE: self.check_string,
self.TYPE_STRING: self.check_string,
self.TYPE_FORMULA: unicode,
self.TYPE_NUMERIC: self.check_numeric,
self.TYPE_BOOL: bool, }
try:
self._value = type_coercion_map[data_type](value)
except KeyError:
if data_type not in self.VALID_TYPES:
msg = 'Invalid data type: %s' % data_type
raise DataTypeException(msg)
self._data_type = data_type
def data_type_for_value(self, value):
"""Given a value, infer the correct data type"""
if value is None:
data_type = self.TYPE_NULL
elif value is True or value is False:
data_type = self.TYPE_BOOL
elif isinstance(value, (int, float)):
data_type = self.TYPE_NUMERIC
elif not value:
data_type = self.TYPE_STRING
elif isinstance(value, (datetime.datetime, datetime.date)):
data_type = self.TYPE_NUMERIC
elif isinstance(value, basestring) and value[0] == '=':
data_type = self.TYPE_FORMULA
elif self.RE_PATTERNS['numeric'].match(value):
data_type = self.TYPE_NUMERIC
elif value.strip() in self.ERROR_CODES:
data_type = self.TYPE_ERROR
else:
data_type = self.TYPE_STRING
return data_type
def bind_value(self, value):
"""Given a value, infer type and display options."""
self._data_type = self.data_type_for_value(value)
if value is None:
self.set_value_explicit('', self.TYPE_NULL)
return True
elif self._data_type == self.TYPE_STRING:
# percentage detection
percentage_search = self.RE_PATTERNS['percentage'].match(value)
if percentage_search and value.strip() != '%':
value = float(value.replace('%', '')) / 100.0
self.set_value_explicit(value, self.TYPE_NUMERIC)
self._set_number_format(NumberFormat.FORMAT_PERCENTAGE)
return True
# time detection
time_search = self.RE_PATTERNS['time'].match(value)
if time_search:
sep_count = value.count(':') #pylint: disable-msg=E1103
if sep_count == 1:
hours, minutes = [int(bit) for bit in value.split(':')] #pylint: disable-msg=E1103
seconds = 0
elif sep_count == 2:
hours, minutes, seconds = \
[int(bit) for bit in value.split(':')] #pylint: disable-msg=E1103
days = (hours / 24.0) + (minutes / 1440.0) + \
(seconds / 86400.0)
self.set_value_explicit(days, self.TYPE_NUMERIC)
self._set_number_format(NumberFormat.FORMAT_DATE_TIME3)
return True
if self._data_type == self.TYPE_NUMERIC:
# date detection
# if the value is a date, but not a date time, make it a
# datetime, and set the time part to 0
if isinstance(value, datetime.date) and not \
isinstance(value, datetime.datetime):
value = datetime.datetime.combine(value, datetime.time())
if isinstance(value, datetime.datetime):
value = SharedDate().datetime_to_julian(date = value)
self.set_value_explicit(value, self.TYPE_NUMERIC)
self._set_number_format(NumberFormat.FORMAT_DATE_YYYYMMDD2)
return True
self.set_value_explicit(value, self._data_type)
def _get_value(self):
"""Return the value, formatted as a date if needed"""
value = self._value
if self.is_date():
value = SharedDate().from_julian(value)
return value
def _set_value(self, value):
"""Set the value and infer type and display options."""
self.bind_value(value)
value = property(_get_value, _set_value,
doc = 'Get or set the value held in the cell.\n\n'
':rtype: depends on the value (string, float, int or '
':class:`datetime.datetime`)')
def _set_hyperlink(self, val):
"""Set value and display for hyperlinks in a cell"""
if self._hyperlink_rel is None:
self._hyperlink_rel = self.parent.create_relationship("hyperlink")
self._hyperlink_rel.target = val
self._hyperlink_rel.target_mode = "External"
if self._value is None:
self.value = val
def _get_hyperlink(self):
"""Return the hyperlink target or an empty string"""
return self._hyperlink_rel is not None and \
self._hyperlink_rel.target or ''
hyperlink = property(_get_hyperlink, _set_hyperlink,
doc = 'Get or set the hyperlink held in the cell. '
'Automatically sets the `value` of the cell with link text, '
'but you can modify it afterwards by setting the '
'`value` property, and the hyperlink will remain.\n\n'
':rtype: string')
@property
def hyperlink_rel_id(self):
"""Return the id pointed to by the hyperlink, or None"""
return self._hyperlink_rel is not None and \
self._hyperlink_rel.id or None
def _set_number_format(self, format_code):
"""Set a new formatting code for numeric values"""
self.style.number_format.format_code = format_code
@property
def has_style(self):
"""Check if the parent worksheet has a style for this cell"""
return self.get_coordinate() in self.parent._styles #pylint: disable-msg=W0212
@property
def style(self):
"""Returns the :class:`openpyxl.style.Style` object for this cell"""
return self.parent.get_style(self.get_coordinate())
@property
def data_type(self):
"""Return the data type represented by this cell"""
return self._data_type
def get_coordinate(self):
"""Return the coordinate string for this cell (e.g. 'B12')
:rtype: string
"""
return '%s%s' % (self.column, self.row)
@property
def address(self):
"""Return the coordinate string for this cell (e.g. 'B12')
:rtype: string
"""
return self.get_coordinate()
def offset(self, row = 0, column = 0):
"""Returns a cell location relative to this cell.
:param row: number of rows to offset
:type row: int
:param column: number of columns to offset
:type column: int
:rtype: :class:`openpyxl.cell.Cell`
"""
offset_column = get_column_letter(column_index_from_string(
column = self.column) + column)
offset_row = self.row + row
return self.parent.cell('%s%s' % (offset_column, offset_row))
def is_date(self):
"""Returns whether the value is *probably* a date or not
:rtype: bool
"""
return (self.has_style
and self.style.number_format.is_date_format()
and isinstance(self._value, (int, float)))
-340
View File
@@ -1,340 +0,0 @@
'''
Copyright (c) 2010 openpyxl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@license: http://www.opensource.org/licenses/mit-license.php
@author: Eric Gazoni
'''
import math
from .style import NumberFormat
from .drawing import Drawing, Shape
from .shared.units import pixels_to_EMU, short_color
from .cell import get_column_letter
class Axis(object):
POSITION_BOTTOM = 'b'
POSITION_LEFT = 'l'
ORIENTATION_MIN_MAX = "minMax"
def __init__(self):
self.orientation = self.ORIENTATION_MIN_MAX
self.number_format = NumberFormat()
for attr in ('position','tick_label_position','crosses',
'auto','label_align','label_offset','cross_between'):
setattr(self, attr, None)
self.min = 0
self.max = None
self.unit = None
@classmethod
def default_category(cls):
""" default values for category axes """
ax = Axis()
ax.id = 60871424
ax.cross = 60873344
ax.position = Axis.POSITION_BOTTOM
ax.tick_label_position = 'nextTo'
ax.crosses = "autoZero"
ax.auto = True
ax.label_align = 'ctr'
ax.label_offset = 100
return ax
@classmethod
def default_value(cls):
""" default values for value axes """
ax = Axis()
ax.id = 60873344
ax.cross = 60871424
ax.position = Axis.POSITION_LEFT
ax.major_gridlines = None
ax.tick_label_position = 'nextTo'
ax.crosses = 'autoZero'
ax.auto = False
ax.cross_between = 'between'
return ax
class Reference(object):
""" a simple wrapper around a serie of reference data """
def __init__(self, sheet, pos1, pos2=None):
self.sheet = sheet
self.pos1 = pos1
self.pos2 = pos2
def get_type(self):
if isinstance(self.cache[0], basestring):
return 'str'
else:
return 'num'
def _get_ref(self):
""" format excel reference notation """
if self.pos2:
return '%s!$%s$%s:$%s$%s' % (self.sheet.title,
get_column_letter(self.pos1[1]+1), self.pos1[0]+1,
get_column_letter(self.pos2[1]+1), self.pos2[0]+1)
else:
return '%s!$%s$%s' % (self.sheet.title,
get_column_letter(self.pos1[1]+1), self.pos1[0]+1)
def _get_cache(self):
""" read data in sheet - to be used at writing time """
cache = []
if self.pos2:
for row in range(self.pos1[0], self.pos2[0]+1):
for col in range(self.pos1[1], self.pos2[1]+1):
cache.append(self.sheet.cell(row=row, column=col).value)
else:
cell = self.sheet.cell(row=self.pos1[0], column=self.pos1[1])
cache.append(cell.value)
return cache
class Serie(object):
""" a serie of data and possibly associated labels """
MARKER_NONE = 'none'
def __init__(self, values, labels=None, legend=None, color=None, xvalues=None):
self.marker = Serie.MARKER_NONE
self.values = values
self.xvalues = xvalues
self.labels = labels
self.legend = legend
self.error_bar = None
self._color = color
def _get_color(self):
return self._color
def _set_color(self, color):
self._color = short_color(color)
color = property(_get_color, _set_color)
def get_min_max(self):
if self.error_bar:
err_cache = self.error_bar.values._get_cache()
vals = [v + err_cache[i] \
for i,v in enumerate(self.values._get_cache())]
else:
vals = self.values._get_cache()
return min(vals), max(vals)
def __len__(self):
return len(self.values.cache)
class Legend(object):
def __init__(self):
self.position = 'r'
self.layout = None
class ErrorBar(object):
PLUS = 1
MINUS = 2
PLUS_MINUS = 3
def __init__(self, _type, values):
self.type = _type
self.values = values
class Chart(object):
""" raw chart class """
GROUPING_CLUSTERED = 'clustered'
GROUPING_STANDARD = 'standard'
BAR_CHART = 1
LINE_CHART = 2
SCATTER_CHART = 3
def __init__(self, _type, grouping):
self._series = []
# public api
self.type = _type
self.grouping = grouping
self.x_axis = Axis.default_category()
self.y_axis = Axis.default_value()
self.legend = Legend()
self.lang = 'fr-FR'
self.title = ''
self.print_margins = dict(b=.75, l=.7, r=.7, t=.75, header=0.3, footer=.3)
# the containing drawing
self.drawing = Drawing()
# the offset for the plot part in percentage of the drawing size
self.width = .6
self.height = .6
self.margin_top = self._get_max_margin_top()
self.margin_left = 0
# the user defined shapes
self._shapes = []
def add_serie(self, serie):
serie.id = len(self._series)
self._series.append(serie)
self._compute_min_max()
if not None in [s.xvalues for s in self._series]:
self._compute_xmin_xmax()
def add_shape(self, shape):
shape._chart = self
self._shapes.append(shape)
def get_x_units(self):
""" calculate one unit for x axis in EMU """
return max([len(s.values._get_cache()) for s in self._series])
def get_y_units(self):
""" calculate one unit for y axis in EMU """
dh = pixels_to_EMU(self.drawing.height)
return (dh * self.height) / self.y_axis.max
def get_y_chars(self):
""" estimate nb of chars for y axis """
_max = max([max(s.values._get_cache()) for s in self._series])
return len(str(int(_max)))
def _compute_min_max(self):
""" compute y axis limits and units """
maxi = max([max(s.values._get_cache()) for s in self._series])
mul = None
if maxi < 1:
s = str(maxi).split('.')[1]
mul = 10
for x in s:
if x == '0':
mul *= 10
else:
break
maxi = maxi * mul
maxi = math.ceil(maxi * 1.1)
sz = len(str(int(maxi))) - 1
unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1))
maxi = math.ceil(maxi/unit) * unit
if mul is not None:
maxi = maxi/mul
unit = unit/mul
if maxi / unit > 9:
# no more that 10 ticks
unit *= 2
self.y_axis.max = maxi
self.y_axis.unit = unit
def _compute_xmin_xmax(self):
""" compute x axis limits and units """
maxi = max([max(s.xvalues._get_cache()) for s in self._series])
mul = None
if maxi < 1:
s = str(maxi).split('.')[1]
mul = 10
for x in s:
if x == '0':
mul *= 10
else:
break
maxi = maxi * mul
maxi = math.ceil(maxi * 1.1)
sz = len(str(int(maxi))) - 1
unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1))
maxi = math.ceil(maxi/unit) * unit
if mul is not None:
maxi = maxi/mul
unit = unit/mul
if maxi / unit > 9:
# no more that 10 ticks
unit *= 2
self.x_axis.max = maxi
self.x_axis.unit = unit
def _get_max_margin_top(self):
mb = Shape.FONT_HEIGHT + Shape.MARGIN_BOTTOM
plot_height = self.drawing.height * self.height
return float(self.drawing.height - plot_height - mb)/self.drawing.height
def _get_min_margin_left(self):
ml = (self.get_y_chars() * Shape.FONT_WIDTH) + Shape.MARGIN_LEFT
return float(ml)/self.drawing.width
def _get_margin_top(self):
""" get margin in percent """
return min(self.margin_top, self._get_max_margin_top())
def _get_margin_left(self):
return max(self._get_min_margin_left(), self.margin_left)
class BarChart(Chart):
def __init__(self):
super(BarChart, self).__init__(Chart.BAR_CHART, Chart.GROUPING_CLUSTERED)
class LineChart(Chart):
def __init__(self):
super(LineChart, self).__init__(Chart.LINE_CHART, Chart.GROUPING_STANDARD)
class ScatterChart(Chart):
def __init__(self):
super(ScatterChart, self).__init__(Chart.SCATTER_CHART, Chart.GROUPING_STANDARD)
-401
View File
@@ -1,401 +0,0 @@
'''
Copyright (c) 2010 openpyxl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@license: http://www.opensource.org/licenses/mit-license.php
@author: Eric Gazoni
'''
import math
from .style import Color
from .shared.units import pixels_to_EMU, EMU_to_pixels, short_color
class Shadow(object):
SHADOW_BOTTOM = 'b'
SHADOW_BOTTOM_LEFT = 'bl'
SHADOW_BOTTOM_RIGHT = 'br'
SHADOW_CENTER = 'ctr'
SHADOW_LEFT = 'l'
SHADOW_TOP = 't'
SHADOW_TOP_LEFT = 'tl'
SHADOW_TOP_RIGHT = 'tr'
def __init__(self):
self.visible = False
self.blurRadius = 6
self.distance = 2
self.direction = 0
self.alignment = self.SHADOW_BOTTOM_RIGHT
self.color = Color(Color.BLACK)
self.alpha = 50
class Drawing(object):
""" a drawing object - eg container for shapes or charts
we assume user specifies dimensions in pixels; units are
converted to EMU in the drawing part
"""
count = 0
def __init__(self):
self.name = ''
self.description = ''
self.coordinates = ((1,2), (16,8))
self.left = 0
self.top = 0
self._width = EMU_to_pixels(200000)
self._height = EMU_to_pixels(1828800)
self.resize_proportional = False
self.rotation = 0
# self.shadow = Shadow()
def _set_width(self, w):
if self.resize_proportional and w:
ratio = self._height / self._width
self._height = round(ratio * w)
self._width = w
def _get_width(self):
return self._width
width = property(_get_width, _set_width)
def _set_height(self, h):
if self.resize_proportional and h:
ratio = self._width / self._height
self._width = round(ratio * h)
self._height = h
def _get_height(self):
return self._height
height = property(_get_height, _set_height)
def set_dimension(self, w=0, h=0):
xratio = w / self._width
yratio = h / self._height
if self.resize_proportional and w and h:
if (xratio * self._height) < h:
self._height = math.ceil(xratio * self._height)
self._width = width
else:
self._width = math.ceil(yratio * self._width)
self._height = height
def get_emu_dimensions(self):
""" return (x, y, w, h) in EMU """
return (pixels_to_EMU(self.left), pixels_to_EMU(self.top),
pixels_to_EMU(self._width), pixels_to_EMU(self._height))
class Shape(object):
""" a drawing inside a chart
coordiantes are specified by the user in the axis units
"""
MARGIN_LEFT = 6 + 13 + 1
MARGIN_BOTTOM = 17 + 11
FONT_WIDTH = 7
FONT_HEIGHT = 8
ROUND_RECT = 'roundRect'
RECT = 'rect'
# other shapes to define :
'''
"line"
"lineInv"
"triangle"
"rtTriangle"
"diamond"
"parallelogram"
"trapezoid"
"nonIsoscelesTrapezoid"
"pentagon"
"hexagon"
"heptagon"
"octagon"
"decagon"
"dodecagon"
"star4"
"star5"
"star6"
"star7"
"star8"
"star10"
"star12"
"star16"
"star24"
"star32"
"roundRect"
"round1Rect"
"round2SameRect"
"round2DiagRect"
"snipRoundRect"
"snip1Rect"
"snip2SameRect"
"snip2DiagRect"
"plaque"
"ellipse"
"teardrop"
"homePlate"
"chevron"
"pieWedge"
"pie"
"blockArc"
"donut"
"noSmoking"
"rightArrow"
"leftArrow"
"upArrow"
"downArrow"
"stripedRightArrow"
"notchedRightArrow"
"bentUpArrow"
"leftRightArrow"
"upDownArrow"
"leftUpArrow"
"leftRightUpArrow"
"quadArrow"
"leftArrowCallout"
"rightArrowCallout"
"upArrowCallout"
"downArrowCallout"
"leftRightArrowCallout"
"upDownArrowCallout"
"quadArrowCallout"
"bentArrow"
"uturnArrow"
"circularArrow"
"leftCircularArrow"
"leftRightCircularArrow"
"curvedRightArrow"
"curvedLeftArrow"
"curvedUpArrow"
"curvedDownArrow"
"swooshArrow"
"cube"
"can"
"lightningBolt"
"heart"
"sun"
"moon"
"smileyFace"
"irregularSeal1"
"irregularSeal2"
"foldedCorner"
"bevel"
"frame"
"halfFrame"
"corner"
"diagStripe"
"chord"
"arc"
"leftBracket"
"rightBracket"
"leftBrace"
"rightBrace"
"bracketPair"
"bracePair"
"straightConnector1"
"bentConnector2"
"bentConnector3"
"bentConnector4"
"bentConnector5"
"curvedConnector2"
"curvedConnector3"
"curvedConnector4"
"curvedConnector5"
"callout1"
"callout2"
"callout3"
"accentCallout1"
"accentCallout2"
"accentCallout3"
"borderCallout1"
"borderCallout2"
"borderCallout3"
"accentBorderCallout1"
"accentBorderCallout2"
"accentBorderCallout3"
"wedgeRectCallout"
"wedgeRoundRectCallout"
"wedgeEllipseCallout"
"cloudCallout"
"cloud"
"ribbon"
"ribbon2"
"ellipseRibbon"
"ellipseRibbon2"
"leftRightRibbon"
"verticalScroll"
"horizontalScroll"
"wave"
"doubleWave"
"plus"
"flowChartProcess"
"flowChartDecision"
"flowChartInputOutput"
"flowChartPredefinedProcess"
"flowChartInternalStorage"
"flowChartDocument"
"flowChartMultidocument"
"flowChartTerminator"
"flowChartPreparation"
"flowChartManualInput"
"flowChartManualOperation"
"flowChartConnector"
"flowChartPunchedCard"
"flowChartPunchedTape"
"flowChartSummingJunction"
"flowChartOr"
"flowChartCollate"
"flowChartSort"
"flowChartExtract"
"flowChartMerge"
"flowChartOfflineStorage"
"flowChartOnlineStorage"
"flowChartMagneticTape"
"flowChartMagneticDisk"
"flowChartMagneticDrum"
"flowChartDisplay"
"flowChartDelay"
"flowChartAlternateProcess"
"flowChartOffpageConnector"
"actionButtonBlank"
"actionButtonHome"
"actionButtonHelp"
"actionButtonInformation"
"actionButtonForwardNext"
"actionButtonBackPrevious"
"actionButtonEnd"
"actionButtonBeginning"
"actionButtonReturn"
"actionButtonDocument"
"actionButtonSound"
"actionButtonMovie"
"gear6"
"gear9"
"funnel"
"mathPlus"
"mathMinus"
"mathMultiply"
"mathDivide"
"mathEqual"
"mathNotEqual"
"cornerTabs"
"squareTabs"
"plaqueTabs"
"chartX"
"chartStar"
"chartPlus"
'''
def __init__(self, coordinates=((0,0), (1,1)), text=None, scheme="accent1"):
self.coordinates = coordinates # in axis unit
self.text = text
self.scheme = scheme
self.style = Shape.RECT
self._border_width = 3175 # in EMU
self._border_color = Color.BLACK[2:] #"F3B3C5"
self._color = Color.WHITE[2:]
self._text_color = Color.BLACK[2:]
def _get_border_color(self):
return self._border_color
def _set_border_color(self, color):
self._border_color = short_color(color)
border_color = property(_get_border_color, _set_border_color)
def _get_color(self):
return self._color
def _set_color(self, color):
self._color = short_color(color)
color = property(_get_color, _set_color)
def _get_text_color(self):
return self._text_color
def _set_text_color(self, color):
self._text_color = short_color(color)
text_color = property(_get_text_color, _set_text_color)
def _get_border_width(self):
return EMU_to_pixels(self._border_width)
def _set_border_width(self, w):
self._border_width = pixels_to_EMU(w)
# print self._border_width
border_width = property(_get_border_width, _set_border_width)
def get_coordinates(self):
""" return shape coordinates in percentages (left, top, right, bottom) """
(x1, y1), (x2, y2) = self.coordinates
drawing_width = pixels_to_EMU(self._chart.drawing.width)
drawing_height = pixels_to_EMU(self._chart.drawing.height)
plot_width = drawing_width * self._chart.width
plot_height = drawing_height * self._chart.height
margin_left = self._chart._get_margin_left() * drawing_width
xunit = plot_width / self._chart.get_x_units()
margin_top = self._chart._get_margin_top() * drawing_height
yunit = self._chart.get_y_units()
x_start = (margin_left + (float(x1) * xunit)) / drawing_width
y_start = (margin_top + plot_height - (float(y1) * yunit)) / drawing_height
x_end = (margin_left + (float(x2) * xunit)) / drawing_width
y_end = (margin_top + plot_height - (float(y2) * yunit)) / drawing_height
def _norm_pct(pct):
""" force shapes to appear by truncating too large sizes """
if pct>1: pct = 1
elif pct<0: pct = 0
return pct
# allow user to specify y's in whatever order
# excel expect y_end to be lower
if y_end < y_start:
y_end, y_start = y_start, y_end
return (_norm_pct(x_start), _norm_pct(y_start),
_norm_pct(x_end), _norm_pct(y_end))
-68
View File
@@ -1,68 +0,0 @@
# file openpyxl/namedrange.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Track named groups of cells in a worksheet"""
# Python stdlib imports
import re
# package imports
from .shared.exc import NamedRangeException
# constants
NAMED_RANGE_RE = re.compile("'?([^']*)'?!((\$([A-Za-z]+))?\$([0-9]+)(:(\$([A-Za-z]+))?(\$([0-9]+)))?)$")
class NamedRange(object):
"""A named group of cells"""
__slots__ = ('name', 'destinations', 'local_only')
def __init__(self, name, destinations):
self.name = name
self.destinations = destinations
self.local_only = False
def __str__(self):
return ','.join(['%s!%s' % (sheet, name) for sheet, name in self.destinations])
def __repr__(self):
return '<%s "%s">' % (self.__class__.__name__, str(self))
def split_named_range(range_string):
"""Separate a named range into its component parts"""
destinations = []
for range_string in range_string.split(','):
match = NAMED_RANGE_RE.match(range_string)
if not match:
raise NamedRangeException('Invalid named range string: "%s"' % range_string)
else:
sheet_name, xlrange = match.groups()[:2]
destinations.append((sheet_name, xlrange))
return destinations
@@ -1,33 +0,0 @@
# file openpyxl/reader/__init__.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Imports for the openpyxl.reader namespace."""
# package imports
from ..reader import excel
from ..reader import strings
from ..reader import style
from ..reader import workbook
from ..reader import worksheet
-109
View File
@@ -1,109 +0,0 @@
# file openpyxl/reader/excel.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Read an xlsx file into Python"""
# Python stdlib imports
from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile
# package imports
from ..shared.exc import OpenModeError, InvalidFileException
from ..shared.ooxml import ARC_SHARED_STRINGS, ARC_CORE, ARC_APP, \
ARC_WORKBOOK, PACKAGE_WORKSHEETS, ARC_STYLE
from ..workbook import Workbook
from ..reader.strings import read_string_table
from ..reader.style import read_style_table
from ..reader.workbook import read_sheets_titles, read_named_ranges, \
read_properties_core, get_sheet_ids
from ..reader.worksheet import read_worksheet
from ..reader.iter_worksheet import unpack_worksheet
def load_workbook(filename, use_iterators = False):
"""Open the given filename and return the workbook
:param filename: the path to open
:type filename: string
:param use_iterators: use lazy load for cells
:type use_iterators: bool
:rtype: :class:`openpyxl.workbook.Workbook`
.. note::
When using lazy load, all worksheets will be :class:`openpyxl.reader.iter_worksheet.IterableWorksheet`
and the returned workbook will be read-only.
"""
if isinstance(filename, file):
# fileobject must have been opened with 'rb' flag
# it is required by zipfile
if 'b' not in filename.mode:
raise OpenModeError("File-object must be opened in binary mode")
try:
archive = ZipFile(filename, 'r', ZIP_DEFLATED)
except (BadZipfile, RuntimeError, IOError, ValueError):
raise InvalidFileException()
wb = Workbook()
if use_iterators:
wb._set_optimized_read()
try:
_load_workbook(wb, archive, filename, use_iterators)
except KeyError:
raise InvalidFileException()
finally:
archive.close()
return wb
def _load_workbook(wb, archive, filename, use_iterators):
# get workbook-level information
wb.properties = read_properties_core(archive.read(ARC_CORE))
try:
string_table = read_string_table(archive.read(ARC_SHARED_STRINGS))
except KeyError:
string_table = {}
style_table = read_style_table(archive.read(ARC_STYLE))
# get worksheets
wb.worksheets = [] # remove preset worksheet
sheet_names = read_sheets_titles(archive.read(ARC_APP))
for i, sheet_name in enumerate(sheet_names):
sheet_codename = 'sheet%d.xml' % (i + 1)
worksheet_path = '%s/%s' % (PACKAGE_WORKSHEETS, sheet_codename)
if not use_iterators:
new_ws = read_worksheet(archive.read(worksheet_path), wb, sheet_name, string_table, style_table)
else:
xml_source = unpack_worksheet(archive, worksheet_path)
new_ws = read_worksheet(xml_source, wb, sheet_name, string_table, style_table, filename, sheet_codename)
#new_ws = read_worksheet(archive.read(worksheet_path), wb, sheet_name, string_table, style_table, filename, sheet_codename)
wb.add_sheet(new_ws, index = i)
wb._named_ranges = read_named_ranges(archive.read(ARC_WORKBOOK), wb)
@@ -1,348 +0,0 @@
# file openpyxl/reader/iter_worksheet.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
""" Iterators-based worksheet reader
*Still very raw*
"""
from ....compat import BytesIO as StringIO
import warnings
import operator
from functools import partial
from itertools import groupby, ifilter
from ..worksheet import Worksheet
from ..cell import coordinate_from_string, get_column_letter, Cell
from ..reader.excel import get_sheet_ids
from ..reader.strings import read_string_table
from ..reader.style import read_style_table, NumberFormat
from ..shared.date_time import SharedDate
from ..reader.worksheet import read_dimension
from ..shared.ooxml import (MIN_COLUMN, MAX_COLUMN, PACKAGE_WORKSHEETS,
MAX_ROW, MIN_ROW, ARC_SHARED_STRINGS, ARC_APP, ARC_STYLE)
try:
from xml.etree.cElementTree import iterparse
except ImportError:
from xml.etree.ElementTree import iterparse
from zipfile import ZipFile
from .. import cell
import re
import tempfile
import zlib
import zipfile
import struct
TYPE_NULL = Cell.TYPE_NULL
MISSING_VALUE = None
RE_COORDINATE = re.compile('^([A-Z]+)([0-9]+)$')
SHARED_DATE = SharedDate()
_COL_CONVERSION_CACHE = dict((get_column_letter(i), i) for i in xrange(1, 18279))
def column_index_from_string(str_col, _col_conversion_cache=_COL_CONVERSION_CACHE):
# we use a function argument to get indexed name lookup
return _col_conversion_cache[str_col]
del _COL_CONVERSION_CACHE
RAW_ATTRIBUTES = ['row', 'column', 'coordinate', 'internal_value', 'data_type', 'style_id', 'number_format']
try:
from collections import namedtuple
BaseRawCell = namedtuple('RawCell', RAW_ATTRIBUTES)
except ImportError:
# warnings.warn("""Unable to import 'namedtuple' module, this may cause memory issues when using optimized reader. Please upgrade your Python installation to 2.6+""")
class BaseRawCell(object):
def __init__(self, *args):
assert len(args)==len(RAW_ATTRIBUTES)
for attr, val in zip(RAW_ATTRIBUTES, args):
setattr(self, attr, val)
def _replace(self, **kwargs):
self.__dict__.update(kwargs)
return self
class RawCell(BaseRawCell):
"""Optimized version of the :class:`openpyxl.cell.Cell`, using named tuples.
Useful attributes are:
* row
* column
* coordinate
* internal_value
You can also access if needed:
* data_type
* number_format
"""
@property
def is_date(self):
res = (self.data_type == Cell.TYPE_NUMERIC
and self.number_format is not None
and ('d' in self.number_format
or 'm' in self.number_format
or 'y' in self.number_format
or 'h' in self.number_format
or 's' in self.number_format
))
return res
def iter_rows(workbook_name, sheet_name, xml_source, range_string = '', row_offset = 0, column_offset = 0):
archive = get_archive_file(workbook_name)
source = xml_source
if range_string:
min_col, min_row, max_col, max_row = get_range_boundaries(range_string, row_offset, column_offset)
else:
min_col, min_row, max_col, max_row = read_dimension(xml_source = source)
min_col = column_index_from_string(min_col)
max_col = column_index_from_string(max_col) + 1
max_row += 6
try:
string_table = read_string_table(archive.read(ARC_SHARED_STRINGS))
except KeyError:
string_table = {}
style_table = read_style_table(archive.read(ARC_STYLE))
source.seek(0)
p = iterparse(source)
return get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table)
def get_rows(p, min_column = MIN_COLUMN, min_row = MIN_ROW, max_column = MAX_COLUMN, max_row = MAX_ROW):
return groupby(get_cells(p, min_row, min_column, max_row, max_column), operator.attrgetter('row'))
def get_cells(p, min_row, min_col, max_row, max_col, _re_coordinate=RE_COORDINATE):
for _event, element in p:
if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c':
coord = element.get('r')
column_str, row = _re_coordinate.match(coord).groups()
row = int(row)
column = column_index_from_string(column_str)
if min_col <= column <= max_col and min_row <= row <= max_row:
data_type = element.get('t', 'n')
style_id = element.get('s')
value = element.findtext('{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v')
yield RawCell(row, column_str, coord, value, data_type, style_id, None)
if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v':
continue
element.clear()
def get_range_boundaries(range_string, row = 0, column = 0):
if ':' in range_string:
min_range, max_range = range_string.split(':')
min_col, min_row = coordinate_from_string(min_range)
max_col, max_row = coordinate_from_string(max_range)
min_col = column_index_from_string(min_col) + column
max_col = column_index_from_string(max_col) + column
min_row += row
max_row += row
else:
min_col, min_row = coordinate_from_string(range_string)
min_col = column_index_from_string(min_col)
max_col = min_col + 1
max_row = min_row
return (min_col, min_row, max_col, max_row)
def get_archive_file(archive_name):
return ZipFile(archive_name, 'r')
def get_xml_source(archive_file, sheet_name):
return archive_file.read('%s/%s' % (PACKAGE_WORKSHEETS, sheet_name))
def get_missing_cells(row, columns):
return dict([(column, RawCell(row, column, '%s%s' % (column, row), MISSING_VALUE, TYPE_NULL, None, None)) for column in columns])
def get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table):
expected_columns = [get_column_letter(ci) for ci in xrange(min_col, max_col)]
current_row = min_row
for row, cells in get_rows(p, min_row = min_row, max_row = max_row, min_column = min_col, max_column = max_col):
full_row = []
if current_row < row:
for gap_row in xrange(current_row, row):
dummy_cells = get_missing_cells(gap_row, expected_columns)
yield tuple([dummy_cells[column] for column in expected_columns])
current_row = row
temp_cells = list(cells)
retrieved_columns = dict([(c.column, c) for c in temp_cells])
missing_columns = list(set(expected_columns) - set(retrieved_columns.keys()))
replacement_columns = get_missing_cells(row, missing_columns)
for column in expected_columns:
if column in retrieved_columns:
cell = retrieved_columns[column]
if cell.style_id is not None:
style = style_table[int(cell.style_id)]
cell = cell._replace(number_format = style.number_format.format_code) #pylint: disable-msg=W0212
if cell.internal_value is not None:
if cell.data_type == Cell.TYPE_STRING:
cell = cell._replace(internal_value = string_table[int(cell.internal_value)]) #pylint: disable-msg=W0212
elif cell.data_type == Cell.TYPE_BOOL:
cell = cell._replace(internal_value = cell.internal_value == 'True')
elif cell.is_date:
cell = cell._replace(internal_value = SHARED_DATE.from_julian(float(cell.internal_value)))
elif cell.data_type == Cell.TYPE_NUMERIC:
cell = cell._replace(internal_value = float(cell.internal_value))
full_row.append(cell)
else:
full_row.append(replacement_columns[column])
current_row = row + 1
yield tuple(full_row)
#------------------------------------------------------------------------------
class IterableWorksheet(Worksheet):
def __init__(self, parent_workbook, title, workbook_name,
sheet_codename, xml_source):
Worksheet.__init__(self, parent_workbook, title)
self._workbook_name = workbook_name
self._sheet_codename = sheet_codename
self._xml_source = xml_source
def iter_rows(self, range_string = '', row_offset = 0, column_offset = 0):
""" Returns a squared range based on the `range_string` parameter,
using generators.
:param range_string: range of cells (e.g. 'A1:C4')
:type range_string: string
:param row: row index of the cell (e.g. 4)
:type row: int
:param column: column index of the cell (e.g. 3)
:type column: int
:rtype: generator
"""
return iter_rows(workbook_name = self._workbook_name,
sheet_name = self._sheet_codename,
xml_source = self._xml_source,
range_string = range_string,
row_offset = row_offset,
column_offset = column_offset)
def cell(self, *args, **kwargs):
raise NotImplementedError("use 'iter_rows()' instead")
def range(self, *args, **kwargs):
raise NotImplementedError("use 'iter_rows()' instead")
def unpack_worksheet(archive, filename):
temp_file = tempfile.TemporaryFile(mode='r+', prefix='openpyxl.', suffix='.unpack.temp')
zinfo = archive.getinfo(filename)
if zinfo.compress_type == zipfile.ZIP_STORED:
decoder = None
elif zinfo.compress_type == zipfile.ZIP_DEFLATED:
decoder = zlib.decompressobj(-zlib.MAX_WBITS)
else:
raise zipfile.BadZipFile("Unrecognized compression method")
archive.fp.seek(_get_file_offset(archive, zinfo))
bytes_to_read = zinfo.compress_size
while True:
buff = archive.fp.read(min(bytes_to_read, 102400))
if not buff:
break
bytes_to_read -= len(buff)
if decoder:
buff = decoder.decompress(buff)
temp_file.write(buff)
if decoder:
temp_file.write(decoder.decompress('Z'))
return temp_file
def _get_file_offset(archive, zinfo):
try:
return zinfo.file_offset
except AttributeError:
# From http://stackoverflow.com/questions/3781261/how-to-simulate-zipfile-open-in-python-2-5
# Seek over the fixed size fields to the "file name length" field in
# the file header (26 bytes). Unpack this and the "extra field length"
# field ourselves as info.extra doesn't seem to be the correct length.
archive.fp.seek(zinfo.header_offset + 26)
file_name_len, extra_len = struct.unpack("<HH", archive.fp.read(4))
return zinfo.header_offset + 30 + file_name_len + extra_len
@@ -1,64 +0,0 @@
# file openpyxl/reader/strings.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Read the shared strings table."""
# package imports
from ..shared.xmltools import fromstring, QName
from ..shared.ooxml import NAMESPACES
def read_string_table(xml_source):
"""Read in all shared strings in the table"""
table = {}
xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
root = fromstring(text=xml_source)
string_index_nodes = root.findall(QName(xmlns, 'si').text)
for index, string_index_node in enumerate(string_index_nodes):
table[index] = get_string(xmlns, string_index_node)
return table
def get_string(xmlns, string_index_node):
"""Read the contents of a specific string index"""
rich_nodes = string_index_node.findall(QName(xmlns, 'r').text)
if rich_nodes:
reconstructed_text = []
for rich_node in rich_nodes:
partial_text = get_text(xmlns, rich_node)
reconstructed_text.append(partial_text)
return ''.join(reconstructed_text)
else:
return get_text(xmlns, string_index_node)
def get_text(xmlns, rich_node):
"""Read rich text, discarding formatting if not disallowed"""
text_node = rich_node.find(QName(xmlns, 't').text)
partial_text = text_node.text or ''
if text_node.get(QName(NAMESPACES['xml'], 'space').text) != 'preserve':
partial_text = partial_text.strip()
return unicode(partial_text)
-69
View File
@@ -1,69 +0,0 @@
# file openpyxl/reader/style.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Read shared style definitions"""
# package imports
from ..shared.xmltools import fromstring, QName
from ..shared.exc import MissingNumberFormat
from ..style import Style, NumberFormat
def read_style_table(xml_source):
"""Read styles from the shared style table"""
table = {}
xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
root = fromstring(xml_source)
custom_num_formats = parse_custom_num_formats(root, xmlns)
builtin_formats = NumberFormat._BUILTIN_FORMATS
cell_xfs = root.find(QName(xmlns, 'cellXfs').text)
cell_xfs_nodes = cell_xfs.findall(QName(xmlns, 'xf').text)
for index, cell_xfs_node in enumerate(cell_xfs_nodes):
new_style = Style()
number_format_id = int(cell_xfs_node.get('numFmtId'))
if number_format_id < 164:
new_style.number_format.format_code = \
builtin_formats.get(number_format_id, 'General')
else:
if number_format_id in custom_num_formats:
new_style.number_format.format_code = \
custom_num_formats[number_format_id]
else:
raise MissingNumberFormat('%s' % number_format_id)
table[index] = new_style
return table
def parse_custom_num_formats(root, xmlns):
"""Read in custom numeric formatting rules from the shared style table"""
custom_formats = {}
num_fmts = root.find(QName(xmlns, 'numFmts').text)
if num_fmts is not None:
num_fmt_nodes = num_fmts.findall(QName(xmlns, 'numFmt').text)
for num_fmt_node in num_fmt_nodes:
custom_formats[int(num_fmt_node.get('numFmtId'))] = \
num_fmt_node.get('formatCode')
return custom_formats
-156
View File
@@ -1,156 +0,0 @@
# file openpyxl/reader/workbook.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Read in global settings to be maintained by the workbook object."""
# package imports
from ..shared.xmltools import fromstring, QName
from ..shared.ooxml import NAMESPACES
from ..workbook import DocumentProperties
from ..shared.date_time import W3CDTF_to_datetime
from ..namedrange import NamedRange, split_named_range
import datetime
# constants
BUGGY_NAMED_RANGES = ['NA()', '#REF!']
DISCARDED_RANGES = ['Excel_BuiltIn', 'Print_Area']
def get_sheet_ids(xml_source):
sheet_names = read_sheets_titles(xml_source)
return dict((sheet, 'sheet%d.xml' % (i + 1)) for i, sheet in enumerate(sheet_names))
def read_properties_core(xml_source):
"""Read assorted file properties."""
properties = DocumentProperties()
root = fromstring(xml_source)
creator_node = root.find(QName(NAMESPACES['dc'], 'creator').text)
if creator_node is not None:
properties.creator = creator_node.text
else:
properties.creator = ''
last_modified_by_node = root.find(
QName(NAMESPACES['cp'], 'lastModifiedBy').text)
if last_modified_by_node is not None:
properties.last_modified_by = last_modified_by_node.text
else:
properties.last_modified_by = ''
created_node = root.find(QName(NAMESPACES['dcterms'], 'created').text)
if created_node is not None:
properties.created = W3CDTF_to_datetime(created_node.text)
else:
properties.created = datetime.datetime.now()
modified_node = root.find(QName(NAMESPACES['dcterms'], 'modified').text)
if modified_node is not None:
properties.modified = W3CDTF_to_datetime(modified_node.text)
else:
properties.modified = properties.created
return properties
def get_number_of_parts(xml_source):
"""Get a list of contents of the workbook."""
parts_size = {}
parts_names = []
root = fromstring(xml_source)
heading_pairs = root.find(QName('http://schemas.openxmlformats.org/officeDocument/2006/extended-properties',
'HeadingPairs').text)
vector = heading_pairs.find(QName(NAMESPACES['vt'], 'vector').text)
children = vector.getchildren()
for child_id in range(0, len(children), 2):
part_name = children[child_id].find(QName(NAMESPACES['vt'],
'lpstr').text).text
if not part_name in parts_names:
parts_names.append(part_name)
part_size = int(children[child_id + 1].find(QName(
NAMESPACES['vt'], 'i4').text).text)
parts_size[part_name] = part_size
return parts_size, parts_names
def read_sheets_titles(xml_source):
"""Read titles for all sheets."""
root = fromstring(xml_source)
titles_root = root.find(QName('http://schemas.openxmlformats.org/officeDocument/2006/extended-properties',
'TitlesOfParts').text)
vector = titles_root.find(QName(NAMESPACES['vt'], 'vector').text)
parts, names = get_number_of_parts(xml_source)
# we can't assume 'Worksheets' to be written in english,
# but it's always the first item of the parts list (see bug #22)
size = parts[names[0]]
children = [c.text for c in vector.getchildren()]
return children[:size]
def read_named_ranges(xml_source, workbook):
"""Read named ranges, excluding poorly defined ranges."""
named_ranges = []
root = fromstring(xml_source)
names_root = root.find(QName('http://schemas.openxmlformats.org/spreadsheetml/2006/main',
'definedNames').text)
if names_root is not None:
for name_node in names_root.getchildren():
range_name = name_node.get('name')
if name_node.get("hidden", '0') == '1':
continue
valid = True
for discarded_range in DISCARDED_RANGES:
if discarded_range in range_name:
valid = False
for bad_range in BUGGY_NAMED_RANGES:
if bad_range in name_node.text:
valid = False
if valid:
destinations = split_named_range(name_node.text)
new_destinations = []
for worksheet, cells_range in destinations:
# it can happen that a valid named range references
# a missing worksheet, when Excel didn't properly maintain
# the named range list
#
# we just ignore them here
worksheet = workbook.get_sheet_by_name(worksheet)
if worksheet:
new_destinations.append((worksheet, cells_range))
named_range = NamedRange(range_name, new_destinations)
named_ranges.append(named_range)
return named_ranges
@@ -1,114 +0,0 @@
# file openpyxl/reader/worksheet.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Reader for a single worksheet."""
# Python stdlib imports
try:
from xml.etree.cElementTree import iterparse
except ImportError:
from xml.etree.ElementTree import iterparse
from ....compat import ifilter
from ....compat import BytesIO as StringIO
# package imports
from ..cell import Cell, coordinate_from_string
from ..worksheet import Worksheet
def _get_xml_iter(xml_source):
if not hasattr(xml_source, 'name'):
return StringIO(xml_source)
else:
xml_source.seek(0)
return xml_source
def read_dimension(xml_source):
source = _get_xml_iter(xml_source)
it = iterparse(source)
for event, element in it:
if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}dimension':
ref = element.get('ref')
min_range, max_range = ref.split(':')
min_col, min_row = coordinate_from_string(min_range)
max_col, max_row = coordinate_from_string(max_range)
return min_col, min_row, max_col, max_row
else:
element.clear()
return None
def filter_cells(x):
(event, element) = x
return element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c'
def fast_parse(ws, xml_source, string_table, style_table):
source = _get_xml_iter(xml_source)
it = iterparse(source)
for event, element in ifilter(filter_cells, it):
value = element.findtext('{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v')
if value is not None:
coordinate = element.get('r')
data_type = element.get('t', 'n')
style_id = element.get('s')
if data_type == Cell.TYPE_STRING:
value = string_table.get(int(value))
ws.cell(coordinate).value = value
if style_id is not None:
ws._styles[coordinate] = style_table.get(int(style_id))
# to avoid memory exhaustion, clear the item after use
element.clear()
from ..reader.iter_worksheet import IterableWorksheet
def read_worksheet(xml_source, parent, preset_title, string_table,
style_table, workbook_name = None, sheet_codename = None):
"""Read an xml worksheet"""
if workbook_name and sheet_codename:
ws = IterableWorksheet(parent, preset_title, workbook_name,
sheet_codename, xml_source)
else:
ws = Worksheet(parent, preset_title)
fast_parse(ws, xml_source, string_table, style_table)
return ws
@@ -1,33 +0,0 @@
# file openpyxl/shared/__init__.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Imports for the openpyxl.shared namespace."""
# package imports
from . import date_time
from . import exc
from . import ooxml
from . import password_hasher
from . import xmltools
@@ -1,154 +0,0 @@
# file openpyxl/shared/date_time.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Manage Excel date weirdness."""
# Python stdlib imports
from __future__ import division
from math import floor
import calendar
import datetime
import time
import re
# constants
W3CDTF_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
RE_W3CDTF = '(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.(\d{2}))?Z'
EPOCH = datetime.datetime.utcfromtimestamp(0)
def datetime_to_W3CDTF(dt):
"""Convert from a datetime to a timestamp string."""
return datetime.datetime.strftime(dt, W3CDTF_FORMAT)
def W3CDTF_to_datetime(formatted_string):
"""Convert from a timestamp string to a datetime object."""
match = re.match(RE_W3CDTF,formatted_string)
digits = map(int, match.groups()[:6])
return datetime.datetime(*digits)
class SharedDate(object):
"""Date formatting utilities for Excel with shared state.
Excel has a two primary date tracking schemes:
Windows - Day 1 == 1900-01-01
Mac - Day 1 == 1904-01-01
SharedDate stores which system we are using and converts dates between
Python and Excel accordingly.
"""
CALENDAR_WINDOWS_1900 = 1900
CALENDAR_MAC_1904 = 1904
datetime_object_type = 'DateTime'
def __init__(self):
self.excel_base_date = self.CALENDAR_WINDOWS_1900
def datetime_to_julian(self, date):
"""Convert from python datetime to excel julian date representation."""
if isinstance(date, datetime.datetime):
return self.to_julian(date.year, date.month, date.day, \
hours=date.hour, minutes=date.minute, seconds=date.second)
elif isinstance(date, datetime.date):
return self.to_julian(date.year, date.month, date.day)
def to_julian(self, year, month, day, hours=0, minutes=0, seconds=0):
"""Convert from Python date to Excel JD."""
# explicitly disallow bad years
# Excel 2000 treats JD=0 as 1/0/1900 (buggy, disallow)
# Excel 2000 treats JD=2958466 as a bad date (Y10K bug!)
if year < 1900 or year > 10000:
msg = 'Year not supported by Excel: %s' % year
raise ValueError(msg)
if self.excel_base_date == self.CALENDAR_WINDOWS_1900:
# Fudge factor for the erroneous fact that the year 1900 is
# treated as a Leap Year in MS Excel. This affects every date
# following 28th February 1900
if year == 1900 and month <= 2:
excel_1900_leap_year = False
else:
excel_1900_leap_year = True
excel_base_date = 2415020
else:
raise NotImplementedError('Mac dates are not yet supported.')
#excel_base_date = 2416481
#excel_1900_leap_year = False
# Julian base date adjustment
if month > 2:
month = month - 3
else:
month = month + 9
year -= 1
# Calculate the Julian Date, then subtract the Excel base date
# JD 2415020 = 31 - Dec - 1899 -> Excel Date of 0
century, decade = int(str(year)[:2]), int(str(year)[2:])
excel_date = floor(146097 * century / 4) + \
floor((1461 * decade) / 4) + floor((153 * month + 2) / 5) + \
day + 1721119 - excel_base_date
if excel_1900_leap_year:
excel_date += 1
# check to ensure that we exclude 2/29/1900 as a possible value
if self.excel_base_date == self.CALENDAR_WINDOWS_1900 \
and excel_date == 60:
msg = 'Error: Excel believes 1900 was a leap year'
raise ValueError(msg)
excel_time = ((hours * 3600) + (minutes * 60) + seconds) / 86400
return excel_date + excel_time
def from_julian(self, value=0):
"""Convert from the Excel JD back to a date"""
if self.excel_base_date == self.CALENDAR_WINDOWS_1900:
excel_base_date = 25569
if value < 60:
excel_base_date -= 1
elif value == 60:
msg = 'Error: Excel believes 1900 was a leap year'
raise ValueError(msg)
else:
raise NotImplementedError('Mac dates are not yet supported.')
#excel_base_date = 24107
if value >= 1:
utc_days = value - excel_base_date
return EPOCH + datetime.timedelta(days=utc_days)
elif value >= 0:
hours = floor(value * 24)
mins = floor(value * 24 * 60) - floor(hours * 60)
secs = floor(value * 24 * 60 * 60) - floor(hours * 60 * 60) - \
floor(mins * 60)
return datetime.time(int(hours), int(mins), int(secs))
else:
msg = 'Negative dates (%s) are not supported' % value
raise ValueError(msg)
-59
View File
@@ -1,59 +0,0 @@
# file openpyxl/shared/exc.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Definitions for openpyxl shared exception classes."""
class CellCoordinatesException(Exception):
"""Error for converting between numeric and A1-style cell references."""
class ColumnStringIndexException(Exception):
"""Error for bad column names in A1-style cell references."""
class DataTypeException(Exception):
"""Error for any data type inconsistencies."""
class NamedRangeException(Exception):
"""Error for badly formatted named ranges."""
class SheetTitleException(Exception):
"""Error for bad sheet names."""
class InsufficientCoordinatesException(Exception):
"""Error for partially specified cell coordinates."""
class OpenModeError(Exception):
"""Error for fileobj opened in non-binary mode."""
class InvalidFileException(Exception):
"""Error for trying to open a non-ooxml file."""
class ReadOnlyWorkbookException(Exception):
"""Error for trying to modify a read-only workbook"""
class MissingNumberFormat(Exception):
"""Error when a referenced number format is not in the stylesheet"""
-60
View File
@@ -1,60 +0,0 @@
# file openpyxl/shared/ooxml.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Constants for fixed paths in a file and xml namespace urls."""
MIN_ROW = 0
MIN_COLUMN = 0
MAX_COLUMN = 16384
MAX_ROW = 1048576
# constants
PACKAGE_PROPS = 'docProps'
PACKAGE_XL = 'xl'
PACKAGE_RELS = '_rels'
PACKAGE_THEME = PACKAGE_XL + '/' + 'theme'
PACKAGE_WORKSHEETS = PACKAGE_XL + '/' + 'worksheets'
PACKAGE_DRAWINGS = PACKAGE_XL + '/' + 'drawings'
PACKAGE_CHARTS = PACKAGE_XL + '/' + 'charts'
ARC_CONTENT_TYPES = '[Content_Types].xml'
ARC_ROOT_RELS = PACKAGE_RELS + '/.rels'
ARC_WORKBOOK_RELS = PACKAGE_XL + '/' + PACKAGE_RELS + '/workbook.xml.rels'
ARC_CORE = PACKAGE_PROPS + '/core.xml'
ARC_APP = PACKAGE_PROPS + '/app.xml'
ARC_WORKBOOK = PACKAGE_XL + '/workbook.xml'
ARC_STYLE = PACKAGE_XL + '/styles.xml'
ARC_THEME = PACKAGE_THEME + '/theme1.xml'
ARC_SHARED_STRINGS = PACKAGE_XL + '/sharedStrings.xml'
NAMESPACES = {
'cp': 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties',
'dc': 'http://purl.org/dc/elements/1.1/',
'dcterms': 'http://purl.org/dc/terms/',
'dcmitype': 'http://purl.org/dc/dcmitype/',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes',
'xml': 'http://www.w3.org/XML/1998/namespace'
}

Some files were not shown because too many files have changed in this diff Show More