numbers-parser 4.10.4__py3-none-any.whl → 4.10.5__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.
Files changed (32) hide show
  1. numbers_parser/__init__.py +1 -1
  2. numbers_parser/_cat_numbers.py +4 -4
  3. numbers_parser/_unpack_numbers.py +5 -5
  4. numbers_parser/cell.py +119 -119
  5. numbers_parser/constants.py +5 -6
  6. numbers_parser/containers.py +11 -8
  7. numbers_parser/document.py +135 -113
  8. numbers_parser/exceptions.py +0 -7
  9. numbers_parser/formula.py +3 -4
  10. numbers_parser/generated/TNArchives_pb2.py +24 -26
  11. numbers_parser/generated/TSAArchives_pb2.py +26 -28
  12. numbers_parser/generated/TSCEArchives_pb2.py +8 -10
  13. numbers_parser/generated/TSCHArchives_GEN_pb2.py +28 -34
  14. numbers_parser/generated/TSCHArchives_pb2.py +61 -65
  15. numbers_parser/generated/TSDArchives_pb2.py +100 -110
  16. numbers_parser/generated/TSKArchives_pb2.py +168 -170
  17. numbers_parser/generated/TSPArchiveMessages_pb2.py +32 -34
  18. numbers_parser/generated/TSPMessages_pb2.py +40 -40
  19. numbers_parser/generated/TSSArchives_pb2.py +36 -36
  20. numbers_parser/generated/TSTArchives_pb2.py +321 -325
  21. numbers_parser/generated/TSTStylePropertyArchiving_pb2.py +8 -10
  22. numbers_parser/generated/TSWPArchives_pb2.py +250 -286
  23. numbers_parser/generated/TSWPCommandArchives_pb2.py +95 -95
  24. numbers_parser/iwafile.py +16 -16
  25. numbers_parser/iwork.py +35 -29
  26. numbers_parser/model.py +64 -52
  27. numbers_parser/numbers_uuid.py +6 -4
  28. {numbers_parser-4.10.4.dist-info → numbers_parser-4.10.5.dist-info}/METADATA +7 -7
  29. {numbers_parser-4.10.4.dist-info → numbers_parser-4.10.5.dist-info}/RECORD +32 -32
  30. {numbers_parser-4.10.4.dist-info → numbers_parser-4.10.5.dist-info}/LICENSE.rst +0 -0
  31. {numbers_parser-4.10.4.dist-info → numbers_parser-4.10.5.dist-info}/WHEEL +0 -0
  32. {numbers_parser-4.10.4.dist-info → numbers_parser-4.10.5.dist-info}/entry_points.txt +0 -0
numbers_parser/cell.py CHANGED
@@ -10,7 +10,7 @@ from fractions import Fraction
10
10
  from hashlib import sha1
11
11
  from os.path import basename
12
12
  from struct import pack, unpack
13
- from typing import Any, List, Tuple, Union
13
+ from typing import Any, List, Optional, Tuple, Union
14
14
  from warnings import warn
15
15
 
16
16
  import sigfig
