tksheet 7.4.5__py3-none-any.whl → 7.4.7__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.
tksheet/functions.py CHANGED
@@ -10,7 +10,7 @@ from collections import deque
10
10
  from collections.abc import Callable, Generator, Hashable, Iterable, Iterator, Sequence
11
11
  from difflib import SequenceMatcher
12
12
  from itertools import islice, repeat
13
- from typing import Literal
13
+ from typing import Any, Literal
14
14
 
15
15
  from .colors import color_map
16
16
  from .constants import align_value_error, symbols_set
@@ -20,6 +20,7 @@ from .tksheet_types import AnyIter
20
20
 
21
21
  unpickle_obj = pickle.loads
22
22
  lines_re = re.compile(r"[^\n]+")
23
+ ORD_A = ord("A")
23
24
 
24
25
 
25
26
  def wrap_text(
@@ -202,7 +203,7 @@ def recursive_bind(widget: tk.Misc, event: str, callback: Callable) -> None:
202
203
  recursive_bind(child, event, callback)
203
204
 
204
205
 
205
- def tksheet_type_error(kwarg: str, valid_types: list[str], not_type: object) -> str:
206
+ def tksheet_type_error(kwarg: str, valid_types: list[str], not_type: Any) -> str:
206
207
  valid_types = ", ".join(f"{type_}" for type_ in valid_types)
207
208
  return f"Argument '{kwarg}' must be one of the following types: {valid_types}, not {type(not_type)}."
208
209
 
@@ -213,7 +214,7 @@ def new_tk_event(keysym: str) -> tk.Event:
213
214
  return event
214
215
 
215
216
 
216
- def event_has_char_key(event: object) -> bool:
217
+ def event_has_char_key(event: Any) -> bool:
217
218
  return (
218
219
  event and hasattr(event, "char") and (event.char.isalpha() or event.char.isdigit() or event.char in symbols_set)
219
220
  )
@@ -232,7 +233,7 @@ def event_opens_dropdown_or_checkbox(event=None) -> bool:
232
233
  )
233
234
 
234
235
 
235
- def dropdown_search_function(search_for: str, data: Iterable[object]) -> None | int:
236
+ def dropdown_search_function(search_for: str, data: Iterable[Any]) -> None | int:
236
237
  search_for = search_for.lower()
237
238
  search_len = len(search_for)
238
239
  if not search_len:
@@ -281,16 +282,16 @@ def float_to_int(f: int | float) -> int | float:
281
282
 
