def _dotted_get(
        self, dotted_key, default=None, parent=None, cast=None, **kwargs
    ):
        """
        Perform dotted key lookups and keep track of where we are.
        :param key: The name of the setting value, will always be upper case
        :param default: In case of not found it will be returned
        :param parent: Is there a pre-loaded parent in a nested data?
        """
        # if parent is not traverseable raise error
        if parent and not hasattr(parent, "get"):
            raise AttributeError(
                f"cannot lookup {dotted_key!r} from {type(parent).__name__!r}"
            )

        split_key = dotted_key.split(".")
        name, keys = split_key[0], split_key[1:]
        result = self.get(name, default=default, parent=parent, **kwargs)

        # If we've reached the end, or parent key not found, then return result
        if not keys or result == default:
            if cast and cast in converters:
                return apply_converter(cast, result, box_settings=self)
            elif cast is True:
                return parse_conf_data(result, tomlfy=True, box_settings=self)
            return result

        # If we've still got key elements to traverse, let's do that.
        return self._dotted_get(
            ".".join(keys), default=default, parent=result, cast=cast, **kwargs
        )

        Perform dotted key lookups and keep track of where we are.
        :param key: The name of the setting value, will always be upper case
        :param default: In case of not found it will be returned
        :param parent: Is there a pre-loaded parent in a nested data?
        
dynaconf/base.py