# Exploiting Python's data model¶

To make our session store easy to use as if it was a dictionary, we implement out middleware by crafting a class which behaves like that. For that purpose, we are taking a small diversion from WSGI to Python's Data Model. We enter the realm of so called magic methods also known as __dunder__ (which stands of double underscore).

The first thing to know about special methods is that they are meant to be called by the Python interpreter, and not by you. [Fluent Python, pg. 8].

Our goal is to build the following elements of a WSGI framework.

## A Dictionary like Session storage¶

Using a Python session like storage, we should be able to check membership using in.

>>> 'a72e7a6b4fcf8ae611953' in session_storage


We should also be able to retrieve items as if it was a dictionary.

>>> session_storage['a72e7a6b4fcf8ae611953']


Our goal is to create a middleware that stores information in some kind of a persistent storage. For simplicity we start by writing this information to a file on a disk, but this can easily be extended to a Redis storage, MongoDB or any database of your liking. Let's assume though that session data is unstructured might look like a dictionary of session ID as keys, with values which are another dictionary:

sessions = {
"id1": {'data': {'k1': 'v1', 'k2': 'v2', ..., 'kn': 'vn'}},
"id2": {'data': {'ka1': 'va1', 'ka2': 'va2', ..., 'kan': 'van'}},
# ...
}


A Request object is a wrapper around the environment. Some frameworks, like Bottle, Flask and WebOb, make the attribute of a Request object read only.

The most obvious way is to use @property, but this creates a very verbose code, here is an example from web.request.Request (which is almost 1000 lines of code long!):

class BaseRequest(object):

# ...

@property
def host_url(self):
"""
The URL through the host (no path)
"""
e = self.environ
scheme = e.get('wsgi.url_scheme')
url = scheme + '://'
host = e.get('HTTP_HOST')
if host is not None:
if ':' in host and host[-1] != ']':
host, port = host.rsplit(':', 1)
else:
port = None
else:
host = e.get('SERVER_NAME')
port = e.get('SERVER_PORT')
if scheme == 'https':
if port == '443':
port = None
elif scheme == 'http':
if port == '80':
port = None
url += host
if port:
url += ':%s' % port
return url


We can do much better than creating methods and decorating them with properties. Instead we craft a special container class which wraps the environment and allows us to access keys as if they where attributes.

>>> req = Request(environment)
>>> req.request_method  REQUEST_METHOD
'GET'


Sometimes accessing a property can be expensive! As can be seen in the example above, building the host URL, we make 4 dictionary lookups, which isn't taking much, but if we pass our Request object through 4 middlewares each asking for this property, we already make 16 lookups. This could be improved by calculating such properties and save the result, by using a specially crafted decorator:

@cached_property
def host_url(self):
"""
This will be calucalated only once
"""
# ...
# ...
return url


## Ability to extend¶

If we want our framework to be public it might be a good idea to have some kind of a plugin system. But even if our framework is intended for a use of a small team of developers, it might be a good idea to supply some base classes and maybe meta-classes to make sure development and extension are easy enough, but also safe to use. For example, suppose we want to replace our dictionary based session with a Redis cache, but we don't want to break the API. We do this with caution, and we think, we might want to replace Redis in some other Key-Value storage. We demonstrate how the use of meta classes can enforce programmers to obey some certain structure, without throwing a RuntimeError or an AttributeError, which in some cases might be too late.

>>> class RedisSession(BaseSession):
...     pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __new__
ValueError: RedisSession must define a method called __setitem__