282
283
  def event_dict(
283
284
  name: str = None,
284
- sheet: object = None,
285
+ sheet: Any = None,
285
286
  widget: tk.Canvas | None = None,
286
287
  boxes: None | dict | tuple = None,
287
288
  cells_table: None | dict = None,
288
289
  cells_header: None | dict = None,
289
290
  cells_index: None | dict = None,
290
291
  selected: None | tuple = None,
291
- data: object = None,
292
+ data: Any = None,
292
293
  key: None | str = None,
293
- value: object = None,
294
+ value: Any = None,
294
295
  loc: None | int | tuple[int] = None,
295
296
  row: None | int = None,
296
297
  column: None | int = None,
@@ -366,7 +367,8 @@ def stored_event_dict(d: DotDict) -> DotDict:
366
367
  def len_to_idx(n: int) -> int:
367
368
  if n < 1:
368
369
  return 0
369
- return n - 1
370
+ else:
371
+ return n - 1
370
372
 
371
373
 
372
374
  def b_index(sorted_seq: Sequence[int], num_to_index: int) -> int:
@@ -376,13 +378,15 @@ def b_index(sorted_seq: Sequence[int], num_to_index: int) -> int:
376
378
  """
377
379
  if (idx := bisect_left(sorted_seq, num_to_index)) == len(sorted_seq) or sorted_seq[idx] != num_to_index:
378
380
  raise ValueError(f"{num_to_index} is not in Sequence")
379
- return idx
381
+ else:
382
+ return idx
380
383
 
381
384
 
382
385
  def try_b_index(sorted_seq: Sequence[int], num_to_index: int) -> int | None:
383
386
  if (idx := bisect_left(sorted_seq, num_to_index)) == len(sorted_seq) or sorted_seq[idx] != num_to_index:
384
387
  return None
385
- return idx
388
+ else:
389
+ return idx
386
390
 
387
391
 
388
392
  def bisect_in(sorted_seq: Sequence[int], num: int) -> bool:
@@ -399,27 +403,20 @@ def push_n(num: int, sorted_seq: Sequence[int]) -> int:
399
403
  if num < sorted_seq[0]:
400
404
  return num
401
405
  else:
402
- if (hi := len(sorted_seq)) < 5:
403
- for e in sorted_seq:
404
- if num >= e:
405
- num += 1
406
- else:
407
- return num
408
- return num
409
- else:
410
- lo = 0
411
- while lo < hi:
412
- mid = (lo + hi) // 2
413
- if sorted_seq[mid] < num + mid + 1:
414
- lo = mid + 1
415
- else:
416
- hi = mid
417
- return num + lo
406
+ hi = len(sorted_seq)
407
+ lo = 0
408
+ while lo < hi:
409
+ mid = (lo + hi) // 2
410
+ if sorted_seq[mid] < num + mid + 1:
411
+ lo = mid + 1
412
+ else:
413
+ hi = mid
414
+ return num + lo
418
415
 
419
416
 
420
417
  def get_dropdown_kwargs(
421
- values: list[object] | None = None,
422
- set_value: object = None,
418
+ values: list[Any] | None = None,
419
+ set_value: Any = None,
423
420
  state: str = "normal",
424
421
  redraw: bool = True,
425
422
  selection_function: Callable | None = None,
@@ -477,7 +474,7 @@ def get_checkbox_dict(**kwargs) -> dict:
477
474
  }
478
475
 
479
476
 
480
- def is_iterable(o: object) -> bool:
477
+ def is_iterable(o: Any) -> bool:
481
478
  if isinstance(o, str):
482
479
  return False
483
480
  try:
@@ -499,7 +496,7 @@ def int_x_tuple(i: AnyIter[int] | int) -> tuple[int]:
499
496
  return tuple(i)
500
497
 
501
498
 
502
- def unpack(t: tuple[object] | tuple[AnyIter[object]]) -> tuple[object]:
499
+ def unpack(t: tuple[Any] | tuple[AnyIter[Any]]) -> tuple[Any]:
503
500
  if not len(t):
504
501
  return t
505
502
  if is_iterable(t[0]) and len(t) == 1:
@@ -507,11 +504,11 @@ def unpack(t: tuple[object] | tuple[AnyIter[object]]) -> tuple[object]:
507
504
  return t
508
505
 
509
506
 
510
- def is_type_int(o: object) -> bool:
507
+ def is_type_int(o: Any) -> bool:
511
508
  return isinstance(o, int) and not isinstance(o, bool)
512
509
 
513
510
 
514
- def force_bool(o: object) -> bool:
511
+ def force_bool(o: Any) -> bool:
515
512
  try:
516
513
  return to_bool(o)
517
514
  except Exception:
@@ -521,9 +518,8 @@ def force_bool(o: object) -> bool:
521
518
  def alpha2idx(a: str) -> int | None:
522
519
  try:
523
520
  n = 0
524
- orda = ord("A")
525
521
  for c in a.upper():
526
- n = n * 26 + ord(c) - orda + 1
522
+ n = n * 26 + ord(c) - ORD_A + 1
527
523
  return n - 1
528
524
  except Exception:
529
525
  return None
@@ -702,8 +698,8 @@ def cell_right_within_box(
702
698
 
703
699
 
704
700
  def get_last(
705
- it: AnyIter[object],
706
- ) -> object:
701
+ it: AnyIter[Any],
702
+ ) -> Any:
707
703
  if hasattr(it, "__reversed__"):
708
704
  try:
709
705
  return next(reversed(it))
@@ -716,7 +712,7 @@ def get_last(
716
712
  return None
717
713
 
718
714
 
719
- def index_exists(seq: Sequence[object], index: int) -> bool:
715
+ def index_exists(seq: Sequence[Any], index: int) -> bool:
720
716
  try:
721
717
  seq[index]
722
718
  return True
@@ -733,10 +729,10 @@ def add_to_displayed(displayed: list[int], to_add: Iterable[int]) -> list[int]:
733
729
 
734
730
 
735
731
  def move_elements_by_mapping(
736
- seq: list[object],
732
+ seq: list[Any],
737
733
  new_idxs: dict[int, int],
738
734
  old_idxs: dict[int, int] | None = None,
739
- ) -> list[object]:
735
+ ) -> list[Any]:
740
736
  # move elements of a list around
741
737
  # displacing other elements based on mapping
742
738
  # new_idxs = {old index: new index, ...}
@@ -748,10 +744,10 @@ def move_elements_by_mapping(
748
744
 
749
745
 
750
746
  def move_elements_by_mapping_gen(
751
- seq: list[object],
747
+ seq: list[Any],
752
748
  new_idxs: dict[int, int],
753
749
  old_idxs: dict[int, int] | None = None,
754
- ) -> Generator[object]:
750
+ ) -> Generator[Any]:
755
751
  if old_idxs is None:
756
752
  old_idxs = dict(zip(new_idxs.values(), new_idxs))
757
753
  remaining_values = (e for i, e in enumerate(seq) if i not in new_idxs)
@@ -759,10 +755,10 @@ def move_elements_by_mapping_gen(
759
755
 
760
756
 
761
757
  def move_elements_to(
762
- seq: list[object],
758
+ seq: list[Any],
763
759
  move_to: int,
764
760
  to_move: list[int],
765
- ) -> list[object]:
761
+ ) -> list[Any]:
766
762
  return move_elements_by_mapping(
767
763
  seq,
768
764
  *get_new_indexes(
@@ -791,14 +787,14 @@ def get_new_indexes(
791
787
 
792
788
 
793
789
  def insert_items(
794
- seq: list[object],
795
- to_insert: dict[int, object],
790
+ seq: list[Any],
791
+ to_insert: dict[int, Any],
796
792
  seq_len_func: Callable | None = None,
797
- ) -> list[object]:
793
+ ) -> list[Any]:
798
794
  """
799
- seq: list[object]
795
+ seq: list[Any]
800
796
  to_insert: keys are ints sorted in reverse, representing list indexes to insert items.
801
- Values are any object, e.g. {1: 200, 0: 200}
797
+ Values are any, e.g. {1: 200, 0: 200}
802
798
  """
803
799
  if to_insert:
804
800
  if seq_len_func and next(iter(to_insert)) >= len(seq) + len(to_insert):
@@ -809,11 +805,11 @@ def insert_items(
809
805
 
810
806
 
811
807
  def del_placeholder_dict_key(
812
- d: dict[Hashable, object],
808
+ d: dict[Hashable, Any],
813
809
  k: Hashable,
814
- v: object,
810
+ v: Any,
815
811
  p: tuple = (),
816
- ) -> dict[Hashable, object]:
812
+ ) -> dict[Hashable, Any]:
817
813
  if p in d:
818
814
  del d[p]
819
815
  d[k] = v
@@ -994,7 +990,7 @@ def is_last_cell(
994
990
  return row == end_row - 1 and col == end_col - 1
995
991
 
996
992
 
997
- def zip_fill_2nd_value(x: AnyIter[object], o: object) -> Generator[object, object]:
993
+ def zip_fill_2nd_value(x: AnyIter[Any], o: Any) -> Generator[Any, Any]:
998
994
  return zip(x, repeat(o))
999
995
 
1000
996
 
@@ -1011,7 +1007,7 @@ def str_to_int(s: str) -> int | None:
1011
1007
 
1012
1008
  def gen_formatted(
1013
1009
  options: dict,
1014
- formatter: object = None,
1010
+ formatter: Any = None,
1015
1011
  ) -> Generator[tuple[int, int]] | Generator[int]:
1016
1012
  if formatter is None:
1017
1013
  return (k for k, dct in options.items() if "format" in dct)
@@ -1060,7 +1056,7 @@ def span_dict(
1060
1056
  convert: Callable | None = None,
1061
1057
  undo: bool = False,
1062
1058
  emit_event: bool = False,
1063
- widget: object = None,
1059
+ widget: Any = None,
1064
1060
  ) -> Span:
1065
1061
  d: Span = Span(
1066
1062
  from_r=from_r,
@@ -1087,7 +1083,7 @@ def span_dict(
1087
1083
 
1088
1084
 
1089
1085
  def coords_to_span(
1090
- widget: object,
1086
+ widget: Any,
1091
1087
  from_r: int | None = None,
1092
1088
  from_c: int | None = None,
1093
1089
  upto_r: int | None = None,
@@ -1112,6 +1108,37 @@ def coords_to_span(
1112
1108
  )
1113
1109
 
1114
1110
 
1111
+ PATTERN_ROW = re.compile(r"^(\d+)$") # "1"
1112
+ PATTERN_COL = re.compile(r"^([A-Z]+)$") # "A"
1113
+ PATTERN_CELL = re.compile(r"^([A-Z]+)(\d+)$") # "A1"
1114
+ PATTERN_RANGE = re.compile(r"^([A-Z]+)(\d+):([A-Z]+)(\d+)$") # "A1:B2"
1115
+ PATTERN_ROW_RANGE = re.compile(r"^(\d+):(\d+)$") # "1:2"
1116
+ PATTERN_ROW_START = re.compile(r"^(\d+):$") # "2:"
1117
+ PATTERN_ROW_END = re.compile(r"^:(\d+)$") # ":2"
1118
+ PATTERN_COL_RANGE = re.compile(r"^([A-Z]+):([A-Z]+)$") # "A:B"
1119
+ PATTERN_COL_START = re.compile(r"^([A-Z]+):$") # "A:"
1120
+ PATTERN_COL_END = re.compile(r"^:([A-Z]+)$") # ":B"
1121
+ PATTERN_CELL_START = re.compile(r"^([A-Z]+)(\d+):$") # "A1:"
1122
+ PATTERN_CELL_END = re.compile(r"^:([A-Z]+)(\d+)$") # ":B1"
1123
+ PATTERN_CELL_COL = re.compile(r"^([A-Z]+)(\d+):([A-Z]+)$") # "A1:B"
1124
+ PATTERN_CELL_ROW = re.compile(r"^([A-Z]+)(\d+):(\d+)$") # "A1:2"
1125
+ PATTERN_ALL = re.compile(r"^:$") # ":"
1126
+
1127
+
1128
+ def span_a2i(a: str) -> int | None:
1129
+ n = 0
1130
+ for c in a:
1131
+ n = n * 26 + ord(c) - ORD_A + 1
1132
+ return n - 1
1133
+
1134
+
1135
+ def span_a2n(a: str) -> int | None:
1136
+ n = 0
1137
+ for c in a:
1138
+ n = n * 26 + ord(c) - ORD_A + 1
1139
+ return n
1140
+
1141
+
1115
1142
  def key_to_span(
1116
1143
  key: (
1117
1144
  str
@@ -1122,78 +1149,34 @@ def key_to_span(
1122
1149
  | Sequence[Sequence[int | None, int | None], Sequence[int | None, int | None]]
1123
1150
  ),
1124
1151
  spans: dict[str, Span],
1125
- widget: object = None,
1152
+ widget: Any = None,
1126
1153
  ) -> Span:
1154
+ """
1155
+ Convert various input types to a Span object representing a 2D range.
1156
+
1157
+ Args:
1158
+ key: The input to convert (str, int, slice, sequence, or None).
1159
+ spans: A dictionary of named spans (e.g., {"<name>": Span(...)}).
1160
+ widget: Optional widget context for span creation.
1161
+
1162
+ Returns:
1163
+ A Span object or an error message string if the key is invalid.
1164
+ """
1165
+ # Handle Span object directly
1127
1166
  if isinstance(key, Span):
1128
1167
  return key
1168
+
1169
+ # Handle None as full span
1129
1170
  elif key is None:
1130
- key = (None, None, None, None)
1171
+ return coords_to_span(widget=widget, from_r=None, from_c=None, upto_r=None, upto_c=None)
1172
+
1173
+ # Validate input type
1131
1174
  elif not isinstance(key, (str, int, slice, list, tuple)):
1132
- return f"Key type must be either str, int, list, tuple or slice, not '{type(key)}'."
1175
+ return f"Key type must be either str, int, list, tuple or slice, not '{type(key).__name__}'."
1176
+
1133
1177
  try:
1134
- if isinstance(key, (list, tuple)):
1135
- if isinstance(key[0], int) or key[0] is None:
1136
- if len(key) == 2:
1137
- """
1138
- (int | None, int | None) -
1139
- (0, 0) - row 0, column 0 - the first cell
1140
- (0, None) - row 0, all columns
1141
- (None, 0) - column 0, all rows
1142
- """
1143
- return span_dict(
1144
- from_r=key[0] if isinstance(key[0], int) else 0,
1145
- from_c=key[1] if isinstance(key[1], int) else 0,
1146
- upto_r=(key[0] + 1) if isinstance(key[0], int) else None,
1147
- upto_c=(key[1] + 1) if isinstance(key[1], int) else None,
1148
- widget=widget,
1149
- )
1150
-
1151
- elif len(key) == 4:
1152
- """
1153
- (int | None, int | None, int | None, int | None) -
1154
- (from row, from column, up to row, up to column)
1155
- """
1156
- return coords_to_span(
1157
- widget=widget,
1158
- from_r=key[0],
1159
- from_c=key[1],
1160
- upto_r=key[2],
1161
- upto_c=key[3],
1162
- )
1163
- # return span_dict(
1164
- # from_r=key[0] if isinstance(key[0], int) else 0,
1165
- # from_c=key[1] if isinstance(key[1], int) else 0,
1166
- # upto_r=key[2] if isinstance(key[2], int) else None,
1167
- # upto_c=key[3] if isinstance(key[3], int) else None,
1168
- # widget=widget,
1169
- # )
1170
-
1171
- elif isinstance(key[0], (list, tuple)):
1172
- """
1173
- ((int | None, int | None), (int | None, int | None))
1174
-
1175
- First Sequence is start row and column
1176
- Second Sequence is up to but not including row and column
1177
- """
1178
- return coords_to_span(
1179
- widget=widget,
1180
- from_r=key[0][0],
1181
- from_c=key[0][1],
1182
- upto_r=key[1][0],
1183
- upto_c=key[1][1],
1184
- )
1185
- # return span_dict(
1186
- # from_r=key[0][0] if isinstance(key[0][0], int) else 0,
1187
- # from_c=key[0][1] if isinstance(key[0][1], int) else 0,
1188
- # upto_r=key[1][0],
1189
- # upto_c=key[1][1],
1190
- # widget=widget,
1191
- # )
1192
-
1193
- elif isinstance(key, int):
1194
- """
1195
- [int] - Whole row at that index
1196
- """
1178
+ # Integer key: whole row
1179
+ if isinstance(key, int):
1197
1180
  return span_dict(
1198
1181
  from_r=key,
1199
1182
  from_c=None,
@@ -1202,29 +1185,8 @@ def key_to_span(
1202
1185
  widget=widget,
1203
1186
  )
1204
1187
 
1188
+ # Slice key: row range
1205
1189
  elif isinstance(key, slice):
1206
- """
1207
- [slice]
1208
- """
1209
- """
1210
- [:] - All rows
1211
- """
1212
- if key.start is None and key.stop is None:
1213
- """
1214
- [:]
1215
- """
1216
- return span_dict(
1217
- from_r=0,
1218
- from_c=None,
1219
- upto_r=None,
1220
- upto_c=None,
1221
- widget=widget,
1222
- )
1223
- """
1224
- [1:3] - Rows 1, 2
1225
- [:2] - Rows up to but not including 2
1226
- [2:] - Rows starting from and including 2
1227
- """
1228
1190
  start = 0 if key.start is None else key.start
1229
1191
  return span_dict(
1230
1192
  from_r=start,
@@ -1234,251 +1196,187 @@ def key_to_span(
1234
1196
  widget=widget,
1235
1197
  )
1236
1198
 
1199
+ # Sequence key: various span formats
1200
+ elif isinstance(key, (list, tuple)):
1201
+ if (
1202
+ len(key) == 2
1203
+ and (isinstance(key[0], int) or key[0] is None)
1204
+ and (isinstance(key[1], int) or key[1] is None)
1205
+ ):
1206
+ # Single cell or partial span: (row, col)
1207
+ r_int = isinstance(key[0], int)
1208
+ c_int = isinstance(key[1], int)
1209
+ return span_dict(
1210
+ from_r=key[0] if r_int else 0,
1211
+ from_c=key[1] if c_int else 0,
1212
+ upto_r=key[0] + 1 if r_int else None,
1213
+ upto_c=key[1] + 1 if c_int else None,
1214
+ widget=widget,
1215
+ )
1216
+ elif len(key) == 4:
1217
+ # Full span coordinates: (from_r, from_c, upto_r, upto_c)
1218
+ return coords_to_span(
1219
+ widget=widget,
1220
+ from_r=key[0],
1221
+ from_c=key[1],
1222
+ upto_r=key[2],
1223
+ upto_c=key[3],
1224
+ )
1225
+ elif len(key) == 2 and all(isinstance(k, (list, tuple)) for k in key):
1226
+ # Start and end points: ((from_r, from_c), (upto_r, upto_c))
1227
+ return coords_to_span(
1228
+ widget=widget,
1229
+ from_r=key[0][0],
1230
+ from_c=key[0][1],
1231
+ upto_r=key[1][0],
1232
+ upto_c=key[1][1],
1233
+ )
1234
+
1235
+ # String key: parse various span formats
1237
1236
  elif isinstance(key, str):
1238
1237
  if not key:
1239
- key = ":"
1240
-
1241
- if key.startswith("<") and key.endswith(">"):
1242
- if (key := key[1:-1]) in spans:
1243
- """
1244
- ["<name>"] - Surrounded by "<" ">" cells from a named range
1245
- """
1246
- return spans[key]
1247
- return f"'{key}' not in named spans."
1248
-
1249
- key = key.upper()
1250
-
1251
- if key.isdigit():
1252
- """
1253
- ["1"] - Row 0
1254
- """
1238
+ # Empty string treated as full span
1255
1239
  return span_dict(
1256
- from_r=int(key) - 1,
1240
+ from_r=0,
1257
1241
  from_c=None,
1258
- upto_r=int(key),
1242
+ upto_r=None,
1259
1243
  upto_c=None,
1260
1244
  widget=widget,
1261
1245
  )
1246
+ elif key.startswith("<") and key.endswith(">"):
1247
+ name = key[1:-1]
1248
+ return spans.get(name, f"'{name}' not in named spans.")
1249
+
1250
+ key = key.upper() # Case-insensitive parsing
1262
1251
 
1263
- if key.isalpha():
1264
- """
1265
- ["A"] - Column 0
1266
- """
1252
+ # Match string against precompiled patterns
1253
+ if m := PATTERN_ROW.match(key):
1254
+ return span_dict(
1255
+ from_r=int(m[1]) - 1,
1256
+ from_c=None,
1257
+ upto_r=int(m[1]),
1258
+ upto_c=None,
1259
+ widget=widget,
1260
+ )
1261
+ elif m := PATTERN_COL.match(key):
1267
1262
  return span_dict(
1268
1263
  from_r=None,
1269
- from_c=alpha2idx(key),
1264
+ from_c=span_a2i(m[1]),
1270
1265
  upto_r=None,
1271
- upto_c=alpha2idx(key) + 1,
1266
+ upto_c=span_a2n(m[1]),
1272
1267
  widget=widget,
1273
1268
  )
1274
-
1275
- splitk = key.split(":")
1276
- if len(splitk) > 2:
1277
- return f"'{key}' could not be converted to span."
1278
-
1279
- if len(splitk) == 1 and not splitk[0].isdigit() and not splitk[0].isalpha() and not splitk[0][0].isdigit():
1280
- """
1281
- ["A1"] - Cell (0, 0)
1282
- """
1283
- keys_digits = re.search(r"\d", splitk[0])
1284
- if keys_digits:
1285
- digits_start = keys_digits.start()
1286
- if not digits_start:
1287
- return f"'{key}' could not be converted to span."
1288
- if digits_start:
1289
- key_row = splitk[0][digits_start:]
1290
- key_column = splitk[0][:digits_start]
1291
- return span_dict(
1292
- from_r=int(key_row) - 1,
1293
- from_c=alpha2idx(key_column),
1294
- upto_r=int(key_row),
1295
- upto_c=alpha2idx(key_column) + 1,
1296
- widget=widget,
1297
- )
1298
-
1299
- if not splitk[0] and not splitk[1]:
1300
- """
1301
- [":"] - All rows
1302
- """
1269
+ elif m := PATTERN_CELL.match(key):
1270
+ c = span_a2i(m[1])
1271
+ r = int(m[2]) - 1
1303
1272
  return span_dict(
1304
- from_r=0,
1305
- from_c=None,
1306
- upto_r=None,
1307
- upto_c=None,
1273
+ from_r=r,
1274
+ from_c=c,
1275
+ upto_r=r + 1,
1276
+ upto_c=c + 1,
1308
1277
  widget=widget,
1309
1278
  )
1310
-
1311
- if splitk[0].isdigit() and not splitk[1]:
1312
- """
1313
- ["2:"] - Rows starting from and including 1
1314
- """
1279
+ elif m := PATTERN_RANGE.match(key):
1280
+ return span_dict(
1281
+ from_r=int(m[2]) - 1,
1282
+ from_c=span_a2i(m[1]),
1283
+ upto_r=int(m[4]),
1284
+ upto_c=span_a2n(m[3]),
1285
+ widget=widget,
1286
+ )
1287
+ elif m := PATTERN_ROW_RANGE.match(key):
1315
1288
  return span_dict(
1316
- from_r=int(splitk[0]) - 1,
1289
+ from_r=int(m[1]) - 1,
1317
1290
  from_c=None,
1318
- upto_r=None,
1291
+ upto_r=int(m[2]),
1319
1292
  upto_c=None,
1320
1293
  widget=widget,
1321
1294
  )
1322
-
1323
- if splitk[1].isdigit() and not splitk[0]:
1324
- """
1325
- [":2"] - Rows up to and including 1
1326
- """
1295
+ elif m := PATTERN_ROW_START.match(key):
1327
1296
  return span_dict(
1328
- from_r=0,
1297
+ from_r=int(m[1]) - 1,
1329
1298
  from_c=None,
1330
- upto_r=int(splitk[1]),
1299
+ upto_r=None,
1331
1300
  upto_c=None,
1332
1301
  widget=widget,
1333
1302
  )
1334
-
1335
- if splitk[0].isdigit() and splitk[1].isdigit():
1336
- """
1337
- ["1:2"] - Rows 0, 1
1338
- """
1303
+ elif m := PATTERN_ROW_END.match(key):
1339
1304
  return span_dict(
1340
- from_r=int(splitk[0]) - 1,
1305
+ from_r=0,
1341
1306
  from_c=None,
1342
- upto_r=int(splitk[1]),
1307
+ upto_r=int(m[1]),
1343
1308
  upto_c=None,
1344
1309
  widget=widget,
1345
1310
  )
1346
-
1347
- if splitk[0].isalpha() and not splitk[1]:
1348
- """
1349
- ["B:"] - Columns starting from and including 2
1350
- """
1311
+ elif m := PATTERN_COL_RANGE.match(key):
1351
1312
  return span_dict(
1352
1313
  from_r=None,
1353
- from_c=alpha2idx(splitk[0]),
1314
+ from_c=span_a2i(m[1]),
1354
1315
  upto_r=None,
1355
- upto_c=None,
1316
+ upto_c=span_a2n(m[2]),
1356
1317
  widget=widget,
1357
1318
  )
1358
-
1359
- if splitk[1].isalpha() and not splitk[0]:
1360
- """
1361
- [":B"] - Columns up to and including 2
1362
- """
1319
+ elif m := PATTERN_COL_START.match(key):
1363
1320
  return span_dict(
1364
1321
  from_r=None,
1365
- from_c=0,
1322
+ from_c=span_a2i(m[1]),
1366
1323
  upto_r=None,
1367
- upto_c=alpha2idx(splitk[1]) + 1,
1324
+ upto_c=None,
1368
1325
  widget=widget,
1369
1326
  )
1370
-
1371
- if splitk[0].isalpha() and splitk[1].isalpha():
1372
- """
1373
- ["A:B"] - Columns 0, 1
1374
- """
1327
+ elif m := PATTERN_COL_END.match(key):
1375
1328
  return span_dict(
1376
1329
  from_r=None,
1377
- from_c=alpha2idx(splitk[0]),
1330
+ from_c=0,
1378
1331
  upto_r=None,
1379
- upto_c=alpha2idx(splitk[1]) + 1,
1332
+ upto_c=span_a2n(m[1]),
1380
1333
  widget=widget,
1381
1334
  )
1382
-
1383
- m1 = re.search(r"\d", splitk[0])
1384
- m2 = re.search(r"\d", splitk[1])
1385
- m1start = m1.start() if m1 else None
1386
- m2start = m2.start() if m2 else None
1387
- if m1start and m2start:
1388
- """
1389
- ["A1:B1"] - Cells (0, 0), (0, 1)
1390
- """
1391
- c1 = splitk[0][:m1start]
1392
- r1 = splitk[0][m1start:]
1393
- c2 = splitk[1][:m2start]
1394
- r2 = splitk[1][m2start:]
1335
+ elif m := PATTERN_CELL_START.match(key):
1395
1336
  return span_dict(
1396
- from_r=int(r1) - 1,
1397
- from_c=alpha2idx(c1),
1398
- upto_r=int(r2),
1399
- upto_c=alpha2idx(c2) + 1,
1337
+ from_r=int(m[2]) - 1,
1338
+ from_c=span_a2i(m[1]),
1339
+ upto_r=None,
1340
+ upto_c=None,
1400
1341
  widget=widget,
1401
1342
  )
1402
-
1403
- if not splitk[0] and m2start:
1404
- """
1405
- [":B1"] - Cells (0, 0), (0, 1)
1406
- """
1407
- c2 = splitk[1][:m2start]
1408
- r2 = splitk[1][m2start:]
1343
+ elif m := PATTERN_CELL_END.match(key):
1409
1344
  return span_dict(
1410
1345
  from_r=0,
1411
1346
  from_c=0,
1412
- upto_r=int(r2),
1413
- upto_c=alpha2idx(c2) + 1,
1347
+ upto_r=int(m[2]),
1348
+ upto_c=span_a2n(m[1]),
1414
1349
  widget=widget,
1415
1350
  )
1416
-
1417
- if not splitk[1] and m1start:
1418
- """
1419
- ["A1:"] - Cells starting from and including (0, 0)
1420
- """
1421
- c1 = splitk[0][:m1start]
1422
- r1 = splitk[0][m1start:]
1351
+ elif m := PATTERN_CELL_COL.match(key):
1423
1352
  return span_dict(
1424
- from_r=int(r1) - 1,
1425
- from_c=alpha2idx(c1),
1353
+ from_r=int(m[2]) - 1,
1354
+ from_c=span_a2i(m[1]),
1426
1355
  upto_r=None,
1427
- upto_c=None,
1356
+ upto_c=span_a2n(m[3]),
1428
1357
  widget=widget,
1429
1358
  )
1430
-
1431
- if m1start and splitk[1].isalpha():
1432
- """
1433
- ["A1:B"] - All the cells starting from (0, 0)
1434
- expanding out to include column 1
1435
- but not including cells beyond column
1436
- 1 and expanding down to include all rows
1437
- A B C D
1438
- 1 x x
1439
- 2 x x
1440
- 3 x x
1441
- 4 x x
1442
- ...
1443
- """
1444
- c1 = splitk[0][:m1start]
1445
- r1 = splitk[0][m1start:]
1359
+ elif m := PATTERN_CELL_ROW.match(key):
1446
1360
  return span_dict(
1447
- from_r=int(r1) - 1,
1448
- from_c=alpha2idx(c1),
1449
- upto_r=None,
1450
- upto_c=alpha2idx(splitk[1]) + 1,
1361
+ from_r=int(m[2]) - 1,
1362
+ from_c=span_a2i(m[1]),
1363
+ upto_r=int(m[3]),
1364
+ upto_c=None,
1451
1365
  widget=widget,
1452
1366
  )
1453
-
1454
- if m1start and splitk[1].isdigit():
1455
- """
1456
- ["A1:2"] - All the cells starting from (0, 0)
1457
- expanding down to include row 1
1458
- but not including cells beyond row
1459
- 1 and expanding out to include all
1460
- columns
1461
- A B C D
1462
- 1 x x x x
1463
- 2 x x x x
1464
- 3
1465
- 4
1466
- ...
1467
- """
1468
- c1 = splitk[0][:m1start]
1469
- r1 = splitk[0][m1start:]
1367
+ elif PATTERN_ALL.match(key):
1470
1368
  return span_dict(
1471
- from_r=int(r1) - 1,
1472
- from_c=alpha2idx(c1),
1473
- upto_r=int(splitk[1]),
1369
+ from_r=0,
1370
+ from_c=None,
1371
+ upto_r=None,
1474
1372
  upto_c=None,
1475
1373
  widget=widget,
1476
1374
  )
1375
+ else:
1376
+ return f"'{key}' could not be converted to span."
1477
1377
 
1478
1378
  except ValueError as error:
1479
1379
  return f"Error, '{key}' could not be converted to span: {error}"
1480
- else:
1481
- return f"'{key}' could not be converted to span."
1482
1380
 
1483
1381
 
1484
1382
  def span_is_cell(span: Span) -> bool:
@@ -1623,7 +1521,7 @@ def add_to_options(
1623
1521
  options: dict,
1624
1522
  coords: int | tuple[int, int],
1625
1523
  key: str,
1626
- value: object,
1524
+ value: Any,
1627
1525
  ) -> dict:
1628
1526
  if coords not in options:
1629
1527
  options[coords] = {}
@@ -1694,14 +1592,14 @@ def mod_span(
1694
1592
  return to_set_to
1695
1593
 
1696
1594
 
1697
- def mod_span_widget(span: Span, widget: object) -> Span:
1595
+ def mod_span_widget(span: Span, widget: Any) -> Span:
1698
1596
  span.widget = widget
1699
1597
  return span
1700
1598
 
1701
1599
 
1702
1600
  def mod_event_val(
1703
1601
  event_data: EventDataDict,
1704
- val: object,
1602
+ val: Any,
1705
1603
  loc: Loc | None = None,
1706
1604
  row: int | None = None,
1707
1605
  column: int | None = None,