Skip to content
Iury O. G. Figueiredo edited this page Nov 27, 2020 · 6 revisions

Welcome to the untwisted wiki!

In this wiki you'll find enough documentation to get along with untwisted design. :)

The event-driven paradigm is a powerful tool to deal with some specific systems, in the event-driven paradigm the flow of the program is based on events. Events are intrinsically related to handles, if there is a handle mapped to an event and such an event occurs then the handle is called. An event can carry arguments that better characterize the type of happening that is related to the event.

In untwisted context, events can be any kind of python object, an integer, a function, a class, an exception etc. It is possible to map one or more handles to a given event, when the event occurs then the handles will be called with the event's arguments.

In event-driven applications there will exist generally an event loop that is responsible by processing a set of handles according to some set of events. Untwisted reactor is responsible by processing a set of handles according to a few basic events. These basic events are related to the state of a given set of sockets. So, when a socket is ready to send data it spawns an event WRITE, when there is data available for reading then it spawns READ, etc.

Untwisted is built on top of the concepts of super sockets and extensions. A super socket is a socket that is also an event emitter. The reactor scales opened super sockets for read and write events.

When a given handle that is mapped to a given event also spawns an event then it is said to be an extension in the untwisted context.

A handle or an extension can be any kind of callable object. The example below shows a basic example of a string event and a function being used as a handle.

from untwisted.network import SuperSocket

def handle(ssock):
    print('200 was spawned!')

ssock = SuperSocket()
ssock.add_map('200', handle)
ssock.drive('200')

That would output:

200 was spawned!

That should be useful to help you to understand how untwisted is used to implement networking applications.

The line below imports the most important class in the whole framework.

from untwisted.network import SuperSocket

The module below contains three important abstractions that are used in untwisted to perform I/O asynchronously. Untwisted also offers SSL connections on sockets.

untwisted.network

Here it is defined a handle that will be called when the string '200' is spawned as an event.

def handle(ssock):
    print('200 was spawned!')

It instantiates a SuperSocket instance it is pretty much like a normal socket, it has a file descriptor etc. When a SuperSocket class is instantiated it registers itself in untwisted default reactor. Untwisted default reactor is select based on Windows or platforms that do not implement Epoll. Epoll reactor is default on Linux systems.

ssock = SuperSocket()

It merely binds the so defined function to the string event '200'. That basically means that when such an event occurs then that handle will be called.

ssock.add_map('200', handle)

This basically spawns the string event '200' when it happens the handle function is called with a ssock socket instance as argument. That is useful to have handles called with the SuperSocket instance where it arouse the event.

ssock.drive('200')

There will exist circumstances where an application has tons of opened super sockets and handles mapped to them and events will be raising all time. When a handle knows from where an event comes it is possible to send data back through the super socket or take specific action to change the application state.

Consider the example below as a mean of getting yourself more familiar with untwisted workings.

from untwisted.network import SuperSocket

def handle_response(ssock, data):
    version, code, reason = data.split(' ')
    ssock.drive(code)

def handle_success(ssock):
    print('200 was spawned!')

RESPONSE = 0
ssock = SuperSocket()
ssock.add_map(RESPONSE, handle_response)
ssock.add_map('200', handle_success)

ssock.drive(RESPONSE, 'HTTP/1.1 200 OK')

That would output:

200 was spawned!

The code above simulates part of a Web Server response after a succesful request. It first defines two handles.

def handle_response(ssock, data):
    version, code, reason = data.split(' ')
    ssock.drive(code)

The above handle receives an argument that is supposed to be a string with a defined format. In the context it would be a http response head line. It merely splits the string into three chunks and spawns the second chunk as an event in the SuperSocket instance where it was called from.

The second handle just prints out the message that informs the string event '200' just happened.

def handle_success(ssock):
    print('200 was spawned!')

The following statement just spawns the RESPONSE event that was previously defined as an integer. It also sends the string 'HTTP/1.1 200 OK' as a RESPONSE event argument.

ssock.drive(RESPONSE, 'HTTP/1.1 200 OK')

Untwisted has builtin extensions/handles that are used to abstract the complexity of implementing client or server networking applications. You can implement your own extensions and reuse them along your applications.

The function defined in the previous example is an extension since it spawns events when it is called.

def handle_response(ssock, data):
    version, code, reason = data.split(' ')
    ssock.drive(code)

On the other hand the function below isn't an extension since it doesn't spawn events from.

def handle_success(ssock):
    print('200 was spawned!')

Let us try something hotter. Consider the following example that helps to understand a bit of untwisted reactor.

from untwisted.network import SuperSocket
from untwisted.event import WRITE
from untwisted import core
from untwisted.core import die

def handle_write(ssock):
    print('Socket is available for writing!')
    die('Stopping reactor!')

ssock = SuperSocket()
sock.connect_ex(('www.google.com.br', 443))

ssock.add_map(WRITE, handle_write)
core.gear.mainloop()

That would output:

Socket is available for writing!
Stopping reactor!

The below line is importing one of the builtin events of untwisted. Such a module event contains other basic events that are used along untwisted extensions.

from untwisted.event import WRITE

The following statement is responsible for connecting to the host www.google.com.br on port 443. It is made in a non blocking manner due to the usage of the method connect_ex.

The line below just maps the WRITE event to the handle_write.

ssock.add_map(WRITE, handle_write)

Once everything is set then the reactor is started with:

core.gear.mainloop()

When the reactor is started it will keep scaling sockets for reading or writing and when one is available for either reading or writing then events like READ or WRITE are spawned in the given SuperSocket.

In the previous example when the super socket is available for writing it calls handle_write function then a message is printed out and the reactor is stopped due to the call to the die function.

In case the call to the die function wasn't made then the reactor would run forever. That is usually what you want in your networking applications however in the previous example it wouldn't be much of a pedagogical approach.