<commit_before><?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Twisted as an Erlang node</title>
  </head>

  <body>
    <h1>Twisted as an Erlang node</h1>

    <h2>Prequesites</h2>

    <p>
        In this document, we'll assume that you have a basic knowledge of
        Twisted (how to create a client, how to create a server, what is
        the protocol/factory mechanism, what is a deferred), and of Erlang
        (what is a node, what is the EPMD, what are the basic types). Please
        refer to the respective documentation if it's not the case.
    </p>

    <h2>Invoking methods on an Erlang node</h2>

    <p>
        To create connection to an erlang node, you'll need to get the cookie
        and create a node name. The utility functions
        <code class="API" base="twotp.node">readCookie</code>
        and  <code class="API" base="twotp.node">buildNodeName</code>
        are here to help you out: the first read the cookie at the usual
        place ($HOME/.erlang.cookie), the second append the name of the
        host to the name you have chosen.
    </p>

    <p>
        Then, instantiate the class
        <code class="API" base="twotp.node">Process</code>
        with the infos, and call callRemote on it. This method will:
        <ul>
            <li>create a connection with the EPMD</li>
            <li>ask for the port info of the node</li>
            <li>connect to the node</li>
            <li>return the connected node client protocol</li>
            <li>make a RPC call on the connected node</li>
            <li>return the result to you</li>
        </ul>
    </p>

    <pre class="python">
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName


def gotResult(resp):
    print "Got response", resp
    reactor.stop()


def eb(error):
    print "Got error", error
    reactor.stop()


cookie = readCookie()
nodeName = buildNodeName('nodename')
process = Process(nodeName, cookie)
process.callRemote("foo", "file", "get_cwd").addCallback(gotResult).addErrback(eb)
reactor.run()
    </pre>

    <h2>Instantiating a node</h2>

    <p>
        The process for creating a node is really straightforward: you just
        have to call the <code>listen</code> of your <code>Process</code>.
        What happens behind the scene is that first it creates a node server
        factory, and makes it listen on an arbitrary port. Then it creates a
        client connection to the EPMD, giving the server port as argument.

        The EPMD should be started outside Twisted. It is
        started automatically when you start an erlang application.
    </p>

    <pre class="python">
import sys
from twisted.python import log
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName

cookie = readCookie()
nodeName = buildNodeName('nodename')
log.startLogging(sys.stdout)
process = Process(nodeName, cookie)
process.listen()
reactor.run()
    </pre>

    <p>
        Of course, for now it does nothing useful, so we now see how to
        get data from an erlang nodes.
    </p>


    <h2>Receiving method calls from an Erlang node</h2>

    <p>
        The key method here is the method <code>registerModule</code> of the
        <code>Process</code> class: this method takes a name and an object. The
        name is the name of the module used when making a call from Erlang. The
        object is an instance of a class with methods prefixed by
        <code>remote_</code>.
    </p>

    <pre class="python">
import sys
from twisted.python import log
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName

class Proxy(object):
    def remote_double(self, arg):
        return arg * 2

cookie = readCookie()
nodeName = buildNodeName('nodename')
log.startLogging(sys.stdout)
process = Process(nodeName, cookie)
process.registerModule("math", Proxy())
process.listen()
reactor.run()
    </pre>

    <p>
        The script above define a <code>Proxy</code> with a
        <code>remote_double</code> method. We can publish with the keyword
        math, which creates a module named math with the method double. We can
        now call this method in an Erlang console:
    </p>

    <pre>
(erlang@node)1> rpc:call('nodename@node', math, double, [3]).
{ok,6}
    </pre>

    <h2>Using the twotp twisted plugin</h2>

    <p>
        When you install twotp, and type
        <code class="shell">twistd --help</code> in a shell, you should see a
        new <code>twotp</code> command. It has several options:
    </p>

<pre class="shell">
$ twistd twotp --help
Usage: twistd [options] twotp [options]
Options:
  -n, --nodename=  Name of the node [default: twisted]
  -c, --cookie=    Erlang cookie value [default: ]
  -m, --methods=   Methods holder [default: ]
      --version    
      --help       Display this help and exit.
