<commit_before>REQUIRED = object()
DEFAULTS = {
    'THEME': 'bootstrap-4',
    'PASSWORD_LEN': (6, 30),
    'LOGIN_REDIRECT': '/',
    'LOGOUT_REDIRECT': 'auth_login',
    'REGISTRATION_CONFIRMATION_REQUIRED': True,

    'ADMIN_EMAILS': [],
    'CSRF_SECRET': REQUIRED,
    'BACK_URL_QS_KEY': 'back_to',
    'SESSION_USER_KEY': 'user',
    'REQUEST_USER_KEY': 'user',

    'SESSION_FLASH_KEY': 'flash',
    'REQUEST_FLASH_INCOMING_KEY': 'flash_incoming',
    'REQUEST_FLASH_OUTGOING_KEY': 'flash_outgoing',
    'FLASH_QUEUE_LIMIT': 10,

    'VKONTAKTE_ID': None,
    'VKONTAKTE_SECRET': None,
    'GOOGLE_ID': None,
    'GOOGLE_SECRET': None,
    'FACEBOOK_ID': None,
    'FACEBOOK_SECRET': None,

    'SMTP_SENDER': None,
    'SMTP_HOST': REQUIRED,
    'SMTP_PORT': REQUIRED,
    'SMTP_TLS': True,
    'SMTP_USERNAME': None,
    'SMTP_PASSWORD': None,

    # email confirmation links lifetime in days
    'REGISTRATION_CONFIRMATION_LIFETIME': 5,
    'RESET_PASSWORD_CONFIRMATION_LIFETIME': 5,
    'CHANGE_EMAIL_CONFIRMATION_LIFETIME': 5,

    'MSG_LOGGED_IN': 'You are logged in',
    'MSG_LOGGED_OUT': 'You are logged out',
    'MSG_ACTIVATED': 'Your account is activated',
    'MSG_UNKNOWN_EMAIL': 'This email is not registered',
    'MSG_WRONG_PASSWORD': '<PASSWORD>',
    'MSG_USER_BANNED': 'This user is banned',
    'MSG_ACTIVATION_REQUIRED': ('You have to activate your account via'
                                ' email, before you can login'),
    'MSG_EMAIL_EXISTS': 'This email is already registered',
    'MSG_OFTEN_RESET_PASSWORD': (
        'You can\'t request of restoring your password so often. Please, use'
        ' the link we sent you recently'),
    'MSG_CANT_SEND_MAIL': 'Can\'t send email, try a little later',
    'MSG_PASSWORDS_NOT_MATCH': 'Passwords must match',
    'MSG_PASSWORD_CHANGED': 'Your password is changed',
    'MSG_CHANGE_EMAIL_REQUESTED': ('Please, click on the verification link'
                                   ' we sent to your new email address'),
    'MSG_EMAIL_CHANGED': 'Your email is changed',
    'MSG_AUTH_FAILED': 'Authorization failed',

    # next settings are initialized during `setup()`, do not set it manually
    'APP': REQUIRED,
    'STORAGE': REQUIRED,
}


class Cfg(dict):
    '''
    Settings storage witch suports both, dict and dot notations

    >>> cfg = Cfg({'foo': 1, 'bar': 2, 'baz': REQUIRED})

    >>> cfg.attr
    Traceback (most recent call last):
        ...
    RuntimeError: Settings are not configured yet

    >>> cfg['item']
    Traceback (most recent call last):
        ...
    RuntimeError: Settings are not configured yet

    >>> cfg.configure({})
    Traceback (most recent call last):
        ...
    RuntimeError: You have to set `baz`

    >>> cfg.configure({'bar': 3, 'baz': 4})
    >>> cfg['foo']
    1
    >>> cfg['bar']
    3
    >>> cfg['baz']
    4
    >>> cfg.foo
    1
    >>> cfg.bar
    3
    >>> cfg.baz
    4

    >>> cfg['unknown']
    Traceback (most recent call last):
        ...
    KeyError: 'unknown'

    >>> cfg.unknown
    Traceback (most recent call last):
        ...
    AttributeError
    '''

    def __init__(self, defaults):
        self.defaults = defaults
        self.configured = False

    def __getitem__(self, name):
        if not self.configured:
            raise RuntimeError('Settings are not configured yet')
        self.__getitem__ = super().__getitem__
        return super().__getitem__(name)

    def __getattr__(self, name):
        if name == '__wrapped__':
            raise AttributeError
        try:
            return self[name]
        except KeyError:
            raise AttributeError

    def configure(self, updates):
        self.clear()
        for key in self.defaults:
            value = updates.get(key, self.defaults[key])
            if value == REQUIRED:
                raise RuntimeError('You have to set `{}`'.format(key))
            self[key] = value
        self.configured = True


if __name__ == '__main__':
    import doctest
    print(doctest.testmod())
else:
    cfg = Cfg(DEFAULTS)
