<commit_before># carafe

Flask application factory with extensions geared towards JSON APIs

## Extensions

```python
```

### json

Sets `app.json_encoder` and default app error handler to return JSON response for error messages.

#### init_app()

```python
opts = {
    'json': {
        # default error handler for application exceptions
        'error_handler': ext.json.json_error_handler,
        # json encoder to be used when jsonifying
        'encoder': ext.json.JSONEncoder
    }
}
```

#### Configuration


```python
# enable/disable extension
CARAFE_JSON_ENABLED = True
```

### session

Sets `app.session_interface = ext.session.SessionInterface()` which uses `SecureCookieSession` as session class.

#### init_app()

```python
opts = {
    'session': {}
}
```

#### Configuration

```python
# enable/disable extension
CARAFE_SESSION_ENABLED = True
# combined with SECRET_KEY when signing cookies
CARAFE_SESSION_SALT = 'my salt'
# when true and SESSION_PERMANENT_LIFETIME > 0, session is set to permanent
CARAFE_SESSION_PERMANENT = True
# (from flask) lifetime of permanent session int/timedelta
SESSION_PERMANENT_LIFETIME = timedelta(days=15000) # or `1296000000`
# (from flask) must be set to enable secure signing string
SECRET_KEY = 'my secret key'
```

### Signaler

Generic signal interface to `flask.signals`. Functions mainly as a named signal factory but can also handle string signals. Has no configuration options.

```python
from carafe import signaler

# using named signals
signaler.my_signal.send(**kargs)
signaler.my_signal.connect(handler, **kargs)

# using string signals
signaler.send('my_signal', **kargs)
signaler.connect('my_signal', handler, **kargs)
```

### Cache

Extends `flask-cache` to provide `cache.cached_view()` decorator which supports cache invalidation via key prefix modification cascades.

```python
from carafe import cache, signaler
from flask.ext.classy import FlaskView

class MyView(FlaskView):
    cache_cascade = ['MyDependentView']

    @cache.cached_view(timeout=3600)
    def index(self):
        # this route gets cached with key = `MyView:view:{path}`
        # and includes any request.args used (i.e. `/route/` and `/route/?foo=bar` have different cache keys)
        return ''

    def post(self):
        return ''

    def after_post(self):
        # after post, then signal is sent which tells `cache` to delete both `MyView` prefixed keys
        # as well as `MyDependentView` prefixed keys
        signaler.after_post.send()

class MyDependentView(FlaskView):
    def index(self):
        return ''

class MyOtherView(FlaskView):
    cache_namespace = 'my-awesome-namespace'

    @cache.cached_view(include_request_args=False)
    def index(self):
        # this route gets cached with key = `my-awesome-namespace:view:{path}`
        # and doesn't include request.args (i.e. '/route/' and '/route/?foo=bar' have the same cache key)
        return ''
```

#### init_app()

```python
opts = {
    'cache': {}
}
```

#### Configuration

```python
# enable/disable extension
CARAFE_CACHE_ENABLED = True
# ignore these request args when creating cached view key from request path
CARAFE_CACHE_IGNORED_REQUEST_ARGS = []
# cache key prefix
CACHE_KEY_PREFIX = 'my_prefix:'
# default timeout (in seconds) for cache key expiration
CACHE_DEFAULT_TIMEOUT = 300
# cache server host
CACHE_REDIS_HOST = 'localhost'
# cache server port
CACHE_REDIS_PORT = 6379
redis db index (zero-based); defaults to `0`
CACHE_REDIS_DB = 0
```

### Auth

Uses `flask-principal` for managing/generating permission based access.

Requires an Identity Provider class instance which exposes identity information. An example `AuthProvider` is defined in `ext.auth.SQLAlchemyAuthProvider`.

```python
from carafe import create_app, auth
from carafe.ext.auth import SQLAlchemyAuthProvider

# use flask-sqlalchemy for demo purposes
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()

from models import User

MyAuthProvider(SQLAlchemyAuthProvider):
    __model__ = User

    ##
    # Override these methods if they don't return user data correctly based on your models
    ##

    def get_user(self, _id):
        '''Return user object from database.'''
        return self.session.query(self.__model__).get(_id)

    def get_roles(self, user):
        '''Return a list of `roles` as strings.'''
        return getattr(user, self.__roles_key__, [])

provider = MyAuthProvider(db.session)

app = create_app(__name__, options={'auth': {'provider': provider}})

@app.route('/login/')
def login():
    if valid:
        auth.login(request.form['user_id'])
    return ''

@app.route('/logout/')
def logout():
    auth.logout()
    reutrn ''

@app.route('/session-user-id/')
def session_user_id():
	return str(auth.user_id)

@app.route('/auth-protected/')
@auth.require.auth(401)
def auth_protected():
    return ''

@app.route('/some-role-protected/')
@auth.require.some_role(403)
def some_role_protected():
    # user has permission if their `user.roles` list contains `'some_role'`
    return ''
```<commit_msg>Minor formatting changes.<commit_after># carafe

Flask application factory with extensions geared towards JSON APIs

## Extensions

### json

