numbers-parser 4.14.3__tar.gz → 4.15.1__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.
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/PKG-INFO +8 -25
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/README.md +4 -21
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/pyproject.toml +21 -23
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/__init__.py +1 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/_cat_numbers.py +15 -6
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/cell.py +28 -202
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/constants.py +8 -4
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/document.py +26 -3
- numbers_parser-4.15.1/src/numbers_parser/formula.py +689 -0
- numbers_parser-4.15.1/src/numbers_parser/generated/TSCEArchives_pb2.py +363 -0
- numbers_parser-4.15.1/src/numbers_parser/generated/TSSArchives_pb2.py +64 -0
- numbers_parser-4.15.1/src/numbers_parser/generated/TSTArchives_pb2.py +437 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/mapping.py +1 -2
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/model.py +281 -187
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/numbers_uuid.py +6 -0
- numbers_parser-4.15.1/src/numbers_parser/tokenizer.py +548 -0
- numbers_parser-4.15.1/src/numbers_parser/xrefs.py +850 -0
- numbers_parser-4.14.3/src/numbers_parser/formula.py +0 -322
- numbers_parser-4.14.3/src/numbers_parser/generated/TSCEArchives_pb2.py +0 -347
- numbers_parser-4.14.3/src/numbers_parser/generated/TSSArchives_pb2.py +0 -64
- numbers_parser-4.14.3/src/numbers_parser/generated/TSTArchives_pb2.py +0 -441
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/LICENSE.rst +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/_csv2numbers.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/_unpack_numbers.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/bullets.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/containers.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/currencies.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/data/empty.numbers +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/exceptions.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/experimental.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TNArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TNArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TNCommandArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TNCommandArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSAArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSAArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSACommandArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSCH3DArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSCHArchives_Common_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSCHArchives_GEN_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSCHArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSCHArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSCHCommandArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSCHPreUFFArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSCKArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSCKArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSDArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSDArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSDCommandArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSKArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSPArchiveMessages_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSPDatabaseMessages_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSPMessages_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSSArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSTArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSTCommandArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSTStylePropertyArchiving_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSWPArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSWPArchives_sos_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/TSWPCommandArchives_pb2.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/__init__.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/fontmap.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/generated/functionmap.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/iwafile.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/iwork.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/numbers_cache.py +0 -0
- {numbers_parser-4.14.3 → numbers_parser-4.15.1}/src/numbers_parser/roman.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: numbers-parser
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.15.1
|
|
4
4
|
Summary: Read and write Apple Numbers spreadsheets
|
|
5
|
-
Home-page: https://github.com/masaccio/numbers-parser
|
|
6
5
|
License: MIT
|
|
7
6
|
Author: Jon Connell
|
|
8
7
|
Author-email: python@figsandfudge.com
|
|
@@ -14,11 +13,12 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
18
18
|
Requires-Dist: compact-json (>=1.1.3,<2.0.0)
|
|
19
19
|
Requires-Dist: enum-tools (>=0.11)
|
|
20
20
|
Requires-Dist: importlib-resources (>=6.1)
|
|
21
|
-
Requires-Dist: protobuf
|
|
21
|
+
Requires-Dist: protobuf (>=4.0,<6.0)
|
|
22
22
|
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
|
23
23
|
Requires-Dist: python-snappy (>=0.7,<0.8)
|
|
24
24
|
Requires-Dist: setuptools (>=70.0.0)
|
|
@@ -40,32 +40,15 @@ with earlier versions of Python.
|
|
|
40
40
|
|
|
41
41
|
## Installation
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
python3 -m pip install numbers-parser
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
A pre-requisite for this package is [python-snappy](https://pypi.org/project/python-snappy/)
|
|
48
|
-
which will be installed by Python automatically, but python-snappy also requires that the binary
|
|
49
|
-
libraries for snappy compression are present.
|
|
43
|
+
A pre-requisite for this package is [python-snappy](https://pypi.org/project/python-snappy/) which will be installed by Python automatically, but python-snappy also requires binary libraries for snappy compression.
|
|
50
44
|
|
|
51
45
|
The most straightforward way to install the binary dependencies is to use
|
|
52
46
|
[Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described
|
|
53
|
-
in the [python-snappy github](https://github.com/andrix/python-snappy):
|
|
54
|
-
|
|
55
|
-
For Intel Macs:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
brew install snappy python3
|
|
59
|
-
CPPFLAGS="-I/usr/local/include -L/usr/local/lib" \
|
|
60
|
-
python3 -m pip install python-snappy
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
For Apple Silicon Macs:
|
|
47
|
+
in the [python-snappy github](https://github.com/andrix/python-snappy). Using [pipx](https://pipx.pypa.io/stable/installation/) for package management is also strongly recommended:
|
|
64
48
|
|
|
65
49
|
```bash
|
|
66
|
-
brew install snappy python3
|
|
67
|
-
|
|
68
|
-
python3 -m pip install python-snappy
|
|
50
|
+
brew install snappy python3 pipx
|
|
51
|
+
pipx install numbers-parser
|
|
69
52
|
```
|
|
70
53
|
|
|
71
54
|
For Linux (your package manager may be different):
|
|
@@ -11,32 +11,15 @@ with earlier versions of Python.
|
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
python3 -m pip install numbers-parser
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
A pre-requisite for this package is [python-snappy](https://pypi.org/project/python-snappy/)
|
|
19
|
-
which will be installed by Python automatically, but python-snappy also requires that the binary
|
|
20
|
-
libraries for snappy compression are present.
|
|
14
|
+
A pre-requisite for this package is [python-snappy](https://pypi.org/project/python-snappy/) which will be installed by Python automatically, but python-snappy also requires binary libraries for snappy compression.
|
|
21
15
|
|
|
22
16
|
The most straightforward way to install the binary dependencies is to use
|
|
23
17
|
[Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described
|
|
24
|
-
in the [python-snappy github](https://github.com/andrix/python-snappy):
|
|
25
|
-
|
|
26
|
-
For Intel Macs:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
brew install snappy python3
|
|
30
|
-
CPPFLAGS="-I/usr/local/include -L/usr/local/lib" \
|
|
31
|
-
python3 -m pip install python-snappy
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
For Apple Silicon Macs:
|
|
18
|
+
in the [python-snappy github](https://github.com/andrix/python-snappy). Using [pipx](https://pipx.pypa.io/stable/installation/) for package management is also strongly recommended:
|
|
35
19
|
|
|
36
20
|
```bash
|
|
37
|
-
brew install snappy python3
|
|
38
|
-
|
|
39
|
-
python3 -m pip install python-snappy
|
|
21
|
+
brew install snappy python3 pipx
|
|
22
|
+
pipx install numbers-parser
|
|
40
23
|
```
|
|
41
24
|
|
|
42
25
|
For Linux (your package manager may be different):
|
|
@@ -12,7 +12,7 @@ name = "numbers-parser"
|
|
|
12
12
|
packages = [{include = "numbers_parser", from = "src"}]
|
|
13
13
|
readme = "README.md"
|
|
14
14
|
repository = "https://github.com/masaccio/numbers-parser"
|
|
15
|
-
version = "4.
|
|
15
|
+
version = "4.15.1"
|
|
16
16
|
|
|
17
17
|
[tool.poetry.scripts]
|
|
18
18
|
cat-numbers = "numbers_parser._cat_numbers:main"
|
|
@@ -21,7 +21,7 @@ csv2numbers = "numbers_parser._csv2numbers:main"
|
|
|
21
21
|
|
|
22
22
|
[tool.poetry.dependencies]
|
|
23
23
|
compact-json = "^1.1.3"
|
|
24
|
-
protobuf = "
|
|
24
|
+
protobuf = ">=4.0,<6.0"
|
|
25
25
|
python = ">=3.9,<4.0"
|
|
26
26
|
python-snappy = "^0.7"
|
|
27
27
|
sigfig = "^1.3.3"
|
|
@@ -34,7 +34,6 @@ python-dateutil = "^2.9.0.post0"
|
|
|
34
34
|
gprof2dot = "^2022.7.29"
|
|
35
35
|
line-profiler = "^4.0.3"
|
|
36
36
|
mock = ">=5.1.0"
|
|
37
|
-
psutil = ">=5.9"
|
|
38
37
|
pytest = ">=7.2.0"
|
|
39
38
|
pytest-check = ">=1.0"
|
|
40
39
|
pytest-console-scripts = "^1.3.1"
|
|
@@ -45,7 +44,7 @@ tox = "^4.11.4"
|
|
|
45
44
|
python-magic = ">=0.4"
|
|
46
45
|
tqdm = ">=4.66"
|
|
47
46
|
colorama = "^0.4.6"
|
|
48
|
-
|
|
47
|
+
pympler = "^1.1"
|
|
49
48
|
|
|
50
49
|
[tool.poetry.group.docs]
|
|
51
50
|
optional = true
|
|
@@ -54,7 +53,7 @@ optional = true
|
|
|
54
53
|
sphinx = ">= 7.3"
|
|
55
54
|
enum-tools = ">=0.11"
|
|
56
55
|
sphinx-toolbox = ">=3.5"
|
|
57
|
-
sphinx-nefertiti = ">=0.
|
|
56
|
+
sphinx-nefertiti = ">=0.7"
|
|
58
57
|
sphinx-markdown-builder = ">=0.6"
|
|
59
58
|
sphinx-copybutton = ">=0.5"
|
|
60
59
|
|
|
@@ -82,23 +81,23 @@ show_contexts = true
|
|
|
82
81
|
addopts = "--cov=src/numbers_parser --cov-report=html --cov-report=term-missing:skip-covered --cov-context=test"
|
|
83
82
|
|
|
84
83
|
[tool.tox]
|
|
85
|
-
|
|
86
|
-
[
|
|
87
|
-
|
|
88
|
-
envlist = py39, py310, py311, py312, py313
|
|
84
|
+
requires = ["tox>=4.0"]
|
|
85
|
+
envlist = ["py39", "py310", "py311", "py312", "py313"]
|
|
86
|
+
|
|
89
87
|
[testenv]
|
|
90
|
-
deps =
|
|
91
|
-
pytest
|
|
92
|
-
pytest-check
|
|
93
|
-
pytest-console-scripts
|
|
94
|
-
pytest-cov
|
|
95
|
-
pytest-xdist
|
|
96
|
-
python-magic
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
""
|
|
88
|
+
deps = [
|
|
89
|
+
"pytest",
|
|
90
|
+
"pytest-check",
|
|
91
|
+
"pytest-console-scripts",
|
|
92
|
+
"pytest-cov",
|
|
93
|
+
"pytest-xdist",
|
|
94
|
+
"python-magic",
|
|
95
|
+
"pympler",
|
|
96
|
+
"colorama"
|
|
97
|
+
]
|
|
98
|
+
commands = [
|
|
99
|
+
"pytest tests/ --import-mode importlib -n logical --no-cov"
|
|
100
|
+
]
|
|
102
101
|
|
|
103
102
|
[tool.isort]
|
|
104
103
|
profile = "black"
|
|
@@ -115,7 +114,6 @@ exclude = [
|
|
|
115
114
|
]
|
|
116
115
|
fix = true
|
|
117
116
|
lint.ignore = [
|
|
118
|
-
# "PLR2004", # Allow constant values
|
|
119
117
|
"T201", # Allow print()
|
|
120
118
|
# To fix:
|
|
121
119
|
"ANN001", #Missing type annotation
|
|
@@ -144,7 +142,7 @@ lint.ignore = [
|
|
|
144
142
|
"D401", #First line of docstring should be in imperative mood
|
|
145
143
|
"D415", #First line should end with a period, question mark, or exclamation point
|
|
146
144
|
"E501", #Line too long
|
|
147
|
-
"ERA001", #
|
|
145
|
+
"ERA001", # Remove commented out code
|
|
148
146
|
"FBT001", #Boolean-typed positional argument in function definition
|
|
149
147
|
"FBT002", #Boolean default positional argument in function definition
|
|
150
148
|
"FBT003", #Boolean positional value in function call
|
|
@@ -13,6 +13,7 @@ with warnings.catch_warnings():
|
|
|
13
13
|
from numbers_parser.constants import * # noqa: F403
|
|
14
14
|
from numbers_parser.document import * # noqa: F403
|
|
15
15
|
from numbers_parser.exceptions import * # noqa: F403
|
|
16
|
+
from numbers_parser.xrefs import * # noqa: F403
|
|
16
17
|
|
|
17
18
|
__version__ = importlib.metadata.version("numbers-parser")
|
|
18
19
|
|
|
@@ -5,7 +5,15 @@ import sys
|
|
|
5
5
|
|
|
6
6
|
from sigfig import round as sigfig
|
|
7
7
|
|
|
8
|
-
from numbers_parser import
|
|
8
|
+
from numbers_parser import (
|
|
9
|
+
Document,
|
|
10
|
+
ErrorCell,
|
|
11
|
+
FileError,
|
|
12
|
+
FileFormatError,
|
|
13
|
+
NumberCell,
|
|
14
|
+
UnsupportedError,
|
|
15
|
+
_get_version,
|
|
16
|
+
)
|
|
9
17
|
from numbers_parser import __name__ as numbers_parser_name
|
|
10
18
|
from numbers_parser.constants import MAX_SIGNIFICANT_DIGITS
|
|
11
19
|
from numbers_parser.experimental import _enable_experimental_features
|
|
@@ -137,11 +145,12 @@ def main() -> None:
|
|
|
137
145
|
print_table_names(filename)
|
|
138
146
|
else:
|
|
139
147
|
print_table(args, filename)
|
|
140
|
-
except FileFormatError as e: # noqa: PERF203
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
148
|
+
except (FileFormatError, FileError, UnsupportedError) as e: # noqa: PERF203
|
|
149
|
+
err_str = str(e)
|
|
150
|
+
if filename in err_str:
|
|
151
|
+
print(err_str, file=sys.stderr)
|
|
152
|
+
else:
|
|
153
|
+
print(f"{filename}: {err_str}", file=sys.stderr)
|
|
145
154
|
sys.exit(1)
|
|
146
155
|
|
|
147
156
|
|
|
@@ -22,6 +22,7 @@ from numbers_parser.constants import (
|
|
|
22
22
|
CURRENCY_CELL_TYPE,
|
|
23
23
|
CUSTOM_TEXT_PLACEHOLDER,
|
|
24
24
|
DATETIME_FIELD_MAP,
|
|
25
|
+
DECIMAL128_BIAS,
|
|
25
26
|
DECIMAL_PLACES_AUTO,
|
|
26
27
|
DEFAULT_ALIGNMENT,
|
|
27
28
|
DEFAULT_BORDER_COLOR,
|
|
@@ -55,6 +56,7 @@ from numbers_parser.constants import (
|
|
|
55
56
|
)
|
|
56
57
|
from numbers_parser.currencies import CURRENCIES, CURRENCY_SYMBOLS
|
|
57
58
|
from numbers_parser.exceptions import UnsupportedError, UnsupportedWarning
|
|
59
|
+
from numbers_parser.formula import Formula
|
|
58
60
|
from numbers_parser.generated import TSPMessages_pb2 as TSPMessages
|
|
59
61
|
from numbers_parser.generated import TSTArchives_pb2 as TSTArchives
|
|
60
62
|
from numbers_parser.generated.TSWPArchives_pb2 import (
|
|
@@ -62,12 +64,14 @@ from numbers_parser.generated.TSWPArchives_pb2 import (
|
|
|
62
64
|
)
|
|
63
65
|
from numbers_parser.numbers_cache import Cacheable, cache
|
|
64
66
|
from numbers_parser.numbers_uuid import NumbersUUID
|
|
67
|
+
from numbers_parser.xrefs import xl_range
|
|
65
68
|
|
|
66
69
|
logger = logging.getLogger(numbers_parser_name)
|
|
67
70
|
debug = logger.debug
|
|
68
71
|
|
|
69
72
|
|
|
70
73
|
__all__ = [
|
|
74
|
+
"RGB",
|
|
71
75
|
"Alignment",
|
|
72
76
|
"BackgroundImage",
|
|
73
77
|
"BoolCell",
|
|
@@ -88,14 +92,9 @@ __all__ = [
|
|
|
88
92
|
"MergedCell",
|
|
89
93
|
"NumberCell",
|
|
90
94
|
"RichTextCell",
|
|
91
|
-
"RGB",
|
|
92
95
|
"Style",
|
|
93
96
|
"TextCell",
|
|
94
97
|
"VerticalJustification",
|
|
95
|
-
"xl_cell_to_rowcol",
|
|
96
|
-
"xl_col_to_name",
|
|
97
|
-
"xl_range",
|
|
98
|
-
"xl_rowcol_to_cell",
|
|
99
98
|
]
|
|
100
99
|
|
|
101
100
|
|
|
@@ -562,8 +561,6 @@ class CellStorageFlags:
|
|
|
562
561
|
_rich_id: int = None
|
|
563
562
|
_cell_style_id: int = None
|
|
564
563
|
_text_style_id: int = None
|
|
565
|
-
# _cond_style_id: int = None
|
|
566
|
-
# _cond_rule_style_id: int = None
|
|
567
564
|
_formula_id: int = None
|
|
568
565
|
_control_id: int = None
|
|
569
566
|
_formula_error_id: int = None
|
|
@@ -574,8 +571,6 @@ class CellStorageFlags:
|
|
|
574
571
|
_duration_format_id: int = None
|
|
575
572
|
_text_format_id: int = None
|
|
576
573
|
_bool_format_id: int = None
|
|
577
|
-
# _comment_id: int = None
|
|
578
|
-
# _import_warning_id: int = None
|
|
579
574
|
|
|
580
575
|
def __str__(self) -> str:
|
|
581
576
|
fields = [
|
|
@@ -599,6 +594,7 @@ class Cell(CellStorageFlags, Cacheable):
|
|
|
599
594
|
self.row = row
|
|
600
595
|
self.col = col
|
|
601
596
|
self._is_bulleted = False
|
|
597
|
+
self._formula_id = None
|
|
602
598
|
self._storage = None
|
|
603
599
|
self._style = None
|
|
604
600
|
self._d128 = None
|
|
@@ -641,11 +637,9 @@ class Cell(CellStorageFlags, Cacheable):
|
|
|
641
637
|
@property
|
|
642
638
|
def is_formula(self) -> bool:
|
|
643
639
|
"""bool: ``True`` if the cell contains a formula."""
|
|
644
|
-
|
|
645
|
-
return table_formulas.is_formula(self.row, self.col)
|
|
640
|
+
return self._formula_id is not None
|
|
646
641
|
|
|
647
642
|
@property
|
|
648
|
-
@cache(num_args=0)
|
|
649
643
|
def formula(self) -> str:
|
|
650
644
|
"""
|
|
651
645
|
str: The formula in a cell.
|
|
@@ -666,6 +660,17 @@ class Cell(CellStorageFlags, Cacheable):
|
|
|
666
660
|
return table_formulas.formula(self._formula_id, self.row, self.col)
|
|
667
661
|
return None
|
|
668
662
|
|
|
663
|
+
@formula.setter
|
|
664
|
+
def formula(self, value: str) -> None:
|
|
665
|
+
self._formula_id = Formula.from_str(
|
|
666
|
+
self._model,
|
|
667
|
+
self._table_id,
|
|
668
|
+
self.row,
|
|
669
|
+
self.col,
|
|
670
|
+
value,
|
|
671
|
+
)
|
|
672
|
+
self._model.add_formula_dependency(self.row, self.col, self._table_id)
|
|
673
|
+
|
|
669
674
|
@property
|
|
670
675
|
def is_bulleted(self) -> bool:
|
|
671
676
|
"""bool: ``True`` if the cell contains text bullets."""
|
|
@@ -740,7 +745,7 @@ class Cell(CellStorageFlags, Cacheable):
|
|
|
740
745
|
or self._bool_format_id is not None
|
|
741
746
|
):
|
|
742
747
|
return self._custom_format()
|
|
743
|
-
return str(self.value)
|
|
748
|
+
return str(self.value).upper() if isinstance(self.value, bool) else str(self.value)
|
|
744
749
|
|
|
745
750
|
@property
|
|
746
751
|
def style(self) -> Style | None:
|
|
@@ -872,20 +877,16 @@ class Cell(CellStorageFlags, Cacheable):
|
|
|
872
877
|
storage_flags._text_style_id = unpack("<i", buffer[offset : offset + 4])[0]
|
|
873
878
|
offset += 4
|
|
874
879
|
if flags & 0x80:
|
|
875
|
-
#
|
|
880
|
+
# cond_style_id skipped
|
|
876
881
|
offset += 4
|
|
877
|
-
#
|
|
878
|
-
# storage_flags._cond_rule_style_id = unpack("<i", buffer[offset : offset + 4])[0]
|
|
879
|
-
# offset += 4
|
|
882
|
+
# Skip flag 0x100 (cond_rule_style_id)
|
|
880
883
|
if flags & 0x200:
|
|
881
884
|
storage_flags._formula_id = unpack("<i", buffer[offset : offset + 4])[0]
|
|
882
885
|
offset += 4
|
|
883
886
|
if flags & 0x400:
|
|
884
887
|
storage_flags._control_id = unpack("<i", buffer[offset : offset + 4])[0]
|
|
885
888
|
offset += 4
|
|
886
|
-
#
|
|
887
|
-
# storage_flags._formula_error_id = unpack("<i", buffer[offset : offset + 4])[0]
|
|
888
|
-
# offset += 4
|
|
889
|
+
# Skip flag 0x800 (formula_error_id)
|
|
889
890
|
if flags & 0x1000:
|
|
890
891
|
storage_flags._suggest_id = unpack("<i", buffer[offset : offset + 4])[0]
|
|
891
892
|
offset += 4
|
|
@@ -909,12 +910,7 @@ class Cell(CellStorageFlags, Cacheable):
|
|
|
909
910
|
if flags & 0x40000:
|
|
910
911
|
storage_flags._bool_format_id = unpack("<i", buffer[offset : offset + 4])[0]
|
|
911
912
|
offset += 4
|
|
912
|
-
#
|
|
913
|
-
# cstorage_flags._omment_id = unpack("<i", buffer[offset : offset + 4])[0]
|
|
914
|
-
# offset += 4
|
|
915
|
-
# if flags & 0x100000:
|
|
916
|
-
# storage_flags._import_warning_id = unpack("<i", buffer[offset : offset + 4])[0]
|
|
917
|
-
# offset += 4
|
|
913
|
+
# Skip 0x80000 (comment_id) and 0x100000 (import_warning_id)
|
|
918
914
|
|
|
919
915
|
cell_type = buffer[1]
|
|
920
916
|
if cell_type == TSTArchives.genericCellType:
|
|
@@ -1032,7 +1028,6 @@ class Cell(CellStorageFlags, Cacheable):
|
|
|
1032
1028
|
flags = 4
|
|
1033
1029
|
length += 8
|
|
1034
1030
|
cell_type = TSTArchives.dateCellType
|
|
1035
|
-
# date_delta = self._value.astimezone() - EPOCH
|
|
1036
1031
|
if self._value.tzinfo is None:
|
|
1037
1032
|
date_delta = self._value - EPOCH
|
|
1038
1033
|
else:
|
|
@@ -1104,7 +1099,6 @@ class Cell(CellStorageFlags, Cacheable):
|
|
|
1104
1099
|
length += 4
|
|
1105
1100
|
storage += pack("<i", self._num_format_id)
|
|
1106
1101
|
storage[6] |= 1
|
|
1107
|
-
# storage[6:8] = pack("<h", 1)
|
|
1108
1102
|
if self._currency_format_id is not None:
|
|
1109
1103
|
flags |= 0x4000
|
|
1110
1104
|
length += 4
|
|
@@ -1560,8 +1554,8 @@ class MergedCell(Cell):
|
|
|
1560
1554
|
def _pack_decimal128(value: float) -> bytearray:
|
|
1561
1555
|
buffer = bytearray(16)
|
|
1562
1556
|
exp = math.floor(math.log10(math.e) * math.log(abs(value))) if value != 0.0 else 0
|
|
1563
|
-
exp +=
|
|
1564
|
-
mantissa = abs(int(value / math.pow(10, exp -
|
|
1557
|
+
exp += DECIMAL128_BIAS - 16
|
|
1558
|
+
mantissa = abs(int(value / math.pow(10, exp - DECIMAL128_BIAS)))
|
|
1565
1559
|
buffer[15] |= exp >> 7
|
|
1566
1560
|
buffer[14] |= (exp & 0x7F) << 1
|
|
1567
1561
|
i = 0
|
|
@@ -1575,7 +1569,7 @@ def _pack_decimal128(value: float) -> bytearray:
|
|
|
1575
1569
|
|
|
1576
1570
|
|
|
1577
1571
|
def _unpack_decimal128(buffer: bytearray) -> float:
|
|
1578
|
-
exp = (((buffer[15] & 0x7F) << 7) | (buffer[14] >> 1)) -
|
|
1572
|
+
exp = (((buffer[15] & 0x7F) << 7) | (buffer[14] >> 1)) - DECIMAL128_BIAS
|
|
1579
1573
|
mantissa = buffer[14] & 1
|
|
1580
1574
|
for i in range(13, -1, -1):
|
|
1581
1575
|
mantissa = mantissa * 256 + buffer[i]
|
|
@@ -1756,16 +1750,6 @@ def _decode_number_format(number_format, value, name): # noqa: PLR0912
|
|
|
1756
1750
|
int_pad = None
|
|
1757
1751
|
int_width = num_integers
|
|
1758
1752
|
|
|
1759
|
-
# value_1 = str(value).split(".")[0]
|
|
1760
|
-
# value_2 = sigfig(str(value).split(".")[1], sigfig=MAX_SIGNIFICANT_DIGITS, warn=False)
|
|
1761
|
-
# int_pad_space_as_zero = (
|
|
1762
|
-
# num_integers > 0
|
|
1763
|
-
# and num_decimals > 0
|
|
1764
|
-
# and int_pad == CellPadding.SPACE
|
|
1765
|
-
# and dec_pad is None
|
|
1766
|
-
# and num_integers > len(value_1)
|
|
1767
|
-
# and num_decimals > len(value_2)
|
|
1768
|
-
# )
|
|
1769
1753
|
int_pad_space_as_zero = False
|
|
1770
1754
|
|
|
1771
1755
|
# Formatting integer zero:
|
|
@@ -1779,16 +1763,11 @@ def _decode_number_format(number_format, value, name): # noqa: PLR0912
|
|
|
1779
1763
|
formatted_value = "".rjust(int_width)
|
|
1780
1764
|
elif integer == 0 and int_pad is None and dec_pad == CellPadding.SPACE:
|
|
1781
1765
|
formatted_value = ""
|
|
1782
|
-
elif (
|
|
1766
|
+
elif (integer == 0 and int_pad == CellPadding.SPACE and dec_pad is not None) or (
|
|
1783
1767
|
integer == 0
|
|
1784
1768
|
and int_pad == CellPadding.SPACE
|
|
1785
|
-
and dec_pad is
|
|
1786
|
-
|
|
1787
|
-
integer == 0
|
|
1788
|
-
and int_pad == CellPadding.SPACE
|
|
1789
|
-
and dec_pad is None
|
|
1790
|
-
and len(str(decimal)) > num_decimals
|
|
1791
|
-
)
|
|
1769
|
+
and dec_pad is None
|
|
1770
|
+
and len(str(decimal)) > num_decimals
|
|
1792
1771
|
):
|
|
1793
1772
|
formatted_value = "".rjust(int_width)
|
|
1794
1773
|
elif int_pad_space_as_zero or int_pad == CellPadding.ZERO:
|
|
@@ -2016,159 +1995,6 @@ def _auto_units(cell_value, number_format):
|
|
|
2016
1995
|
return unit_smallest, unit_largest
|
|
2017
1996
|
|
|
2018
1997
|
|
|
2019
|
-
# Cell reference conversion from https://github.com/jmcnamara/XlsxWriter
|
|
2020
|
-
# Copyright (c) 2013-2021, John McNamara <jmcnamara@cpan.org>
|
|
2021
|
-
range_parts = re.compile(r"(\$?)([A-Z]{1,3})(\$?)(\d+)")
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
def xl_cell_to_rowcol(cell_str: str) -> tuple:
|
|
2025
|
-
"""
|
|
2026
|
-
Convert a cell reference in A1 notation to a zero indexed row and column.
|
|
2027
|
-
|
|
2028
|
-
Parameters
|
|
2029
|
-
----------
|
|
2030
|
-
cell_str: str
|
|
2031
|
-
A1 notation cell reference
|
|
2032
|
-
|
|
2033
|
-
Returns
|
|
2034
|
-
-------
|
|
2035
|
-
row, col: int, int
|
|
2036
|
-
Cell row and column numbers (zero indexed).
|
|
2037
|
-
|
|
2038
|
-
"""
|
|
2039
|
-
if not cell_str:
|
|
2040
|
-
return 0, 0
|
|
2041
|
-
|
|
2042
|
-
match = range_parts.match(cell_str)
|
|
2043
|
-
if not match:
|
|
2044
|
-
msg = f"invalid cell reference {cell_str}"
|
|
2045
|
-
raise IndexError(msg)
|
|
2046
|
-
|
|
2047
|
-
col_str = match.group(2)
|
|
2048
|
-
row_str = match.group(4)
|
|
2049
|
-
|
|
2050
|
-
# Convert base26 column string to number.
|
|
2051
|
-
col = 0
|
|
2052
|
-
for expn, char in enumerate(reversed(col_str)):
|
|
2053
|
-
col += (ord(char) - ord("A") + 1) * (26**expn)
|
|
2054
|
-
|
|
2055
|
-
# Convert 1-index to zero-index
|
|
2056
|
-
row = int(row_str) - 1
|
|
2057
|
-
col -= 1
|
|
2058
|
-
|
|
2059
|
-
return row, col
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
def xl_range(first_row, first_col, last_row, last_col):
|
|
2063
|
-
"""
|
|
2064
|
-
Convert zero indexed row and col cell references to a A1:B1 range string.
|
|
2065
|
-
|
|
2066
|
-
Parameters
|
|
2067
|
-
----------
|
|
2068
|
-
first_row: int
|
|
2069
|
-
The first cell row.
|
|
2070
|
-
first_col: int
|
|
2071
|
-
The first cell column.
|
|
2072
|
-
last_row: int
|
|
2073
|
-
The last cell row.
|
|
2074
|
-
last_col: int
|
|
2075
|
-
The last cell column.
|
|
2076
|
-
|
|
2077
|
-
Returns
|
|
2078
|
-
-------
|
|
2079
|
-
str:
|
|
2080
|
-
A1:B1 style range string.
|
|
2081
|
-
|
|
2082
|
-
"""
|
|
2083
|
-
range1 = xl_rowcol_to_cell(first_row, first_col)
|
|
2084
|
-
range2 = xl_rowcol_to_cell(last_row, last_col)
|
|
2085
|
-
|
|
2086
|
-
if range1 == range2:
|
|
2087
|
-
return range1
|
|
2088
|
-
return range1 + ":" + range2
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False):
|
|
2092
|
-
"""
|
|
2093
|
-
Convert a zero indexed row and column cell reference to a A1 style string.
|
|
2094
|
-
|
|
2095
|
-
Parameters
|
|
2096
|
-
----------
|
|
2097
|
-
row: int
|
|
2098
|
-
The cell row.
|
|
2099
|
-
col: int
|
|
2100
|
-
The cell column.
|
|
2101
|
-
row_abs: bool
|
|
2102
|
-
If ``True``, make the row absolute.
|
|
2103
|
-
col_abs: bool
|
|
2104
|
-
If ``True``, make the column absolute.
|
|
2105
|
-
|
|
2106
|
-
Returns
|
|
2107
|
-
-------
|
|
2108
|
-
str:
|
|
2109
|
-
A1 style string.
|
|
2110
|
-
|
|
2111
|
-
"""
|
|
2112
|
-
if row < 0:
|
|
2113
|
-
msg = f"row reference {row} below zero"
|
|
2114
|
-
raise IndexError(msg)
|
|
2115
|
-
|
|
2116
|
-
if col < 0:
|
|
2117
|
-
msg = f"column reference {col} below zero"
|
|
2118
|
-
raise IndexError(msg)
|
|
2119
|
-
|
|
2120
|
-
row += 1 # Change to 1-index.
|
|
2121
|
-
row_abs = "$" if row_abs else ""
|
|
2122
|
-
|
|
2123
|
-
col_str = xl_col_to_name(col, col_abs)
|
|
2124
|
-
|
|
2125
|
-
return col_str + row_abs + str(row)
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
def xl_col_to_name(col, col_abs=False):
|
|
2129
|
-
"""
|
|
2130
|
-
Convert a zero indexed column cell reference to a string.
|
|
2131
|
-
|
|
2132
|
-
Parameters
|
|
2133
|
-
----------
|
|
2134
|
-
col: int
|
|
2135
|
-
The column number (zero indexed).
|
|
2136
|
-
col_abs: bool, default: False
|
|
2137
|
-
If ``True``, make the column absolute.
|
|
2138
|
-
|
|
2139
|
-
Returns
|
|
2140
|
-
-------
|
|
2141
|
-
str:
|
|
2142
|
-
Column in A1 notation.
|
|
2143
|
-
|
|
2144
|
-
"""
|
|
2145
|
-
if col < 0:
|
|
2146
|
-
msg = f"column reference {col} below zero"
|
|
2147
|
-
raise IndexError(msg)
|
|
2148
|
-
|
|
2149
|
-
col += 1 # Change to 1-index.
|
|
2150
|
-
col_str = ""
|
|
2151
|
-
col_abs = "$" if col_abs else ""
|
|
2152
|
-
|
|
2153
|
-
while col:
|
|
2154
|
-
# Set remainder from 1 .. 26
|
|
2155
|
-
remainder = col % 26
|
|
2156
|
-
|
|
2157
|
-
if remainder == 0:
|
|
2158
|
-
remainder = 26
|
|
2159
|
-
|
|
2160
|
-
# Convert the remainder to a character.
|
|
2161
|
-
col_letter = chr(ord("A") + remainder - 1)
|
|
2162
|
-
|
|
2163
|
-
# Accumulate the column letters, right to left.
|
|
2164
|
-
col_str = col_letter + col_str
|
|
2165
|
-
|
|
2166
|
-
# Get the next order of magnitude.
|
|
2167
|
-
col = int((col - 1) / 26)
|
|
2168
|
-
|
|
2169
|
-
return col_abs + col_str
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
1998
|
@dataclass()
|
|
2173
1999
|
class Formatting:
|
|
2174
2000
|
allow_none: bool = False
|
|
@@ -12,16 +12,16 @@ except ImportError: # pragma: nocover
|
|
|
12
12
|
from importlib_resources import files
|
|
13
13
|
|
|
14
14
|
__all__ = [
|
|
15
|
-
"CellType",
|
|
16
|
-
"PaddingType",
|
|
17
15
|
"CellPadding",
|
|
16
|
+
"CellType",
|
|
17
|
+
"ControlFormattingType",
|
|
18
18
|
"DurationStyle",
|
|
19
19
|
"DurationUnits",
|
|
20
20
|
"FormatType",
|
|
21
21
|
"FormattingType",
|
|
22
|
-
"NegativeNumberStyle",
|
|
23
22
|
"FractionAccuracy",
|
|
24
|
-
"
|
|
23
|
+
"NegativeNumberStyle",
|
|
24
|
+
"PaddingType",
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
DEFAULT_DOCUMENT = files("numbers_parser") / "data" / "empty.numbers"
|
|
@@ -51,6 +51,8 @@ DEFAULT_DATETIME_FORMAT = "dd MMM YYY HH:MM"
|
|
|
51
51
|
CHECKBOX_FALSE_VALUE = "☐"
|
|
52
52
|
CHECKBOX_TRUE_VALUE = "☑"
|
|
53
53
|
STAR_RATING_VALUE = "★"
|
|
54
|
+
OPERATOR_PRECEDENCE = {"%": 6, "^": 5, "×": 4, "*": 4, "/": 4, "÷": 4, "+": 3, "-": 3, "&": 2}
|
|
55
|
+
|
|
54
56
|
|
|
55
57
|
# Numbers limits
|
|
56
58
|
MAX_TILE_SIZE = 256
|
|
@@ -69,6 +71,7 @@ EPOCH = datetime(2001, 1, 1) # noqa: DTZ001
|
|
|
69
71
|
SECONDS_IN_HOUR = 60 * 60
|
|
70
72
|
SECONDS_IN_DAY = SECONDS_IN_HOUR * 24
|
|
71
73
|
SECONDS_IN_WEEK = SECONDS_IN_DAY * 7
|
|
74
|
+
DECIMAL128_BIAS = 0x1820
|
|
72
75
|
|
|
73
76
|
# File format enumerations
|
|
74
77
|
DECIMAL_PLACES_AUTO = 253
|
|
@@ -91,6 +94,7 @@ SUPPORTED_NUMBERS_VERSIONS = [
|
|
|
91
94
|
"14.1",
|
|
92
95
|
"14.2",
|
|
93
96
|
"14.3",
|
|
97
|
+
"14.4",
|
|
94
98
|
]
|
|
95
99
|
|
|
96
100
|
|