@@ -104,8 +104,7 @@ __all__ = [
104
104
 
105
105
 
106
106
  class BackgroundImage:
107
- """
108
- A named document style that can be applied to cells.
107
+ """A named document style that can be applied to cells.
109
108
 
110
109
  .. code-block:: python
111
110
 
@@ -129,7 +128,7 @@ class BackgroundImage:
129
128
  Path to the image file.
130
129
  """
131
130
 
132
- def __init__(self, data: bytes = None, filename: str = None):
131
+ def __init__(self, data: Optional[bytes] = None, filename: Optional[str] = None) -> None:
133
132
  self._data = data
134
133
  self._filename = basename(filename)
135
134
 
@@ -180,13 +179,15 @@ class Alignment(_Alignment):
180
179
  if isinstance(horizontal, str):
181
180
  horizontal = horizontal.lower()
182
181
  if horizontal not in HORIZONTAL_MAP:
183
- raise TypeError("invalid horizontal alignment")
182
+ msg = "invalid horizontal alignment"
183
+ raise TypeError(msg)
184
184
  horizontal = HORIZONTAL_MAP[horizontal]
185
185
 
186
186
  if isinstance(vertical, str):
187
187
  vertical = vertical.lower()
188
188
  if vertical not in VERTICAL_MAP:
189
- raise TypeError("invalid vertical alignment")
189
+ msg = "invalid vertical alignment"
190
+ raise TypeError(msg)
190
191
  vertical = VERTICAL_MAP[vertical]
191
192
 
192
193
  return super(_Alignment, cls).__new__(cls, (horizontal, vertical))
@@ -199,8 +200,7 @@ RGB = namedtuple("RGB", ["r", "g", "b"])
199
200
 
200
201
  @dataclass
201
202
  class Style:
202
- """
203
- A named document style that can be applied to cells.
203
+ """A named document style that can be applied to cells.
204
204
 
205
205
  Parameters
206
206
  ----------
@@ -296,10 +296,7 @@ class Style:
296
296
 
297
297
  @classmethod
298
298
  def from_storage(cls, cell: object, model: object):
299
- if cell._image_data is not None:
300
- bg_image = BackgroundImage(*cell._image_data)
301
- else:
302
- bg_image = None
299
+ bg_image = BackgroundImage(*cell._image_data) if cell._image_data is not None else None
303
300
  return Style(
304
301
  alignment=model.cell_alignment(cell),
305
302
  bg_image=bg_image,
@@ -326,13 +323,16 @@ class Style:
326
323
  self.font_color = rgb_color(self.font_color)
327
324
 
328
325
  if not isinstance(self.font_size, float):
329
- raise TypeError("size must be a float number of points")
326
+ msg = "size must be a float number of points"
327
+ raise TypeError(msg)
330
328
  if not isinstance(self.font_name, str):
331
- raise TypeError("font name must be a string")
329
+ msg = "font name must be a string"
330
+ raise TypeError(msg)
332
331
 
333
332
  for attr in ["bold", "italic", "underline", "strikethrough"]:
334
333
  if not isinstance(getattr(self, attr), bool):
335
- raise TypeError(f"{attr} argument must be boolean")
334
+ msg = f"{attr} argument must be boolean"
335
+ raise TypeError(msg)
336
336
 
337
337
  def __setattr__(self, name: str, value: Any) -> None:
338
338
  """Detect changes to cell styles and flag the style for
@@ -359,11 +359,13 @@ def rgb_color(color) -> RGB:
359
359
  return color
360
360
  if isinstance(color, tuple):
361
361
  if not (len(color) == 3 and all(isinstance(x, int) for x in color)):
362
- raise TypeError("RGB color must be an RGB or a tuple of 3 integers")
362
+ msg = "RGB color must be an RGB or a tuple of 3 integers"
363
+ raise TypeError(msg)
363
364
  return RGB(*color)
364
365
  elif isinstance(color, list):
365
366
  return [rgb_color(c) for c in color]
366
- raise TypeError("RGB color must be an RGB or a tuple of 3 integers")
367
+ msg = "RGB color must be an RGB or a tuple of 3 integers"
368
+ raise TypeError(msg)
367
369
 
368
370
 
369
371
  def alignment(value) -> Alignment:
@@ -374,9 +376,11 @@ def alignment(value) -> Alignment:
374
376
  return value
375
377
  if isinstance(value, tuple):
376
378
  if not (len(value) == 2 and all(isinstance(x, (int, str)) for x in value)):
377
- raise TypeError("Alignment must be an Alignment or a tuple of 2 integers/strings")
379
+ msg = "Alignment must be an Alignment or a tuple of 2 integers/strings"
380
+ raise TypeError(msg)
378
381
  return Alignment(*value)
379
- raise TypeError("Alignment must be an Alignment or a tuple of 2 integers/strings")
382
+ msg = "Alignment must be an Alignment or a tuple of 2 integers/strings"
383
+ raise TypeError(msg)
380
384
 
381
385
 
382
386
  BORDER_STYLE_MAP = {"solid": 0, "dashes": 1, "dots": 2, "none": 3}
@@ -390,8 +394,7 @@ class BorderType(IntEnum):
390
394
 
391
395
 
392
396
  class Border:
393
- """
394
- Create a cell border to use with the :py:class:`~numbers_parser.Table` method
397
+ """Create a cell border to use with the :py:class:`~numbers_parser.Table` method
395
398
  :py:meth:`~numbers_parser.Table.set_cell_border`.
396
399
 
397
400
  .. code-block:: python
@@ -426,9 +429,10 @@ class Border:
426
429
  color: RGB = None,
427
430
  style: BorderType = None,
428
431
  _order: int = 0,
429
- ):
432
+ ) -> None:
430
433
  if not isinstance(width, float):
