|
1 | 1 | # Copyright (c) 2020 6WIND S.A. |
2 | 2 | # SPDX-License-Identifier: MIT |
3 | 3 |
|
| 4 | +import contextlib |
4 | 5 | import fnmatch |
5 | 6 | import re |
6 | 7 | from typing import Any, Dict, Iterator, List, Optional, Tuple, Union |
@@ -56,17 +57,28 @@ def xpath_split(xpath: str) -> Iterator[Tuple[str, str, List[Tuple[str, str]]]]: |
56 | 57 | while i < len(xpath) and xpath[i] == "[": |
57 | 58 | i += 1 # skip opening '[' |
58 | 59 | j = xpath.find("=", i) # find key name end |
59 | | - key_name = xpath[i:j] |
60 | | - quote = xpath[j + 1] # record opening quote character |
61 | | - j = i = j + 2 # skip '=' and opening quote |
62 | | - while True: |
63 | | - if xpath[j] == quote and xpath[j - 1] != "\\": |
64 | | - break |
65 | | - j += 1 |
66 | | - # replace escaped chars by their non-escape version |
67 | | - key_value = xpath[i:j].replace(f"\\{quote}", f"{quote}") |
68 | | - keys.append((key_name, key_value)) |
69 | | - i = j + 2 # skip closing quote and ']' |
| 60 | + |
| 61 | + if j != -1: # keyed specifier |
| 62 | + key_name = xpath[i:j] |
| 63 | + quote = xpath[j + 1] # record opening quote character |
| 64 | + j = i = j + 2 # skip '=' and opening quote |
| 65 | + while True: |
| 66 | + if xpath[j] == quote and xpath[j - 1] != "\\": |
| 67 | + break |
| 68 | + j += 1 |
| 69 | + # replace escaped chars by their non-escape version |
| 70 | + key_value = xpath[i:j].replace(f"\\{quote}", f"{quote}") |
| 71 | + keys.append((key_name, key_value)) |
| 72 | + i = j + 2 # skip closing quote and ']' |
| 73 | + else: # index specifier |
| 74 | + j = i |
| 75 | + while True: |
| 76 | + if xpath[j] == "]": |
| 77 | + break |
| 78 | + j += 1 |
| 79 | + key_value = xpath[i:j] |
| 80 | + keys.append(("", key_value)) |
| 81 | + i = j + 2 |
70 | 82 |
|
71 | 83 | yield prefix, name, keys |
72 | 84 |
|
@@ -134,6 +146,12 @@ def _list_find_key_index(keys: List[Tuple[str, str]], lst: List) -> int: |
134 | 146 | if py_to_yang(elem) == keys[0][1]: |
135 | 147 | return i |
136 | 148 |
|
| 149 | + elif keys[0][0] == "": |
| 150 | + # keys[0][1] is directly the index |
| 151 | + index = int(keys[0][1]) - 1 |
| 152 | + if len(lst) > index: |
| 153 | + return index |
| 154 | + |
137 | 155 | else: |
138 | 156 | for i, elem in enumerate(lst): |
139 | 157 | if not isinstance(elem, dict): |
@@ -410,32 +428,47 @@ def xpath_set( |
410 | 428 | lst.append(value) |
411 | 429 | return lst[key_val] |
412 | 430 |
|
413 | | - if isinstance(lst, list): |
414 | | - # regular python list, need to iterate over it |
415 | | - try: |
416 | | - i = _list_find_key_index(keys, lst) |
417 | | - # found |
418 | | - if force: |
419 | | - lst[i] = value |
420 | | - return lst[i] |
421 | | - except ValueError: |
422 | | - # not found |
423 | | - if after is None: |
424 | | - lst.append(value) |
425 | | - elif after == "": |
426 | | - lst.insert(0, value) |
427 | | - else: |
428 | | - if after[0] != "[": |
429 | | - after = "[.=%r]" % str(after) |
430 | | - _, _, after_keys = next(xpath_split("/*" + after)) |
431 | | - insert_index = _list_find_key_index(after_keys, lst) + 1 |
432 | | - if insert_index == len(lst): |
433 | | - lst.append(value) |
434 | | - else: |
435 | | - lst.insert(insert_index, value) |
436 | | - return value |
| 431 | + # regular python list from now |
| 432 | + if not isinstance(lst, list): |
| 433 | + raise TypeError("expected a list") |
| 434 | + |
| 435 | + with contextlib.suppress(ValueError): |
| 436 | + i = _list_find_key_index(keys, lst) |
| 437 | + # found |
| 438 | + if force: |
| 439 | + lst[i] = value |
| 440 | + return lst[i] |
| 441 | + |
| 442 | + # value not found; handle insertion based on 'after' |
| 443 | + if after is None: |
| 444 | + lst.append(value) |
| 445 | + return value |
| 446 | + |
| 447 | + if after == "": |
| 448 | + lst.insert(0, value) |
| 449 | + return value |
| 450 | + |
| 451 | + # first try to find the value in the leaf list |
| 452 | + try: |
| 453 | + _, _, after_keys = next( |
| 454 | + xpath_split(f"/*{after}" if after[0] == "[" else f"/*[.={after!r}]") |
| 455 | + ) |
| 456 | + insert_index = _list_find_key_index(after_keys, lst) + 1 |
| 457 | + except ValueError: |
| 458 | + # handle 'after' as numeric index |
| 459 | + if not after.isnumeric(): |
| 460 | + raise |
| 461 | + |
| 462 | + insert_index = int(after) |
| 463 | + if insert_index > len(lst): |
| 464 | + raise |
| 465 | + |
| 466 | + if insert_index == len(lst): |
| 467 | + lst.append(value) |
| 468 | + else: |
| 469 | + lst.insert(insert_index, value) |
437 | 470 |
|
438 | | - raise TypeError("expected a list") |
| 471 | + return value |
439 | 472 |
|
440 | 473 |
|
441 | 474 | # ------------------------------------------------------------------------------------- |
|
0 commit comments