1
2 '''
3 $Id: api.py 27734 2013-03-13 16:30:16Z dcarlier $
4
5 Used to load the recognition tree and perform lookups of all properties, or
6 individual properties. Typical usage is as follows:
7
8 >>> import api
9 >>> dir(api)
10 ['DaApi', 'IncorrectPropertyTypeException', 'InvalidPropertyException', 'JsonException', 'Mobi_Mtld_DA_UaProps', 'UnkownPropertyException', '__author__', '__builtins__', '__contributors__', '__doc__', '__file__', '__license__', '__name__', '__package__', '__url__', '__version__', 're', 'simplejson', 'string', 'sys', 'time', 'utilities']
11 >>> da = api.DaApi()
12 >>> tree = da.getTreeFromFile('DeviceAtlas.json')
13 >>> da.getTreeRevision(tree)
14 18251
15 >>> ua = 'SonyEricssonW850i/R1GB Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1'
16 >>> da.getProperties(tree, ua)
17 {u'drmOmaForwardLock': 1, u'umts': 1, u'displayWidth': 240, u'3gp.aac.lc': 1, u'mp3': 1, u'3gp.amr.wb': 1, u'cookieSupport': 1, u'midiPolyphonic': 1, u'image.Gif87': 1, u'3gp.h264.level10b': 1, u'wmv': 1, u'jsr118': 1, u'markup.xhtmlBasic10': 1, u'mp4.h264.level13': 0, u'mp4.h264.level11': 1, u'jqm': 0, u'vendor': u'Sony Ericsson', u'uriSchemeTel': 1, u'3gp.amr.nb': 1, u'amr': 1, u'stream.3gp.h264.level12': 0, u'stream.3gp.aac.lc': 1, u'stream.3gp.h264.level10': 1, u'stream.3gp.h264.level11': 0, u'osOsx': 0, u'midiMonophonic': 1, u'mp4.aac.lc': 1, u'gprs': 1, u'jsr30': 1, u'stream.3gp.h263': 1, u'usableDisplayHeight': 262, u'markup.xhtmlMp10': 1, u'markup.xhtmlMp12': 0, u'osAndroid': 0, u'qcelp': 0, u'osWindowsMobile': 0, u'3gpp2': 0, u'osSymbian': 0, u'mobileDevice': 1, '_unmatched': 'R1GB Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1', u'stream.mp4.h264.level13': 0, u'stream.mp4.h264.level11': 0, u'developerPlatformVersion': 7, u'osBada': 0, u'yearReleased': 2002, '_matched': 'SonyEricssonW850i/', u'midp': u'2.0', u'uriSchemeSmsTo': 0, u'developerPlatform': u'JavaPlatform', u'markup.wml1': 1, u'memoryLimitMarkup': 45000, u'cldc': u'1.1', u'amrInVideo': 1, u'qcelpInVideo': 0, u'https': 1, u'hscsd': 1, u'drmOmaSeparateDelivery': 1, u'image.Jpg': 1, u'uriSchemeSms': 1, u'stream.3gp.h264.level10b': 1, u'drmOmaCombinedDelivery': 1, u'vCardDownload': 1, u'aac': 1, u'osWindowsPhone': 0, u'mpeg4InVideo': 1, u'stream.mp4.aac.lc': 0, u'displayHeight': 320, u'mpeg4': 1, u'jsr37': 1, u'isTablet': 0, u'isEReader': 0, u'displayColorDepth': 18, u'3gp.h264.level13': 0, u'3gp.h264.level12': 0, u'3gp.h264.level11': 0, u'3gp.h264.level10': 1, u'id': 205009, u'image.Gif89a': 1, u'touchScreen': 0, u'csd': 1, u'3gpp': 1, u'jsr139': 1, u'aacInVideo': 1, u'image.Png': 1, u'markup.xhtmlMp11': 1, u'stream.3gp.amr.wb': 1, u'stream.3gp.amr.nb': 1, u'osWindows': 0, u'stream.3gp.h264.level13': 0, u'isMobilePhone': 1, u'3gp.h263': 1, u'osiOs': 0, u'hsdpa': 0, u'edge': 0, u'usableDisplayWidth': 230, u'h263Type0InVideo': 1, u'model': u'W850i', u'osWebOs': 0, u'isGamesConsole': 0}
18 >>>
19 >>> da.getProperty(tree, ua, 'displayWidth')
20 240
21 >>> da.getProperty(tree, ua, 'displayHeight')
22 320
23 >>> da.getPropertiesAsTyped(tree, ua)
24 {u'gprs': True, u'mpeg4': True, u'drmOmaForwardLock': True, u'umts': False, u'displayWidth': 240, u'mp3': True, u'markup.xhtmlMp11': True, u'markup.xhtmlMp10': True, u'markup.xhtmlMp12': False, u'id': 205009, u'memoryLimitMarkup': 45000, u'midiPolyphonic': True, u'image.Gif87': True, u'csd': True, u'3gpp': True, u'qcelp': False, u'wmv': True, u'markup.xhtmlBasic10': True, u'https': True, u'image.Gif89a': False, u'3gpp2': False, u'hscsd': False, u'midiMonophonic': True, u'drmOmaSeparateDelivery': True, u'displayColorDepth': 18, u'vendor': 'Sony Ericsson', u'image.Jpg': True, u'uriSchemeTel': True, u'mobileDevice': True, '_unmatched': 'R1GB Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1', u'hsdpa': False, u'amr': True, u'model': 'W850i', u'drmOmaCombinedDelivery': True, u'aac': False, u'mpeg4InVideo': True, u'image.Png': True, u'edge': False, u'h263Type0InVideo': True, u'displayHeight': 320, u'aacInVideo': True, '_matched': 'SonyEricssonW850i/'}
25 >>> da.getApiRevision()
26 2426
27 >>> da.listProperties(tree)
28 {u'drmOmaForwardLock': 'boolean', u'umts': 'boolean', u'displayWidth': 'integer', u'3gp.aac.lc': 'boolean', u'mp3': 'boolean', u'3gp.amr.wb': 'boolean', u'cookieSupport': 'boolean', u'osRim': 'boolean', u'midiPolyphonic': 'boolean', u'image.Gif87': 'boolean', u'3gp.h264.level10b': 'boolean', u'wmv': 'boolean', u'jsr118': 'boolean', u'markup.xhtmlBasic10': 'boolean', u'mp4.h264.level13': 'boolean', u'mp4.h264.level11': 'boolean', u'stylesheetSupport': 'string', u'httpDirectDownload': 'boolean', u'jqm': 'boolean', u'vendor': 'string', u'uriSchemeTel': 'boolean', u'isBrowser': 'boolean', u'3gp.amr.nb': 'boolean', u'browserName': 'string', u'amr': 'boolean', u'stream.3gp.h264.level12': 'boolean', u'stream.3gp.h264.level13': 'boolean', u'stream.3gp.h264.level10': 'boolean', u'stream.3gp.h264.level11': 'boolean', u'osLinux': 'boolean', u'osOsx': 'boolean', u'aacInVideo': 'boolean', u'mp4.aac.lc': 'boolean', u'gprs': 'boolean', u'memoryLimitEmbeddedMedia': 'integer', u'osWindowsMobile': 'boolean', u'jsr30': 'boolean', u'jsr37': 'boolean', u'markup.xhtmlMp11': 'boolean', u'markup.xhtmlMp10': 'boolean', u'markup.xhtmlMp12': 'boolean', u'osAndroid': 'boolean', u'qcelp': 'boolean', u'version': 'string', u'isFeedReader': 'boolean', u'osProprietary': 'string', u'3gpp2': 'boolean', u'oma': 'boolean', u'osSymbian': 'boolean', u'mobileDevice': 'boolean', u'stream.mp4.h264.level13': 'boolean', u'stream.mp4.h264.level11': 'boolean', u'isFilter': 'boolean', u'developerPlatformVersion': 'string', u'osBada': 'boolean', u'yearReleased': 'integer', u'awbInVideo': 'boolean', u'midp': 'string', u'uriSchemeSmsTo': 'boolean', u'memoryLimitDownload': 'integer', u'developerPlatform': 'string', u'markup.wml1': 'boolean', u'isSpam': 'boolean', u'memoryLimitMarkup': 'integer', u'cldc': 'string', u'inputDevices': 'string', u'amrInVideo': 'boolean', u'qcelpInVideo': 'boolean', u'https': 'boolean', u'markupSupport': 'string', u'hscsd': 'boolean', u'drmOmaSeparateDelivery': 'boolean', u'image.Jpg': 'boolean', u'uriSchemeSms': 'boolean', u'isRobot': 'boolean', u'stream.3gp.h264.level10b': 'boolean', u'drmOmaCombinedDelivery': 'boolean', u'vCardDownload': 'boolean', u'aac': 'boolean', u'isEReader': 'boolean', u'mpeg4InVideo': 'boolean', u'osWindowsPhone': 'boolean', u'stream.mp4.aac.lc': 'boolean', u'isDownloader': 'boolean', u'displayHeight': 'integer', u'mpeg4': 'boolean', u'stream.3gp.h263': 'boolean', u'imageFormatSupport': 'string', u'marketingName': 'string', u'isTablet': 'boolean', u'usableDisplayHeight': 'integer', u'displayColorDepth': 'integer', u'3gp.h264.level13': 'boolean', u'3gp.h264.level12': 'boolean', u'3gp.h264.level11': 'boolean', u'3gp.h264.level10': 'boolean', u'id': 'integer', u'stream.3gp.aac.lc': 'boolean', u'image.Gif89a': 'boolean', u'h263Type3InVideo': 'boolean', u'touchScreen': 'boolean', u'isChecker': 'boolean', u'jqm_alpha': 'boolean', u'csd': 'boolean', u'3gpp': 'boolean', u'aacLtpInVideo': 'boolean', u'jsr139': 'boolean', u'midiMonophonic': 'boolean', u'image.Png': 'boolean', u'stream.3gp.amr.wb': 'boolean', u'stream.3gp.amr.nb': 'boolean', u'osWindows': 'boolean', u'isMobilePhone': 'boolean', u'3gp.h263': 'boolean', u'osiOs': 'boolean', u'hsdpa': 'boolean', u'osVersion': 'string', u'browserVersion': 'string', u'inputModeSupport': 'string', u'scriptSupport': 'string', u'edge': 'boolean', u'usableDisplayWidth': 'integer', u'h263Type0InVideo': 'boolean', u'model': 'string', u'osWebOs': 'boolean', u'isGamesConsole': 'boolean'}
29 >>> da.getPropertiesAsTyped(tree, ua)['mobileDevice']
30 True
31 >>> da.getPropertiesAsTyped(tree, ua)['vendor']
32 'Sony Ericsson'
33 >>> da.getPropertiesAsTyped(tree, ua)['model']
34 'W850i'
35 >>> len(da.getPropertiesAsTyped(tree, ua))
36 4
37
38 In some contexts, the user-agent you want to recognise may have been provided
39 in a different header. Opera's mobile browser, for example, makes requests via
40 an HTTP proxy, which rewrites the headers. in that case, the original device's
41 user-agent is in the HTTP_X_OPERAMINI_PHONE_UA header.
42
43 '''
44
45 __version__ = '1.6 Build $Rev: 27734 $'
46 __url__ = 'http://deviceatlas.com'
47 __author__ = 'Ronan Cremin <http://deviceatlas.com/>'
48 __contributors__ = ['Adrian Hope-Balie <http://mobiforge.com/>','James Pearce <http://mobiforge.com/>', 'Hamish Graham <http://deviceatlas.com>', 'David Carlier <http://deviceatlas.com>']
49 __license__ = '''Copyright (c) 2008, mTLD (dotMobi), All rights reserved.
50 Portions copyright (c) 2008 by Argo Interactive Limited.
51 Portions copyright (c) 2008 by Nokia Inc.
52 Portions copyright (c) 2008 by Telecom Italia Mobile S.p.A.
53 Portions copyright (c) 2008 by Volantis Systems Limited.
54 Portions copyright (c) 2002-2008 by Andreas Staeding.
55 Portions copyright (c) 2008 by Zandan.
56 '''
57
58
59 import time, string, sys, re
60 from daExceptions import InvalidPropertyException, IncorrectPropertyTypeException, JsonException, UnkownPropertyException, ClientPropertiesException
61 from UaProps import *
62 from ClientProps import *
63 import utilities
64
65 try:
66 import json as simplejson
67 except ImportError:
68 import simplejson
72 API_ID = '6'
73
74 CLIENT_PROPS_HANDLER = "_cprops"
75 UA_PROPS_HANDLER = "_uaprops"
76
77 ID_TO_PROPERTIES_WITH_TYPE = "p"
78 ID_TO_PROPERTY_WITHOUT_TYPE = "pnr"
79 PROPERTIES_WITH_TYPE_TO_ID = "pr"
80 PROPERTIES_WITHOUT_TYPE_TO_ID = "pn"
81
82 ID_TO_VALUES = "v"
83 MAIN_TREE_BRANCH = "t"
84 REGEX = "r"
85 COMPILED_REGEX = "creg"
86
87 @staticmethod
89 '''Returns a tree from a JSON string return array tree
90
91 Some properties cannot be known before runtime and can change from user-agent to
92 user-agent. The most common of these are the OS Version and the Browser Version. This
93 API is able to dynamically detect these changing properties but introduces a small
94 overhead to do so. To disable returning these extra properties set
95 <i>includeChangeableUserAgentProperties</i> to <b>false</b>.
96
97 Arguments:
98 json -- a string of json data
99 includeChangeableUserAgentProperties -- also detect changeable user-agent properties
100
101 Returns:
102 The loaded JSON Trr
103
104 Throws:
105 RunTimeError
106 '''
107
108 if list(sys.version_info[:2]) < [2, 3]:
109 raise ImportError('Version 2.3 or later of Python is required')
110
111 tree = simplejson.loads(json)
112
113 if tree == {}:
114 raise JsonException('Unable to load JSON data')
115 elif ( '$' not in tree ):
116 raise JsonException('Bad data loaded into the tree')
117 elif float(tree['$']['Ver']) < 0.7:
118 raise JsonException('DeviceAtlas JSON file must be version 0.7 or later. Please download a more recent version')
119
120 pr = {}
121 pn = {}
122 idToProp = {}
123 for value in tree[DaApi.ID_TO_PROPERTIES_WITH_TYPE]:
124 key = tree[DaApi.ID_TO_PROPERTIES_WITH_TYPE].index(value)
125 name = value[1:]
126
127 pr[value] = key
128 pn[name] = key
129 idToProp[key] = name
130
131 tree[DaApi.PROPERTIES_WITH_TYPE_TO_ID] = pr
132 tree[DaApi.PROPERTIES_WITHOUT_TYPE_TO_ID] = pn
133 tree[DaApi.ID_TO_PROPERTY_WITHOUT_TYPE] = idToProp
134
135 if DaApi.REGEX not in tree:
136 tree[DaApi.REGEX] = {}
137
138
139
140
141
142 if Mobi_Mtld_DA_UaProps.UA_RULES in tree:
143
144 if not includeChangeableUserAgentProperties:
145
146 del tree[Mobi_Mtld_DA_UaProps.UA_RULES]
147 else:
148 tree[DaApi.UA_PROPS_HANDLER] = Mobi_Mtld_DA_UaProps(tree)
149
150 if Mobi_Mtld_DA_ClientProps.CP_RULES in tree:
151 tree[DaApi.CLIENT_PROPS_HANDLER] = Mobi_Mtld_DA_ClientProps(tree)
152
153 return tree
154
155
156 @staticmethod
158 '''Returns a tree from a JSON file.
159 Use absolute path name to be sure of success if the current working directory is not clear
160
161 Some properties cannot be known before runtime and can change from user-agent to
162 user-agent. The most common of these are the OS Version and the Browser Version. This
163 API is able to dynamically detect these changing properties but introduces a small
164 overhead to do so.
165
166 Arguments:
167 file -- the location of the file to read
168 includeChangeableUserAgentProperties -- set to False to disable returning extra properties set
169
170 Returns:
171 Hash tree
172 '''
173
174 json = open(filep, 'r').read()
175 if json == '':
176 raise JsonException('Unable to load file: %s' % filep)
177
178 return DaApi.getTreeFromString(json, includeChangeableUserAgentProperties)
179
180
181 @staticmethod
183 '''Get the generation date for this tree
184
185 Arguments:
186 tree -- previously loaded tree
187
188 Returns:
189 The date/time when the tree was generated
190 '''
191
192 return tree['$']['Gen']
193
194
195 @staticmethod
197 '''Returns revision number of the tree
198
199 Arguments:
200 tree -- previously loaded tree
201
202 Returns:
203 Integer revision number
204 '''
205
206 return tree['$']['Utc']
207
208
209 @staticmethod
211 '''Returns revision number of the tree
212
213 Arguments:
214 tree -- previously loaded tree
215
216 Returns:
217 Integer revision number
218 '''
219
220 return DaApi._getRevisionFromKeyword(tree['$']['Rev'])
221
222
223 @staticmethod
225 '''Returns revision number of the API
226
227 Arguments:
228 tree -- previously loaded tree
229
230 Returns:
231 Integer revision number
232 '''
233
234 return DaApi._getRevisionFromKeyword('$Rev: 27734 $')
235
236
237 @staticmethod
239 '''Returns array of known property names. Returns all properties
240 available for all user agents in this tree, with their data type
241 names
242
243 Arguments:
244 tree -- previously generated tree
245
246 Returns:
247 Dictionary of properties
248 '''
249
250 types = {
251 's': 'string',
252 'b': 'boolean',
253 'i': 'integer',
254 'd': 'date',
255 'u': 'unknown'
256 }
257 listProperties = {}
258
259 for prop in tree[DaApi.ID_TO_PROPERTIES_WITH_TYPE]:
260 listProperties[prop[1:]] = types[prop[0]]
261
262 return listProperties
263
264 @staticmethod
265 - def getProperties(tree, userAgent, cookie = None, typedValues = False, sought = None, uaPropsNeeded = False):
266 '''Returns an array of known properties (as strings) for the UA
267
268 Arguments:
269 tree -- previously generated tree
270 userAgent -- string from devices User-Agent header
271
272 Returns:
273 Dictionary of properties
274 '''
275
276 if cookie == None:
277 return DaApi._getProperties(tree, userAgent, typedValues, None, uaPropsNeeded)
278 else:
279 if DaApi.CLIENT_PROPS_HANDLER in tree:
280 clientProps = tree[DaApi.CLIENT_PROPS_HANDLER]
281 treeWalkProperties = DaApi._getProperties(tree, userAgent, typedValues, None, uaPropsNeeded)
282 return clientProps.getProperties(treeWalkProperties, cookie, typedValues)
283 else:
284 raise ClientPropertiesException("JSON file does not support client properties.")
285
286
287 @staticmethod
289 '''Returns an array of known properties (as typed) for the UA
290
291 Arguments:
292 tree --- previously generated tree
293 userAgent -- string from devices User-Agent header
294
295 Returns:
296 String property
297 '''
298
299 return DaApi.getProperties(tree, userAgent, cookie, True, None)
300
301
302 @staticmethod
303 - def getProperty(tree, userAgent, prop, cookie = None):
304 '''Returns a value for the named property of this user agent
305
306 Arguments:
307 tree -- previously generated tree
308 userAgent -- string from devices User-Agent header
309 property -- the name of the property to return
310
311 Returns:
312 String property
313 '''
314
315 return DaApi._getProperty(tree, userAgent, prop, cookie, False)
316
317
318 @staticmethod
320 '''Strongly-typed property accessor. Returns boolean property.
321 Throws exception of property is actually a different type
322
323 Arguments:
324 tree -- previously generated tree
325 userAgent -- string from devices User-Agent header
326 property -- the name of the property to return
327
328 Returns:
329 Boolean property
330 '''
331
332 DaApi._propertyTypeCheck(tree, prop, 'b', 'boolean')
333 return DaApi._getProperty(tree, userAgent, prop, cookie, True)
334
335
336 @staticmethod
338 '''Strongly-typed property accessor. Returns date property.
339 Throws exception of property is actually a different type
340
341 Arguments:
342 tree is previously generated tree
343 userAgent is string from devices User-Agent header
344 property is the name of the property to return
345
346 Returns:
347 String property
348 '''
349 DaApi._propertyTypeCheck(tree, prop, 'd', 'string')
350 return DaApi._getProperty(tree, userAgent, prop, cookie, True)
351
352
353 @staticmethod
355 '''Strongly-typed property accessor. Returns integer property.
356 Throws exception of property is actually a different type
357
358 Arguments:
359 tree -- previously generated tree
360 userAgent -- string from devices User-Agent header
361 property -- the name of the property to return
362
363 Returns:
364 Integer property
365 '''
366
367 DaApi._propertyTypeCheck(tree, prop, 'i', 'integer')
368 return DaApi._getProperty(tree, userAgent, prop, cookie, True)
369
370
371 @staticmethod
373 '''Strongly-typed property accessor. Returns string property.
374 Throws exception of property is actually a different type
375
376 Arguments:
377 tree -- previously generated tree
378 userAgent -- string from devices User-Agent header
379 property -- the name of the property to return
380
381 Returns:
382 String property
383 '''
384 DaApi._propertyTypeCheck(tree, prop, 's', 'string')
385 return DaApi._getProperty(tree, userAgent, prop, cookie, True)
386
387
388
389
390
391 @staticmethod
393 '''Returns cleaned up version of SVN revision string as integer
394 keyword is string
395 '''
396 return int(keyword[6:].replace('$', '').strip())
397
398
399 @staticmethod
400 - def _getProperties(tree, userAgent, typedValues, sought, uaPropsNeeded):
401 '''Returns the properties for a given User-Agent by first walking the tree
402 and then supplementing the tree properties with properties from the
403 User-Agent string itself.
404
405 - tree previously generated HashMap tree
406 - userAgent the device's User-Agent header string
407 - typedValues whether values in the hashmap are typed
408 - sought a set of any specific properties to find, if null then all properties are returned
409 - uaPropsNeeded whether the extra properties from the UA String are needed
410 the found properties
411 '''
412
413 uaPropsHandler = None
414
415 if sought:
416 uaPropsNeeded = False
417
418 uaPropsHandler = tree[DaApi.UA_PROPS_HANDLER]
419
420 if uaPropsHandler:
421 for prop in sought:
422 if uaPropsHandler.propIsOutput(prop):
423 uaPropsNeeded = True
424 break
425
426 if uaPropsNeeded:
427 sought = sought + uaPropsHandler.getRequiredProperties()
428
429
430 idProperties = {}
431 matched = ''
432
433 userAgent = userAgent.strip()
434 rules = tree[DaApi.REGEX][DaApi.API_ID]
435
436 sought, matched = DaApi._seekProperties(tree[DaApi.MAIN_TREE_BRANCH], userAgent.strip(), idProperties, sought, matched, rules)
437
438 properties = DaApi._lookupNameValue(tree, idProperties, typedValues)
439
440 if sought == None:
441 properties['_matched'] = matched
442 properties['_unmatched'] = userAgent[len(matched):]
443
444 if uaPropsNeeded:
445 if not uaPropsHandler:
446 uaPropsHandler = tree[DaApi.UA_PROPS_HANDLER]
447
448 if uaPropsHandler:
449 uaProps = uaPropsHandler.getProperties(userAgent, idProperties, sought, typedValues)
450 utilities.mergeProperties(properties, uaProps)
451
452 return properties
453
454
455
456 @staticmethod
458 '''Lookup the property names and values from the holder dictionaries
459
460 Arguments:
461 tree -- previously instantiated tree
462 idProperties --
463 typedValues --
464 '''
465
466 properties = {}
467 arr = []
468 try:
469 arr = idProperties.itertems()
470 except AttributeError:
471 arr = idProperties.items()
472
473 for propId, propValueId in arr:
474 propName = utilities.propertyFromId(tree, propId)
475 properties[propName] = utilities.getValue(tree, propId, propValueId, typedValues)
476
477 return properties
478
479
480 @staticmethod
481 - def _getProperty(tree, userAgent, prop, cookie, typedValues):
482 '''Get the property from the tree walk and User-Agent string.
483 '''
484
485 if cookie == None:
486 propertyId = DaApi._idFromProperty(tree, prop)
487 sought = [propertyId]
488
489 properties = DaApi.getProperties(tree, userAgent, None, typedValues, sought, True)
490
491 if prop not in properties:
492 raise InvalidPropertyException("The property \"%s\" does not exist for the User-Agent:\"%s\"" % (prop, userAgent))
493
494 return properties[prop]
495 else:
496 clientPropsHandler = tree[DaApi.CLIENT_PROPS_HANDLER]
497
498 if not clientPropsHandler:
499 raise ClientPropertiesException("JSON file does not support client properties")
500
501
502 parsedCookie = None
503 propertyId = DaApi._idFromProperty(tree, prop)
504
505 properties = None
506
507 if cookie:
508 isRuleProp = clientPropsHandler.propIsOutput(propertyId)
509
510 if not isRuleProp:
511 parsedCookie = clientPropsHandler.parseClientSideProperties(cookie, typedValues)
512
513 if parsedCookie:
514 properties = clientPropsHandler.getClientProperty(propertyId, prop, parsedCookie, typedValues)
515
516 if not properties or prop not in properties:
517 sought = [propertyId]
518 sought = sought + clientPropsHandler.getRequiredProperties()
519 properties = DaApi.getProperties(tree, userAgent, None, typedValues, sought, True)
520 properties = clientPropsHandler.getProperties(properties, parsedCookie, typedValues)
521
522 if prop not in properties:
523 raise InvalidPropertyException("The property \"%s\" does not exist for the User-Agent:\"%s\"" % (prop, userAgent))
524
525 return properties[prop]
526
527 @staticmethod
529 '''Return the coded ID for a property's name
530
531 Arguments:
532 tree -- a previously generated tree
533 property -- string
534
535 Raises UnkownPropertyException if property not found
536 '''
537
538 if prop in tree[DaApi.PROPERTIES_WITHOUT_TYPE_TO_ID]:
539 return tree[DaApi.PROPERTIES_WITHOUT_TYPE_TO_ID][prop]
540 else:
541 raise UnkownPropertyException('The property %s is not known in this tree' % prop)
542
543
544 @staticmethod
546 '''Return the name for a property's coded id
547
548 Arguments:
549 tree -- previously generated tree
550 '''
551
552
553 propName = tree[DaApi.ID_TO_PROPERTY_WITHOUT_TYPE][int(pid)]
554 return propName
555
556
557 @staticmethod
559 '''Checks that the property is of the supplied type or
560 throws exception
561
562 array tree Previously generated HashMap tree
563 array property The name of the property to return
564 string prefix The type prefix (i for integer)
565 string typeName Easy readable type name
566 '''
567
568 if (prefix + prop) not in tree[DaApi.PROPERTIES_WITH_TYPE_TO_ID]:
569 raise IncorrectPropertyTypeException('The property %s is not of type %s' % (prop, typeName))
570
571
572 @staticmethod
574 '''Seek properties for a UA within a node
575 This is designed to be recursed, and only externally called with the node representing the top of the tree
576
577 Arguments:
578 node -- dictionary representing JSON tree
579 string -- string
580 properties -- properties found thus far
581 sought -- properties being sought
582 matched -- portion of UA that has been matched thus far
583 '''
584
585 unmatched = string
586 if 'd' in node:
587 if (sought != None and len(sought) == 0):
588 return sought, matched
589
590 nodes = {}
591
592 try:
593 nodes = node['d'].iteritems()
594 except AttributeError:
595 nodes = node['d'].items()
596
597 for prop, value in nodes:
598
599 if sought == None or prop in sought:
600 properties[prop] = value
601
602
603
604
605 if sought != None and (
606 'm' not in node
607 or
608 ('m' in node and (prop not in node['m']))
609 ):
610
611 if prop in sought:
612 del sought[prop]
613
614 if 'c' in node:
615
616
617 if 'r' in node:
618 for i in range(0, len(node['r'])):
619 ruleId = node['r'][i]
620 string = re.sub(rules[ruleId], '', string)
621 for c in range(1, len(string)+1):
622 seek = string[0:c]
623 if seek in node['c']:
624 matched += seek
625 sought, matched = DaApi._seekProperties(node['c'][seek], string[c:], properties, sought, matched, rules)
626 break
627 return sought, matched
628
629
630 if __name__ == '__main__':
631 print(__doc__)
632 print('Version:', __version__)
633 print('License:', __license__)
634