numbers-parser 4.17.0.post1__py3-none-any.whl → 4.18.0__py3-none-any.whl

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/model.py CHANGED
@@ -807,38 +807,6 @@ class _NumbersModel(Cacheable):
807
807
  # Table can be empty if the document does not use FormulaOwnerDependenciesArchive
808
808
  return self._table_id_to_base_id.get(table_id)
809
809
 
810
- def get_formula_owner(self, table_id: int) -> object:
811
- table_uuid = self.table_base_id(table_id)
812
- return self.objects[self._table_base_id_to_formula_owner_id[table_uuid]]
813
-
814
- def add_formula_dependency(self, row: int, col: int, table_id: int) -> None:
815
- calc_engine = self.calc_engine()
816
- calc_engine.dependency_tracker.number_of_formulas += 1
817
- internal_formula_id = calc_engine.dependency_tracker.number_of_formulas
818
-
819
- formula_owner = self.get_formula_owner(table_id)
820
- formula_owner.cell_dependencies.cell_record.append(
821
- TSCEArchives.CellRecordExpandedArchive(column=col, row=row),
822
- )
823
- if len(formula_owner.tiled_cell_dependencies.cell_record_tiles) == 0:
824
- cell_record_id, cell_record = self.objects.create_object_from_dict(
825
- "CalculationEngine",
826
- {
827
- "internal_owner_id": internal_formula_id,
828
- "tile_column_begin": 0,
829
- "tile_row_begin": 0,
830
- },
831
- TSCEArchives.CellRecordTileArchive,
832
- )
833
- formula_owner.tiled_cell_dependencies.cell_record_tiles.append(
834
- TSPMessages.Reference(identifier=cell_record_id),
835
- )
836
- else:
837
- cell_record_id = formula_owner.tiled_cell_dependencies.cell_record_tiles[0].identifier
838
- cell_record = self.objects[cell_record_id]
839
-
840
- cell_record.cell_records.append(formula_owner.cell_dependencies.cell_record[-1])
841
-
842
810
  @cache(num_args=0)
843
811
  def calc_engine_id(self):
844
812
  """Return the CalculationEngine ID for the current document."""
@@ -867,7 +835,7 @@ class _NumbersModel(Cacheable):
867
835
  self._merge_cells[table_id].add_anchor(row_start, col_start, size)
868
836
 
869
837
  @cache()
870
- def calculate_new_merge_cell_ranges(self, table_id) -> None:
838
+ def calculate_merges_using_formula_stores(self, table_id) -> None:
871
839
  table_model = self.objects[table_id]
872
840
  formulas = table_model.merge_owner.formula_store.formulas
873
841
  if len(formulas) == 0:
@@ -887,7 +855,7 @@ class _NumbersModel(Cacheable):
887
855
  )
888
856
 
889
857
  @cache()
890
- def calculate_merge_cell_ranges(self, table_id) -> None:
858
+ def calculate_merges_using_dependency_archives(self, table_id) -> None:
891
859
  """Extract all the merge cell ranges for the Table."""
892
860
  # See details in Numbers.md#merge-ranges.
893
861
  owner_id_map = self.owner_id_map()
@@ -910,6 +878,8 @@ class _NumbersModel(Cacheable):
910
878
  record_range.bottom_right_column,
911
879
  )
912
880
 
881
+ @cache()
882
+ def calculate_merges_using_region_map(self, table_id) -> None:
913
883
  base_data_store = self.objects[table_id].base_data_store
914
884
  if base_data_store.merge_region_map.identifier == 0:
915
885
  return
@@ -926,18 +896,12 @@ class _NumbersModel(Cacheable):
926
896
  )
927
897
  row_end = row_start + num_rows - 1
928
898
  col_end = col_start + num_columns - 1
