~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/protocols/dict.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
 
 
5
"""
 
6
Dict client protocol implementation.
 
7
 
 
8
@author: Pavel Pergamenshchik
 
9
"""
 
10
 
 
11
from twisted.protocols import basic
 
12
from twisted.internet import defer, protocol
 
13
from twisted.python import log
 
14
from StringIO import StringIO
 
15
 
 
16
def parseParam(line):
 
17
    """Chew one dqstring or atom from beginning of line and return (param, remaningline)"""
 
18
    if line == '':
 
19
        return (None, '')
 
20
    elif line[0] != '"': # atom
 
21
        mode = 1
 
22
    else: # dqstring
 
23
        mode = 2
 
24
    res = ""
 
25
    io = StringIO(line)
 
26
    if mode == 2: # skip the opening quote
 
27
        io.read(1)
 
28
    while 1:
 
29
        a = io.read(1)
 
30
        if a == '"':
 
31
            if mode == 2:
 
32
                io.read(1) # skip the separating space
 
33
                return (res, io.read())
 
34
        elif a == '\\':
 
35
            a = io.read(1)
 
36
            if a == '':
 
37
                return (None, line) # unexpected end of string
 
38
        elif a == '':
 
39
            if mode == 1:
 
40
                return (res, io.read())
 
41
            else:
 
42
                return (None, line) # unexpected end of string
 
43
        elif a == ' ':
 
44
            if mode == 1:
 
45
                return (res, io.read())
 
46
        res += a
 
47
 
 
48
def makeAtom(line):
 
49
    """Munch a string into an 'atom'"""
 
50
    # FIXME: proper quoting
 
51
    return filter(lambda x: not (x in map(chr, range(33)+[34, 39, 92])), line)
 
52
 
 
53
def makeWord(s):
 
54
    mustquote = range(33)+[34, 39, 92]
 
55
    result = []
 
56
    for c in s:
 
57
        if ord(c) in mustquote:
 
58
            result.append("\\")
 
59
        result.append(c)
 
60
    s = "".join(result)
 
61
    return s
 
62
 
 
63
def parseText(line):
 
64
    if len(line) == 1 and line == '.':
 
65
        return None
 
66
    else:
 
67
        if len(line) > 1 and line[0:2] == '..':
 
68
            line = line[1:]
 
69
        return line
 
70
 
 
71
class Definition:
 
72
    """A word definition"""
 
73
    def __init__(self, name, db, dbdesc, text):
 
74
        self.name = name
 
75
        self.db = db
 
76
        self.dbdesc = dbdesc
 
77
        self.text = text # list of strings not terminated by newline
 
78
 
 
79
class DictClient(basic.LineReceiver):
 
80
    """dict (RFC2229) client"""
 
81
 
 
82
    data = None # multiline data
 
83
    MAX_LENGTH = 1024
 
84
    state = None
 
85
    mode = None
 
86
    result = None
 
87
    factory = None
 
88
 
 
89
    def __init__(self):
 
90
        self.data = None
 
91
        self.result = None
 
92
 
 
93
    def connectionMade(self):
 
94
        self.state = "conn"
 
95
        self.mode = "command"
 
96
 
 
97
    def sendLine(self, line):
 
98
        """Throw up if the line is longer than 1022 characters"""
 
99
        if len(line) > self.MAX_LENGTH - 2:
 
100
            raise ValueError("DictClient tried to send a too long line")
 
101
        basic.LineReceiver.sendLine(self, line)
 
102
 
 
103
    def lineReceived(self, line):
 
104
        try:
 
105
            line = line.decode("UTF-8")
 
106
        except UnicodeError: # garbage received, skip
 
107
            return
 
108
        if self.mode == "text": # we are receiving textual data
 
109
            code = "text"
 
110
        else:
 
111
            if len(line) < 4:
 
112
                log.msg("DictClient got invalid line from server -- %s" % line)
 
113
                self.protocolError("Invalid line from server")
 
114
                self.transport.LoseConnection()
 
115
                return
 
116
            code = int(line[:3])
 
117
            line = line[4:]
 
118
        method = getattr(self, 'dictCode_%s_%s' % (code, self.state), self.dictCode_default)
 
119
        method(line)
 
120
 
 
121
    def dictCode_default(self, line):
 
122
        """Unkown message"""
 
123
        log.msg("DictClient got unexpected message from server -- %s" % line)
 
124
        self.protocolError("Unexpected server message")
 
