Tutorial¶
Introduction: Middlewares and URLs¶
A middleware is a class which can be instantiated with an URL.
The middleware is used to provide a Python API for different protocols, those protocol are used as scheme of the middleware URL to identify which one to instantiate.
http://user:pass@localhost:81/path/sub#fragment?foo=bar
The above URL will instantiate the class HTTPMiddleware
:
HTTPMiddleware(
username='user',
password='pass',
hosts=[('localhost', 81)],
path=['path', 'sub'],
fragment='fragment',
foo='bar'
)
NB: As you can see, hosts
is an array, middleware URLs can have multiple
hosts:
http://user:pass@host1:80,host2:80,host3/path/sub#fragment?foo=bar
With the following instantiation:
HTTPMiddleware(
username='user',
password='pass',
hosts=[
('host1', 80),
('host2', 80),
('host3', None)
],
path=['path', 'sub'],
fragment='fragment',
foo='bar'
)
Creating a middleware¶
In order to create a middleware for a protocol (database, messaging, ...), you
just need to create a new class inheriting from the Middleware
class, and
register it:
from link.middleware.core import Middleware, register_middleware
@register_middleware
class MyMiddleware(Middleware):
__protocols__ = ['myprotocol']
def __init__(self, foo=None, *args, **kwargs):
super(MyMiddleware, self).__init__(*args, **kwargs)
self.foo = foo
Then, you can instantiate your new middleware:
mid = Middleware.get_middleware_by_uri('myprotocol://?foo=bar')
assert mid.foo == 'bar'
assert mid.tourl() == 'myprotocol://?foo=bar'
NB: In order to be available for the Middleware.get_middleware_by_uri()
method, the middleware MUST have been imported first (so the register_middleware
decorator can do its job).
Constraints and child middleware¶
Sometimes, your middleware need another middleware to do its job, especially when your protocol can use multiple backends (i.e.: git via SSH or HTTPS).
Middlewares can have a child middleware specified in the URL:
git+ssh://user@remotehost/myproject.git
With the following instantiation:
child = Middleware.get_middleware_by_uri(
'ssh://user@remotehost/myproject.git'
)
mid = MyGitMiddleware(
username='user',
hosts=[('remotehost', None)],
path=['myproject.git']
)
mid.set_child_middleware(child)
Then, you can get the child middleware to use it:
child = mid.set_child_middleware()
Of course, not all middlewares are compatible, you can provide a list of accepted
classes for child middlewares, and the set_child_middleware()
method will check
if the instance match the constraint:
from link.middleware.core import Middleware, register_middleware
@register_middleware
class MyDriver(Middleware):
__protocols__ = ['mybackend']
@register_middleware
class MyMiddleware(Middleware):
__protocols__ = ['myprotocol']
__constraints__ = [MyDriver]
And then, someone can inherit from your driver:
@register_middleware
class MyCustomDriver(MyDriver):
_protocols__ = ['mycustombackend']
And instantiate the whole thing:
mid = Middleware.get_middleware_by_uri(
'myprotocol+mycustombackend://'
)
NB: There is no limit to how much children you want:
proto1+proto2+proto3://
Here:
proto3
is the child middleware ofproto2
proto2
is the child middleware ofproto1
proto1
will be returned byMiddleware.get_middleware_by_uri()
Finally, the class method get_middleware_by_uri()
verify that the new middleware
is an instance of the calling class, thus, this code will raise an exception:
@register_middleware
class MyMiddleware(Middleware):
__protocols__ = ['myprotocol']
@register_middleware
class NotMyMiddleware(Middleware):
__protocols__ = ['notmyprotocol']
mid = MyMiddleware.get_middleware_by_uri('notmyprotocol://')