1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 __doc__ = \
21 """
22 pywurfl - Python tools for processing and querying the Wireless Universal Resource File (WURFL)
23 """
24
25 import re
26 from copy import copy
27
28 from pywurfl.exceptions import (BaseException, ActualDeviceRootNotFound,
29 DeviceNotFound, ExistsException)
30
31
32 __author__ = "Armand Lynch <lyncha@users.sourceforge.net>"
33 __contributors__ = "Pau Aliagas <pau@newtral.org>"
34 __copyright__ = "Copyright 2006, Armand Lynch"
35 __license__ = "LGPL"
36 __url__ = "http://celljam.net/"
37 __version__ = "6.3.1b"
38 __all__ = ['devclass', 'Devices']
39
40
42 """
43 pywurfl Root Device base class.
44
45 All classes created by pywurfl are at root a subclass of this class.
46 """
47 pass
48
49
50 -def devclass(parent, devid, devua, actual_device_root, new_caps=None):
51 """
52 Return a pywurfl.Device class.
53
54 @param parent: A Device class or None.
55 @type parent: Device
56 @param devid: The device id for the returned class.
57 @type devid: string
58 @param devua: The user agent for the returned class.
59 @type devua: string
60 @param actual_device_root: Whether or not the returned class is an actual
61 device.
62 @type actual_device_root: boolean
63 @param new_caps: The new capabilities for the returned class.
64 @type new_caps: dict
65 """
66 if parent is None:
67 class Device(RootDevice):
68 """pywurfl Generic Device"""
69 def __iter__(self):
70 for group in sorted(self.groups.keys()):
71 for capability in sorted(self.groups[group]):
72 yield (group, capability, getattr(self, capability))
73
74 def __str__(self):
75 s = []
76 s.append("User Agent: %s\n" % self.devua)
77 s.append("WURFL ID: %s\n" % self.devid)
78 s.append("Fallbacks: ")
79 fbs = []
80 base_class = self.__class__.__bases__[0]
81 while base_class is not RootDevice:
82 fbs.append(base_class.devid)
83 base_class = base_class.__bases__[0]
84 s.append("%s\n" % fbs)
85 s.append("Actual Device Root: %s\n\n" % self.actual_device_root)
86 for group in sorted(self.groups.keys()):
87 s.append("%s\n" % group.upper())
88 for cap in sorted(self.groups[group]):
89 s.append("%s: %s\n" % (cap, getattr(self, cap)))
90 s.append("\n")
91 return ''.join(s)
92
93 Device.fall_back = 'root'
94 Device.groups = {}
95 else:
96 class Device(parent):
97 """pywurfl Device"""
98 pass
99 parent.children.add(Device)
100 Device.fall_back = parent.devid
101
102 if new_caps is not None:
103 for name, value in new_caps.iteritems():
104 setattr(Device, name, value)
105
106 Device.devid = devid
107 Device.devua = devua
108 Device.children = set()
109 Device.actual_device_root = actual_device_root
110
111 return Device
112
113
115 """
116 Main pywurfl API class.
117 """
118
120 self.devids = {}
121 self.devuas = {}
122
123
124 self._noise = (("/SN\d{15}", "/SNXXXXXXXXXXXXXXX"),
125 ("UP.Link\/.*?(\s|$)", ""),
126 ("\(via IBM WBI \d+\.\d+\)", ""),
127 ("GMCC\/\d\.\d", ""))
128 self._noise_re = [(re.compile(exp), repl) for exp, repl in self._noise]
129 self._name_test_re = re.compile(r'^(_|[a-z])(_|[a-z]|[0-9])+$')
130
132 """
133 Find an actual device root.
134
135 @param device: A Device class.
136 @type device: Device class
137 @raise ActualDeviceNotFound:
138 """
139 while device is not RootDevice:
140 if device.actual_device_root:
141 return device
142 device = device.__bases__[0]
143 raise ActualDeviceRootNotFound
144
145 - def select_ua(self, devua, actual_device_root=False, filter_noise=True,
146 search=None, instance=True):
147 """
148 Return a Device object based on the user agent.
149
150 @param devua: The device user agent to search for.
151 @type devua: string
152 @param actual_device_root: Return a device that is an actual device
153 root
154 @type actual_device_root: boolean
155 @param filter_noise: Remove noise words from devua.
156 @type filter_noise: boolean
157 @param search: The algorithm to use for searching. If 'search' is None,
158 a search will not be performed.
159 @type search: pywurfl.Algorithm
160 @param instance: Used to select that you want an instance instead of a
161 class object.
162 @type instance: boolean
163 @raise DeviceNotFound:
164 """
165 devua = devua.strip()
166 device = None
167 if devua in self.devuas:
168 device = self.devuas.get(devua)
169 if actual_device_root:
170 device = self.find_actual_root(device)
171
172 if device is None:
173 if filter_noise:
174 for exp, repl in self._noise_re:
175 devua = exp.sub(repl, devua)
176 devua = devua.strip()
177
178 if devua in self.devuas:
179 device = self.devuas.get(devua)
180 if actual_device_root:
181 device = self.find_actual_root(device)
182
183 if device is None and search is not None:
184 device = search(devua, self)
185 if actual_device_root:
186 device = self.find_actual_root(device)
187
188 if device is not None:
189 if instance:
190 return device()
191 else:
192 return device
193 else:
194 raise DeviceNotFound(devua)
195
196 - def select_id(self, devid, actual_device_root=False, instance=True):
197 """
198 Return a Device object based on the WURFL ID.
199
200 @param devid: The WURFL id to search for.
201 @type devid: string
202 @param actual_device_root: Return a device that is an actual device
203 root.
204 @param instance: Used to select that you want an instance instead of a
205 class.
206 @type instance: boolean
207 @raise DeviceNotFound:
208 """
209 if devid in self.devids:
210 device = self.devids.get(devid)
211 if actual_device_root:
212 device = self.find_actual_root(device)
213 if instance:
214 return device()
215 else:
216 return device
217 else:
218 raise DeviceNotFound(devid)
219
221 """
222 Add a group to the WURFL class hierarchy
223 @param group: The group's name. The group name should match this regexp
224 ^(_|[a-z])(_|[a-z]|[0-9])+$
225 @type group: string
226 """
227 self._name_test('group', group)
228 if group not in self.devids['generic'].groups:
229 self.devids['generic'].groups[group] = []
230 else:
231 raise ExistsException("'%s' group exists" % group)
232
234 """
235 Remove a group and all its capabilities from the WURFL class hierarchy
236 @param group: The group name. The group name should match this
237 regex '^[a-z]+(_|[a-z])+$' and be unique.
238 @type group: string
239 """
240 if group not in self.devids['generic'].groups:
241 raise BaseException("'%s' group not found" % group)
242 caps = self.devids['generic'].groups[group]
243 generic = self.devids['generic']
244 for cap in caps:
245 self._remove_capability(generic, cap)
246 del self.devids['generic'].groups[group]
247
249 if capability in device.__dict__:
250 delattr(device, capability)
251 for child in device.children:
252 self._remove_capability(child, capability)
253
255 device = self.devids[devid]
256 for child in copy(device.children):
257 self._remove_tree(child.devid)
258 del self.devids[device.devid]
259 del self.devuas[device.devua]
260
262 """
263 Add a capability to the WURFL class hierarchy
264 @param group: The group name. The group name should match this
265 regex ^(_|[a-z])(_|[a-z]|[0-9])+$
266 @type group: string
267 @param capability: The capability name. The capability name should match
268 this regex ^(_|[a-z])(_|[a-z]|[0-9])+$' and be
269 unique amongst all capabilities.
270 @type capability: string
271 """
272 try:
273 self.add_group(group)
274 except ExistsException:
275
276 pass
277
278 self._name_test('capability', capability)
279
280 for grp, caps in self.devids['generic'].groups.iteritems():
281 if capability in caps:
282 raise ExistsException("'%s' capability exists in group '%s'" %
283 (capability, grp))
284 else:
285 self.devids['generic'].groups[group].append(capability)
286 setattr(self.devids['generic'], capability, default)
287
289 """
290 Remove a capability from the WURFL class hierarchy
291 @param capability: The capability name.
292 @type capability: string
293 """
294 for group in self.devids['generic'].groups:
295 if capability in self.devids['generic'].groups[group]:
296 break
297 else:
298 raise BaseException("'%s' capability not found" % capability)
299 generic = self.devids['generic']
300 self._remove_capability(generic, capability)
301 self.devids['generic'].groups[group].remove(capability)
302
303 - def add(self, parent, devid, devua, actual_device_root=False,
304 capabilities=None):
305 """
306 Add a device to the WURFL class hierarchy
307
308 @param parent: A WURFL ID.
309 @type parent: string
310 @param devid: The device id for the new device.
311 @type devid: string
312 @param devua: The user agent for the new device.
313 @type devua: string
314 @param actual_device_root: Whether or not the new device is an
315 actual device.
316 @type actual_device_root: boolean
317 @param capabilities: The new capabilities for the new device class.
318 @type capabilities: dict
319 """
320 if parent not in self.devids:
321 raise DeviceNotFound(parent)
322 if devid in self.devids:
323 raise ExistsException("'%s' device already exists" % devid)
324 elif devua in self.devuas:
325 dup_devid = self.devuas[devua].devid
326 raise ExistsException("'%s' duplicate user agent with '%s'" %
327 (devua, dup_devid))
328
329 self.devids[devid] = devclass(self.devids[parent], devid, devua,
330 actual_device_root, capabilities)
331 self.devuas[devua] = self.devids[devid]
332
333 - def insert_before(self, child, devid, devua, actual_device_root=False,
334 capabilities=None):
335 """
336 Create and insert a device before another. The parent of the inserted
337 device becomes the parent of the child device. The child device's
338 parent is changed to the inserted device.
339
340 @param child: A WURFL ID. The child device cannot be the generic
341 device.
342 @type child: string
343 @param devid: The device id for the new device.
344 @type devid: string
345 @param devua: The user agent for the new device.
346 @type devua: string
347 @param actual_device_root: Whether or not the new device is an
348 actual device.
349 @type actual_device_root: boolean
350 @param capabilities: The new capabilities for the new device class.
351 @type capabilities: dict
352 """
353 if child == 'generic':
354 raise BaseException("cannot insert device before generic device")
355 if child not in self.devids:
356 raise DeviceNotFound(child)
357 if devid in self.devids:
358 raise ExistsException("'%s' device already exists" % devid)
359 elif devua in self.devuas:
360 dup_devid = self.devuas[devua].devid
361 raise ExistsException("'%s' duplicate user agent with '%s'" %
362 (devua, dup_devid))
363
364 child_device = self.devids[child]
365 parent_device = child_device.__bases__[0]
366 new_device = devclass(parent_device, devid, devua, actual_device_root,
367 capabilities)
368 parent_device.children.remove(child_device)
369 new_device.children.add(child_device)
370 child_device.__bases__ = (new_device,)
371 child_device.fall_back = devid
372 self.devids[devid] = new_device
373 self.devuas[devua] = self.devids[devid]
374
375 - def insert_after(self, parent, devid, devua, actual_device_root=False,
376 capabilities=None):
377 """
378 Create and insert a device after another. The parent of the inserted
379 device becomes the parent argument. The children of the parent device
380 become the children of the inserted device then the parent device's
381 children attribute is to the inserted device.
382
383 @param parent: A WURFL ID.
384 @type parent: string
385 @param devid: The device id for the new device.
386 @type devid: string
387 @param devua: The user agent for the new device.
388 @type devua: string
389 @param actual_device_root: Whether or not the new device is an
390 actual device.
391 @type actual_device_root: boolean
392 @param capabilities: The new capabilities for the new device class.
393 @type capabilities: dict
394 """
395 if parent not in self.devids:
396 raise DeviceNotFound(parent)
397 if devid in self.devids:
398 raise ExistsException("'%s' device already exists" % devid)
399 elif devua in self.devuas:
400 dup_devid = self.devuas[devua].devid
401 raise ExistsException("'%s' duplicate user agent with '%s'" %
402 (devua, dup_devid))
403
404 parent_device = self.devids[parent]
405 new_device = devclass(parent_device, devid, devua, actual_device_root,
406 capabilities)
407 new_device.children = parent_device.children
408 new_device.children.remove(new_device)
409 parent_device.children = set([new_device])
410
411 for child_device in new_device.children:
412 child_device.__bases__ = (new_device,)
413 child_device.fall_back = devid
414 self.devids[devid] = new_device
415 self.devuas[devua] = self.devids[devid]
416
418 """
419 Remove a device from the WURFL class hierarchy
420
421 @param devid: A WURFL ID. The generic device cannot be removed.
422 @type devid: string
423 """
424 if devid not in self.devids:
425 raise DeviceNotFound(devid)
426 if devid == 'generic':
427 raise BaseException("cannot remove generic device")
428
429 device = self.devids[devid]
430 parent_device = device.__bases__[0]
431 for cls in device.children:
432
433 cls.__bases__ = device.__bases__
434 parent_device.children.add(cls)
435 cls.fall_back = parent_device.devid
436 parent_device.children.remove(device)
437
438 del self.devids[device.devid]
439 del self.devuas[device.devua]
440
442 """
443 Remove a device and all of its children from the WURFL class hierarchy
444
445 @param devid: A WURFL ID. The generic device cannot be removed.
446 @type devid: string
447 """
448 if devid not in self.devids:
449 raise DeviceNotFound(devid)
450 if devid == 'generic':
451 raise BaseException("cannot remove generic device")
452
453 device = self.devids[devid]
454 self._remove_tree(devid)
455 parent_device = device.__bases__[0]
456 parent_device.children.remove(device)
457
458 @property
460 """
461 Yields all group names
462 """
463 return self.devids['generic'].groups.iterkeys()
464
466 for group in self.devids['generic'].groups:
467 for capability in self.devids['generic'].groups[group]:
468 if return_groups:
469 yield (group, capability)
470 else:
471 yield capability
472
473 @property
475 """
476 Yields all capability names
477 """
478 for capability in self._capability_generator():
479 yield capability
480
481 @property
483 """
484 Yields the tuple (group, capability) for all capabilities
485 """
486 for grp_cap in self._capability_generator(return_groups=True):
487 yield grp_cap
488
489 @property
491 """
492 Return an iterator of all WURFL device ids
493 """
494 return self.devids.iterkeys()
495
496 @property
498 """
499 Return an iterator of all device user agents
500 """
501 return self.devuas.iterkeys()
502
505
507 return len(self.devids)
508
510 type_set = set()
511 common_caps = ['actual_device_root', 'children', 'devid', 'devua',
512 'groups', 'fall_back']
513
514 for device in self.devids.itervalues():
515 for cap in (c for c in device.__dict__ if c not in common_caps and
516 not c.startswith('_')):
517 if isinstance(getattr(device, cap), str):
518 type_set.add((cap, str))
519 try:
520 type_set.remove((cap, float))
521 except KeyError:
522 pass
523 try:
524 type_set.remove((cap, int))
525 except KeyError:
526 pass
527 try:
528 type_set.remove((cap, bool))
529 except KeyError:
530 pass
531 elif isinstance(getattr(device, cap), float):
532 if (cap, str) not in type_set:
533 type_set.add((cap, float))
534 try:
535 type_set.remove((cap, int))
536 except KeyError:
537 pass
538 try:
539 type_set.remove((cap, bool))
540 except KeyError:
541 pass
542 elif isinstance(getattr(device, cap), int):
543 if ((cap, str) not in type_set and
544 (cap, float) not in type_set):
545 if isinstance(getattr(device, cap), bool):
546 if (cap, int) not in type_set:
547 type_set.add((cap, bool))
548 else:
549 type_set.add((cap, int))
550 try:
551 type_set.remove((cap, bool))
552 except KeyError:
553 pass
554
555 conv_dict = {}
556 for cap, cap_type in type_set:
557 conv_dict[cap] = cap_type
558
559 for device in self.devids.itervalues():
560 for cap in conv_dict:
561 if cap in device.__dict__:
562 setattr(device, cap, conv_dict[cap](device.__dict__[cap]))
563
565 if not self._name_test_re.match(value):
566 msg = "%s '%s' does not conform to regexp "
567 msg += "r'^(_|[a-z])(_|[a-z]|[0-9])+$'"
568 raise BaseException(msg % (name, value))
569