numbers-parser 4.4.6__py3-none-any.whl → 4.4.8__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.
@@ -23,9 +23,9 @@ import os
23
23
  import plistlib
24
24
  import warnings
25
25
 
26
- from numbers_parser.document import Document # NOQA
27
- from numbers_parser.cell import * # NOQA
28
- from numbers_parser.exceptions import * # NOQA
26
+ from numbers_parser.cell import *
27
+ from numbers_parser.document import Document
28
+ from numbers_parser.exceptions import *
29
29
 
30
30
  __version__ = importlib.metadata.version("numbers-parser")
31
31
 
@@ -53,12 +53,14 @@ def _get_version():
53
53
  def _check_installed_numbers_version():
54
54
  try:
55
55
  fp = open(os.path.join(_DEFAULT_NUMBERS_INSTALL_PATH, _VERSION_PLIST_PATH), "rb")
56
- except IOError:
56
+ except OSError:
57
57
  return None
58
58
  version_dict = plistlib.load(fp)
59
59
  installed_version = version_dict["CFBundleShortVersionString"]
60
60
  if installed_version not in _SUPPORTED_NUMBERS_VERSIONS:
61
- warnings.warn(f"Numbers version {installed_version} not tested with this version")
61
+ warnings.warn(
62
+ f"Numbers version {installed_version} not tested with this version", stacklevel=2
63
+ )
62
64
  fp.close()
63
65
  return installed_version
64
66
 
@@ -5,9 +5,8 @@ import sys
5
5
 
6
6
  import sigfig
7
7
 
8
- from numbers_parser import Document, ErrorCell, FileFormatError, NumberCell, FileError
8
+ from numbers_parser import Document, ErrorCell, FileError, FileFormatError, NumberCell, _get_version
9
9
  from numbers_parser import __name__ as numbers_parser_name
10
- from numbers_parser import _get_version
11
10
  from numbers_parser.constants import MAX_SIGNIFICANT_DIGITS
12
11
  from numbers_parser.experimental import _enable_experimental_features
13
12
 
@@ -1,33 +1,31 @@
1
- import os
1
+ import contextlib
2
2
  import json
3
3
  import logging
4
- import regex
4
+ import os
5
5
  import sys
6
-
7
- from array import array
8
6
  from argparse import ArgumentParser
7
+ from array import array
9
8
  from base64 import b64decode
10
9
  from binascii import hexlify
10
+
11
+ import regex
11
12
  from compact_json import Formatter
12
13
 
13
- from numbers_parser.file import read_numbers_file
14
- from numbers_parser import _get_version
15
14
  from numbers_parser import __name__ as numbers_parser_name
15
+ from numbers_parser import _get_version
16
+ from numbers_parser.exceptions import FileError, FileFormatError, UnsupportedError
17
+ from numbers_parser.file import read_numbers_file
16
18
  from numbers_parser.iwafile import IWAFile
17
- from numbers_parser.exceptions import FileFormatError, UnsupportedError, FileError
18
19
  from numbers_parser.numbers_uuid import NumbersUUID
19
20
 
20
-
21
21
  logger = logging.getLogger(numbers_parser_name)
22
22
 
23
23
 
24
24
  def ensure_directory_exists(prefix, path):
25
25
  """Ensure that a path's directory exists."""
26
26
  parts = os.path.split(path)
27
- try:
27
+ with contextlib.suppress(OSError):
28
28
  os.makedirs(os.path.join(*([prefix] + list(parts[:-1]))))
29
- except OSError:
30
- pass
31
29
 
32
30
 
33
31
  def prettify_uuids(obj):
@@ -54,18 +52,18 @@ def prettify_uuids(obj):
54
52
  def prettify_cell_storage(obj):
55
53
  if isinstance(obj, dict):
56
54
  for k, v in obj.items():
57
- if isinstance(v, dict) or isinstance(v, list):
55
+ if isinstance(v, (dict, list)):
58
56
  prettify_cell_storage(v)
59
- elif k == "cell_storage_buffer" or k == "cell_storage_buffer_pre_bnc":
57
+ elif k in ["cell_storage_buffer", "cell_storage_buffer_pre_bnc"]:
60
58
  obj[k] = str(hexlify(b64decode(obj[k]), sep=":"))
61
59
  obj[k] = obj[k].replace("b'", "").replace("'", "")
62
- elif k == "cell_offsets" or k == "cell_offsets_pre_bnc":
60
+ elif k in ["cell_offsets", k == "cell_offsets_pre_bnc"]:
63
61
  offsets = array("h", b64decode(obj[k])).tolist()
64
62
  obj[k] = ",".join([str(x) for x in offsets])
65
63
  obj[k] = regex.sub(r"(?:,-1)+$", ",[...]", obj[k])
66
64
  else: # list
67
65
  for v in obj:
68
- if isinstance(v, dict) or isinstance(v, list):
66
+ if isinstance(v, (dict, list)):
69
67
  prettify_cell_storage(v)
70
68
 
71
69
 
@@ -137,7 +135,7 @@ def main():
137
135
  try:
138
136
  read_numbers_file(
139
137
  document,
140
- file_handler=lambda filename, blob: process_file(
138
+ file_handler=lambda filename, blob, output_dir=output_dir: process_file(
141
139
  filename, blob, output_dir, args
142
140
  ),
143
141
  )
numbers_parser/bullets.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from roman import toRoman
2
- from numbers_parser.generated.TSWPArchives_pb2 import ListStyleArchive
3
2
 
3
+ from numbers_parser.generated.TSWPArchives_pb2 import ListStyleArchive
4
4
 
5
5
  BULLET_PREFIXES = {
6
6
  ListStyleArchive.kNumericDecimal: "",
numbers_parser/cell.py CHANGED
@@ -29,7 +29,7 @@ from numbers_parser.generated import TSTArchives_pb2 as TSTArchives
29
29
  from numbers_parser.generated.TSWPArchives_pb2 import (
30
30
  ParagraphStylePropertiesArchive as ParagraphStyle,
31
31
  )
32
- from numbers_parser.numbers_cache import cache, Cacheable
32
+ from numbers_parser.numbers_cache import Cacheable, cache
33
33
 
34
34
  __all__ = [
35
35
  "Alignment",
@@ -122,8 +122,7 @@ class Alignment(_Alignment):
122
122
  raise TypeError("invalid vertical alignment")
123
123
  vertical = VERTICAL_MAP[vertical]
124
124
 
125
- self = super(_Alignment, cls).__new__(cls, (horizontal, vertical))
126
- return self
125
+ return super(_Alignment, cls).__new__(cls, (horizontal, vertical))
127
126
 
128
127
 
129
128
  DEFAULT_ALIGNMENT_CLASS = Alignment(*DEFAULT_ALIGNMENT)
@@ -186,13 +185,11 @@ class Style:
186
185
 
187
186
  @classmethod
188
187
  def from_storage(cls, cell_storage: object, model: object):
189
- style = Style()
190
-
191
188
  if cell_storage.image_data is not None:
192
189
  bg_image = BackgroundImage(*cell_storage.image_data)
193
190
  else:
194
191
  bg_image = None
195
- style = Style(
192
+ return Style(
196
193
  alignment=model.cell_alignment(cell_storage),
197
194
  bg_image=bg_image,
198
195
  bg_color=model.cell_bg_color(cell_storage),
@@ -212,7 +209,6 @@ class Style:
212
209
  _text_style_obj_id=model.text_style_object_id(cell_storage),
213
210
  _cell_style_obj_id=model.cell_style_object_id(cell_storage),
214
211
  )
215
- return style
216
212
 
217
213
  def __post_init__(self):
218
214
  self.bg_color = rgb_color(self.bg_color)
@@ -229,7 +225,8 @@ class Style:
229
225
 
230
226
  def __setattr__(self, name: str, value: Any) -> None:
231
227
  """Detect changes to cell styles and flag the style for
232
- possible updates when saving the document"""
228
+ possible updates when saving the document.
229
+ """
233
230
  if name in ["bg_color", "font_color"]:
234
231
  value = rgb_color(value)
235
232
  if name == "alignment":
@@ -244,13 +241,13 @@ class Style:
244
241
 
245
242
 
246
243
  def rgb_color(color) -> RGB:
247
- """Raise a TypeError if a color is not a valid RGB value"""
244
+ """Raise a TypeError if a color is not a valid RGB value."""
248
245
  if color is None:
249
246
  return None
250
247
  if isinstance(color, RGB):
251
248
  return color
252
249
  if isinstance(color, tuple):
253
- if not (len(color) == 3 and all([isinstance(x, int) for x in color])):
250
+ if not (len(color) == 3 and all(isinstance(x, int) for x in color)):
254
251
  raise TypeError("RGB color must be an RGB or a tuple of 3 integers")
255
252
  return RGB(*color)
256
253
  elif isinstance(color, list):
@@ -259,13 +256,13 @@ def rgb_color(color) -> RGB:
259
256
 
260
257
 
261
258
  def alignment(value) -> Alignment:
262
- """Raise a TypeError if a alignment is not a valid"""
259
+ """Raise a TypeError if a alignment is not a valid."""
263
260
  if value is None:
264
261
  return Alignment()
265
262
  if isinstance(value, Alignment):
266
263
  return value
267
264
  if isinstance(value, tuple):
268
- if not (len(value) == 2 and all([isinstance(x, (int, str)) for x in value])):
265
+ if not (len(value) == 2 and all(isinstance(x, (int, str)) for x in value)):
269
266
  raise TypeError("Alignment must be an Alignment or a tuple of 2 integers/strings")
270
267
  return Alignment(*value)
271
268
  raise TypeError("Alignment must be an Alignment or a tuple of 2 integers/strings")
@@ -285,16 +282,20 @@ class Border:
285
282
  def __init__(
286
283
  self,
287
284
  width: float = DEFAULT_BORDER_WIDTH,
288
- color: RGB = RGB(*DEFAULT_BORDER_COLOR),
289
- style: BorderType = BorderType(BORDER_STYLE_MAP[DEFAULT_BORDER_STYLE]),
285
+ color: RGB = None,
286
+ style: BorderType = None,
290
287
  _order: int = 0,
291
288
  ):
292
289
  if not isinstance(width, float):
293
290
  raise TypeError("width must be a float number of points")
294
291
  self.width = width
295
292
 
293
+ if color is None:
294
+ color = RGB(*DEFAULT_BORDER_COLOR)
296
295
  self.color = rgb_color(color)
297
296
 
297
+ if style is None:
298
+ style = BorderType(BORDER_STYLE_MAP[DEFAULT_BORDER_STYLE])
298
299
  if isinstance(style, str):
299
300
  style = style.lower()
300
301
  if style not in BORDER_STYLE_MAP:
@@ -394,14 +395,14 @@ class CellBorder:
394
395
 
395
396
 
396
397
  class MergeReference:
397
- """Cell reference for cells eliminated by a merge"""
398
+ """Cell reference for cells eliminated by a merge."""
398
399
 
399
400
  def __init__(self, row_start: int, col_start: int, row_end: int, col_end: int):
400
401
  self.rect = (row_start, col_start, row_end, col_end)
401
402
 
402
403
 
403
404
  class MergeAnchor:
404
- """Cell reference for the merged cell"""
405
+ """Cell reference for the merged cell."""
405
406
 
406
407
  def __init__(self, size: Tuple):
407
408
  self.size = size
@@ -475,11 +476,12 @@ class Cell(Cacheable):
475
476
  warn(
476
477
  f"'{value}' rounded to {MAX_SIGNIFICANT_DIGITS} significant digits",
477
478
  RuntimeWarning,
479
+ stacklevel=2,
478
480
  )
479
481
  return NumberCell(row_num, col_num, rounded_value)
480
- elif isinstance(value, builtin_datetime) or isinstance(value, DateTime):
482
+ elif isinstance(value, (DateTime, builtin_datetime)):
481
483
  return DateCell(row_num, col_num, pendulum_instance(value))
482
- elif isinstance(value, builtin_timedelta) or isinstance(value, Duration):
484
+ elif isinstance(value, (Duration, builtin_timedelta)):
483
485
  return DurationCell(row_num, col_num, value)
484
486
  else:
485
487
  raise ValueError("Can't determine cell type from type " + type(value).__name__)
@@ -527,6 +529,7 @@ class Cell(Cacheable):
527
529
  "image_filename is deprecated and will be removed in the future. "
528
530
  + "Please use the style property",
529
531
  DeprecationWarning,
532
+ stacklevel=2,
530
533
  )
531
534
  if self.style is not None and self.style.bg_image is not None:
532
535
  return self.style.bg_image.filename
@@ -539,6 +542,7 @@ class Cell(Cacheable):
539
542
  "image_data is deprecated and will be removed in the future. "
540
543
  + "Please use the style property",
541
544
  DeprecationWarning,
545
+ stacklevel=2,
542
546
  )
543
547
  if self.style is not None and self.style.bg_image is not None:
544
548
  return self.style.bg_image.data
@@ -555,8 +559,7 @@ class Cell(Cacheable):
555
559
  def formula(self):
556
560
  if self._formula_key is not None:
557
561
  table_formulas = self._model.table_formulas(self._table_id)
558
- formula = table_formulas.formula(self._formula_key, self.row, self.col)
559
- return formula
562
+ return table_formulas.formula(self._formula_key, self.row, self.col)
560
563
  else:
561
564
  return None
562
565
 
@@ -580,7 +583,11 @@ class Cell(Cacheable):
580
583
 
581
584
  @style.setter
582
585
  def style(self, _):
583
- warn("cell style cannot be set; use Table.set_cell_style() instead", UnsupportedWarning)
586
+ warn(
587
+ "cell style cannot be set; use Table.set_cell_style() instead",
588
+ UnsupportedWarning,
589
+ stacklevel=2,
590
+ )
584
591
 
585
592
  @property
586
593
  def border(self):
@@ -592,6 +599,7 @@ class Cell(Cacheable):
592
599
  warn(
593
600
  "cell border values cannot be set; use Table.set_cell_border() instead",
594
601
  UnsupportedWarning,
602
+ stacklevel=2,
595
603
  )
596
604
 
597
605
 
@@ -718,8 +726,7 @@ range_parts = re.compile(r"(\$?)([A-Z]{1,3})(\$?)(\d+)")
718
726
 
719
727
 
720
728
  def xl_cell_to_rowcol(cell_str: str) -> tuple:
721
- """
722
- Convert a cell reference in A1 notation to a zero indexed row and column.
729
+ """Convert a cell reference in A1 notation to a zero indexed row and column.
723
730
  Args:
724
731
  cell_str: A1 style string.
725
732
  Returns:
@@ -750,8 +757,7 @@ def xl_cell_to_rowcol(cell_str: str) -> tuple:
750
757
 
751
758
 
752
759
  def xl_range(first_row, first_col, last_row, last_col):
753
- """
754
- Convert zero indexed row and col cell references to a A1:B1 range string.
760
+ """Convert zero indexed row and col cell references to a A1:B1 range string.
755
761
  Args:
756
762
  first_row: The first cell row. Int.
757
763
  first_col: The first cell column. Int.
@@ -770,8 +776,7 @@ def xl_range(first_row, first_col, last_row, last_col):
770
776
 
771
777
 
772
778
  def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False):
773
- """
774
- Convert a zero indexed row and column cell reference to a A1 style string.
779
+ """Convert a zero indexed row and column cell reference to a A1 style string.
775
780
  Args:
776
781
  row: The cell row. Int.
777
782
  col: The cell column. Int.
@@ -795,8 +800,7 @@ def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False):
795
800
 
796
801
 
797
802
  def xl_col_to_name(col, col_abs=False):
798
- """
799
- Convert a zero indexed column cell reference to a string.
803
+ """Convert a zero indexed column cell reference to a string.
800
804
  Args:
801
805
  col: The cell column. Int.
802
806
  col_abs: Optional flag to make the column absolute. Bool.
@@ -27,7 +27,7 @@ from numbers_parser.constants import (
27
27
  )
28
28
  from numbers_parser.exceptions import UnsupportedError, UnsupportedWarning
29
29
  from numbers_parser.generated import TSTArchives_pb2 as TSTArchives
30
- from numbers_parser.numbers_cache import cache, Cacheable
30
+ from numbers_parser.numbers_cache import Cacheable, cache
31
31
  from numbers_parser.numbers_uuid import NumbersUUID
32
32
 
33
33
  logger = logging.getLogger(numbers_parser_name)
@@ -111,7 +111,9 @@ class CellStorage(Cacheable):
111
111
  )
112
112
 
113
113
  # @profile
114
- def __init__(self, model: object, table_id: int, buffer, row_num, col_num): # noqa: C901
114
+ def __init__( # noqa: PLR0912, PLR0913, PLR0915
115
+ self, model: object, table_id: int, buffer, row_num, col_num
116
+ ):
115
117
  self.buffer = buffer
116
118
  self.model = model
117
119
  self.table_id = table_id
@@ -277,7 +279,7 @@ class CellStorage(Cacheable):
277
279
  @property
278
280
  @cache(num_args=0)
279
281
  def image_data(self) -> Tuple[bytes, str]:
280
- """Return the background image data for a cell or None if no image"""
282
+ """Return the background image data for a cell or None if no image."""
281
283
  if self.cell_style_id is None:
282
284
  return None
283
285
  style = self.model.table_style(self.table_id, self.cell_style_id)
@@ -286,10 +288,18 @@ class CellStorage(Cacheable):
286
288
 
287
289
  image_id = style.cell_properties.cell_fill.image.imagedata.identifier
288
290
  datas = self.model.objects[PACKAGE_ID].datas
289
- image_filename = [x.file_name for x in datas if x.identifier == image_id][0]
291
+ stored_filename = [x.file_name for x in datas if x.identifier == image_id][0]
292
+ preferred_filename = [x.preferred_file_name for x in datas if x.identifier == image_id][0]
290
293
  all_paths = self.model.objects.file_store.keys()
291
- image_pathname = [x for x in all_paths if x == f"Data/{image_filename}"][0]
292
- return (self.model.objects.file_store[image_pathname], image_filename)
294
+ image_pathnames = [x for x in all_paths if x == f"Data/{stored_filename}"]
295
+ if len(image_pathnames) == 0:
296
+ warn(
297
+ f"Cannot find file '{preferred_filename}' in Numbers archive",
298
+ RuntimeWarning,
299
+ stacklevel=3,
300
+ )
301
+ else:
302
+ return (self.model.objects.file_store[image_pathnames[0]], preferred_filename)
293
303
 
294
304
  def custom_format(self) -> str:
295
305
  if self.text_format_id is not None and self.type == CellType.TEXT:
@@ -341,14 +351,16 @@ class CellStorage(Cacheable):
341
351
  formatted_value = decode_date_format(custom_format_string, self.datetime)
342
352
  else:
343
353
  warn(
344
- f"Unexpected custom format type {custom_format.format_type}", UnsupportedWarning
354
+ f"Unexpected custom format type {custom_format.format_type}",
355
+ UnsupportedWarning,
356
+ stacklevel=3,
345
357
  )
346
358
  return ""
347
359
  else:
348
360
  formatted_value = decode_date_format(format.date_time_format, self.datetime)
349
361
  return formatted_value
350
362
 
351
- def duration_format(self) -> str: # noqa: C901
363
+ def duration_format(self) -> str:
352
364
  format = self.model.table_format(self.table_id, self.duration_format_id)
353
365
 
354
366
  duration_style = format.duration_style
@@ -433,7 +445,7 @@ def unpack_decimal128(buffer: bytearray) -> float:
433
445
 
434
446
 
435
447
  def days_occurred_in_month(value: datetime) -> str:
436
- """Return how many times the day of the datetime value has fallen in the month"""
448
+ """Return how many times the day of the datetime value has fallen in the month."""
437
449
  n_days = int((value - value.replace(day=1)).days / 7) + 1
438
450
  return str(n_days)
439
451
 
@@ -446,12 +458,12 @@ def decode_date_format_field(field: str, value: datetime) -> str:
446
458
  else:
447
459
  return value.strftime(s)
448
460
  else:
449
- warn(f"Unsupported field code '{field}'", UnsupportedWarning)
461
+ warn(f"Unsupported field code '{field}'", UnsupportedWarning, stacklevel=4)
450
462
  return ""
451
463
 
452
464
 
453
465
  def decode_date_format(format, value):
454
- """Parse a custom date format string and return a formatted datetime value"""
466
+ """Parse a custom date format string and return a formatted datetime value."""
455
467
  chars = [*format]
456
468
  index = 0
457
469
  in_string = False
@@ -498,8 +510,8 @@ def decode_date_format(format, value):
498
510
  return result
499
511
 
500
512
 
501
- def decode_text_format(format, value: str): # noqa: C901
502
- """Parse a custom date format string and return a formatted number value"""
513
+ def decode_text_format(format, value: str):
514
+ """Parse a custom date format string and return a formatted number value."""
503
515
  custom_format_string = format.custom_format_string
504
516
  return custom_format_string.replace("\ue421", value)
505
517
 
@@ -530,8 +542,8 @@ def expand_quotes(value: str) -> str:
530
542
  return formatted_value
531
543
 
532
544
 
533
- def decode_number_format(format, value, name): # noqa: C901
534
- """Parse a custom date format string and return a formatted number value"""
545
+ def decode_number_format(format, value, name): # noqa: PLR0912
546
+ """Parse a custom date format string and return a formatted number value."""
535
547
  custom_format_string = format.custom_format_string
536
548
  value *= format.scale_factor
537
549
  if "%" in custom_format_string and format.scale_factor == 1.0:
@@ -548,6 +560,7 @@ def decode_number_format(format, value, name): # noqa: C901
548
560
  warn(
549
561
  f"Can't parse format string '{custom_format_string}'; skipping",
550
562
  UnsupportedWarning,
563
+ stacklevel=1,
551
564
  )
552
565
  return custom_format_string
553
566
  format_spec = match.group(1)
@@ -596,10 +609,7 @@ def decode_number_format(format, value, name): # noqa: C901
596
609
  elif format.num_nonspace_integer_digits > 0:
597
610
  int_pad = CellPadding.ZERO
598
611
  if format.show_thousands_separator:
599
- if integer != 0:
600
- num_commas = int(math.floor(math.log10(integer)) / 3)
601
- else:
602
- num_commas = 0
612
+ num_commas = int(math.floor(math.log10(integer)) / 3) if integer != 0 else 0
603
613
  num_commas = max([num_commas, int((num_integers - 1) / 3)])
604
614
  int_width = num_integers + num_commas
605
615
  else:
@@ -653,11 +663,10 @@ def decode_number_format(format, value, name): # noqa: C901
653
663
  formatted_value = f"{integer:,}".rjust(int_width)
654
664
  else:
655
665
  formatted_value = str(integer).rjust(int_width)
666
+ elif format.show_thousands_separator:
667
+ formatted_value = f"{integer:,}"
656
668
  else:
657
- if format.show_thousands_separator:
658
- formatted_value = f"{integer:,}"
659
- else:
660
- formatted_value = str(integer)
669
+ formatted_value = str(integer)
661
670
 
662
671
  if num_decimals:
663
672
  if dec_pad == CellPadding.ZERO or (dec_pad == CellPadding.SPACE and num_integers == 0):
@@ -683,10 +692,7 @@ def format_decimal(value: float, format) -> str:
683
692
  value = -value
684
693
  else:
685
694
  accounting_style = False
686
- if format.show_thousands_separator:
687
- thousands = ","
688
- else:
689
- thousands = ""
695
+ thousands = "," if format.show_thousands_separator else ""
690
696
 
691
697
  if value.is_integer() and format.decimal_places >= DECIMAL_PLACES_AUTO:
692
698
  formatted_value = f"{int(value):{thousands}}"
@@ -710,7 +716,7 @@ def format_decimal(value: float, format) -> str:
710
716
 
711
717
 
712
718
  def float_to_fraction(value: float, denominator: int) -> str:
713
- """Convert a float to the nearest fraction and return as a string"""
719
+ """Convert a float to the nearest fraction and return as a string."""
714
720
  whole = int(value)
715
721
  numerator = round(denominator * (value - whole))
716
722
  if numerator == 0:
@@ -724,7 +730,8 @@ def float_to_fraction(value: float, denominator: int) -> str:
724
730
 
725
731
  def float_to_n_digit_fraction(value: float, max_digits: int) -> str:
726
732
  """Convert a float to a fraction of a maxinum number of digits
727
- and return as a string"""
733
+ and return as a string.
734
+ """
728
735
  max_denominator = 10**max_digits - 1
729
736
  (numerator, denominator) = (
730
737
  Fraction.from_float(value).limit_denominator(max_denominator).as_integer_ratio()
@@ -1,6 +1,6 @@
1
1
  import os
2
-
3
2
  from enum import IntEnum
3
+
4
4
  from pendulum import datetime
5
5
  from pkg_resources import resource_filename
6
6
 
@@ -8,7 +8,7 @@ from pkg_resources import resource_filename
8
8
  DEFAULT_DOCUMENT = resource_filename(__name__, os.path.join("data", "empty.numbers"))
9
9
  DEFAULT_COLUMN_COUNT = 8
10
10
  DEFAULT_COLUMN_WIDTH = 98.0
11
- DEFAULT_PRE_BNC_BYTES = "🤠".encode("utf-8") # Yes, really!
11
+ DEFAULT_PRE_BNC_BYTES = "🤠".encode() # Yes, really!
12
12
  DEFAULT_ROW_COUNT = 12
13
13
  DEFAULT_ROW_HEIGHT = 20.0
14
14
  DEFAULT_NUM_HEADERS = 1
@@ -1,8 +1,8 @@
1
1
  import math
2
2
 
3
- from numbers_parser.file import read_numbers_file
4
3
  from numbers_parser.constants import PACKAGE_ID
5
- from numbers_parser.iwafile import create_iwa_segment, copy_object_to_iwa_file, IWAFile
4
+ from numbers_parser.file import read_numbers_file
5
+ from numbers_parser.iwafile import IWAFile, copy_object_to_iwa_file, create_iwa_segment
6
6
 
7
7
 
8
8
  class ItemsList:
@@ -11,13 +11,13 @@ class ItemsList:
11
11
  self._items = [item_class(model, _) for _ in refs]
12
12
 
13
13
  def __getitem__(self, key: int):
14
- if type(key) == int:
14
+ if isinstance(key, int):
15
15
  if key < 0:
16
16
  key += len(self._items)
17
17
  if key >= len(self._items):
18
18
  raise IndexError(f"index {key} out of range")
19
19
  return self._items[key]
20
- elif type(key) == str:
20
+ elif isinstance(key, str):
21
21
  for item in self._items:
22
22
  if item.name == key:
23
23
  return item
@@ -54,7 +54,7 @@ class ObjectStore:
54
54
  self._max_id = math.ceil(self._max_id / 1000000) * 1000000
55
55
 
56
56
  def new_message_id(self):
57
- """Return the next available message ID for object creation"""
57
+ """Return the next available message ID for object creation."""
58
58
  self._max_id += 1
59
59
  self._objects[PACKAGE_ID].last_object_identifier = self._max_id
60
60
  return self._max_id
@@ -62,12 +62,10 @@ class ObjectStore:
62
62
  def create_object_from_dict(self, iwa_file: str, object_dict: dict, cls: object):
63
63
  """Create a new object and store the associated IWA segment. Return the
64
64
  message ID for the object and the newly created object. If the IWA
65
- file cannot be found, it will be created."""
65
+ file cannot be found, it will be created.
66
+ """
66
67
  paths = [k for k, v in self._file_store.items() if iwa_file in k]
67
- if len(paths) == 0:
68
- iwa_pathname = None
69
- else:
70
- iwa_pathname = paths[0]
68
+ iwa_pathname = None if len(paths) == 0 else paths[0]
71
69
 
72
70
  new_id = self.new_message_id()
73
71
  iwa_segment = create_iwa_segment(new_id, cls, object_dict)
@@ -85,8 +83,9 @@ class ObjectStore:
85
83
 
86
84
  def update_object_file_store(self):
87
85
  """Copy the protobuf messages from any updated object to the cached
88
- version in the file store so this can be saved to a new document"""
89
- for obj_id in self._objects.keys():
86
+ version in the file store so this can be saved to a new document.
87
+ """
88
+ for obj_id in self._objects:
90
89
  copy_object_to_iwa_file(
91
90
  self._file_store[self._object_to_filename_map[obj_id]],
92
91
  self._objects[obj_id],
@@ -112,5 +111,4 @@ class ObjectStore:
112
111
 
113
112
  # Don't cache: new tables and sheets can be added at runtime
114
113
  def find_refs(self, ref_name) -> list:
115
- refs = [k for k, v in self._objects.items() if type(v).__name__ == ref_name]
116
- return refs
114
+ return [k for k, v in self._objects.items() if type(v).__name__ == ref_name]