125
        self.transport.loseConnection()
 
126
 
 
127
    def dictCode_221_ready(self, line):
 
128
        """We are about to get kicked off, do nothing"""
 
129
        pass
 
130
 
 
131
    def dictCode_220_conn(self, line):
 
132
        """Greeting message"""
 
133
        self.state = "ready"
 
134
        self.dictConnected()
 
135
 
 
136
    def dictCode_530_conn(self):
 
137
        self.protocolError("Access denied")
 
138
        self.transport.loseConnection()
 
139
 
 
140
    def dictCode_420_conn(self):
 
141
        self.protocolError("Server temporarily unavailable")
 
142
        self.transport.loseConnection()
 
143
 
 
144
    def dictCode_421_conn(self):
 
145
        self.protocolError("Server shutting down at operator request")
 
146
        self.transport.loseConnection()
 
147
 
 
148
    def sendDefine(self, database, word):
 
149
        """Send a dict DEFINE command"""
 
150
        assert self.state == "ready", "DictClient.sendDefine called when not in ready state"
 
151
        self.result = None  # these two are just in case. In "ready" state, result and data
 
152
        self.data = None    # should be None
 
153
        self.state = "define"
 
154
        command = "DEFINE %s %s" % (makeAtom(database.encode("UTF-8")), makeWord(word.encode("UTF-8")))
 
155
        self.sendLine(command)
 
156
 
 
157
    def sendMatch(self, database, strategy, word):
 
158
        """Send a dict MATCH command"""
 
159
        assert self.state == "ready", "DictClient.sendMatch called when not in ready state"
 
160
        self.result = None
 
161
        self.data = None
 
162
        self.state = "match"
 
163
        command = "MATCH %s %s %s" % (makeAtom(database), makeAtom(strategy), makeAtom(word))
 
164
        self.sendLine(command.encode("UTF-8"))
 
165
 
 
166
    def dictCode_550_define(self, line):
 
167
        """Invalid database"""
 
168
        self.mode = "ready"
 
169
        self.defineFailed("Invalid database")
 
170
 
 
171
    def dictCode_550_match(self, line):
 
172
        """Invalid database"""
 
173
        self.mode = "ready"
 
174
        self.matchFailed("Invalid database")
 
175
 
 
176
    def dictCode_551_match(self, line):
 
177
        """Invalid strategy"""
 
178
        self.mode = "ready"
 
179
        self.matchFailed("Invalid strategy")
 
180
 
 
181
    def dictCode_552_define(self, line):
 
182
        """No match"""
 
183
        self.mode = "ready"
 
184
        self.defineFailed("No match")
 
185
 
 
186
    def dictCode_552_match(self, line):
 
187
        """No match"""
 
188
        self.mode = "ready"
 
189
        self.matchFailed("No match")
 
190
 
 
191
    def dictCode_150_define(self, line):
 
192
        """n definitions retrieved"""
 
193
        self.result = []
 
194
 
 
195
    def dictCode_151_define(self, line):
 
196
        """Definition text follows"""
 
197
        self.mode = "text"
 
198
        (word, line) = parseParam(line)
 
199
        (db, line) = parseParam(line)
 
200
        (dbdesc, line) = parseParam(line)
 
201
        if not (word and db and dbdesc):
 
202
            self.protocolError("Invalid server response")
 
203
            self.transport.loseConnection()
 
204
        else:
 
205
            self.result.append(Definition(word, db, dbdesc, []))
 
206
            self.data = []
 
207
 
 
208
    def dictCode_152_match(self, line):
 
209
        """n matches found, text follows"""
 
210
        self.mode = "text"
 
211
        self.result = []
 
212
        self.data = []
 
213
 
 
214
    def dictCode_text_define(self, line):
 
215
        """A line of definition text received"""
 
216
        res = parseText(line)
 
217
        if res == None:
 
218
            self.mode = "command"
 
219
            self.result[-1].text = self.data
 
220
            self.data = None
 
221
        else:
 
222
            self.data.append(line)
 
223
 
 
224
    def dictCode_text_match(self, line):
 
225
        """One line of match text received"""
 
226
        def l(s):
 
227
            p1, t = parseParam(s)
 
228
            p2, t = parseParam(t)
 
229
            return (p1, p2)
 
230
        res = parseText(line)
 
231
        if res == None:
 
232
            self.mode = "command"
 
233
            self.result = map(l, self.data)
 
234
            self.data = None
 
235
        else:
 