Sets `app.json_encoder` and default app error handler to return JSON response for error messages.

#### init_app()

```python
opts = {
    'json': {
        # default error handler for application exceptions
        'error_handler': ext.json.json_error_handler,
        # json encoder to be used when jsonifying
        'encoder': ext.json.JSONEncoder
    }
}
```

#### Configuration


```python
# enable/disable extension
CARAFE_JSON_ENABLED = True
```

### session

Sets `app.session_interface = ext.session.SessionInterface()` which uses `SecureCookieSession` as session class.

#### init_app()

```python
opts = {
    'session': {}
}
```

#### Configuration

```python
# enable/disable extension
CARAFE_SESSION_ENABLED = True
# combined with SECRET_KEY when signing cookies
CARAFE_SESSION_SALT = 'my salt'
# when true and SESSION_PERMANENT_LIFETIME > 0, session is set to permanent
CARAFE_SESSION_PERMANENT = True
# (from flask) lifetime of permanent session int/timedelta
SESSION_PERMANENT_LIFETIME = timedelta(days=15000) # or `1296000000`
# (from flask) must be set to enable secure signing string
SECRET_KEY = 'my secret key'
```

### Signaler

Generic signal interface to `flask.signals`. Functions mainly as a named signal factory but can also handle string signals. Has no configuration options.

```python
from carafe import signaler

# using named signals
signaler.my_signal.send(**kargs)
signaler.my_signal.connect(handler, **kargs)

# using string signals
signaler.send('my_signal', **kargs)
signaler.connect('my_signal', handler, **kargs)
```

### Cache

Extends `flask-cache` to provide `cache.cached_view()` decorator which supports cache invalidation via key prefix modification cascades.

```python
from carafe import cache, signaler
from flask.ext.classy import FlaskView

class MyView(FlaskView):
    cache_cascade = ['MyDependentView']

    @cache.cached_view(timeout=3600)
    def index(self):
        # this route gets cached with key = `MyView:view:{path}`
        # and includes any request.args used (i.e. `/route/`
        # and `/route/?foo=bar` have different cache keys)
        return ''

    def post(self):
        return ''

    def after_post(self):
        # after post, then signal is sent which tells `cache` to delete
        # both `MyView` prefixed keys as well as `MyDependentView` prefixed keys
        signaler.after_post.send()

class MyDependentView(FlaskView):
    def index(self):
        return ''

class MyOtherView(FlaskView):
    cache_namespace = 'my-awesome-namespace'

    @cache.cached_view(include_request_args=False)
    def index(self):
        # this route gets cached with key = `my-awesome-namespace:view:{path}`
        # and doesn't include request.args
        # (i.e. '/route/' and '/route/?foo=bar' have the same cache key)
        return ''
```

#### init_app()

```python
opts = {
    'cache': {}
}
```

#### Configuration

```python
# enable/disable extension
CARAFE_CACHE_ENABLED = True
# ignore these request args when creating cached view key from request path
CARAFE_CACHE_IGNORED_REQUEST_ARGS = []
# cache key prefix
CACHE_KEY_PREFIX = 'my_prefix:'
# default timeout (in seconds) for cache key expiration
CACHE_DEFAULT_TIMEOUT = 300
# cache server host
CACHE_REDIS_HOST = 'localhost'
# cache server port
CACHE_REDIS_PORT = 6379
redis db index (zero-based); defaults to `0`
CACHE_REDIS_DB = 0
```

### Auth

Uses `flask-principal` for managing/generating permission based access.

Requires an Identity Provider class instance which exposes identity information. An example `AuthProvider` is defined in `ext.auth.SQLAlchemyAuthProvider`.

```python
from carafe import create_app, auth
from carafe.ext.auth import SQLAlchemyAuthProvider

# use flask-sqlalchemy for demo purposes
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()

from models import User

MyAuthProvider(SQLAlchemyAuthProvider):
    __model__ = User

    ##
    # Override these methods if they don't return user data
    # correctly based on your models
    ##

    def get_user(self, _id):
        '''Return user object from database.'''
        return self.session.query(self.__model__).get(_id)

    def get_roles(self, user):
        '''Return a list of `roles` as strings.'''
        return getattr(user, self.__roles_key__, [])

provider = MyAuthProvider(db.session)

app = create_app(__name__, options={'auth': {'provider': provider}})

@app.route('/login/')
def login():
    if valid:
        auth.login(request.form['user_id'])
    return ''

@app.route('/logout/')
def logout():
    auth.logout()
    reutrn ''

@app.route('/session-user-id/')
def session_user_id():
    return str(auth.user_id)

@app.route('/auth-protected/')
@auth.require.auth(401)
def auth_protected():
    return ''

@app.route('/some-role-protected/')
@auth.require.some_role(403)
def some_role_protected():
    # user has permission if their `user.roles` list contains `'some_role'`
    return ''
```
{'fraction_non_alphanumeric': 0.11448874851954205, 'fraction_numerical': 0.006908803789972365, 'mean_word_length': 4.099647710115752, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2154638', 'n_tokens_mistral': 3216, 'n_tokens_neox': 3052, 'n_words': 935}