</pre>

    <ul>
        <li>
            <code>-n</code> or <code>--nodename</code> let you specify the name
            of the node. It's the equivalent of the <code>-sname</code> option
            of <code>erl</code>.
        </li>
        <li>
            <code>-c</code> or <code>--cookie</code> let you specify the
            coookie. It's the equivalent of the <code>-setcookie</code> option
            of <code>erl</code>.
        </li>
        <li>
            <code>-m</code> or <code>--methods</code> let you specify the
            methods you want to publish. It takes the fully qualified name of a
            python dictionary.
        </li>
    </ul>

    <p>
        Let's create a module rpc.py and put some objects with methods in them.
    </p>

<pre class="python">
class File(object):

    def remote_get_cwd(self):
        from twisted.python import filepath
        return filepath.FilePath(".").path
        

class Time(object):

    def remote_get_time(self):
        import time
        return time.time()


methods = {"file": File(), "time": Time()}
</pre>

    <p>
        Then launch on the command line twistd, after making sure your module
        is in an importable place.
    </p>

<pre class="shell">
$ twistd -n twotp --methods rpc.methods
2009-05-09 21:24:31+0200 [-] Log opened.
2009-05-09 21:24:31+0200 [-] twistd 8.2.0+r26899 (/usr/bin/python 2.5.2) starting up.
2009-05-09 21:24:31+0200 [-] reactor class: twisted.internet.selectreactor.SelectReactor.
2009-05-09 21:24:31+0200 [-] &lt;class 'twotp.server.NodeServerFactory'&gt; starting on 40962
2009-05-09 21:24:31+0200 [-] Starting factory &lt;twotp.server.NodeServerFactory object at 0x9896b2c&gt;
2009-05-09 21:24:31+0200 [-] Starting factory &lt;twotp.epmd.PersistentPortMapperFactory instance at 0x952978c&gt;
2009-05-09 21:24:31+0200 [Uninitialized] Connection made with EPMD
</pre>

    <p>
        This is it! You've exposed methods callable from Erlang. Let's fire an
        erlang shell to try.
    </p>

<pre class="shell">
(erlang@node)1&gt; rpc:call(twisted@node, file, get_cwd, []).
{ok,"/home/user"}
(erlang@node)2&gt; rpc:call(twisted@node, time, get_time, []).
{ok,1241897194.455415}
</pre>

    <p>
        Now try and define some interesting methods instead.
    </p>

    <h2>Linking and monitoring an Erlang process</h2>

    <p>
        To get exit information with an Erlang process, you can create a link
        or a monitor operation with it. A link is symmetrical, which means that
        the remote process can be notified of the exit status of the Twisted
        process as well. Let's try monitoring first.
    </p>

    <p>
        Open an Erlang shell, create and register a simple process that can
        fail.
    </p>

    <pre>
(erlang@node)1&gt; F = fun() -&gt; receive X -&gt; list_to_atom(X) end end.
#Funi&lt;erl_eval.20.67289768&gt;
(erlang@node)2&gt; Pid1 = spawn(F).
&lt;0.42.0&gt;
(erlang@node)3&gt; register(pid1, Pid1).
true
    </pre>

    <p>
        It registers a process under the name <code>pid1</code>, which can
        easily fails. Now let's start a twisted process monitoring that pid:
    </p>

    <pre class="python">
import sys
from twisted.python import log
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName

def gotWhereis(pid):
    return process.monitor(pid).addCallback(gotRef)

def gotRef(ref):
    process.addMonitorHandler(ref, gotExit)

def gotExit(reason):
    print "Remote process exited with:", reason

cookie = readCookie()
nodeName = buildNodeName('nodename')
log.startLogging(sys.stdout)
process = Process(nodeName, cookie)
process.whereis('erlang', 'pid1').addCallback(gotWhereis)
reactor.run()
    </pre>

    <p>
        There are several interesting methods here. First, <code>whereis</code>
        allows you to get a Pid object from a name, if this name is registered
        on the other node. Then <code>monitor</code> creates a monitor link,
        and returns the monitor reference in its callback. You can then call
        <code>addMonitorHandler</code> as many times you want with this
        reference: the specified callbacks will be called with the exit
        notification happens.
    </p>

    <p>
        Return to the Erlang shell, and create an error on the process:
    </p>

    <pre>
(erlang@node)4&gt; pid1 ! 1.

=ERROR REPORT==== 9-May-2009::19:18:29 ===
Error in process &lt;0.42.0&gt; on node 'erlang@node' with exit value: {badarg,[{erlang,list_to_atom,[1]}]}

1
    </pre>

    <p>
        If everything went fine, you should have the following log in your
        Twisted application:
    </p>

    <pre>