<commit_msg>change default theme<commit_after>REQUIRED = object()
DEFAULTS = {
    'THEME': 'aiohttp_login/bootstrap-4',
    'PASSWORD_LEN': (6, 30),
    'LOGIN_REDIRECT': '/',
    'LOGOUT_REDIRECT': 'auth_login',
    'REGISTRATION_CONFIRMATION_REQUIRED': True,

    'ADMIN_EMAILS': [],
    'CSRF_SECRET': REQUIRED,
    'BACK_URL_QS_KEY': 'back_to',
    'SESSION_USER_KEY': 'user',
    'REQUEST_USER_KEY': 'user',

    'SESSION_FLASH_KEY': 'flash',
    'REQUEST_FLASH_INCOMING_KEY': 'flash_incoming',
    'REQUEST_FLASH_OUTGOING_KEY': 'flash_outgoing',
    'FLASH_QUEUE_LIMIT': 10,

    'VKONTAKTE_ID': None,
    'VKONTAKTE_SECRET': None,
    'GOOGLE_ID': None,
    'GOOGLE_SECRET': None,
    'FACEBOOK_ID': None,
    'FACEBOOK_SECRET': None,

    'SMTP_SENDER': None,
    'SMTP_HOST': REQUIRED,
    'SMTP_PORT': REQUIRED,
    'SMTP_TLS': True,
    'SMTP_USERNAME': None,
    'SMTP_PASSWORD': None,

    # email confirmation links lifetime in days
    'REGISTRATION_CONFIRMATION_LIFETIME': 5,
    'RESET_PASSWORD_CONFIRMATION_LIFETIME': 5,
    'CHANGE_EMAIL_CONFIRMATION_LIFETIME': 5,

    'MSG_LOGGED_IN': 'You are logged in',
    'MSG_LOGGED_OUT': 'You are logged out',
    'MSG_ACTIVATED': 'Your account is activated',
    'MSG_UNKNOWN_EMAIL': 'This email is not registered',
    'MSG_WRONG_PASSWORD': '<PASSWORD>',
    'MSG_USER_BANNED': 'This user is banned',
    'MSG_ACTIVATION_REQUIRED': ('You have to activate your account via'
                                ' email, before you can login'),
    'MSG_EMAIL_EXISTS': 'This email is already registered',
    'MSG_OFTEN_RESET_PASSWORD': (
        'You can\'t request of restoring your password so often. Please, use'
        ' the link we sent you recently'),
    'MSG_CANT_SEND_MAIL': 'Can\'t send email, try a little later',
    'MSG_PASSWORDS_NOT_MATCH': 'Passwords must match',
    'MSG_PASSWORD_CHANGED': 'Your password is changed',
    'MSG_CHANGE_EMAIL_REQUESTED': ('Please, click on the verification link'
                                   ' we sent to your new email address'),
    'MSG_EMAIL_CHANGED': 'Your email is changed',
    'MSG_AUTH_FAILED': 'Authorization failed',

    # next settings are initialized during `setup()`, do not set it manually
    'APP': REQUIRED,
    'STORAGE': REQUIRED,
}


class Cfg(dict):
    '''
    Settings storage witch suports both, dict and dot notations

    >>> cfg = Cfg({'foo': 1, 'bar': 2, 'baz': REQUIRED})

    >>> cfg.attr
    Traceback (most recent call last):
        ...
    RuntimeError: Settings are not configured yet

    >>> cfg['item']
    Traceback (most recent call last):
        ...
    RuntimeError: Settings are not configured yet

    >>> cfg.configure({})
    Traceback (most recent call last):
        ...
    RuntimeError: You have to set `baz`

    >>> cfg.configure({'bar': 3, 'baz': 4})
    >>> cfg['foo']
    1
    >>> cfg['bar']
    3
    >>> cfg['baz']
    4
    >>> cfg.foo
    1
    >>> cfg.bar
    3
    >>> cfg.baz
    4

    >>> cfg['unknown']
    Traceback (most recent call last):
        ...
    KeyError: 'unknown'

    >>> cfg.unknown
    Traceback (most recent call last):
        ...
    AttributeError
    '''

    def __init__(self, defaults):
        self.defaults = defaults
        self.configured = False

    def __getitem__(self, name):
        if not self.configured:
            raise RuntimeError('Settings are not configured yet')
        self.__getitem__ = super().__getitem__
        return super().__getitem__(name)

    def __getattr__(self, name):
        if name == '__wrapped__':
            raise AttributeError
        try:
            return self[name]
        except KeyError:
            raise AttributeError

    def configure(self, updates):
        self.clear()
        for key in self.defaults:
            value = updates.get(key, self.defaults[key])
            if value == REQUIRED:
                raise RuntimeError('You have to set `{}`'.format(key))
            self[key] = value
        self.configured = True


if __name__ == '__main__':
    import doctest
    print(doctest.testmod())
else:
    cfg = Cfg(DEFAULTS)

{'fraction_non_alphanumeric': 0.1261183968623606, 'fraction_numerical': 0.004657433509008457, 'mean_word_length': 2.6856368563685638, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2716144', 'n_tokens_mistral': 2743, 'n_tokens_neox': 2662, 'n_words': 684}