예제 #1
0
 def test_issue588(self):
     """`ConfigDict` namespaces break route options"""
     c = ConfigDict()
     c.load_dict({'a': {'b': 'c'}}, make_namespaces=True)
     self.assertEqual('c', c['a.b'])
     self.assertEqual('c', c['a']['b'])
     self.assertEqual({'b': 'c'}, c['a'])
예제 #2
0
 def test_load_dict(self):
     c = ConfigDict()
     d = dict(a=dict(b=dict(foo=5, bar=6), baz=7))
     c.load_dict(d)
     self.assertEqual(c['a.b.foo'], 5)
     self.assertEqual(c['a.b.bar'], 6)
     self.assertEqual(c['a.baz'], 7)
예제 #3
0
 def test_gc_overlays(self):
     root = ConfigDict()
     overlay = root._make_overlay()
     del overlay
     import gc; gc.collect()
     root._make_overlay()  # This triggers the weakref-collect
     self.assertEqual(len(root._overlays), 1)
예제 #4
0
 def test_issue588(self):
     """`ConfigDict` namespaces break route options"""
     c = ConfigDict()
     c.load_dict({'a': {'b': 'c'}}, make_namespaces=True)
     self.assertEqual('c', c['a.b'])
     self.assertEqual('c', c['a']['b'])
     self.assertEqual({'b': 'c'}, c['a'])
예제 #5
0
파일: hymby.py 프로젝트: blankoworld/hymby
def config_page():
    """
    Display general configuration.
    Display specific static weblog engine configuration.
    Allow to change values.
    """
    check_config()
    message = ''
    message_type = 'none'
    config = hymby.params
    engine_config = hymby.engine.get_config(hymby)
    if request.POST.get('save', '').strip():
        r = request.POST
        config_filename = hymby.params['filename']
        # Write changes using an ugly method: Get current config, and update it with form values. Then write all in the config.
        conf = ConfigDict().load_config(config_filename)
        for field in dict(r):
            # do not take save button value
            if field == 'save':
                continue
            value = r[field]
            conf.update('general', {field: value})
        reset_config(conf)
        refresh()
        message = 'General configuration updated.'
        message_type = 'success'
        config = hymby.params # needed as we update it
    elif request.POST.get('save_engine'):
        r = request.POST
        hymby.engine.set_config(hymby, dict(r))
        refresh()
        message = '%s Configuration updated.' % hymby.params.get('general.engine', '')
        message_type = 'success'
        engine_config = hymby.engine.get_config(hymby) # needed as we update it
    return template('config', title='Configuration', config=config, engine_config=engine_config, message=message, message_type=message_type)
예제 #6
0
 def test_load_dict(self):
     c = ConfigDict()
     d = dict(a=dict(b=dict(foo=5, bar=6), baz=7))
     c.load_dict(d)
     self.assertEqual(c['a.b.foo'], 5)
     self.assertEqual(c['a.b.bar'], 6)
     self.assertEqual(c['a.baz'], 7)
예제 #7
0
 def test_gc_overlays(self):
     root = ConfigDict()
     overlay = root._make_overlay()
     del overlay
     import gc
     gc.collect()
     root._make_overlay()  # This triggers the weakref-collect
     self.assertEqual(len(root._overlays), 1)
예제 #8
0
 def test_load_config(self):
     c = ConfigDict()
     c.load_config(self.config_file.name)
     self.assertDictEqual({
         'compression.default': '45',
         'compression.status': 'single',
         'default': '45',
         'namespace.key': 'test',
         'namespace.section.default': 'otherDefault',
         'namespace.section.sub.namespace.key': 'test2',
         'port': '8080'}, c)
예제 #9
0
 def test_attr_access(self):
     """ ConfigDict allow attribute access to keys. """
     c = ConfigDict()
     c.test = 5
     self.assertEqual(5, c.test)
     self.assertEqual(5, c['test'])
     c['test'] = 6
     self.assertEqual(6, c.test)
     self.assertEqual(6, c['test'])
     del c.test
     self.assertTrue('test' not in c)
예제 #10
0
 def test_load_config(self):
     c = ConfigDict()
     c.load_config(self.config_file.name)
     self.assertDictEqual({
         'compression.default': '45',
         'compression.status': 'single',
         'default': '45',
         'namespace.key': 'test',
         'namespace.section.default': 'otherDefault',
         'namespace.section.sub.namespace.key': 'test2',
         'port': '8080'}, c)