2009-05-09 19:18:29+0200 [NodeClientProtocol,client] Remote process exited with: (&lt;Atom at 0x8b9840c, text 'badarg'&gt;, [(&lt;Atom at 0x8b983cc, text 'erlang'&gt;, &lt;Atom at 0x8b9844c, text 'list_to_atom'&gt;, [1])])
    </pre>

    <h2>An example: using a Mnesia database</h2>

    <p>
        To try the communication as client, we'll set up a mnesia database to
        be used within a Twisted application. Please refer to the mnesia
        documentation for further information. Note that there is nothing new
        here: it's just a concrete example to put the things together.
    </p>

    <p>
        First create a file named twisted.hrl with following record:
    </p>

    <pre class="erlang">
-record(user, {id, name}).
    </pre>

    <p>
        Then create a file named twisted.erl
    </p>

    <pre class="erlang">
-module(twisted).
-export([init/0, insert_user/2, get_user/1]).
-include("twisted.hrl").

init() -&gt;
    mnesia:create_table(user,
                        [{attributes, record_info(fields, user)}]).

insert_user(UserName, UserId) -&gt;
    User = #user{id=UserId, name=UserName},
    Fun = fun() -&gt;
            mnesia:write(User)
        end,
    mnesia:transaction(Fun).

get_user(UserId) -&gt;
    Fun = fun() -&gt;
            [User] = mnesia:read(user, UserId, read),
            User
        end,
    case mnesia:transaction(Fun) of
        {atomic, User} -&gt;
            User
    end.
    </pre>

    <p>
        Now fire an erlang interpreter. We'll compile the twisted file, start
        the mnesia database, create the table, and insert a test user.
    </p>

    <pre class="shell">
$ erl -sname twisted_mnesia

(twisted_mnesia@localhost)1&gt; c(twisted).
{ok,twisted}
(twisted_mnesia@localhost)2&gt; mnesia:start().
ok
(twisted_mnesia@localhost)3&gt; twisted:init().
{atomic,ok}
(twisted_mnesia@localhost)4&gt; twisted:insert_user(test1, 1).
{atomic,ok}
(twisted_mnesia@localhost)5&gt; twisted:get_user(1).
{user,1,test1}
    </pre>

    <p>
        The node is ready to accept our python calls.
    </p>

    <pre class="python">
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName

def gotResult(resp):
    print "Got response", resp
    reactor.stop()

def eb(error):
    print "Got error", error
    reactor.stop()

cookie = readCookie()
nodeName = buildNodeName('nodename')
process = Process(nodeName, cookie)
process.callRemote("twisted_mnesia", "twisted", "get_user", 1
    ).addCallback(gotResult).addErrback(eb)

reactor.run()
    </pre>

    <p>
        If everything is fine, you should get the following response:
    </p>

    <pre class="shell">
Got response (&lt;Atom at -0x48806e54, text 'user'&gt;, 1, &lt;Atom at -0x48806e74, text 'test1'&gt;)
    </pre>

  </body>
</html>

<commit_msg>Add some doc about the send/receive API.<commit_after><?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Twisted as an Erlang node</title>
  </head>

  <body>
    <h1>Twisted as an Erlang node</h1>

    <h2>Prequesites</h2>

    <p>
        In this document, we'll assume that you have a basic knowledge of
        Twisted (how to create a client, how to create a server, what is
        the protocol/factory mechanism, what is a deferred), and of Erlang
        (what is a node, what is the EPMD, what are the basic types). Please
        refer to the respective documentation if it's not the case.
    </p>

    <h2>Invoking methods on an Erlang node</h2>

    <p>
        To create connection to an erlang node, you'll need to get the cookie
        and create a node name. The utility functions
        <code class="API" base="twotp.node">readCookie</code>
        and  <code class="API" base="twotp.node">buildNodeName</code>
        are here to help you out: the first read the cookie at the usual
        place ($HOME/.erlang.cookie), the second append the name of the
        host to the name you have chosen.
    </p>

    <p>
        Then, instantiate the class
        <code class="API" base="twotp.node">Process</code>
        with the infos, and call callRemote on it. This method will:
        <ul>
            <li>create a connection with the EPMD</li>
            <li>ask for the port info of the node</li>
            <li>connect to the node</li>
            <li>return the connected node client protocol</li>
            <li>make a RPC call on the connected node</li>
            <li>return the result to you</li>
        </ul>
    </p>

    <pre class="python">
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName


def gotResult(resp):
    print "Got response", resp
    reactor.stop()


