Kinto is a minimalist JSON storage service
(store, sync, share)
Data belong to the users
(not app developers)
With Kinto:
Featured on Hackernews as «Self-hostable alternative to Parse and Firebase»
Examples of available addons:
PostgreSQL backend (recommended):
from kinto_client import Client client = Client(server="https://kinto.dev.mozaws.net/v1") client.get_records(bucket="blog", collection="articles")
kinto-admin Web UI
Coming soon: Web Extensions storage.sync() API
Community apps...
http://fourmilieres.net → Forms service!
Telegram Wall idea by Xavier Orduña
PyBCN Meetup Nov 2015
The main entry point:
from pyramid.config import Configurator def main(**settings): config = Configurator(settings=settings) # Initialization steps using `config`. return config.make_wsgi_app()
With a minimalist app.wsgi file:
from myapp import main config = configparser.ConfigParser() config.read('config.ini') application = main(**dict(config.items('app:main')))
Imperative
config.add_route('hello', '/') config.add_view(view_hello, route_name='hello')
Declarative with decorators:
@view_config(route_name='hello') def view_hello(request): return {"hello": "pybcn"}
(+ explicit config.scan())
Configuration is the project «backbone»
# Map URLs to views config.add_route() config.add_view() # Setup authn/authz config.set_authentication_policy() config.set_authorization_policy() # Add event subscriber config.add_subscriber() # Add custom response renderers config.add_renderer() config.add_response_adapter() # ...and more!
Include any package:
config.include('cornice')
Or via the settings:
pyramid.includes = webmaps_addon
Can be used to modularize any application part like views or event subscribers.
An addon is just a single Python module with a flat def includeme(config):
def includeme(config): # Add custom view renderer. config.add_renderer(name='geojson', factory='webmaps.GeoJSONRenderer')
# config.ini cache_backend = mypackage.cache
Easily load modules from settings files:
# main.py settings = config.get_settings() cache_mod_name = settings['cache_backend'] cache_module = config.maybe_dotted(cache_mod_name) backend = cache_module.Cache(settings=settings)
Declare interfaces and register components:
from pyramid.interfaces import IRoutesMapper mapper = DummyRoutesMapper() config.registry.registerUtility(mapper, IRoutesMapper)
Other parts of the code can query the registry:
route_mapper = request.registry.queryUtility(IRoutesMapper) info = route_mapper(request)
Application initialization:
class ServerFlushed(object): def __init__(self, request, timestamp): self.request = request self.timestamp = timestamp
Trigger event from view:
from .events import ServerFlushed def view_flush_post(request): request.registry.storage.flush() event = ServerFlushed(request, timestamp=datetime.now()) request.registry.notify(event) return {"status": "ok"}
Subscribe to event during initialization:
def on_server_flush(event): request = event.request # Add header to response request.response.headers['Alert'] = 'Flush' config.add_subscriber(on_server_flush, ServerFlushed)
Executed synchronously → use job queue for long tasks
Example of domain specific initialization method:
def add_api_capability(config, identifier, description=""): capability = dict(description=description) # The application registry is a singleton config.registry.api_capabilities[identifier] = capability config.add_directive('add_api_capability', add_api_capability)
New initialization directive becomes available:
config.add_api_capability('history', description="History plugin")
This view exposes what plugins have registered via our custom method:
@view_config(route_name='hello') def get_hello(request): data = { 'capabilities': request.registry.api_capabilities } return data
Craft your own special-purpose, domain-specific Web system → «framework framework»
from myapp import main class PluginSetupTest(unittest.TestCase): settings = { 'pyramid.includes': 'extra_plugin' } def __init__(self, *args, **kwargs): super(WebTest, self).__init__(*args, **kwargs) wsgi_app = testapp(self.settings) self.app = webtest.TestApp(wsgi_app) self.headers = {"Content-Type": "application/json"} def test_capability_is_shown_in_hello_view(self): resp = self.app.get("/hello", headers=self.headers) assert "extra_plugin" in resp.json["capabilities"]
class MyAuthz(Authorization): def permits(self): permits = super(MyAuthz, self).permits() return permits and custom_check()
With inheritance, substitution occurs before instantiation.
With composition, we can do:
class Authorization: def permits(self): return self.context.is_allowed() authz.context = MyContext()
Prefer composition because:
Table of contents | t |
---|---|
Exposé | ESC |
Autoscale | e |
Full screen slides | f |
Presenter view | p |
Source files | s |
Slide numbers | n |
Blank screen | b |
Notes | 2 |
Help | h |