# Source code for dataclass_wizard.utils.object_path

    from dataclasses import MISSING

    from ..errors import ParseError

    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

    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, True) 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 v1_env_safe_get(data, first_key, path, raise_):
        from ..v1.type_conv import as_collection_v1

        current_data = data

        try:
            current_data = as_collection_v1(current_data[first_key])

            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_:
                path = [first_key] + list(path)
                p = locals().get('p', path)  # to suppress "unbound local variable"
                raise _format_err(e, current_data, path, p, True) 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')
            path = [first_key] + list(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, 'load',
            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(('.', '['))

    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