929
- for row in range(row_start, row_end + 1):
930
- for col in range(col_start, col_end + 1):
931
- self._merge_cells[table_id].add_reference(
932
- row,
933
- col,
934
- (row_start, col_start, row_end, col_end),
935
- )
936
- self._merge_cells[table_id].add_anchor(row_start, col_start, (num_rows, num_columns))
899
+ self.add_merge_range(table_id, row_start, row_end, col_start, col_end)
937
900
 
938
901
  def merge_cells(self, table_id):
939
- self.calculate_new_merge_cell_ranges(table_id)
940
- self.calculate_merge_cell_ranges(table_id)
902
+ self.calculate_merges_using_formula_stores(table_id)
903
+ self.calculate_merges_using_dependency_archives(table_id)
904
+ self.calculate_merges_using_region_map(table_id)
941
905
  return self._merge_cells[table_id]
942
906
 
943
907
  def table_id_to_sheet_id(self, table_id: int) -> int:
@@ -946,25 +910,14 @@ class _NumbersModel(Cacheable):
946
910
  return sheet_id
947
911
  return None
948
912
 
949
- def table_name_to_uuid(self, sheet_name: str, table_name: str) -> str:
950
- table_ids = [tid for tid in self.table_ids() if table_name == self.table_name(tid)]
951
- if len(table_ids) == 1:
952
- return self.table_base_id(table_ids[0])
953
-
954
- sheet_name_to_id = {self.sheet_name(x): x for x in self.sheet_ids()}
955
- sheet_id = sheet_name_to_id[sheet_name]
956
- table_name_to_id = {self.table_name(x): x for x in self.table_ids(sheet_id)}
957
- table_id = table_name_to_id[table_name]
958
- return self.table_base_id(table_id)
959
-
960
913
  @cache()
961
914
  def table_uuids_to_id(self, table_uuid) -> int | None:
962
- for sheet_id in self.sheet_ids(): # pragma: no branch
915
+ for sheet_id in self.sheet_ids(): # pragma: no branch # noqa: RET503
963
916
  for table_id in self.table_ids(sheet_id):
964
917
  if table_uuid == self.table_base_id(table_id):
965
918
  return table_id
966
919
 
967
- def node_to_ref(self, table_id: int, row: int, col: int, node, merge_mode: bool = False):
920
+ def node_to_ref(self, table_id: int, row: int, col: int, node):
968
921
  def resolve_range(is_absolute, absolute_list, relative_list, offset, max_val):
969
922
  if is_absolute:
970
923
  return absolute_list[0].range_begin
@@ -1030,7 +983,6 @@ class _NumbersModel(Cacheable):
1030
983
  col_end_is_abs=node.AST_sticky_bits.end_column_is_absolute,
1031
984
  from_table_id=table_id,
1032
985
  to_table_id=to_table_id,
1033
- _do_init=not merge_mode,
1034
986
  )
1035
987
 
1036
988
  row = node.AST_row.row if node.AST_row.absolute else row + node.AST_row.row
@@ -1510,7 +1462,7 @@ class _NumbersModel(Cacheable):
1510
1462
  },
1511
1463
  TSTArchives.TableModelArchive,
1512
1464
  )
1513
- # Supresses Numbers assertions for tables sharing the same data
1465
+ # Suppress Numbers assertions for tables sharing the same data
1514
1466
  table_model.category_owner.identifier = 0
1515
1467
 
1516
1468
  column_headers_id, column_headers = self.objects.create_object_from_dict(
@@ -1719,7 +1671,7 @@ class _NumbersModel(Cacheable):
1719
1671
  "bottom_right_column": 0x7FFF,
1720
1672
  "bottom_right_row": 0x7FFFFFFF,
1721
1673
  }
