Compare commits

..

166 Commits

Author SHA1 Message Date
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 9ba0451843 Merge pull request #219 from timofurrer/bugfix/export-only
Fix export only formats
2016-02-16 08:17:56 -05:00
346 changed files with 2228 additions and 105471 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).
+12
View File
@@ -26,3 +26,15 @@ junit-py27.xml
# tox noise
.tox
# pyenv noise
.python-version
tablib.egg-info/*
# Coverage
.coverage
htmlcov
# setuptools noise
.eggs
*.egg-info
+6
View File
@@ -0,0 +1,6 @@
[settings]
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88
+20 -8
View File
@@ -1,10 +1,22 @@
language: python
python:
- 2.6
- 2.7
- 3.2
- 3.3
- 3.4
install:
- python setup.py install
script: python test_tablib.py
- 2.7
- 3.6
- 3.7
- 3.8
cache: pip
dist: xenial
install: travis_retry pip install tox-travis
script: tox
after_success: bash <(curl -s https://codecov.io/bash)
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
+28 -32
View File
@@ -1,34 +1,30 @@
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 <me@kennethreitz.org>
Core Contributors
`````````````````
- Iuri de Silvio <iurisilvio@gmail.com>
Patches and Suggestions
```````````````````````
- Luke Lee
- Josh Ourisman
- Luca Beltrame
- Benjamin Wohlwend
- Erik Youngren
- Mark Rogers
- Mark Walling
- Mike Waldner
- Joel Friedly
- Jakub Janoszek
- Marc Abramowitz
- Alex Gaynor
- James Douglass
- Tommy Anthony
- Rabin Nankhwa
- Marco Dallagiacoma
- Mathias Loesch
Alex Gaynor
Andrii Soldatenko
Benjamin Wohlwend
Bruno Soares
Claude Paroz
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
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 :)
+273
View File
@@ -0,0 +1,273 @@
# History
## 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.
-236
View File
@@ -1,236 +0,0 @@
History
-------
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.
+2 -1
View File
@@ -1,4 +1,5 @@
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.
+6 -1
View File
@@ -1 +1,6 @@
include HISTORY.rst README.rst LICENSE AUTHORS NOTICE test_tablib.py
recursive-include docs *
recursive-include tests *
include pytest.ini tox.ini .isort.cfg .coveragerc HISTORY.md README.md LICENSE AUTHORS
prune docs/_build
prune *.pyc
prune __pycache__
-6
View File
@@ -1,6 +0,0 @@
test:
python test_tablib.py
publish:
python setup.py register
python setup.py sdist upload
python setup.py bdist_wheel --universal upload
-459
View File
@@ -1,459 +0,0 @@
Tablib includes some vendorized python libraries: ordereddict, odfpy, pyyaml,
simplejson, unicodecsv, xlrd, xlrd3, xlwt, and xlwt3.
Markup License
==============
Markup is in the public domain.
Odfpy License
=============
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
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.
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 (and XLWT3) 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
XLRD (and XLRD3) License
========================
Portions copyright © 2005-2009, 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) 2001 David Giffin.
All rights reserved.
Based on the the Java version: Andrew Khan Copyright (c) 2000.
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
David Giffin <david@giffin.org>."
4. Redistributions of any form whatsoever must retain the following
acknowledgment:
"This product includes software developed by
David Giffin <david@giffin.org>."
THIS SOFTWARE IS PROVIDED BY DAVID GIFFIN ``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 DAVID GIFFIN 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.
+177
View File
@@ -0,0 +1,177 @@
# Tablib: format-agnostic tabular dataset library
[![Jazzband](https://jazzband.co/static/img/badge.svg)](https://jazzband.co/)
[![Build Status](https://travis-ci.org/jazzband/tablib.svg?branch=master)](https://travis-ci.org/jazzband/tablib)
[![codecov](https://codecov.io/gh/jazzband/tablib/branch/master/graph/badge.svg)](https://codecov.io/gh/jazzband/tablib)
_____ ______ ___________ ______
__ /_______ ____ /_ ___ /___(_)___ /_
_ __/_ __ `/__ __ \__ / __ / __ __ \
/ /_ / /_/ / _ /_/ /_ / _ / _ /_/ /
\__/ \__,_/ /_.___/ /_/ /_/ /_.___/
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.)
## 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, DBF, and CSV;
they can be exported to XLSX, XLS, ODS, JSON, YAML, DBF, CSV, TSV, and HTML.
`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 XLSX, XLS, ODS, JSON, and YAML.
## Usage
Populate fresh data files:
```python
headers = ('first_name', 'last_name')
data = [
('John', 'Adams'),
('George', 'Washington')
]
data = tablib.Dataset(*data, headers=headers)
```
Intelligently add new rows:
```python
>>> data.append(('Henry', 'Ford'))
```
Intelligently add new columns:
```python
>>> data.append_col((90, 67, 83), header='age')
```
Slice rows:
```python
>>> print(data[:2])
[('John', 'Adams', 90), ('George', 'Washington', 67)]
```
Slice columns by header:
```python
>>> print(data['first_name'])
['John', 'George', 'Henry']
```
Easily delete rows:
```python
>>> del data[1]
```
## Exports
Drumroll please...........
### JSON!
```python
>>> print(data.export('json'))
[
{
"last_name": "Adams",
"age": 90,
"first_name": "John"
},
{
"last_name": "Ford",
"age": 83,
"first_name": "Henry"
}
]
```
### YAML!
```python
>>> print(data.export('yaml'))
- {age: 90, first_name: John, last_name: Adams}
- {age: 83, first_name: Henry, last_name: Ford}
```
### CSV...
```python
>>> print(data.export('csv'))
first_name,last_name,age
John,Adams,90
Henry,Ford,83
```
### EXCEL!
```python
>>> with open('people.xls', 'wb') as f:
... f.write(data.export('xls'))
```
### DBF!
```python
>>> with open('people.dbf', 'wb') as f:
... f.write(data.export('dbf'))
```
### Pandas DataFrame!
```python
>>> print(data.export('df')):
first_name last_name age
0 John Adams 90
1 Henry Ford 83
```
It's that easy.
## Installation
To install tablib, simply:
```console
$ pip install tablib[pandas]
```
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).
-159
View File
@@ -1,159 +0,0 @@
Tablib: format-agnostic tabular dataset library
===============================================
.. image:: https://travis-ci.org/kennethreitz/tablib.svg?branch=develop
:target: https://travis-ci.org/kennethreitz/tablib
::
_____ ______ ___________ ______
__ /_______ ____ /_ ___ /___(_)___ /_
_ __/_ __ `/__ __ \__ / __ / __ __ \
/ /_ / /_/ / _ /_/ /_ / _ / _ /_/ /
\__/ \__,_/ /_.___/ /_/ /_/ /_.___/
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)
- OSD (Sets)
- CSV (Sets)
- DBF (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, DBF, and CSV; they can be exported to XLSX, XLS, ODS, JSON, YAML, DBF, CSV, TSV, and HTML.
`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 XLSX, XLS, ODS, 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!
++++++
::
>>> with open('people.xls', 'wb') as f:
... f.write(data.xls)
DBF!
++++
::
>>> with open('people.dbf', 'wb') as f:
... f.write(data.dbf)
It's that easy.
Installation
------------
To install tablib, simply: ::
$ pip install tablib
Make sure to check out `Tablib on PyPi <https://pypi.python.org/pypi/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_.
.. _`the repository`: http://github.com/kennethreitz/tablib
.. _AUTHORS: http://github.com/kennethreitz/tablib/blob/master/AUTHORS
+18
View File
@@ -0,0 +1,18 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
"backports.csv" = "*"
odfpy = "*"
openpyxl = ">=2.4.0"
pandas = "*"
xlrd = "*"
xlwt = "*"
PyYAML = "*"
[requires]
python_version = "3.6"
+5 -14
View File
@@ -1,20 +1,11 @@
<h3><a href="http://docs.python-tablib.org">About Tablib</a></h3>
<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>Feedback</h3>
<p>
Feedback is greatly appreciated. If you have any questions, comments,
random praise, or anonymous threats, <a href="mailto:me@kennethreitz.com">
shoot me an email</a>.
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="http://docs.python-tablib.org/">The Tablib Website</a></li>
<li><a href="http://pypi.python.org/pypi/tablib">Tablib @ PyPI</a></li>
<li><a href="http://github.com/kennethreitz/tablib">Tablib @ GitHub</a></li>
<li><a href="http://github.com/kennethreitz/tablib/issues">Issue Tracker</a></li>
<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>
+2 -2
View File
@@ -1,4 +1,4 @@
<h3><a href="http://docs.python-tablib.org/">About Tablib</a></h3>
<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>
</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'
}
-54
View File
@@ -1,54 +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 }}.
</div>
<a href="https://github.com/kennethreitz/tablib">
<img style="position: absolute; top: 0; right: 0; border: 0;" src="//s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" />
</a>
<script type="text/javascript" src="//www.hellobar.com/hellobar.js"></script>
<script type="text/javascript">
new HelloBar(36402,48802);
</script>
<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>
<script type="text/javascript">
(function() {
var t = document.createElement('script');
t.type = 'text/javascript';
t.async = true;
t.id = 'gauges-tracker';
t.setAttribute('data-site-id',
'4ddc284f613f5d2f1a000001');
t.src = '//secure.gaug.es/track.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(t, 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>
-470
View File
@@ -1,470 +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;
}
}
/* scrollbars */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-button:start:decrement,
::-webkit-scrollbar-button:end:increment {
display: block;
height: 10px;
}
::-webkit-scrollbar-button:vertical:increment {
background-color: #fff;
}
::-webkit-scrollbar-track-piece {
background-color: #eee;
-webkit-border-radius: 3px;
}
::-webkit-scrollbar-thumb:vertical {
height: 50px;
background-color: #ccc;
-webkit-border-radius: 3px;
}
::-webkit-scrollbar-thumb:horizontal {
width: 50px;
background-color: #ccc;
-webkit-border-radius: 3px;
}
/* misc. */
.revsys-inline {
display: none!important;
}
-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="https://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
src="//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>`.
+13 -24
View File
@@ -10,18 +10,16 @@
#
# 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.
@@ -41,16 +39,17 @@ master_doc = 'index'
# General information about the project.
project = u'Tablib'
copyright = u'2011. A <a href="http://kennethreitz.com/pages/open-projects.html">Kenneth Reitz</a> Project'
copyright = u'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 = tablib.__version__
# 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 +80,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 +90,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 +119,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.
@@ -183,23 +182,17 @@ htmlhelp_basename = 'Tablibdoc'
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Tablib.tex', u'Tablib Documentation',
u'Kenneth Reitz', 'manual'),
u'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
@@ -230,9 +223,5 @@ latex_additional_files = ['krstyle.sty']
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'tablib', u'Tablib Documentation',
[u'Kenneth Reitz'], 1)
[u'Jazzband'], 1)
]
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'kr'
+52 -52
View File
@@ -8,7 +8,7 @@ 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_.
.. _GitHub: http://github.com/kennethreitz/tablib/
.. _GitHub: https://github.com/jazzband/tablib/
@@ -43,22 +43,22 @@ control machine.
The repository is publicly accessible.
``git clone git://github.com/kennethreitz/tablib.git``
.. code-block:: console
git clone git://github.com/jazzband/tablib.git
The project is hosted on **GitHub**.
GitHub:
http://github.com/kennethreitz/tablib
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.
@@ -67,9 +67,9 @@ Each release is tagged.
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:
@@ -86,11 +86,14 @@ Tablib welcomes new format additions! Format suggestions include:
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.
:class:`tablib.core` follows a simple pattern for automatically utilizing your format throughout Tablib. Function names are crucial.
:class:`tablib.core` follows a simple pattern for automatically utilizing your format throughout Tablib.
Function names are crucial.
Example **tablib/formats/_xxx.py**: ::
@@ -116,17 +119,18 @@ Tablib features a micro-framework for adding format support. The easiest way to
...
# 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 functions.
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. Add your new format module to the :class:`tablib.formats.available` tuple.
2.
Add your new format module to the :class:`tablib.formats.available` tuple.
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.
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>`.
@@ -136,44 +140,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
`Jenkins 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/
.. _jenkins:
$ tox
----------------------
Continuous Integration
----------------------
Every commit made to the **develop** branch is automatically tested and inspected upon receipt with `Travis 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.
https://travis-ci.org/kennethreitz/tablib
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.
.. _`Jenkins CI`: https://travis-ci.org/
.. _`Travis CI`: https://travis-ci.org/
.. _docs:
@@ -182,25 +174,33 @@ 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
$ pip install sphinx
Then, to build an HTML version of the docs, simply run the following from the **docs** directory: ::
Then, to build an HTML version of the docs, simply run the following from the ``docs`` directory:
$ make html
.. code-block:: console
Your ``docs/_build/html`` directory will then contain an HTML representation of the documentation, ready for publication on most web servers.
$ 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 **epub**, **latex**, **json**, *&c* similarly.
.. _`reStructured Text`: http://docutils.sourceforge.net/rst.html
.. _Sphinx: http://sphinx.pocoo.org
.. _`GitHub Pages`: http://pages.github.com
.. _`GitHub Pages`: https://pages.github.com
----------
+28 -14
View File
@@ -22,46 +22,60 @@ Release v\ |version|. (:ref:`Installation <install>`)
.. * :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'])
>>> map(data.append, [('Kenneth', 'Reitz', 22), ('Bessie', 'Monke', 21)])
>>> for i in [('Kenneth', 'Reitz', 22), ('Bessie', 'Monke', 21)]:
... data.append(i)
>>> print data.json
>>> print(data.export('json'))
[{"Last Name": "Reitz", "First Name": "Kenneth", "Age": 22}, {"Last Name": "Monke", "First Name": "Bessie", "Age": 21}]
>>> print data.yaml
>>> print(data.export('yaml'))
- {Age: 22, First Name: Kenneth, Last Name: Reitz}
- {Age: 21, First Name: Bessie, Last Name: Monke}
>>> data.xlsx
<censored binary data>
>>> 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 <http://www.nationalgeographic.com/>`_,
`Digg, Inc <http://digg.com/>`_,
`Northrop Grumman <http://www.northropgrumman.com/>`_,
`Discovery Channel <http://dsc.discovery.com/>`_,
and `The Sunlight Foundation <http://sunlightfoundation.com/>`_ use Tablib internally.
`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!
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.
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 think you nailed the "Python Zen" with tablib.
Thanks again for an awesome lib!
User's Guide
+19 -27
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-extensions>`.
This part of the documentation covers the installation of Tablib. The first step to using any software package is getting it properly installed.
.. _installing:
@@ -14,58 +15,49 @@ Installing Tablib
Distribute & Pip
----------------
Of course, the recommended way to install Tablib is with `pip <http://www.pip-installer.org/>`_::
Of course, the recommended way to install Tablib is with `pip <https://pip.pypa.io>`_:
$ pip install tablib
.. code-block:: console
$ pip install tablib[pandas]
-------------------
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-extensions:
Speed Extensions
----------------
.. 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 extensions 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:
* 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::
+10 -19
View File
@@ -6,16 +6,15 @@ 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
Advanced features include segregation, dynamic columns, tags / filtering, and
seamless format import/export.
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.
@@ -40,8 +39,8 @@ 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
.. _`GPL Licensed`: https://opensource.org/licenses/gpl-license.php
.. _`The MIT License`: https://opensource.org/licenses/mit-license.php
.. _license:
@@ -49,7 +48,7 @@ Tablib is released under terms of `The MIT License`_.
Tablib License
--------------
Copyright 2016 Kenneth Reitz
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
@@ -75,19 +74,11 @@ THE SOFTWARE.
Pythons Supported
-----------------
At this time, the following Python platforms are officially supported:
* cPython 2.5
* cPython 2.6
* cPython 2.7
* cPython 3.1
* cPython 3.2
* PyPy-c 1.4
* PyPy-c 1.5
Support for other Pythons will be rolled out soon.
At this time, the following Python versions are officially supported:
* CPython 2.7
* CPython 3.5
* CPython 3.6
* CPython 3.7
Now, go :ref:`Install Tablib <install>`.
+63 -38
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,8 +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.
@@ -57,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')]
@@ -69,14 +69,16 @@ Adding Headers
--------------
It's time to 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'}]
@@ -93,7 +95,8 @@ Now that we have a basic :class:`Dataset` in place, let's add a column of **ages
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.
@@ -115,28 +118,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
------------------------
@@ -150,7 +161,8 @@ 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']
@@ -158,9 +170,9 @@ To do so, we access the :class:`Dataset` as if it were a standard Python diction
You can also access the column using its index. ::
>>> d.headers
>>> data.headers
['Last Name', 'First Name', 'Age']
>>> d.get_col(1)
>>> data.get_col(1)
['Kenneth', 'Bessie']
Let's find the average age. ::
@@ -175,11 +187,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]
@@ -188,7 +200,6 @@ 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>`
And now for something completely different.
@@ -202,9 +213,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
@@ -216,7 +229,7 @@ Let's add a dynamic column to our :class:`Dataset` object. In this example, we h
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}
@@ -226,7 +239,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. ::
@@ -246,7 +260,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}
@@ -260,9 +274,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 to 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. ::
@@ -281,14 +297,24 @@ Now that we have extra meta-data on our rows, we can easily filter our :class:`D
It's that simple. The original :class:`Dataset` is untouched.
Open an Excel Workbook and read first sheet
-------------------------------------------
To open an Excel 2007 and later workbook with a single sheet (or a workbook with multiple sheets but you just want the first sheet), use the following:
data = tablib.Dataset()
data.xlsx = open('my_excel_file.xlsx', 'rb').read()
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 then to a :class:`Databook` object... ::
Let's say we have 3 different :class:`Datasets <Dataset>`.
All we have to do is add them to a :class:`Databook` object... ::
book = tablib.Databook((data1, data2, data3))
@@ -297,7 +323,7 @@ Let's say we have 3 different :class:`Datasets <Dataset>`. All we have to do is
with open('students.xls', 'wb') as f:
f.write(book.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
@@ -312,9 +338,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,
::
@@ -346,7 +371,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:
+4
View File
@@ -0,0 +1,4 @@
[pytest]
norecursedirs = .git .*
addopts = -rsxX --showlocals --tb=native --cov=tablib --cov-report xml --cov-report term --cov-report html
python_paths = .
+33 -68
View File
@@ -2,91 +2,56 @@
# -*- coding: utf-8 -*-
import os
import re
import sys
import tablib
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
from setuptools import find_packages, setup
if sys.argv[-1] == 'publish':
os.system("python setup.py sdist upload")
sys.exit()
if sys.argv[-1] == 'speedups':
try:
__import__('pip')
except ImportError:
print('Pip required.')
sys.exit(1)
os.system('pip install ujson pyyaml')
sys.exit()
if sys.argv[-1] == 'test':
try:
__import__('py')
except ImportError:
print('py.test required.')
sys.exit(1)
errors = os.system('py.test test_tablib.py')
sys.exit(bool(errors))
packages = [
'tablib', 'tablib.formats',
'tablib.packages',
'tablib.packages.omnijson',
'tablib.packages.unicodecsv',
'tablib.packages.xlwt',
'tablib.packages.xlrd',
'tablib.packages.odf',
'tablib.packages.openpyxl',
'tablib.packages.openpyxl.shared',
'tablib.packages.openpyxl.reader',
'tablib.packages.openpyxl.writer',
'tablib.packages.yaml',
'tablib.packages.dbfpy',
'tablib.packages.xlwt3',
'tablib.packages.xlrd3',
'tablib.packages.odf3',
'tablib.packages.openpyxl3',
'tablib.packages.openpyxl3.shared',
'tablib.packages.openpyxl3.reader',
'tablib.packages.openpyxl3.writer',
'tablib.packages.yaml3',
'tablib.packages.dbfpy3'
install = [
'odfpy',
'openpyxl>=2.4.0',
'backports.csv;python_version<"3.0"',
'markuppy',
'xlrd',
'xlwt',
'pyyaml',
]
setup(
name='tablib',
version=tablib.__version__,
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.org',
url='http://python-tablib.org',
packages=packages,
maintainer='Jazzband',
maintainer_email='roadies@jazzband.co',
url='https://tablib.readthedocs.io',
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',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.0',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
),
tests_require=['pytest'],
'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='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
install_requires=install,
extras_require={
'pandas': ['pandas'],
},
)
+13
View File
@@ -0,0 +1,13 @@
""" Tablib. """
from pkg_resources import get_distribution, DistributionNotFound
from tablib.core import (
Databook, Dataset, detect_format, import_set, import_book,
InvalidDatasetType, InvalidDimensions, UnsupportedFormat
)
try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
# package is not installed
__version__ = None
+36
View File
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
"""
tablib.compat
~~~~~~~~~~~~~
Tablib compatiblity module.
"""
import sys
is_py3 = (sys.version_info[0] > 2)
if is_py3:
from io import StringIO
from statistics import median
from itertools import zip_longest as izip_longest
import csv
import tablib.packages.dbfpy3 as dbfpy
unicode = str
xrange = range
else:
from StringIO import StringIO
from tablib.packages.statistics import median
from itertools import izip_longest
from backports import csv
import tablib.packages.dbfpy as dbfpy
unicode = unicode
xrange = xrange
from MarkupPy import markup # Kept temporarily to avoid breaking existing imports
+62 -75
View File
@@ -5,24 +5,23 @@
This module implements the central Tablib objects.
:copyright: (c) 2016 by Kenneth Reitz.
:copyright: (c) 2016 by Kenneth Reitz. 2019 Jazzband.
:license: MIT, see LICENSE for more details.
"""
from collections import OrderedDict
from copy import copy
from operator import itemgetter
from tablib import formats
from tablib.compat import OrderedDict, unicode
from tablib.compat import unicode
__title__ = 'tablib'
__version__ = '0.11.2'
__build__ = 0x001102
__author__ = 'Kenneth Reitz'
__license__ = 'MIT'
__copyright__ = 'Copyright 2016 Kenneth Reitz'
__copyright__ = 'Copyright 2017 Kenneth Reitz. 2019 Jazzband.'
__docformat__ = 'restructuredtext'
@@ -105,8 +104,6 @@ class Row(object):
return bool(len(set(tag) & set(self.tags)))
class Dataset(object):
"""The :class:`Dataset` object is the heart of Tablib. It provides all core
functionality.
@@ -141,8 +138,9 @@ class Dataset(object):
data = tablib.Dataset(*data, headers=headers)
:param \*args: (optional) list of rows to populate Dataset
:param \\*args: (optional) list of rows to populate Dataset
:param headers: (optional) list strings for Dataset header row
:param title: (optional) string to use as title of the Dataset
.. admonition:: Format Attributes Definition
@@ -171,13 +169,11 @@ class Dataset(object):
self._register_formats()
def __len__(self):
return self.height
def __getitem__(self, key):
if isinstance(key, str) or isinstance(key, unicode):
if isinstance(key, (str, unicode)):
if key in self.headers:
pos = self.headers.index(key) # get 'key' index from each data
return [row[pos] for row in self._data]
@@ -194,9 +190,8 @@ class Dataset(object):
self._validate(value)
self._data[key] = Row(value)
def __delitem__(self, key):
if isinstance(key, str) or isinstance(key, unicode):
if isinstance(key, (str, unicode)):
if key in self.headers:
@@ -212,7 +207,6 @@ class Dataset(object):
else:
del self._data[key]
def __repr__(self):
try:
return '<%s dataset>' % (self.title.lower())
@@ -223,7 +217,8 @@ class Dataset(object):
result = []
# Add unicode representation of headers.
result.append([unicode(h) for h in self.__headers])
if self.__headers:
result.append([unicode(h) for h in self.__headers])
# Add unicode representation of rows.
result.extend(list(map(unicode, row)) for row in self._data)
@@ -232,7 +227,8 @@ class Dataset(object):
field_lens = list(map(max, zip(*lens)))
# delimiter between header and data
result.insert(1, ['-' * length for length in field_lens])
if self.__headers:
result.insert(1, ['-' * length for length in field_lens])
format_string = '|'.join('{%s:%s}' % item for item in enumerate(field_lens))
@@ -263,7 +259,6 @@ class Dataset(object):
except AttributeError:
cls._formats[fmt.title] = (None, None)
def _validate(self, row=None, col=None, safety=False):
"""Assures size of every row in dataset is of proper proportions."""
if row:
@@ -283,7 +278,6 @@ class Dataset(object):
raise InvalidDimensions
return False
def _package(self, dicts=True, ordered=True):
"""Packages Dataset into lists of dictionaries for transmission."""
# TODO: Dicts default to false?
@@ -308,7 +302,6 @@ class Dataset(object):
except IndexError:
raise InvalidDatasetIndex
if self.headers:
if dicts:
data = [dict_pack(list(zip(self.headers, data_row))) for data_row in _data]
@@ -319,8 +312,6 @@ class Dataset(object):
return data
def _get_headers(self):
"""An *optional* list of strings to be used for header rows and attribute names.
@@ -329,7 +320,6 @@ class Dataset(object):
"""
return self.__headers
def _set_headers(self, collection):
"""Validating headers setter."""
self._validate(collection)
@@ -343,7 +333,6 @@ class Dataset(object):
headers = property(_get_headers, _set_headers)
def _get_dict(self):
"""A native Python representation of the :class:`Dataset` object. If headers have
been set, a list of Python dictionaries will be returned. If no headers have been set,
@@ -357,7 +346,6 @@ class Dataset(object):
"""
return self._package()
def _set_dict(self, pickle):
"""A native Python representation of the Dataset object. If headers have been
set, a list of Python dictionaries will be returned. If no headers have been
@@ -390,7 +378,6 @@ class Dataset(object):
dict = property(_get_dict, _set_dict)
def _clean_col(self, col):
"""Prepares the given column for insert/append."""
@@ -408,7 +395,6 @@ class Dataset(object):
return col
@property
def height(self):
"""The number of rows currently in the :class:`Dataset`.
@@ -416,7 +402,6 @@ class Dataset(object):
"""
return len(self._data)
@property
def width(self):
"""The number of columns currently in the :class:`Dataset`.
@@ -431,12 +416,11 @@ class Dataset(object):
except TypeError:
return 0
def load(self, in_stream, format=None, **kwargs):
"""
Import `in_stream` to the :class:`Dataset` object using the `format`.
:param \*\*kwargs: (optional) custom configuration to the format `import_set`.
:param \\*\\*kwargs: (optional) custom configuration to the format `import_set`.
"""
if not format:
@@ -449,13 +433,11 @@ class Dataset(object):
import_set(self, in_stream, **kwargs)
return self
def export(self, format, **kwargs):
"""
Export :class:`Dataset` object to `format`.
:param \*\*kwargs: (optional) custom configuration to the format `export_set`.
:param \\*\\*kwargs: (optional) custom configuration to the format `export_set`.
"""
export_set, import_set = self._formats.get(format, (None, None))
if not export_set:
@@ -523,9 +505,9 @@ class Dataset(object):
Import assumes (for now) that headers exist.
.. admonition:: Binary Warning
.. admonition:: Binary Warning for Python 2
:class:`Dataset.csv` uses \\r\\n line endings by default, so make
:class:`Dataset.csv` uses \\r\\n line endings by default so, in Python 2, make
sure to write in binary mode::
with open('output.csv', 'wb') as f:
@@ -533,10 +515,21 @@ class Dataset(object):
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.
.. admonition:: Line endings for Python 3
:class:`Dataset.csv` uses \\r\\n line endings by default so, in Python 3, 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(data.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.
"""
pass
@property
def tsv():
"""A TSV representation of the :class:`Dataset` object. The top row will contain
@@ -567,6 +560,18 @@ class Dataset(object):
"""
pass
@property
def df():
"""A DataFrame representation of the :class:`Dataset` object.
A dataset object can also be imported by setting the :class:`Dataset.df` attribute: ::
data = tablib.Dataset()
data.df = DataFrame(np.random.randn(6,4))
Import assumes (for now) that headers exist.
"""
pass
@property
def json():
@@ -601,7 +606,7 @@ class Dataset(object):
# To import data from an existing DBF file:
data = tablib.Dataset()
data.dbf = open('existing_table.dbf').read()
data.dbf = open('existing_table.dbf', mode='rb').read()
# to import data from an ASCII-encoded bytestring:
data = tablib.Dataset()
@@ -616,7 +621,6 @@ class Dataset(object):
"""
pass
@property
def latex():
"""A LaTeX booktabs representation of the :class:`Dataset` object. If a
@@ -626,6 +630,13 @@ class Dataset(object):
"""
pass
@property
def jira():
"""A Jira table representation of the :class:`Dataset` object.
.. note:: This method can be used for export only.
"""
pass
# ----
# Rows
@@ -643,7 +654,6 @@ class Dataset(object):
self._validate(row)
self._data.insert(index, Row(row, tags=tags))
def rpush(self, row, tags=list()):
"""Adds a row to the end of the :class:`Dataset`.
See :class:`Dataset.insert` for additional documentation.
@@ -651,7 +661,6 @@ class Dataset(object):
self.insert(self.height, row=row, tags=tags)
def lpush(self, row, tags=list()):
"""Adds a row to the top of the :class:`Dataset`.
See :class:`Dataset.insert` for additional documentation.
@@ -659,7 +668,6 @@ class Dataset(object):
self.insert(0, row=row, tags=tags)
def append(self, row, tags=list()):
"""Adds a row to the :class:`Dataset`.
See :class:`Dataset.insert` for additional documentation.
@@ -675,7 +683,6 @@ class Dataset(object):
for row in rows:
self.append(row, tags)
def lpop(self):
"""Removes and returns the first row of the :class:`Dataset`."""
@@ -684,7 +691,6 @@ class Dataset(object):
return cache
def rpop(self):
"""Removes and returns the last row of the :class:`Dataset`."""
@@ -693,13 +699,11 @@ class Dataset(object):
return cache
def pop(self):
"""Removes and returns the last row of the :class:`Dataset`."""
return self.rpop()
# -------
# Columns
# -------
@@ -754,7 +758,6 @@ class Dataset(object):
self.headers.insert(index, header)
if self.height and self.width:
for i, row in enumerate(self._data):
@@ -764,8 +767,6 @@ class Dataset(object):
else:
self._data = [Row([row]) for row in col]
def rpush_col(self, col, header=None):
"""Adds a column to the end of the :class:`Dataset`.
See :class:`Dataset.insert` for additional documentation.
@@ -773,7 +774,6 @@ class Dataset(object):
self.insert_col(self.width, col, header=header)
def lpush_col(self, col, header=None):
"""Adds a column to the top of the :class:`Dataset`.
See :class:`Dataset.insert` for additional documentation.
@@ -781,14 +781,12 @@ class Dataset(object):
self.insert_col(0, col, header=header)
def insert_separator(self, index, text='-'):
"""Adds a separator to :class:`Dataset` at given index."""
sep = (index, text)
self._separators.append(sep)
def append_separator(self, text='-'):
"""Adds a :ref:`separator <separators>` to the :class:`Dataset`."""
@@ -800,7 +798,6 @@ class Dataset(object):
self.insert_separator(index, text)
def append_col(self, col, header=None):
"""Adds a column to the :class:`Dataset`.
See :class:`Dataset.insert_col` for additional documentation.
@@ -808,27 +805,26 @@ class Dataset(object):
self.rpush_col(col, header)
def get_col(self, index):
"""Returns the column from the :class:`Dataset` at the given index."""
return [row[index] for row in self._data]
# ----
# Misc
# ----
def add_formatter(self, col, handler):
"""Adds a :ref:`formatter` to the :class:`Dataset`.
"""Adds a formatter to the :class:`Dataset`.
.. versionadded:: 0.9.5
:param col: column to. Accepts index int or header str.
:param handler: reference to callback function to execute
against each cell value.
:param col: column to. Accepts index int or header str.
:param handler: reference to callback function to execute against
each cell value.
"""
if isinstance(col, str):
if isinstance(col, unicode):
if col in self.headers:
col = self.headers.index(col) # get 'key' index from each data
else:
@@ -841,7 +837,6 @@ class Dataset(object):
return True
def filter(self, tag):
"""Returns a new instance of the :class:`Dataset`, excluding any rows
that do not contain the given :ref:`tags <tags>`.
@@ -851,7 +846,6 @@ class Dataset(object):
return _dset
def sort(self, col, reverse=False):
"""Sort a :class:`Dataset` by a specific column, given string (for
header) or integer (for column index). The order can be reversed by
@@ -861,7 +855,7 @@ class Dataset(object):
sorted.
"""
if isinstance(col, str) or isinstance(col, unicode):
if isinstance(col, (str, unicode)):
if not self.headers:
raise HeadersNeeded
@@ -887,10 +881,8 @@ class Dataset(object):
row = item
_dset.append(row=row)
return _dset
def transpose(self):
"""Transpose a :class:`Dataset`, turning rows into columns and vice
versa, returning a new ``Dataset`` instance. The first row of the
@@ -919,7 +911,6 @@ class Dataset(object):
_dset.append(row=row_data)
return _dset
def stack(self, other):
"""Stack two :class:`Dataset` instances together by
joining at the row level, and return new combined
@@ -942,7 +933,6 @@ class Dataset(object):
return _dset
def stack_cols(self, other):
"""Stack two :class:`Dataset` instances together by
joining at the column level, and return a new
@@ -976,20 +966,17 @@ class Dataset(object):
return _dset
def remove_duplicates(self):
"""Removes all duplicate rows from the :class:`Dataset` object
while maintaining the original order."""
seen = set()
self._data[:] = [row for row in self._data if not (tuple(row) in seen or seen.add(tuple(row)))]
def wipe(self):
"""Removes all content and headers from the :class:`Dataset` object."""
self._data = list()
self.__headers = None
def subset(self, rows=None, cols=None):
"""Returns a new instance of the :class:`Dataset`,
including only specified rows and columns.
@@ -1030,7 +1017,6 @@ class Dataset(object):
return _dset
class Databook(object):
"""A book of :class:`Dataset` objects.
"""
@@ -1056,7 +1042,6 @@ class Databook(object):
"""Removes all :class:`Dataset` objects from the :class:`Databook`."""
self._datasets = []
@classmethod
def _register_formats(cls):
"""Adds format properties."""
@@ -1082,7 +1067,6 @@ class Databook(object):
else:
raise InvalidDatasetType
def _package(self, ordered=True):
"""Packages :class:`Databook` for delivery."""
collector = []
@@ -1099,17 +1083,16 @@ class Databook(object):
))
return collector
@property
def size(self):
"""The number of the :class:`Dataset` objects within :class:`Databook`."""
return len(self._datasets)
def load(self, format, in_stream, **kwargs):
def load(self, in_stream, format, **kwargs):
"""
Import `in_stream` to the :class:`Databook` object using the `format`.
:param \*\*kwargs: (optional) custom configuration to the format `import_book`.
:param \\*\\*kwargs: (optional) custom configuration to the format `import_book`.
"""
if not format:
@@ -1126,7 +1109,7 @@ class Databook(object):
"""
Export :class:`Databook` object to `format`.
:param \*\*kwargs: (optional) custom configuration to the format `export_book`.
:param \\*\\*kwargs: (optional) custom configuration to the format `export_book`.
"""
export_book, import_book = self._formats.get(format, (None, None))
if not export_book:
@@ -1144,6 +1127,7 @@ def detect_format(stream):
except AttributeError:
pass
def import_set(stream, format=None, **kwargs):
"""Return dataset of given stream."""
@@ -1163,11 +1147,14 @@ class InvalidDatasetType(Exception):
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"
@@ -13,5 +13,9 @@ from . import _xlsx as xlsx
from . import _ods as ods
from . import _dbf as dbf
from . import _latex as latex
from . import _df as df
from . import _rst as rst
from . import _jira as jira
available = (json, xls, yaml, csv, dbf, tsv, html, latex, xlsx, ods)
# xlsx before as xls (xlrd) can also read xlsx
available = (json, xlsx, xls, yaml, csv, dbf, tsv, html, jira, latex, ods, df, rst)
@@ -3,30 +3,34 @@
""" Tablib - *SV Support.
"""
from tablib.compat import is_py3, csv, StringIO
from tablib.compat import csv, StringIO, unicode
title = 'csv'
extensions = ('csv',)
DEFAULT_ENCODING = 'utf-8'
DEFAULT_DELIMITER = ','
DEFAULT_DELIMITER = unicode(',')
def export_set(dataset, **kwargs):
"""Returns CSV representation of Dataset."""
def export_stream_set(dataset, **kwargs):
"""Returns CSV representation of Dataset as file-like."""
stream = StringIO()
kwargs.setdefault('delimiter', DEFAULT_DELIMITER)
if not is_py3:
kwargs.setdefault('encoding', DEFAULT_ENCODING)
_csv = csv.writer(stream, **kwargs)
for row in dataset._package(dicts=False):
_csv.writerow(row)
stream.seek(0)
return stream
def export_set(dataset, **kwargs):
"""Returns CSV representation of Dataset."""
stream = export_stream_set(dataset, **kwargs)
return stream.getvalue()
@@ -36,15 +40,13 @@ def import_set(dset, in_stream, headers=True, **kwargs):
dset.wipe()
kwargs.setdefault('delimiter', DEFAULT_DELIMITER)
if not is_py3:
kwargs.setdefault('encoding', DEFAULT_ENCODING)
rows = csv.reader(StringIO(in_stream), **kwargs)
for i, row in enumerate(rows):
if (i == 0) and (headers):
dset.headers = row
else:
elif row:
dset.append(row)
@@ -53,5 +55,5 @@ def detect(stream, delimiter=DEFAULT_DELIMITER):
try:
csv.Sniffer().sniff(stream, delimiters=delimiter)
return True
except (csv.Error, TypeError):
except Exception:
return False
@@ -55,6 +55,7 @@ def export_set(dataset):
else:
stream = StringIO(dbf_stream.read())
dbf_stream.close()
os.close(temp_file)
os.remove(temp_uri)
return stream.getvalue()
@@ -82,12 +83,5 @@ def detect(stream):
else:
_dbf = dbf.Dbf(StringIO(stream), readOnly=True)
return True
except (ValueError, struct.error):
# When we try to open up a file that's not a DBF, dbfpy raises a
# ValueError.
# When unpacking a string argument with less than 8 chars, struct.error is
# raised.
except Exception:
return False
+43
View File
@@ -0,0 +1,43 @@
""" Tablib - DataFrame Support.
"""
import sys
from io import BytesIO
try:
from pandas import DataFrame
except ImportError:
DataFrame = None
import tablib
from tablib.compat import unicode
title = 'df'
extensions = ('df', )
def detect(stream):
"""Returns True if given stream is a DataFrame."""
if DataFrame is None:
return False
try:
DataFrame(stream)
return True
except ValueError:
return False
def export_set(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
def import_set(dset, in_stream):
"""Returns dataset from DataFrame."""
dset.wipe()
dset.dict = in_stream.to_dict(orient='records')
@@ -3,18 +3,13 @@
""" Tablib - HTML export support.
"""
import codecs
import sys
from io import BytesIO
if sys.version_info[0] > 2:
from io import BytesIO as StringIO
from tablib.packages import markup3 as markup
else:
from cStringIO import StringIO
from tablib.packages import markup
from MarkupPy import markup
import tablib
from tablib.compat import unicode
import codecs
BOOK_ENDINGS = 'h3'
@@ -25,13 +20,13 @@ extensions = ('html', )
def export_set(dataset):
"""HTML representation of a Dataset."""
stream = StringIO()
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]
new_header = [item if item is not None else '' for item in dataset.headers]
page.thead.open()
headers = markup.oneliner.th(new_header)
@@ -39,7 +34,7 @@ def export_set(dataset):
page.thead.close()
for row in dataset:
new_row = [item if item is not None else '' for item in row]
new_row = [item if item is not None else '' for item in row]
html_row = markup.oneliner.td(new_row)
page.tr(html_row)
@@ -56,12 +51,15 @@ def export_set(dataset):
def export_book(databook):
"""HTML representation of a Databook."""
stream = StringIO()
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))
stream.write('<%s>%s</%s>\n' % (BOOK_ENDINGS, title, BOOK_ENDINGS))
stream.write(dset.html)
stream.write('\n')
wrapper.write('<%s>%s</%s>\n' % (BOOK_ENDINGS, title, BOOK_ENDINGS))
wrapper.write(dset.html)
wrapper.write('\n')
return stream.getvalue()
return stream.getvalue().decode('utf-8')
+39
View File
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
"""Tablib - Jira table export support.
Generates a Jira table from the dataset.
"""
from tablib.compat import unicode
title = 'jira'
def export_set(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 = _get_header(dataset.headers) if dataset.headers else ''
body = _get_body(dataset)
return '%s\n%s' % (header, body) if header else body
def _get_body(dataset):
return '\n'.join([_serialize_row(row) for row in dataset])
def _get_header(headers):
return _serialize_row(headers, delimiter='||')
def _serialize_row(row, delimiter='|'):
return '%s%s%s' % (delimiter,
delimiter.join([unicode(item) if item else ' ' for item in row]),
delimiter)
@@ -2,29 +2,34 @@
""" Tablib - JSON Support
"""
import decimal
import json
from uuid import UUID
import tablib
import sys
from tablib.packages import omnijson as json
title = 'json'
extensions = ('json', 'jsn')
def date_handler(obj):
return obj.isoformat() if hasattr(obj, 'isoformat') else obj
def serialize_objects_handler(obj):
if isinstance(obj, (decimal.Decimal, UUID)):
return str(obj)
elif hasattr(obj, 'isoformat'):
return obj.isoformat()
else:
return obj
def export_set(dataset):
"""Returns JSON representation of Dataset."""
return json.dumps(dataset.dict, default=date_handler)
return json.dumps(dataset.dict, default=serialize_objects_handler)
def export_book(databook):
"""Returns JSON representation of Databook."""
return json.dumps(databook._package(), default=date_handler)
return json.dumps(databook._package(), default=serialize_objects_handler)
def import_set(dset, in_stream):
@@ -50,5 +55,5 @@ def detect(stream):
try:
json.loads(stream)
return True
except ValueError:
except (TypeError, ValueError):
return False
@@ -3,15 +3,9 @@
""" Tablib - ODF Support.
"""
import sys
if sys.version_info[0] > 2:
from io import BytesIO
else:
from cStringIO import StringIO as BytesIO
from tablib.compat import opendocument, style, table, text, unicode
from io import BytesIO
from odf import opendocument, style, table, text
from tablib.compat import unicode
title = 'ods'
extensions = ('ods',)
@@ -45,7 +39,6 @@ def export_book(databook):
wb.spreadsheet.addElement(ws)
dset_sheet(dset, ws)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
@@ -97,4 +90,15 @@ def dset_sheet(dataset, ws):
ws.addElement(odf_row)
cell = table.TableCell()
cell.addElement(text.P(text=col))
odf_row.addElement(cell)
odf_row.addElement(cell)
def detect(stream):
if isinstance(stream, bytes):
# load expects a file-like object.
stream = BytesIO(stream)
try:
opendocument.load(stream)
return True
except Exception:
return False
+273
View File
@@ -0,0 +1,273 @@
# -*- coding: utf-8 -*-
""" Tablib - reStructuredText Support
"""
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from textwrap import TextWrapper
from tablib.compat import (
median,
unicode,
izip_longest,
)
title = 'rst'
extensions = ('rst',)
MAX_TABLE_WIDTH = 80 # Roughly. It may be wider to avoid breaking words.
JUSTIFY_LEFT = 'left'
JUSTIFY_CENTER = 'center'
JUSTIFY_RIGHT = 'right'
JUSTIFY_VALUES = (JUSTIFY_LEFT, JUSTIFY_CENTER, JUSTIFY_RIGHT)
def to_unicode(value):
if isinstance(value, bytes):
return value.decode('utf-8')
return unicode(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()))
def _get_column_string_lengths(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_unicode(val)
column_lengths[i].append(len(text))
word_lens[i] = max(word_lens[i], _max_word_len(text))
return column_lengths, word_lens
def _row_to_lines(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_unicode(value)
cell = wrapper.wrap(text)
cells.append(cell)
lines = izip_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
def _get_column_widths(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 = _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
def export_set_as_simple_table(dataset, column_widths=None):
"""
Returns reStructuredText grid table representation of dataset.
"""
lines = []
wrapper = TextWrapper()
if column_widths is None:
column_widths = _get_column_widths(dataset, pad_len=2)
border = ' '.join(['=' * w for w in column_widths])
lines.append(border)
if dataset.headers:
lines.extend(_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(_row_to_lines(values, column_widths, wrapper, ''))
lines.append(border)
return '\n'.join(lines)
def export_set_as_grid_table(dataset, column_widths=None):
"""
Returns reStructuredText grid table representation of dataset.
>>> from tablib import Dataset
>>> from tablib.formats import rst
>>> 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)])
>>> 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 = _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(_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(_row_to_lines(values, column_widths, wrapper))
lines.append(row_sep)
return '\n'.join(lines)
def _use_simple_table(head0, col0, width0):
"""
Use a simple table if the text in the first column is never wrapped
>>> _use_simple_table('menu', ['egg', 'bacon'], 10)
True
>>> _use_simple_table(None, ['lobster thermidor', 'spam'], 10)
False
"""
if head0 is not None:
head0 = to_unicode(head0)
if len(head0) > width0:
return False
for cell in col0:
cell = to_unicode(cell)
if len(cell) > width0:
return False
return True
def export_set(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', MAX_TABLE_WIDTH)
column_widths = _get_column_widths(dataset, max_table_width)
use_simple_table = _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 export_set_as_simple_table(dataset, column_widths)
else:
return export_set_as_grid_table(dataset, column_widths)
def export_book(databook):
"""
reStructuredText representation of a Databook.
Tables are separated by a blank line. All tables use the grid
format.
"""
return '\n\n'.join(export_set(dataset, force_grid=True)
for dataset in databook._datasets)
@@ -3,6 +3,7 @@
""" Tablib - TSV (Tab Separated Values) Support.
"""
from tablib.compat import unicode
from tablib.formats._csv import (
export_set as export_set_wrapper,
import_set as import_set_wrapper,
@@ -12,8 +13,7 @@ from tablib.formats._csv import (
title = 'tsv'
extensions = ('tsv',)
DEFAULT_ENCODING = 'utf-8'
DELIMITER = '\t'
DELIMITER = unicode('\t')
def export_set(dataset):
"""Returns TSV representation of Dataset."""
@@ -4,9 +4,13 @@
"""
import sys
from io import BytesIO
from tablib.compat import BytesIO, xlwt, xlrd, XLRDError, xrange
from tablib.compat import xrange
import tablib
import xlrd
import xlwt
from xlrd.biffh import XLRDError
title = 'xls'
extensions = ('xls',)
@@ -21,17 +25,17 @@ def detect(stream):
try:
xlrd.open_workbook(file_contents=stream)
return True
except (TypeError, XLRDError):
pass
except Exception:
pass
try:
xlrd.open_workbook(file_contents=stream.read())
return True
except (AttributeError, XLRDError):
except Exception:
pass
try:
xlrd.open_workbook(filename=stream)
return True
except:
except Exception:
return False
@@ -58,7 +62,6 @@ def export_book(databook):
dset_sheet(dset, ws)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
@@ -119,7 +122,6 @@ def dset_sheet(dataset, ws):
ws.panes_frozen = True
ws.horz_split_pos = 1
# bold separators
elif len(row) < dataset.width:
ws.write(i, j, col, bold)
@@ -4,19 +4,14 @@
"""
import sys
from io import BytesIO
if sys.version_info[0] > 2:
from io import BytesIO
else:
from cStringIO import StringIO as BytesIO
from tablib.compat import openpyxl
import openpyxl
import tablib
Workbook = openpyxl.workbook.Workbook
ExcelWriter = openpyxl.writer.excel.ExcelWriter
get_column_letter = openpyxl.cell.get_column_letter
get_column_letter = openpyxl.utils.get_column_letter
from tablib.compat import unicode
@@ -27,11 +22,14 @@ extensions = ('xlsx',)
def detect(stream):
"""Returns True if given stream is a readable excel file."""
if isinstance(stream, bytes):
# load_workbook expects a file-like object.
stream = BytesIO(stream)
try:
openpyxl.reader.excel.load_workbook(stream)
openpyxl.reader.excel.load_workbook(stream, read_only=True)
return True
except openpyxl.shared.exc.InvalidFileException:
pass
except Exception:
return False
def export_set(dataset, freeze_panes=True):
"""Returns XLSX representation of Dataset."""
@@ -51,14 +49,14 @@ def export_book(databook, freeze_panes=True):
"""Returns XLSX representation of DataBook."""
wb = Workbook()
wb.worksheets = []
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)
dset_sheet(dset, ws, freeze_panes=freeze_panes)
stream = BytesIO()
wb.save(stream)
return stream.getvalue()
@@ -69,8 +67,8 @@ def import_set(dset, in_stream, headers=True):
dset.wipe()
xls_book = openpyxl.reader.excel.load_workbook(BytesIO(in_stream))
sheet = xls_book.get_active_sheet()
xls_book = openpyxl.reader.excel.load_workbook(BytesIO(in_stream), read_only=True)
sheet = xls_book.active
dset.title = sheet.title
@@ -87,7 +85,7 @@ def import_book(dbook, in_stream, headers=True):
dbook.wipe()
xls_book = openpyxl.reader.excel.load_workbook(BytesIO(in_stream))
xls_book = openpyxl.reader.excel.load_workbook(BytesIO(in_stream), read_only=True)
for sheet in xls_book.worksheets:
data = tablib.Dataset()
@@ -111,42 +109,36 @@ def dset_sheet(dataset, ws, freeze_panes=True):
_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['%s%s' % (col_idx, row_number)]
# 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
cell.font = bold
if freeze_panes:
# As already done in #53, but after Merge lost:
# Export Freeze only after first Line
ws.freeze_panes = 'A2'
# 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
cell.font = bold
# 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')
str_col_value = unicode(col)
except TypeError:
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(col)
str_col_value = ''
if '\n' in str_col_value:
cell.alignment = wrap_text
try:
cell.value = col
except (ValueError, TypeError):
cell.value = unicode(col)
@@ -3,26 +3,13 @@
""" 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
import yaml
title = 'yaml'
extensions = ('yaml', 'yml')
def export_set(dataset):
"""Returns YAML representation of Dataset."""
@@ -46,12 +33,13 @@ def import_book(dbook, in_stream):
dbook.wipe()
for sheet in yaml.load(in_stream):
for sheet in yaml.safe_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:
@@ -31,9 +31,9 @@ Examples:
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
print('%s:\t %s (%s)' % (fldName, rec[fldName],
type(rec[fldName])))
print()
dbf.close()
"""
@@ -63,9 +63,10 @@ __author__ = "Jeff Kunce <kuncej@mail.conservation.state.mo.us>"
__all__ = ["Dbf"]
from . import header
from .import record
from . import record
from utils import INVALID_VALUE
class Dbf(object):
"""DBF accessor.
@@ -82,13 +83,13 @@ class Dbf(object):
"""
__slots__ = ("name", "header", "stream",
"_changed", "_new", "_ignore_errors")
"_changed", "_new", "_ignore_errors")
HeaderClass = header.DbfHeader
RecordClass = record.DbfRecord
INVALID_VALUE = INVALID_VALUE
## initialization and creation helpers
# initialization and creation helpers
def __init__(self, f, readOnly=False, new=False, ignoreErrors=False):
"""Initialize instance.
@@ -137,7 +138,7 @@ class Dbf(object):
self._new = bool(new)
self._changed = False
## properties
# properties
closed = property(lambda self: self.stream.closed)
recordCount = property(lambda self: self.header.recordCount)
@@ -149,6 +150,7 @@ class Dbf(object):
def ignoreErrors(self, value):
"""Update `ignoreErrors` flag on the header object and self"""
self.header.ignoreErrors = self._ignore_errors = bool(value)
ignoreErrors = property(
lambda self: self._ignore_errors,
ignoreErrors,
@@ -159,7 +161,7 @@ class Dbf(object):
""")
## protected methods
# protected methods
def _fixIndex(self, index):
"""Return fixed index.
@@ -185,7 +187,7 @@ class Dbf(object):
raise IndexError("Record index out of range")
return index
## iterface methods
# iterface methods
def close(self):
self.flush()
@@ -226,9 +228,9 @@ class Dbf(object):
self.header.addField(*defs)
else:
raise TypeError("At least one record was added, "
"structure can't be changed")
"structure can't be changed")
## 'magic' methods (representation and sequence interface)
# 'magic' methods (representation and sequence interface)
def __repr__(self):
return "Dbf stream '%s'\n" % self.stream + repr(self.header)
@@ -248,19 +250,20 @@ class Dbf(object):
self._changed = True
self._new = False
#def __del__(self):
# """Flush stream upon deletion of the object."""
# self.flush()
# def __del__(self):
# """Flush stream upon deletion of the object."""
# self.flush()
def demoRead(filename):
def demo_read(filename):
_dbf = Dbf(filename, True)
for _rec in _dbf:
print
print()
print(repr(_rec))
_dbf.close()
def demoCreate(filename):
def demo_create(filename):
_dbf = Dbf(filename, new=True)
_dbf.addField(
("NAME", "C", 15),
@@ -269,10 +272,10 @@ def demoCreate(filename):
("BIRTHDATE", "D"),
)
for (_n, _s, _i, _b) in (
("John", "Miller", "YC", (1981, 1, 2)),
("Andy", "Larkin", "AL", (1982, 3, 4)),
("Bill", "Clinth", "", (1983, 5, 6)),
("Bobb", "McNail", "", (1984, 7, 8)),
("John", "Miller", "YC", (1981, 1, 2)),
("Andy", "Larkin", "AL", (1982, 3, 4)),
("Bill", "Clinth", "", (1983, 5, 6)),
("Bobb", "McNail", "", (1984, 7, 8)),
):
_rec = _dbf.newRecord()
_rec["NAME"] = _n
@@ -283,10 +286,12 @@ def demoCreate(filename):
print(repr(_dbf))
_dbf.close()
if (__name__=='__main__'):
import sys
_name = len(sys.argv) > 1 and sys.argv[1] or "county.dbf"
demoCreate(_name)
demoRead(_name)
# vim: set et sw=4 sts=4 :
if __name__ == '__main__':
import sys
_name = len(sys.argv) > 1 and sys.argv[1] or "county.dbf"
demo_create(_name)
demo_read(_name)
# vim: set et sw=4 sts=4 :
@@ -29,6 +29,7 @@ from fields import *
from header import *
from record import *
class _FieldDefinition(object):
"""Field definition.
@@ -151,38 +152,38 @@ class dbf_new(object):
_dbfh.write(stream)
if (__name__=='__main__'):
if __name__ == '__main__':
# create a new DBF-File
dbfn=dbf_new()
dbfn.add_field("name",'C',80)
dbfn.add_field("price",'N',10,2)
dbfn.add_field("date",'D',8)
dbfn = dbf_new()
dbfn.add_field("name", 'C', 80)
dbfn.add_field("price", 'N', 10, 2)
dbfn.add_field("date", 'D', 8)
dbfn.write("tst.dbf")
# test new dbf
print "*** created tst.dbf: ***"
print("*** created tst.dbf: ***")
dbft = Dbf('tst.dbf', readOnly=0)
print repr(dbft)
print(repr(dbft))
# add a record
rec=DbfRecord(dbft)
rec['name']='something'
rec['price']=10.5
rec['date']=(2000,1,12)
rec = DbfRecord(dbft)
rec['name'] = 'something'
rec['price'] = 10.5
rec['date'] = (2000, 1, 12)
rec.store()
# add another record
rec=DbfRecord(dbft)
rec['name']='foo and bar'
rec['price']=12234
rec['date']=(1992,7,15)
rec = DbfRecord(dbft)
rec['name'] = 'foo and bar'
rec['price'] = 12234
rec['date'] = (1992, 7, 15)
rec.store()
# show the records
print "*** inserted 2 records into tst.dbf: ***"
print repr(dbft)
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 '%s:\t %s'%(fldName, rec[fldName])
print
print('%s:\t %s' % (fldName, rec[fldName]))
print()
dbft.close()
# vim: set et sts=4 sw=4 :
# vim: set et sts=4 sw=4 :
@@ -31,9 +31,8 @@ Examples:
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
print('%s:\t %s (%s)' % (fldName, rec[fldName],
type(rec[fldName])))
dbf.close()
"""
@@ -66,6 +65,7 @@ from . import header
from . import record
from .utils import INVALID_VALUE
class Dbf(object):
"""DBF accessor.
@@ -82,13 +82,13 @@ class Dbf(object):
"""
__slots__ = ("name", "header", "stream",
"_changed", "_new", "_ignore_errors")
"_changed", "_new", "_ignore_errors")
HeaderClass = header.DbfHeader
RecordClass = record.DbfRecord
INVALID_VALUE = INVALID_VALUE
## initialization and creation helpers
# initialization and creation helpers
def __init__(self, f, readOnly=False, new=False, ignoreErrors=False):
"""Initialize instance.
@@ -137,7 +137,7 @@ class Dbf(object):
self._new = bool(new)
self._changed = False
## properties
# properties
closed = property(lambda self: self.stream.closed)
recordCount = property(lambda self: self.header.recordCount)
@@ -149,6 +149,7 @@ class Dbf(object):
def ignoreErrors(self, value):
"""Update `ignoreErrors` flag on the header object and self"""
self.header.ignoreErrors = self._ignore_errors = bool(value)
ignoreErrors = property(
lambda self: self._ignore_errors,
ignoreErrors,
@@ -159,7 +160,7 @@ class Dbf(object):
""")
## protected methods
# protected methods
def _fixIndex(self, index):
"""Return fixed index.
@@ -185,7 +186,7 @@ class Dbf(object):
raise IndexError("Record index out of range")
return index
## iterface methods
# iterface methods
def close(self):
self.flush()
@@ -227,9 +228,9 @@ class Dbf(object):
self.header.addField(*defs)
else:
raise TypeError("At least one record was added, "
"structure can't be changed")
"structure can't be changed")
## 'magic' methods (representation and sequence interface)
# 'magic' methods (representation and sequence interface)
def __repr__(self):
return "Dbf stream '%s'\n" % self.stream + repr(self.header)
@@ -249,19 +250,20 @@ class Dbf(object):
self._changed = True
self._new = False
#def __del__(self):
# """Flush stream upon deletion of the object."""
# self.flush()
# def __del__(self):
# """Flush stream upon deletion of the object."""
# self.flush()
def demoRead(filename):
def demo_read(filename):
_dbf = Dbf(filename, True)
for _rec in _dbf:
print()
print(repr(_rec))
_dbf.close()
def demoCreate(filename):
def demo_create(filename):
_dbf = Dbf(filename, new=True)
_dbf.addField(
("NAME", "C", 15),
@@ -270,10 +272,10 @@ def demoCreate(filename):
("BIRTHDATE", "D"),
)
for (_n, _s, _i, _b) in (
("John", "Miller", "YC", (1981, 1, 2)),
("Andy", "Larkin", "AL", (1982, 3, 4)),
("Bill", "Clinth", "", (1983, 5, 6)),
("Bobb", "McNail", "", (1984, 7, 8)),
("John", "Miller", "YC", (1981, 1, 2)),
("Andy", "Larkin", "AL", (1982, 3, 4)),
("Bill", "Clinth", "", (1983, 5, 6)),
("Bobb", "McNail", "", (1984, 7, 8)),
):
_rec = _dbf.newRecord()
_rec["NAME"] = _n
@@ -284,10 +286,12 @@ def demoCreate(filename):
print(repr(_dbf))
_dbf.close()
if (__name__=='__main__'):
if __name__ == '__main__':
import sys
_name = len(sys.argv) > 1 and sys.argv[1] or "county.dbf"
demoCreate(_name)
demoRead(_name)
demo_create(_name)
demo_read(_name)
# vim: set et sw=4 sts=4 :
@@ -29,6 +29,7 @@ from .fields import *
from .header import *
from .record import *
class _FieldDefinition(object):
"""Field definition.
@@ -145,28 +146,28 @@ class dbf_new(object):
_dbfStream.close()
if (__name__=='__main__'):
if __name__ == '__main__':
# create a new DBF-File
dbfn=dbf_new()
dbfn.add_field("name",'C',80)
dbfn.add_field("price",'N',10,2)
dbfn.add_field("date",'D',8)
dbfn = dbf_new()
dbfn.add_field("name", 'C', 80)
dbfn.add_field("price", 'N', 10, 2)
dbfn.add_field("date", 'D', 8)
dbfn.write("tst.dbf")
# test new dbf
print("*** created tst.dbf: ***")
dbft = Dbf('tst.dbf', readOnly=0)
print(repr(dbft))
# add a record
rec=DbfRecord(dbft)
rec['name']='something'
rec['price']=10.5
rec['date']=(2000,1,12)
rec = DbfRecord(dbft)
rec['name'] = 'something'
rec['price'] = 10.5
rec['date'] = (2000, 1, 12)
rec.store()
# add another record
rec=DbfRecord(dbft)
rec['name']='foo and bar'
rec['price']=12234
rec['date']=(1992,7,15)
rec = DbfRecord(dbft)
rec['name'] = 'foo and bar'
rec['price'] = 12234
rec['date'] = (1992, 7, 15)
rec.store()
# show the records
@@ -175,7 +176,7 @@ if (__name__=='__main__'):
for i1 in range(len(dbft)):
rec = dbft[i1]
for fldName in dbft.fieldNames:
print('%s:\t %s'%(fldName, rec[fldName]))
print('%s:\t %s' % (fldName, rec[fldName]))
print()
dbft.close()
@@ -56,7 +56,6 @@ class DbfFieldDef(object):
"""
__slots__ = ("name", "decimalCount",
"start", "end", "ignoreErrors")
@@ -220,7 +220,7 @@ class DbfRecord(object):
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)
+24
View File
@@ -0,0 +1,24 @@
from __future__ import division
def median(data):
"""
Return the median (middle value) of numeric data, using the common
"mean of middle two" method. If data is empty, ValueError is raised.
Mimics the behaviour of Python3's statistics.median
>>> median([1, 3, 5])
3
>>> median([1, 3, 5, 7])
4.0
"""
data = sorted(data)
n = len(data)
if not n:
raise ValueError("No median for empty data")
i = n // 2
if n % 2:
return data[i]
return (data[i - 1] + data[i]) / 2
-8
View File
@@ -1,8 +0,0 @@
""" Tablib. """
from tablib.core import (
Databook, Dataset, detect_format, import_set, import_book,
InvalidDatasetType, InvalidDimensions, UnsupportedFormat,
__version__
)
-57
View File
@@ -1,57 +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
import tablib.packages.xlrd3 as xlrd
from tablib.packages.xlrd3.biffh import XLRDError
from tablib.packages import markup3 as markup
from tablib.packages import openpyxl3 as openpyxl
from tablib.packages.odf3 import opendocument, style, text, table
import tablib.packages.dbfpy3 as dbfpy
import csv
from io import StringIO
# py3 mappings
unicode = str
bytes = bytes
basestring = str
xrange = range
else:
from cStringIO import StringIO as BytesIO
from cStringIO import StringIO
import tablib.packages.xlwt as xlwt
import tablib.packages.xlrd as xlrd
from tablib.packages.xlrd.biffh import XLRDError
from tablib.packages import markup
from itertools import ifilter
from tablib.packages import openpyxl
from tablib.packages.odf import opendocument, style, text, table
from tablib.packages import unicodecsv as csv
import tablib.packages.dbfpy as dbfpy
unicode = unicode
xrange = xrange
-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 = u"<%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 = u"%s %s=\"%s\"" % ( out, key, escape( value ) )
else:
out = u"%s %s" % ( out, key )
if between is not None:
out = u"%s>%s</%s>" % ( out, between, tag )
else:
if single:
out = u"%s />" % out
else:
out = u"%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__)
View File
-61
View File
@@ -1,61 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import ANIMNS
from element import Element
# Autogenerated
def Animate(**args):
return Element(qname = (ANIMNS,'animate'), **args)
def Animatecolor(**args):
return Element(qname = (ANIMNS,'animateColor'), **args)
def Animatemotion(**args):
return Element(qname = (ANIMNS,'animateMotion'), **args)
def Animatetransform(**args):
return Element(qname = (ANIMNS,'animateTransform'), **args)
def Audio(**args):
return Element(qname = (ANIMNS,'audio'), **args)
def Command(**args):
return Element(qname = (ANIMNS,'command'), **args)
def Iterate(**args):
return Element(qname = (ANIMNS,'iterate'), **args)
def Par(**args):
return Element(qname = (ANIMNS,'par'), **args)
def Param(**args):
return Element(qname = (ANIMNS,'param'), **args)
def Seq(**args):
return Element(qname = (ANIMNS,'seq'), **args)
def Set(**args):
return Element(qname = (ANIMNS,'set'), **args)
def Transitionfilter(**args):
return Element(qname = (ANIMNS,'transitionFilter'), **args)
File diff suppressed because it is too large Load Diff
-87
View File
@@ -1,87 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import CHARTNS
from element import Element
# Autogenerated
def Axis(**args):
return Element(qname = (CHARTNS,'axis'), **args)
def Categories(**args):
return Element(qname = (CHARTNS,'categories'), **args)
def Chart(**args):
return Element(qname = (CHARTNS,'chart'), **args)
def DataPoint(**args):
return Element(qname = (CHARTNS,'data-point'), **args)
def Domain(**args):
return Element(qname = (CHARTNS,'domain'), **args)
def ErrorIndicator(**args):
return Element(qname = (CHARTNS,'error-indicator'), **args)
def Floor(**args):
return Element(qname = (CHARTNS,'floor'), **args)
def Footer(**args):
return Element(qname = (CHARTNS,'footer'), **args)
def Grid(**args):
return Element(qname = (CHARTNS,'grid'), **args)
def Legend(**args):
return Element(qname = (CHARTNS,'legend'), **args)
def MeanValue(**args):
return Element(qname = (CHARTNS,'mean-value'), **args)
def PlotArea(**args):
return Element(qname = (CHARTNS,'plot-area'), **args)
def RegressionCurve(**args):
return Element(qname = (CHARTNS,'regression-curve'), **args)
def Series(**args):
return Element(qname = (CHARTNS,'series'), **args)
def StockGainMarker(**args):
return Element(qname = (CHARTNS,'stock-gain-marker'), **args)
def StockLossMarker(**args):
return Element(qname = (CHARTNS,'stock-loss-marker'), **args)
def StockRangeLine(**args):
return Element(qname = (CHARTNS,'stock-range-line'), **args)
def Subtitle(**args):
return Element(qname = (CHARTNS,'subtitle'), **args)
def SymbolImage(**args):
return Element(qname = (CHARTNS,'symbol-image'), **args)
def Title(**args):
return Element(qname = (CHARTNS,'title'), **args)
def Wall(**args):
return Element(qname = (CHARTNS,'wall'), **args)
-39
View File
@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import CONFIGNS
from element import Element
# Autogenerated
def ConfigItem(**args):
return Element(qname = (CONFIGNS, 'config-item'), **args)
def ConfigItemMapEntry(**args):
return Element(qname = (CONFIGNS,'config-item-map-entry'), **args)
def ConfigItemMapIndexed(**args):
return Element(qname = (CONFIGNS,'config-item-map-indexed'), **args)
def ConfigItemMapNamed(**args):
return Element(qname = (CONFIGNS,'config-item-map-named'), **args)
def ConfigItemSet(**args):
return Element(qname = (CONFIGNS, 'config-item-set'), **args)
-72
View File
@@ -1,72 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import DCNS
from element import Element
# Autogenerated
def Creator(**args):
return Element(qname = (DCNS,'creator'), **args)
def Date(**args):
return Element(qname = (DCNS,'date'), **args)
def Description(**args):
return Element(qname = (DCNS,'description'), **args)
def Language(**args):
return Element(qname = (DCNS,'language'), **args)
def Subject(**args):
return Element(qname = (DCNS,'subject'), **args)
def Title(**args):
return Element(qname = (DCNS,'title'), **args)
# The following complete the Dublin Core elements, but there is no
# guarantee a compliant implementation of OpenDocument will preserve
# these elements
#def Contributor(**args):
# return Element(qname = (DCNS,'contributor'), **args)
#def Coverage(**args):
# return Element(qname = (DCNS,'coverage'), **args)
#def Format(**args):
# return Element(qname = (DCNS,'format'), **args)
#def Identifier(**args):
# return Element(qname = (DCNS,'identifier'), **args)
#def Publisher(**args):
# return Element(qname = (DCNS,'publisher'), **args)
#def Relation(**args):
# return Element(qname = (DCNS,'relation'), **args)
#def Rights(**args):
# return Element(qname = (DCNS,'rights'), **args)
#def Source(**args):
# return Element(qname = (DCNS,'source'), **args)
#def Type(**args):
# return Element(qname = (DCNS,'type'), **args)
-43
View File
@@ -1,43 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import DR3DNS
from element import Element
from draw import StyleRefElement
# Autogenerated
def Cube(**args):
return StyleRefElement(qname = (DR3DNS,'cube'), **args)
def Extrude(**args):
return StyleRefElement(qname = (DR3DNS,'extrude'), **args)
def Light(Element):
return StyleRefElement(qname = (DR3DNS,'light'), **args)
def Rotate(**args):
return StyleRefElement(qname = (DR3DNS,'rotate'), **args)
def Scene(**args):
return StyleRefElement(qname = (DR3DNS,'scene'), **args)
def Sphere(**args):
return StyleRefElement(qname = (DR3DNS,'sphere'), **args)
-182
View File
@@ -1,182 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import DRAWNS, STYLENS, PRESENTATIONNS
from element import Element
def StyleRefElement(stylename=None, classnames=None, **args):
qattrs = {}
if stylename is not None:
f = stylename.getAttrNS(STYLENS, 'family')
if f == 'graphic':
qattrs[(DRAWNS,u'style-name')]= stylename
elif f == 'presentation':
qattrs[(PRESENTATIONNS,u'style-name')]= stylename
else:
raise ValueError, "Style's family must be either 'graphic' or 'presentation'"
if classnames is not None:
f = classnames[0].getAttrNS(STYLENS, 'family')
if f == 'graphic':
qattrs[(DRAWNS,u'class-names')]= classnames
elif f == 'presentation':
qattrs[(PRESENTATIONNS,u'class-names')]= classnames
else:
raise ValueError, "Style's family must be either 'graphic' or 'presentation'"
return Element(qattributes=qattrs, **args)
def DrawElement(name=None, **args):
e = Element(name=name, **args)
if not args.has_key('displayname'):
e.setAttrNS(DRAWNS,'display-name', name)
return e
# Autogenerated
def A(**args):
return Element(qname = (DRAWNS,'a'), **args)
def Applet(**args):
return Element(qname = (DRAWNS,'applet'), **args)
def AreaCircle(**args):
return Element(qname = (DRAWNS,'area-circle'), **args)
def AreaPolygon(**args):
return Element(qname = (DRAWNS,'area-polygon'), **args)
def AreaRectangle(**args):
return Element(qname = (DRAWNS,'area-rectangle'), **args)
def Caption(**args):
return StyleRefElement(qname = (DRAWNS,'caption'), **args)
def Circle(**args):
return StyleRefElement(qname = (DRAWNS,'circle'), **args)
def Connector(**args):
return StyleRefElement(qname = (DRAWNS,'connector'), **args)
def ContourPath(**args):
return Element(qname = (DRAWNS,'contour-path'), **args)
def ContourPolygon(**args):
return Element(qname = (DRAWNS,'contour-polygon'), **args)
def Control(**args):
return StyleRefElement(qname = (DRAWNS,'control'), **args)
def CustomShape(**args):
return StyleRefElement(qname = (DRAWNS,'custom-shape'), **args)
def Ellipse(**args):
return StyleRefElement(qname = (DRAWNS,'ellipse'), **args)
def EnhancedGeometry(**args):
return Element(qname = (DRAWNS,'enhanced-geometry'), **args)
def Equation(**args):
return Element(qname = (DRAWNS,'equation'), **args)
def FillImage(**args):
return DrawElement(qname = (DRAWNS,'fill-image'), **args)
def FloatingFrame(**args):
return Element(qname = (DRAWNS,'floating-frame'), **args)
def Frame(**args):
return StyleRefElement(qname = (DRAWNS,'frame'), **args)
def G(**args):
return StyleRefElement(qname = (DRAWNS,'g'), **args)
def GluePoint(**args):
return Element(qname = (DRAWNS,'glue-point'), **args)
def Gradient(**args):
return DrawElement(qname = (DRAWNS,'gradient'), **args)
def Handle(**args):
return Element(qname = (DRAWNS,'handle'), **args)
def Hatch(**args):
return DrawElement(qname = (DRAWNS,'hatch'), **args)
def Image(**args):
return Element(qname = (DRAWNS,'image'), **args)
def ImageMap(**args):
return Element(qname = (DRAWNS,'image-map'), **args)
def Layer(**args):
return Element(qname = (DRAWNS,'layer'), **args)
def LayerSet(**args):
return Element(qname = (DRAWNS,'layer-set'), **args)
def Line(**args):
return StyleRefElement(qname = (DRAWNS,'line'), **args)
def Marker(**args):
return DrawElement(qname = (DRAWNS,'marker'), **args)
def Measure(**args):
return StyleRefElement(qname = (DRAWNS,'measure'), **args)
def Object(**args):
return Element(qname = (DRAWNS,'object'), **args)
def ObjectOle(**args):
return Element(qname = (DRAWNS,'object-ole'), **args)
def Opacity(**args):
return DrawElement(qname = (DRAWNS,'opacity'), **args)
def Page(**args):
return Element(qname = (DRAWNS,'page'), **args)
def PageThumbnail(**args):
return StyleRefElement(qname = (DRAWNS,'page-thumbnail'), **args)
def Param(**args):
return Element(qname = (DRAWNS,'param'), **args)
def Path(**args):
return StyleRefElement(qname = (DRAWNS,'path'), **args)
def Plugin(**args):
return Element(qname = (DRAWNS,'plugin'), **args)
def Polygon(**args):
return StyleRefElement(qname = (DRAWNS,'polygon'), **args)
def Polyline(**args):
return StyleRefElement(qname = (DRAWNS,'polyline'), **args)
def Rect(**args):
return StyleRefElement(qname = (DRAWNS,'rect'), **args)
def RegularPolygon(**args):
return StyleRefElement(qname = (DRAWNS,'regular-polygon'), **args)
def StrokeDash(**args):
return DrawElement(qname = (DRAWNS,'stroke-dash'), **args)
def TextBox(**args):
return Element(qname = (DRAWNS,'text-box'), **args)
-103
View File
@@ -1,103 +0,0 @@
# -*- coding: utf-8 -*-
# Create a <text:list-style> element from a text string.
# Copyright (C) 2008 J. David Eisenberg
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Contributor(s):
#
import re
from style import Style, TextProperties, ListLevelProperties
from text import ListStyle,ListLevelStyleNumber,ListLevelStyleBullet
"""
Create a <text:list-style> element from a string or array.
List styles require a lot of code to create one level at a time.
These routines take a string and delimiter, or a list of
strings, and creates a <text:list-style> element for you.
Each item in the string (or array) represents a list level
* style for levels 1-10.</p>
*
* <p>If an item contains <code>1</code>, <code>I</code>,
* <code>i</code>, <code>A</code>, or <code>a</code>, then it is presumed
* to be a numbering style; otherwise it is a bulleted style.</p>
"""
_MAX_LIST_LEVEL = 10
SHOW_ALL_LEVELS = True
SHOW_ONE_LEVEL = False
def styleFromString(name, specifiers, delim, spacing, showAllLevels):
specArray = specifiers.split(delim)
return styleFromList( name, specArray, spacing, showAllLevels )
def styleFromList( styleName, specArray, spacing, showAllLevels):
bullet = ""
numPrefix = ""
numSuffix = ""
numberFormat = ""
cssLengthNum = 0
cssLengthUnits = ""
numbered = False
displayLevels = 0
listStyle = ListStyle(name=styleName)
numFormatPattern = re.compile("([1IiAa])")
cssLengthPattern = re.compile("([^a-z]+)\\s*([a-z]+)?")
m = cssLengthPattern.search( spacing )
if (m != None):
cssLengthNum = float(m.group(1))
if (m.lastindex == 2):
cssLengthUnits = m.group(2)
i = 0
while i < len(specArray):
specification = specArray[i]
m = numFormatPattern.search(specification)
if (m != None):
numberFormat = m.group(1)
numPrefix = specification[0:m.start(1)]
numSuffix = specification[m.end(1):]
bullet = ""
numbered = True
if (showAllLevels):
displayLevels = i + 1
else:
displayLevels = 1
else: # it's a bullet style
bullet = specification
numPrefix = ""
numSuffix = ""
numberFormat = ""
displayLevels = 1
numbered = False
if (numbered):
lls = ListLevelStyleNumber(level=(i+1))
if (numPrefix != ''):
lls.setAttribute('numprefix', numPrefix)
if (numSuffix != ''):
lls.setAttribute('numsuffix', numSuffix)
lls.setAttribute('displaylevels', displayLevels)
else:
lls = ListLevelStyleBullet(level=(i+1),bulletchar=bullet[0])
llp = ListLevelProperties()
llp.setAttribute('spacebefore', str(cssLengthNum * (i+1)) + cssLengthUnits)
llp.setAttribute('minlabelwidth', str(cssLengthNum) + cssLengthUnits)
lls.addElement( llp )
listStyle.addElement(lls)
i += 1
return listStyle
# vim: set expandtab sw=4 :
-513
View File
@@ -1,513 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2007-2010 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
# Note: This script has copied a lot of text from xml.dom.minidom.
# Whatever license applies to that file also applies to this file.
#
import xml.dom
from xml.dom.minicompat import *
from namespaces import nsdict
import grammar
from attrconverters import AttrConverters
# The following code is pasted form xml.sax.saxutils
# Tt makes it possible to run the code without the xml sax package installed
# To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
def _escape(data, entities={}):
""" Escape &, <, and > in a string of data.
You can escape other strings of data by passing a dictionary as
the optional entities parameter. The keys and values must all be
strings; each key will be replaced with its corresponding value.
"""
data = data.replace("&", "&amp;")
data = data.replace("<", "&lt;")
data = data.replace(">", "&gt;")
for chars, entity in entities.items():
data = data.replace(chars, entity)
return data
def _quoteattr(data, entities={}):
""" Escape and quote an attribute value.
Escape &, <, and > in a string of data, then quote it for use as
an attribute value. The \" character will be escaped as well, if
necessary.
You can escape other strings of data by passing a dictionary as
the optional entities parameter. The keys and values must all be
strings; each key will be replaced with its corresponding value.
"""
entities['\n']='&#10;'
entities['\r']='&#12;'
data = _escape(data, entities)
if '"' in data:
if "'" in data:
data = '"%s"' % data.replace('"', "&quot;")
else:
data = "'%s'" % data
else:
data = '"%s"' % data
return data
def _nssplit(qualifiedName):
""" Split a qualified name into namespace part and local part. """
fields = qualifiedName.split(':', 1)
if len(fields) == 2:
return fields
else:
return (None, fields[0])
def _nsassign(namespace):
return nsdict.setdefault(namespace,"ns" + str(len(nsdict)))
# Exceptions
class IllegalChild(StandardError):
""" Complains if you add an element to a parent where it is not allowed """
class IllegalText(StandardError):
""" Complains if you add text or cdata to an element where it is not allowed """
class Node(xml.dom.Node):
""" super class for more specific nodes """
parentNode = None
nextSibling = None
previousSibling = None
def hasChildNodes(self):
""" Tells whether this element has any children; text nodes,
subelements, whatever.
"""
if self.childNodes:
return True
else:
return False
def _get_childNodes(self):
return self.childNodes
def _get_firstChild(self):
if self.childNodes:
return self.childNodes[0]
def _get_lastChild(self):
if self.childNodes:
return self.childNodes[-1]
def insertBefore(self, newChild, refChild):
""" Inserts the node newChild before the existing child node refChild.
If refChild is null, insert newChild at the end of the list of children.
"""
if newChild.nodeType not in self._child_node_types:
raise IllegalChild, "%s cannot be child of %s" % (newChild.tagName, self.tagName)
if newChild.parentNode is not None:
newChild.parentNode.removeChild(newChild)
if refChild is None:
self.appendChild(newChild)
else:
try:
index = self.childNodes.index(refChild)
except ValueError:
raise xml.dom.NotFoundErr()
self.childNodes.insert(index, newChild)
newChild.nextSibling = refChild
refChild.previousSibling = newChild
if index:
node = self.childNodes[index-1]
node.nextSibling = newChild
newChild.previousSibling = node
else:
newChild.previousSibling = None
newChild.parentNode = self
return newChild
def appendChild(self, newChild):
""" Adds the node newChild to the end of the list of children of this node.
If the newChild is already in the tree, it is first removed.
"""
if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
for c in tuple(newChild.childNodes):
self.appendChild(c)
### The DOM does not clearly specify what to return in this case
return newChild
if newChild.nodeType not in self._child_node_types:
raise IllegalChild, "<%s> is not allowed in %s" % ( newChild.tagName, self.tagName)
if newChild.parentNode is not None:
newChild.parentNode.removeChild(newChild)
_append_child(self, newChild)
newChild.nextSibling = None
return newChild
def removeChild(self, oldChild):
""" Removes the child node indicated by oldChild from the list of children, and returns it.
"""
#FIXME: update ownerDocument.element_dict or find other solution
try:
self.childNodes.remove(oldChild)
except ValueError:
raise xml.dom.NotFoundErr()
if oldChild.nextSibling is not None:
oldChild.nextSibling.previousSibling = oldChild.previousSibling
if oldChild.previousSibling is not None:
oldChild.previousSibling.nextSibling = oldChild.nextSibling
oldChild.nextSibling = oldChild.previousSibling = None
if self.ownerDocument:
self.ownerDocument.clear_caches()
oldChild.parentNode = None
return oldChild
def __str__(self):
val = []
for c in self.childNodes:
val.append(str(c))
return ''.join(val)
def __unicode__(self):
val = []
for c in self.childNodes:
val.append(unicode(c))
return u''.join(val)
defproperty(Node, "firstChild", doc="First child node, or None.")
defproperty(Node, "lastChild", doc="Last child node, or None.")
def _append_child(self, node):
# fast path with less checks; usable by DOM builders if careful
childNodes = self.childNodes
if childNodes:
last = childNodes[-1]
node.__dict__["previousSibling"] = last
last.__dict__["nextSibling"] = node
childNodes.append(node)
node.__dict__["parentNode"] = self
class Childless:
""" Mixin that makes childless-ness easy to implement and avoids
the complexity of the Node methods that deal with children.
"""
attributes = None
childNodes = EmptyNodeList()
firstChild = None
lastChild = None
def _get_firstChild(self):
return None
def _get_lastChild(self):
return None
def appendChild(self, node):
""" Raises an error """
raise xml.dom.HierarchyRequestErr(
self.tagName + " nodes cannot have children")
def hasChildNodes(self):
return False
def insertBefore(self, newChild, refChild):
""" Raises an error """
raise xml.dom.HierarchyRequestErr(
self.tagName + " nodes do not have children")
def removeChild(self, oldChild):
""" Raises an error """
raise xml.dom.NotFoundErr(
self.tagName + " nodes do not have children")
def replaceChild(self, newChild, oldChild):
""" Raises an error """
raise xml.dom.HierarchyRequestErr(
self.tagName + " nodes do not have children")
class Text(Childless, Node):
nodeType = Node.TEXT_NODE
tagName = "Text"
def __init__(self, data):
self.data = data
def __str__(self):
return self.data.encode()
def __unicode__(self):
return self.data
def toXml(self,level,f):
""" Write XML in UTF-8 """
if self.data:
f.write(_escape(unicode(self.data).encode('utf-8')))
class CDATASection(Childless, Text):
nodeType = Node.CDATA_SECTION_NODE
def toXml(self,level,f):
""" Generate XML output of the node. If the text contains "]]>", then
escape it by going out of CDATA mode (]]>), then write the string
and then go into CDATA mode again. (<![CDATA[)
"""
if self.data:
f.write('<![CDATA[%s]]>' % self.data.replace(']]>',']]>]]><![CDATA['))
class Element(Node):
""" Creates a arbitrary element and is intended to be subclassed not used on its own.
This element is the base of every element it defines a class which resembles
a xml-element. The main advantage of this kind of implementation is that you don't
have to create a toXML method for every different object. Every element
consists of an attribute, optional subelements, optional text and optional cdata.
"""
nodeType = Node.ELEMENT_NODE
namespaces = {} # Due to shallow copy this is a static variable
_child_node_types = (Node.ELEMENT_NODE,
Node.PROCESSING_INSTRUCTION_NODE,
Node.COMMENT_NODE,
Node.TEXT_NODE,
Node.CDATA_SECTION_NODE,
Node.ENTITY_REFERENCE_NODE)
def __init__(self, attributes=None, text=None, cdata=None, qname=None, qattributes=None, check_grammar=True, **args):
if qname is not None:
self.qname = qname
assert(hasattr(self, 'qname'))
self.ownerDocument = None
self.childNodes=[]
self.allowed_children = grammar.allowed_children.get(self.qname)
prefix = self.get_nsprefix(self.qname[0])
self.tagName = prefix + ":" + self.qname[1]
if text is not None:
self.addText(text)
if cdata is not None:
self.addCDATA(cdata)
allowed_attrs = self.allowed_attributes()
if allowed_attrs is not None:
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
self.attributes={}
# Load the attributes from the 'attributes' argument
if attributes:
for attr, value in attributes.items():
self.setAttribute(attr, value)
# Load the qualified attributes
if qattributes:
for attr, value in qattributes.items():
self.setAttrNS(attr[0], attr[1], value)
if allowed_attrs is not None:
# Load the attributes from the 'args' argument
for arg in args.keys():
self.setAttribute(arg, args[arg])
else:
for arg in args.keys(): # If any attribute is allowed
self.attributes[arg]=args[arg]
if not check_grammar:
return
# Test that all mandatory attributes have been added.
required = grammar.required_attributes.get(self.qname)
if required:
for r in required:
if self.getAttrNS(r[0],r[1]) is None:
raise AttributeError, "Required attribute missing: %s in <%s>" % (r[1].lower().replace('-',''), self.tagName)
def get_knownns(self, prefix):
""" Odfpy maintains a list of known namespaces. In some cases a prefix is used, and
we need to know which namespace it resolves to.
"""
global nsdict
for ns,p in nsdict.items():
if p == prefix: return ns
return None
def get_nsprefix(self, namespace):
""" Odfpy maintains a list of known namespaces. In some cases we have a namespace URL,
and needs to look up or assign the prefix for it.
"""
if namespace is None: namespace = ""
prefix = _nsassign(namespace)
if not self.namespaces.has_key(namespace):
self.namespaces[namespace] = prefix
return prefix
def allowed_attributes(self):
return grammar.allowed_attributes.get(self.qname)
def _setOwnerDoc(self, element):
element.ownerDocument = self.ownerDocument
for child in element.childNodes:
self._setOwnerDoc(child)
def addElement(self, element, check_grammar=True):
""" adds an element to an Element
Element.addElement(Element)
"""
if check_grammar and self.allowed_children is not None:
if element.qname not in self.allowed_children:
raise IllegalChild, "<%s> is not allowed in <%s>" % ( element.tagName, self.tagName)
self.appendChild(element)
self._setOwnerDoc(element)
if self.ownerDocument:
self.ownerDocument.rebuild_caches(element)
def addText(self, text, check_grammar=True):
""" Adds text to an element
Setting check_grammar=False turns off grammar checking
"""
if check_grammar and self.qname not in grammar.allows_text:
raise IllegalText, "The <%s> element does not allow text" % self.tagName
else:
if text != '':
self.appendChild(Text(text))
def addCDATA(self, cdata, check_grammar=True):
""" Adds CDATA to an element
Setting check_grammar=False turns off grammar checking
"""
if check_grammar and self.qname not in grammar.allows_text:
raise IllegalText, "The <%s> element does not allow text" % self.tagName
else:
self.appendChild(CDATASection(cdata))
def removeAttribute(self, attr, check_grammar=True):
""" Removes an attribute by name. """
allowed_attrs = self.allowed_attributes()
if allowed_attrs is None:
if type(attr) == type(()):
prefix, localname = attr
self.removeAttrNS(prefix, localname)
else:
raise AttributeError, "Unable to add simple attribute - use (namespace, localpart)"
else:
# Construct a list of allowed arguments
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
if check_grammar and attr not in allowed_args:
raise AttributeError, "Attribute %s is not allowed in <%s>" % ( attr, self.tagName)
i = allowed_args.index(attr)
self.removeAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
def setAttribute(self, attr, value, check_grammar=True):
""" Add an attribute to the element
This is sort of a convenience method. All attributes in ODF have
namespaces. The library knows what attributes are legal and then allows
the user to provide the attribute as a keyword argument and the
library will add the correct namespace.
Must overwrite, If attribute already exists.
"""
allowed_attrs = self.allowed_attributes()
if allowed_attrs is None:
if type(attr) == type(()):
prefix, localname = attr
self.setAttrNS(prefix, localname, value)
else:
raise AttributeError, "Unable to add simple attribute - use (namespace, localpart)"
else:
# Construct a list of allowed arguments
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
if check_grammar and attr not in allowed_args:
raise AttributeError, "Attribute %s is not allowed in <%s>" % ( attr, self.tagName)
i = allowed_args.index(attr)
self.setAttrNS(allowed_attrs[i][0], allowed_attrs[i][1], value)
def setAttrNS(self, namespace, localpart, value):
""" Add an attribute to the element
In case you need to add an attribute the library doesn't know about
then you must provide the full qualified name
It will not check that the attribute is legal according to the schema.
Must overwrite, If attribute already exists.
"""
allowed_attrs = self.allowed_attributes()
prefix = self.get_nsprefix(namespace)
# if allowed_attrs and (namespace, localpart) not in allowed_attrs:
# raise AttributeError, "Attribute %s:%s is not allowed in element <%s>" % ( prefix, localpart, self.tagName)
c = AttrConverters()
self.attributes[(namespace, localpart)] = c.convert((namespace, localpart), value, self)
def getAttrNS(self, namespace, localpart):
prefix = self.get_nsprefix(namespace)
return self.attributes.get((namespace, localpart))
def removeAttrNS(self, namespace, localpart):
del self.attributes[(namespace, localpart)]
def getAttribute(self, attr):
""" Get an attribute value. The method knows which namespace the attribute is in
"""
allowed_attrs = self.allowed_attributes()
if allowed_attrs is None:
if type(attr) == type(()):
prefix, localname = attr
return self.getAttrNS(prefix, localname)
else:
raise AttributeError, "Unable to get simple attribute - use (namespace, localpart)"
else:
# Construct a list of allowed arguments
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
i = allowed_args.index(attr)
return self.getAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
def write_open_tag(self, level, f):
f.write('<'+self.tagName)
if level == 0:
for namespace, prefix in self.namespaces.items():
f.write(' xmlns:' + prefix + '="'+ _escape(str(namespace))+'"')
for qname in self.attributes.keys():
prefix = self.get_nsprefix(qname[0])
f.write(' '+_escape(str(prefix+':'+qname[1]))+'='+_quoteattr(unicode(self.attributes[qname]).encode('utf-8')))
f.write('>')
def write_close_tag(self, level, f):
f.write('</'+self.tagName+'>')
def toXml(self, level, f):
""" Generate XML stream out of the tree structure """
f.write('<'+self.tagName)
if level == 0:
for namespace, prefix in self.namespaces.items():
f.write(' xmlns:' + prefix + '="'+ _escape(str(namespace))+'"')
for qname in self.attributes.keys():
prefix = self.get_nsprefix(qname[0])
f.write(' '+_escape(str(prefix+':'+qname[1]))+'='+_quoteattr(unicode(self.attributes[qname]).encode('utf-8')))
if self.childNodes:
f.write('>')
for element in self.childNodes:
element.toXml(level+1,f)
f.write('</'+self.tagName+'>')
else:
f.write('/>')
def _getElementsByObj(self, obj, accumulator):
if self.qname == obj.qname:
accumulator.append(self)
for e in self.childNodes:
if e.nodeType == Node.ELEMENT_NODE:
accumulator = e._getElementsByObj(obj, accumulator)
return accumulator
def getElementsByType(self, element):
""" Gets elements based on the type, which is function from text.py, draw.py etc. """
obj = element(check_grammar=False)
return self._getElementsByObj(obj,[])
def isInstanceOf(self, element):
""" This is a check to see if the object is an instance of a type """
obj = element(check_grammar=False)
return self.qname == obj.qname
-325
View File
@@ -1,325 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2008 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import *
# Inline element don't cause a box
# They are analogous to the HTML elements SPAN, B, I etc.
inline_elements = (
(TEXTNS,u'a'),
(TEXTNS,u'author-initials'),
(TEXTNS,u'author-name'),
(TEXTNS,u'bibliography-mark'),
(TEXTNS,u'bookmark-ref'),
(TEXTNS,u'chapter'),
(TEXTNS,u'character-count'),
(TEXTNS,u'conditional-text'),
(TEXTNS,u'creation-date'),
(TEXTNS,u'creation-time'),
(TEXTNS,u'creator'),
(TEXTNS,u'database-display'),
(TEXTNS,u'database-name'),
(TEXTNS,u'database-next'),
(TEXTNS,u'database-row-number'),
(TEXTNS,u'database-row-select'),
(TEXTNS,u'date'),
(TEXTNS,u'dde-connection'),
(TEXTNS,u'description'),
(TEXTNS,u'editing-cycles'),
(TEXTNS,u'editing-duration'),
(TEXTNS,u'execute-macro'),
(TEXTNS,u'expression'),
(TEXTNS,u'file-name'),
(TEXTNS,u'hidden-paragraph'),
(TEXTNS,u'hidden-text'),
(TEXTNS,u'image-count'),
(TEXTNS,u'initial-creator'),
(TEXTNS,u'keywords'),
(TEXTNS,u'measure'),
(TEXTNS,u'modification-date'),
(TEXTNS,u'modification-time'),
(TEXTNS,u'note-ref'),
(TEXTNS,u'object-count'),
(TEXTNS,u'page-continuation'),
(TEXTNS,u'page-count'),
(TEXTNS,u'page-number'),
(TEXTNS,u'page-variable-get'),
(TEXTNS,u'page-variable-set'),
(TEXTNS,u'paragraph-count'),
(TEXTNS,u'placeholder'),
(TEXTNS,u'print-date'),
(TEXTNS,u'printed-by'),
(TEXTNS,u'print-time'),
(TEXTNS,u'reference-ref'),
(TEXTNS,u'ruby'),
(TEXTNS,u'ruby-base'),
(TEXTNS,u'ruby-text'),
(TEXTNS,u'script'),
(TEXTNS,u'sender-city'),
(TEXTNS,u'sender-company'),
(TEXTNS,u'sender-country'),
(TEXTNS,u'sender-email'),
(TEXTNS,u'sender-fax'),
(TEXTNS,u'sender-firstname'),
(TEXTNS,u'sender-initials'),
(TEXTNS,u'sender-lastname'),
(TEXTNS,u'sender-phone-private'),
(TEXTNS,u'sender-phone-work'),
(TEXTNS,u'sender-position'),
(TEXTNS,u'sender-postal-code'),
(TEXTNS,u'sender-state-or-province'),
(TEXTNS,u'sender-street'),
(TEXTNS,u'sender-title'),
(TEXTNS,u'sequence'),
(TEXTNS,u'sequence-ref'),
(TEXTNS,u'sheet-name'),
(TEXTNS,u'span'),
(TEXTNS,u'subject'),
(TEXTNS,u'table-count'),
(TEXTNS,u'table-formula'),
(TEXTNS,u'template-name'),
(TEXTNS,u'text-input'),
(TEXTNS,u'time'),
(TEXTNS,u'title'),
(TEXTNS,u'user-defined'),
(TEXTNS,u'user-field-get'),
(TEXTNS,u'user-field-input'),
(TEXTNS,u'variable-get'),
(TEXTNS,u'variable-input'),
(TEXTNS,u'variable-set'),
(TEXTNS,u'word-count'),
)
# It is almost impossible to determine what elements are block elements.
# There are so many that don't fit the form
block_elements = (
(TEXTNS,u'h'),
(TEXTNS,u'p'),
(TEXTNS,u'list'),
(TEXTNS,u'list-item'),
(TEXTNS,u'section'),
)
declarative_elements = (
(OFFICENS,u'font-face-decls'),
(PRESENTATIONNS,u'date-time-decl'),
(PRESENTATIONNS,u'footer-decl'),
(PRESENTATIONNS,u'header-decl'),
(TABLENS,u'table-template'),
(TEXTNS,u'alphabetical-index-entry-template'),
(TEXTNS,u'alphabetical-index-source'),
(TEXTNS,u'bibliography-entry-template'),
(TEXTNS,u'bibliography-source'),
(TEXTNS,u'dde-connection-decls'),
(TEXTNS,u'illustration-index-entry-template'),
(TEXTNS,u'illustration-index-source'),
(TEXTNS,u'index-source-styles'),
(TEXTNS,u'index-title-template'),
(TEXTNS,u'note-continuation-notice-backward'),
(TEXTNS,u'note-continuation-notice-forward'),
(TEXTNS,u'notes-configuration'),
(TEXTNS,u'object-index-entry-template'),
(TEXTNS,u'object-index-source'),
(TEXTNS,u'sequence-decls'),
(TEXTNS,u'table-index-entry-template'),
(TEXTNS,u'table-index-source'),
(TEXTNS,u'table-of-content-entry-template'),
(TEXTNS,u'table-of-content-source'),
(TEXTNS,u'user-field-decls'),
(TEXTNS,u'user-index-entry-template'),
(TEXTNS,u'user-index-source'),
(TEXTNS,u'variable-decls'),
)
empty_elements = (
(ANIMNS,u'animate'),
(ANIMNS,u'animateColor'),
(ANIMNS,u'animateMotion'),
(ANIMNS,u'animateTransform'),
(ANIMNS,u'audio'),
(ANIMNS,u'param'),
(ANIMNS,u'set'),
(ANIMNS,u'transitionFilter'),
(CHARTNS,u'categories'),
(CHARTNS,u'data-point'),
(CHARTNS,u'domain'),
(CHARTNS,u'error-indicator'),
(CHARTNS,u'floor'),
(CHARTNS,u'grid'),
(CHARTNS,u'legend'),
(CHARTNS,u'mean-value'),
(CHARTNS,u'regression-curve'),
(CHARTNS,u'stock-gain-marker'),
(CHARTNS,u'stock-loss-marker'),
(CHARTNS,u'stock-range-line'),
(CHARTNS,u'symbol-image'),
(CHARTNS,u'wall'),
(DR3DNS,u'cube'),
(DR3DNS,u'extrude'),
(DR3DNS,u'light'),
(DR3DNS,u'rotate'),
(DR3DNS,u'sphere'),
(DRAWNS,u'contour-path'),
(DRAWNS,u'contour-polygon'),
(DRAWNS,u'equation'),
(DRAWNS,u'fill-image'),
(DRAWNS,u'floating-frame'),
(DRAWNS,u'glue-point'),
(DRAWNS,u'gradient'),
(DRAWNS,u'handle'),
(DRAWNS,u'hatch'),
(DRAWNS,u'layer'),
(DRAWNS,u'marker'),
(DRAWNS,u'opacity'),
(DRAWNS,u'page-thumbnail'),
(DRAWNS,u'param'),
(DRAWNS,u'stroke-dash'),
(FORMNS,u'connection-resource'),
(FORMNS,u'list-value'),
(FORMNS,u'property'),
(MANIFESTNS,u'algorithm'),
(MANIFESTNS,u'key-derivation'),
(METANS,u'auto-reload'),
(METANS,u'document-statistic'),
(METANS,u'hyperlink-behaviour'),
(METANS,u'template'),
(NUMBERNS,u'am-pm'),
(NUMBERNS,u'boolean'),
(NUMBERNS,u'day'),
(NUMBERNS,u'day-of-week'),
(NUMBERNS,u'era'),
(NUMBERNS,u'fraction'),
(NUMBERNS,u'hours'),
(NUMBERNS,u'minutes'),
(NUMBERNS,u'month'),
(NUMBERNS,u'quarter'),
(NUMBERNS,u'scientific-number'),
(NUMBERNS,u'seconds'),
(NUMBERNS,u'text-content'),
(NUMBERNS,u'week-of-year'),
(NUMBERNS,u'year'),
(OFFICENS,u'dde-source'),
(PRESENTATIONNS,u'date-time'),
(PRESENTATIONNS,u'footer'),
(PRESENTATIONNS,u'header'),
(PRESENTATIONNS,u'placeholder'),
(PRESENTATIONNS,u'play'),
(PRESENTATIONNS,u'show'),
(PRESENTATIONNS,u'sound'),
(SCRIPTNS,u'event-listener'),
(STYLENS,u'column'),
(STYLENS,u'column-sep'),
(STYLENS,u'drop-cap'),
(STYLENS,u'footnote-sep'),
(STYLENS,u'list-level-properties'),
(STYLENS,u'map'),
(STYLENS,u'ruby-properties'),
(STYLENS,u'table-column-properties'),
(STYLENS,u'tab-stop'),
(STYLENS,u'text-properties'),
(SVGNS,u'definition-src'),
(SVGNS,u'font-face-format'),
(SVGNS,u'font-face-name'),
(SVGNS,u'stop'),
(TABLENS,u'body'),
(TABLENS,u'cell-address'),
(TABLENS,u'cell-range-source'),
(TABLENS,u'change-deletion'),
(TABLENS,u'consolidation'),
(TABLENS,u'database-source-query'),
(TABLENS,u'database-source-sql'),
(TABLENS,u'database-source-table'),
(TABLENS,u'data-pilot-display-info'),
(TABLENS,u'data-pilot-field-reference'),
(TABLENS,u'data-pilot-group-member'),
(TABLENS,u'data-pilot-layout-info'),
(TABLENS,u'data-pilot-member'),
(TABLENS,u'data-pilot-sort-info'),
(TABLENS,u'data-pilot-subtotal'),
(TABLENS,u'dependency'),
(TABLENS,u'error-macro'),
(TABLENS,u'even-columns'),
(TABLENS,u'even-rows'),
(TABLENS,u'filter-condition'),
(TABLENS,u'first-column'),
(TABLENS,u'first-row'),
(TABLENS,u'highlighted-range'),
(TABLENS,u'insertion-cut-off'),
(TABLENS,u'iteration'),
(TABLENS,u'label-range'),
(TABLENS,u'last-column'),
(TABLENS,u'last-row'),
(TABLENS,u'movement-cut-off'),
(TABLENS,u'named-expression'),
(TABLENS,u'named-range'),
(TABLENS,u'null-date'),
(TABLENS,u'odd-columns'),
(TABLENS,u'odd-rows'),
(TABLENS,u'operation'),
(TABLENS,u'scenario'),
(TABLENS,u'sort-by'),
(TABLENS,u'sort-groups'),
(TABLENS,u'source-range-address'),
(TABLENS,u'source-service'),
(TABLENS,u'subtotal-field'),
(TABLENS,u'table-column'),
(TABLENS,u'table-source'),
(TABLENS,u'target-range-address'),
(TEXTNS,u'alphabetical-index-auto-mark-file'),
(TEXTNS,u'alphabetical-index-mark'),
(TEXTNS,u'alphabetical-index-mark-end'),
(TEXTNS,u'alphabetical-index-mark-start'),
(TEXTNS,u'bookmark'),
(TEXTNS,u'bookmark-end'),
(TEXTNS,u'bookmark-start'),
(TEXTNS,u'change'),
(TEXTNS,u'change-end'),
(TEXTNS,u'change-start'),
(TEXTNS,u'dde-connection-decl'),
(TEXTNS,u'index-entry-bibliography'),
(TEXTNS,u'index-entry-chapter'),
(TEXTNS,u'index-entry-link-end'),
(TEXTNS,u'index-entry-link-start'),
(TEXTNS,u'index-entry-page-number'),
(TEXTNS,u'index-entry-tab-stop'),
(TEXTNS,u'index-entry-text'),
(TEXTNS,u'index-source-style'),
(TEXTNS,u'line-break'),
(TEXTNS,u'page'),
(TEXTNS,u'reference-mark'),
(TEXTNS,u'reference-mark-end'),
(TEXTNS,u'reference-mark-start'),
(TEXTNS,u's'),
(TEXTNS,u'section-source'),
(TEXTNS,u'sequence-decl'),
(TEXTNS,u'soft-page-break'),
(TEXTNS,u'sort-key'),
(TEXTNS,u'tab'),
(TEXTNS,u'toc-mark'),
(TEXTNS,u'toc-mark-end'),
(TEXTNS,u'toc-mark-start'),
(TEXTNS,u'user-field-decl'),
(TEXTNS,u'user-index-mark'),
(TEXTNS,u'user-index-mark-end'),
(TEXTNS,u'user-index-mark-start'),
(TEXTNS,u'variable-decl')
)
-115
View File
@@ -1,115 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import FORMNS
from element import Element
# Autogenerated
def Button(**args):
return Element(qname = (FORMNS,'button'), **args)
def Checkbox(**args):
return Element(qname = (FORMNS,'checkbox'), **args)
def Column(**args):
return Element(qname = (FORMNS,'column'), **args)
def Combobox(**args):
return Element(qname = (FORMNS,'combobox'), **args)
def ConnectionResource(**args):
return Element(qname = (FORMNS,'connection-resource'), **args)
def Date(**args):
return Element(qname = (FORMNS,'date'), **args)
def File(**args):
return Element(qname = (FORMNS,'file'), **args)
def FixedText(**args):
return Element(qname = (FORMNS,'fixed-text'), **args)
def Form(**args):
return Element(qname = (FORMNS,'form'), **args)
def FormattedText(**args):
return Element(qname = (FORMNS,'formatted-text'), **args)
def Frame(**args):
return Element(qname = (FORMNS,'frame'), **args)
def GenericControl(**args):
return Element(qname = (FORMNS,'generic-control'), **args)
def Grid(**args):
return Element(qname = (FORMNS,'grid'), **args)
def Hidden(**args):
return Element(qname = (FORMNS,'hidden'), **args)
def Image(**args):
return Element(qname = (FORMNS,'image'), **args)
def ImageFrame(**args):
return Element(qname = (FORMNS,'image-frame'), **args)
def Item(**args):
return Element(qname = (FORMNS,'item'), **args)
def ListProperty(**args):
return Element(qname = (FORMNS,'list-property'), **args)
def ListValue(**args):
return Element(qname = (FORMNS,'list-value'), **args)
def Listbox(**args):
return Element(qname = (FORMNS,'listbox'), **args)
def Number(**args):
return Element(qname = (FORMNS,'number'), **args)
def Option(**args):
return Element(qname = (FORMNS,'option'), **args)
def Password(**args):
return Element(qname = (FORMNS,'password'), **args)
def Properties(**args):
return Element(qname = (FORMNS,'properties'), **args)
def Property(**args):
return Element(qname = (FORMNS,'property'), **args)
def Radio(**args):
return Element(qname = (FORMNS,'radio'), **args)
def Text(**args):
return Element(qname = (FORMNS,'text'), **args)
def Textarea(**args):
return Element(qname = (FORMNS,'textarea'), **args)
def Time(**args):
return Element(qname = (FORMNS,'time'), **args)
def ValueRange(**args):
return Element(qname = (FORMNS,'value-range'), **args)
File diff suppressed because it is too large Load Diff
-112
View File
@@ -1,112 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2007-2008 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
# This script is to be embedded in opendocument.py later
# The purpose is to read an ODT/ODP/ODS file and create the datastructure
# in memory. The user should then be able to make operations and then save
# the structure again.
from xml.sax import make_parser,handler
from xml.sax.xmlreader import InputSource
import xml.sax.saxutils
from element import Element
from namespaces import OFFICENS
from cStringIO import StringIO
#
# Parse the XML files
#
class LoadParser(handler.ContentHandler):
""" Extract headings from content.xml of an ODT file """
triggers = (
(OFFICENS, 'automatic-styles'), (OFFICENS, 'body'),
(OFFICENS, 'font-face-decls'), (OFFICENS, 'master-styles'),
(OFFICENS, 'meta'), (OFFICENS, 'scripts'),
(OFFICENS, 'settings'), (OFFICENS, 'styles') )
def __init__(self, document):
self.doc = document
self.data = []
self.level = 0
self.parse = False
def characters(self, data):
if self.parse == False:
return
self.data.append(data)
def startElementNS(self, tag, qname, attrs):
if tag in self.triggers:
self.parse = True
if self.doc._parsing != "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
self.parse = False
if self.parse == False:
return
self.level = self.level + 1
# Add any accumulated text content
content = ''.join(self.data)
if len(content.strip()) > 0:
self.parent.addText(content, check_grammar=False)
self.data = []
# Create the element
attrdict = {}
for (att,value) in attrs.items():
attrdict[att] = value
try:
e = Element(qname = tag, qattributes=attrdict, check_grammar=False)
self.curr = e
except AttributeError, v:
print "Error: %s" % v
if tag == (OFFICENS, 'automatic-styles'):
e = self.doc.automaticstyles
elif tag == (OFFICENS, 'body'):
e = self.doc.body
elif tag == (OFFICENS, 'master-styles'):
e = self.doc.masterstyles
elif tag == (OFFICENS, 'meta'):
e = self.doc.meta
elif tag == (OFFICENS,'scripts'):
e = self.doc.scripts
elif tag == (OFFICENS,'settings'):
e = self.doc.settings
elif tag == (OFFICENS,'styles'):
e = self.doc.styles
elif self.doc._parsing == "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
e = self.doc.fontfacedecls
elif hasattr(self,'parent'):
self.parent.addElement(e, check_grammar=False)
self.parent = e
def endElementNS(self, tag, qname):
if self.parse == False:
return
self.level = self.level - 1
str = ''.join(self.data)
if len(str.strip()) > 0:
self.curr.addText(str, check_grammar=False)
self.data = []
self.curr = self.curr.parentNode
self.parent = self.curr
if tag in self.triggers:
self.parse = False
-41
View File
@@ -1,41 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
#
from namespaces import MANIFESTNS
from element import Element
# Autogenerated
def Manifest(**args):
return Element(qname = (MANIFESTNS,'manifest'), **args)
def FileEntry(**args):
return Element(qname = (MANIFESTNS,'file-entry'), **args)
def EncryptionData(**args):
return Element(qname = (MANIFESTNS,'encryption-data'), **args)
def Algorithm(**args):
return Element(qname = (MANIFESTNS,'algorithm'), **args)
def KeyDerivation(**args):
return Element(qname = (MANIFESTNS,'key-derivation'), **args)
-30
View File
@@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import MATHNS
from element import Element
# ODF 1.0 section 12.5
# Mathematical content is represented by MathML 2.0
# Autogenerated
def Math(**args):
return Element(qname = (MATHNS,'math'), **args)
-66
View File
@@ -1,66 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import METANS
from element import Element
# Autogenerated
def AutoReload(**args):
return Element(qname = (METANS,'auto-reload'), **args)
def CreationDate(**args):
return Element(qname = (METANS,'creation-date'), **args)
def DateString(**args):
return Element(qname = (METANS,'date-string'), **args)
def DocumentStatistic(**args):
return Element(qname = (METANS,'document-statistic'), **args)
def EditingCycles(**args):
return Element(qname = (METANS,'editing-cycles'), **args)
def EditingDuration(**args):
return Element(qname = (METANS,'editing-duration'), **args)
def Generator(**args):
return Element(qname = (METANS,'generator'), **args)
def HyperlinkBehaviour(**args):
return Element(qname = (METANS,'hyperlink-behaviour'), **args)
def InitialCreator(**args):
return Element(qname = (METANS,'initial-creator'), **args)
def Keyword(**args):
return Element(qname = (METANS,'keyword'), **args)
def PrintDate(**args):
return Element(qname = (METANS,'print-date'), **args)
def PrintedBy(**args):
return Element(qname = (METANS,'printed-by'), **args)
def Template(**args):
return Element(qname = (METANS,'template'), **args)
def UserDefined(**args):
return Element(qname = (METANS,'user-defined'), **args)
-97
View File
@@ -1,97 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2010 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
TOOLSVERSION = u"ODFPY/0.9.3"
ANIMNS = u"urn:oasis:names:tc:opendocument:xmlns:animation:1.0"
DBNS = u"urn:oasis:names:tc:opendocument:xmlns:database:1.0"
CHARTNS = u"urn:oasis:names:tc:opendocument:xmlns:chart:1.0"
CONFIGNS = u"urn:oasis:names:tc:opendocument:xmlns:config:1.0"
#DBNS = u"http://openoffice.org/2004/database"
DCNS = u"http://purl.org/dc/elements/1.1/"
DOMNS = u"http://www.w3.org/2001/xml-events"
DR3DNS = u"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"
DRAWNS = u"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
FIELDNS = u"urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0"
FONS = u"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
FORMNS = u"urn:oasis:names:tc:opendocument:xmlns:form:1.0"
GRDDLNS = u"http://www.w3.org/2003/g/data-view#"
KOFFICENS = u"http://www.koffice.org/2005/"
MANIFESTNS = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
MATHNS = u"http://www.w3.org/1998/Math/MathML"
METANS = u"urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
NUMBERNS = u"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"
OFFICENS = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0"
OFNS = u"urn:oasis:names:tc:opendocument:xmlns:of:1.2"
OOONS = u"http://openoffice.org/2004/office"
OOOWNS = u"http://openoffice.org/2004/writer"
OOOCNS = u"http://openoffice.org/2004/calc"
PRESENTATIONNS = u"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"
RDFANS = u"http://docs.oasis-open.org/opendocument/meta/rdfa#"
RPTNS = u"http://openoffice.org/2005/report"
SCRIPTNS = u"urn:oasis:names:tc:opendocument:xmlns:script:1.0"
SMILNS = u"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0"
STYLENS = u"urn:oasis:names:tc:opendocument:xmlns:style:1.0"
SVGNS = u"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
TABLENS = u"urn:oasis:names:tc:opendocument:xmlns:table:1.0"
TEXTNS = u"urn:oasis:names:tc:opendocument:xmlns:text:1.0"
XFORMSNS = u"http://www.w3.org/2002/xforms"
XLINKNS = u"http://www.w3.org/1999/xlink"
XMLNS = u"http://www.w3.org/XML/1998/namespace"
XSDNS = u"http://www.w3.org/2001/XMLSchema"
XSINS = u"http://www.w3.org/2001/XMLSchema-instance"
nsdict = {
ANIMNS: u'anim',
CHARTNS: u'chart',
CONFIGNS: u'config',
DBNS: u'db',
DCNS: u'dc',
DOMNS: u'dom',
DR3DNS: u'dr3d',
DRAWNS: u'draw',
FIELDNS: u'field',
FONS: u'fo',
FORMNS: u'form',
GRDDLNS: u'grddl',
KOFFICENS: u'koffice',
MANIFESTNS: u'manifest',
MATHNS: u'math',
METANS: u'meta',
NUMBERNS: u'number',
OFFICENS: u'office',
OFNS: u'of',
OOONS: u'ooo',
OOOWNS: u'ooow',
OOOCNS: u'oooc',
PRESENTATIONNS: u'presentation',
RDFANS: u'rdfa',
RPTNS: u'rpt',
SCRIPTNS: u'script',
SMILNS: u'smil',
STYLENS: u'style',
SVGNS: u'svg',
TABLENS: u'table',
TEXTNS: u'text',
XFORMSNS: u'xforms',
XLINKNS: u'xlink',
XMLNS: u'xml',
XSDNS: u'xsd',
XSINS: u'xsi',
}
-104
View File
@@ -1,104 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import NUMBERNS
from element import Element
from style import StyleElement
# Autogenerated
def AmPm(**args):
return Element(qname = (NUMBERNS,'am-pm'), **args)
def Boolean(**args):
return Element(qname = (NUMBERNS,'boolean'), **args)
def BooleanStyle(**args):
return StyleElement(qname = (NUMBERNS,'boolean-style'), **args)
def CurrencyStyle(**args):
return StyleElement(qname = (NUMBERNS,'currency-style'), **args)
def CurrencySymbol(**args):
return Element(qname = (NUMBERNS,'currency-symbol'), **args)
def DateStyle(**args):
return StyleElement(qname = (NUMBERNS,'date-style'), **args)
def Day(**args):
return Element(qname = (NUMBERNS,'day'), **args)
def DayOfWeek(**args):
return Element(qname = (NUMBERNS,'day-of-week'), **args)
def EmbeddedText(**args):
return Element(qname = (NUMBERNS,'embedded-text'), **args)
def Era(**args):
return Element(qname = (NUMBERNS,'era'), **args)
def Fraction(**args):
return Element(qname = (NUMBERNS,'fraction'), **args)
def Hours(**args):
return Element(qname = (NUMBERNS,'hours'), **args)
def Minutes(**args):
return Element(qname = (NUMBERNS,'minutes'), **args)
def Month(**args):
return Element(qname = (NUMBERNS,'month'), **args)
def Number(**args):
return Element(qname = (NUMBERNS,'number'), **args)
def NumberStyle(**args):
return StyleElement(qname = (NUMBERNS,'number-style'), **args)
def PercentageStyle(**args):
return StyleElement(qname = (NUMBERNS,'percentage-style'), **args)
def Quarter(**args):
return Element(qname = (NUMBERNS,'quarter'), **args)
def ScientificNumber(**args):
return Element(qname = (NUMBERNS,'scientific-number'), **args)
def Seconds(**args):
return Element(qname = (NUMBERNS,'seconds'), **args)
def Text(**args):
return Element(qname = (NUMBERNS,'text'), **args)
def TextContent(**args):
return Element(qname = (NUMBERNS,'text-content'), **args)
def TextStyle(**args):
return StyleElement(qname = (NUMBERNS,'text-style'), **args)
def TimeStyle(**args):
return StyleElement(qname = (NUMBERNS,'time-style'), **args)
def WeekOfYear(**args):
return Element(qname = (NUMBERNS,'week-of-year'), **args)
def Year(**args):
return Element(qname = (NUMBERNS,'year'), **args)
-579
View File
@@ -1,579 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2008 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# See http://trac.edgewall.org/wiki/WikiFormatting
#
# Contributor(s):
#
import sys, zipfile, xml.dom.minidom
from namespaces import nsdict
from elementtypes import *
IGNORED_TAGS = [
'draw:a'
'draw:g',
'draw:line',
'draw:object-ole',
'office:annotation',
'presentation:notes',
'svg:desc',
] + [ nsdict[item[0]]+":"+item[1] for item in empty_elements]
INLINE_TAGS = [ nsdict[item[0]]+":"+item[1] for item in inline_elements]
class TextProps:
""" Holds properties for a text style. """
def __init__(self):
self.italic = False
self.bold = False
self.fixed = False
self.underlined = False
self.strikethrough = False
self.superscript = False
self.subscript = False
def setItalic(self, value):
if value == "italic":
self.italic = True
elif value == "normal":
self.italic = False
def setBold(self, value):
if value == "bold":
self.bold = True
elif value == "normal":
self.bold = False
def setFixed(self, value):
self.fixed = value
def setUnderlined(self, value):
if value and value != "none":
self.underlined = True
def setStrikethrough(self, value):
if value and value != "none":
self.strikethrough = True
def setPosition(self, value):
if value is None or value == '':
return
posisize = value.split(' ')
textpos = posisize[0]
if textpos.find('%') == -1:
if textpos == "sub":
self.superscript = False
self.subscript = True
elif textpos == "super":
self.superscript = True
self.subscript = False
else:
itextpos = int(textpos[:textpos.find('%')])
if itextpos > 10:
self.superscript = False
self.subscript = True
elif itextpos < -10:
self.superscript = True
self.subscript = False
def __str__(self):
return "[italic=%s, bold=i%s, fixed=%s]" % (str(self.italic),
str(self.bold),
str(self.fixed))
class ParagraphProps:
""" Holds properties of a paragraph style. """
def __init__(self):
self.blockquote = False
self.headingLevel = 0
self.code = False
self.title = False
self.indented = 0
def setIndented(self, value):
self.indented = value
def setHeading(self, level):
self.headingLevel = level
def setTitle(self, value):
self.title = value
def setCode(self, value):
self.code = value
def __str__(self):
return "[bq=%s, h=%d, code=%s]" % (str(self.blockquote),
self.headingLevel,
str(self.code))
class ListProperties:
""" Holds properties for a list style. """
def __init__(self):
self.ordered = False
def setOrdered(self, value):
self.ordered = value
class ODF2MoinMoin(object):
def __init__(self, filepath):
self.footnotes = []
self.footnoteCounter = 0
self.textStyles = {"Standard": TextProps()}
self.paragraphStyles = {"Standard": ParagraphProps()}
self.listStyles = {}
self.fixedFonts = []
self.hasTitle = 0
self.lastsegment = None
# Tags
self.elements = {
'draw:page': self.textToString,
'draw:frame': self.textToString,
'draw:image': self.draw_image,
'draw:text-box': self.textToString,
'text:a': self.text_a,
'text:note': self.text_note,
}
for tag in IGNORED_TAGS:
self.elements[tag] = self.do_nothing
for tag in INLINE_TAGS:
self.elements[tag] = self.inline_markup
self.elements['text:line-break'] = self.text_line_break
self.elements['text:s'] = self.text_s
self.elements['text:tab'] = self.text_tab
self.load(filepath)
def processFontDeclarations(self, fontDecl):
""" Extracts necessary font information from a font-declaration
element.
"""
for fontFace in fontDecl.getElementsByTagName("style:font-face"):
if fontFace.getAttribute("style:font-pitch") == "fixed":
self.fixedFonts.append(fontFace.getAttribute("style:name"))
def extractTextProperties(self, style, parent=None):
""" Extracts text properties from a style element. """
textProps = TextProps()
if parent:
parentProp = self.textStyles.get(parent, None)
if parentProp:
textProp = parentProp
textPropEl = style.getElementsByTagName("style:text-properties")
if not textPropEl: return textProps
textPropEl = textPropEl[0]
textProps.setItalic(textPropEl.getAttribute("fo:font-style"))
textProps.setBold(textPropEl.getAttribute("fo:font-weight"))
textProps.setUnderlined(textPropEl.getAttribute("style:text-underline-style"))
textProps.setStrikethrough(textPropEl.getAttribute("style:text-line-through-style"))
textProps.setPosition(textPropEl.getAttribute("style:text-position"))
if textPropEl.getAttribute("style:font-name") in self.fixedFonts:
textProps.setFixed(True)
return textProps
def extractParagraphProperties(self, style, parent=None):
""" Extracts paragraph properties from a style element. """
paraProps = ParagraphProps()
name = style.getAttribute("style:name")
if name.startswith("Heading_20_"):
level = name[11:]
try:
level = int(level)
paraProps.setHeading(level)
except:
level = 0
if name == "Title":
paraProps.setTitle(True)
paraPropEl = style.getElementsByTagName("style:paragraph-properties")
if paraPropEl:
paraPropEl = paraPropEl[0]
leftMargin = paraPropEl.getAttribute("fo:margin-left")
if leftMargin:
try:
leftMargin = float(leftMargin[:-2])
if leftMargin > 0.01:
paraProps.setIndented(True)
except:
pass
textProps = self.extractTextProperties(style)
if textProps.fixed:
paraProps.setCode(True)
return paraProps
def processStyles(self, styleElements):
""" Runs through "style" elements extracting necessary information.
"""
for style in styleElements:
name = style.getAttribute("style:name")
if name == "Standard": continue
family = style.getAttribute("style:family")
parent = style.getAttribute("style:parent-style-name")
if family == "text":
self.textStyles[name] = self.extractTextProperties(style, parent)
elif family == "paragraph":
self.paragraphStyles[name] = \
self.extractParagraphProperties(style, parent)
self.textStyles[name] = self.extractTextProperties(style, parent)
def processListStyles(self, listStyleElements):
for style in listStyleElements:
name = style.getAttribute("style:name")
prop = ListProperties()
if style.hasChildNodes():
subitems = [el for el in style.childNodes
if el.nodeType == xml.dom.Node.ELEMENT_NODE
and el.tagName == "text:list-level-style-number"]
if len(subitems) > 0:
prop.setOrdered(True)
self.listStyles[name] = prop
def load(self, filepath):
""" Loads an ODT file. """
zip = zipfile.ZipFile(filepath)
styles_doc = xml.dom.minidom.parseString(zip.read("styles.xml"))
fontfacedecls = styles_doc.getElementsByTagName("office:font-face-decls")
if fontfacedecls:
self.processFontDeclarations(fontfacedecls[0])
self.processStyles(styles_doc.getElementsByTagName("style:style"))
self.processListStyles(styles_doc.getElementsByTagName("text:list-style"))
self.content = xml.dom.minidom.parseString(zip.read("content.xml"))
fontfacedecls = self.content.getElementsByTagName("office:font-face-decls")
if fontfacedecls:
self.processFontDeclarations(fontfacedecls[0])
self.processStyles(self.content.getElementsByTagName("style:style"))
self.processListStyles(self.content.getElementsByTagName("text:list-style"))
def compressCodeBlocks(self, text):
""" Removes extra blank lines from code blocks. """
return text
lines = text.split("\n")
buffer = []
numLines = len(lines)
for i in range(numLines):
if (lines[i].strip() or i == numLines-1 or i == 0 or
not ( lines[i-1].startswith(" ")
and lines[i+1].startswith(" ") ) ):
buffer.append("\n" + lines[i])
return ''.join(buffer)
#-----------------------------------
def do_nothing(self, node):
return ''
def draw_image(self, node):
"""
"""
link = node.getAttribute("xlink:href")
if link and link[:2] == './': # Indicates a sub-object, which isn't supported
return "%s\n" % link
if link and link[:9] == 'Pictures/':
link = link[9:]
return "[[Image(%s)]]\n" % link
def text_a(self, node):
text = self.textToString(node)
link = node.getAttribute("xlink:href")
if link.strip() == text.strip():
return "[%s] " % link.strip()
else:
return "[%s %s] " % (link.strip(), text.strip())
def text_line_break(self, node):
return "[[BR]]"
def text_note(self, node):
cite = (node.getElementsByTagName("text:note-citation")[0]
.childNodes[0].nodeValue)
body = (node.getElementsByTagName("text:note-body")[0]
.childNodes[0])
self.footnotes.append((cite, self.textToString(body)))
return "^%s^" % cite
def text_s(self, node):
try:
num = int(node.getAttribute("text:c"))
return " "*num
except:
return " "
def text_tab(self, node):
return " "
def inline_markup(self, node):
text = self.textToString(node)
if not text.strip():
return '' # don't apply styles to white space
styleName = node.getAttribute("text:style-name")
style = self.textStyles.get(styleName, TextProps())
if style.fixed:
return "`" + text + "`"
mark = []
if style:
if style.italic:
mark.append("''")
if style.bold:
mark.append("'''")
if style.underlined:
mark.append("__")
if style.strikethrough:
mark.append("~~")
if style.superscript:
mark.append("^")
if style.subscript:
mark.append(",,")
revmark = mark[:]
revmark.reverse()
return "%s%s%s" % (''.join(mark), text, ''.join(revmark))
#-----------------------------------
def listToString(self, listElement, indent = 0):
self.lastsegment = listElement.tagName
buffer = []
styleName = listElement.getAttribute("text:style-name")
props = self.listStyles.get(styleName, ListProperties())
i = 0
for item in listElement.childNodes:
buffer.append(" "*indent)
i += 1
if props.ordered:
number = str(i)
number = " " + number + ". "
buffer.append(" 1. ")
else:
buffer.append(" * ")
subitems = [el for el in item.childNodes
if el.tagName in ["text:p", "text:h", "text:list"]]
for subitem in subitems:
if subitem.tagName == "text:list":
buffer.append("\n")
buffer.append(self.listToString(subitem, indent+3))
else:
buffer.append(self.paragraphToString(subitem, indent+3))
self.lastsegment = subitem.tagName
self.lastsegment = item.tagName
buffer.append("\n")
return ''.join(buffer)
def tableToString(self, tableElement):
""" MoinMoin uses || to delimit table cells
"""
self.lastsegment = tableElement.tagName
buffer = []
for item in tableElement.childNodes:
self.lastsegment = item.tagName
if item.tagName == "table:table-header-rows":
buffer.append(self.tableToString(item))
if item.tagName == "table:table-row":
buffer.append("\n||")
for cell in item.childNodes:
buffer.append(self.inline_markup(cell))
buffer.append("||")
self.lastsegment = cell.tagName
return ''.join(buffer)
def toString(self):
""" Converts the document to a string.
FIXME: Result from second call differs from first call
"""
body = self.content.getElementsByTagName("office:body")[0]
text = body.childNodes[0]
buffer = []
paragraphs = [el for el in text.childNodes
if el.tagName in ["draw:page", "text:p", "text:h","text:section",
"text:list", "table:table"]]
for paragraph in paragraphs:
if paragraph.tagName == "text:list":
text = self.listToString(paragraph)
elif paragraph.tagName == "text:section":
text = self.textToString(paragraph)
elif paragraph.tagName == "table:table":
text = self.tableToString(paragraph)
else:
text = self.paragraphToString(paragraph)
if text:
buffer.append(text)
if self.footnotes:
buffer.append("----")
for cite, body in self.footnotes:
buffer.append("%s: %s" % (cite, body))
buffer.append("")
return self.compressCodeBlocks('\n'.join(buffer))
def textToString(self, element):
buffer = []
for node in element.childNodes:
if node.nodeType == xml.dom.Node.TEXT_NODE:
buffer.append(node.nodeValue)
elif node.nodeType == xml.dom.Node.ELEMENT_NODE:
tag = node.tagName
if tag in ("draw:text-box", "draw:frame"):
buffer.append(self.textToString(node))
elif tag in ("text:p", "text:h"):
text = self.paragraphToString(node)
if text:
buffer.append(text)
elif tag == "text:list":
buffer.append(self.listToString(node))
else:
method = self.elements.get(tag)
if method:
buffer.append(method(node))
else:
buffer.append(" {" + tag + "} ")
return ''.join(buffer)
def paragraphToString(self, paragraph, indent = 0):
dummyParaProps = ParagraphProps()
style_name = paragraph.getAttribute("text:style-name")
paraProps = self.paragraphStyles.get(style_name, dummyParaProps)
text = self.inline_markup(paragraph)
if paraProps and not paraProps.code:
text = text.strip()
if paragraph.tagName == "text:p" and self.lastsegment == "text:p":
text = "\n" + text
self.lastsegment = paragraph.tagName
if paraProps.title:
self.hasTitle = 1
return "= " + text + " =\n"
outlinelevel = paragraph.getAttribute("text:outline-level")
if outlinelevel:
level = int(outlinelevel)
if self.hasTitle: level += 1
if level >= 1:
return "=" * level + " " + text + " " + "=" * level + "\n"
elif paraProps.code:
return "{{{\n" + text + "\n}}}\n"
if paraProps.indented:
return self.wrapParagraph(text, indent = indent, blockquote = True)
else:
return self.wrapParagraph(text, indent = indent)
def wrapParagraph(self, text, indent = 0, blockquote=False):
counter = 0
buffer = []
LIMIT = 50
if blockquote:
buffer.append(" ")
return ''.join(buffer) + text
# Unused from here
for token in text.split():
if counter > LIMIT - indent:
buffer.append("\n" + " "*indent)
if blockquote:
buffer.append(" ")
counter = 0
buffer.append(token + " ")
counter += len(token)
return ''.join(buffer)
File diff suppressed because it is too large Load Diff
-115
View File
@@ -1,115 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
# This script lists the content of the manifest.xml file
import zipfile
from xml.sax import make_parser,handler
from xml.sax.xmlreader import InputSource
import xml.sax.saxutils
from cStringIO import StringIO
MANIFESTNS="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
#-----------------------------------------------------------------------------
#
# ODFMANIFESTHANDLER
#
#-----------------------------------------------------------------------------
class ODFManifestHandler(handler.ContentHandler):
""" The ODFManifestHandler parses a manifest file and produces a list of
content """
def __init__(self):
self.manifest = {}
# Tags
# FIXME: Also handle encryption data
self.elements = {
(MANIFESTNS, 'file-entry'): (self.s_file_entry, self.donothing),
}
def handle_starttag(self, tag, method, attrs):
method(tag,attrs)
def handle_endtag(self, tag, method):
method(tag)
def startElementNS(self, tag, qname, attrs):
method = self.elements.get(tag, (None, None))[0]
if method:
self.handle_starttag(tag, method, attrs)
else:
self.unknown_starttag(tag,attrs)
def endElementNS(self, tag, qname):
method = self.elements.get(tag, (None, None))[1]
if method:
self.handle_endtag(tag, method)
else:
self.unknown_endtag(tag)
def unknown_starttag(self, tag, attrs):
pass
def unknown_endtag(self, tag):
pass
def donothing(self, tag, attrs=None):
pass
def s_file_entry(self, tag, attrs):
m = attrs.get((MANIFESTNS, 'media-type'),"application/octet-stream")
p = attrs.get((MANIFESTNS, 'full-path'))
self.manifest[p] = { 'media-type':m, 'full-path':p }
#-----------------------------------------------------------------------------
#
# Reading the file
#
#-----------------------------------------------------------------------------
def manifestlist(manifestxml):
odhandler = ODFManifestHandler()
parser = make_parser()
parser.setFeature(handler.feature_namespaces, 1)
parser.setContentHandler(odhandler)
parser.setErrorHandler(handler.ErrorHandler())
inpsrc = InputSource()
inpsrc.setByteStream(StringIO(manifestxml))
parser.parse(inpsrc)
return odhandler.manifest
def odfmanifest(odtfile):
z = zipfile.ZipFile(odtfile)
manifest = z.read('META-INF/manifest.xml')
z.close()
return manifestlist(manifest)
if __name__ == "__main__":
import sys
result = odfmanifest(sys.argv[1])
for file in result.values():
print "%-40s %-40s" % (file['media-type'], file['full-path'])
-104
View File
@@ -1,104 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import OFFICENS
from element import Element
from draw import StyleRefElement
# Autogenerated
def Annotation(**args):
return StyleRefElement(qname = (OFFICENS,'annotation'), **args)
def AutomaticStyles(**args):
return Element(qname = (OFFICENS, 'automatic-styles'), **args)
def BinaryData(**args):
return Element(qname = (OFFICENS,'binary-data'), **args)
def Body(**args):
return Element(qname = (OFFICENS, 'body'), **args)
def ChangeInfo(**args):
return Element(qname = (OFFICENS,'change-info'), **args)
def Chart(**args):
return Element(qname = (OFFICENS,'chart'), **args)
def DdeSource(**args):
return Element(qname = (OFFICENS,'dde-source'), **args)
def Document(version="1.1", **args):
return Element(qname = (OFFICENS,'document'), version=version, **args)
def DocumentContent(version="1.1", **args):
return Element(qname = (OFFICENS, 'document-content'), version=version, **args)
def DocumentMeta(version="1.1", **args):
return Element(qname = (OFFICENS, 'document-meta'), version=version, **args)
def DocumentSettings(version="1.1", **args):
return Element(qname = (OFFICENS, 'document-settings'), version=version, **args)
def DocumentStyles(version="1.1", **args):
return Element(qname = (OFFICENS, 'document-styles'), version=version, **args)
def Drawing(**args):
return Element(qname = (OFFICENS,'drawing'), **args)
def EventListeners(**args):
return Element(qname = (OFFICENS,'event-listeners'), **args)
def FontFaceDecls(**args):
return Element(qname = (OFFICENS, 'font-face-decls'), **args)
def Forms(**args):
return Element(qname = (OFFICENS,'forms'), **args)
def Image(**args):
return Element(qname = (OFFICENS,'image'), **args)
def MasterStyles(**args):
return Element(qname = (OFFICENS, 'master-styles'), **args)
def Meta(**args):
return Element(qname = (OFFICENS, 'meta'), **args)
def Presentation(**args):
return Element(qname = (OFFICENS,'presentation'), **args)
def Script(**args):
return Element(qname = (OFFICENS, 'script'), **args)
def Scripts(**args):
return Element(qname = (OFFICENS, 'scripts'), **args)
def Settings(**args):
return Element(qname = (OFFICENS, 'settings'), **args)
def Spreadsheet(**args):
return Element(qname = (OFFICENS, 'spreadsheet'), **args)
def Styles(**args):
return Element(qname = (OFFICENS, 'styles'), **args)
def Text(**args):
return Element(qname = (OFFICENS, 'text'), **args)
# Autogenerated end
-654
View File
@@ -1,654 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2010 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
__doc__="""Use OpenDocument to generate your documents."""
import zipfile, time, sys, mimetypes, copy
from cStringIO import StringIO
from namespaces import *
import manifest, meta
from office import *
import element
from attrconverters import make_NCName
from xml.sax.xmlreader import InputSource
from odfmanifest import manifestlist
__version__= TOOLSVERSION
_XMLPROLOGUE = u"<?xml version='1.0' encoding='UTF-8'?>\n"
UNIXPERMS = 0100644 << 16L # -rw-r--r--
IS_FILENAME = 0
IS_IMAGE = 1
# We need at least Python 2.2
assert sys.version_info[0]>=2 and sys.version_info[1] >= 2
#sys.setrecursionlimit(100)
#The recursion limit is set conservative so mistakes like
# s=content() s.addElement(s) won't eat up too much processor time.
odmimetypes = {
'application/vnd.oasis.opendocument.text': '.odt',
'application/vnd.oasis.opendocument.text-template': '.ott',
'application/vnd.oasis.opendocument.graphics': '.odg',
'application/vnd.oasis.opendocument.graphics-template': '.otg',
'application/vnd.oasis.opendocument.presentation': '.odp',
'application/vnd.oasis.opendocument.presentation-template': '.otp',
'application/vnd.oasis.opendocument.spreadsheet': '.ods',
'application/vnd.oasis.opendocument.spreadsheet-template': '.ots',
'application/vnd.oasis.opendocument.chart': '.odc',
'application/vnd.oasis.opendocument.chart-template': '.otc',
'application/vnd.oasis.opendocument.image': '.odi',
'application/vnd.oasis.opendocument.image-template': '.oti',
'application/vnd.oasis.opendocument.formula': '.odf',
'application/vnd.oasis.opendocument.formula-template': '.otf',
'application/vnd.oasis.opendocument.text-master': '.odm',
'application/vnd.oasis.opendocument.text-web': '.oth',
}
class OpaqueObject:
def __init__(self, filename, mediatype, content=None):
self.mediatype = mediatype
self.filename = filename
self.content = content
class OpenDocument:
""" A class to hold the content of an OpenDocument document
Use the xml method to write the XML
source to the screen or to a file
d = OpenDocument(mimetype)
fd.write(d.xml())
"""
thumbnail = None
def __init__(self, mimetype, add_generator=True):
self.mimetype = mimetype
self.childobjects = []
self._extra = []
self.folder = "" # Always empty for toplevel documents
self.topnode = Document(mimetype=self.mimetype)
self.topnode.ownerDocument = self
self.clear_caches()
self.Pictures = {}
self.meta = Meta()
self.topnode.addElement(self.meta)
if add_generator:
self.meta.addElement(meta.Generator(text=TOOLSVERSION))
self.scripts = Scripts()
self.topnode.addElement(self.scripts)
self.fontfacedecls = FontFaceDecls()
self.topnode.addElement(self.fontfacedecls)
self.settings = Settings()
self.topnode.addElement(self.settings)
self.styles = Styles()
self.topnode.addElement(self.styles)
self.automaticstyles = AutomaticStyles()
self.topnode.addElement(self.automaticstyles)
self.masterstyles = MasterStyles()
self.topnode.addElement(self.masterstyles)
self.body = Body()
self.topnode.addElement(self.body)
def rebuild_caches(self, node=None):
if node is None: node = self.topnode
self.build_caches(node)
for e in node.childNodes:
if e.nodeType == element.Node.ELEMENT_NODE:
self.rebuild_caches(e)
def clear_caches(self):
self.element_dict = {}
self._styles_dict = {}
self._styles_ooo_fix = {}
def build_caches(self, element):
""" Called from element.py
"""
if not self.element_dict.has_key(element.qname):
self.element_dict[element.qname] = []
self.element_dict[element.qname].append(element)
if element.qname == (STYLENS, u'style'):
self.__register_stylename(element) # Add to style dictionary
styleref = element.getAttrNS(TEXTNS,u'style-name')
if styleref is not None and self._styles_ooo_fix.has_key(styleref):
element.setAttrNS(TEXTNS,u'style-name', self._styles_ooo_fix[styleref])
def __register_stylename(self, element):
''' Register a style. But there are three style dictionaries:
office:styles, office:automatic-styles and office:master-styles
Chapter 14
'''
name = element.getAttrNS(STYLENS, u'name')
if name is None:
return
if element.parentNode.qname in ((OFFICENS,u'styles'), (OFFICENS,u'automatic-styles')):
if self._styles_dict.has_key(name):
newname = 'M'+name # Rename style
self._styles_ooo_fix[name] = newname
# From here on all references to the old name will refer to the new one
name = newname
element.setAttrNS(STYLENS, u'name', name)
self._styles_dict[name] = element
def toXml(self, filename=''):
xml=StringIO()
xml.write(_XMLPROLOGUE)
self.body.toXml(0, xml)
if not filename:
return xml.getvalue()
else:
f=file(filename,'w')
f.write(xml.getvalue())
f.close()
def xml(self):
""" Generates the full document as an XML file
Always written as a bytestream in UTF-8 encoding
"""
self.__replaceGenerator()
xml=StringIO()
xml.write(_XMLPROLOGUE)
self.topnode.toXml(0, xml)
return xml.getvalue()
def contentxml(self):
""" Generates the content.xml file
Always written as a bytestream in UTF-8 encoding
"""
xml=StringIO()
xml.write(_XMLPROLOGUE)
x = DocumentContent()
x.write_open_tag(0, xml)
if self.scripts.hasChildNodes():
self.scripts.toXml(1, xml)
if self.fontfacedecls.hasChildNodes():
self.fontfacedecls.toXml(1, xml)
a = AutomaticStyles()
stylelist = self._used_auto_styles([self.styles, self.automaticstyles, self.body])
if len(stylelist) > 0:
a.write_open_tag(1, xml)
for s in stylelist:
s.toXml(2, xml)
a.write_close_tag(1, xml)
else:
a.toXml(1, xml)
self.body.toXml(1, xml)
x.write_close_tag(0, xml)
return xml.getvalue()
def __manifestxml(self):
""" Generates the manifest.xml file
The self.manifest isn't avaible unless the document is being saved
"""
xml=StringIO()
xml.write(_XMLPROLOGUE)
self.manifest.toXml(0,xml)
return xml.getvalue()
def metaxml(self):
""" Generates the meta.xml file """
self.__replaceGenerator()
x = DocumentMeta()
x.addElement(self.meta)
xml=StringIO()
xml.write(_XMLPROLOGUE)
x.toXml(0,xml)
return xml.getvalue()
def settingsxml(self):
""" Generates the settings.xml file """
x = DocumentSettings()
x.addElement(self.settings)
xml=StringIO()
xml.write(_XMLPROLOGUE)
x.toXml(0,xml)
return xml.getvalue()
def _parseoneelement(self, top, stylenamelist):
""" Finds references to style objects in master-styles
and add the style name to the style list if not already there.
Recursive
"""
for e in top.childNodes:
if e.nodeType == element.Node.ELEMENT_NODE:
for styleref in ( (DRAWNS,u'style-name'),
(DRAWNS,u'text-style-name'),
(PRESENTATIONNS,u'style-name'),
(STYLENS,u'data-style-name'),
(STYLENS,u'list-style-name'),
(STYLENS,u'page-layout-name'),
(STYLENS,u'style-name'),
(TABLENS,u'default-cell-style-name'),
(TABLENS,u'style-name'),
(TEXTNS,u'style-name') ):
if e.getAttrNS(styleref[0],styleref[1]):
stylename = e.getAttrNS(styleref[0],styleref[1])
if stylename not in stylenamelist:
stylenamelist.append(stylename)
stylenamelist = self._parseoneelement(e, stylenamelist)
return stylenamelist
def _used_auto_styles(self, segments):
""" Loop through the masterstyles elements, and find the automatic
styles that are used. These will be added to the automatic-styles
element in styles.xml
"""
stylenamelist = []
for top in segments:
stylenamelist = self._parseoneelement(top, stylenamelist)
stylelist = []
for e in self.automaticstyles.childNodes:
if e.getAttrNS(STYLENS,u'name') in stylenamelist:
stylelist.append(e)
return stylelist
def stylesxml(self):
""" Generates the styles.xml file """
xml=StringIO()
xml.write(_XMLPROLOGUE)
x = DocumentStyles()
x.write_open_tag(0, xml)
if self.fontfacedecls.hasChildNodes():
self.fontfacedecls.toXml(1, xml)
self.styles.toXml(1, xml)
a = AutomaticStyles()
a.write_open_tag(1, xml)
for s in self._used_auto_styles([self.masterstyles]):
s.toXml(2, xml)
a.write_close_tag(1, xml)
if self.masterstyles.hasChildNodes():
self.masterstyles.toXml(1, xml)
x.write_close_tag(0, xml)
return xml.getvalue()
def addPicture(self, filename, mediatype=None, content=None):
""" Add a picture
It uses the same convention as OOo, in that it saves the picture in
the zipfile in the subdirectory 'Pictures'
If passed a file ptr, mediatype must be set
"""
if content is None:
if mediatype is None:
mediatype, encoding = mimetypes.guess_type(filename)
if mediatype is None:
mediatype = ''
try: ext = filename[filename.rindex('.'):]
except: ext=''
else:
ext = mimetypes.guess_extension(mediatype)
manifestfn = "Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
self.Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
else:
manifestfn = filename
self.Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
return manifestfn
def addPictureFromFile(self, filename, mediatype=None):
""" Add a picture
It uses the same convention as OOo, in that it saves the picture in
the zipfile in the subdirectory 'Pictures'.
If mediatype is not given, it will be guessed from the filename
extension.
"""
if mediatype is None:
mediatype, encoding = mimetypes.guess_type(filename)
if mediatype is None:
mediatype = ''
try: ext = filename[filename.rindex('.'):]
except ValueError: ext=''
else:
ext = mimetypes.guess_extension(mediatype)
manifestfn = "Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
self.Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
return manifestfn
def addPictureFromString(self, content, mediatype):
""" Add a picture
It uses the same convention as OOo, in that it saves the picture in
the zipfile in the subdirectory 'Pictures'. The content variable
is a string that contains the binary image data. The mediatype
indicates the image format.
"""
ext = mimetypes.guess_extension(mediatype)
manifestfn = "Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
self.Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
return manifestfn
def addThumbnail(self, filecontent=None):
""" Add a fixed thumbnail
The thumbnail in the library is big, so this is pretty useless.
"""
if filecontent is None:
import thumbnail
self.thumbnail = thumbnail.thumbnail()
else:
self.thumbnail = filecontent
def addObject(self, document, objectname=None):
""" Adds an object (subdocument). The object must be an OpenDocument class
The return value will be the folder in the zipfile the object is stored in
"""
self.childobjects.append(document)
if objectname is None:
document.folder = "%s/Object %d" % (self.folder, len(self.childobjects))
else:
document.folder = objectname
return ".%s" % document.folder
def _savePictures(self, object, folder):
hasPictures = False
for arcname, picturerec in object.Pictures.items():
what_it_is, fileobj, mediatype = picturerec
self.manifest.addElement(manifest.FileEntry(fullpath="%s%s" % ( folder ,arcname), mediatype=mediatype))
hasPictures = True
if what_it_is == IS_FILENAME:
self._z.write(fileobj, arcname, zipfile.ZIP_STORED)
else:
zi = zipfile.ZipInfo(str(arcname), self._now)
zi.compress_type = zipfile.ZIP_STORED
zi.external_attr = UNIXPERMS
self._z.writestr(zi, fileobj)
# According to section 17.7.3 in ODF 1.1, the pictures folder should not have a manifest entry
# if hasPictures:
# self.manifest.addElement(manifest.FileEntry(fullpath="%sPictures/" % folder, mediatype=""))
# Look in subobjects
subobjectnum = 1
for subobject in object.childobjects:
self._savePictures(subobject,'%sObject %d/' % (folder, subobjectnum))
subobjectnum += 1
def __replaceGenerator(self):
""" Section 3.1.1: The application MUST NOT export the original identifier
belonging to the application that created the document.
"""
for m in self.meta.childNodes[:]:
if m.qname == (METANS, u'generator'):
self.meta.removeChild(m)
self.meta.addElement(meta.Generator(text=TOOLSVERSION))
def save(self, outputfile, addsuffix=False):
""" Save the document under the filename.
If the filename is '-' then save to stdout
"""
if outputfile == '-':
outputfp = zipfile.ZipFile(sys.stdout,"w")
else:
if addsuffix:
outputfile = outputfile + odmimetypes.get(self.mimetype,'.xxx')
outputfp = zipfile.ZipFile(outputfile, "w")
self.__zipwrite(outputfp)
outputfp.close()
def write(self, outputfp):
""" User API to write the ODF file to an open file descriptor
Writes the ZIP format
"""
zipoutputfp = zipfile.ZipFile(outputfp,"w")
self.__zipwrite(zipoutputfp)
def __zipwrite(self, outputfp):
""" Write the document to an open file pointer
This is where the real work is done
"""
self._z = outputfp
self._now = time.localtime()[:6]
self.manifest = manifest.Manifest()
# Write mimetype
zi = zipfile.ZipInfo('mimetype', self._now)
zi.compress_type = zipfile.ZIP_STORED
zi.external_attr = UNIXPERMS
self._z.writestr(zi, self.mimetype)
self._saveXmlObjects(self,"")
# Write pictures
self._savePictures(self,"")
# Write the thumbnail
if self.thumbnail is not None:
self.manifest.addElement(manifest.FileEntry(fullpath="Thumbnails/", mediatype=''))
self.manifest.addElement(manifest.FileEntry(fullpath="Thumbnails/thumbnail.png", mediatype=''))
zi = zipfile.ZipInfo("Thumbnails/thumbnail.png", self._now)
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = UNIXPERMS
self._z.writestr(zi, self.thumbnail)
# Write any extra files
for op in self._extra:
if op.filename == "META-INF/documentsignatures.xml": continue # Don't save signatures
self.manifest.addElement(manifest.FileEntry(fullpath=op.filename, mediatype=op.mediatype))
zi = zipfile.ZipInfo(op.filename.encode('utf-8'), self._now)
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = UNIXPERMS
if op.content is not None:
self._z.writestr(zi, op.content)
# Write manifest
zi = zipfile.ZipInfo("META-INF/manifest.xml", self._now)
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = UNIXPERMS
self._z.writestr(zi, self.__manifestxml() )
del self._z
del self._now
del self.manifest
def _saveXmlObjects(self, object, folder):
if self == object:
self.manifest.addElement(manifest.FileEntry(fullpath="/", mediatype=object.mimetype))
else:
self.manifest.addElement(manifest.FileEntry(fullpath=folder, mediatype=object.mimetype))
# Write styles
self.manifest.addElement(manifest.FileEntry(fullpath="%sstyles.xml" % folder, mediatype="text/xml"))
zi = zipfile.ZipInfo("%sstyles.xml" % folder, self._now)
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = UNIXPERMS
self._z.writestr(zi, object.stylesxml() )
# Write content
self.manifest.addElement(manifest.FileEntry(fullpath="%scontent.xml" % folder, mediatype="text/xml"))
zi = zipfile.ZipInfo("%scontent.xml" % folder, self._now)
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = UNIXPERMS
self._z.writestr(zi, object.contentxml() )
# Write settings
if object.settings.hasChildNodes():
self.manifest.addElement(manifest.FileEntry(fullpath="%ssettings.xml" % folder, mediatype="text/xml"))
zi = zipfile.ZipInfo("%ssettings.xml" % folder, self._now)
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = UNIXPERMS
self._z.writestr(zi, object.settingsxml() )
# Write meta
if self == object:
self.manifest.addElement(manifest.FileEntry(fullpath="meta.xml", mediatype="text/xml"))
zi = zipfile.ZipInfo("meta.xml", self._now)
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = UNIXPERMS
self._z.writestr(zi, object.metaxml() )
# Write subobjects
subobjectnum = 1
for subobject in object.childobjects:
self._saveXmlObjects(subobject, '%sObject %d/' % (folder, subobjectnum))
subobjectnum += 1
# Document's DOM methods
def createElement(self, element):
""" Inconvenient interface to create an element, but follows XML-DOM.
Does not allow attributes as argument, therefore can't check grammar.
"""
return element(check_grammar=False)
def createTextNode(self, data):
""" Method to create a text node """
return element.Text(data)
def createCDATASection(self, data):
""" Method to create a CDATA section """
return element.CDATASection(cdata)
def getMediaType(self):
""" Returns the media type """
return self.mimetype
def getStyleByName(self, name):
""" Finds a style object based on the name """
ncname = make_NCName(name)
if self._styles_dict == {}:
self.rebuild_caches()
return self._styles_dict.get(ncname, None)
def getElementsByType(self, element):
""" Gets elements based on the type, which is function from text.py, draw.py etc. """
obj = element(check_grammar=False)
if self.element_dict == {}:
self.rebuild_caches()
return self.element_dict.get(obj.qname, [])
# Convenience functions
def OpenDocumentChart():
""" Creates a chart document """
doc = OpenDocument('application/vnd.oasis.opendocument.chart')
doc.chart = Chart()
doc.body.addElement(doc.chart)
return doc
def OpenDocumentDrawing():
""" Creates a drawing document """
doc = OpenDocument('application/vnd.oasis.opendocument.graphics')
doc.drawing = Drawing()
doc.body.addElement(doc.drawing)
return doc
def OpenDocumentImage():
""" Creates an image document """
doc = OpenDocument('application/vnd.oasis.opendocument.image')
doc.image = Image()
doc.body.addElement(doc.image)
return doc
def OpenDocumentPresentation():
""" Creates a presentation document """
doc = OpenDocument('application/vnd.oasis.opendocument.presentation')
doc.presentation = Presentation()
doc.body.addElement(doc.presentation)
return doc
def OpenDocumentSpreadsheet():
""" Creates a spreadsheet document """
doc = OpenDocument('application/vnd.oasis.opendocument.spreadsheet')
doc.spreadsheet = Spreadsheet()
doc.body.addElement(doc.spreadsheet)
return doc
def OpenDocumentText():
""" Creates a text document """
doc = OpenDocument('application/vnd.oasis.opendocument.text')
doc.text = Text()
doc.body.addElement(doc.text)
return doc
def OpenDocumentTextMaster():
""" Creates a text master document """
doc = OpenDocument('application/vnd.oasis.opendocument.text-master')
doc.text = Text()
doc.body.addElement(doc.text)
return doc
def __loadxmlparts(z, manifest, doc, objectpath):
from load import LoadParser
from xml.sax import make_parser, handler
for xmlfile in (objectpath+'settings.xml', objectpath+'meta.xml', objectpath+'content.xml', objectpath+'styles.xml'):
if not manifest.has_key(xmlfile):
continue
try:
xmlpart = z.read(xmlfile)
doc._parsing = xmlfile
parser = make_parser()
parser.setFeature(handler.feature_namespaces, 1)
parser.setContentHandler(LoadParser(doc))
parser.setErrorHandler(handler.ErrorHandler())
inpsrc = InputSource()
inpsrc.setByteStream(StringIO(xmlpart))
parser.parse(inpsrc)
del doc._parsing
except KeyError, v: pass
def load(odffile):
""" Load an ODF file into memory
Returns a reference to the structure
"""
z = zipfile.ZipFile(odffile)
mimetype = z.read('mimetype')
doc = OpenDocument(mimetype, add_generator=False)
# Look in the manifest file to see if which of the four files there are
manifestpart = z.read('META-INF/manifest.xml')
manifest = manifestlist(manifestpart)
__loadxmlparts(z, manifest, doc, '')
for mentry,mvalue in manifest.items():
if mentry[:9] == "Pictures/" and len(mentry) > 9:
doc.addPicture(mvalue['full-path'], mvalue['media-type'], z.read(mentry))
elif mentry == "Thumbnails/thumbnail.png":
doc.addThumbnail(z.read(mentry))
elif mentry in ('settings.xml', 'meta.xml', 'content.xml', 'styles.xml'):
pass
# Load subobjects into structure
elif mentry[:7] == "Object " and len(mentry) < 11 and mentry[-1] == "/":
subdoc = OpenDocument(mvalue['media-type'], add_generator=False)
doc.addObject(subdoc, "/" + mentry[:-1])
__loadxmlparts(z, manifest, subdoc, mentry)
elif mentry[:7] == "Object ":
pass # Don't load subobjects as opaque objects
else:
if mvalue['full-path'][-1] == '/':
doc._extra.append(OpaqueObject(mvalue['full-path'], mvalue['media-type'], None))
else:
doc._extra.append(OpaqueObject(mvalue['full-path'], mvalue['media-type'], z.read(mentry)))
# Add the SUN junk here to the struct somewhere
# It is cached data, so it can be out-of-date
z.close()
b = doc.getElementsByType(Body)
if mimetype[:39] == 'application/vnd.oasis.opendocument.text':
doc.text = b[0].firstChild
elif mimetype[:43] == 'application/vnd.oasis.opendocument.graphics':
doc.graphics = b[0].firstChild
elif mimetype[:47] == 'application/vnd.oasis.opendocument.presentation':
doc.presentation = b[0].firstChild
elif mimetype[:46] == 'application/vnd.oasis.opendocument.spreadsheet':
doc.spreadsheet = b[0].firstChild
elif mimetype[:40] == 'application/vnd.oasis.opendocument.chart':
doc.chart = b[0].firstChild
elif mimetype[:40] == 'application/vnd.oasis.opendocument.image':
doc.image = b[0].firstChild
elif mimetype[:42] == 'application/vnd.oasis.opendocument.formula':
doc.formula = b[0].firstChild
return doc
# vim: set expandtab sw=4 :

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