File: Connector.py

package info (click to toggle)
pybliographer 1.2.12-4squeeze1
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 3,560 kB
  • ctags: 1,413
  • sloc: python: 9,817; xml: 2,560; sh: 813; makefile: 621
file content (172 lines) | stat: -rw-r--r-- 4,991 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# This file has been borrowed from :
#
# Sketch - A Python-based interactive drawing program
# Copyright (C) 1997, 1998 by Bernhard Herzog
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	USA

#
#	The Connector
#

from types import MethodType

class ConnectorError:
    pass

class Connector:

    def __init__(self):
	self.connections = {}

    def Connect(self, object, channel, function, args):
	idx = id(object)
	if self.connections.has_key(idx):
	    channels = self.connections[idx]
	else:
	    channels = self.connections[idx] = {}

	if channels.has_key(channel):
	    receivers = channels[channel]
	else:
	    receivers = channels[channel] = []

	info = (function, args)
	try:
	    receivers.remove(info)
	except ValueError:
	    pass
	receivers.append(info)

    def Disconnect(self, object, channel, function, args):
	try:
	    receivers = self.connections[id(object)][channel]
	except KeyError:
	    raise ConnectorError, \
		  'no receivers for channel %s of %s' % (channel, object)
	try:
	    receivers.remove((function, args))
	except ValueError:
	    raise ConnectorError,\
	    'receiver %s%s is not connected to channel %s of %s' \
	    % (function, args, channel, object)

	if not receivers:
	    # the list of receivers is empty now, remove the channel
	    channels = self.connections[id(object)]
	    del channels[channel]
	    if not channels:
		# the object has no more channels
		del self.connections[id(object)]

    def Issue(self, object, channel, *args):
	#print object, channel, args
	try:
	    receivers = self.connections[id(object)][channel]
	except KeyError:
	    return
	for func, fargs in receivers:
            apply(func, args + fargs)
                
    def RemovePublisher(self, object):
	i = id(object)
	if self.connections.has_key(i):
	    del self.connections[i]
	# don't use try: del ... ; except KeyError here. That would create a
	# new reference of object in a traceback object and this method should
	# be callable from a __del__ method (at least for versions prior
	# Python 1.5)

    def HasSubscribers(self, object):
	return self.connections.has_key(id(object))

    def print_connections(self):
	# for debugging
	for id, channels in self.connections.items():
	    for name, subscribers in channels.items():
		print id, name
		for func, args in subscribers:
		    if type(func) == MethodType:
			print '\tmethod %s of %s' % (func.im_func.func_name,
						     func.im_self)
		    else:
			print '\t', func



_the_connector = Connector()

Connect = _the_connector.Connect
Issue = _the_connector.Issue
RemovePublisher = _the_connector.RemovePublisher
Disconnect = _the_connector.Disconnect
def Subscribe(channel, function, *args):
    return Connect(None, channel, function, args)


class Publisher:

    ignore_issue = 0

    def __del__(self):
	# the new finalization code in 1.5.1 might bind RemovePublisher
	# to None before all objects derived from Publisher are deleted...
	if RemovePublisher is not None:
	    RemovePublisher(self)

    def Subscribe(self, channel, func, *args):
	Connect(self, channel, func, args)

    def Unsubscribe(self, channel, func, *args):
	Disconnect(self, channel, func, args)

    def issue(self, channel, *args):
	if not self.ignore_issue:
	    apply(Issue, (self, channel,) + args)

    def Destroy(self):
	RemovePublisher(self)


class QueueingPublisher(Publisher):

    def __init__(self):
	self.clear_message_queue()

    def queue_message(self, channel, *args):
	# Put message in the queue. If it is already queued put it at
	# the end. This is done to make certain that no channel gets
	# called twice between two calls to flush_message_queue. If the
	# order of channel invocation is important two or more queues
	# should be used.
	message = (channel, args)
	if message not in self.message_queue:
	    self.message_queue.append(message)

    def flush_message_queue(self):
	# Issue all queued messages and make the queue empty
	#
	# Issueing messages might result in new messages being queued.
	# This does not happen in sketch yet (Jul 1997) but let's hope
	# that we don't get infinite loops here...
	while self.message_queue:
	    queue = self.message_queue
	    self.message_queue = []
	    for channel, args in queue:
		apply(Issue, (self, channel) + args)

    def clear_message_queue(self):
	self.message_queue = []