def eb(error):
    print "Got error", error
    reactor.stop()


cookie = readCookie()
nodeName = buildNodeName('nodename')
process = Process(nodeName, cookie)
process.callRemote("foo", "file", "get_cwd").addCallback(gotResult).addErrback(eb)
reactor.run()
    </pre>

    <h2>Instantiating a node</h2>

    <p>
        The process for creating a node is really straightforward: you just
        have to call the <code>listen</code> of your <code>Process</code>.
        What happens behind the scene is that first it creates a node server
        factory, and makes it listen on an arbitrary port. Then it creates a
        client connection to the EPMD, giving the server port as argument.

        The EPMD should be started outside Twisted. It is
        started automatically when you start an erlang application.
    </p>

    <pre class="python">
import sys
from twisted.python import log
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName

cookie = readCookie()
nodeName = buildNodeName('nodename')
log.startLogging(sys.stdout)
process = Process(nodeName, cookie)
process.listen()
reactor.run()
    </pre>

    <p>
        Of course, for now it does nothing useful, so we now see how to
        get data from an erlang nodes.
    </p>


    <h2>Receiving method calls from an Erlang node</h2>

    <p>
        The key method here is the method <code>registerModule</code> of the
        <code>Process</code> class: this method takes a name and an object. The
        name is the name of the module used when making a call from Erlang. The
        object is an instance of a class with methods prefixed by
        <code>remote_</code>.
    </p>

    <pre class="python">
import sys
from twisted.python import log
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName

class Proxy(object):
    def remote_double(self, arg):
        return arg * 2

cookie = readCookie()
nodeName = buildNodeName('nodename')
log.startLogging(sys.stdout)
process = Process(nodeName, cookie)
process.registerModule("math", Proxy())
process.listen()
reactor.run()
    </pre>

    <p>
        The script above define a <code>Proxy</code> with a
        <code>remote_double</code> method. We can publish with the keyword
        math, which creates a module named math with the method double. We can
        now call this method in an Erlang console:
    </p>

    <pre>
(erlang@node)1> rpc:call('nodename@node', math, double, [3]).
{ok,6}
    </pre>


    <h2>Reading data sent by an Erlang node</h2>

    <p>
        This is the next step after using RPC, which is the basic communication
        mechanism. But we can do better using standard process communication of
        Erlang, which provides a nice asynchronous mechanism. The
        <code>receive</code> method of the <code>Process</code> class will
        return a <code>Deferred</code> which will be fired when someone sends a
        message to your process.
    </p>

    <pre class="python">
import sys
from twisted.python import log
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName


def receive(process):
    def cb(resp):
        print "Got response", resp
    def eb(error):
        print "Got error", error
    return process.receive(
        ).addCallback(cb
        ).addErrback(eb
        ).addBoth(lambda x: receive(process))


cookie = readCookie()
nodeName = buildNodeName('nodename')
log.startLogging(sys.stdout)
process = Process(nodeName, cookie)
process.register("main")
process.listen().addCallback(lambda x: receive(process))
reactor.run()
    </pre>

    <p>
        This example shows how you can easily read message forever in a TwOTP
        program, by calling receive indefinitely. This method also supports a
        timeout argument if you don't want to wait forever for a message.

        To send data back to an Erlang node, you can easily use the
        <code>send</code> method which takes a PID attribute. For example,
        emulating the previous RPC:
    </p>

    <pre class="python">
import sys
from twisted.python import log
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName


def receive(process):
    def cb(resp):
        print "Got response", resp
        pid = resp[0]
        process.send(pid, resp[1] * 2)
    def eb(error):
        print "Got error", error
    return process.receive(
        ).addCallback(cb
        ).addErrback(eb
        ).addBoth(lambda x: receive(process))


cookie = readCookie()
nodeName = buildNodeName('nodename')
log.startLogging(sys.stdout)
process = Process(nodeName, cookie)
process.register("main")
process.listen().addCallback(lambda x: receive(process))
reactor.run()
    </pre>

    <p>
        The only difference here is that we're making some assumption on the
        format, namely that the first argument with be a PID, and the second
        something to double.

        And on the Erlang side:
    </p>

<pre>
(erlang@node)1&gt;  {main, nodename@node} ! {self(), 1}.
{&lt;0.37.0&gt;,1}
(erlang@node)2&gt; receive
(erlang@node)2&gt;     X -&gt; io:format("Result: ~p~n", [X])
(erlang@node)2&gt; end.
Result: 2
</pre>

    <p>
        Note that by calling the <code>register</code> method, we've been able
        to use the passed name to make the call from Erlang.
    </p>

    <h2>Using the twotp twisted plugin</h2>

    <p>
        When you install twotp, and type
        <code class="shell">twistd --help</code> in a shell, you should see a
        new <code>twotp</code> command. It has several options:
    </p>

<pre class="shell">
$ twistd twotp --help
Usage: twistd [options] twotp [options]
Options:
  -n, --nodename=  Name of the node [default: twisted]
  -c, --cookie=    Erlang cookie value [default: ]
  -m, --methods=   Methods holder [default: ]
      --version    
      --help       Display this help and exit.
</pre>

    <ul>
        <li>
            <code>-n</code> or <code>--nodename</code> let you specify the name
            of the node. It's the equivalent of the <code>-sname</code> option
            of <code>erl</code>.
        </li>
        <li>
            <code>-c</code> or <code>--cookie</code> let you specify the
            coookie. It's the equivalent of the <code>-setcookie</code> option
            of <code>erl</code>.
        </li>
        <li>
            <code>-m</code> or <code>--methods</code> let you specify the
            methods you want to publish. It takes the fully qualified name of a
            python dictionary.
        </li>
    </ul>

    <p>
        Let's create a module rpc.py and put some objects with methods in them.
    </p>

<pre class="python">
class File(object):

    def remote_get_cwd(self):
        from twisted.python import filepath
        return filepath.FilePath(".").path
        

class Time(object):

    def remote_get_time(self):
        import time
        return time.time()


methods = {"file": File(), "time": Time()}
</pre>

    <p>
        Then launch on the command line twistd, after making sure your module
        is in an importable place.
    </p>

<pre class="shell">
$ twistd -n twotp --methods rpc.methods
2009-05-09 21:24:31+0200 [-] Log opened.
2009-05-09 21:24:31+0200 [-] twistd 8.2.0+r26899 (/usr/bin/python 2.5.2) starting up.
2009-05-09 21:24:31+0200 [-] reactor class: twisted.internet.selectreactor.SelectReactor.
2009-05-09 21:24:31+0200 [-] &lt;class 'twotp.server.NodeServerFactory'&gt; starting on 40962
2009-05-09 21:24:31+0200 [-] Starting factory &lt;twotp.server.NodeServerFactory object at 0x9896b2c&gt;
2009-05-09 21:24:31+0200 [-] Starting factory &lt;twotp.epmd.PersistentPortMapperFactory instance at 0x952978c&gt;
2009-05-09 21:24:31+0200 [Uninitialized] Connection made with EPMD
</pre>

    <p>
        This is it! You've exposed methods callable from Erlang. Let's fire an
        erlang shell to try.
    </p>

<pre class="shell">
(erlang@node)1&gt; rpc:call(twisted@node, file, get_cwd, []).
{ok,"/home/user"}
(erlang@node)2&gt; rpc:call(twisted@node, time, get_time, []).
{ok,1241897194.455415}
</pre>

    <p>
        Now try and define some interesting methods instead.
    </p>

    <h2>Linking and monitoring an Erlang process</h2>

    <p>
        To get exit information with an Erlang process, you can create a link
        or a monitor operation with it. A link is symmetrical, which means that
        the remote process can be notified of the exit status of the Twisted
        process as well. Let's try monitoring first.
    </p>

    <p>
        Open an Erlang shell, create and register a simple process that can
        fail.
    </p>

    <pre>
(erlang@node)1&gt; F = fun() -&gt; receive X -&gt; list_to_atom(X) end end.
#Funi&lt;erl_eval.20.67289768&gt;
(erlang@node)2&gt; Pid1 = spawn(F).
&lt;0.42.0&gt;
(erlang@node)3&gt; register(pid1, Pid1).
true
    </pre>

    <p>
        It registers a process under the name <code>pid1</code>, which can
        easily fails. Now let's start a twisted process monitoring that pid:
    </p>

    <pre class="python">
import sys
from twisted.python import log
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName

def gotWhereis(pid):
    return process.monitor(pid).addCallback(gotRef)

def gotRef(ref):
    process.addMonitorHandler(ref, gotExit)

def gotExit(reason):
    print "Remote process exited with:", reason

cookie = readCookie()
nodeName = buildNodeName('nodename')
log.startLogging(sys.stdout)
process = Process(nodeName, cookie)
process.whereis('erlang', 'pid1').addCallback(gotWhereis)
reactor.run()
    </pre>

    <p>
        There are several interesting methods here. First, <code>whereis</code>
        allows you to get a Pid object from a name, if this name is registered
        on the other node. Then <code>monitor</code> creates a monitor link,
        and returns the monitor reference in its callback. You can then call
        <code>addMonitorHandler</code> as many times you want with this
        reference: the specified callbacks will be called with the exit
        notification happens.
    </p>

    <p>
        Return to the Erlang shell, and create an error on the process:
    </p>

    <pre>
(erlang@node)4&gt; pid1 ! 1.

=ERROR REPORT==== 9-May-2009::19:18:29 ===
Error in process &lt;0.42.0&gt; on node 'erlang@node' with exit value: {badarg,[{erlang,list_to_atom,[1]}]}

1
    </pre>

    <p>
        If everything went fine, you should have the following log in your
        Twisted application:
    </p>

    <pre>
2009-05-09 19:18:29+0200 [NodeClientProtocol,client] Remote process exited with: (&lt;Atom at 0x8b9840c, text 'badarg'&gt;, [(&lt;Atom at 0x8b983cc, text 'erlang'&gt;, &lt;Atom at 0x8b9844c, text 'list_to_atom'&gt;, [1])])
    </pre>

    <h2>An example: using a Mnesia database</h2>

    <p>
        To try the communication as client, we'll set up a mnesia database to
        be used within a Twisted application. Please refer to the mnesia
        documentation for further information. Note that there is nothing new
        here: it's just a concrete example to put the things together.
    </p>

    <p>
        First create a file named twisted.hrl with following record:
    </p>

    <pre class="erlang">
-record(user, {id, name}).
    </pre>

    <p>
        Then create a file named twisted.erl
    </p>

    <pre class="erlang">
-module(twisted).
-export([init/0, insert_user/2, get_user/1]).
-include("twisted.hrl").

init() -&gt;
    mnesia:create_table(user,
                        [{attributes, record_info(fields, user)}]).

insert_user(UserName, UserId) -&gt;
    User = #user{id=UserId, name=UserName},
    Fun = fun() -&gt;
            mnesia:write(User)
        end,
    mnesia:transaction(Fun).

get_user(UserId) -&gt;
    Fun = fun() -&gt;
            [User] = mnesia:read(user, UserId, read),
            User
        end,
    case mnesia:transaction(Fun) of
        {atomic, User} -&gt;
            User
    end.
    </pre>

    <p>
        Now fire an erlang interpreter. We'll compile the twisted file, start
        the mnesia database, create the table, and insert a test user.
    </p>

    <pre class="shell">
$ erl -sname twisted_mnesia

(twisted_mnesia@localhost)1&gt; c(twisted).
{ok,twisted}
(twisted_mnesia@localhost)2&gt; mnesia:start().
ok
(twisted_mnesia@localhost)3&gt; twisted:init().
{atomic,ok}
(twisted_mnesia@localhost)4&gt; twisted:insert_user(test1, 1).
{atomic,ok}
(twisted_mnesia@localhost)5&gt; twisted:get_user(1).
{user,1,test1}
    </pre>

    <p>
        The node is ready to accept our python calls.
    </p>

    <pre class="python">
from twisted.internet import reactor
from twotp import Process, readCookie, buildNodeName

def gotResult(resp):
    print "Got response", resp
    reactor.stop()

def eb(error):
    print "Got error", error
    reactor.stop()

cookie = readCookie()
nodeName = buildNodeName('nodename')
process = Process(nodeName, cookie)
process.callRemote("twisted_mnesia", "twisted", "get_user", 1
    ).addCallback(gotResult).addErrback(eb)

reactor.run()
    </pre>

    <p>
        If everything is fine, you should get the following response:
    </p>

    <pre class="shell">
Got response (&lt;Atom at -0x48806e54, text 'user'&gt;, 1, &lt;Atom at -0x48806e74, text 'test1'&gt;)
    </pre>

  </body>
</html>


{'fraction_non_alphanumeric': 0.11628741655301127, 'fraction_numerical': 0.023257483310602255, 'mean_word_length': 2.865566037735849, 'pattern_counts': {'":': 4, '<': 393, '<?xml version=': 2, '>': 395, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3775916', 'n_tokens_mistral': 9371, 'n_tokens_neox': 8791, 'n_words': 3049}