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