236
            self.data.append(line)
 
237
 
 
238
    def dictCode_250_define(self, line):
 
239
        """ok"""
 
240
        t = self.result
 
241
        self.result = None
 
242
        self.state = "ready"
 
243
        self.defineDone(t)
 
244
 
 
245
    def dictCode_250_match(self, line):
 
246
        """ok"""
 
247
        t = self.result
 
248
        self.result = None
 
249
        self.state = "ready"
 
250
        self.matchDone(t)
 
251
    
 
252
    def protocolError(self, reason):
 
253
        """override to catch unexpected dict protocol conditions"""
 
254
        pass
 
255
 
 
256
    def dictConnected(self):
 
257
        """override to be notified when the server is ready to accept commands"""
 
258
        pass
 
259
 
 
260
    def defineFailed(self, reason):
 
261
        """override to catch reasonable failure responses to DEFINE"""
 
262
        pass
 
263
 
 
264
    def defineDone(self, result):
 
265
        """override to catch succesful DEFINE"""
 
266
        pass
 
267
    
 
268
    def matchFailed(self, reason):
 
269
        """override to catch resonable failure responses to MATCH"""
 
270
        pass
 
271
 
 
272
    def matchDone(self, result):
 
273
        """override to catch succesful MATCH"""
 
274
        pass
 
275
 
 
276
 
 
277
class InvalidResponse(Exception):
 
278
    pass
 
279
 
 
280
 
 
281
class DictLookup(DictClient):
 
282
    """Utility class for a single dict transaction. To be used with DictLookupFactory"""
 
283
 
 
284
    def protocolError(self, reason):
 
285
        if not self.factory.done:
 
286
            self.factory.d.errback(InvalidResponse(reason))
 
287
            self.factory.clientDone()
 
288
 
 
289
    def dictConnected(self):
 
290
        if self.factory.queryType == "define":
 
291
            apply(self.sendDefine, self.factory.param)
 
292
        elif self.factory.queryType == "match":
 
293
            apply(self.sendMatch, self.factory.param)
 
294
 
 
295
    def defineFailed(self, reason):
 
296
        self.factory.d.callback([])
 
297
        self.factory.clientDone()
 
298
        self.transport.loseConnection()
 
299
 
 
300
    def defineDone(self, result):
 
301
        self.factory.d.callback(result)
 
302
        self.factory.clientDone()
 
303
        self.transport.loseConnection()
 
304
 
 
305
    def matchFailed(self, reason):
 
306
        self.factory.d.callback([])
 
307
        self.factory.clientDone()
 
308
        self.transport.loseConnection()
 
309
 
 
310
    def matchDone(self, result):
 
311
        self.factory.d.callback(result)
 
312
        self.factory.clientDone()
 
313
        self.transport.loseConnection()
 
314
 
 
315
 
 
316
class DictLookupFactory(protocol.ClientFactory):
 
317
    """Utility factory for a single dict transaction"""
 
318
    protocol = DictLookup
 
319
    done = None
 
320
 
 
321
    def __init__(self, queryType, param, d):
 
322
        self.queryType = queryType
 
323
        self.param = param
 
324
        self.d = d
 
325
        self.done = 0
 
326
 
 
327
    def clientDone(self):
 
328
        """Called by client when done."""
 
329
        self.done = 1
 
330
        del self.d
 
331
    
 
332
    def clientConnectionFailed(self, connector, error):
 
333
        self.d.errback(error)
 
334
 
 
335
    def clientConnectionLost(self, connector, error):
 
336
        if not self.done:
 
337
            self.d.errback(error)
 
338
 
 
339
    def buildProtocol(self, addr):
 
340
        p = self.protocol()
 
341
        p.factory = self
 
342
        return p
 
343
 
 
344
 
 
345
def define(host, port, database, word):
 
346
    """Look up a word using a dict server"""
 
347
    d = defer.Deferred()
 
348
    factory = DictLookupFactory("define", (database, word), d)
 
349
    
 
350
    from twisted.internet import reactor
 
351
    reactor.connectTCP(host, port, factory)
 
352
    return d
 
353
 
 
354
def match(host, port, database, strategy, word):
 
355
    """Match a word using a dict server"""
 
356
    d = defer.Deferred()
 
357
    factory = DictLookupFactory("match", (database, strategy, word), d)
 
358
 
 
359
    from twisted.internet import reactor
 
360
    reactor.connectTCP(host, port, factory)
 
361
    return d
 
362