1722
- spanning_depdendencies = {
1674
+ spanning_dependencies = {
1723
1675
  "total_range_for_table": null_range_ref,
1724
1676
  "body_range_for_table": null_range_ref,
1725
1677
  }
@@ -1732,8 +1684,8 @@ class _NumbersModel(Cacheable):
1732
1684
  "cell_dependencies": {},
1733
1685
  "range_dependencies": {},
1734
1686
  "volatile_dependencies": volatile_dependencies,
1735
- "spanning_column_dependencies": spanning_depdendencies,
1736
- "spanning_row_dependencies": spanning_depdendencies,
1687
+ "spanning_column_dependencies": spanning_dependencies,
1688
+ "spanning_row_dependencies": spanning_dependencies,
1737
1689
  "whole_owner_dependencies": {"dependent_cells": {}},
1738
1690
  "cell_errors": {},
1739
1691
  "base_owner_uid": base_owner_uuid.dict2,
@@ -1809,7 +1761,7 @@ class _NumbersModel(Cacheable):
1809
1761
  for k, v in presets_map.items()
1810
1762
  }
1811
1763
  for style in styles.values():
1812
- # Override __setattr__ behaviour for builtin styles
1764
+ # Override __setattr__ behavior for builtin styles
1813
1765
  style.__dict__["_update_text_style"] = False
1814
1766
  style.__dict__["_update_cell_style"] = False
1815
1767
  return styles
@@ -2142,7 +2094,7 @@ class _NumbersModel(Cacheable):
2142
2094
  # a string with a new bullet character
2143
2095
  bds = self.objects[table_id].base_data_store
2144
2096
  rich_text_table = self.objects[bds.rich_text_table.identifier]
2145
- for entry in rich_text_table.entries: # pragma: no branch
2097
+ for entry in rich_text_table.entries: # pragma: no branch # noqa: RET503
2146
2098
  if string_key == entry.key:
2147
2099
  payload = self.objects[entry.rich_text_payload.identifier]
2148
2100
  payload_storage = self.objects[payload.storage.identifier]
@@ -2596,6 +2548,59 @@ class _NumbersModel(Cacheable):
2596
2548
  # datas never appears to be an empty list (default themes include images)
2597
2549
  return max(image_ids) + 1
2598
2550
 
2551
+ @classmethod
2552
+ def cell_value_to_key(
2553
+ cls,
2554
+ cell_value: TSCEArchives.CellValueArchive,
2555
+ ) -> str | int | bool | datetime:
2556
+ """Convert a CellValueArchive to a key."""
2557
+ cell_value_type = cell_value.cell_value_type
2558
+ if cell_value_type == CellValueType.STRING_TYPE:
2559
+ return cell_value.string_value.value
2560
+ if cell_value_type == CellValueType.NUMBER_TYPE:
2561
+ return cell_value.number_value.value
2562
+ if cell_value_type == CellValueType.BOOLEAN_TYPE:
2563
+ return cell_value.boolean_value.value
2564
+ # Must be DATE_TYPE
2565
+ return cell_value.date_value.value
2566
+
2567
+ @cache(num_args=0)
2568
+ def group_uuid_values(self):
2569
+ return {
2570
+ NumbersUUID(self.objects[_id].group_uid): _NumbersModel.cell_value_to_key(
2571
+ self.objects[_id].group_cell_value,
2572
+ )
2573
+ for _id in self.find_refs("GroupNodeArchive")
2574
+ }
2575
+
2576
+ @cache()
2577
+ def table_category_row_map(self, table_id: int) -> dict[int, int] | None:
2578
+ category_owner_id = self.objects[table_id].category_owner.identifier
2579
+ if not category_owner_id:
2580
+ return None
2581
+ category_archive_id = self.objects[category_owner_id].group_by[0].identifier
2582
+ category_archive = self.objects[category_archive_id]
2583
+ if not category_archive.is_enabled:
2584
+ return None
2585
+
2586
+ table_info = self.objects[self.table_info_id(table_id)]
2587
+ category_order = self.objects[table_info.category_order.identifier]
2588
+ row_uid_map = self.objects[category_order.uid_map.identifier]
2589
+
2590
+ group_uuids = self.group_uuid_values()
2591
+ row_uuid_to_offset = {
2592
+ NumbersUUID(uuid): row for row, uuid in enumerate(category_archive.row_uid_lookup.uuids)
2593
+ }
2594
+ row_uid_for_index = [
2595
+ NumbersUUID(row_uid_map.sorted_row_uids[i]) for i in row_uid_map.row_uid_for_index
2596
+ ]
2597
+ return {
2598
+ row: row_uuid_to_offset[uuid]
2599
+ for row, uuid in enumerate(
2600
+ uuid for uuid in row_uid_for_index if uuid not in group_uuids
2601
+ )
2602
+ }
2603
+
2599
2604
  def table_category_data(self, table_id: int) -> dict | None:
2600
2605
  category_owner_id = self.objects[table_id].category_owner.identifier
2601
2606
  category_archive_id = self.objects[category_owner_id].group_by[0].identifier
@@ -2606,8 +2611,9 @@ class _NumbersModel(Cacheable):
2606
2611
  table_info = self.objects[self.table_info_id(table_id)]
2607
2612
  category_order = self.objects[table_info.category_order.identifier]
2608
2613
  row_uid_map = self.objects[category_order.uid_map.identifier]
2614
+
2609
2615
  sorted_row_uuids = [
2610
- NumbersUUID(row_uid_map.sorted_row_uids[i]).hex for i in row_uid_map.row_uid_for_index
2616
+ NumbersUUID(row_uid_map.sorted_row_uids[i]).hex for i in row_uid_map.row_index_for_uid
2611
2617
  ]
2612
2618
 
2613
2619
  data = self._table_data[table_id]
@@ -2623,22 +2629,8 @@ class _NumbersModel(Cacheable):
2623
2629
  offsets += list(range(entry.range_begin, entry.range_begin + 1))
2624
2630
  return offsets
2625
2631
 
2626
- def cell_value_to_key(
2627
- cell_value: TSCEArchives.CellValueArchive,
2628
- ) -> str | int | bool | datetime:
2629
- """Convert a CellValueArchive to a key."""
2630
- cell_value_type = cell_value.cell_value_type
2631
- if cell_value_type == CellValueType.STRING_TYPE:
2632
- return cell_value.string_value.value
2633
- if cell_value_type == CellValueType.NUMBER_TYPE:
2634
- return cell_value.number_value.value
2635
- if cell_value_type == CellValueType.BOOLEAN_TYPE:
2636
- return cell_value.boolean_value.value
2637
- # Must be DATE_TYPE
2638
- return cell_value.date_value.value
2639
-
2640
2632
  group_node_to_key = {
2641
- NumbersUUID(self.objects[_id].group_uid).hex: cell_value_to_key(
2633
+ NumbersUUID(self.objects[_id].group_uid).hex: _NumbersModel.cell_value_to_key(
2642
2634
  self.objects[_id].group_cell_value,
2643
2635
  )
2644
2636
  for _id in self.find_refs("GroupNodeArchive")
@@ -2660,7 +2652,7 @@ class _NumbersModel(Cacheable):
2660
2652
  for child in children:
2661
2653
  group_uuid = NumbersUUID(child.group_uid).hex
2662
2654
  if len(child.child) == 0:
2663
- key = cell_value_to_key(child.group_cell_value)
2655
+ key = _NumbersModel.cell_value_to_key(child.group_cell_value)
2664
2656
 
2665
2657
  row_offsets = index_set_to_offsets(child.row_lookup_uids)
2666
2658
  categories[group_uuid] = {
numbers_parser/xrefs.py CHANGED
@@ -67,8 +67,6 @@ class CellRange:
67
67
  _table_names: list[str] = field(init=False, default=None, repr=False)
68
68
 
69
69
  def __post_init__(self):
70
- if not self._do_init:
71
- return
72
70
  if self._table_names is None:
73
71
  self._initialize_table_data()
74
72
  self.model.name_ref_cache.refresh()
@@ -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
 
@@ -1,24 +1,23 @@
1
1
  numbers_parser/__init__.py,sha256=fKvZmUi_UsY3DMwt54s7G20DCwUTuG28B7kGsQRw3wU,1481
2
- numbers_parser/_cat_numbers.py,sha256=LQDBHLdVP8JIk0heV50T0Xz1oOMVNGILaX2mk8pVhCg,4846
2
+ numbers_parser/_cat_numbers.py,sha256=6JR69SW3yNx1zOPfxpd0QA8mb9XGnIQckzl5O1L-pK4,5311
3
3
  numbers_parser/_csv2numbers.py,sha256=R_13gVe_RfLWLOv-DzVJ60xkqhdOA-RKna-Ce5ud4V8,14624
4
4
  numbers_parser/_unpack_numbers.py,sha256=x4Od4qEruPpzkoOq_i_zOFiNbNonlSRHBT3WGwZGYo8,6685
5
5
  numbers_parser/bullets.py,sha256=CKMjSYRzLM123wQNDG8B5vfodRLjW5UOc2HDPlvfNr4,2637
6
- numbers_parser/cell.py,sha256=RSjzkYWTUN753Q6V5xIQZ0GC2gkZDmnpB6uz4GJGZ0U,72311
6
+ numbers_parser/cell.py,sha256=l0r7n0QX-MrPNo5DoHNrtn4iJYidpHHGfW3ioyhE2tw,71955
7
7
  numbers_parser/constants.py,sha256=FPkKRrkPtiV_KX9SWjR6DLlkDw644lmMw8GOBEG6Gsc,10658
8
8
  numbers_parser/containers.py,sha256=SBD99t0ABHraGA8729jPXy5VcRHT21U_WM3ijOIiwd4,4886
9
9
  numbers_parser/currencies.py,sha256=8k4a3WKmDoHeurkDICymHX13N7ManHSTaka_JNXCZYA,3767
10
- numbers_parser/document.py,sha256=6eCNbLRUmNoNEim8Wbk9DkhuhWHAtTmbexKDvld8Bp4,60866
11
- numbers_parser/exceptions.py,sha256=51yJTYm0X2am4YK-F6sVYOHmlYrBex_e4lYtbmiQX3I,611
12
- numbers_parser/experimental.py,sha256=WARjTa-2ePb8Ga8Q6oDP6EJCs12ofLRF2YpwzUu66ZI,374
13
- numbers_parser/formula.py,sha256=XC6maWkkQ-LKByiBQDIhzHOwvQIu-df6MN-vh4EETH8,26459
10
+ numbers_parser/document.py,sha256=i0Bta5JtXYHRCcoARH6Yos2ZASugqv8RJIqLEpamWOc,61610
11
+ numbers_parser/exceptions.py,sha256=aTpY9FMXa7RgsazJ8WEPxgA9qOzYd3lC5BK1TfcgCTs,612
12
+ numbers_parser/experimental.py,sha256=Pox7smoekWsa4-TO0IN05-omE7HMf9PViWr_swf_B40,807
13
+ numbers_parser/formula.py,sha256=0X5BsRzotN5ktPoafJITNo3mHTLjtasZ0PiBkiiHMFQ,9260
14
14
  numbers_parser/iwafile.py,sha256=nVwCcXvx8TgX-DkYqSbcJQzKyXR1Z8_y547XeY7Y-fs,11948
15
15
  numbers_parser/iwork.py,sha256=Qx1Izf9cfQKnLTDiJyeCOsmL9PSsfV-XOf0Kkb2oPMo,9396
16
- numbers_parser/model.py,sha256=VFD9CR33jKVCbsXFUrKV2elACQEnq8Ykhi5cqKvchlo,116600
16
+ numbers_parser/model.py,sha256=WPVWnd8_QgBsMyi_sqdy1ML8WfxKAvBjyh_1lXavTfs,115812
17
17
  numbers_parser/numbers_cache.py,sha256=uGngtJmP_AEDGZphyJpOMJ1dq8VGEeDyPdyrVy1E7lU,1100
18
18
  numbers_parser/numbers_uuid.py,sha256=vVTgXgmtGx6PdbHfwyi82oRsZccmP7IW_o2q-6PGUNg,2880
19
19
  numbers_parser/roman.py,sha256=CErv1uRp8eE9Bsuh_QsrBAzKFsfHSEs8CHpfFW0Gd70,725
20
- numbers_parser/tokenizer.py,sha256=4pGdzX9zoxSYzaP7ywLwq8lOpVAm8hk96tMg9em7S6o,19804
21
- numbers_parser/xrefs.py,sha256=Oq-EL5bSSmXvJGSX-MPtXDcX-EROX859knm9y_Zz568,30043
20
+ numbers_parser/xrefs.py,sha256=JZXH1FssaLVZL9tX8rvrdM6PfoM9Cte5F-hUxZVw-ZI,29994
22
21
  numbers_parser/data/empty.numbers,sha256=zQjVqcNGN-xNZh51_eAxD8Zxmxh88Sd7kpf4JygUA_E,90601
23
22
  numbers_parser/generated/TNArchives_pb2.py,sha256=ACEMFxxhu7QpKJT9bhx6k2WZMs_qBin-csiAiUi0YNc,17387
24
23
  numbers_parser/generated/TNArchives_sos_pb2.py,sha256=kBurQK0i7wFZ6nIh3we0n7xHbOTLJ6IYx2yZzF336G4,1534
@@ -57,9 +56,9 @@ numbers_parser/generated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
57
56
  numbers_parser/generated/fontmap.py,sha256=N7RelrdanYW9mK7mcl42THasVcw0uQ9XeZ3wn4rOfKo,23586
58
57
  numbers_parser/generated/functionmap.py,sha256=7GzgkZfONzhI35pnByINiIGCbhMxhNz6EWiJEBX4s1g,6672
59
58
  numbers_parser/generated/mapping.py,sha256=W6fEWqar3TFvx2wyWgK4NutIz9EYKtjChhSfho2FGQQ,32266
60
- numbers_parser-4.17.0.post1.dist-info/licenses/LICENSE.rst,sha256=8vTa1-5KSdHrTpU9rlheO5005EWReEPMpjV7BjSaMc4,1050
61
- numbers_parser-4.17.0.post1.dist-info/METADATA,sha256=NdWr7x7X9xlPL-nkHopaz_xn0FkpP5aWzBiWEdu6G-8,23099
62
- numbers_parser-4.17.0.post1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
63
- numbers_parser-4.17.0.post1.dist-info/entry_points.txt,sha256=_5biENfY3S-XG8BZL2AuE7Gvrw0zBwGjF6Un52u7FAk,165
64
- numbers_parser-4.17.0.post1.dist-info/top_level.txt,sha256=EOag5_Q4xjG7gstnrNVeAF_CLUAJBR_HDvciqMQ9RQo,15
65
- numbers_parser-4.17.0.post1.dist-info/RECORD,,
59
+ numbers_parser-4.18.0.dist-info/licenses/LICENSE.rst,sha256=8vTa1-5KSdHrTpU9rlheO5005EWReEPMpjV7BjSaMc4,1050
60
+ numbers_parser-4.18.0.dist-info/METADATA,sha256=5wcfPZkOtf-S2t-YsNxDhQ12lqc9h45nDZUiy7TVzKc,23358
61
+ numbers_parser-4.18.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
62
+ numbers_parser-4.18.0.dist-info/entry_points.txt,sha256=_5biENfY3S-XG8BZL2AuE7Gvrw0zBwGjF6Un52u7FAk,165
63
+ numbers_parser-4.18.0.dist-info/top_level.txt,sha256=EOag5_Q4xjG7gstnrNVeAF_CLUAJBR_HDvciqMQ9RQo,15
64
+ numbers_parser-4.18.0.dist-info/RECORD,,