|
9 | 9 | from contextlib import contextmanager
|
10 | 10 | from copy import deepcopy, copy
|
11 | 11 | import itertools
|
| 12 | +from functools import reduce |
12 | 13 |
|
13 | 14 | from _plotly_utils.utils import (
|
14 | 15 | _natural_sort_strings,
|
15 | 16 | _get_int_type,
|
16 | 17 | split_multichar,
|
17 | 18 | split_string_positions,
|
18 | 19 | display_string_positions,
|
| 20 | + chomp_empty_strings, |
19 | 21 | )
|
20 | 22 | from _plotly_utils.exceptions import PlotlyKeyError
|
21 | 23 | from .optional_imports import get_module
|
@@ -63,40 +65,69 @@ def _str_to_dict_path_full(key_path_str):
|
63 | 65 | tuple[str | int]
|
64 | 66 | tuple [int]
|
65 | 67 | """
|
66 |
| - key_path2 = split_multichar([key_path_str], list(".[]")) |
67 |
| - # Split out underscore |
68 |
| - # e.g. ['foo', 'bar_baz', '1'] -> ['foo', 'bar', 'baz', '1'] |
69 |
| - key_path3 = [] |
70 |
| - underscore_props = BaseFigure._valid_underscore_properties |
71 |
| - |
72 |
| - def _make_hyphen_key(key): |
73 |
| - if "_" in key[1:]: |
74 |
| - # For valid properties that contain underscores (error_x) |
75 |
| - # replace the underscores with hyphens to protect them |
76 |
| - # from being split up |
77 |
| - for under_prop, hyphen_prop in underscore_props.items(): |
78 |
| - key = key.replace(under_prop, hyphen_prop) |
79 |
| - return key |
80 |
| - |
81 |
| - def _make_underscore_key(key): |
82 |
| - return key.replace("-", "_") |
83 |
| - |
84 |
| - key_path2b = map(_make_hyphen_key, key_path2) |
85 |
| - key_path2c = split_multichar(key_path2b, list("_")) |
86 |
| - key_path2d = list(map(_make_underscore_key, key_path2c)) |
87 |
| - all_elem_idcs = tuple(split_string_positions(list(key_path2d))) |
88 |
| - # remove empty strings, and indices pointing to them |
89 |
| - key_elem_pairs = list(filter(lambda t: len(t[1]), enumerate(key_path2d))) |
90 |
| - key_path3 = [x for _, x in key_elem_pairs] |
91 |
| - elem_idcs = [all_elem_idcs[i] for i, _ in key_elem_pairs] |
92 |
| - |
93 |
| - # Convert elements to ints if possible. |
94 |
| - # e.g. ['foo', 'bar', '0'] -> ['foo', 'bar', 0] |
95 |
| - for i in range(len(key_path3)): |
96 |
| - try: |
97 |
| - key_path3[i] = int(key_path3[i]) |
98 |
| - except ValueError as _: |
99 |
| - pass |
| 68 | + # skip all the parsing if the string is empty |
| 69 | + if len(key_path_str): |
| 70 | + # split string on ".[]" and filter out empty strings |
| 71 | + key_path2 = split_multichar([key_path_str], list(".[]")) |
| 72 | + # Split out underscore |
| 73 | + # e.g. ['foo', 'bar_baz', '1'] -> ['foo', 'bar', 'baz', '1'] |
| 74 | + key_path3 = [] |
| 75 | + underscore_props = BaseFigure._valid_underscore_properties |
| 76 | + |
| 77 | + def _make_hyphen_key(key): |
| 78 | + if "_" in key[1:]: |
| 79 | + # For valid properties that contain underscores (error_x) |
| 80 | + # replace the underscores with hyphens to protect them |
| 81 | + # from being split up |
| 82 | + for under_prop, hyphen_prop in underscore_props.items(): |
| 83 | + key = key.replace(under_prop, hyphen_prop) |
| 84 | + return key |
| 85 | + |
| 86 | + def _make_underscore_key(key): |
| 87 | + return key.replace("-", "_") |
| 88 | + |
| 89 | + key_path2b = list(map(_make_hyphen_key, key_path2)) |
| 90 | + # Here we want to split up each non-empty string in the list at |
| 91 | + # underscores and recombine the strings using chomp_empty_strings so |
| 92 | + # that leading, trailing and multiple _ will be preserved |
| 93 | + def _split_and_chomp(s): |
| 94 | + if not len(s): |
| 95 | + return s |
| 96 | + s_split = split_multichar([s], list("_")) |
| 97 | + # handle key paths like "a_path_", "_another_path", or |
| 98 | + # "yet__another_path" by joining extra "_" to the string to the left or |
| 99 | + # the empty string if at the beginning |
| 100 | + s_chomped = chomp_empty_strings(s_split, "_") |
| 101 | + return s_chomped |
| 102 | + |
| 103 | + # after running _split_and_chomp on key_path2b, it will be a list |
| 104 | + # containing strings and lists of strings; concatenate the sublists with |
| 105 | + # the list ("lift" the items out of the sublists) |
| 106 | + key_path2c = list( |
| 107 | + reduce( |
| 108 | + lambda x, y: x + y if type(y) == type(list()) else x + [y], |
| 109 | + map(_split_and_chomp, key_path2b), |
| 110 | + [], |
| 111 | + ) |
| 112 | + ) |
| 113 | + |
| 114 | + key_path2d = list(map(_make_underscore_key, key_path2c)) |
| 115 | + all_elem_idcs = tuple(split_string_positions(list(key_path2d))) |
| 116 | + # remove empty strings, and indices pointing to them |
| 117 | + key_elem_pairs = list(filter(lambda t: len(t[1]), enumerate(key_path2d))) |
| 118 | + key_path3 = [x for _, x in key_elem_pairs] |
| 119 | + elem_idcs = [all_elem_idcs[i] for i, _ in key_elem_pairs] |
| 120 | + |
| 121 | + # Convert elements to ints if possible. |
| 122 | + # e.g. ['foo', 'bar', '0'] -> ['foo', 'bar', 0] |
| 123 | + for i in range(len(key_path3)): |
| 124 | + try: |
| 125 | + key_path3[i] = int(key_path3[i]) |
| 126 | + except ValueError as _: |
| 127 | + pass |
| 128 | + else: |
| 129 | + key_path3 = [] |
| 130 | + elem_idcs = [] |
100 | 131 |
|
101 | 132 | return (tuple(key_path3), elem_idcs)
|
102 | 133 |
|
|
0 commit comments