예제 #11
0
 def test_attr_access(self):
     """ ConfigDict allow attribute access to keys. """
     c = ConfigDict()
     c.test = 5
     self.assertEqual(5, c.test)
     self.assertEqual(5, c['test'])
     c['test'] = 6
     self.assertEqual(6, c.test)
     self.assertEqual(6, c['test'])
     del c.test
     self.assertTrue('test' not in c)
     self.assertEqual(None, c.test)
예제 #12
0
    def test_load_module(self):
        c = ConfigDict()
        c.load_module('example_settings', True)
        self.assertEqual(c['A.B.C'], 3)

        c = ConfigDict()
        c.load_module('example_settings', False)
        self.assertEqual(c['A']['B']['C'], 3)
예제 #13
0
    def test_fallback(self):
        fallback = ConfigDict()
        fallback['key'] = 'fallback'
        primary = ConfigDict()
        primary._set_fallback(fallback)

        # Check copy of existing values from fallback to primary
        self.assertEqual(primary['key'], 'fallback')

        # Check value change in fallback
        fallback['key'] = 'fallback2'
        self.assertEqual(fallback['key'], 'fallback2')
        self.assertEqual(primary['key'], 'fallback2')

        # Check value change in primary
        primary['key'] = 'primary'
        self.assertEqual(fallback['key'], 'fallback2')
        self.assertEqual(primary['key'], 'primary')

        # Check delete of mirrored value in primary
        del primary['key']
        self.assertEqual(fallback['key'], 'fallback2')
        self.assertEqual(primary['key'], 'fallback2')

        # Check delete on mirrored key in fallback
        del fallback['key']
        self.assertTrue('key' not in primary)
        self.assertTrue('key' not in fallback)

        # Check new key in fallback
        fallback['key2'] = 'fallback'
        self.assertEqual(fallback['key2'], 'fallback')
        self.assertEqual(primary['key2'], 'fallback')

        # Check new key in primary
        primary['key3'] = 'primary'
        self.assertEqual(primary['key3'], 'primary')
        self.assertTrue('key3' not in fallback)

        # Check delete of primary-only key
        del primary['key3']
        self.assertTrue('key3' not in primary)
        self.assertTrue('key3' not in fallback)

        # Check delete of fallback value
        del fallback['key2']
        self.assertTrue('key2' not in primary)
        self.assertTrue('key2' not in fallback)
예제 #14
0
    def test_fallback(self):
        fallback = ConfigDict()
        fallback['key'] = 'fallback'
        primary = ConfigDict()
        primary._set_fallback(fallback)

        # Check copy of existing values from fallback to primary
        self.assertEqual(primary['key'], 'fallback')

        # Check value change in fallback
        fallback['key'] = 'fallback2'
        self.assertEqual(fallback['key'], 'fallback2')
        self.assertEqual(primary['key'], 'fallback2')

        # Check value change in primary
        primary['key'] = 'primary'
        self.assertEqual(fallback['key'], 'fallback2')
        self.assertEqual(primary['key'], 'primary')

        # Check delete of mirrored value in primary
        del primary['key']
        self.assertEqual(fallback['key'], 'fallback2')
        self.assertEqual(primary['key'], 'fallback2')

        # Check delete on mirrored key in fallback
        del fallback['key']
        self.assertTrue('key' not in primary)
        self.assertTrue('key' not in fallback)

        # Check new key in fallback
        fallback['key2'] = 'fallback'
        self.assertEqual(fallback['key2'], 'fallback')
        self.assertEqual(primary['key2'], 'fallback')

        # Check new key in primary
        primary['key3'] = 'primary'
        self.assertEqual(primary['key3'], 'primary')
        self.assertTrue('key3' not in fallback)

        # Check delete of primary-only key
        del primary['key3']
        self.assertTrue('key3' not in primary)
        self.assertTrue('key3' not in fallback)

        # Check delete of fallback value
        del fallback['key2']
        self.assertTrue('key2' not in primary)
        self.assertTrue('key2' not in fallback)
예제 #15
0
 def test_write(self):
     c = ConfigDict()
     c['key'] = 'value'
     self.assertEqual(c['key'], 'value')
     self.assertTrue('key' in c)
     c['key'] = 'value2'
     self.assertEqual(c['key'], 'value2')
예제 #16
0
 def test_issue720(self):
     """Accept unicode keys."""
     try:
         key = unichr(12354)
     except NameError:
         key = chr(12354)
     c = ConfigDict()
     c.load_dict({key: 'value'})
     self.assertEqual('value', c[key])
     c = ConfigDict()
     c.load_dict({key: {'subkey': 'value'}})
     self.assertEqual('value', c[key + '.subkey'])
예제 #17
0
 def test_meta(self):
     c = ConfigDict()
     c.meta_set('bool', 'filter', bool)
     c.meta_set('int', 'filter', int)
     c['bool'] = 'I am so true!'
     c['int'] = '6'
     self.assertTrue(c['bool'] is True)
     self.assertEqual(c['int'], 6)
     self.assertRaises(ValueError, lambda: c.update(int='not an int'))
예제 #18
0
 def test_load_dict(self):
     c = ConfigDict()
     d = dict(a=dict(b=dict(foo=5, bar=6), baz=7))
     c.load_dict(d)
     self.assertEqual(c['a.b.foo'], 5)
     self.assertEqual(c['a.b.bar'], 6)
     self.assertEqual(c['a.baz'], 7)
     # unicode keys (see issue #720)
     try:
         key = unichr(12354)
     except NameError:
         key = chr(12354)
     c = ConfigDict()
     c.load_dict({key: 'value'})
     self.assertEqual('value', c[key])
     c = ConfigDict()
     c.load_dict({key: {'subkey': 'value'}})
     self.assertEqual('value', c[key + '.subkey'])
예제 #19
0
    def test_load_module(self):
        c = ConfigDict()
        c.load_module('example_settings', True)
        self.assertEqual(c['A.B.C'], 3)

        c = ConfigDict()
        c.load_module('example_settings', False)
        self.assertEqual(c['A']['B']['C'], 3)
예제 #20
0
 def test_meta(self):
     c = ConfigDict()
     c.meta_set('bool', 'filter', bool)
     c.meta_set('int', 'filter', int)
     c['bool'] = 'I am so true!'
     c['int']  = '6'
     self.assertTrue(c['bool'] is True)
     self.assertEqual(c['int'], 6)
     self.assertRaises(ValueError, lambda: c.update(int='not an int'))
예제 #21
0
 def test_namespaces(self):
     """ Access to a non-existent uppercase attribute creates a new namespace. """
     c = ConfigDict()
     self.assertEqual(c.__class__, c.Name.Space.__class__)
     c.Name.Space.value = 5
     self.assertEqual(5, c.Name.Space.value)
     self.assertTrue('value' in c.Name.Space)
     self.assertTrue('Space' in c.Name)
     self.assertTrue('Name' in c)
     self.assertTrue('value' not in c)
     # Overwriting namespaces is not allowed.
     self.assertRaises(AttributeError, lambda: setattr(c, 'Name', 5))
     # Overwriting methods defined on dict is not allowed.
     self.assertRaises(AttributeError, lambda: setattr(c, 'keys', 5))
     # but not with the dict API:
     c['Name'] = 5
     self.assertEquals(5, c.Name)
예제 #22
0
 def test_isadict(self):
     """ ConfigDict should behaves like a normal dict. """
     # It is a dict-subclass, so this kind of pointless, but it doen't hurt.
     d, m = dict(), ConfigDict()
     d['key'], m['key'] = 'value', 'value'
     d['k2'], m['k2'] = 'v1', 'v1'
     d['k2'], m['k2'] = 'v2', 'v2'
     self.assertEqual(d.keys(), m.keys())
     self.assertEqual(list(d.values()), list(m.values()))
     self.assertEqual(d.get('key'), m.get('key'))
     self.assertEqual(d.get('cay'), m.get('cay'))
     self.assertEqual(list(iter(d)), list(iter(m)))
     self.assertEqual([k for k in d], [k for k in m])
     self.assertEqual(len(d), len(m))
     self.assertEqual('key' in d, 'key' in m)
     self.assertEqual('cay' in d, 'cay' in m)
     self.assertRaises(KeyError, lambda: m['cay'])
예제 #23
0
 def test_issue720(self):
     """Accept unicode keys."""
     try:
         key = unichr(12354)
     except NameError:
         key = chr(12354)
     c = ConfigDict()
     c.load_dict({key: 'value'})
     self.assertEqual('value', c[key])
     c = ConfigDict()
     c.load_dict({key: {'subkey': 'value'}})
     self.assertEqual('value', c[key + '.subkey'])
예제 #24
0
    def setUp(self):
        """
        Initializes the unit test global configs
        """

        self.maxDiff = None  # pylint: disable=invalid-name
        self.config_sample = tempfile.NamedTemporaryFile(delete=False)
        self.dbfile = tempfile.NamedTemporaryFile(delete=False)
        self.os_environ = {u'SHA_API_CONFIG': self.config_sample.name}

        self.configdict_ns = ConfigDict().load_dict({
            u'sha_api': {
                u'test_variable': u'test_value'
            },
            u'sqlite': {
                u'dbfile': self.dbfile.name
            }
        })

        with open(self.config_sample.name, 'w') as fout:
            fout.write(
                u"[sha_api]\ntest_variable = test_value\n[sqlite]\ndbfile = %s"
                % self.dbfile.name)
예제 #25
0
 def test_update(self):
     c = ConfigDict()
     c['key'] = 'value'
     c.update(key='value2', key2='value3')
     self.assertEqual(c['key'], 'value2')
     self.assertEqual(c['key2'], 'value3')
예제 #26
0
 def test_namespaces(self):
     c = ConfigDict()
     c.update('a.b', key='value')
     self.assertEqual(c['a.b.key'], 'value')
예제 #27
0
    def test_overlay(self):
        source = ConfigDict()
        source['key'] = 'source'
        intermediate = source._make_overlay()
        overlay = intermediate._make_overlay()

        # Overlay contains values from source
        self.assertEqual(overlay['key'], 'source')
        self.assertEqual(overlay.get('key'), 'source')
        self.assertTrue('key' in overlay)

        # Overlay is updated with source
        source['key'] = 'source2'
        self.assertEqual(source['key'], 'source2')
        self.assertEqual(overlay['key'], 'source2')

        # Overlay 'overlays' source (hence the name)
        overlay['key'] = 'overlay'
        self.assertEqual(source['key'], 'source2')
        self.assertEqual(intermediate['key'], 'source2')
        self.assertEqual(overlay['key'], 'overlay')

        # Deleting an overlayed key restores the value from source
        del overlay['key']
        self.assertEqual(source['key'], 'source2')
        self.assertEqual(overlay['key'], 'source2')

        # Deleting a virtual key is actually not possible.
        with self.assertRaises(KeyError):
            del overlay['key']

        # Deleting a key in the source also removes it from overlays.
        del source['key']
        self.assertTrue('key' not in overlay)
        self.assertTrue('key' not in intermediate)
        self.assertTrue('key' not in source)

        # New keys in source are copied to overlay
        source['key2'] = 'source'
        self.assertEqual(source['key2'], 'source')
        self.assertEqual(intermediate['key2'], 'source')
        self.assertEqual(overlay['key2'], 'source')

        # New keys in overlay do not change the source
        overlay['key3'] = 'overlay'
        self.assertEqual(overlay['key3'], 'overlay')
        self.assertTrue('key3' not in intermediate)
        self.assertTrue('key3' not in source)

        # Setting the same key in the source does not affect the overlay
        # because it already has this key.
        source['key3'] = 'source'
        self.assertEqual(source['key3'], 'source')
        self.assertEqual(intermediate['key3'], 'source')
        self.assertEqual(overlay['key3'], 'overlay')

        # But as soon as the overlayed key is deleted, it gets the
        # copy from the source
        del overlay['key3']
        self.assertEqual(source['key3'], 'source')
        self.assertEqual(overlay['key3'], 'source')
예제 #28
0
from bottle import ConfigDict

app_config = ConfigDict()
app_config.load_dict({
    'app': {
        'debug': True,
        'timezone': 'Europe/Moscow',
        'server': 'tornado',
        'port': 5040,
        'auth': {
            'admin': '$2a$10$YOUR-BCRYPT-HASH'
        },
        'db': {
            'path': './data/sqlite.db'
        }
    },
    'blog': {
        'label': {
            'read_more': 'Read full article'
        },
        'html_parser':
        'lxml',  # you must install 'lxml' package or use 'html.parser' instead
    },
    'feed': {
        'author': 'Nikita Dementev',
        'title': 'Neutral notes',
        'subtitle': 'О коде и погоде',
    },
    'deploy': {
        'production': {
            'host': '*****@*****.**',
예제 #29
0
 def test_string_save_keys(self):
     c = ConfigDict()
     with self.assertRaises(TypeError):
         c[5] = 'value'
     with self.assertRaises(TypeError):
         c.load_dict({5: 'value'})
예제 #30
0
 def test_load_dict(self):
     c = ConfigDict()
     d = dict(a=dict(b=dict(foo=5, bar=6), baz=7))
     c.load_dict(d)
     self.assertEqual(c['a.b.foo'], 5)
     self.assertEqual(c['a.b.bar'], 6)
     self.assertEqual(c['a.baz'], 7)
     # unicode keys (see issue #720)
     try:
         key = unichr(12354)
     except NameError:
         key = chr(12354)
     c = ConfigDict()
     c.load_dict({key: 'value'})
     self.assertEqual('value', c[key])
     c = ConfigDict()
     c.load_dict({key: {'subkey': 'value'}})
     self.assertEqual('value', c[key + '.subkey'])
예제 #31
0
 def test_string_save_keys(self):
     c = ConfigDict()
     with self.assertRaises(TypeError):
         c[5] = 'value'
     with self.assertRaises(TypeError):
         c.load_dict({5: 'value'})
예제 #32
0
 def test_call(self):
     """ Calling updates and returns the dict. """
     c = ConfigDict()
     self.assertEqual(c, c(a=1))
     self.assertTrue('a' in c)
     self.assertEqual(1, c.a)
예제 #33
0
#!/bin/env python
from bottle import (run, static_file, request, view, redirect, abort, get,
                    post, ConfigDict, response, default_app, error)
from utils import random_name, file_validation, remove_media, board_directory, get_directory_size
from json import loads, dumps
from os import path, mkdir
from string import punctuation
from waitress import serve
from models import db, Post, Anon, Board, Report
from datetime import datetime

config = ConfigDict()
config.load_config('imageboard.conf')

basename = config['app.basename']

if basename[-1] == '/': basename = basename[:-1]  # remove trailing slash


@get('/static/<filename:path>')
def send_static(filename):
    return static_file(filename, root='static')


@get('/uploads/<filename:path>')
def send_upload(filename):
    return static_file(filename, root='uploads')


def get_current_user(req):
    ip = req.get('REMOTE_ADDR')
예제 #34
0
 def test_update(self):
     c = ConfigDict()
     c['key'] = 'value'
     c.update(key='value2', key2='value3')
     self.assertEqual(c['key'], 'value2')
     self.assertEqual(c['key2'], 'value3')
예제 #35
0
 def test_string_key_only(self):
     c = ConfigDict()
     self.assertRaises(TypeError, lambda: setitem(c, 5, 6))
     self.assertRaises(TypeError, lambda: c.load_dict({5: 6}))
예제 #36
0
    def test_overlay(self):
        source = ConfigDict()
        source['key'] = 'source'
        intermediate = source._make_overlay()
        overlay = intermediate._make_overlay()

        # Overlay contains values from source
        self.assertEqual(overlay['key'], 'source')
        self.assertEqual(overlay.get('key'), 'source')
        self.assertTrue('key' in overlay)

        # Overlay is updated with source
        source['key'] = 'source2'
        self.assertEqual(source['key'], 'source2')
        self.assertEqual(overlay['key'], 'source2')

        # Overlay 'overlays' source (hence the name)
        overlay['key'] = 'overlay'
        self.assertEqual(source['key'], 'source2')
        self.assertEqual(intermediate['key'], 'source2')
        self.assertEqual(overlay['key'], 'overlay')

        # Deleting an overlayed key restores the value from source
        del overlay['key']
        self.assertEqual(source['key'], 'source2')
        self.assertEqual(overlay['key'], 'source2')

        # Deleting a virtual key is actually not possible.
        with self.assertRaises(KeyError):
            del overlay['key']

        # Deleting a key in the source also removes it from overlays.
        del source['key']
        self.assertTrue('key' not in overlay)
        self.assertTrue('key' not in intermediate)
        self.assertTrue('key' not in source)

        # New keys in source are copied to overlay
        source['key2'] = 'source'
        self.assertEqual(source['key2'], 'source')
        self.assertEqual(intermediate['key2'], 'source')
        self.assertEqual(overlay['key2'], 'source')

        # New keys in overlay do not change the source
        overlay['key3'] = 'overlay'
        self.assertEqual(overlay['key3'], 'overlay')
        self.assertTrue('key3' not in intermediate)
        self.assertTrue('key3' not in source)

        # Setting the same key in the source does not affect the overlay
        # because it already has this key.
        source['key3'] = 'source'
        self.assertEqual(source['key3'], 'source')
        self.assertEqual(intermediate['key3'], 'source')
        self.assertEqual(overlay['key3'], 'overlay')

        # But as soon as the overlayed key is deleted, it gets the
        # copy from the source
        del overlay['key3']
        self.assertEqual(source['key3'], 'source')
        self.assertEqual(overlay['key3'], 'source')
예제 #37
0
def get_config(path):
    config = ConfigDict().load_config(path or getenv('APP_CONFIG'))
    get_offset(OFFSET_FILE, config)
    return config
예제 #38
0
def OAuthServerMock(config):
    conf = ConfigDict().load_dict(config)
    app = Bottle()
    request = LocalRequest()

    token = {
        'access_token': conf.access_token,
        'token_type': 'bearer',
        'refresh_token': conf.refresh_token,
        'expires_in': 3600,
        'scope': conf.scope
    }

    @app.get('/')
    def get_root():
        abort(200, 'OK')

    @app.get('/authorize')
    def get_authorize():
        query = request.query

        if query.client_id != conf.client_id:
            raise OAuthError(401, 'invalid_client',
                             "Invalid client_id: %s" % query.client_id)

        if query.response_type != 'code':
            raise OAuthError(400, 'unsupported_response_type',
                             "Unsupported response type: %s",
                             query.response_type)

        if query.redirect_uri != conf.redirect_uri:
            raise OAuthError(400, 'invalid_grant',
                             "Invalid redirect %s does not match %s",
                             query.redirect_uri, conf.redirect_uri)

        if conf.get('approve_request',
                    True) and query.scope in conf.scope.split(' '):
            params = {'code': conf.auth_code}
        else:
            params = {'error': 'invalid_scope'}
        if query.state:
            params['state'] = query.state

        redirect(query.redirect_uri + '?' + urlencode(params))

    @app.post('/token')
    def post_token():
        auth = get_authorization_header(request)
        grant_type = request.forms.grant_type

        if parse_client_auth(auth) != (conf.client_id, conf.client_secret):
            raise OAuthError(401, 'unauthorized', 'Bad credentials')

        if grant_type == 'authorization_code':
            return handle_authorization_code()
        elif grant_type == 'refresh_token':
            return handle_refresh_token()
        else:
            raise OAuthError(400, 'invalid_request',
                             'Missing or invalid grant type')

    def handle_authorization_code():
        code = request.forms.code
        redirect_uri = request.forms.redirect_uri

        if code != conf.auth_code:
            raise OAuthError(400, 'invalid_grant',
                             "Invalid authorization code: %s" % code)

        return token

    def handle_refresh_token():
        refresh_token = request.forms.refresh_token

        if refresh_token != conf.refresh_token:
            raise OAuthError(400, 'invalid_grant',
                             "Invalid refresh token: %s" % refresh_token)

        return token

    @app.get('/userinfo')
    def get_userinfo():
        assert_access_token(request, conf.access_token)

        return {'username': conf.username}

    @app.error(400)
    @app.error(401)
    def handle_error(error):
        return error.body

    return app
예제 #39
0
 def test_namespaces(self):
     c = ConfigDict()
     c.update('a.b', key='value')
     self.assertEqual(c['a.b.key'], 'value')
예제 #40
0
 def test_string_key_only(self):
     c = ConfigDict()
     self.assertRaises(TypeError, lambda: setitem(c, 5, 6))
     self.assertRaises(TypeError, lambda: c.load_dict({5:6}))
예제 #41
0
from bottle import ConfigDict

app_config = ConfigDict()
app_config.load_dict({
    'app': {
        'debug': True,
        'timezone': 'Europe/Moscow',
        'server': 'tornado',
        'port': 5040,
        'auth': {
            'admin': '$2a$10$YOUR-BCRYPT-HASH'
        },
        'db': {
            'path': './data/sqlite.db'
        }
    },
    'blog': {
        'label': {'read_more': 'Read full article'},
        'html_parser': 'lxml',  # you must install 'lxml' package or use 'html.parser' instead
    },
    'feed': {
        'author': 'Nikita Dementev',
        'title': 'Neutral notes',
        'subtitle': 'О коде и погоде',
    },
    'deploy': {
        'production': {
            'host': '*****@*****.**',
            'key_file': '~/.ssh/same_rsa',
            'target_dir': '~/www/example.com'
        }