Imitiating buttfly in being explicitly typed
[theonering] / src / gvoice / locations.py
1 #!/usr/bin/python
2
3 from __future__ import with_statement
4
5 import re
6 import logging
7
8 try:
9         import cPickle
10         pickle = cPickle
11 except ImportError:
12         import pickle
13
14 try:
15         import simplejson as _simplejson
16         simplejson = _simplejson
17 except ImportError:
18         simplejson = None
19
20 import constants
21 import util.coroutines as coroutines
22 import util.misc as misc_utils
23 import util.go_utils as gobject_utils
24
25 import browser_emu
26
27
28 _moduleLogger = logging.getLogger(__name__)
29
30
31 class Locations(object):
32
33         OLDEST_COMPATIBLE_FORMAT_VERSION = misc_utils.parse_version("0.8.0")
34
35         def __init__(self, asyncPool):
36                 self._asyncPool = asyncPool
37                 self._locations = {}
38                 self._browser = browser_emu.MozillaEmulator()
39
40                 self.updateSignalHandler = coroutines.CoTee()
41
42         def load(self, path):
43                 _moduleLogger.debug("Loading cache")
44                 assert not self._locations
45                 try:
46                         with open(path, "rb") as f:
47                                 fileVersion, fileBuild, locs = pickle.load(f)
48                 except (pickle.PickleError, IOError, EOFError, ValueError):
49                         _moduleLogger.exception("While loading for %s" % self._name)
50                         return
51
52                 if misc_utils.compare_versions(
53                         self.OLDEST_COMPATIBLE_FORMAT_VERSION,
54                         misc_utils.parse_version(fileVersion),
55                 ) <= 0:
56                         _moduleLogger.info("Loaded cache")
57                         self._locations = locs
58                 else:
59                         _moduleLogger.debug(
60                                 "Skipping cache due to version mismatch (%s-%s)" % (
61                                         fileVersion, fileBuild
62                                 )
63                         )
64
65         def save(self, path):
66                 _moduleLogger.info("Saving cache")
67                 if not self._locations:
68                         _moduleLogger.info("Odd, no conversations to cache.  Did we never load the cache?")
69                         return
70
71                 try:
72                         dataToDump = (constants.__version__, constants.__build__, self._locations)
73                         with open(path, "wb") as f:
74                                 pickle.dump(dataToDump, f, pickle.HIGHEST_PROTOCOL)
75                 except (pickle.PickleError, IOError):
76                         _moduleLogger.exception("While saving for %s" % self._name)
77                 _moduleLogger.info("%s Cache saved" % (self._name, ))
78
79         def request_location(self, number):
80                 try:
81                         return self._locations[number]
82                 except KeyError:
83                         le = gobject_utils.AsyncLinearExecution(self._asyncPool, self._request_location)
84                         le.start(number)
85                         return None
86
87         def _download_location(self, number):
88                 numberURL = "http://digits.cloudvox.com/%s.json" % number
89                 page = self._browser.download(numberURL)
90                 data = parse_json(page)
91                 return data
92
93         @misc_utils.log_exception(_moduleLogger)
94         def _request_location(self, number):
95                 try:
96                         locationData = yield (
97                                 self._download_location,
98                                 (number, ),
99                                 {},
100                         )
101                 except Exception:
102                         _moduleLogger.exception("%s While updating conversations" % (self._name, ))
103                         return
104
105                 self._locations[number] = locationData
106                 message = (locationData, )
107                 self.updateSignalHandler.stage.send(message)
108
109
110 def safe_eval(s):
111         _TRUE_REGEX = re.compile("true")
112         _FALSE_REGEX = re.compile("false")
113         s = _TRUE_REGEX.sub("True", s)
114         s = _FALSE_REGEX.sub("False", s)
115         return eval(s, {}, {})
116
117
118 def _fake_parse_json(flattened):
119         return safe_eval(flattened)
120
121
122 def _actual_parse_json(flattened):
123         return simplejson.loads(flattened)
124
125
126 if simplejson is None:
127         parse_json = _fake_parse_json
128 else:
129         parse_json = _actual_parse_json