xlsxlite 0.1.0__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of xlsxlite might be problematic. Click here for more details.
- xlsxlite-1.0.0/LICENSE +21 -0
- xlsxlite-1.0.0/PKG-INFO +13 -0
- xlsxlite-1.0.0/pyproject.toml +32 -0
- xlsxlite-1.0.0/xlsxlite/test/__init__.py +0 -0
- {xlsxlite-0.1.0 → xlsxlite-1.0.0}/xlsxlite/test/base.py +11 -2
- xlsxlite-1.0.0/xlsxlite/test/test_perf.py +76 -0
- xlsxlite-1.0.0/xlsxlite/test/test_utils.py +15 -0
- xlsxlite-0.1.0/xlsxlite/test/test_book.py → xlsxlite-1.0.0/xlsxlite/test/test_writer.py +18 -59
- xlsxlite-1.0.0/xlsxlite/utils.py +14 -0
- xlsxlite-0.1.0/xlsxlite/book.py → xlsxlite-1.0.0/xlsxlite/writer.py +73 -10
- xlsxlite-0.1.0/PKG-INFO +0 -50
- xlsxlite-0.1.0/README.md +0 -32
- xlsxlite-0.1.0/setup.cfg +0 -29
- xlsxlite-0.1.0/setup.py +0 -53
- xlsxlite-0.1.0/xlsxlite/__init__.py +0 -1
- xlsxlite-0.1.0/xlsxlite.egg-info/PKG-INFO +0 -50
- xlsxlite-0.1.0/xlsxlite.egg-info/SOURCES.txt +0 -12
- xlsxlite-0.1.0/xlsxlite.egg-info/dependency_links.txt +0 -1
- xlsxlite-0.1.0/xlsxlite.egg-info/top_level.txt +0 -1
- {xlsxlite-0.1.0/xlsxlite/test → xlsxlite-1.0.0/xlsxlite}/__init__.py +0 -0
xlsxlite-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-2022 TextIt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
xlsxlite-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: xlsxlite
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Lightweight XLSX writer with emphasis on minimizing memory usage.
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: TextIt
|
|
7
|
+
Author-email: code@textit.com
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Project-URL: repository, http://github.com/nyaruka/xlsxlite
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "xlsxlite"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Lightweight XLSX writer with emphasis on minimizing memory usage."
|
|
5
|
+
authors = [
|
|
6
|
+
{"name" = "TextIt", "email" = "code@textit.com"}
|
|
7
|
+
]
|
|
8
|
+
license = "MIT"
|
|
9
|
+
classifiers=[
|
|
10
|
+
'Intended Audience :: Developers',
|
|
11
|
+
'License :: OSI Approved :: MIT License',
|
|
12
|
+
'Operating System :: OS Independent',
|
|
13
|
+
'Programming Language :: Python',
|
|
14
|
+
]
|
|
15
|
+
requires-python = ">=3.10"
|
|
16
|
+
dependencies = []
|
|
17
|
+
|
|
18
|
+
[tool.poetry.urls]
|
|
19
|
+
repository = "http://github.com/nyaruka/xlsxlite"
|
|
20
|
+
|
|
21
|
+
[tool.poetry.group.dev.dependencies]
|
|
22
|
+
pytest = "^8.3.4"
|
|
23
|
+
pytest-cov = "^6.0.0"
|
|
24
|
+
pytz = "^2024.2"
|
|
25
|
+
XlsxWriter = "^3.2.0"
|
|
26
|
+
openpyxl = "3.1.5"
|
|
27
|
+
lxml = "^5.3.0"
|
|
28
|
+
flake8 = "^7.1.1"
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
32
|
+
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import os
|
|
2
|
+
import pytest
|
|
3
|
+
import shutil
|
|
2
4
|
import unittest
|
|
5
|
+
from datetime import datetime, timedelta
|
|
3
6
|
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def tests_dir():
|
|
10
|
+
os.mkdir("_tests")
|
|
11
|
+
yield
|
|
12
|
+
shutil.rmtree("_tests")
|
|
6
13
|
|
|
14
|
+
|
|
15
|
+
class XLSXTest(unittest.TestCase):
|
|
7
16
|
def assertExcelRow(self, sheet, row_num, values, tz=None):
|
|
8
17
|
"""
|
|
9
18
|
Asserts the cell values in the given worksheet row. Date values are converted using the provided timezone.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import random
|
|
3
|
+
import string
|
|
4
|
+
import xlsxwriter
|
|
5
|
+
|
|
6
|
+
from openpyxl import Workbook
|
|
7
|
+
from openpyxl.cell.cell import WriteOnlyCell
|
|
8
|
+
from openpyxl.cell._writer import etree_write_cell
|
|
9
|
+
from unittest.mock import patch
|
|
10
|
+
from xlsxlite.writer import XLSXBook
|
|
11
|
+
from .base import tests_dir # noqa
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
NUM_ROWS = 1000
|
|
15
|
+
NUM_COLS = 10
|
|
16
|
+
|
|
17
|
+
# generate some random strings to use as cell values
|
|
18
|
+
DATA = ["".join(random.choices(string.ascii_uppercase + string.digits, k=16)) for d in range(1000)]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.usefixtures("tests_dir")
|
|
22
|
+
def test_xlxslite():
|
|
23
|
+
book = XLSXBook()
|
|
24
|
+
sheet1 = book.add_sheet("Sheet1")
|
|
25
|
+
|
|
26
|
+
for r in range(NUM_ROWS):
|
|
27
|
+
row = [DATA[(r * c) % len(DATA)] for c in range(NUM_COLS)]
|
|
28
|
+
|
|
29
|
+
sheet1.append_row(*row)
|
|
30
|
+
|
|
31
|
+
book.finalize(to_file="_tests/test.xlsx")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.mark.usefixtures("tests_dir")
|
|
35
|
+
@patch("openpyxl.cell._writer.write_cell")
|
|
36
|
+
def test_openpyxl_etree(mock_write_cell):
|
|
37
|
+
mock_write_cell.side_effect = etree_write_cell
|
|
38
|
+
|
|
39
|
+
book = Workbook(write_only=True)
|
|
40
|
+
sheet1 = book.create_sheet("Sheet1")
|
|
41
|
+
|
|
42
|
+
for r in range(NUM_ROWS):
|
|
43
|
+
row = [DATA[(r * c) % len(DATA)] for c in range(NUM_COLS)]
|
|
44
|
+
|
|
45
|
+
cells = [WriteOnlyCell(sheet1, value=v) for v in row]
|
|
46
|
+
sheet1.append(cells)
|
|
47
|
+
|
|
48
|
+
book.save("_tests/test.xlsx")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest.mark.usefixtures("tests_dir")
|
|
52
|
+
def test_openpyxl_lxml():
|
|
53
|
+
book = Workbook(write_only=True)
|
|
54
|
+
sheet1 = book.create_sheet("Sheet1")
|
|
55
|
+
|
|
56
|
+
for r in range(NUM_ROWS):
|
|
57
|
+
row = [DATA[(r * c) % len(DATA)] for c in range(NUM_COLS)]
|
|
58
|
+
|
|
59
|
+
cells = [WriteOnlyCell(sheet1, value=v) for v in row]
|
|
60
|
+
sheet1.append(cells)
|
|
61
|
+
|
|
62
|
+
book.save("_tests/test.xlsx")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@pytest.mark.usefixtures("tests_dir")
|
|
66
|
+
def test_xlsxwriter():
|
|
67
|
+
book = xlsxwriter.Workbook("_tests/test.xlsx")
|
|
68
|
+
sheet1 = book.add_worksheet()
|
|
69
|
+
|
|
70
|
+
for r in range(NUM_ROWS):
|
|
71
|
+
row = [DATA[(r * c) % len(DATA)] for c in range(NUM_COLS)]
|
|
72
|
+
|
|
73
|
+
for c, val in enumerate(row):
|
|
74
|
+
sheet1.write(r, c, val)
|
|
75
|
+
|
|
76
|
+
book.close()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import pytz
|
|
5
|
+
|
|
6
|
+
from xlsxlite.utils import datetime_to_serial
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_datetime_to_serial():
|
|
10
|
+
assert datetime_to_serial(datetime(2013, 1, 1, 12, 0, 0)) == 41275.5
|
|
11
|
+
assert datetime_to_serial(datetime(2018, 6, 15, 11, 24, 30, 0)) == 43266.47534722222
|
|
12
|
+
|
|
13
|
+
# try with a non-naive datetime
|
|
14
|
+
with pytest.raises(ValueError):
|
|
15
|
+
datetime_to_serial(datetime(2018, 6, 15, 11, 24, 30, 0, pytz.UTC))
|
|
@@ -1,29 +1,15 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import pytest
|
|
3
|
-
import pytz
|
|
4
|
-
import random
|
|
5
|
-
import shutil
|
|
6
|
-
import string
|
|
7
|
-
import time
|
|
8
|
-
|
|
9
1
|
from datetime import datetime, timedelta
|
|
10
|
-
from mock import patch
|
|
11
|
-
from openpyxl.reader.excel import load_workbook
|
|
12
|
-
from unittest import skip
|
|
13
|
-
from xlsxlite.book import XLSXBook
|
|
14
|
-
from .base import XLSXTest
|
|
15
2
|
|
|
3
|
+
import pytest
|
|
4
|
+
from openpyxl.reader.excel import load_workbook
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
from xlsxlite.writer import XLSXBook
|
|
16
7
|
|
|
17
|
-
|
|
18
|
-
def tests_dir():
|
|
19
|
-
os.mkdir("_tests")
|
|
20
|
-
yield
|
|
21
|
-
shutil.rmtree("_tests")
|
|
8
|
+
from .base import XLSXTest, tests_dir # noqa
|
|
22
9
|
|
|
23
10
|
|
|
24
11
|
@pytest.mark.usefixtures("tests_dir")
|
|
25
12
|
class BookTest(XLSXTest):
|
|
26
|
-
|
|
27
13
|
def test_empty(self):
|
|
28
14
|
book = XLSXBook()
|
|
29
15
|
book.finalize(to_file="_tests/empty.xlsx")
|
|
@@ -54,16 +40,16 @@ class BookTest(XLSXTest):
|
|
|
54
40
|
assert sheet2.title == "People"
|
|
55
41
|
assert sheet3.title == "Empty"
|
|
56
42
|
|
|
57
|
-
self.assertExcelSheet(sheet1, [
|
|
43
|
+
self.assertExcelSheet(sheet1, [])
|
|
58
44
|
self.assertExcelSheet(sheet2, [("Name", "Email"), ("Jim", "jim@acme.com"), ("Bob", "bob@acme.com")])
|
|
59
|
-
self.assertExcelSheet(sheet3, [
|
|
45
|
+
self.assertExcelSheet(sheet3, [])
|
|
60
46
|
|
|
61
47
|
def test_cell_types(self):
|
|
62
|
-
d1 = datetime(
|
|
48
|
+
d1 = datetime(2013, 1, 1, 12, 0, 0)
|
|
63
49
|
|
|
64
50
|
book = XLSXBook()
|
|
65
51
|
sheet1 = book.add_sheet("Test")
|
|
66
|
-
sheet1.append_row("str", 3, 1.23, d1)
|
|
52
|
+
sheet1.append_row("str", True, False, 3, 1.23, d1)
|
|
67
53
|
|
|
68
54
|
# try to write a cell value with an unsupported type
|
|
69
55
|
with pytest.raises(ValueError):
|
|
@@ -72,58 +58,31 @@ class BookTest(XLSXTest):
|
|
|
72
58
|
book.finalize(to_file="_tests/types.xlsx")
|
|
73
59
|
|
|
74
60
|
book = load_workbook(filename="_tests/types.xlsx")
|
|
75
|
-
self.assertExcelSheet(book.worksheets[0], [("str", 3, 1.23, d1)]
|
|
61
|
+
self.assertExcelSheet(book.worksheets[0], [("str", True, False, 3, 1.23, d1)])
|
|
76
62
|
|
|
77
63
|
def test_escaping(self):
|
|
78
64
|
book = XLSXBook()
|
|
79
65
|
sheet1 = book.add_sheet("Test")
|
|
80
|
-
sheet1.append_row(
|
|
66
|
+
sheet1.append_row('< & > " ! =')
|
|
81
67
|
book.finalize(to_file="_tests/escaped.xlsx")
|
|
82
68
|
|
|
83
69
|
book = load_workbook(filename="_tests/escaped.xlsx")
|
|
84
|
-
self.assertExcelSheet(book.worksheets[0], [(
|
|
70
|
+
self.assertExcelSheet(book.worksheets[0], [('< & > " ! =',)])
|
|
85
71
|
|
|
86
72
|
def test_sheet_limits(self):
|
|
87
73
|
book = XLSXBook()
|
|
88
74
|
sheet1 = book.add_sheet("Sheet1")
|
|
89
75
|
|
|
90
76
|
# try to add row with too many columns
|
|
91
|
-
column = [
|
|
77
|
+
column = ["x"] * 20000
|
|
92
78
|
with pytest.raises(ValueError):
|
|
93
79
|
sheet1.append_row(*column)
|
|
94
80
|
|
|
95
81
|
# try to add more rows than allowed
|
|
96
|
-
with patch(
|
|
97
|
-
sheet1.append_row(
|
|
98
|
-
sheet1.append_row(
|
|
99
|
-
sheet1.append_row(
|
|
82
|
+
with patch("xlsxlite.writer.XLSXSheet.MAX_ROWS", 3):
|
|
83
|
+
sheet1.append_row("x")
|
|
84
|
+
sheet1.append_row("x")
|
|
85
|
+
sheet1.append_row("x")
|
|
100
86
|
|
|
101
87
|
with pytest.raises(ValueError):
|
|
102
|
-
sheet1.append_row(
|
|
103
|
-
|
|
104
|
-
@skip
|
|
105
|
-
def test_performance(self):
|
|
106
|
-
def random_val():
|
|
107
|
-
return ''.join(random.choices(string.ascii_uppercase + string.digits, k=16))
|
|
108
|
-
|
|
109
|
-
t0 = time.time()
|
|
110
|
-
|
|
111
|
-
book = XLSXBook()
|
|
112
|
-
sheet1 = book.add_sheet("Sheet1")
|
|
113
|
-
book.add_sheet("Sheet2")
|
|
114
|
-
|
|
115
|
-
t1 = time.time()
|
|
116
|
-
print(f"Initialized workbook in {t1 - t0} seconds")
|
|
117
|
-
|
|
118
|
-
num_rows = 1024 * 1024
|
|
119
|
-
for r in range(num_rows):
|
|
120
|
-
row = [random_val() for c in range(10)]
|
|
121
|
-
sheet1.append_row(*row)
|
|
122
|
-
|
|
123
|
-
t2 = time.time()
|
|
124
|
-
print(f"Wrote {num_rows} rows in {t2 - t1} seconds")
|
|
125
|
-
|
|
126
|
-
book.finalize(to_file="_tests/simple.xlsx")
|
|
127
|
-
|
|
128
|
-
t3 = time.time()
|
|
129
|
-
print(f"Finalized XLSX file in {t3 - t2} seconds")
|
|
88
|
+
sheet1.append_row("x")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def datetime_to_serial(dt):
|
|
5
|
+
"""
|
|
6
|
+
Converts the given datetime to the Excel serial format
|
|
7
|
+
"""
|
|
8
|
+
if dt.tzinfo:
|
|
9
|
+
raise ValueError("Doesn't support datetimes with timezones")
|
|
10
|
+
|
|
11
|
+
temp = datetime(1899, 12, 30)
|
|
12
|
+
delta = dt - temp
|
|
13
|
+
|
|
14
|
+
return delta.days + (float(delta.seconds) + float(delta.microseconds) / 1E6) / (60 * 60 * 24)
|
|
@@ -3,18 +3,53 @@ import shutil
|
|
|
3
3
|
import tempfile
|
|
4
4
|
import xml.etree.ElementTree as ET
|
|
5
5
|
import zipfile
|
|
6
|
-
|
|
7
6
|
from datetime import datetime
|
|
8
7
|
from xml.sax.saxutils import escape
|
|
9
8
|
|
|
9
|
+
from .utils import datetime_to_serial
|
|
10
10
|
|
|
11
11
|
XML_HEADER = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n"""
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
|
|
13
|
+
WORKBOOK_HEADER = """<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">"""
|
|
14
|
+
WORKSHEET_HEADER = """<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">"""
|
|
15
|
+
|
|
16
|
+
MINIMAL_STYLESHEET = """<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
|
17
|
+
<numFmts count="1">
|
|
18
|
+
<numFmt formatCode="yyyy-mm-dd h:mm:ss" numFmtId="164"/>
|
|
19
|
+
</numFmts>
|
|
20
|
+
<fonts count="1">
|
|
21
|
+
<font>
|
|
22
|
+
<name val="Calibri"/>
|
|
23
|
+
<family val="2"/>
|
|
24
|
+
<color theme="1"/>
|
|
25
|
+
<sz val="11"/>
|
|
26
|
+
<scheme val="minor"/>
|
|
27
|
+
</font>
|
|
28
|
+
</fonts>
|
|
29
|
+
<fills count="2">
|
|
30
|
+
<fill><patternFill/></fill>
|
|
31
|
+
<fill><patternFill patternType="gray125"/></fill>
|
|
32
|
+
</fills>
|
|
33
|
+
<borders count="1">
|
|
34
|
+
<border>
|
|
35
|
+
<left/>
|
|
36
|
+
<right/>
|
|
37
|
+
<top/>
|
|
38
|
+
<bottom/>
|
|
39
|
+
<diagonal/>
|
|
40
|
+
</border>
|
|
41
|
+
</borders>
|
|
42
|
+
<cellStyleXfs count="1">
|
|
43
|
+
<xf borderId="0" fillId="0" fontId="0" numFmtId="0"/>
|
|
44
|
+
</cellStyleXfs>
|
|
45
|
+
<cellXfs count="2">
|
|
46
|
+
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" pivotButton="0" quotePrefix="0" xfId="0"/>
|
|
47
|
+
<xf borderId="0" fillId="0" fontId="0" numFmtId="164" pivotButton="0" quotePrefix="0" xfId="0"/>
|
|
48
|
+
</cellXfs>
|
|
49
|
+
<cellStyles count="1">
|
|
50
|
+
<cellStyle builtinId="0" hidden="0" name="Normal" xfId="0"/>
|
|
51
|
+
</cellStyles>
|
|
52
|
+
</styleSheet>"""
|
|
18
53
|
|
|
19
54
|
# use a nice big 1MB I/O buffer for the worksheet files
|
|
20
55
|
WORKSHEET_IO_BUFFER = 1048576
|
|
@@ -24,6 +59,7 @@ class XLSXSheet:
|
|
|
24
59
|
"""
|
|
25
60
|
A worksheet within a XLSX workbook
|
|
26
61
|
"""
|
|
62
|
+
|
|
27
63
|
MAX_ROWS = 1048576
|
|
28
64
|
MAX_COLS = 16384
|
|
29
65
|
|
|
@@ -53,11 +89,13 @@ class XLSXSheet:
|
|
|
53
89
|
row = "<row>"
|
|
54
90
|
for val in columns:
|
|
55
91
|
if isinstance(val, str):
|
|
56
|
-
row += f
|
|
92
|
+
row += f'<c t="inlineStr"><is><t>{escape(val)}</t></is></c>'
|
|
93
|
+
elif isinstance(val, bool):
|
|
94
|
+
row += f'<c t="b"><v>{int(val)}</v></c>'
|
|
57
95
|
elif isinstance(val, (int, float)):
|
|
58
|
-
row += f
|
|
96
|
+
row += f'<c t="n"><v>{str(val)}</v></c>'
|
|
59
97
|
elif isinstance(val, datetime):
|
|
60
|
-
row += f
|
|
98
|
+
row += f'<c t="n" s="1"><v>{datetime_to_serial(val)}</v></c>'
|
|
61
99
|
else:
|
|
62
100
|
raise ValueError(f"Unsupported type in column data: {type(val)}")
|
|
63
101
|
|
|
@@ -78,6 +116,7 @@ class XLSXBook:
|
|
|
78
116
|
"""
|
|
79
117
|
An XLSX workbook
|
|
80
118
|
"""
|
|
119
|
+
|
|
81
120
|
def __init__(self):
|
|
82
121
|
self.base_dir = tempfile.mkdtemp()
|
|
83
122
|
self.app_dir = os.path.join(self.base_dir, "xl")
|
|
@@ -108,6 +147,14 @@ class XLSXBook:
|
|
|
108
147
|
{"Extension": "rels", "ContentType": "application/vnd.openxmlformats-package.relationships+xml"},
|
|
109
148
|
)
|
|
110
149
|
ET.SubElement(types, "Default", {"Extension": "xml", "ContentType": "application/xml"})
|
|
150
|
+
ET.SubElement(
|
|
151
|
+
types,
|
|
152
|
+
"Override",
|
|
153
|
+
{
|
|
154
|
+
"PartName": "/xl/styles.xml",
|
|
155
|
+
"ContentType": "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
|
156
|
+
},
|
|
157
|
+
)
|
|
111
158
|
ET.SubElement(
|
|
112
159
|
types,
|
|
113
160
|
"Override",
|
|
@@ -171,10 +218,25 @@ class XLSXBook:
|
|
|
171
218
|
},
|
|
172
219
|
)
|
|
173
220
|
|
|
221
|
+
ET.SubElement(
|
|
222
|
+
relationships,
|
|
223
|
+
"Relationship",
|
|
224
|
+
{
|
|
225
|
+
"Id": "rIdStyles",
|
|
226
|
+
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
|
227
|
+
"Target": "styles.xml",
|
|
228
|
+
},
|
|
229
|
+
)
|
|
230
|
+
|
|
174
231
|
with open(os.path.join(self.app_dir, "_rels/workbook.xml.rels"), "w", encoding="utf-8") as f:
|
|
175
232
|
f.write(XML_HEADER)
|
|
176
233
|
f.write(ET.tostring(relationships, encoding="unicode"))
|
|
177
234
|
|
|
235
|
+
def _create_styles(self):
|
|
236
|
+
with open(os.path.join(self.app_dir, "styles.xml"), "w", encoding="utf-8") as f:
|
|
237
|
+
f.write(XML_HEADER)
|
|
238
|
+
f.write(MINIMAL_STYLESHEET)
|
|
239
|
+
|
|
178
240
|
def _create_workbook(self):
|
|
179
241
|
sheets = ET.Element("sheets")
|
|
180
242
|
for sheet in self.sheets:
|
|
@@ -204,6 +266,7 @@ class XLSXBook:
|
|
|
204
266
|
self._create_content_types()
|
|
205
267
|
self._create_root_rels()
|
|
206
268
|
self._create_app_rels()
|
|
269
|
+
self._create_styles()
|
|
207
270
|
self._create_workbook()
|
|
208
271
|
|
|
209
272
|
for sheet in self.sheets:
|
xlsxlite-0.1.0/PKG-INFO
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: xlsxlite
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Lightweight XLSX writer with emphasis on minimizing memory usage.
|
|
5
|
-
Home-page: http://github.com/nyaruka/xlxslite
|
|
6
|
-
Author: Nyaruka
|
|
7
|
-
Author-email: code@nyaruka.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Project-URL: Bug Reports, https://github.com/nyaruka/xlsxlite/issues
|
|
10
|
-
Project-URL: Source, https://github.com/nyaruka/xlsxlite/
|
|
11
|
-
Description: # Introduction
|
|
12
|
-
|
|
13
|
-
[](https://travis-ci.org/nyaruka/xlsxlite)
|
|
14
|
-
[](https://coveralls.io/github/nyaruka/xlsxlite?branch=master)
|
|
15
|
-
[](https://pypi.python.org/pypi/xlsxlite/)
|
|
16
|
-
|
|
17
|
-
XLSXLite is a lightweight XLSX writer with emphasis on minimizing memory usage. It's also really fast.
|
|
18
|
-
|
|
19
|
-
```python
|
|
20
|
-
from xlsxlite.book import XLSXBook
|
|
21
|
-
book = XLSXBook()
|
|
22
|
-
sheet1 = book.add_sheet("People")
|
|
23
|
-
sheet1.append_row("Name", "Email", "Age")
|
|
24
|
-
sheet1.append_row("Jim", "jim@acme.com", 45)
|
|
25
|
-
book.finalize(to_file="simple.xlsx")
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Limitations
|
|
29
|
-
|
|
30
|
-
This library is for projects which need to generate large spreadsheets, quickly, for the purposes of data exchange, and
|
|
31
|
-
so it intentionally only supports a tiny subset of XLSX specification:
|
|
32
|
-
|
|
33
|
-
* No styling
|
|
34
|
-
* Only strings, numbers and dates are supported cell types
|
|
35
|
-
|
|
36
|
-
## Development
|
|
37
|
-
|
|
38
|
-
To run all tests:
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
py.test xlsxlite -s
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Keywords: excel xlxs
|
|
45
|
-
Platform: UNKNOWN
|
|
46
|
-
Classifier: Intended Audience :: Developers
|
|
47
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
48
|
-
Classifier: Operating System :: OS Independent
|
|
49
|
-
Classifier: Programming Language :: Python
|
|
50
|
-
Description-Content-Type: text/markdown
|
xlsxlite-0.1.0/README.md
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Introduction
|
|
2
|
-
|
|
3
|
-
[](https://travis-ci.org/nyaruka/xlsxlite)
|
|
4
|
-
[](https://coveralls.io/github/nyaruka/xlsxlite?branch=master)
|
|
5
|
-
[](https://pypi.python.org/pypi/xlsxlite/)
|
|
6
|
-
|
|
7
|
-
XLSXLite is a lightweight XLSX writer with emphasis on minimizing memory usage. It's also really fast.
|
|
8
|
-
|
|
9
|
-
```python
|
|
10
|
-
from xlsxlite.book import XLSXBook
|
|
11
|
-
book = XLSXBook()
|
|
12
|
-
sheet1 = book.add_sheet("People")
|
|
13
|
-
sheet1.append_row("Name", "Email", "Age")
|
|
14
|
-
sheet1.append_row("Jim", "jim@acme.com", 45)
|
|
15
|
-
book.finalize(to_file="simple.xlsx")
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Limitations
|
|
19
|
-
|
|
20
|
-
This library is for projects which need to generate large spreadsheets, quickly, for the purposes of data exchange, and
|
|
21
|
-
so it intentionally only supports a tiny subset of XLSX specification:
|
|
22
|
-
|
|
23
|
-
* No styling
|
|
24
|
-
* Only strings, numbers and dates are supported cell types
|
|
25
|
-
|
|
26
|
-
## Development
|
|
27
|
-
|
|
28
|
-
To run all tests:
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
py.test xlsxlite -s
|
|
32
|
-
```
|
xlsxlite-0.1.0/setup.cfg
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
[flake8]
|
|
2
|
-
max-line-length = 120
|
|
3
|
-
filename = *.py,*.py.dev
|
|
4
|
-
exclude = ./env/*,./config/*,./fab*,.git/*,static/bower/*,sitestatic/bower/,*/gen/*
|
|
5
|
-
ignore = E501,F405,T003,E203,W503
|
|
6
|
-
|
|
7
|
-
[isort]
|
|
8
|
-
multi_line_output = 3
|
|
9
|
-
including_trailing_comma = True
|
|
10
|
-
force_grid_wrap = 0
|
|
11
|
-
line_length = 119
|
|
12
|
-
include_trailing_comma = True
|
|
13
|
-
combine_as_imports = True
|
|
14
|
-
sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
|
|
15
|
-
|
|
16
|
-
[coverage:run]
|
|
17
|
-
source = xlsxlite
|
|
18
|
-
omit = xlsxlite/test/*
|
|
19
|
-
|
|
20
|
-
[metadata]
|
|
21
|
-
license_file = LICENSE
|
|
22
|
-
|
|
23
|
-
[bdist_wheel]
|
|
24
|
-
universal = 1
|
|
25
|
-
|
|
26
|
-
[egg_info]
|
|
27
|
-
tag_build =
|
|
28
|
-
tag_date = 0
|
|
29
|
-
|
xlsxlite-0.1.0/setup.py
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# Always prefer setuptools over distutils
|
|
2
|
-
from setuptools import setup, find_packages
|
|
3
|
-
from os import path
|
|
4
|
-
|
|
5
|
-
here = path.abspath(path.dirname(__file__))
|
|
6
|
-
|
|
7
|
-
# Get the long description from the README file
|
|
8
|
-
with open(path.join(here, 'README.md'), encoding='utf-8') as f:
|
|
9
|
-
long_description = f.read()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _is_requirement(line):
|
|
13
|
-
"""Returns whether the line is a valid package requirement."""
|
|
14
|
-
line = line.strip()
|
|
15
|
-
return line and not line.startswith("#")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def _read_requirements(filename):
|
|
19
|
-
"""Parses a file for pip installation requirements."""
|
|
20
|
-
with open(filename) as requirements_file:
|
|
21
|
-
contents = requirements_file.read()
|
|
22
|
-
return [line.strip() for line in contents.splitlines() if _is_requirement(line)]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
setup(
|
|
26
|
-
name='xlsxlite',
|
|
27
|
-
version=__import__('xlsxlite').__version__,
|
|
28
|
-
description='Lightweight XLSX writer with emphasis on minimizing memory usage.',
|
|
29
|
-
long_description=long_description,
|
|
30
|
-
long_description_content_type='text/markdown',
|
|
31
|
-
|
|
32
|
-
classifiers=[
|
|
33
|
-
'Intended Audience :: Developers',
|
|
34
|
-
'License :: OSI Approved :: MIT License',
|
|
35
|
-
'Operating System :: OS Independent',
|
|
36
|
-
'Programming Language :: Python'
|
|
37
|
-
],
|
|
38
|
-
keywords='excel xlxs',
|
|
39
|
-
url='http://github.com/nyaruka/xlxslite',
|
|
40
|
-
license='MIT',
|
|
41
|
-
|
|
42
|
-
author='Nyaruka',
|
|
43
|
-
author_email='code@nyaruka.com',
|
|
44
|
-
|
|
45
|
-
packages=find_packages(),
|
|
46
|
-
install_requires=_read_requirements("requirements/base.txt"),
|
|
47
|
-
tests_require=_read_requirements("requirements/tests.txt"),
|
|
48
|
-
|
|
49
|
-
project_urls={
|
|
50
|
-
'Bug Reports': 'https://github.com/nyaruka/xlsxlite/issues',
|
|
51
|
-
'Source': 'https://github.com/nyaruka/xlsxlite/',
|
|
52
|
-
},
|
|
53
|
-
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.0"
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: xlsxlite
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Lightweight XLSX writer with emphasis on minimizing memory usage.
|
|
5
|
-
Home-page: http://github.com/nyaruka/xlxslite
|
|
6
|
-
Author: Nyaruka
|
|
7
|
-
Author-email: code@nyaruka.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Project-URL: Bug Reports, https://github.com/nyaruka/xlsxlite/issues
|
|
10
|
-
Project-URL: Source, https://github.com/nyaruka/xlsxlite/
|
|
11
|
-
Description: # Introduction
|
|
12
|
-
|
|
13
|
-
[](https://travis-ci.org/nyaruka/xlsxlite)
|
|
14
|
-
[](https://coveralls.io/github/nyaruka/xlsxlite?branch=master)
|
|
15
|
-
[](https://pypi.python.org/pypi/xlsxlite/)
|
|
16
|
-
|
|
17
|
-
XLSXLite is a lightweight XLSX writer with emphasis on minimizing memory usage. It's also really fast.
|
|
18
|
-
|
|
19
|
-
```python
|
|
20
|
-
from xlsxlite.book import XLSXBook
|
|
21
|
-
book = XLSXBook()
|
|
22
|
-
sheet1 = book.add_sheet("People")
|
|
23
|
-
sheet1.append_row("Name", "Email", "Age")
|
|
24
|
-
sheet1.append_row("Jim", "jim@acme.com", 45)
|
|
25
|
-
book.finalize(to_file="simple.xlsx")
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Limitations
|
|
29
|
-
|
|
30
|
-
This library is for projects which need to generate large spreadsheets, quickly, for the purposes of data exchange, and
|
|
31
|
-
so it intentionally only supports a tiny subset of XLSX specification:
|
|
32
|
-
|
|
33
|
-
* No styling
|
|
34
|
-
* Only strings, numbers and dates are supported cell types
|
|
35
|
-
|
|
36
|
-
## Development
|
|
37
|
-
|
|
38
|
-
To run all tests:
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
py.test xlsxlite -s
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Keywords: excel xlxs
|
|
45
|
-
Platform: UNKNOWN
|
|
46
|
-
Classifier: Intended Audience :: Developers
|
|
47
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
48
|
-
Classifier: Operating System :: OS Independent
|
|
49
|
-
Classifier: Programming Language :: Python
|
|
50
|
-
Description-Content-Type: text/markdown
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
README.md
|
|
2
|
-
setup.cfg
|
|
3
|
-
setup.py
|
|
4
|
-
xlsxlite/__init__.py
|
|
5
|
-
xlsxlite/book.py
|
|
6
|
-
xlsxlite.egg-info/PKG-INFO
|
|
7
|
-
xlsxlite.egg-info/SOURCES.txt
|
|
8
|
-
xlsxlite.egg-info/dependency_links.txt
|
|
9
|
-
xlsxlite.egg-info/top_level.txt
|
|
10
|
-
xlsxlite/test/__init__.py
|
|
11
|
-
xlsxlite/test/base.py
|
|
12
|
-
xlsxlite/test/test_book.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
xlsxlite
|
|
File without changes
|