431
- raise TypeError("width must be a float number of points")
434
+ msg = "width must be a float number of points"
435
+ raise TypeError(msg)
432
436
  self.width = width
433
437
 
434
438
  if color is None:
@@ -440,7 +444,8 @@ class Border:
440
444
  if isinstance(style, str):
441
445
  style = style.lower()
442
446
  if style not in BORDER_STYLE_MAP:
443
- raise TypeError("invalid border style")
447
+ msg = "invalid border style"
448
+ raise TypeError(msg)
444
449
  self.style = BORDER_STYLE_MAP[style]
445
450
  else:
446
451
  self.style = style
@@ -453,7 +458,7 @@ class Border:
453
458
 
454
459
  def __eq__(self, value: object) -> bool:
455
460
  return all(
456
- [self.width == value.width, self.color == value.color, self.style == value.style]
461
+ [self.width == value.width, self.color == value.color, self.style == value.style],
457
462
  )
458
463
 
459
464
 
@@ -464,7 +469,7 @@ class CellBorder:
464
469
  right_merged: bool = False,
465
470
  bottom_merged: bool = False,
466
471
  left_merged: bool = False,
467
- ):
472
+ ) -> None:
468
473
  self._top = None
469
474
  self._right = None
470
475
  self._bottom = None
@@ -538,14 +543,14 @@ class CellBorder:
538
543
  class MergeReference:
539
544
  """Cell reference for cells eliminated by a merge."""
540
545
 
541
- def __init__(self, row_start: int, col_start: int, row_end: int, col_end: int):
546
+ def __init__(self, row_start: int, col_start: int, row_end: int, col_end: int) -> None:
542
547
  self.rect = (row_start, col_start, row_end, col_end)
543
548
 
544
549
 
545
550
  class MergeAnchor:
546
551
  """Cell reference for the merged cell."""
547
552
 
548
- def __init__(self, size: Tuple):
553
+ def __init__(self, size: Tuple) -> None:
549
554
  self.size = size
550
555
 
551
556
 
@@ -574,20 +579,18 @@ class CellStorageFlags:
574
579
  fields = [
575
580
  f"{k[1:]}={v}" for k, v in asdict(self).items() if k.endswith("_id") and v is not None
576
581
  ]
577
- fields = ", ".join([x for x in fields if x if not None])
578
- return fields
582
+ return ", ".join([x for x in fields if x if not None])
579
583
 
580
584
  def flags(self):
581
585
  return [x.name for x in fields(self)]
582
586
 
583
587
 
584
588
  class Cell(CellStorageFlags, Cacheable):
585
- """
586
- .. NOTE::
587
- Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
589
+ """.. NOTE::
590
+ Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
588
591
  """
589
592
 
590
- def __init__(self, row: int, col: int, value):
593
+ def __init__(self, row: int, col: int, value) -> None:
591
594
  self._value = value
592
595
  self.row = row
593
596
  self.col = col
@@ -599,14 +602,13 @@ class Cell(CellStorageFlags, Cacheable):
599
602
  self._seconds = None
600
603
  super().__init__()
601
604
 
602
- def __str__(self):
605
+ def __str__(self) -> str:
603
606
  table_name = self._model.table_name(self._table_id)
604
607
  sheet_name = self._model.sheet_name(self._model.table_id_to_sheet_id(self._table_id))
605
608
  cell_str = f"{sheet_name}@{table_name}[{self.row},{self.col}]:"
606
609
  cell_str += f"table_id={self._table_id}, type={self._type.name}, "
607
610
  cell_str += f"value={self._value}, flags={self._flags:08x}, extras={self._extras:04x}"
608
- cell_str = ", ".join([cell_str, super().__str__()])
609
- return cell_str
611
+ return ", ".join([cell_str, super().__str__()])
610
612
 
611
613
  @property
612
614
  def image_filename(self):
@@ -643,14 +645,14 @@ class Cell(CellStorageFlags, Cacheable):
643
645
  @property
644
646
  @cache(num_args=0)
645
647
  def formula(self) -> str:
646
- """
647
- str: The formula in a cell.
648
+ """str: The formula in a cell.
648
649
 
649
650
  Formula evaluation relies on Numbers storing current values which should
650
651
  usually be the case. In cells containing a formula, :py:meth:`numbers_parser.Cell.value`
651
652
  returns computed value of the formula.
652
653
 
653
- Returns:
654
+ Returns
655
+ -------
654
656
  str:
655
657
  The text of the foruma in a cell, or `None` if there is no formula
656
658
  present in a cell.
@@ -668,8 +670,7 @@ class Cell(CellStorageFlags, Cacheable):
668
670
 
669
671
  @property
670
672
  def bullets(self) -> Union[List[str], None]:
671
- r"""
672
- List[str] | None: The bullets in a cell, or ``None``
673
+ r"""List[str] | None: The bullets in a cell, or ``None``.
673
674
 
674
675
  Cells that contain bulleted or numbered lists are identified
675
676
  by :py:attr:`numbers_parser.Cell.is_bulleted`. For these cells,
@@ -678,9 +679,8 @@ class Cell(CellStorageFlags, Cacheable):
678
679
  bullet or numbering character. Newlines are not included in the
679
680
  bullet list.
680
681
 
681
- Example
682
+ Example:
682
683
  -------
683
-
684
684
  .. code-block:: python
685
685
 
686
686
  doc = Document("bullets.numbers")
@@ -698,8 +698,7 @@ class Cell(CellStorageFlags, Cacheable):
698
698
 
699
699
  @property
700
700
  def formatted_value(self) -> str:
701
- """
702
- str: The formatted value of the cell as it appears in Numbers.
701
+ """str: The formatted value of the cell as it appears in Numbers.
703
702
 
704
703
  Interactive elements are converted into a suitable text format where
705
704
  supported, or as their number values where there is no suitable
@@ -743,6 +742,7 @@ class Cell(CellStorageFlags, Cacheable):
743
742
  """Style | None: The :class:`Style` associated with the cell or ``None``.
