Skip to main content

a micro web framework

Project description

# klar

a micro web framework built for fun

* argument annotation
* jsonschema intergration

```py
from klar import App

app = 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)

```sh
wsgi-supervisor app:app
```

```sh
$ curl 'localhost:3000/hello/klar?times=2'
hello hello klar
```

## custom types using jsonschema

```python
product = {
"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: {...}
}
```

```python
from schemas import product

@app.post('/product')
def create(body: product):
pass
```

## dependency injection

provide a custom dependency using decorator

```python
@app.provide('db')
def get_db_connection():
conn = SomeDB(url="localhost:3349")
return conn

import redis
app.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

```python
from resource import product, catalog

app = App()

app.resources(product, catalog, prefix="/v1")

if __name__ == '__main__':
app.run()
```

in product

```python
from schema import product

# curl -X POST $host/v1/product -d @body
def create(body: product, db):
return db.products.insert(body)

# curl $host/v1/product/$id
def 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=10
def query(shift: int, limit: int, db):
return db.products.find().skip(shift).limit(shift)

# curl -X PATCH $host/v1/product/$id -d @body
def modify(body: product, product_id: str):
return db.products.update({_id: product_id}, {'$set': body})

# curl -X PUT $host/v1/product/$id -d @body
def replace(body: product, product_id: str):
return db.products.update({_id: product_id}, body)

# curl -X DELETE $host/v1/product/$id
def destroy(product_id: str):
return db.products.delete({_id: product_id})
```

### custom method

```python
from 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
```

## events

listening 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

```python
def login(emitter):
emitter.emit('user-login', userid=id)
```

## post processing

```python
def 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
```

```python
import templates.home

@app.get('/') -> templates.home:
return {"key": "value"}
```

`templates.home` accecpts an optional dict as argument
it's basically equivalent to this:

```python
@app.get('/'):
return templates.home({"key": "value"})
```

### mustache

depends on pystache, `pip install pystache`

use `.mustache` as extension

```
|--templates
|--home.mustache
```

```python
import templates.home
```

## session

session depends on `cache`, but klar does't has it builtin

to use redis as session backend:

```python
import redis

@app.provide('cache')
def cache():
return redis.Redis(host='localhost', port=6379, db=0)
```

or

```python
app.privide('cache', (redis.Redis, {'host': 'localhost'}))
```

### use session

```python
@app.post('/login')
def login(body, session):
# check body.username and body.password
if 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

```python
cookies.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 files

should only be used in development enviroment

```
app.static('/public/')
```

```
app.static('/public/', 'path/to/public/dir')
```

## config

config file path will be read from enviroment variable `$CONFIG`

if it's empty config.py will be loaded

config.py

```python
mongo = {
"host": "127.0.0.1"
"port": 27017
}
```

```python
from 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

```python
from bson.objectid import ObjectId

@app.json_encode(ObjectId)
def encode_objectid(obj):
return str(obj)
```

by default `Iterable` is converted to `list`

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

klar-0.0.6.tar.gz (12.7 kB view details)

Uploaded Source

File details

Details for the file klar-0.0.6.tar.gz.

File metadata

  • Download URL: klar-0.0.6.tar.gz
  • Upload date:
  • Size: 12.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for klar-0.0.6.tar.gz
Algorithm Hash digest
SHA256 aed1c2352f3e43db5ba999ad73fb5b272a7a5b4128469eb7820204996b8d6c99
MD5 25015e85cf9d797ee2993aaa029a01cc
BLAKE2b-256 763548f95e0cf423e2eb6cd57cfb32a024f1b1d97bf642a3c98691d6c78b8d35

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page