Fluid Bindings

Database Connection

See fluid – Fluid Bindings for an example of a fluid database connection.

Fluid print()

Writing to standard output and reading from standard input are good use-cases for fluid bindings. This example implements context managers similar to with-input-from-file / with-output-to-file (defined in R5RS) and with-input-from-string / with-output-to-string (defined in Gambit-C). These allow methods using the dynamically parameterized read() and display() operations to have their input and output redirected by the caller. If __future__.print_function is being used, a better implementation would be to override it instead of defining a display() function.

>>> import os, time, threading
>>> from docs.examples.fluidprint import *

>>> def hello():
...     display("Hello, world!")

>>> def capture(thunk):
...     with output_to_string():
...         thunk()
...         return current_output_port().getvalue()

>>> def show():
...     port = current_input_port()
...     display("showing %s:" % port, read().strip())

>>> hello()
Hello, world!

>>> TEMP = "/tmp/fluid-bindings-example.txt"
>>> with output_to_file(TEMP):
...     hello()

>>> capture(hello)
'Hello, world!\n'

>>> with input_from_file(TEMP):
...     t1 = threading.Thread(target=lambda: time.sleep(0.01) or show())
...     t1.start()
...     with input_from_string("main thread has different input"):
...         show()
...         t1.join()
showing <StringIO.StringIO instance ...>: main thread has different input
showing <open file '/tmp/fluid-bindings-example.txt', ...>: Hello, world!

>>> os.unlink(TEMP)

And the implementation:

from __future__ import print_function
import __builtin__, sys, contextlib, StringIO
from md import fluid

__all__ = (
    'display', 'read', 'current_input_port', 'current_output_port',
    'output_to_string', 'input_from_string',
    'output_to_file', 'input_from_file'
)

## Default to None.  This let's the cells play more nicely with code
## that changes sys.stdout/sys.stderr directly (like doctest).
## Binding directly to sys.stdout / sys.stderr is more ideal.
CURRENT_OUTPUT_PORT = fluid.cell(None, type=fluid.acquired)
CURRENT_INPUT_PORT = fluid.cell(None, type=fluid.acquired)

def current_output_port():
    return CURRENT_OUTPUT_PORT.value or sys.stdout

def current_input_port():
    return CURRENT_INPUT_PORT.value or sys.stdin

def display(*args, **kwargs):
    kwargs.setdefault('file', current_output_port())
    return __builtin__.print(*args, **kwargs)

def read(*args):
    return current_input_port().read(*args)

@contextlib.contextmanager
def output_to_string(*args):
    with contextlib.closing(StringIO.StringIO(*args)) as port:
	with CURRENT_OUTPUT_PORT.let(port):
	    yield

@contextlib.contextmanager
def input_from_string(*args):
    with contextlib.closing(StringIO.StringIO(*args)) as port:
	with CURRENT_INPUT_PORT.let(port):
	    yield

@contextlib.contextmanager
def output_to_file(filename, mode='w'):
    with contextlib.closing(open(filename, mode)) as port:
	with CURRENT_OUTPUT_PORT.let(port):
	    yield

@contextlib.contextmanager
def input_from_file(filename, mode='r'):
    with contextlib.closing(open(filename, mode)) as port:
	with CURRENT_INPUT_PORT.let(port):
	    yield

Django Request Middleware

Django views are called with the current request as their first parameter. Sometimes it’s inconvenient to pass this around to every place in your code that needs it. Sometimes it’s even impossible because you’re writing a signal handler or hook for some third-party django plugin that hasn’t anticipated your need to have access to the current request.

Using a fluid cell to track the current request is a simple task using this example middleware:

from md import fluid

CURRENT_REQUEST = fluid.cell(None, type=fluid.private)
current_request = fluid.accessor(CURRENT_REQUEST, 'request')

class RequestMiddleware(object):
    def process_request(self, request):
	CURRENT_REQUEST.value = request

Here is an example site (run it using django-admin.py runserver --settings='docs.examples.django_request'):


### Settings

ROOT_URLCONF = __name__

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'docs.examples.request_middleware.RequestMiddleware'
)


### Views

from django.http import HttpResponse

def example(request):
    return HttpResponse(logic())


### Urls

from django.conf.urls.defaults import patterns

urlpatterns = patterns(
    '',
    (r'^example', example)
)


### Business Logic

from docs.examples.request_middleware import current_request

def logic():
    request = current_request()
    return '%s %s' % (request.method, request.get_full_path())

Transactions

The md.stm module uses fluid bindings to track which journal is currently in scope for a transaction().

from __future__ import absolute_import
import threading
from contextlib import contextmanager
from md import fluid
from .interfaces import *
from .journal import *

__all__ = (
    'initialize',
    'allocate', 'readable', 'writable', 'delete',
    'use', 'transaction', 'transactionally',
    'save', 'rollback', 'commit', 'abort',
    'saved', 'unsaved'
)


### Transasctional Data Type Operations

def allocate(cursor, state):
    return alloc(current_journal(), cursor, state)

def readable(cursor):
    return read_unsaved(current_journal(), cursor)

def writable(cursor):
    return write(current_journal(), cursor)

def delete(cursor):
    return dealloc(current_journal(), cursor)


### Transactions

def use(mem=None):
    assert isinstance(mem, Memory)
    return current_journal(mem or memory())

@contextmanager
def transaction(name='*nested*', autocommit=True, autosave=True):
    try:
	with current_journal(journal(name, current_journal())):
	    yield
	    if autosave: save()
	    if autocommit: commit()
    except Abort:
	pass

def transactionally(proc, *args, **kwargs):
    limit = kwargs.pop('__attempts__', 3)
    autocommit = kwargs.pop('autocommit', True)
    autosave = kwargs.pop('autosave', True)

    for attempt in xrange(limit):
	try:
	    with transaction(autocommit=autocommit, autosave=autosave):
		return proc(*args, **kwargs)
	except CannotCommit as exc:
	    pass
    raise exc

def save(what=None):
    change_state(save_state, current_journal(), what or unsaved())
    return what

def commit(journal=None):
    journal = journal or current_journal()
    return commit_changes(journal.source, journal)

def rollback(what=None):
    change_state(revert_state, current_journal(), what or unsaved())
    return what

def abort():
    raise Abort

def saved():
    return (c.cursor for c in current_journal().changed())

def unsaved():
    return current_journal().unsaved()


### Journal

class acquire_memory(fluid.acquired):
    def localize(self, loc):
	if isinstance(loc.value, Journal):
	    return self.make_location(find_memory(loc.value))
	else:
	    return super(acquire_memory, self).localize(loc)

def find_memory(journal):
    while journal.source:
	journal = journal.source
    return journal

JOURNAL = fluid.cell(type=acquire_memory)

current_journal = fluid.accessor(JOURNAL, name='current_journal')

def current_memory():
    return find_memory(current_journal())

def initialize(mem=None):
    journal = JOURNAL.value
    if isinstance(journal, Journal) and not isinstance(journal, Memory):
	raise RuntimeError('Cannot uninitialize a transaction', journal)
    JOURNAL.value = mem or memory()

Table Of Contents

Previous topic

Examples

Next topic

Software Transactional Memory

This Page