[Zope-CVS] CVS: Packages/Spread - README:1.1 setup.py:1.1 spreadmodule.c:1.1 testspread.py:1.1
Jeremy Hylton
jeremy@zope.com
Tue, 25 Sep 2001 15:20:44 -0400
Update of /cvs-repository/Packages/Spread
In directory cvs.zope.org:/tmp/cvs-serv3529
Added Files:
README setup.py spreadmodule.c testspread.py
Log Message:
Initial checkin of Spread library
=== Added File Packages/Spread/README ===
This package contains a simple wrapper for the Spread toolkit.
Spread (www.spread.org) is a group communications package. You'll
need to download and install it separately. The Python API has been
built and tested against Spread 3.16, including a patch available from
the Web page.
The Spread makefiles do an odd install, putting things in
/var/tmp/testinstall by default. It does *not* install header files,
so you'll need to copy those into place by hand. I copied sp.h,
sp_events.h, and sp_funcs.h into /var/tmp/testinstall/include. I'm
told the next release will install headers.
The distutils setup.py script needs to be told where the spread code
is installed. I still don't know the preferred way to do that, so
you'll need to edit the SPREAD_DIR constant. The setup script assumes
you've installed the header files manually.
The man pages installed by Spread (and available at www.spread.org)
are fairly helpful, although obscure at times. There is no
documentation for the Python wrapper yet.
To run the testspread.py unit tests, you'll need to manually set your
Python path to the build directory. Again, I don't know if distutils
provides a good way to do this. (I wish it did.) The following works
for me:
PYTHONPATH=build/lib.linux-i686-2.1 python testspread.py
=== Added File Packages/Spread/setup.py ===
#! /usr/bin/env python
from distutils.core import setup, Extension
SPREAD_DIR = "/usr/local/spread"
ext = Extension('spread', ['spreadmodule.c'],
include_dirs = [SPREAD_DIR + "/include"],
library_dirs = [SPREAD_DIR + "/lib"],
libraries = ['tsp'],
)
setup(name = "Spread API for Python",
ext_modules = [ext],
)
=== Added File Packages/Spread/spreadmodule.c === (748/848 lines abridged)
/* Wrapper for the Spread toolkit: http://www.spread.org/
*/
#include "Python.h"
#include "structmember.h"
#include "sp.h"
/* XXX This ought to be in sp.h */
static sp_time Zero_timeout = { 0, 0 };
static PyObject *SpreadError;
static PyObject *spread_error(int);
static void spread_too_short_error(int, int, short, int);
#define MAX_GROUPS 10
#define MSG_SIZE 8 * 1024
typedef struct {
PyObject_HEAD
mailbox mbox;
PyObject *private_group;
int max_groups;
int msg_size;
} MailboxObject;
typedef struct {
PyObject_HEAD
PyObject *sender;
PyObject *groups;
short msg_type; /* sp.h: #define int16 short*/
int endian;
PyObject *message;
int truncated;
} RegularMsg;
typedef struct {
group_id gid;
int num_members;
char members[1][MAX_GROUP_NAME];
} extra_member_info;
typedef struct {
PyObject_HEAD
int reason;
PyObject *group;
PyObject *group_id;
PyObject *members;
PyObject *extra; /* that are still members */
} MembershipMsg;
[-=- -=- -=- 748 lines omitted -=- -=- -=-]
PyDict_SetItemString(d, "error", SpreadError);
/* programmatically generated from sp.h */
PyModule_AddIntConstant(m, "LOW_PRIORITY", LOW_PRIORITY);
PyModule_AddIntConstant(m, "MEDIUM_PRIORITY", MEDIUM_PRIORITY);
PyModule_AddIntConstant(m, "HIGH_PRIORITY", HIGH_PRIORITY);
PyModule_AddIntConstant(m, "DEFAULT_SPREAD_PORT", DEFAULT_SPREAD_PORT);
PyModule_AddIntConstant(m, "SPREAD_VERSION", SPREAD_VERSION);
PyModule_AddIntConstant(m, "MAX_GROUP_NAME", MAX_GROUP_NAME);
PyModule_AddIntConstant(m, "MAX_PRIVATE_NAME", MAX_PRIVATE_NAME);
PyModule_AddIntConstant(m, "MAX_PROC_NAME", MAX_PROC_NAME);
PyModule_AddIntConstant(m, "UNRELIABLE_MESS", UNRELIABLE_MESS);
PyModule_AddIntConstant(m, "RELIABLE_MESS", RELIABLE_MESS);
PyModule_AddIntConstant(m, "FIFO_MESS", FIFO_MESS);
PyModule_AddIntConstant(m, "CAUSAL_MESS", CAUSAL_MESS);
PyModule_AddIntConstant(m, "AGREED_MESS", AGREED_MESS);
PyModule_AddIntConstant(m, "SAFE_MESS", SAFE_MESS);
PyModule_AddIntConstant(m, "REGULAR_MESS", REGULAR_MESS);
PyModule_AddIntConstant(m, "SELF_DISCARD", SELF_DISCARD);
PyModule_AddIntConstant(m, "DROP_RECV", DROP_RECV);
PyModule_AddIntConstant(m, "REG_MEMB_MESS", REG_MEMB_MESS);
PyModule_AddIntConstant(m, "TRANSITION_MESS", TRANSITION_MESS);
PyModule_AddIntConstant(m, "CAUSED_BY_JOIN", CAUSED_BY_JOIN);
PyModule_AddIntConstant(m, "CAUSED_BY_LEAVE", CAUSED_BY_LEAVE);
PyModule_AddIntConstant(m, "CAUSED_BY_DISCONNECT",
CAUSED_BY_DISCONNECT);
PyModule_AddIntConstant(m, "CAUSED_BY_NETWORK", CAUSED_BY_NETWORK);
PyModule_AddIntConstant(m, "MEMBERSHIP_MESS", MEMBERSHIP_MESS);
PyModule_AddIntConstant(m, "ENDIAN_RESERVED", ENDIAN_RESERVED);
PyModule_AddIntConstant(m, "RESERVED", RESERVED);
PyModule_AddIntConstant(m, "REJECT_MESS", REJECT_MESS);
PyModule_AddIntConstant(m, "ACCEPT_SESSION", ACCEPT_SESSION);
PyModule_AddIntConstant(m, "ILLEGAL_SPREAD", ILLEGAL_SPREAD);
PyModule_AddIntConstant(m, "COULD_NOT_CONNECT", COULD_NOT_CONNECT);
PyModule_AddIntConstant(m, "REJECT_QUOTA", REJECT_QUOTA);
PyModule_AddIntConstant(m, "REJECT_NO_NAME", REJECT_NO_NAME);
PyModule_AddIntConstant(m, "REJECT_ILLEGAL_NAME", REJECT_ILLEGAL_NAME);
PyModule_AddIntConstant(m, "REJECT_NOT_UNIQUE", REJECT_NOT_UNIQUE);
PyModule_AddIntConstant(m, "REJECT_VERSION", REJECT_VERSION);
PyModule_AddIntConstant(m, "CONNECTION_CLOSED", CONNECTION_CLOSED);
PyModule_AddIntConstant(m, "REJECT_AUTH", REJECT_AUTH);
PyModule_AddIntConstant(m, "ILLEGAL_SESSION", ILLEGAL_SESSION);
PyModule_AddIntConstant(m, "ILLEGAL_SERVICE", ILLEGAL_SERVICE);
PyModule_AddIntConstant(m, "ILLEGAL_MESSAGE", ILLEGAL_MESSAGE);
PyModule_AddIntConstant(m, "ILLEGAL_GROUP", ILLEGAL_GROUP);
PyModule_AddIntConstant(m, "BUFFER_TOO_SHORT", BUFFER_TOO_SHORT);
PyModule_AddIntConstant(m, "GROUPS_TOO_SHORT", GROUPS_TOO_SHORT);
PyModule_AddIntConstant(m, "MESSAGE_TOO_LONG", MESSAGE_TOO_LONG);
}
=== Added File Packages/Spread/testspread.py ===
import spread
import time
import unittest
class SpreadTest(unittest.TestCase):
spread_name = "4803@localhost"
conn_counter = 0
def _connect(self, membership=1):
"""Return a new connection"""
c = SpreadTest.conn_counter
SpreadTest.conn_counter += 1
mb = spread.connect(self.spread_name, "conn%d" % c, 0, membership)
return mb
group_counter = 0
def _group(self):
"""Return a new group name"""
c = SpreadTest.group_counter
SpreadTest.group_counter += 1
return "group%d" % c
def _connect_group(self, size):
"""Return a group and a list of size connections in that group.
All the connections should have consumed all their membership
messages.
"""
group = self._group()
l = [self._connect() for i in range(size)]
for i in range(len(l)):
c = l[i]
if c.max_groups < size:
c.max_groups = size
c.join(group)
# read membership messages until all receive notification of
# the appropriate group size
ready = []
while l:
for c in l[:]:
if c.poll():
msg = c.receive()
self.assertEqual(type(msg), spread.MembershipMsgType)
if len(msg.members) == size:
ready.append(c)
l.remove(c)
return group, ready
def testSingleConnect(self):
mbox = self._connect()
group = self._group()
mbox.join(group)
while mbox.poll() == 0:
time.sleep(0.1)
msg = mbox.receive()
# Should get a message indicating that we've joined the group
self.assertEqual(msg.group, group,
"expected message group to match joined group")
self.assertEqual(msg.reason, spread.CAUSED_BY_JOIN,
"expected message to be caused by join")
self.assertEqual(len(msg.members), 1,
"expected group to have one member")
self.assertEqual(msg.members[0], mbox.private_group,
"expected this mbox to be in group")
self.assertEqual(len(msg.extra), 1,
"expected one mbox to cause the join")
self.assertEqual(msg.extra[0], mbox.private_group,
"expected this mbox to cause the join")
mbox.leave(group)
# should get a self-leave message
msg = mbox.receive()
self.assertEqual(msg.group, group)
self.assertEqual(msg.reason, spread.CAUSED_BY_LEAVE)
self.assertEqual(len(msg.members), 0)
self.assertEqual(len(msg.extra), 1)
self.assertEqual(msg.extra[0], mbox.private_group)
mbox.disconnect()
def testTwoConnect(self):
group = self._group()
mbox1 = self._connect()
mbox1.join(group)
mbox2 = self._connect()
mbox2.join(group)
msg1_1 = mbox1.receive()
msg1_2 = mbox1.receive()
msg2_1 = mbox2.receive()
self.assertEqual(msg1_1.reason, spread.CAUSED_BY_JOIN)
self.assertEqual(msg1_2.reason, spread.CAUSED_BY_JOIN)
self.assertEqual(msg2_1.reason, spread.CAUSED_BY_JOIN)
self.assertEqual(len(msg1_2.members), 2)
self.assertEqual(msg1_2.members, msg2_1.members)
mbox1.leave(group)
mbox2.leave(group)
mbox1.disconnect()
mbox2.disconnect()
def testTwoGroups(self):
mbox = self._connect()
group1 = self._group()
group2 = self._group()
mbox.join(group1)
mbox.join(group2)
mbox.leave(group1)
mbox.leave(group2)
msg1 = mbox.receive()
msg2 = mbox.receive()
msg3 = mbox.receive()
msg4 = mbox.receive()
self.assertEqual(msg1.reason, spread.CAUSED_BY_JOIN)
self.assertEqual(msg2.reason, spread.CAUSED_BY_JOIN)
self.assertEqual(len(msg1.members), 1)
self.assertEqual(len(msg2.members), 1)
self.assertEqual(msg1.members[0], mbox.private_group)
self.assertEqual(msg3.reason, spread.CAUSED_BY_LEAVE)
self.assertEqual(msg4.reason, spread.CAUSED_BY_LEAVE)
self.assertEqual(len(msg3.members), 0)
self.assertEqual(len(msg4.members), 0)
mbox.disconnect()
def testMultipleRecipients(self):
group, members = self._connect_group(12)
wr = members[0]
wr.multicast(spread.FIFO_MESS, group, "1")
for rd in members:
msg = rd.receive()
if not hasattr(msg, 'message'):
print msg
print msg.reason
print msg.svc_type
self.assertEqual(msg.message, "1")
self.assertEqual(msg.sender, wr.private_group)
self.assertEqual(len(msg.groups), 1)
self.assertEqual(msg.groups[0], group)
wr = members[0]
wr.multicast(spread.FIFO_MESS, group, "2")
for rd in members:
msg = rd.receive()
self.assertEqual(msg.message, "2")
self.assertEqual(msg.sender, wr.private_group)
self.assertEqual(len(msg.groups), 1)
self.assertEqual(msg.groups[0], group)
def testBigMessage(self):
group = self._group()
mbox = self._connect()
mbox.join(group)
self.assertEqual(type(mbox.receive()), spread.MembershipMsgType)
size = mbox.msg_size * 2
try:
mbox.multicast(spread.SAFE_MESS, group, "X" * size)
except spread.error, err:
print err
print size
raise
try:
mbox.receive()
except spread.error, err:
self.assertEqual(err[0], spread.BUFFER_TOO_SHORT)
self.assertEqual(err[4], size)
msg = mbox.receive(size)
self.assertEqual(len(msg.message), size)
def testBigGroup(self):
group, members = self._connect_group(12)
m1 = members[1]
m2 = members[2]
m1.max_groups = 6
m2.leave(group)
try:
m1.receive()
except spread.error, err:
self.assertEqual(err[0], spread.GROUPS_TOO_SHORT)
else:
self.fail()
m1.max_groups = err[4]
m1.receive()
if __name__ == "__main__":
unittest.main()