# Buggy code
def __getattr__(self, name):
    """Allow getting keys from self.store using dot notation"""
    if self._wrapped is empty:
        self._setup()
    if name in self._wrapped._deleted:  # noqa
        raise AttributeError(
            f"Attribute {name} was deleted, or belongs to different env"
        )

    if name not in RESERVED_ATTRS:
        lowercase_mode = self._kwargs.get(
            "LOWERCASE_READ_FOR_DYNACONF",
            default_settings.LOWERCASE_READ_FOR_DYNACONF,
        )
        if lowercase_mode is True:
            name = name.upper()

    if (
        name.isupper()
        and (self._wrapped._fresh or name in self._wrapped.FRESH_VARS_FOR_DYNACONF)
        and name not in dir(default_settings)
    ):
        return self._wrapped.get_fresh(name)
    value = getattr(self._wrapped, name)
    if name not in RESERVED_ATTRS:
        return recursively_evaluate_lazy_format(value, self)
    return value


# Traceback (AttributeError)
Traceback (most recent call last):
File "app.py", line 11, in <module>
assert settings['s3_url'] == expected_value # fails
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/utils/functional.py", line 17, in inner
return func(self._wrapped, *args)
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/base.py", line 285, in __getitem__
value = self.get(item, default=empty)
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/base.py", line 419, in get
data = (parent or self.store).get(key, default)
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/utils/boxing.py", line 15, in evaluate
value = f(dynabox, item, *args, **kwargs)
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/utils/boxing.py", line 64, in get
return super(DynaBox, self).get(item, default, *args, **kwargs)
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/vendor/box/box.py", line 109, in get
return B[C]
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/utils/boxing.py", line 23, in evaluate
return recursively_evaluate_lazy_format(value, settings)
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/utils/__init__.py", line 355, in recursively_evaluate_lazy_format
value = value(settings)
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/utils/parse_conf.py", line 176, in __call__
return self.formatter(self.value, **self.context)
File "/home/user/dev/venv/lib/python3.7/site-packages/dynaconf/utils/parse_conf.py", line 138, in __call__
return self.function(value, **context)
AttributeError: 'Settings' object has no attribute 's3_protocol'

# Fixed code
def __getattr__(self, item):
    """This method is called only when the attribute does not exits
    usually on access to the direct `Settings` object instead of the
    `LazySettings`.
    All first level attributes are stored as UPPERCASE, so self.FOO
    will always be accessible, however in some cases `self.foo` might
    be acessible. This method routes this access to the underlying `_store`
    which handles casing and recursive access.
    """
    try:
        if (
            item.islower()
            and self._store.get("LOWERCASE_READ_FOR_DYNACONF", empty) is False
        ):
            raise KeyError
        value = self._store[item]
    except KeyError:
        raise AttributeError(f"Settings has no attribute '{item}'")
    else:
        return value