Source code for dataclass_wizard.utils.object_path

from dataclasses import MISSING

from ..errors import ParseError


[docs] def safe_get(data, path, default=MISSING, raise_=True): current_data = data p = path # to avoid "unbound local variable" warnings try: for p in path: current_data = current_data[p] return current_data # IndexError - # raised when `data` is a `list`, and we access an index that is "out of bounds" # KeyError - # raised when `data` is a `dict`, and we access a key that is not present # AttributeError - # raised when `data` is an invalid type, such as a `None` except (IndexError, KeyError, AttributeError) as e: if raise_ and default is MISSING: raise _format_err(e, current_data, path, p) from None return default # TypeError - # raised when `data` is a `list`, but we try to use it like a `dict` except TypeError: e = TypeError('Invalid path') raise _format_err(e, current_data, path, p, True) from None
[docs] def v1_safe_get(data, path, raise_): current_data = data try: for p in path: current_data = current_data[p] return current_data # IndexError - # raised when `data` is a `list`, and we access an index that is "out of bounds" # KeyError - # raised when `data` is a `dict`, and we access a key that is not present # AttributeError - # raised when `data` is an invalid type, such as a `None` except (IndexError, KeyError, AttributeError) as e: if raise_: p = locals().get('p', path) # to suppress "unbound local variable" raise _format_err(e, current_data, path, p) from None return MISSING # TypeError - # raised when `data` is a `list`, but we try to use it like a `dict` except TypeError: e = TypeError('Invalid path') p = locals().get('p', path) # to suppress "unbound local variable" raise _format_err(e, current_data, path, p, True) from None
def _format_err(e, current_data, path, current_path, invalid_path=False): return ParseError( e, current_data, dict if invalid_path else None, path=' => '.join(repr(p) for p in path), current_path=repr(current_path), ) # What values are considered "truthy" when converting to a boolean type. # noinspection SpellCheckingInspection _TRUTHY_VALUES = frozenset(("True", "true")) # What values are considered "falsy" when converting to a boolean type. # noinspection SpellCheckingInspection _FALSY_VALUES = frozenset(("False", "false")) # Valid starting separators in our custom "object path", # for example `a.b[c].d.[-1]` has 5 start separators. _START_SEP = frozenset(('.', '['))
[docs] def split_object_path(_input): res = [] s = "" start_new = True in_literal = False parsed_string_literal = False in_braces = False escape_next_quote = False quote_char = None possible_number = False for c in _input: if c in _START_SEP: if in_literal: s += c else: if c == '.': # A period within braces [xxx] OR within a string "xxx", # should be captured. if in_braces: s += c continue in_braces = False else: in_braces = True start_new = True if s: if possible_number: possible_number = False try: num = int(s) res.append(num) except ValueError: try: num = float(s) res.append(num) except ValueError: res.append(s) elif parsed_string_literal: parsed_string_literal = False res.append(s) else: if s in _TRUTHY_VALUES: res.append(True) elif s in _FALSY_VALUES: res.append(False) else: res.append(s) s = "" elif c == '\\' and in_literal: escape_next_quote = True elif escape_next_quote: if c != quote_char: # It was not an escape character after all! s += '\\' # Capture escaped character s += c escape_next_quote = False elif c == quote_char: in_literal = False quote_char = None parsed_string_literal = True elif c in {'"', "'"} and start_new: start_new = False in_literal = True quote_char = c elif (c in {'+', '-'} or c.isdigit()) and start_new: start_new = False possible_number = True s += c elif start_new: start_new = False s += c elif c == ']': if in_literal: s += c else: in_braces = False else: s += c if s: if possible_number: try: num = int(s) res.append(num) except ValueError: try: num = float(s) res.append(num) except ValueError: res.append(s) elif parsed_string_literal: res.append(s) else: if s in _TRUTHY_VALUES: res.append(True) elif s in _FALSY_VALUES: res.append(False) else: res.append(s) return res