744
743
 
745
744
  Warns:
745
+ -----
746
746
  UnsupportedWarning: On assignment; use
747
747
  :py:meth:`numbers_parser.Table.set_cell_style` instead.
748
748
  """
@@ -763,6 +763,7 @@ class Cell(CellStorageFlags, Cacheable):
763
763
  """CellBorder| None: The :class:`CellBorder` associated with the cell or ``None``.
764
764
 
765
765
  Warns:
766
+ -----
766
767
  UnsupportedWarning: On assignment; use
767
768
  :py:meth:`numbers_parser.Table.set_cell_border` instead.
768
769
  """
@@ -818,8 +819,8 @@ class Cell(CellStorageFlags, Cacheable):
818
819
  return cell
819
820
 
820
821
  @classmethod
821
- def _from_storage( # noqa: PLR0913, PLR0915, PLR0912
822
- cls, table_id: int, row: int, col: int, buffer: bytearray, model: object
822
+ def _from_storage( # noqa: PLR0913, PLR0912
823
+ cls, table_id: int, row: int, col: int, buffer: bytearray, model: object,
823
824
  ) -> None:
824
825
  d128 = None
825
826
  double = None
@@ -827,7 +828,8 @@ class Cell(CellStorageFlags, Cacheable):
827
828
 
828
829
  version = buffer[0]
829
830
  if version != 5:
830
- raise UnsupportedError(f"Cell storage version {version} is unsupported")
831
+ msg = f"Cell storage version {version} is unsupported"
832
+ raise UnsupportedError(msg)
831
833
 
832
834
  offset = 12
833
835
  storage_flags = CellStorageFlags()
@@ -921,7 +923,8 @@ class Cell(CellStorageFlags, Cacheable):
921
923
  elif cell_type == CURRENCY_CELL_TYPE:
922
924
  cell = NumberCell(row, col, d128, cell_type=CellType.CURRENCY)
923
925
  else:
924
- raise UnsupportedError(f"Cell type ID {cell_type} is not recognised")
926
+ msg = f"Cell type ID {cell_type} is not recognised"
927
+ raise UnsupportedError(msg)
925
928
 
926
929
  cell._copy_flags(storage_flags)
927
930
  cell._buffer = buffer
@@ -1136,8 +1139,8 @@ class Cell(CellStorageFlags, Cacheable):
1136
1139
 
1137
1140
  image_id = style.cell_properties.cell_fill.image.imagedata.identifier
1138
1141
  datas = self._model.objects[PACKAGE_ID].datas
1139
- stored_filename = [x.file_name for x in datas if x.identifier == image_id][0]
1140
- preferred_filename = [x.preferred_file_name for x in datas if x.identifier == image_id][0]
1142
+ stored_filename = next(x.file_name for x in datas if x.identifier == image_id)
1143
+ preferred_filename = next(x.preferred_file_name for x in datas if x.identifier == image_id)
1141
1144
  all_paths = self._model.objects.file_store.keys()
1142
1145
  image_pathnames = [x for x in all_paths if x == f"Data/{stored_filename}"]
1143
1146
 
@@ -1147,6 +1150,7 @@ class Cell(CellStorageFlags, Cacheable):
1147
1150
  RuntimeWarning,
1148
1151
  stacklevel=3,
1149
1152
  )
1153
+ return None
1150
1154
  else:
1151
1155
  image_data = self._model.objects.file_store[image_pathnames[0]]
1152
1156
  digest = sha1(image_data).digest()
@@ -1182,7 +1186,7 @@ class Cell(CellStorageFlags, Cacheable):
1182
1186
  )
1183
1187
  else:
1184
1188
  formatted_value = _decode_number_format(
1185
- custom_format, self._d128, format_map[format_uuid].name
1189
+ custom_format, self._d128, format_map[format_uuid].name,
1186
1190
  )
1187
1191
  elif format.format_type == FormatType.DECIMAL:
1188
1192
  return _format_decimal(self._d128, format)
@@ -1309,7 +1313,7 @@ class Cell(CellStorageFlags, Cacheable):
1309
1313
  self,
1310
1314
  format_id: int,
1311
1315
  format_type: Union[FormattingType, CustomFormattingType],
1312
- control_id: int = None,
1316
+ control_id: Optional[int] = None,
1313
1317
  is_currency: bool = False,
1314
1318
  ) -> None:
1315
1319
  self._is_currency = is_currency
@@ -1339,12 +1343,11 @@ class Cell(CellStorageFlags, Cacheable):
1339
1343
 
1340
1344
 
1341
1345
  class NumberCell(Cell):
1342
- """
1343
- .. NOTE::
1344
- Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1346
+ """.. NOTE::
1347
+ Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1345
1348
  """
