Package pywurfl
[hide private]
[frames] | no frames]

Source Code for Package pywurfl

  1  # pywurfl - Wireless Universal Resource File Tools in Python 
  2  # Copyright (C) 2006 Armand Lynch 
  3  # 
  4  # This library is free software; you can redistribute it and/or modify it 
  5  # under the terms of the GNU Lesser General Public License as published by the 
  6  # Free Software Foundation; either version 2.1 of the License, or (at your 
  7  # option) any later version. 
  8  # 
  9  # This library is distributed in the hope that it will be useful, but WITHOUT 
 10  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 11  # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 
 12  # details. 
 13  # 
 14  # You should have received a copy of the GNU Lesser General Public License 
 15  # along with this library; if not, write to the Free Software Foundation, Inc., 
 16  # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 17  # 
 18  # Armand Lynch <lyncha@users.sourceforge.net> 
 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   
41 -class RootDevice(object):
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
114 -class Devices(object):
115 """ 116 Main pywurfl API class. 117 """ 118
119 - def __init__(self):
120 self.devids = {} 121 self.devuas = {} 122 123 # Regexes to remove noise from user agents 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
131 - def find_actual_root(self, device=RootDevice):
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
220 - def add_group(self, group):
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
233 - def remove_group(self, group):
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
248 - def _remove_capability(self, device, capability):
249 if capability in device.__dict__: 250 delattr(device, capability) 251 for child in device.children: 252 self._remove_capability(child, capability)
253
254 - def _remove_tree(self, devid):
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
261 - def add_capability(self, group, capability, default):
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 # If the group already exists, pass 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
288 - def remove_capability(self, capability):
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
417 - def remove(self, devid):
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 # set the base class of children devices to the base of this device 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
441 - def remove_tree(self, devid):
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
459 - def groups(self):
460 """ 461 Yields all group names 462 """ 463 return self.devids['generic'].groups.iterkeys()
464
465 - def _capability_generator(self, return_groups=False):
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
474 - def capabilities(self):
475 """ 476 Yields all capability names 477 """ 478 for capability in self._capability_generator(): 479 yield capability
480 481 @property
482 - def grouped_capabilities(self):
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
490 - def ids(self):
491 """ 492 Return an iterator of all WURFL device ids 493 """ 494 return self.devids.iterkeys()
495 496 @property
497 - def uas(self):
498 """ 499 Return an iterator of all device user agents 500 """ 501 return self.devuas.iterkeys()
502
503 - def __iter__(self):
504 return self.devids.__iter__()
505
506 - def __len__(self):
507 return len(self.devids)
508
509 - def _normalize_types(self):
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
564 - def _name_test(self, name, value):
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