Passed
Pull Request — master (#114)
by
unknown
04:20
created

benedict.dicts.keypath.keypath_util.parse_keys()   A

Complexity

Conditions 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 3
nop 2
1
# -*- coding: utf-8 -*-
2
3
from benedict.core import traverse
4
from benedict.utils import type_util
5
6
import re
7
8
9
KEY_INDEX_RE = r"(?:\[[\'\"]*(\-?[\d]+)[\'\"]*\]){1}$"
10
KEY_WILDCARD_RE = r"(?:\[[\'\"]*(\-?[\*]+)[\'\"]*\]){1}$"
11
12
13
def check_keys(d, separator):
14
    """
15
    Check if dict keys contain keypath separator.
16
    """
17
    if not type_util.is_dict(d) or not separator:
18
        return
19
20
    def check_key(parent, key, value):
21
        if key and type_util.is_string(key) and separator in key:
22
            raise ValueError(
23
                f"Key should not contain keypath separator '{separator}', found: '{key}'."
24
            )
25
26
    traverse(d, check_key)
27
28
29
def parse_keys(keypath, separator):
30
    """
31
    Parse keys from keylist or keypath using the given separator.
32
    """
33
    if type_util.is_list_or_tuple(keypath):
34
        keys = []
35
        for key in keypath:
36
            keys += parse_keys(key, separator)
37
        return keys
38
    return _split_keys_and_indexes(keypath, separator)
39
40
41
def _split_key_indexes(key):
42
    """
43
    Splits key indexes:
44
    eg. 'item[0][1]' -> ['item', 0, 1].
45
    """
46
    if "[" in key and key.endswith("]"):
47
        keys = []
48
        while True:
49
            index_matches = re.findall(KEY_INDEX_RE, key)
50
            if index_matches:
51
                key = re.sub(KEY_INDEX_RE, "", key)
52
                index = int(index_matches[0])
53
                keys.insert(0, index)
54
                continue
55
            index_matches = re.findall(KEY_WILDCARD_RE, key)
56
            if bool(re.search(KEY_WILDCARD_RE, key)):
57
                key = re.sub(KEY_WILDCARD_RE, "", key)
58
                index = index_matches[0]
59
                keys.insert(0, index)
60
                continue
61
            keys.insert(0, key)
62
            break
63
        return keys
64
    return [key]
65
66
67
def _split_keys(keypath, separator):
68
    """
69
    Splits keys using the given separator:
70
    eg. 'item.subitem[1]' -> ['item', 'subitem[1]'].
71
    """
72
    if separator:
73
        return keypath.split(separator)
74
    return [keypath]
75
76
77
def _split_keys_and_indexes(keypath, separator):
78
    """
79
    Splits keys and indexes using the given separator:
80
    eg. 'item[0].subitem[1]' -> ['item', 0, 'subitem', 1].
81
    """
82
    if type_util.is_string(keypath):
83
        keys1 = _split_keys(keypath, separator)
84
        keys2 = []
85
        for key in keys1:
86
            keys2 += _split_key_indexes(key)
87
        return keys2
88
    return [keypath]
89