1346
1349
 
1347
- def __init__(self, row: int, col: int, value: float, cell_type=CellType.NUMBER):
1350
+ def __init__(self, row: int, col: int, value: float, cell_type=CellType.NUMBER) -> None:
1348
1351
  self._type = cell_type
1349
1352
  super().__init__(row, col, value)
1350
1353
 
@@ -1354,7 +1357,7 @@ class NumberCell(Cell):
1354
1357
 
1355
1358
 
1356
1359
  class TextCell(Cell):
1357
- def __init__(self, row: int, col: int, value: str):
1360
+ def __init__(self, row: int, col: int, value: str) -> None:
1358
1361
  self._type = CellType.TEXT
1359
1362
  super().__init__(row, col, value)
1360
1363
 
@@ -1364,12 +1367,11 @@ class TextCell(Cell):
1364
1367
 
1365
1368
 
1366
1369
  class RichTextCell(Cell):
1367
- """
1368
- .. NOTE::
1369
- Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1370
+ """.. NOTE::
1371
+ Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1370
1372
  """
1371
1373
 
1372
- def __init__(self, row: int, col: int, value):
1374
+ def __init__(self, row: int, col: int, value) -> None:
1373
1375
  super().__init__(row, col, value["text"])
1374
1376
  self._type = CellType.RICH_TEXT
1375
1377
  self._bullets = value["bullets"]
@@ -1401,8 +1403,7 @@ class RichTextCell(Cell):
1401
1403
 
1402
1404
  @property
1403
1405
  def hyperlinks(self) -> Union[List[Tuple], None]:
1404
- """
1405
- List[Tuple] | None: the hyperlinks in a cell or ``None``
1406
+ """List[Tuple] | None: the hyperlinks in a cell or ``None``.
1406
1407
 
1407
1408
  Numbers does not support hyperlinks to cells within a spreadsheet, but does
1408
1409
  allow embedding links in cells. When cells contain hyperlinks,
@@ -1410,9 +1411,8 @@ class RichTextCell(Cell):
1410
1411
  of cells where :py:attr:`numbers_parser.Cell.is_bulleted` is ``True`` is a
1411
1412
  list of text and URL tuples.
1412
1413
 
1413
- Example
1414
+ Example:
1414
1415
  -------
1415
-
1416
1416
  .. code-block:: python
1417
1417
 
1418
1418
  cell = table.cell(0, 0)
@@ -1427,12 +1427,11 @@ class BulletedTextCell(RichTextCell):
1427
1427
 
1428
1428
 
1429
1429
  class EmptyCell(Cell):
1430
- """
1431
- .. NOTE::
1432
- Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1430
+ """.. NOTE::
1431
+ Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1433
1432
  """
