Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asynchronously deferred content generation. #9

Open
amcgregor opened this issue Oct 8, 2015 · 0 comments
Open

Asynchronously deferred content generation. #9

amcgregor opened this issue Oct 8, 2015 · 0 comments

Comments

@amcgregor
Copy link
Member

Note: Unlike #2, this ticket is more about the inversion of the template include pattern and less about the pure integration of cinje into an asyncio environment, though such integration does need to be kept in mind.

Currently the yield mechanics are used to identify an insertion point for additional content in a template, creating a "wrapping" template function. This gives you an entry point of the most specific template, which then typically dives into its wrapper, then deeper into whatever that is wrapped by internally, etc., etc.

The inverse approach, where you have a "layout" template which you populate with content blocks, is also extremely useful. Because templates are first-class functions, you can easily pass individual, or whole lists of template functions around, using functools.partial to bind values to them for use at render-time. This is a start, as during template processing the layout template would descend into each content block to render, blocking the overall process. Instead, when a child template is encountered, if it's an async def it can be sent up to the reactor for processing. In the resulting HTML insert a placeholder or marker (a la the animated bars on Facebook posts as they load), finally streaming the layout template, then individual content blocks, as MIME multipart. Should also optionally be able to execute the template more classically, without async deferral.

Streaming chunks to the browser as completed can be accomplished via MIME multipart AJAX, via something like mpAjax. A rough p-code example using Futures:

def layout(reactor, *blocks):
    """Mock boostrap row/column layout."""

    # Prepare some content.
    _buffer = []
    _tasks = []

    for block in blocks:
        if not _buffer or not block:
            if _buffer:
                _buffer.extend(('</div><div class="row">\n', ))

            _buffer.extend(('<div class="row">\n', ))

            if not block:
                continue

        _tasks.append(reactor.submit(block))
        _buffer.extend(('<div class="placeholder" data-await="', id(_tasks[-1]), '"></div>\n'))

    return (''.join(_buffer), _tasks)


def render_page():
    identifier = "gc0p4Jq0M2Yt08jU534c0p"

    response.content_type = "multipart/mixed; boundary=" + identifier

    page, content = layout(executor, [
            "some render function, first column",
            "some other render function, second column",
            None,
            "lastly a full width footer",
        ])

    yield page

    for chunk in as_completed(content):
        yield '--' + identifier + '\nIdentifier: ' + str(id(chunk)) + '\n\n' + chunk.result()

    yield '--' + identifier + '--\n'
@amcgregor amcgregor modified the milestones: Matterhorn (Future), Eiger (Feature Release, 1.1) Oct 8, 2015
@amcgregor amcgregor modified the milestones: Matterhorn (Future), Eiger (Feature Release, 1.1) Dec 7, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant