forked from chubin/wttr.in
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcache.py
156 lines (125 loc) · 4.08 KB
/
cache.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""
LRU-Cache implementation for formatted (`format=`) answers
"""
import datetime
import re
import time
import os
import hashlib
import random
import pytz
import pylru
from globals import LRU_CACHE
CACHE_SIZE = 10000
CACHE = pylru.lrucache(CACHE_SIZE)
# strings longer than this are stored not in ram
# but in the file cache
MIN_SIZE_FOR_FILECACHE = 80
def _update_answer(answer):
def _now_in_tz(timezone):
return datetime.datetime.now(pytz.timezone(timezone)).strftime("%H:%M:%S%z")
if isinstance(answer, str) and "%{{NOW(" in answer:
answer = re.sub(r"%{{NOW\(([^}]*)\)}}", lambda x: _now_in_tz(x.group(1)), answer)
return answer
def get_signature(user_agent, query_string, client_ip_address, lang):
"""
Get cache signature based on `user_agent`, `url_string`,
`lang`, and `client_ip_address`
Return `None` if query should not be cached.
"""
if "?" in query_string:
location = query_string.split("?", 1)[0]
else:
location = query_string
if location.startswith("http://"):
location = location[7:]
elif location.startswith("https://"):
location = location[8:]
if ":" in location:
return None
signature = "%s:%s:%s:%s" % \
(user_agent, query_string, client_ip_address, lang)
print(signature)
return signature
def get(signature):
"""
If `update_answer` is not True, return answer as it is
stored in the cache. Otherwise update it, using
the `_update_answer` function.
"""
if not signature:
return None
value_record = CACHE.get(signature)
if not value_record:
return None
value = value_record["val"]
expiry = value_record["expiry"]
if value and time.time() < expiry:
if value.startswith("file:") or value.startswith("bfile:"):
value = _read_from_file(signature, sighash=value)
if not value:
return None
return _update_answer(value)
return None
def _randint(minimum, maximum):
return random.randrange(maximum - minimum)
def store(signature, value):
"""
Store in cache `value` for `signature`
"""
if not signature:
return _update_answer(value)
if len(value) >= MIN_SIZE_FOR_FILECACHE:
value_to_store = _store_in_file(signature, value)
else:
value_to_store = value
value_record = {
"val": value_to_store,
"expiry": time.time() + _randint(1000, 2000),
}
CACHE[signature] = value_record
return _update_answer(value)
def _hash(signature):
return hashlib.md5(signature.encode("utf-8")).hexdigest()
def _store_in_file(signature, value):
"""Store `value` for `signature` in cache file.
Return file name (signature_hash) as the result.
`value` can be string as well as bytes.
Returned filename is prefixed with "file:" (for text files)
or "bfile:" (for binary files).
"""
signature_hash = _hash(signature)
filename = os.path.join(LRU_CACHE, signature_hash)
if not os.path.exists(LRU_CACHE):
os.makedirs(LRU_CACHE)
if isinstance(value, bytes):
mode = "wb"
signature_hash = "bfile:%s" % signature_hash
else:
mode = "w"
signature_hash = "file:%s" % signature_hash
with open(filename, mode) as f_cache:
f_cache.write(value)
return signature_hash
def _read_from_file(signature, sighash=None):
"""Read value for `signature` from cache file,
or return None if file is not found.
If `sighash` is specified, do not calculate file name
from signature, but use `sighash` instead.
`sigash` can be prefixed with "file:" (for text files)
or "bfile:" (for binary files).
"""
mode = "r"
if sighash:
if sighash.startswith("file:"):
sighash = sighash[5:]
elif sighash.startswith("bfile:"):
sighash = sighash[6:]
mode = "rb"
else:
sighash = _hash(signature)
filename = os.path.join(LRU_CACHE, sighash)
if not os.path.exists(filename):
return None
with open(filename, mode) as f_cache:
return f_cache.read()