<script>
-hell in templates
vendor/
directories, no dependencies
management
In template.html,
<script src="/path/to/underscore.js" type="text/javascript"></script>
<script type="text/javascript">
_.each(document.getElementsByTagName("a"), function(item) {
if (item.href.indexOf("http://") || item.href.indexOf("https://")) {
item.href = "/go?" + item.href;
item.onclick = function() {
item.classList.add("visited-link");
return False;
}
}
});
...
</script>
In template.html,
<script src="/path/to/bundle.js" type="text/javascript"></script>
<script type="text/javascript">
processExternalLinks(document.getElementsByTagName("a"));
</script>
In js/external-links.js,
import {each} from "underscore";
function handleExternalLinkClick(evt) {
evt.preventDefault();
evt.target.classList.all("visited-link");
}
export function processExternalLinks(elements) {
each(elements, item => {
if (/^https?\:\/\//.test(item.href)) {
item.href = "/go?" + item.href;
item.addEventListener("click", handleExteranlLinkClick);
}
});
}
export default {
processExternalLinks: processExternalLinks
};
./lib/myLibrary.js
export function myFunction() { ... }
export default {
myFunction: myFunction
};
./app.js
import myLibrary from "./lib/myLibrary";
import {myFunction} from "./lib/myLibrary";
const unchangable = true;
let changeable;
changeable = true;
unchangeable = false; // Error
const episodes = [...];
const aNewHope = episodes.filter(item => item.episode === "IV");
const episodeTitles = episodes.map(item => {
return item.title.toUpperCase();
})
class ANewHope extends Episode {
id = "IV";
name = "A New Hope";
constructor() {
super();
this.directedBy = "George Lucas";
}
render() {
...
}
}
const aNewHope = new ANewHope();
const episode = 'IV';
const title = 'A New Hope';
`A long time ago in a galaxy far,
far away...
STAR
WARS
Episode ${episode}
${title.toUpperCase()}
It is a period of civil war.
Rebel spaceships, striking
from a hidden base, have won
their first victory against
the evil Galactic Empire.`
const [first, , third] = [1, 2, 3, 4, 5, 6];
const {episode, title} = {
episode: "IV",
title: "A New Hope",
opening: "..."
};
const {episode: id, title: name} = {...};
var mapping = new Map();
map.set("IV", "A New Hope");
map.get("IV") === "A New Hope";
var episodes = new Set();
episodes
.add("A New Hope")
.add("The Empire Strikes Back")
.add("Return of the Jedi")
.add("A New Hope");
episodes.has("A New Hope");
episodes.size === 3;
import React, {Component} from "react";
class EuroPython extends Component {
render() {
return (
<div>Hello, EuroPython 2015!</div>
);
}
}
React.render(<EuroPython />, document.getElementById("react-container"));
class EuroPython extends Component {
state = {clicks: 0};
handleClick = () => {
this.setState({clicks: this.state.clicks + 1});
}
render() {
const {clicks} = this.state;
return (
<div>
This button clicked {clicks} time(s).<br />
<button onClick={this.handleClick}>
Click Me
</button>
</div>
)
}
}
React.render(<EuroPython />, document.getElementById("react-container"));
import Markdown from "./components/Markdown";
class CommentForm extends Component {
state = {comment: "", preview: false};
handleCommentChange = (evt) => {
this.setState({comment: evt.target.value});
}
handlePreviewClick = (evt) => {
this.setState({preview: true});
}
render() {
const {comment, preview} = this.state;
if (preview) return <Markdown>{comment}</Markdown>;
return (
<div>
<textarea onChange={this.handleCommentChange} placeholder="Your Comment" value={comment} />
<button onClick={this.handlePreviewClick}>Preview</button>
</div>
);
}
}
class Comments extends Component {
state = {data: [], status: null};
componentDidMount() {
this.loadData();
}
loadData = () => {
fetch(apiUrl)
.then(response => {
if (response.ok()) {
return Promise.resolve(response.json());
}
return Promise.reject(new Error(response.statusText || response.status));
})
.then(
json => {
this.setState({data: json, status: true});
},
() => {
this.setState({data: [], status: false});
})
);
}
...
...
render() {
const {data, status} = this.state;
if (status === null) {
... // Loading
} else if (status === false) {
... // Server error
} else if (!data.length) {
... // Empty data
} else {
... // Valid data
}
}
}
class Comments extends Component {
...
render() {
const content = this.state.data.map(item => (
<Comment data={item} key={"comment-" + item.id} />
));
}
}
class Comment extends Component {
static defaultProps = {data: {}};
static propTypes = {
data: PropTypes.shape({...})
};
render() {
return (...)
}
}
import asyncio
@asyncio.coroutine
def hello():
return "Hello, world!"
loop = asyncio.get_event_loop()
content = loop.run_until_complete(hello())
print(content)
loop.close()
pip install asyncio
0.16.5
import asyncio
import aiohttp
@asyncio.coroutine
def fetch_page(url):
response = yield from aiohttp.request('GET', url)
assert response.status == 200
return (yield from response.read())
loop = asyncio.get_event_loop()
content = loop.run_until_complete(fetch_page('http://ep2015.europython.eu/'))
print(content)
loop.close()
import asyncio
from aiohttp import web
@asyncio.coroutine
def hello(request):
return web.Response(body='Hello, world!', content_type='text/plain')
app = web.Application()
app.router.add_route('GET', '/', hello)
gunicorn -k aiohttp.worker.GunicornWebWorker -w 9 -t 60 app:app
0.7.0
import asyncio
from aiopg import create_pool
@asyncio.coroutine
def hello(dsn):
pool = yield from create_pool(dsn)
with (yield from pool.cursor()) as cursor:
yield from cursor.execute('SELECT 1')
selected = yield from cursor.fetchone()
assert selected == (1, )
loop = asyncio.get_event_loop()
loop.run_until_complete(hello('dbname=aiopg user=... password=... host=...'))
loop.close()
aioredis
from aio-libs
0.1.5
asyncio_redis
from third-party developers0.13.4
web.Response
import asyncio
from aiohttp import web
@asyncio.coroutine
def index(request):
return web.Response(body='Hello, world!', content_type='text/plain')
views.py
module
web.Application
from aiohttp import web
from . import views
app = web.Application()
app.router.add_route('GET', '/', views.index)
app.py
module
gunicorn -b 0.0.0.0:8000 -k aiohttp.worker.GunicornWebWorker -w 9 -t 60 project.app:app
--reload
flag to automatically reload Gunicorn
server on code change
In views.py,
@asyncio.coroutine
def search(request):
"""Search by query from GET params."""
query = request.GET['query']
locale = request.GET.get('locale', 'uk_UA')
...
return web.Response(...)
In views.py,
@asyncio.coroutine
def submit(request):
"""Submit form POST data."""
data = yield from request.post()
# Now POST data available as ``request.POST``
...
return web.Response(...)
In app.py,
app.router.add_route('GET', '/search', views.search)
app.router.add_route('POST', '/submit', views.submit)
In views.py,
@asyncio.coroutine
def project(request):
project_id = request.match_info['project_id']
...
return web.Response(...)
In app.py,
app.router.add_route('GET', '/projects/{project_id}', views.project)
Or even,
app.router.add_route('GET', '/projects/{project_id:\d+}', views.project)
In app.py,
app.router.add_route('GET', '/projects', views.projects, name='projects')
app.router.add_route('POST', '/projects', views.add_project)
In views.py,
@asyncio.coroutine
def add_project(request):
data = yield from request.post()
...
url = request.app.router['projects'].url()
return web.HTTPFound(url)
@asyncio.coroutine
def projects(request):
...
from app import app
from flask import current_app
eitherapp
instance for all your needs
In app.py,
from . import settings
...
app['settings'] = settings
from app import app
In views.py,
@asyncio.coroutine
def search(request):
settings = request.app['settings']
query = request.GET['query']
locale = request.GET.get('locale', settings.DEFAULT_LOCALE)
...
return web.Response(...)
web.Application
accepts optional middlewares
factories sequence
In app.py,
@asyncio.coroutine
def trivial_middleware(app, handler):
@asyncio.coroutine
def middleware(request):
return (yield from handler(request))
return middleware
...
app = web.Application(middlewares=[trivial_middleware])
aiohttp_session
aiohttp_debugtoolbar
In app.py,
import aiohttp_debugtoolbar
from aiohttp_debugtoolbar import toolbar_middleware_factory
from aiohttp_session import session_middleware
from aiohttp_session.cookie_storage import EncryptedCookieStorage
app = web.Application(middlewares=[
toolbar_middleware_factory,
session_middleware(EncryptedCookieStorage(b'1234567890123456'))
])
aiohttp_debugtoolbar.setup(app)
In app.py,
@asyncio.coroutine
def errorhandler_middleware(app, handler):
@asyncio.coroutine
def middleware(request):
try:
return (yield from handler(request))
except web.HTTPError as err:
# As it special case we could pass ``err`` as second argument to
# error handler
return (yield from views.error(request, err))
return middleware
0.1.1
session_middleware
to middleware
factories sequence
In views.py,
from aiohttp_session import get_session
@asyncio.coroutine
def login(request):
...
session = yield from get_session(request)
session['user_id'] = user_id
return web.Response(...)
Jinja2 & Mako supported via aiohttp_jinja2 & aiohttp_mako
In app.py,
import aiohttp_jinja2
import jinja2
...
aiohttp_jinja2.setup(
app,
loader=jinja2.FileSystemLoader('/path/to/templates')
)
In views.py,
import aiohttp_jinja2
@aiohttp_jinja2.template('index.html')
def index(request):
return {'is_index': True}
In views.py,
from aiohttp_jinja2 import render_template
@asyncio.coroutine
def index(request):
return render_template('index.html', request, {'is_index': True})
In utils.py,
import ujson
from aiohttp import web
def json_response(data, **kwargs):
# Sometimes user needs to override default content type for JSON
kwargs.setdefault('content_type', 'application/json')
return web.Response(ujson.dumps(data), **kwargs)
Note: I recommend to use ujson for work with JSON data in Python, cause of speed.
In views.py,
from .utils import json_response
@asyncio.coroutine
def api_browser(request):
return json_response({
'projects_url': request.app.router['projects'].url(),
})
Important: It's highly recommend to use nginx, Apache, or other web server for serving static files in production.
In app.py,
app.router.add_static('/static', '/path/to/static', name='static')
In views.py,
@aiohttp_jinja2.template('index.html')
def index(request):
return {'app': request.app, 'is_index': True}
In index.html,
<script src="{{ app.router.static.url(filename="dist/js/project.js") }}"
type="text/javascript"></script>
from contextlib import contextmanager
@contextmanager
def add_route_context(app, views, url_prefix=None, name_prefix=None):
def add_route(method, url, name):
view = getattr(views, name)
url = ('/'.join((url_prefix.rstrip('/'), url.lstrip('/')))
if url_prefix
else url)
name = '.'.join((name_prefix, name)) if name_prefix else name
return app.router.add_route(method, url, view, name=name)
return add_route
In app.py,
from .api import views as api_views
with add_route_context(app, api_views, '/api', 'api') as add_route:
add_route('GET', '/', 'index')
add_route('GET', '/projects', 'projects')
add_route('POST', '/projects', 'add_project')
add_route('GET', '/project/{project_id:\d+}', 'project')
add_route('PUT', '/project/{project_id:\d+}', 'edit_project')
add_route('DELETE', '/project/{project_id:\d+}', 'delete_project')
In api/views.py,
import asyncio
from ..utils import json_response
@asyncio.coroutine
def index(request):
router = request.app.router
project_url = router['api.proejct'].url(parts={'project_id': 42})
project_url = project_url.replace('42', '{project_id}')
return json_response({
'urls': {
'add_project': router['api.add_project'].url(),
'project': project_url,
'projects': router['api.projects'].url(),
}
})
MappingTypeProxy
In app.py,
from rororo.settings import immutable_settings
from . import settings
def create_app(**options):
settings_dict = immutable_settings(settings, **options)
app = web.Application()
app['settings'] = settings_dict
...
return app
inject_settings
setup_locale
setup_logging
setup_timezome
to_bool
default_logging_dict
update_sentry_logging
class Application(web.Application):
def __init__(self, **kwargs):
self['settings'] = kwargs.pop('settings')
super().__init__(**kwargs)
def make_handler(self, **kwargs):
kwargs['debug'] = self['settings']['DEBUG']
kwargs['loop'] = self.loop
return self._handler_factory(self, self.router, **kwargs)
In api/views.py,
from rororo.schemas import Schema
from . import schemas
@asyncio.coroutine
def add_project(request):
schema = Schema(schemas.add_project, response_factory=json_response)
data = schema.validate_request((yield from request.post()))
...
return schema.make_response(project_dict)
In api/schemas/add_project.py,
from jsl import Document, IntegerField, StringField
class Project(Document):
name = StringField(min_length=1, max_length=32, required=True)
slug = StringField(min_length=1, max_length=32, required=True)
description = StringField(max_length=255)
class Response(Project):
id = IntegerField(minimum=0, required=True)
request = Project.get_schema()
response = Response.get_schema()
Or use plain Python dicts instead
In api/schemas/__init__.py,
from . import add_project # noqa
aiohttp.web
, easy to start and easy to useaiopg.sa
allows you to forget about ORMrororo
contains useful helpers for
aiohttp.web
apps
async def logout(request):
session = await get_session(request)
session.invalidate()
return Response(status=204)
async def projects(request):
projects = []
async with create_engine(DSN) as conn:
query = ...
async for project in conn.execute(query):
projects.append(project)
return json_response(projects)