numbers-parser 4.17.0.post1__tar.gz → 4.18.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.
Files changed (102) hide show
  1. {numbers_parser-4.17.0.post1/src/numbers_parser.egg-info → numbers_parser-4.18.0}/PKG-INFO +35 -121
  2. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/README.md +34 -120
  3. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/pyproject.toml +1 -1
  4. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/_cat_numbers.py +15 -4
  5. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/cell.py +3 -15
  6. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/document.py +42 -30
  7. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/exceptions.py +1 -1
  8. numbers_parser-4.18.0/src/numbers_parser/experimental.py +30 -0
  9. numbers_parser-4.18.0/src/numbers_parser/formula.py +278 -0
  10. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/model.py +73 -81
  11. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/xrefs.py +0 -2
  12. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0/src/numbers_parser.egg-info}/PKG-INFO +35 -121
  13. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser.egg-info/SOURCES.txt +0 -1
  14. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_cat_numbers.py +9 -6
  15. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_categories.py +26 -1
  16. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_coverage.py +12 -7
  17. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_formatting.py +2 -2
  18. numbers_parser-4.18.0/tests/test_formulas.py +114 -0
  19. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_issues.py +1 -1
  20. numbers_parser-4.17.0.post1/src/numbers_parser/experimental.py +0 -16
  21. numbers_parser-4.17.0.post1/src/numbers_parser/formula.py +0 -689
  22. numbers_parser-4.17.0.post1/src/numbers_parser/tokenizer.py +0 -548
  23. numbers_parser-4.17.0.post1/tests/test_formulas.py +0 -404
  24. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/LICENSE.rst +0 -0
  25. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/setup.cfg +0 -0
  26. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/__init__.py +0 -0
  27. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/_csv2numbers.py +0 -0
  28. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/_unpack_numbers.py +0 -0
  29. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/bullets.py +0 -0
  30. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/constants.py +0 -0
  31. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/containers.py +0 -0
  32. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/currencies.py +0 -0
  33. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/data/empty.numbers +0 -0
  34. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TNArchives_pb2.py +0 -0
  35. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TNArchives_sos_pb2.py +0 -0
  36. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TNCommandArchives_pb2.py +0 -0
  37. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TNCommandArchives_sos_pb2.py +0 -0
  38. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSAArchives_pb2.py +0 -0
  39. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSAArchives_sos_pb2.py +0 -0
  40. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSACommandArchives_sos_pb2.py +0 -0
  41. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCEArchives_pb2.py +0 -0
  42. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCH3DArchives_pb2.py +0 -0
  43. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCHArchives_Common_pb2.py +0 -0
  44. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCHArchives_GEN_pb2.py +0 -0
  45. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCHArchives_pb2.py +0 -0
  46. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCHArchives_sos_pb2.py +0 -0
  47. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCHCommandArchives_pb2.py +0 -0
  48. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCHPreUFFArchives_pb2.py +0 -0
  49. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCKArchives_pb2.py +0 -0
  50. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSCKArchives_sos_pb2.py +0 -0
  51. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSDArchives_pb2.py +0 -0
  52. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSDArchives_sos_pb2.py +0 -0
  53. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSDCommandArchives_pb2.py +0 -0
  54. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSKArchives_pb2.py +0 -0
  55. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSPArchiveMessages_pb2.py +0 -0
  56. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSPDatabaseMessages_pb2.py +0 -0
  57. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSPMessages_pb2.py +0 -0
  58. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSSArchives_pb2.py +0 -0
  59. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSSArchives_sos_pb2.py +0 -0
  60. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSTArchives_pb2.py +0 -0
  61. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSTArchives_sos_pb2.py +0 -0
  62. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSTCommandArchives_pb2.py +0 -0
  63. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSTStylePropertyArchiving_pb2.py +0 -0
  64. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSWPArchives_pb2.py +0 -0
  65. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSWPArchives_sos_pb2.py +0 -0
  66. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/TSWPCommandArchives_pb2.py +0 -0
  67. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/__init__.py +0 -0
  68. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/fontmap.py +0 -0
  69. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/functionmap.py +0 -0
  70. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/generated/mapping.py +0 -0
  71. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/iwafile.py +0 -0
  72. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/iwork.py +0 -0
  73. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/numbers_cache.py +0 -0
  74. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/numbers_uuid.py +0 -0
  75. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser/roman.py +0 -0
  76. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser.egg-info/dependency_links.txt +0 -0
  77. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser.egg-info/entry_points.txt +0 -0
  78. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser.egg-info/requires.txt +0 -0
  79. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/src/numbers_parser.egg-info/top_level.txt +0 -0
  80. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_all_formulas.py +0 -0
  81. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_api_change.py +0 -0
  82. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_borders.py +0 -0
  83. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_bullets.py +0 -0
  84. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_create_cells.py +0 -0
  85. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_csv2numbers.py +0 -0
  86. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_currency.py +0 -0
  87. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_folder.py +0 -0
  88. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_large.py +0 -0
  89. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_memory_leaks.py +0 -0
  90. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_merges.py +0 -0
  91. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_package.py +0 -0
  92. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_properties.py +0 -0
  93. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_roman.py +0 -0
  94. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_save.py +0 -0
  95. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_slices.py +0 -0
  96. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_styles.py +0 -0
  97. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_table_size.py +0 -0
  98. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_tables.py +0 -0
  99. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_unpack_numbers.py +0 -0
  100. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_unsupported.py +0 -0
  101. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_uuids.py +0 -0
  102. {numbers_parser-4.17.0.post1 → numbers_parser-4.18.0}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numbers-parser
