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