120 lines
3.3 KiB
Python
Executable File
120 lines
3.3 KiB
Python
Executable File
# The contents of this file are subject to the BitTorrent Open Source License
|
|
# Version 1.1 (the License). You may not copy or use this file, in either
|
|
# source code or executable form, except in compliance with the License. You
|
|
# may obtain a copy of the License at http://www.bittorrent.com/license/.
|
|
#
|
|
# Software distributed under the License is distributed on an AS IS basis,
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
# for the specific language governing rights and limitations under the
|
|
# License.
|
|
|
|
try:
|
|
from random import sample
|
|
except ImportError:
|
|
from random import choice
|
|
def sample(l, n):
|
|
if len(l) <= n:
|
|
return l
|
|
d = {}
|
|
while len(d) < n:
|
|
d[choice(l)] = 1
|
|
return d.keys()
|
|
|
|
from BitTorrent.platform import bttime as time
|
|
|
|
class KItem:
|
|
def __init__(self, key, value):
|
|
self.t = time()
|
|
self.k = key
|
|
self.v = value
|
|
def __cmp__(self, a):
|
|
# same value = same item, only used to keep dupes out of db
|
|
if self.v == a.v:
|
|
return 0
|
|
|
|
# compare by time
|
|
if self.t < a.t:
|
|
return -1
|
|
elif self.t > a.t:
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
def __hash__(self):
|
|
return self.v.__hash__()
|
|
|
|
def __repr__(self):
|
|
return `(self.k, self.v, time() - self.t)`
|
|
|
|
## in memory data store for distributed tracker
|
|
## keeps a list of values per key in dictionary
|
|
## keeps expiration for each key in a queue
|
|
## can efficiently expire all values older than a given time
|
|
## can insert one val at a time, or a list: ks['key'] = 'value' or ks['key'] = ['v1', 'v2', 'v3']
|
|
class KStore:
|
|
def __init__(self):
|
|
self.d = {}
|
|
self.q = []
|
|
|
|
def __getitem__(self, key):
|
|
return [x.v for x in self.d[key]]
|
|
|
|
def __setitem__(self, key, value):
|
|
if type(value) == type([]):
|
|
[self.__setitem__(key, v) for v in value]
|
|
return
|
|
x = KItem(key, value)
|
|
try:
|
|
l = self.d[key]
|
|
except KeyError:
|
|
self.d[key] = [x]
|
|
else:
|
|
# this is slow
|
|
try:
|
|
i = l.index(x)
|
|
del(l[i])
|
|
except ValueError:
|
|
pass
|
|
l.insert(0, x)
|
|
self.q.append(x)
|
|
|
|
def __delitem__(self, key):
|
|
del(self.d[key])
|
|
|
|
def __len__(self):
|
|
return len(self.d)
|
|
|
|
def keys(self):
|
|
return self.d.keys()
|
|
|
|
def values(self):
|
|
return [self[key] for key in self.keys()]
|
|
|
|
def items(self):
|
|
return [(key, self[key]) for key in self.keys()]
|
|
|
|
def expire(self, t):
|
|
#.expire values inserted prior to t
|
|
try:
|
|
while self.q[0].t <= t:
|
|
x = self.q.pop(0)
|
|
try:
|
|
l = self.d[x.k]
|
|
try:
|
|
while l[-1].t <= t:
|
|
l.pop()
|
|
except IndexError:
|
|
del(self.d[x.k])
|
|
except KeyError:
|
|
pass
|
|
except IndexError:
|
|
pass
|
|
|
|
def sample(self, key, n):
|
|
# returns n random values of key, or all values if less than n
|
|
try:
|
|
l = [x.v for x in sample(self.d[key], n)]
|
|
except ValueError:
|
|
l = [x.v for x in self.d[key]]
|
|
return l
|