1434
1433
 
1435
- def __init__(self, row: int, col: int):
1434
+ def __init__(self, row: int, col: int) -> None:
1436
1435
  super().__init__(row, col, None)
1437
1436
  self._type = CellType.EMPTY
1438
1437
 
@@ -1446,12 +1445,11 @@ class EmptyCell(Cell):
1446
1445
 
1447
1446
 
1448
1447
  class BoolCell(Cell):
1449
- """
1450
- .. NOTE::
1451
- Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1448
+ """.. NOTE::
1449
+ Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1452
1450
  """
1453
1451
 
1454
- def __init__(self, row: int, col: int, value: bool):
1452
+ def __init__(self, row: int, col: int, value: bool) -> None:
1455
1453
  super().__init__(row, col, value)
1456
1454
  self._type = CellType.BOOL
1457
1455
  self._value = value
@@ -1462,12 +1460,11 @@ class BoolCell(Cell):
1462
1460
 
1463
1461
 
1464
1462
  class DateCell(Cell):
1465
- """
1466
- .. NOTE::
1467
- Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1463
+ """.. NOTE::
1464
+ Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1468
1465
  """
1469
1466
 
1470
- def __init__(self, row: int, col: int, value: DateTime):
1467
+ def __init__(self, row: int, col: int, value: DateTime) -> None:
1471
1468
  super().__init__(row, col, value)
1472
1469
  self._type = CellType.DATE
1473
1470
 
@@ -1477,7 +1474,7 @@ class DateCell(Cell):
1477
1474
 
1478
1475
 
1479
1476
  class DurationCell(Cell):
1480
- def __init__(self, row: int, col: int, value: Duration):
1477
+ def __init__(self, row: int, col: int, value: Duration) -> None:
1481
1478
  super().__init__(row, col, value)
1482
1479
  self._type = CellType.DURATION
1483
1480
 
@@ -1487,12 +1484,11 @@ class DurationCell(Cell):
1487
1484
 
1488
1485
 
1489
1486
  class ErrorCell(Cell):
1490
- """
1491
- .. NOTE::
1492
- Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1487
+ """.. NOTE::
1488
+ Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1493
1489
  """
1494
1490
 
1495
- def __init__(self, row: int, col: int):
1491
+ def __init__(self, row: int, col: int) -> None:
1496
1492
  super().__init__(row, col, None)
1497
1493
  self._type = CellType.ERROR
1498
1494
 
@@ -1502,12 +1498,11 @@ class ErrorCell(Cell):
1502
1498
 
1503
1499
 
1504
1500
  class MergedCell(Cell):
1505
- """
1506
- .. NOTE::
1507
- Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1501
+ """.. NOTE::
1502
+ Do not instantiate directly. Cells are created by :py:class:`~numbers_parser.Document`.
1508
1503
  """
1509
1504
 
1510
- def __init__(self, row: int, col: int):
1505
+ def __init__(self, row: int, col: int) -> None:
1511
1506
  super().__init__(row, col, None)
1512
1507
  self._type = CellType.MERGED
1513
1508
 
@@ -1648,7 +1643,7 @@ def _decode_number_format(format, value, name): # noqa: PLR0912
1648
1643
  if format.currency_code != "":
1649
1644
  # Replace currency code with symbol and no-break space
1650
1645
  custom_format_string = custom_format_string.replace(
1651
- "\u00a4", format.currency_code + "\u00a0"
1646
+ "\u00a4", format.currency_code + "\u00a0",
1652
1647
  )
1653
1648
 
1654
1649
  if (match := re.search(r"([#0.,]+(E[+]\d+)?)", custom_format_string)) is None:
@@ -1799,7 +1794,7 @@ def _format_decimal(value: float, format, percent: bool = False) -> str:
1799
1794
  else:
1800
1795
  formatted_value = sigfig.round(value, MAX_SIGNIFICANT_DIGITS, type=str, warn=False)
1801
1796
  formatted_value = sigfig.round(
1802
- formatted_value, decimals=format.decimal_places, type=str
1797
+ formatted_value, decimals=format.decimal_places, type=str,
1803
1798
  )
1804
1799
  if format.show_thousands_separator:
1805
1800
  formatted_value = sigfig.round(formatted_value, spacer=",", spacing=3, type=str)
@@ -1832,16 +1827,16 @@ def _format_currency(value: float, format) -> str:
1832
1827
  return symbol + formatted_value
1833
1828
 
1834
1829
 
1835
- INT_TO_BASE_CHAR = [str(x) for x in range(0, 10)] + [chr(x) for x in range(ord("A"), ord("Z") + 1)]
1830
+ INT_TO_BASE_CHAR = [str(x) for x in range(10)] + [chr(x) for x in range(ord("A"), ord("Z") + 1)]
1836
1831
 
1837
1832
 
1838
1833
  def _invert_bit_str(value: str) -> str:
1839
- """Invert a binary value"""
1834
+ """Invert a binary value."""
1840
1835
  return "".join(["0" if b == "1" else "1" for b in value])
1841
1836
 
1842
1837
 
1843
1838
  def _twos_complement(value: int, base: int) -> str:
1844
- """Calculate the twos complement of a negative integer with minimum 32-bit precision"""
1839
+ """Calculate the twos complement of a negative integer with minimum 32-bit precision."""
1845
1840
  num_bits = max([32, math.ceil(math.log2(abs(value))) + 1])
1846
1841
  bin_value = bin(abs(value))[2:]
1847
1842
  inverted_bin_value = _invert_bit_str(bin_value).rjust(num_bits, "1")
@@ -1930,7 +1925,7 @@ def _format_scientific(value: float, format) -> str:
1930
1925
  return f"{formatted_value:.{format.decimal_places}E}"
1931
1926
 
1932
1927
 
1933
- def _unit_format(unit: str, value: int, style: int, abbrev: str = None):
1928
+ def _unit_format(unit: str, value: int, style: int, abbrev: Optional[str] = None):
1934
1929
  plural = "" if value == 1 else "s"
1935
1930
  if abbrev is None:
1936
1931
  abbrev = unit[0]
@@ -1985,8 +1980,7 @@ range_parts = re.compile(r"(\$?)([A-Z]{1,3})(\$?)(\d+)")
1985
1980
 
1986
1981
 
1987
1982
  def xl_cell_to_rowcol(cell_str: str) -> tuple:
1988
- """
1989
- Convert a cell reference in A1 notation to a zero indexed row and column.
1983
+ """Convert a cell reference in A1 notation to a zero indexed row and column.
1990
1984
 
1991
1985
  Parameters
1992
1986
  ----------
@@ -2003,7 +1997,8 @@ def xl_cell_to_rowcol(cell_str: str) -> tuple:
2003
1997
 
2004
1998
  match = range_parts.match(cell_str)
2005
1999
  if not match:
2006
- raise IndexError(f"invalid cell reference {cell_str}")
2000
+ msg = f"invalid cell reference {cell_str}"
2001
+ raise IndexError(msg)
2007
2002
 
2008
2003
  col_str = match.group(2)
2009
2004
  row_str = match.group(4)
@@ -2023,8 +2018,7 @@ def xl_cell_to_rowcol(cell_str: str) -> tuple:
2023
2018
 
2024
2019
 
2025
2020
  def xl_range(first_row, first_col, last_row, last_col):
2026
- """
2027
- Convert zero indexed row and col cell references to a A1:B1 range string.
2021
+ """Convert zero indexed row and col cell references to a A1:B1 range string.
2028
2022
 
2029
2023
  Parameters
2030
2024
  ----------
@@ -2052,8 +2046,7 @@ def xl_range(first_row, first_col, last_row, last_col):
2052
2046
 
2053
2047
 
2054
2048
  def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False):
2055
- """
2056
- Convert a zero indexed row and column cell reference to a A1 style string.
2049
+ """Convert a zero indexed row and column cell reference to a A1 style string.
2057
2050
 
2058
2051
  Parameters
2059
2052
  ----------
@@ -2072,10 +2065,12 @@ def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False):
2072
2065
  A1 style string.
