mirror of
https://github.com/kennethreitz/tablib.git
synced 2026-06-05 23:10:17 +00:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e022d89e5 | |||
| a40852d1c6 | |||
| 9b9fb0aa8a | |||
| ca1aa3ad30 | |||
| 2cfde95fe2 | |||
| 5b94682df3 | |||
| f6bf14afd2 | |||
| f3d02aa3b0 | |||
| ca8dbcf9be | |||
| 4418535030 | |||
| 5bd896b954 | |||
| af414b69d7 | |||
| 91062672b5 | |||
| 34334e72a1 | |||
| 5595bb7993 | |||
| 5fde5259d9 | |||
| 591e8f7448 | |||
| 4dfe2c2f89 | |||
| 91608895d6 | |||
| 0d36390254 | |||
| 8ea082ce60 | |||
| a0df54ca22 | |||
| 0e06b7e328 | |||
| 8aeb5e5158 | |||
| a0d19a56cb | |||
| 8cc024e61b | |||
| a7c40a0881 | |||
| 20de7fad98 | |||
| 4969a71f7f | |||
| 743776371a | |||
| 326d07c2ed | |||
| 2f6ea8c644 | |||
| 8aaed50cc8 | |||
| a21b276d9c | |||
| e8838b5ce6 | |||
| 923711d99a | |||
| aac129db66 | |||
| d9df89f5da | |||
| 22bb20c74b | |||
| d25d24a9bb | |||
| 513bba2c20 | |||
| 2b9ce02e3c | |||
| f9f28d3d86 | |||
| 0cb50bb008 | |||
| f55f56ae1d | |||
| 0937c9f9ec | |||
| 25a66f95ac | |||
| 6ab511f8c0 | |||
| 64816258e6 | |||
| 41cbaa04b9 | |||
| c136940801 | |||
| cf03ecfe25 | |||
| 193b840da2 |
+10
@@ -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__.:
|
||||
@@ -0,0 +1,10 @@
|
||||
[](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).
|
||||
@@ -29,3 +29,12 @@ junit-py27.xml
|
||||
|
||||
# pyenv noise
|
||||
.python-version
|
||||
tablib.egg-info/*
|
||||
|
||||
# Coverage
|
||||
.coverage
|
||||
htmlcov
|
||||
|
||||
# setuptools noise
|
||||
.eggs
|
||||
*.egg-info
|
||||
|
||||
@@ -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
@@ -1,10 +1,22 @@
|
||||
language: python
|
||||
cache: pip
|
||||
python:
|
||||
- 2.7
|
||||
- 3.4
|
||||
- 3.5
|
||||
- 3.6
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
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
|
||||
|
||||
@@ -1,38 +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
|
||||
- Tushar Makkar
|
||||
- Andrii Soldatenko
|
||||
- Bruno Soares
|
||||
- Tsuyoshi Hombashi
|
||||
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
|
||||
|
||||
@@ -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
@@ -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.
|
||||
-256
@@ -1,256 +0,0 @@
|
||||
History
|
||||
-------
|
||||
|
||||
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.
|
||||
@@ -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
@@ -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__
|
||||
|
||||
@@ -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
|
||||
@@ -1,6 +0,0 @@
|
||||
Tablib includes some vendorized Python libraries: markup.
|
||||
|
||||
Markup License
|
||||
==============
|
||||
|
||||
Markup is in the public domain.
|
||||
@@ -0,0 +1,177 @@
|
||||
# Tablib: format-agnostic tabular dataset library
|
||||
|
||||
[](https://jazzband.co/)
|
||||
[](https://travis-ci.org/jazzband/tablib)
|
||||
[](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).
|
||||
-179
@@ -1,179 +0,0 @@
|
||||
Tablib: format-agnostic tabular dataset library
|
||||
===============================================
|
||||
|
||||
.. image:: https://travis-ci.org/kennethreitz/tablib.svg?branch=master
|
||||
: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)
|
||||
- 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.)
|
||||
|
||||
If you're interested in financially supporting Kenneth Reitz open source, consider `visiting this link <https://cash.me/$KennethReitz>`_. Your support helps tremendously with sustainability of motivation, as Open Source is no longer part of my day job.
|
||||
|
||||
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.export('json'))
|
||||
[
|
||||
{
|
||||
"last_name": "Adams",
|
||||
"age": 90,
|
||||
"first_name": "John"
|
||||
},
|
||||
{
|
||||
"last_name": "Ford",
|
||||
"age": 83,
|
||||
"first_name": "Henry"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
YAML!
|
||||
+++++
|
||||
::
|
||||
|
||||
>>> print(data.export('yaml'))
|
||||
- {age: 90, first_name: John, last_name: Adams}
|
||||
- {age: 83, first_name: Henry, last_name: Ford}
|
||||
|
||||
CSV...
|
||||
++++++
|
||||
::
|
||||
|
||||
>>> print(data.export('csv'))
|
||||
first_name,last_name,age
|
||||
John,Adams,90
|
||||
Henry,Ford,83
|
||||
|
||||
EXCEL!
|
||||
++++++
|
||||
::
|
||||
|
||||
>>> with open('people.xls', 'wb') as f:
|
||||
... f.write(data.export('xls'))
|
||||
|
||||
DBF!
|
||||
++++
|
||||
::
|
||||
|
||||
>>> with open('people.dbf', 'wb') as f:
|
||||
... f.write(data.export('dbf'))
|
||||
|
||||
Pandas DataFrame!
|
||||
+++++++++++++++++
|
||||
::
|
||||
|
||||
>>> 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: ::
|
||||
|
||||
$ pip install tablib[pandas]
|
||||
|
||||
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
|
||||
@@ -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"
|
||||
Vendored
+5
-14
@@ -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>
|
||||
|
||||
Vendored
+2
-2
@@ -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>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
*.pyc
|
||||
*.pyo
|
||||
.DS_Store
|
||||
Vendored
-45
@@ -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.
|
||||
Vendored
-24
@@ -1,24 +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.
|
||||
Vendored
-86
@@ -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'
|
||||
}
|
||||
Vendored
-54
@@ -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">
|
||||
© 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 %}
|
||||
Vendored
-19
@@ -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>
|
||||
Vendored
-470
@@ -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
@@ -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;
|
||||
}
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = flasky.css
|
||||
pygments_style = flask_theme_support.FlaskyStyle
|
||||
|
||||
[options]
|
||||
touch_icon =
|
||||
Vendored
-22
@@ -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
@@ -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;
|
||||
}
|
||||
Vendored
-10
@@ -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
@@ -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
@@ -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'2016. A <a href="http://kennethreitz.org/">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'
|
||||
+12
-37
@@ -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/
|
||||
|
||||
|
||||
|
||||
@@ -45,12 +45,12 @@ The repository is publicly accessible.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
git clone git://github.com/kennethreitz/tablib.git
|
||||
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
|
||||
@@ -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:
|
||||
@@ -150,47 +150,22 @@ the easiest way to test your changes for potential issues is to simply run the t
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./test_tablib.py
|
||||
|
||||
|
||||
`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.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pip install nose
|
||||
|
||||
Once installed, we can generate our xUnit report with a single command.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ 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:
|
||||
@@ -225,7 +200,7 @@ You can also generate the documentation in **epub**, **latex**, **json**, *&c* s
|
||||
|
||||
.. _`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
|
||||
|
||||
----------
|
||||
|
||||
|
||||
+5
-5
@@ -53,11 +53,11 @@ and seamless format import & export.
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
+5
-5
@@ -15,7 +15,7 @@ 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>`_:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
@@ -45,8 +45,8 @@ or install it into your site-packages easily.
|
||||
|
||||
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
|
||||
.. _tarball: https://github.com/jazzband/tablib/tarball/master
|
||||
.. _zipball: https://github.com/jazzband/tablib/zipball/master
|
||||
|
||||
|
||||
.. _updates:
|
||||
@@ -56,8 +56,8 @@ 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::
|
||||
|
||||
|
||||
+7
-13
@@ -39,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:
|
||||
@@ -74,17 +74,11 @@ THE SOFTWARE.
|
||||
Pythons Supported
|
||||
-----------------
|
||||
|
||||
At this time, the following Python platforms are officially supported:
|
||||
|
||||
* cPython 2.7
|
||||
* cPython 3.3
|
||||
* cPython 3.4
|
||||
* cPython 3.5
|
||||
* cPython 3.6
|
||||
|
||||
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>`.
|
||||
|
||||
+1
-4
@@ -5,9 +5,6 @@ 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.
|
||||
@@ -301,7 +298,7 @@ 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:
|
||||
|
||||
|
||||
@@ -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 = .
|
||||
@@ -1,22 +0,0 @@
|
||||
backports.csv==1.0.6
|
||||
certifi==2017.7.27.1
|
||||
chardet==3.0.4
|
||||
et-xmlfile==1.0.1
|
||||
idna==2.6
|
||||
jdcal==1.3
|
||||
numpy==1.13.1
|
||||
odfpy==1.3.5
|
||||
openpyxl==2.4.8
|
||||
pandas==0.20.3
|
||||
pkginfo==1.4.1
|
||||
python-dateutil==2.6.1
|
||||
pytz==2017.2
|
||||
PyYAML==3.12
|
||||
requests==2.18.4
|
||||
requests-toolbelt==0.8.0
|
||||
six==1.10.0
|
||||
tqdm==4.15.0
|
||||
unicodecsv==0.14.1
|
||||
urllib3==1.22
|
||||
xlrd==1.1.0
|
||||
xlwt==1.3.0
|
||||
@@ -5,57 +5,35 @@ import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
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] == '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.dbfpy',
|
||||
'tablib.packages.dbfpy3'
|
||||
]
|
||||
|
||||
install = [
|
||||
'odfpy',
|
||||
'openpyxl>=2.4.0',
|
||||
'backports.csv',
|
||||
'backports.csv;python_version<"3.0"',
|
||||
'markuppy',
|
||||
'xlrd',
|
||||
'xlwt',
|
||||
'pyyaml',
|
||||
]
|
||||
|
||||
|
||||
with open('tablib/core.py', 'r') as fd:
|
||||
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
|
||||
fd.read(), re.MULTILINE).group(1)
|
||||
|
||||
setup(
|
||||
name='tablib',
|
||||
version=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=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
@@ -63,12 +41,15 @@ setup(
|
||||
'Natural Language :: English',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
],
|
||||
tests_require=['pytest'],
|
||||
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
|
||||
install_requires=install,
|
||||
extras_require={
|
||||
'pandas': ['pandas'],
|
||||
|
||||
@@ -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
|
||||
@@ -14,9 +14,7 @@ is_py3 = (sys.version_info[0] > 2)
|
||||
|
||||
|
||||
if is_py3:
|
||||
from io import BytesIO
|
||||
from io import StringIO
|
||||
from tablib.packages import markup3 as markup
|
||||
from statistics import median
|
||||
from itertools import zip_longest as izip_longest
|
||||
import csv
|
||||
@@ -26,9 +24,7 @@ if is_py3:
|
||||
xrange = range
|
||||
|
||||
else:
|
||||
from cStringIO import StringIO as BytesIO
|
||||
from StringIO import StringIO
|
||||
from tablib.packages import markup
|
||||
from tablib.packages.statistics import median
|
||||
from itertools import izip_longest
|
||||
from backports import csv
|
||||
@@ -36,3 +32,5 @@ else:
|
||||
|
||||
unicode = unicode
|
||||
xrange = xrange
|
||||
|
||||
from MarkupPy import markup # Kept temporarily to avoid breaking existing imports
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
@@ -19,11 +19,9 @@ from tablib.compat import unicode
|
||||
|
||||
|
||||
__title__ = 'tablib'
|
||||
__version__ = '0.13.0'
|
||||
__build__ = 0x001201
|
||||
__author__ = 'Kenneth Reitz'
|
||||
__license__ = 'MIT'
|
||||
__copyright__ = 'Copyright 2017 Kenneth Reitz'
|
||||
__copyright__ = 'Copyright 2017 Kenneth Reitz. 2019 Jazzband.'
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
|
||||
@@ -106,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.
|
||||
@@ -142,7 +138,7 @@ 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
|
||||
|
||||
@@ -173,11 +169,9 @@ class Dataset(object):
|
||||
|
||||
self._register_formats()
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self.height
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, (str, unicode)):
|
||||
if key in self.headers:
|
||||
@@ -196,7 +190,6 @@ class Dataset(object):
|
||||
self._validate(value)
|
||||
self._data[key] = Row(value)
|
||||
|
||||
|
||||
def __delitem__(self, key):
|
||||
if isinstance(key, (str, unicode)):
|
||||
|
||||
@@ -214,7 +207,6 @@ class Dataset(object):
|
||||
else:
|
||||
del self._data[key]
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
return '<%s dataset>' % (self.title.lower())
|
||||
@@ -267,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:
|
||||
@@ -287,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?
|
||||
@@ -312,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]
|
||||
@@ -323,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.
|
||||
|
||||
@@ -333,7 +320,6 @@ class Dataset(object):
|
||||
"""
|
||||
return self.__headers
|
||||
|
||||
|
||||
def _set_headers(self, collection):
|
||||
"""Validating headers setter."""
|
||||
self._validate(collection)
|
||||
@@ -347,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,
|
||||
@@ -361,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
|
||||
@@ -394,7 +378,6 @@ class Dataset(object):
|
||||
|
||||
dict = property(_get_dict, _set_dict)
|
||||
|
||||
|
||||
def _clean_col(self, col):
|
||||
"""Prepares the given column for insert/append."""
|
||||
|
||||
@@ -412,7 +395,6 @@ class Dataset(object):
|
||||
|
||||
return col
|
||||
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
"""The number of rows currently in the :class:`Dataset`.
|
||||
@@ -420,7 +402,6 @@ class Dataset(object):
|
||||
"""
|
||||
return len(self._data)
|
||||
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
"""The number of columns currently in the :class:`Dataset`.
|
||||
@@ -435,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:
|
||||
@@ -453,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:
|
||||
@@ -552,7 +530,6 @@ class Dataset(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@property
|
||||
def tsv():
|
||||
"""A TSV representation of the :class:`Dataset` object. The top row will contain
|
||||
@@ -629,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()
|
||||
@@ -677,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.
|
||||
@@ -685,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.
|
||||
@@ -693,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.
|
||||
@@ -709,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`."""
|
||||
|
||||
@@ -718,7 +691,6 @@ class Dataset(object):
|
||||
|
||||
return cache
|
||||
|
||||
|
||||
def rpop(self):
|
||||
"""Removes and returns the last row of the :class:`Dataset`."""
|
||||
|
||||
@@ -727,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
|
||||
# -------
|
||||
@@ -788,7 +758,6 @@ class Dataset(object):
|
||||
|
||||
self.headers.insert(index, header)
|
||||
|
||||
|
||||
if self.height and self.width:
|
||||
|
||||
for i, row in enumerate(self._data):
|
||||
@@ -798,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.
|
||||
@@ -807,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.
|
||||
@@ -815,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`."""
|
||||
|
||||
@@ -834,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.
|
||||
@@ -842,24 +805,23 @@ 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, unicode):
|
||||
@@ -875,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>`.
|
||||
@@ -885,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
|
||||
@@ -921,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
|
||||
@@ -953,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
|
||||
@@ -976,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
|
||||
@@ -1010,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.
|
||||
@@ -1064,7 +1017,6 @@ class Dataset(object):
|
||||
return _dset
|
||||
|
||||
|
||||
|
||||
class Databook(object):
|
||||
"""A book of :class:`Dataset` objects.
|
||||
"""
|
||||
@@ -1090,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."""
|
||||
@@ -1116,7 +1067,6 @@ class Databook(object):
|
||||
else:
|
||||
raise InvalidDatasetType
|
||||
|
||||
|
||||
def _package(self, ordered=True):
|
||||
"""Packages :class:`Databook` for delivery."""
|
||||
collector = []
|
||||
@@ -1133,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:
|
||||
@@ -1160,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:
|
||||
@@ -1178,6 +1127,7 @@ def detect_format(stream):
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def import_set(stream, format=None, **kwargs):
|
||||
"""Return dataset of given stream."""
|
||||
|
||||
@@ -1197,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"
|
||||
@@ -17,4 +17,5 @@ from . import _df as df
|
||||
from . import _rst as rst
|
||||
from . import _jira as jira
|
||||
|
||||
available = (json, xls, yaml, csv, dbf, tsv, html, jira, latex, xlsx, ods, df, rst)
|
||||
# xlsx before as xls (xlrd) can also read xlsx
|
||||
available = (json, xlsx, xls, yaml, csv, dbf, tsv, html, jira, latex, ods, df, rst)
|
||||
@@ -13,8 +13,8 @@ extensions = ('csv',)
|
||||
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)
|
||||
@@ -24,6 +24,13 @@ def export_set(dataset, **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()
|
||||
|
||||
|
||||
@@ -48,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
|
||||
@@ -83,9 +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
|
||||
@@ -1,14 +1,8 @@
|
||||
""" Tablib - DataFrame Support.
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
if sys.version_info[0] > 2:
|
||||
from io import BytesIO
|
||||
else:
|
||||
from cStringIO import StringIO as BytesIO
|
||||
from io import BytesIO
|
||||
|
||||
try:
|
||||
from pandas import DataFrame
|
||||
@@ -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,7 +20,7 @@ extensions = ('html', )
|
||||
def export_set(dataset):
|
||||
"""HTML representation of a Dataset."""
|
||||
|
||||
stream = StringIO()
|
||||
stream = BytesIO()
|
||||
|
||||
page = markup.page()
|
||||
page.table.open()
|
||||
@@ -56,7 +51,7 @@ 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)
|
||||
@@ -55,5 +55,5 @@ def detect(stream):
|
||||
try:
|
||||
json.loads(stream)
|
||||
return True
|
||||
except ValueError:
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
@@ -3,8 +3,9 @@
|
||||
""" Tablib - ODF Support.
|
||||
"""
|
||||
|
||||
from io import BytesIO
|
||||
from odf import opendocument, style, table, text
|
||||
from tablib.compat import BytesIO, unicode
|
||||
from tablib.compat import unicode
|
||||
|
||||
title = 'ods'
|
||||
extensions = ('ods',)
|
||||
@@ -38,7 +39,6 @@ def export_book(databook):
|
||||
wb.spreadsheet.addElement(ws)
|
||||
dset_sheet(dset, ws)
|
||||
|
||||
|
||||
stream = BytesIO()
|
||||
wb.save(stream)
|
||||
return stream.getvalue()
|
||||
@@ -91,3 +91,14 @@ def dset_sheet(dataset, ws):
|
||||
cell = table.TableCell()
|
||||
cell.addElement(text.P(text=col))
|
||||
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
|
||||
@@ -4,8 +4,9 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
from io import BytesIO
|
||||
|
||||
from tablib.compat import BytesIO, xrange
|
||||
from tablib.compat import xrange
|
||||
import tablib
|
||||
import xlrd
|
||||
import xlwt
|
||||
@@ -24,17 +25,17 @@ def detect(stream):
|
||||
try:
|
||||
xlrd.open_workbook(file_contents=stream)
|
||||
return True
|
||||
except (TypeError, XLRDError):
|
||||
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
|
||||
|
||||
|
||||
@@ -61,7 +62,6 @@ def export_book(databook):
|
||||
|
||||
dset_sheet(dset, ws)
|
||||
|
||||
|
||||
stream = BytesIO()
|
||||
wb.save(stream)
|
||||
return stream.getvalue()
|
||||
@@ -122,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,12 +4,7 @@
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
if sys.version_info[0] > 2:
|
||||
from io import BytesIO
|
||||
else:
|
||||
from cStringIO import StringIO as BytesIO
|
||||
from io import BytesIO
|
||||
|
||||
import openpyxl
|
||||
import tablib
|
||||
@@ -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."""
|
||||
@@ -59,7 +57,6 @@ def export_book(databook, freeze_panes=True):
|
||||
|
||||
dset_sheet(dset, ws, freeze_panes=freeze_panes)
|
||||
|
||||
|
||||
stream = BytesIO()
|
||||
wb.save(stream)
|
||||
return stream.getvalue()
|
||||
@@ -70,7 +67,7 @@ def import_set(dset, in_stream, headers=True):
|
||||
|
||||
dset.wipe()
|
||||
|
||||
xls_book = openpyxl.reader.excel.load_workbook(BytesIO(in_stream))
|
||||
xls_book = openpyxl.reader.excel.load_workbook(BytesIO(in_stream), read_only=True)
|
||||
sheet = xls_book.active
|
||||
|
||||
dset.title = sheet.title
|
||||
@@ -88,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()
|
||||
@@ -123,8 +120,6 @@ def dset_sheet(dataset, ws, freeze_panes=True):
|
||||
|
||||
# bold headers
|
||||
if (row_number == 1) and dataset.headers:
|
||||
# cell.value = unicode('%s' % col, errors='ignore')
|
||||
cell.value = unicode(col)
|
||||
cell.font = bold
|
||||
if freeze_panes:
|
||||
# Export Freeze only after first Line
|
||||
@@ -132,16 +127,18 @@ def dset_sheet(dataset, ws, freeze_panes=True):
|
||||
|
||||
# bold separators
|
||||
elif len(row) < dataset.width:
|
||||
cell.value = unicode('%s' % col, errors='ignore')
|
||||
cell.font = bold
|
||||
|
||||
# wrap the rest
|
||||
else:
|
||||
try:
|
||||
if '\n' in col:
|
||||
cell.value = unicode('%s' % col, errors='ignore')
|
||||
cell.alignment = wrap_text
|
||||
else:
|
||||
cell.value = unicode('%s' % col, errors='ignore')
|
||||
str_col_value = unicode(col)
|
||||
except TypeError:
|
||||
cell.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)
|
||||
@@ -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()
|
||||
|
||||
"""
|
||||
@@ -258,7 +258,7 @@ class Dbf(object):
|
||||
def demo_read(filename):
|
||||
_dbf = Dbf(filename, True)
|
||||
for _rec in _dbf:
|
||||
print
|
||||
print()
|
||||
print(repr(_rec))
|
||||
_dbf.close()
|
||||
|
||||
@@ -160,9 +160,9 @@ if __name__ == '__main__':
|
||||
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'
|
||||
@@ -177,13 +177,13 @@ if __name__ == '__main__':
|
||||
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 :
|
||||
@@ -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()
|
||||
|
||||
"""
|
||||
@@ -56,7 +56,6 @@ class DbfFieldDef(object):
|
||||
|
||||
"""
|
||||
|
||||
|
||||
__slots__ = ("name", "decimalCount",
|
||||
"start", "end", "ignoreErrors")
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
""" Tablib. """
|
||||
|
||||
from tablib.core import (
|
||||
Databook, Dataset, detect_format, import_set, import_book,
|
||||
InvalidDatasetType, InvalidDimensions, UnsupportedFormat,
|
||||
__version__
|
||||
)
|
||||
@@ -1,482 +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 in ['http_equiv', 'accept_charset']:
|
||||
key.replace('_','-')
|
||||
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 < and >
|
||||
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( '&', '&' )
|
||||
if '>' in text:
|
||||
text = text.replace( '>', '>' )
|
||||
if '<' in text:
|
||||
text = text.replace( '<', '<' )
|
||||
if '\"' in text:
|
||||
text = text.replace( '\"', '"' )
|
||||
if '\'' in text:
|
||||
text = text.replace( '\'', '"' )
|
||||
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 '&' in text:
|
||||
text = text.replace( '&', '&' )
|
||||
if '>' in text:
|
||||
text = text.replace( '>', '>' )
|
||||
if '<' in text:
|
||||
text = text.replace( '<', '<' )
|
||||
if '"' in text:
|
||||
text = text.replace( '"', '\"' )
|
||||
|
||||
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__)
|
||||
@@ -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 < and >
|
||||
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( '&', '&' )
|
||||
if '>' in text:
|
||||
text = text.replace( '>', '>' )
|
||||
if '<' in text:
|
||||
text = text.replace( '<', '<' )
|
||||
if '\"' in text:
|
||||
text = text.replace( '\"', '"' )
|
||||
if '\'' in text:
|
||||
text = text.replace( '\'', '"' )
|
||||
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 '&' in text:
|
||||
text = text.replace( '&', '&' )
|
||||
if '>' in text:
|
||||
text = text.replace( '>', '>' )
|
||||
if '<' in text:
|
||||
text = text.replace( '<', '<' )
|
||||
if '"' in text:
|
||||
text = text.replace( '"', '\"' )
|
||||
|
||||
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__)
|
||||
@@ -0,0 +1,10 @@
|
||||
pytest
|
||||
pytest-cov
|
||||
backports.csv; python_version < '3.0'
|
||||
MarkupPy
|
||||
odfpy
|
||||
openpyxl>=2.4.0
|
||||
pandas
|
||||
pyyaml
|
||||
xlrd
|
||||
xlwt
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,40 @@
|
||||
[tox]
|
||||
usedevelop = true
|
||||
minversion = 2.4
|
||||
envlist = py27, py34, py35, py36
|
||||
envlist =
|
||||
py{27,35,36,37,38}-tests,
|
||||
py37-{docs,lint}
|
||||
|
||||
[testenv]
|
||||
deps = pytest
|
||||
basepython =
|
||||
py27: python2.7
|
||||
py35: python3.5
|
||||
py36: python3.6
|
||||
py37: python3.7
|
||||
py38: python3.8
|
||||
deps =
|
||||
tests: -rtests/requirements.txt
|
||||
docs: sphinx
|
||||
extras = pandas
|
||||
commands = python setup.py test
|
||||
commands =
|
||||
tests: pytest {posargs:tests}
|
||||
docs: sphinx-build -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html
|
||||
|
||||
[testenv:py37-lint]
|
||||
basepython = python3.7
|
||||
deps =
|
||||
flake8
|
||||
# flake8-black
|
||||
# flake8-isort
|
||||
twine
|
||||
check-manifest
|
||||
commands =
|
||||
# flake8 src/tablib tests/
|
||||
check-manifest -v
|
||||
python setup.py sdist
|
||||
twine check dist/*
|
||||
|
||||
[flake8]
|
||||
exclude =
|
||||
.tox
|
||||
ignore=E501,E127,E128,E124
|
||||
|
||||
Reference in New Issue
Block a user