설정 (초안)

보틀 응용에서 Bottle.config에 설정을 저장할 수 있다. dict스러운 객체로, 응용별 설정을 위한 중심 저장소다. 이 딕셔너리는 프레임워크의 많은 부분을 제어하고 (신식) 플러그인들에 할 일을 알려 주며, 응용 자체 설정을 저장하는 데도 사용 가능하다.

설정 기본

Bottle.config 객체는 일반 딕셔너리와 아주 비슷하게 동작한다. dict 공통 메소드들이 모두 기대처럼 동작한다. 몇 가지 예로 시작해 보자.

import bottle
app = bottle.default_app()             # 또는 취향에 따라 bottle.Bottle()

app.config['autojson']    = False      # "autojson" 기능 끄기
app.config['sqlite.db']   = ':memory:' # sqlite 플러그인에 사용 DB 알려 주기
app.config['myapp.param'] = 'value'    # 자체 설정 값 예시

# 여러 값 한꺼번에 바꾸기
app.config.update({
    'autojson': False,
    'sqlite.db': ':memory:',
    'myapp.param': 'value'
})

# 기본값 추가하기
app.config.setdefault('myapp.param2', 'some default')

# 값 얻기
param  = app.config['myapp.param']
param2 = app.config.get('myapp.param2', 'fallback value')

# 설정 값 이용하는 라우트 예시
@app.route('/about', view='about.rst')
def about():
    email = app.config.get('my.email', 'nomail@example.com')
    return {'email': email}

앱 객체가 항상 사용 가능한 건 아니지만 요청 문맥 안에 있는 동안은 언제든 request 객체를 이용해 현재 응용과 그 설정을 얻을 수 있다.

from bottle import request
def is_admin(user):
    return user == request.app.config['myapp.admin_user']

이름 관행

일을 편하게 만들기 위해 플러그인과 응용에서 설정 매개변수 이름에 대한 간단한 규칙 몇 가지를 따르는 게 좋다.

  • 모든 키는 소문자 문자열이고 파이썬 식별자 규칙을 따라야 한다. (특수 문자가 안 되지만 밑줄은 가능.)

  • 점으로 네임스페이스를 구분한다. (예: namespace.field, namespace.subnamespace.field)

  • 보틀에서 자기 설정에 루트 네임스페이스를 쓴다. 플러그인에선 모든 변수들을 각자의 네임스페이스에 저장해야 한다. (예: sqlite.db, werkzeug.use_debugger)

  • 응용에서 별도의 네임스페이스를 써야 한다. (예: myapp.*)

파일에서 설정 읽어 오기

프로그래머 아닌 사람이 응용 설정을 할 수 있게 하고 싶은 경우, 아니면 데이터베이스 포트 바꾸자고 파이썬 모듈을 건드리는 건 싫을 때 설정 파일이 유용하다. 보다시피 설정 파일들에 많이 쓰는 문법이다.

[bottle]
debug = True

[sqlite]
db = /tmp/test.db
commit = auto

[myapp]
admin_user = defnull

ConfigDict.load_config()를 쓰면 디스크의 *.ini 방식 설정 파일을 읽어 들여서 기존 설정 안으로 값들을 가져올 수 있다.

app.config.load_config('/etc/myapp.conf')

중첩 dict에서 설정 읽어 오기

유용한 또 다른 메소드로 ConfigDict.load_dict()가 있다. 이 메소드는 중첩 딕셔너리 구조를 통째로 받아서 평탄한 키와 값 목록으로 바꿔 주며 그 구조가 키의 네임스페이스가 된다.

# dict 구조 통째로 읽어 들이기
app.config.load_dict({
    'autojson': False,
    'sqlite': { 'db': ':memory:' },
    'myapp': {
        'param': 'value',
        'param2': 'value2'
    }
})

assert app.config['myapp.param'] == 'value'

# json 파일에서 설정 읽어 들이기
with open('/etc/myapp.json') as fp:
    app.config.load_dict(json.load(fp))

설정 변경 주시하기

Bottle.config의 값이 바뀔 때마다 응용 객체의 config 훅이 실행된다. 이 훅을 이용해 런타임에 설정 변경에 반응할 수 있다. 예를 들어 새 데이터베이스에 재연결하거나, 백그라운드 서비스의 디버그 설정을 바꾸거나, 작업 스레드 풀 크기를 조정할 수 있다. 훅 콜백은 인자 두 개(key, new_value)를 받으며 딕셔너리에서 값이 실제로 바뀌기 전에 호출된다. 훅 콜백에서 예외를 던지면 그 변경이 취소되고 이전 값이 유지된다.

@app.hook('config')
def on_config_change(key, value):
  if key == 'debug':
      switch_own_debug_mode_to(value)

딕셔너리에 저장되려는 값을 훅 콜백에서 바꾸지는 못한다. 그건 필터의 역할이다.

필터 및 기타 메타데이터

ConfigDict에서는 설정 키별로 메타데이터를 함께 저장할 수 있다. 현재 두 가지 메타 필드가 정의돼 있다.

help

도움말 또는 설명 문자열. 디버깅이나 인트로스펙션에, 또는 관리 도구에서 사이트 관리자의 응용 설정을 돕는 데 이용할 수 있다.

filter

값 하나를 받고 반환하는 콜러블이다. 키에 필터가 정의돼 있으면 그 키에 저장되는 새 값이 먼저 필터 콜백을 거친다. 필터를 이용해 값을 다른 타입으로 바꾸거나, 값이 유효한지 검사하거나 (ValueError 던지기), 부대 효과를 일으킬 수 있다.

이 기능은 플러그인에서 특히 유용하다. 설정 필터를 이용해 매개변수를 검사하거나 부대 효과를 일으킬 수 있으며 help 필드를 통해 설정에 설명을 달 수 있다.

class SomePlugin(object):
    def setup(app):
        app.config.meta_set('some.int', 'filter', int)
        app.config.meta_set('some.list', 'filter',
            lambda val: str(val).split(';'))
        app.config.meta_set('some.list', 'help',
            'A semicolon separated list.')

    def apply(self, callback, route):
        ...

import bottle
app = bottle.default_app()
app.install(SomePlugin())

app.config['some.list'] = 'a;b;c'     # 실제 저장되는 건 ['a', 'b', 'c']
app.config['some.int'] = 'not an int' # ValueError 발생

API 문서

class ConfigDict(*a, **ka)[source]

dict스러운 설정 저장소. 네임스페이스, 값 검사, 메타데이터, 변경 주시 등을 추가로 지원.

이 저장소는 빠른 읽기 접근이 가능하게 최적화돼 있다. 키를 가져오거나 비변경 dict 메소드(예: dict.get()) 사용 시 원래 dict 대비 오버헤드가 전혀 없다.

load_config(filename)[source]

*.ini 방식 설정 파일에서 값 읽어 들이기.

설정 파일에 섹션이 있으면 그 이름을 네임스페이스로 해서 값을 넣는다. 두 가지 특수 섹션 DEFAULTbottle은 루트 네임스페이스(프리픽스 없음)를 가리킨다.

load_dict(source, namespace='', make_namespaces=False)[source]

딕셔너리 구조에서 값 가져오기. 중첩 구조를 써서 네임스페이스를 나타낼 수 있다.

>>> ConfigDict().load_dict({'name': {'space': {'key': 'value'}}})
{'name.space.key': 'value'}
update(*a, **ka)[source]

첫 번째 매개변수가 문자열이면 모든 키 이름 앞에 그 네임스페이스를 붙인다. 그 경우 외에는 일반 dict.update()처럼 동작한다. 예: update('some.namespace', key='value')

setdefault(key, value)[source]

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

clear()None.  Remove all items from D.[source]
meta_get(key, metafield, default=None)[source]

키에 대한 메타 필드 값을 반환.

meta_set(key, metafield, value)[source]

키에 대한 메타 필드에 새 값 설정. 기존 키에 대한 변경 핸들러 실행을 일으킨다.

meta_list(key)[source]

키에 대해 정의된 메타 필드 이름들의 이터러블 반환.