3
- Version: 4.17.0.post1
3
+ Version: 4.18.0
4
4
  Summary: Read and write Apple Numbers spreadsheets
5
5
  Author-email: Jon Connell <python@figsandfudge.com>
6
6
  License-Expression: MIT
@@ -26,20 +26,15 @@ Dynamic: license-file
26
26
 
27
27
  [![Test Status](https://github.com/masaccio/numbers-parser/actions/workflows/run-all-tests.yml/badge.svg)](https://github.com/masaccio/numbers-parser/actions/workflows/run-all-tests.yml)[![Security Checks](https://github.com/masaccio/numbers-parser/actions/workflows/codeql.yml/badge.svg)](https://github.com/masaccio/numbers-parser/actions/workflows/codeql.yml)[![Code Coverage](https://codecov.io/gh/masaccio/numbers-parser/branch/main/graph/badge.svg?token=EKIUFGT05E)](https://codecov.io/gh/masaccio/numbers-parser)[![PyPI Version](https://badge.fury.io/py/numbers-parser.svg)](https://badge.fury.io/py/numbers-parser)
28
28
 
29
- `numbers-parser` is a Python module for parsing [Apple Numbers](https://www.apple.com/numbers/)`.numbers` files. It supports Numbers files
30
- generated by Numbers version 10.3, and up with the latest tested version being 14.1
31
- (current as of June 2024).
29
+ `numbers-parser` is a Python module for reading and editing [Apple Numbers](https://www.apple.com/numbers/)`.numbers` files. It supports Numbers files generated by Numbers versions 3.x and later. It is tested against Numbers documents from 10.0 through to 14.4 (the last version of Numbers) and Numbers Creator Studio up to version 15.1, which the most current as of February 2026.
32
30
 
33
- It supports and is tested against Python versions from 3.10 onwards. It is not compatible
34
- with earlier versions of Python.
31
+ It supports and is tested against Python versions from 3.10 onwards. It is not compatible with earlier versions of Python.
35
32
 
36
33
  ## Installation
37
34
 
38
35
  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.
39
36
 
40
- The most straightforward way to install the binary dependencies is to use
41
- [Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described
42
- 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:
37
+ The most straightforward way to install the binary dependencies is to use [Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described 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:
43
38
 
44
39
  ```bash
45
40
  brew install snappy python3 pipx
@@ -52,9 +47,7 @@ For Linux (your package manager may be different):
52
47
  sudo apt-get -y install libsnappy-dev
53
48
  ```
54
49
 
55
- On Windows, you will need to either arrange for snappy to be found for VSC++ or you can install python
56
- [pre-compiled binary libraries](https://github.com/cgohlke/win_arm64-wheels/) which are only available
57
- for Windows on Arm. There appear to be no x86 pre-compiled packages for Windows.
50
+ On Windows, you will need to either arrange for snappy to be found for VSC++ or you can install python [pre-compiled binary libraries](https://github.com/cgohlke/win_arm64-wheels/) which are only available for Windows on Arm. There appear to be no x86 pre-compiled packages for Windows.
58
51
 
59
52
  ```text
60
53
  pip install python_snappy-0.6.1-cp312-cp312-win_arm64.whl
@@ -118,12 +111,7 @@ python datatype. Available cell types are:
118
111
  | ErrorCell | `None` | |
119
112
  | MergedCell | `None` | See [Merged cells](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.MergedCell) |
120
113
 
121
- Cell references can be either zero-offset row/column integers or an
122
- Excel/Numbers A1 notation. Where cell values are not `None` the
123
- property `formatted_value` returns the cell value as a `str` as
124
- displayed in Numbers. Cells that have no values in a table are
125
- represented as `EmptyCell` and cells containing evaluation errors of
126
- any kind `ErrorCell`.
114
+ Cell references can be either zero-offset row/column integers or an Excel/Numbers A1 notation. Where cell values are not `None` the property `formatted_value` returns the cell value as a `str` as displayed in Numbers. Cells that have no values in a table are represented as `EmptyCell` and cells containing evaluation errors of any kind `ErrorCell`.
127
115
 
128
116
  ```python
129
117
  >>> table.cell(1,0)
@@ -140,10 +128,7 @@ any kind `ErrorCell`.
140
128
 
141
129
  ### Pandas Support
142
130
 
143
- Since the return value of `rows()` is a list of lists, you can pass
144
- this directly to pandas. Assuming you have a Numbers table with a single
145
- header which contains the names of the pandas series you want to create
146
- you can construct a pandas dataframe using:
131
+ Since the return value of `rows()` is a list of lists, you can pass this directly to pandas. Assuming you have a Numbers table with a single header which contains the names of the pandas series you want to create you can construct a pandas dataframe using:
147
132
 
148
133
  ```python
149
134
  import pandas as pd
@@ -157,14 +142,9 @@ df = pd.DataFrame(data[1:], columns=data[0])
157
142
 
158
143
  ### Writing Numbers Documents
159
144
 
160
- Whilst support for writing numbers files has been stable since version
161
- 3.4.0, you are highly recommended not to overwrite working Numbers files
162
- and instead save data to a new file.
145
+ Whilst support for writing numbers files has been stable since version 3.4.0, you are highly recommended not to overwrite working Numbers files and instead save data to a new file.
163
146
 
164
- Cell values are written using
165
- [Table.write()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.write) and
166
- `numbers-parser` will automatically create empty rows and columns
167
- for any cell references that are out of range of the current table.
147
+ Cell values are written using [Table.write()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.write) and `numbers-parser` will automatically create empty rows and columns for any cell references that are out of range of the current table.
168
148
 
169
149
  ```python
170
150
  doc = Document("write.numbers")
@@ -176,9 +156,7 @@ table.write("B7", datetime(2020, 12, 25))
176
156
  doc.save("new-sheet.numbers")
177
157
  ```
178
158
 
179
- Additional tables and worksheets can be added to a `Document` before saving using
180
- [Document.add_sheet()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_sheet) and
181
- [Sheet.add_table()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Sheet.add_table) respectively:
159
+ Additional tables and worksheets can be added to a `Document` before saving using [Document.add_sheet()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_sheet) and [Sheet.add_table()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Sheet.add_table) respectively:
182
160
 
183
161
  ```python
184
162
  doc = Document()
@@ -204,19 +182,11 @@ styles. The following styles are supported:
204
182
  - cell background images
205
183
  - cell indents (first line, left, right, and text inset)
206
184
 
207
- Numbers conflates style attributes that can be stored in paragraph
208
- styles (the style menu in the text panel) with the settings that are
209
- available on the Style tab of the Text panel. Some attributes in Numbers
210
- are not applied to new cells when a style is applied.
185
+ Numbers conflates style attributes that can be stored in paragraph styles (the style menu in the text panel) with the settings that are available on the Style tab of the Text panel. Some attributes in Numbers are not applied to new cells when a style is applied.
211
186
 
212
- To keep the API simple, `numbers-parser` packs all styling into a single
213
- [Style](https://masaccio.github.io/numbers-parser/api/style.html) object. When a document is saved, the attributes
214
- not stored in a paragraph style are applied to each cell that includes it.
187
+ To keep the API simple, `numbers-parser` packs all styling into a single [Style](https://masaccio.github.io/numbers-parser/api/style.html) object. When a document is saved, the attributes not stored in a paragraph style are applied to each cell that includes it.
215
188
 
216
- Styles are read from cells using the
217
- [Cell.style](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.style) property and you can
218
- add new styles with
219
- [Document.add_style](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_style).
189
+ Styles are read from cells using the [Cell.style](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.style) property and you can add new styles with [Document.add_style](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_style).
220
190
 
221
191
  ```python
222
192
  red_text = doc.add_style(
@@ -234,18 +204,11 @@ table.set_cell_style("C2", red_text)
234
204
 
235
205
  ### Cell Data Formatting
236
206
 
237
- Numbers has two different cell formatting types: data formats and custom
238
- formats.
207
+ Numbers has two different cell formatting types: data formats and custom formats.
239
208
 
240
- Data formats are presented in Numbers in the Cell tab of the Format pane
241
- and are applied to individual cells. Like Numbers, `numbers-parsers`
242
- caches formatting information that is identical across multiple cells.
243
- You do not need to take any action for this to happen; this is handled
244
- internally by the package. Changing a data format for cell has no impact
245
- on any other cells.
209
+ Data formats are presented in Numbers in the Cell tab of the Format pane and are applied to individual cells. Like Numbers, `numbers-parsers` caches formatting information that is identical across multiple cells. You do not need to take any action for this to happen; this is handled internally by the package. Changing a data format for cell has no impact on any other cells.
246
210
 
247
- Cell formats are changed using
248
- [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
211
+ Cell formats are changed using [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
249
212
 
250
213
  ```python
251
214
  table.set_cell_formatting(
@@ -262,13 +225,7 @@ table.set_cell_formatting(
262
225
  )
263
226
  ```
264
227
 
265
- Custom formats are shared across a Document and can be applied to
266
- multiple cells in multiple tables. Editing a custom format changes the
267
- appearance of data in all cells that share that format. You must first
268
- add a custom format to the document using
269
- [Document.add_custom_format()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_custom_format)
270
- before assigning it to cells using
271
- [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
228
+ Custom formats are shared across a Document and can be applied to multiple cells in multiple tables. Editing a custom format changes the appearance of data in all cells that share that format. You must first add a custom format to the document using [Document.add_custom_format()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_custom_format) before assigning it to cells using [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
272
229
 
273
230
  ```python
274
231
  long_date = doc.add_custom_format(
@@ -279,33 +236,17 @@ long_date = doc.add_custom_format(
279
236
  table.set_cell_formatting("C1", "custom", format=long_date)
280
237
  ```
281
238
 
282
- A limited number of currencies are formatted using symbolic notation
283
- rather than an ISO code. These are defined in
284
- `numbers_parser.currencies` and match the ones chosen by Numbers. For
285
- example, US dollars are referred to as `US$` whereas Euros and British
286
- Pounds are referred to using their symbols of `€` and `£`
287
- respectively.
239
+ A limited number of currencies are formatted using symbolic notation rather than an ISO code. These are defined in `numbers_parser.currencies` and match the ones chosen by Numbers. For example, US dollars are referred to as `US$` whereas Euros and British Pounds are referred to using their symbols of `€` and `£` respectively.
288
240
 
289
241
  ### Borders
290
242
 
291
- `numbers-parser` supports reading and writing cell borders, though the
292
- interface for each differs. Individual cells can have each of their four
293
- borders tested, but when drawing new borders, these are set for the
294
- table to allow for drawing borders across multiple cells. Setting the
295
- border of merged cells is not possible unless the edge of the cells is
296
- at the end of the merged region.
243
+ `numbers-parser` supports reading and writing cell borders, though the interface for each differs. Individual cells can have each of their four borders tested, but when drawing new borders, these are set for the table to allow for drawing borders across multiple cells. Setting the border of merged cells is not possible unless the edge of the cells is at the end of the merged region.
297
244
 
298
- Borders are represented using the [Border](https://masaccio.github.io/numbers-parser/api/border.html) class
299
- that can be initialized with line width, color and line style. The
300
- current state of a cell border is read using the
301
- [Cell.border](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.border) property
302
- and [Table.set_cell_border()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_border)
303
- sets the border for a cell edge or a range of cells.
245
+ Borders are represented using the [Border](https://masaccio.github.io/numbers-parser/api/border.html) class that can be initialized with line width, color and line style. The current state of a cell border is read using the [Cell.border](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.border) property and [Table.set_cell_border()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_border) sets the border for a cell edge or a range of cells.
304
246
 
305
247
  ## API
306
248
 
307
- For more examples and details of all available classes and methods,
308
- see the [full API docs](https://masaccio.github.io/numbers-parser/).
249
+ For more examples and details of all available classes and methods, see the [full API docs](https://masaccio.github.io/numbers-parser/).
309
250
 
310
251
  ## Command-line scripts
311
252
 
@@ -318,9 +259,7 @@ a number of command-line scripts are installed:
318
259
 
319
260
  ### cat-numbers
320
261
 
321
- This script dumps Numbers spreadsheets into Excel-compatible CSV
322
- format, iterating through all the spreadsheets passed on the
323
- command-line.
262
+ This script dumps Numbers spreadsheets into Excel-compatible CSV format, iterating through all the spreadsheets passed on the command-line.
324
263
 
325
264
  ```text
326
265
  usage: cat-numbers [-h] [-T | -S | -b] [-V] [--formulas] [--formatting]
@@ -349,25 +288,17 @@ options:
349
288
  --debug Enable debug logging
350
289
  ```
351
290
 
352
- Note: `--formatting` will return different capitalization for 12-hour
353
- times due to differences between Numbers’ representation of these dates
354
- and `datetime.strftime`. Numbers in English locales displays 12-hour
355
- times with ‘am’ and ‘pm’, but `datetime.strftime` on macOS at least
356
- cannot return lower-case versions of AM/PM.
291
+ Note: `--formatting` will return different capitalization for 12-hour times due to differences between Numbers’ representation of these dates and `datetime.strftime`. Numbers in English locales displays 12-hour times with ‘am’ and ‘pm’, but `datetime.strftime` on macOS at least cannot return lower-case versions of AM/PM.
357
292
 
358
293
  ### csv2numbers
359
294
 
360
- This script converts Excel-compatible CSV files into Numbers documents. Output files
361
- can optionally be provided, but is none are provided, the output is created by replacing
362
- the input’s files suffix with .numbers. For example:
295
+ This script converts Excel-compatible CSV files into Numbers documents. Output files can optionally be provided, but is none are provided, the output is created by replacing the input’s files suffix with .numbers. For example:
363
296
 
364
297
  ```text
365
298
  csv2numbers file1.csv file2.csv -o file1.numbers file2.numbers
366
299
  ```
367
300
 
368
- Columns of data can have a number of transformations applied to them. The primary use-
369
- case intended for `csv2numbers` is converting banking exports to well-formatted
370
- spreadsheets.
301
+ Columns of data can have a number of transformations applied to them. The primary use- case intended for `csv2numbers` is converting banking exports to well-formatted spreadsheets.
371
302
 
372
303
  ```text
373
304
  usage: csv2numbers [-h] [-V] [--whitespace] [--reverse] [--no-header]
@@ -455,39 +386,22 @@ csv2numbers --transform='Category=LOOKUP:Transaction;mapping.numbers' file1.csv
455
386
 
456
387
  Current known limitations of `numbers-parser` which may be implemented in the future are:
457
388
 
458
- - Table styles that allow new tables to adopt a style across the whole
459
- table are not suppported
389
+ - Table styles that allow new tables to adopt a style across the whole table are not supported
460
390
  - Creating cells of type `BulletedTextCell` is not supported
461
- - New tables are inserted with a fixed offset below the last table in a
462
- worksheet which does not take into account title or caption size
463
- - Captions can be created and edited as of numbers-parser version 4.12, but cannot
464
- be styled. New captions adopt the first caption style available in the current
465
- document
391
+ - New tables are inserted with a fixed offset below the last table in a worksheet which does not take into account title or caption size
392
+ - Captions can be created and edited as of numbers-parser version 4.12, but cannot be styled. New captions adopt the first caption style available in the current document
466
393
  - Formulas cannot be written to a document
467
- - Pivot tables are unsupported and saving a document with a pivot table issues
468
- a UnsupportedWarning (see [issue 73](https://github.com/masaccio/numbers-parser/issues/73) for details).
394
+ - Pivot tables are unsupported and saving a document with a pivot table issues a UnsupportedWarning (see [issue 73](https://github.com/masaccio/numbers-parser/issues/73) for details).
395
+ - Tables which have been saved grouped cannot be safely edited as references to written cells refer to the ungrouped cell rows rather than the row number in the groups.
469
396
 
470
397
  The following limitations are expected to always remain:
471
398
 
472
- - New sheets insert tables with formats copied from the first table in
473
- the previous sheet rather than default table formats
474
- - Due to a limitation in Python’s
475
- [ZipFile](https://docs.python.org/3/library/zipfile.html), Python
476
- versions older than 3.11 do not support image filenames with UTF-8 characters
477
- [Cell.add_style.bg_image()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Style) returns
478
- `None` for such files and issues a `RuntimeWarning`
479
- (see [issue 69](https://github.com/masaccio/numbers-parser/issues/69) for details).
480
- - Password-encrypted documents cannot be opened. You must first re-save without
481
- a password to read (see [issue 88](https://github.com/masaccio/numbers-parser/issues/88) for details).
482
- A UnsupportedError exception is raised when such documents are opened.
483
- - Due to changes in the format of Numbers documents, decoding of category groups
484
- (introduced in `numbers-parser` version 4.16) is supported only for documents
485
- created by Numbers 12.0 and later. No warnings are issued for earlier
399
+ - New sheets insert tables with formats copied from the first table in the previous sheet rather than default table formats
400
+ - Due to a limitation in Python’s [ZipFile](https://docs.python.org/3/library/zipfile.html), Python versions older than 3.11 do not support image filenames with UTF-8 characters [Cell.add_style.bg_image()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Style) returns `None` for such files and issues a `RuntimeWarning` (see [issue 69](https://github.com/masaccio/numbers-parser/issues/69) for details).
401
+ - Password-encrypted documents cannot be opened. You must first re-save without a password to read (see [issue 88](https://github.com/masaccio/numbers-parser/issues/88) for details). A UnsupportedError exception is raised when such documents are opened.
402
+ - Due to changes in the format of Numbers documents, decoding of category groups (introduced in `numbers-parser` version 4.16) is supported only for documents created by Numbers 12.0 and later. No warnings are issued for earlier
486
403
  Numbers documents.
487
- - Only standard macOS fonts are not supported. If a document includes a non-standard
488
- font, numbers-parser will issue a UnsupportedWarning and default styles to
489
- Helvetica Neue. Reading font names from the system would add additional system-specific
490
- dependencies to the package and so this is not planned to changed.
404
+ - Only standard macOS fonts are not supported. If a document includes a non-standard font, numbers-parser will issue a UnsupportedWarning and default styles to Helvetica Neue. Reading font names from the system would add additional system-specific dependencies to the package and so this is not planned to changed.
491
405
 
492
406
  ## License
493
407
 
@@ -2,20 +2,15 @@
2
2
 
3
3
  [![Test Status](https://github.com/masaccio/numbers-parser/actions/workflows/run-all-tests.yml/badge.svg)](https://github.com/masaccio/numbers-parser/actions/workflows/run-all-tests.yml)[![Security Checks](https://github.com/masaccio/numbers-parser/actions/workflows/codeql.yml/badge.svg)](https://github.com/masaccio/numbers-parser/actions/workflows/codeql.yml)[![Code Coverage](https://codecov.io/gh/masaccio/numbers-parser/branch/main/graph/badge.svg?token=EKIUFGT05E)](https://codecov.io/gh/masaccio/numbers-parser)[![PyPI Version](https://badge.fury.io/py/numbers-parser.svg)](https://badge.fury.io/py/numbers-parser)
4
4
 
5
- `numbers-parser` is a Python module for parsing [Apple Numbers](https://www.apple.com/numbers/)`.numbers` files. It supports Numbers files
6
- generated by Numbers version 10.3, and up with the latest tested version being 14.1
7
- (current as of June 2024).
5
+ `numbers-parser` is a Python module for reading and editing [Apple Numbers](https://www.apple.com/numbers/)`.numbers` files. It supports Numbers files generated by Numbers versions 3.x and later. It is tested against Numbers documents from 10.0 through to 14.4 (the last version of Numbers) and Numbers Creator Studio up to version 15.1, which the most current as of February 2026.
8
6
 
9
- It supports and is tested against Python versions from 3.10 onwards. It is not compatible
10
- with earlier versions of Python.
7
+ It supports and is tested against Python versions from 3.10 onwards. It is not compatible with earlier versions of Python.
11
8
 
12
9
  ## Installation
13
10
 
14
11
  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.
15
12
 
16
- The most straightforward way to install the binary dependencies is to use
17
- [Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described
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:
13
+ The most straightforward way to install the binary dependencies is to use [Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described 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:
19
14
 
20
15
  ```bash
21
16
  brew install snappy python3 pipx
@@ -28,9 +23,7 @@ For Linux (your package manager may be different):
28
23
  sudo apt-get -y install libsnappy-dev
29
24
  ```
30
25
 
31
- On Windows, you will need to either arrange for snappy to be found for VSC++ or you can install python
32
- [pre-compiled binary libraries](https://github.com/cgohlke/win_arm64-wheels/) which are only available
33
- for Windows on Arm. There appear to be no x86 pre-compiled packages for Windows.
26
+ On Windows, you will need to either arrange for snappy to be found for VSC++ or you can install python [pre-compiled binary libraries](https://github.com/cgohlke/win_arm64-wheels/) which are only available for Windows on Arm. There appear to be no x86 pre-compiled packages for Windows.
34
27
 
35
28
  ```text
36
29
  pip install python_snappy-0.6.1-cp312-cp312-win_arm64.whl
@@ -94,12 +87,7 @@ python datatype. Available cell types are:
94
87
  | ErrorCell | `None` | |
95
88
  | MergedCell | `None` | See [Merged cells](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.MergedCell) |
96
89
 
97
- Cell references can be either zero-offset row/column integers or an
98
- Excel/Numbers A1 notation. Where cell values are not `None` the
99
- property `formatted_value` returns the cell value as a `str` as
100
- displayed in Numbers. Cells that have no values in a table are
101
- represented as `EmptyCell` and cells containing evaluation errors of
102
- any kind `ErrorCell`.
90
+ Cell references can be either zero-offset row/column integers or an Excel/Numbers A1 notation. Where cell values are not `None` the property `formatted_value` returns the cell value as a `str` as displayed in Numbers. Cells that have no values in a table are represented as `EmptyCell` and cells containing evaluation errors of any kind `ErrorCell`.
103
91
 
104
92
  ```python
105
93
  >>> table.cell(1,0)
@@ -116,10 +104,7 @@ any kind `ErrorCell`.
116
104
 
117
105
  ### Pandas Support
118
106
 
119
- Since the return value of `rows()` is a list of lists, you can pass
120
- this directly to pandas. Assuming you have a Numbers table with a single
121
- header which contains the names of the pandas series you want to create
122
- you can construct a pandas dataframe using:
107
+ Since the return value of `rows()` is a list of lists, you can pass this directly to pandas. Assuming you have a Numbers table with a single header which contains the names of the pandas series you want to create you can construct a pandas dataframe using:
123
108
 
124
109
  ```python
125
110
  import pandas as pd
@@ -133,14 +118,9 @@ df = pd.DataFrame(data[1:], columns=data[0])
133
118
 
134
119
  ### Writing Numbers Documents
135
120
 
136
- Whilst support for writing numbers files has been stable since version
137
- 3.4.0, you are highly recommended not to overwrite working Numbers files
138
- and instead save data to a new file.
121
+ Whilst support for writing numbers files has been stable since version 3.4.0, you are highly recommended not to overwrite working Numbers files and instead save data to a new file.
139
122
 
140
- Cell values are written using
141
- [Table.write()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.write) and
142
- `numbers-parser` will automatically create empty rows and columns
143
- for any cell references that are out of range of the current table.
123
+ Cell values are written using [Table.write()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.write) and `numbers-parser` will automatically create empty rows and columns for any cell references that are out of range of the current table.
144
124
 
145
125
  ```python
146
126
  doc = Document("write.numbers")
@@ -152,9 +132,7 @@ table.write("B7", datetime(2020, 12, 25))
152
132
  doc.save("new-sheet.numbers")
153
133
  ```
154
134
 
155
- Additional tables and worksheets can be added to a `Document` before saving using
156
- [Document.add_sheet()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_sheet) and
157
- [Sheet.add_table()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Sheet.add_table) respectively:
135
+ Additional tables and worksheets can be added to a `Document` before saving using [Document.add_sheet()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_sheet) and [Sheet.add_table()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Sheet.add_table) respectively:
158
136
 
159
137
  ```python
160
138
  doc = Document()
@@ -180,19 +158,11 @@ styles. The following styles are supported:
180
158
  - cell background images
181
159
  - cell indents (first line, left, right, and text inset)
182
160
 
183
- Numbers conflates style attributes that can be stored in paragraph
184
- styles (the style menu in the text panel) with the settings that are
185
- available on the Style tab of the Text panel. Some attributes in Numbers
186
- are not applied to new cells when a style is applied.
161
+ Numbers conflates style attributes that can be stored in paragraph styles (the style menu in the text panel) with the settings that are available on the Style tab of the Text panel. Some attributes in Numbers are not applied to new cells when a style is applied.
187
162
 
188
- To keep the API simple, `numbers-parser` packs all styling into a single
189
- [Style](https://masaccio.github.io/numbers-parser/api/style.html) object. When a document is saved, the attributes
190
- not stored in a paragraph style are applied to each cell that includes it.
163
+ To keep the API simple, `numbers-parser` packs all styling into a single [Style](https://masaccio.github.io/numbers-parser/api/style.html) object. When a document is saved, the attributes not stored in a paragraph style are applied to each cell that includes it.
191
164
 
192
- Styles are read from cells using the
193
- [Cell.style](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.style) property and you can
194
- add new styles with
195
- [Document.add_style](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_style).
165
+ Styles are read from cells using the [Cell.style](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.style) property and you can add new styles with [Document.add_style](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_style).
196
166
 
197
167
  ```python
198
168
  red_text = doc.add_style(
@@ -210,18 +180,11 @@ table.set_cell_style("C2", red_text)
210
180
 
211
181
  ### Cell Data Formatting
212
182
 
213
- Numbers has two different cell formatting types: data formats and custom
214
- formats.
183
+ Numbers has two different cell formatting types: data formats and custom formats.
215
184
 
216
- Data formats are presented in Numbers in the Cell tab of the Format pane
217
- and are applied to individual cells. Like Numbers, `numbers-parsers`
218
- caches formatting information that is identical across multiple cells.
219
- You do not need to take any action for this to happen; this is handled
220
- internally by the package. Changing a data format for cell has no impact
221
- on any other cells.
185
+ Data formats are presented in Numbers in the Cell tab of the Format pane and are applied to individual cells. Like Numbers, `numbers-parsers` caches formatting information that is identical across multiple cells. You do not need to take any action for this to happen; this is handled internally by the package. Changing a data format for cell has no impact on any other cells.
222
186
 
223
- Cell formats are changed using
224
- [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
187
+ Cell formats are changed using [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
225
188
 
226
189
  ```python
227
190
  table.set_cell_formatting(
@@ -238,13 +201,7 @@ table.set_cell_formatting(
238
201
  )
239
202
  ```
240
203
 
241
- Custom formats are shared across a Document and can be applied to
242
- multiple cells in multiple tables. Editing a custom format changes the
243
- appearance of data in all cells that share that format. You must first
244
- add a custom format to the document using
245
- [Document.add_custom_format()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_custom_format)
246
- before assigning it to cells using
247
- [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
204
+ Custom formats are shared across a Document and can be applied to multiple cells in multiple tables. Editing a custom format changes the appearance of data in all cells that share that format. You must first add a custom format to the document using [Document.add_custom_format()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_custom_format) before assigning it to cells using [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
248
205
 
249
206
  ```python
250
207
  long_date = doc.add_custom_format(
@@ -255,33 +212,17 @@ long_date = doc.add_custom_format(
255
212
  table.set_cell_formatting("C1", "custom", format=long_date)
256
213
  ```
257
214
 
258
- A limited number of currencies are formatted using symbolic notation
259
- rather than an ISO code. These are defined in
260
- `numbers_parser.currencies` and match the ones chosen by Numbers. For
261
- example, US dollars are referred to as `US$` whereas Euros and British
262
- Pounds are referred to using their symbols of `€` and `£`
263
- respectively.
215
+ A limited number of currencies are formatted using symbolic notation rather than an ISO code. These are defined in `numbers_parser.currencies` and match the ones chosen by Numbers. For example, US dollars are referred to as `US$` whereas Euros and British Pounds are referred to using their symbols of `€` and `£` respectively.
264
216
 
265
217
  ### Borders
266
218
 
267
- `numbers-parser` supports reading and writing cell borders, though the
268
- interface for each differs. Individual cells can have each of their four
269
- borders tested, but when drawing new borders, these are set for the
270
- table to allow for drawing borders across multiple cells. Setting the
271
- border of merged cells is not possible unless the edge of the cells is
272
- at the end of the merged region.
219
+ `numbers-parser` supports reading and writing cell borders, though the interface for each differs. Individual cells can have each of their four borders tested, but when drawing new borders, these are set for the table to allow for drawing borders across multiple cells. Setting the border of merged cells is not possible unless the edge of the cells is at the end of the merged region.
273
220
 
274
- Borders are represented using the [Border](https://masaccio.github.io/numbers-parser/api/border.html) class
275
- that can be initialized with line width, color and line style. The
276
- current state of a cell border is read using the
277
- [Cell.border](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.border) property
278
- and [Table.set_cell_border()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_border)
279
- sets the border for a cell edge or a range of cells.
221
+ Borders are represented using the [Border](https://masaccio.github.io/numbers-parser/api/border.html) class that can be initialized with line width, color and line style. The current state of a cell border is read using the [Cell.border](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.border) property and [Table.set_cell_border()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_border) sets the border for a cell edge or a range of cells.
280
222
 
281
223
  ## API
282
224
 
283
- For more examples and details of all available classes and methods,
284
- see the [full API docs](https://masaccio.github.io/numbers-parser/).
225
+ For more examples and details of all available classes and methods, see the [full API docs](https://masaccio.github.io/numbers-parser/).
285
226
 
286
227
  ## Command-line scripts
287
228
 
@@ -294,9 +235,7 @@ a number of command-line scripts are installed:
294
235
 
295
236
  ### cat-numbers
296
237
 
297
- This script dumps Numbers spreadsheets into Excel-compatible CSV
298
- format, iterating through all the spreadsheets passed on the
299
- command-line.
238
+ This script dumps Numbers spreadsheets into Excel-compatible CSV format, iterating through all the spreadsheets passed on the command-line.
300
239
 
301
240
  ```text
302
241
  usage: cat-numbers [-h] [-T | -S | -b] [-V] [--formulas] [--formatting]
@@ -325,25 +264,17 @@ options:
325
264
  --debug Enable debug logging
326
265
  ```
327
266
 
328
- Note: `--formatting` will return different capitalization for 12-hour
329
- times due to differences between Numbers’ representation of these dates
330
- and `datetime.strftime`. Numbers in English locales displays 12-hour
331
- times with ‘am’ and ‘pm’, but `datetime.strftime` on macOS at least
332
- cannot return lower-case versions of AM/PM.
267
+ Note: `--formatting` will return different capitalization for 12-hour times due to differences between Numbers’ representation of these dates and `datetime.strftime`. Numbers in English locales displays 12-hour times with ‘am’ and ‘pm’, but `datetime.strftime` on macOS at least cannot return lower-case versions of AM/PM.
333
268
 
334
269
  ### csv2numbers
335
270
 
336
- This script converts Excel-compatible CSV files into Numbers documents. Output files
337
- can optionally be provided, but is none are provided, the output is created by replacing
338
- the input’s files suffix with .numbers. For example:
271
+ This script converts Excel-compatible CSV files into Numbers documents. Output files can optionally be provided, but is none are provided, the output is created by replacing the input’s files suffix with .numbers. For example:
339
272
 
340
273
  ```text
341
274
  csv2numbers file1.csv file2.csv -o file1.numbers file2.numbers
342
275
  ```
343
276
 
344
- Columns of data can have a number of transformations applied to them. The primary use-
345
- case intended for `csv2numbers` is converting banking exports to well-formatted
346
- spreadsheets.
277
+ Columns of data can have a number of transformations applied to them. The primary use- case intended for `csv2numbers` is converting banking exports to well-formatted spreadsheets.
347
278
 
348
279
  ```text
349
280
  usage: csv2numbers [-h] [-V] [--whitespace] [--reverse] [--no-header]
@@ -431,39 +362,22 @@ csv2numbers --transform='Category=LOOKUP:Transaction;mapping.numbers' file1.csv
431
362
 
432
363
  Current known limitations of `numbers-parser` which may be implemented in the future are:
433
364
 
434
- - Table styles that allow new tables to adopt a style across the whole
435
- table are not suppported
365
+ - Table styles that allow new tables to adopt a style across the whole table are not supported
436
366
  - Creating cells of type `BulletedTextCell` is not supported
437
- - New tables are inserted with a fixed offset below the last table in a
438
- worksheet which does not take into account title or caption size
439
- - Captions can be created and edited as of numbers-parser version 4.12, but cannot
440
- be styled. New captions adopt the first caption style available in the current
441
- document
367
+ - New tables are inserted with a fixed offset below the last table in a worksheet which does not take into account title or caption size
368
+ - Captions can be created and edited as of numbers-parser version 4.12, but cannot be styled. New captions adopt the first caption style available in the current document
442
369
  - Formulas cannot be written to a document
443
- - Pivot tables are unsupported and saving a document with a pivot table issues
444
- a UnsupportedWarning (see [issue 73](https://github.com/masaccio/numbers-parser/issues/73) for details).
370
+ - Pivot tables are unsupported and saving a document with a pivot table issues a UnsupportedWarning (see [issue 73](https://github.com/masaccio/numbers-parser/issues/73) for details).
371
+ - Tables which have been saved grouped cannot be safely edited as references to written cells refer to the ungrouped cell rows rather than the row number in the groups.
445
372
 
446
373
  The following limitations are expected to always remain:
447
374
 
448
- - New sheets insert tables with formats copied from the first table in
449
- the previous sheet rather than default table formats
450
- - Due to a limitation in Python’s
451
- [ZipFile](https://docs.python.org/3/library/zipfile.html), Python
452
- versions older than 3.11 do not support image filenames with UTF-8 characters
453
- [Cell.add_style.bg_image()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Style) returns
454
- `None` for such files and issues a `RuntimeWarning`
455
- (see [issue 69](https://github.com/masaccio/numbers-parser/issues/69) for details).
456
- - Password-encrypted documents cannot be opened. You must first re-save without
457
- a password to read (see [issue 88](https://github.com/masaccio/numbers-parser/issues/88) for details).
458
- A UnsupportedError exception is raised when such documents are opened.
459
- - Due to changes in the format of Numbers documents, decoding of category groups
460
- (introduced in `numbers-parser` version 4.16) is supported only for documents
461
- created by Numbers 12.0 and later. No warnings are issued for earlier
375
+ - New sheets insert tables with formats copied from the first table in the previous sheet rather than default table formats
376
+ - Due to a limitation in Python’s [ZipFile](https://docs.python.org/3/library/zipfile.html), Python versions older than 3.11 do not support image filenames with UTF-8 characters [Cell.add_style.bg_image()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Style) returns `None` for such files and issues a `RuntimeWarning` (see [issue 69](https://github.com/masaccio/numbers-parser/issues/69) for details).
377
+ - Password-encrypted documents cannot be opened. You must first re-save without a password to read (see [issue 88](https://github.com/masaccio/numbers-parser/issues/88) for details). A UnsupportedError exception is raised when such documents are opened.
378
+ - Due to changes in the format of Numbers documents, decoding of category groups (introduced in `numbers-parser` version 4.16) is supported only for documents created by Numbers 12.0 and later. No warnings are issued for earlier
462
379
  Numbers documents.
463
- - Only standard macOS fonts are not supported. If a document includes a non-standard
464
- font, numbers-parser will issue a UnsupportedWarning and default styles to
465
- Helvetica Neue. Reading font names from the system would add additional system-specific
466
- dependencies to the package and so this is not planned to changed.
380
+ - Only standard macOS fonts are not supported. If a document includes a non-standard font, numbers-parser will issue a UnsupportedWarning and default styles to Helvetica Neue. Reading font names from the system would add additional system-specific dependencies to the package and so this is not planned to changed.
467
381
 
468
382
  ## License
469
383
 
@@ -22,7 +22,7 @@ classifiers = [
22
22
  description = "Read and write Apple Numbers spreadsheets"
23
23
  name = "numbers-parser"
24
24
  readme = "README.md"
25
- version = "4.17.0.post1"
25
+ version = "4.18.0"
26
26
 
27
27
  [project.urls]
28
28
  repository = "https://github.com/masaccio/numbers-parser"
@@ -16,11 +16,19 @@ from numbers_parser import (
16
16
  )
17
17
  from numbers_parser import __name__ as numbers_parser_name
18
18
  from numbers_parser.constants import MAX_SIGNIFICANT_DIGITS
19
- from numbers_parser.experimental import _enable_experimental_features
19
+ from numbers_parser.experimental import ExperimentalFeatures, enable_experimental_feature
20
20
 
21
21
  logger = logging.getLogger(numbers_parser_name)
22
22
 
23
23
 
24
+ def experimental_feature_choice(s: str) -> ExperimentalFeatures:
25
+ try:
26
+ return ExperimentalFeatures[s]
27
+ except KeyError:
28
+ msg = f"invalid experimental feature: {s}"
29
+ raise argparse.ArgumentTypeError(msg) # noqa: B904
30
+
31
+
24
32
  def command_line_parser():
25
33
  parser = argparse.ArgumentParser(
26
34
  description="Export data from Apple Numbers spreadsheet tables",
@@ -70,10 +78,13 @@ def command_line_parser():
70
78
  )
71
79
  parser.add_argument("document", nargs="*", help="Document(s) to export")
72
80
  parser.add_argument("--debug", default=False, action="store_true", help="Enable debug logging")
81
+ experimental_choices = [
82
+ f.name for f in ExperimentalFeatures if f is not ExperimentalFeatures.NONE
83
+ ]
73
84
  parser.add_argument(
74
85
  "--experimental",
75
- default=False,
76
- action="store_true",
86
+ type=experimental_feature_choice,
87
+ choices=[ExperimentalFeatures[name] for name in experimental_choices],
77
88
  help=argparse.SUPPRESS,
78
89
  )
79
90
  return parser
@@ -136,7 +147,7 @@ def main() -> None:
136
147
  else:
137
148
  logger.setLevel("ERROR")
138
149
  if args.experimental:
139
- _enable_experimental_features(True)
150
+ enable_experimental_feature(args.experimental)
140
151
  for filename in args.document:
141
152
  try:
142
153
  if args.list_sheets: