Skip to content

Commit

Permalink
more on persistent state
Browse files Browse the repository at this point in the history
  • Loading branch information
albertz committed Aug 27, 2012
1 parent 09f068a commit 99a39ab
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 52 deletions.
45 changes: 38 additions & 7 deletions State.py
Expand Up @@ -21,13 +21,20 @@ def songs():

class RecentlyplayedList:
Limit = 10
def __init__(self, list=[], previous=None):
def __init__(self, list=[], previous=None, index=0):
self.index = index
self.list = deque(list)
self.previous = None
def append(self, song):
if not song: return
self.list.append(song)
if len(self.list) >= self.Limit:
newList = RecentlyplayedList(list=self.list, previous=self.previous)
if len(self.list) >= self.Limit:
newList = PersistentObject(RecentlyplayedList, "recentlyplayed-%i.dat" % self.index, persistentRepr=True)
newList.index = self.index
newList.list = self.list
newList.previous = self.previous
newList.save()
self.index += 1
self.previous = newList
self.list = deque()
def __repr__(self):
Expand All @@ -38,18 +45,42 @@ def __repr__(self):
def loadRecentlyplayedList(state):
return PersistentObject(RecentlyplayedList, "recentlyplayed.dat")

class State:
class PlayerEventCallbacks:
onSongChange = None
onSongFinished = None
onPlayingStateChange = None

def loadPlayer(state):
import ffmpeg
player = ffmpeg.createPlayer()
for e in [m for m in dir(PlayerEventCallbacks) if not m.startswith("_")]:
cb = EventCallback(targetQueue=state.updates, name=e)
setattr(PlayerEventCallbacks, e, cb)
setattr(player, e, cb)
player.queue = state.queue
return player

def playerMain():
state.player.playing = True
for ev,args,kwargs in state.updates.read():
if ev is PlayerEventCallbacks.onSongChange:
state.curSong = kwargs["newSong"]
state.curSong.save()
state.recentlyPlayedList.append(kwargs["oldSong"])
state.recentlyPlayedList.save()
pass # onPlayingStateChange

class State(object):
queue = initBy(loadQueue)
recentlyPlayedList = initBy(loadRecentlyplayedList)
curSong = None
curSong = initBy(lambda self: PersistentObject(Song, "cursong.dat"))

playState = oneOf(
"playing",
"paused"
)

updates = initBy(lambda self: OnRequestQueue())
player = None
player = initBy(loadPlayer)

state = State()

18 changes: 1 addition & 17 deletions main.py
Expand Up @@ -6,23 +6,7 @@
from utils import *
from pprint import pprint

from State import state

class PlayerEventCallbacks:
onSongChange = None
onSongFinished = None
onPlayingStateChange = None

def playerMain():
import ffmpeg
state.player = ffmpeg.createPlayer()
for e in [m for m in dir(PlayerEventCallbacks) if not m.startswith("_")]:
cb = EventCallback(targetQueue=state.updates, name=e)
setattr(PlayerEventCallbacks, e, cb)
setattr(state.player, e, cb)
state.player.queue = state.queue
state.player.playing = True
for ev in state.updates.read(): pass # wait for exit
from State import state, playerMain, PlayerEventCallbacks

import lastfm

Expand Down
88 changes: 60 additions & 28 deletions utils.py
Expand Up @@ -50,24 +50,31 @@ def __call__(self, *args, **kwargs):
def __repr__(self):
return "<EventCallback %s>" % self.name

class initBy(property):
class initBy(object):
def __init__(self, initFunc):
property.__init__(self, fget = self.fget)
self.initFunc = initFunc
def fget(self, inst):
if hasattr(self, "value"): return self.value
self.value = self.initFunc(inst)
def load(self, inst):
if not hasattr(self, "value"):
self.value = self.initFunc(inst)
def __get__(self, inst, type=None):
self.load(inst)
if hasattr(self.value, "__get__"):
return self.value.__get__(inst, type)
return self.value

class oneOf(property):
def __set__(self, inst, value):
self.load(inst)
if hasattr(self.value, "__set__"):
return self.value.__set__(inst, value)
self.value = value

class oneOf(object):
def __init__(self, *consts):
property.__init__(self, fget = self.fget, fset = self.fset)
assert len(consts) > 0
self.consts = consts
self.value = consts[0]
def fget(self, inst):
def __get__(self, inst, type=None):
return self
def fset(self, inst, value):
def __set__(self, inst, value):
assert value in self.consts
self.value = value

Expand Down Expand Up @@ -108,54 +115,79 @@ def betterRepr(o):
# the main difference: this one is deterministic
# the orig dict.__repr__ has the order undefined.
if isinstance(o, list):
return "[" + ", ".join(map(betterRepr, o)) + "]"
return "[\n" + "".join(map(lambda v: betterRepr(v) + ",\n", o)) + "]"
if isinstance(o, deque):
return "deque([\n" + "".join(map(lambda v: betterRepr(v) + ",\n", o)) + "])"
if isinstance(o, tuple):
return "(" + ", ".join(map(betterRepr, o)) + ")"
if isinstance(o, dict):
return "{\n" + "".join(map(lambda (k,v): betterRepr(k) + ": " + betterRepr(v) + ",\n", sorted(o.iteritems()))) + "}"
# fallback
return repr(o)

def ObjectProxy(lazyLoader, custom_attribs={}, baseType=object, delHook=None, getattrHook=getattr, setattrHook=setattr):
def ObjectProxy(lazyLoader, custom_attribs={}, baseType=object):
class Value: pass
obj = Value()
def load():
if not hasattr(obj, "value"):
obj.value = lazyLoader()
def obj_getattr(self, key):
load()
if key in custom_attribs:
value = custom_attribs[key]
if hasattr(value, "__get__"):
value = value.__get__(self, type(self))
return value
return getattr(obj.value, key)
def obj_setattr(self, key, value):
load()
return setattr(obj.value, key, value)
def obj_del(self):
if delHook: delHook(self)
attribs = {"__getattr__": obj_getattr, "__setattr__": obj_setattr, "__del__": obj_del}
LazyObject = type("LazyObject", (baseType,), attribs)
def obj_desc_get(self, inst, type=None):
if inst is None:
load()
return obj.value
return self
def obj_desc_set(self, inst, value):
obj.value = value
attribs = custom_attribs.copy()
attribs.update({
"__getattr__": obj_getattr,
"__setattr__": obj_setattr,
"__get__": obj_desc_get,
"__set__": obj_desc_set,
})
LazyObject = type("LazyObject", (object,baseType), attribs)
return LazyObject()

def PersistentObject(baseType, filename):
def PersistentObject(baseType, filename, persistentRepr = False):
def load():
import appinfo
try:
f = open(appinfo.userdir + "/" + filename)
except IOError: # e.g. file-not-found. that's ok
return baseType()

obj = eval(f.read())
# some common types
g = {baseType.__name__: baseType} # the baseType itself
g.update(globals()) # all what we have here
if baseType.__module__:
# the module of the basetype
import sys
m = sys.modules[baseType.__module__]
g.update([(varname,getattr(m,varname)) for varname in dir(m)])
obj = eval(f.read(), g)
assert isinstance(obj, baseType)
return obj
def save(obj):
s = betterRepr(obj)
import appinfo
f = open(appinfo.userdir + "/" + filename)
f.write(betterRepr(obj))
f = open(appinfo.userdir + "/" + filename, "w")
f.write(s)
f.write("\n")
def obj_repr(obj):
return "PersistentObject(%r, %r)" % (baseType, filename)
return ObjectProxy(load, delHook=save, baseType=baseType,
custom_attribs={"save": save, "__repr__": obj_repr})
if persistentRepr:
return "PersistentObject(%r, %r)" % (baseType, filename)
return betterRepr(obj.__get__(None))
def obj_del(obj):
save(obj)
return ObjectProxy(load, baseType=baseType,
custom_attribs={
"save": save,
"__repr__": obj_repr,
"__del__": obj_del,
})

0 comments on commit 99a39ab

Please sign in to comment.