2073
2066
  """
2074
2067
  if row < 0:
2075
- raise IndexError(f"row reference {row} below zero")
2068
+ msg = f"row reference {row} below zero"
2069
+ raise IndexError(msg)
2076
2070
 
2077
2071
  if col < 0:
2078
- raise IndexError(f"column reference {col} below zero")
2072
+ msg = f"column reference {col} below zero"
2073
+ raise IndexError(msg)
2079
2074
 
2080
2075
  row += 1 # Change to 1-index.
2081
2076
  row_abs = "$" if row_abs else ""
@@ -2086,8 +2081,7 @@ def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False):
2086
2081
 
2087
2082
 
2088
2083
  def xl_col_to_name(col, col_abs=False):
2089
- """
2090
- Convert a zero indexed column cell reference to a string.
2084
+ """Convert a zero indexed column cell reference to a string.
2091
2085
 
2092
2086
  Parameters
2093
2087
  ----------
@@ -2095,12 +2089,15 @@ def xl_col_to_name(col, col_abs=False):
2095
2089
  The column number (zero indexed).
2096
2090
  col_abs: bool, default: False
2097
2091
  If ``True``, make the column absolute.
2098
- Returns:
2092
+
2093
+ Returns
2094
+ -------
2099
2095
  str:
2100
2096
  Column in A1 notation.
2101
2097
  """
2102
2098
  if col < 0:
2103
- raise IndexError(f"column reference {col} below zero")
2099
+ msg = f"column reference {col} below zero"
2100
+ raise IndexError(msg)
2104
2101
 
2105
2102
  col += 1 # Change to 1-index.
2106
2103
  col_str = ""
@@ -2149,7 +2146,8 @@ class Formatting:
2149
2146
  def __post_init__(self):
2150
2147
  if not isinstance(self.type, FormattingType):
2151
2148
  type_name = type(self.type).__name__
2152
- raise TypeError(f"Invalid format type '{type_name}'")
2149
+ msg = f"Invalid format type '{type_name}'"
2150
+ raise TypeError(msg)
2153
2151
 
2154
2152
  if self.use_accounting_style and self.negative_style != NegativeNumberStyle.MINUS:
2155
2153
  warn(
@@ -2161,12 +2159,12 @@ class Formatting:
2161
2159
  if self.type == FormattingType.DATETIME:
2162
2160
  formats = re.sub(r"[^a-zA-Z\s]", " ", self.date_time_format).split()
2163
2161
  for el in formats:
2164
- if el not in DATETIME_FIELD_MAP.keys():
2165
- raise TypeError(f"Invalid format specifier '{el}' in date/time format")
2162
+ if el not in DATETIME_FIELD_MAP:
2163
+ msg = f"Invalid format specifier '{el}' in date/time format"
2164
+ raise TypeError(msg)
2166
2165
 
2167
- if self.type == FormattingType.CURRENCY:
2168
- if self.currency_code not in CURRENCIES:
2169
- raise TypeError(f"Unsupported currency code '{self.currency_code}'")
2166
+ if self.type == FormattingType.CURRENCY and self.currency_code not in CURRENCIES:
2167
+ raise TypeError(f"Unsupported currency code '{self.currency_code}'")
2170
2168
 
2171
2169
  if self.decimal_places is None:
2172
2170
  if self.type == FormattingType.CURRENCY:
@@ -2179,10 +2177,12 @@ class Formatting:
2179
2177
  and not self.base_use_minus_sign
2180
2178
  and self.base not in (2, 8, 16)
2181
2179
  ):
2182
- raise TypeError(f"base_use_minus_sign must be True for base {self.base}")
2180
+ msg = f"base_use_minus_sign must be True for base {self.base}"
2181
+ raise TypeError(msg)
2183
2182
 
2184
2183
  if self.type == FormattingType.BASE and (self.base < 2 or self.base > MAX_BASE):
2185
- raise TypeError("base must be in range 2-36")
2184
+ msg = "base must be in range 2-36"
2185
+ raise TypeError(msg)
2186
2186
 
2187
2187
 
2188
2188
  @dataclass
@@ -2199,11 +2199,11 @@ class CustomFormatting:
2199
2199
  def __post_init__(self):
2200
2200
  if not isinstance(self.type, CustomFormattingType):
2201
2201
  type_name = type(self.type).__name__
2202
- raise TypeError(f"Invalid format type '{type_name}'")
2202
+ msg = f"Invalid format type '{type_name}'"
2203
+ raise TypeError(msg)
2203
2204
 
2204
- if self.type == CustomFormattingType.TEXT:
2205
- if self.format.count("%s") > 1:
2206
- raise TypeError("Custom formats only allow one text substitution")
2205
+ if self.type == CustomFormattingType.TEXT and self.format.count("%s") > 1:
2206
+ raise TypeError("Custom formats only allow one text substitution")
2207
2207
 
2208
2208
  @classmethod
2209
2209
  def from_archive(cls, archive: object):