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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
|
# api.py - webvi API
#
# Copyright (c) 2009, 2010 Antti Ajanki <antti.ajanki@iki.fi>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""webvi API
Example workflow:
1) Create a new request. ref is a wvt:// URI.
handle = new_request(ref, WebviRequestType.MENU)
2) Setup a callback function:
setopt(handle, WebviOpt.WRITEFUNC, my_callback)
3) Start the network transfer:
start_handle(handle)
4) Get active file descriptors, wait for activity on them, and let
webvi process the file descriptor.
import select
...
readfd, writefd, excfd = fdset()[1:4]
readfd, writefd, excfd = select.select(readfd, writefd, excfd, 5.0)
for fd in readfd:
perform(fd, WebviSelectBitmask.READ)
for fd in writefd:
perform(fd, WebviSelectBitmask.WRITE)
5) Iterate 4) until pop_message returns handle, which indicates that
the request has been completed.
finished, status, errmsg, remaining = pop_message()
if finished == handle:
print 'done'
"""
import request
import asyncore
import asyncurl
from constants import WebviErr, WebviOpt, WebviInfo, WebviSelectBitmask, WebviConfig
# Human readable messages for WebviErr items
error_messages = {
WebviErr.OK: 'Succeeded',
WebviErr.INVALID_HANDLE: 'Invalid handle',
WebviErr.INVALID_PARAMETER: "Invalid parameter",
WebviErr.INTERNAL_ERROR: "Internal error"
}
# Module-level variables
finished_queue = []
request_list = request.RequestList()
socket_map = asyncore.socket_map
# Internal functions
class MyRequest(request.Request):
def request_done(self, err, errmsg):
"""Calls the inherited function and puts the handle of the
finished request to the finished_queue."""
finished_queue.append(self)
request.Request.request_done(self, err, errmsg)
# Public functions
def strerror(err):
"""Return human readable error message for conststants.WebviErr"""
try:
return error_messages[err]
except KeyError:
return error_messages[WebviErr.INTERNAL_ERROR]
def set_config(conf, value):
"""Set a new value for a global configuration option conf.
Currently the only legal value for conf is
constants.WebviConfig.TEMPLATE_PATH, which sets the base directory
for the XSLT templates.
"""
if conf == WebviConfig.TEMPLATE_PATH:
request.set_template_path(value)
return WebviErr.OK
else:
return WebviErr.INVALID_PARAMETER
def new_request(reference, reqtype):
"""Create a new request.
reference is a wvt:// URI which typically comes from previously
opened menu. reqtype is one of conststants.WebviRequestType and
indicates wheter the reference is a navigation menu, stream that
should be downloaded, or a stream whose URL should be returned.
Returns a handle (an integer) will be given to following
functions. Return value -1 indicates an error.
"""
req = MyRequest(reference, reqtype)
if req.srcurl is None:
return -1
return request_list.put(req)
def set_opt(handle, option, value):
"""Set configuration options on a handle.
option specifies option's name (one of constants.WebviOpt values)
and value is the new value for the option.
"""
try:
req = request_list[handle]
except KeyError:
return WebviErr.INVALID_HANDLE
if option == WebviOpt.WRITEFUNC:
req.writefunc = value
elif option == WebviOpt.WRITEDATA:
req.writedata = value
elif option == WebviOpt.READFUNC:
req.readfunc = value
elif option == WebviOpt.READDATA:
req.readdata = value
else:
return WebviErr.INVALID_PARAMETER
return WebviErr.OK
def get_info(handle, info):
"""Get information about a handle.
info is the type of data that is to be returned (one of
constants.WebviInfo values).
"""
try:
req = request_list[handle]
except KeyError:
return (WebviErr.INVALID_HANDLE, None)
val = None
if info == WebviInfo.URL:
if req.dl is not None:
val = req.dl.get_url()
else:
val = req.srcurl
elif info == WebviInfo.CONTENT_LENGTH:
val = req.contentlength
elif info == WebviInfo.CONTENT_TYPE:
val = req.contenttype
elif info == WebviInfo.STREAM_TITLE:
val = req.streamtitle
else:
return (WebviErr.INVALID_PARAMETER, None)
return (WebviErr.OK, val)
def start_handle(handle):
"""Start the network transfer on a handle."""
try:
req = request_list[handle]
except KeyError:
return WebviErr.INVALID_HANDLE
req.start()
return WebviErr.OK
def stop_handle(handle):
"""Aborts network transfer on a handle.
The abort is confirmed by pop_message() returning the handle with
an non-zero error code.
"""
try:
req = request_list[handle]
except KeyError:
return WebviErr.INVALID_HANDLE
if not req.is_finished():
req.stop()
return WebviErr.OK
def delete_handle(handle):
"""Frees resources related to handle.
This should be called when the transfer has been completed and the
user is done with the handle. If the transfer is still in progress
when delete_handle() is called, the transfer is aborted. After
calling delete_handle() the handle value will be invalid, and
should not be feed to other functions anymore.
"""
try:
del request_list[handle]
except KeyError:
return WebviErr.INVALID_HANDLE
return WebviErr.OK
def pop_message():
"""Retrieve messages about finished requests.
If a request has been finished since the last call to this
function, returns a tuple (handle, status, msg, num_messages),
where handle identifies the finished request, status is a numeric
status code (non-zero for an error), msg is a description of an
error as string, and num_messages is the number of messages that
can be retrieved by calling pop_messages() again immediately. If
the finished requests queue is empty, returns (-1, -1, "", 0).
"""
if finished_queue:
req = finished_queue.pop()
return (req.handle, req.status, req.errmsg, len(finished_queue))
else:
return (-1, -1, "", 0)
def fdset():
"""Get the list of file descriptors that are currently in use by
the library.
Returrns a tuple, where the first item is a constants.WebviErr
value indicating the success of the call, the next three values
are lists of descriptors that should be monitored for reading,
writing, and exceptional conditions, respectively. The last item
is the maximum of the file descriptors in the three lists.
"""
readfd = []
writefd = []
excfd = []
maxfd = -1
for fd, disp in socket_map.iteritems():
if disp.readable():
readfd.append(fd)
if fd > maxfd:
maxfd = fd
if disp.writable():
writefd.append(fd)
if fd > maxfd:
maxfd = fd
return (WebviErr.OK, readfd, writefd, excfd, maxfd)
def perform(fd, ev_bitmask):
"""Perform transfer on file descriptor fd.
fd is a file descriptor that has been signalled to be ready by
select() or similar system call. ev_bitmask specifies what kind of
activity has been detected using values of
constants.WebviSelectBitmask. If ev_bitmask is
constants.WebviSelectBitmask.TIMEOUT the type of activity is check
by the function.
This function should be called every few seconds with fd=-1,
ev_bitmask=constants.WebviSelectBitmask.TIMEOUT even if no
activity has been signalled on the file descriptors to ensure
correct handling of timeouts and other internal processing.
"""
if fd < 0:
asyncurl.poll()
else:
disp = socket_map.get(fd)
if disp is not None:
if ev_bitmask & WebviSelectBitmask.READ != 0 or \
(ev_bitmask == 0 and disp.readable()):
disp.handle_read_event()
if ev_bitmask & WebviSelectBitmask.WRITE != 0 or \
(ev_bitmask == 0 and disp.writable()):
disp.handle_write_event()
return (WebviErr.OK, len(socket_map))
|