Package: klar
# klara micro web framework built for fun* argument annotation* jsonschema intergration```pyfrom klar import Appapp = App()@app.get('/hello/<name>')def hello(name: str, times: int = 1):return "hello " * times + name```run it using [wsgi-supervisor](https://github.com/zweifisch/wsgi-supervisor)```shwsgi-supervisor app:app``````sh$ curl 'localhost:3000/hello/klar?times=2'hello hello klar```## custom types using jsonschema```pythonproduct = {"type": "object""properties": {"name": {"type": "string"},"price": {"type": "number"},},"additionalProperties": False,}@app.post('/product')def create(body: product, db):db.products.insert(body)return {"ok": True}```schemas can and should be imported from json or yaml files```|--app.py|--schemas.json```product in schema.json```{product: {...}}``````pythonfrom schemas import product@app.post('/product')def create(body: product):pass```## dependency injectionprovide a custom dependency using decorator```python@app.provide('db')def get_db_connection():conn = SomeDB(url="localhost:3349")return connimport redisapp.provide('cache', (redis.Redis, {'host': 'localhost'}))```using `db` and `cache` in request handler```python@get('/article/<article_id>')def get_article(article_id:int, db, cache):pass```predefined components:* req* session* cookie* router## rest```pythonfrom resource import product, catalogapp = App()app.resources(product, catalog, prefix="/v1")if __name__ == '__main__':app.run()```in product```pythonfrom schema import product# curl -X POST $host/v1/product -d @bodydef create(body: product, db):return db.products.insert(body)# curl $host/v1/product/$iddef show(product_id: str):item = db.products.find_one({_id: product_id})return item if item else 404# curl $host/v1/product?shift=10&limit=10def query(shift: int, limit: int, db):return db.products.find().skip(shift).limit(shift)# curl -X PATCH $host/v1/product/$id -d @bodydef modify(body: product, product_id: str):return db.products.update({_id: product_id}, {'$set': body})# curl -X PUT $host/v1/product/$id -d @bodydef replace(body: product, product_id: str):return db.products.update({_id: product_id}, body)# curl -X DELETE $host/v1/product/$iddef destroy(product_id: str):return db.products.delete({_id: product_id})```### custom method```pythonfrom klar import method@method('patch')def like(product_id):return db.products.update({_id: product_id}, {'$inc': {'likes': 1}})# curl -X PATCH $host/v1/product/$id/like```## eventslistening for an event```python@on(404)def not_found(req, res):print('%s not found' % req.path)res.body = "%s not found on this server" % req.path```### custom events```python@on('user-login')def onlogin(userid, db):print('user: %s logged in' % userid)db.users.update({_id:userid}, {'$inc': {'logincount': 1}})```emit an event```pythondef login(emitter):emitter.emit('user-login', userid=id)```## post processing```pythondef jsonp(req, res):callback = req.query.get('callback')if callback:res.body = "%s(%s)" % (callback, json.dumps(body))res.headers["Content-Type"] = "application/javascript"@app.get('/resource') -> jsonp:return {"key": "value"}```more than one processors:```python@app.get('/resource') -> (jsonp, etag):return {"key": "value"}```## template rendering```|--app.py|--templates|--home.html``````pythonimport templates.home@app.get('/') -> templates.home:return {"key": "value"}````templates.home` accecpts an optional dict as argumentit's basically equivalent to this:```python@app.get('/'):return templates.home({"key": "value"})```### mustachedepends on pystache, `pip install pystache`use `.mustache` as extension```|--templates|--home.mustache``````pythonimport templates.home```## sessionsession depends on `cache`, but klar does't has it builtinto use redis as session backend:```pythonimport redis@app.provide('cache')def cache():return redis.Redis(host='localhost', port=6379, db=0)```or```pythonapp.privide('cache', (redis.Redis, {'host': 'localhost'}))```### use session```python@app.post('/login')def login(body, session):# check body.username and body.passwordif founduser:session.set('userid', userid)@app.post('/login')def logout(session):session.destroy()@app.get('/admin')def admin(session):if session.get('userid'):pass```## cookies```pythoncookies.get(key, default)cookies.set(key, value)cookies.delete(key)cookies.set(key, value, httponly=True)cookies.set_for_30_days(key, value)```## serving static filesshould only be used in development enviroment```app.static('/public/')``````app.static('/public/', 'path/to/public/dir')```## configconfig file path will be read from enviroment variable `$CONFIG`if it's empty config.py will be loadedconfig.py```pythonmongo = {"host": "127.0.0.1""port": 27017}``````pythonfrom pymongo import MongoClient@app.provide('db')def db(config):return MongoClient(**config.mongo)```## reversed routing```python@app.get('user/<id>')def user(id:str):pass```get a link to previous handler```def another_handler(router):href = router.path_for('user', id=3221)```## custom json encoder```pythonfrom bson.objectid import ObjectId@app.json_encode(ObjectId)def encode_objectid(obj):return str(obj)```by default `Iterable` is converted to `list`