cleared up a lot of boilerplate with lazy properties

This commit is contained in:
Rudi Grinberg 2012-07-13 11:28:16 -04:00
parent 309b071142
commit 120305f383
7 changed files with 53 additions and 34 deletions

View File

@ -3,6 +3,7 @@ import os
import mutagen
import abc
from media.monitor.exceptions import BadSongFile
from media.monitor.pure import LazyProperty
class PathChannel(object):
"""a dumb struct; python has no record types"""
@ -18,32 +19,21 @@ class HasMetaData(object):
__metaclass__ = abc.ABCMeta
# doing weird bullshit here because python constructors only
# call the constructor of the leftmost superclass.
@property
@LazyProperty
def metadata(self):
# Normally this would go in init but we don't like
# relying on consumers of this behaviour to have to call
# the constructor
if not hasattr(self,"_loaded"): self._loaded = False
if self._loaded: return self._metadata
else:
f = None
try: f = mutagen.File(self.path, easy=True)
except Exception: raise BadSongFile(self.path)
# value returned by mutagen only acts like a dictionary.
# in fact it comes with a nice surprise for you if you try
# to add elements to it
self._metadata = {}
for k,v in f:
if isinstance(v, list):
if len(v) == 1:
self._metadata[k] = v[0]
else:
raise Exception("Weird mutagen %s:%s" % (k,str(v)))
else:
self._metadata[k] = v
self._loaded = True
return self.metadata
try: f = mutagen.File(self.path, easy=True)
except Exception: raise BadSongFile(self.path)
metadata = {}
for k,v in f:
# Special handling of attributes here
if isinstance(v, list):
if len(v) == 1: metadata[k] = v[0]
else: raise Exception("Weird mutagen %s:%s" % (k,str(v)))
else: metadata[k] = v
return metadata
class BaseEvent(object):
__metaclass__ = abc.ABCMeta

View File

@ -10,6 +10,12 @@ class Handles(object):
@abc.abstractmethod
def handle(self, sender, event, *args, **kwargs): pass
# TODO : remove the code duplication between ReportHandler and
# ProblemFileHandler. Namely the part where both initialize pydispatch
# TODO : Investigate whether weak reffing in dispatcher.connect could possibly
# cause a memory leak
class ReportHandler(Handles):
__metaclass__ = abc.ABCMeta
def __init__(self, signal):

View File

@ -1,12 +1,14 @@
import logging
import abc
from media.monitor.pure import LazyProperty
logger = logging.getLogger('mediamonitor2')
logging.basicConfig(filename='/home/rudi/throwaway/mm2.log', level=logging.DEBUG)
class Loggable(object):
__metaclass__ = abc.ABCMeta
@property
# TODO : replace this boilerplate with LazyProperty
@LazyProperty
def logger(self):
if not hasattr(self,"_logger"): self._logger = logging.getLogger('mediamonitor2')
return self._logger

View File

@ -5,6 +5,21 @@ import shutil
supported_extensions = ["mp3", "ogg"]
unicode_unknown = u'unknown'
class LazyProperty(object):
"""
meant to be used for lazy evaluation of an object attribute.
property should represent non-mutable data, as it replaces itself.
"""
def __init__(self,fget):
self.fget = fget
self.func_name = fget.__name__
def __get__(self,obj,cls):
if obj is None: return None
value = self.fget(obj)
setattr(obj,self.func_name,value)
return value
class IncludeOnly(object):
"""
A little decorator to help listeners only be called on extensions they support

View File

@ -7,7 +7,10 @@ from media.monitor.handler import ReportHandler
from media.monitor.events import NewFile, DeleteFile
from media.monitor.log import Loggable
from media.monitor.exceptions import BadSongFile
from media.monitor.airtime import Request
import media.monitor.pure as mmp
from media.monitor.pure import LazyProperty
import api_clients.api_client as ac
class RequestSync(threading.Thread,Loggable):
def __init__(self, watcher, requests):
@ -15,11 +18,14 @@ class RequestSync(threading.Thread,Loggable):
self.watcher = watcher
self.requests = requests
@LazyProperty
def apiclient(self):
return ac.AirTimeApiClient()
def run(self):
# TODO : implement proper request sending
self.logger.info("launching request with %d items." % len(self.requests))
try: Request.serialize(self.requests).send()
except: self.logger.info("Failed to send request...")
# self.apiclient.update_media_metadata(self
self.watcher.flag_done()
class TimeoutWatcher(threading.Thread,Loggable):
@ -55,6 +61,7 @@ class WatchSyncer(ReportHandler,Loggable):
# trying to send the http requests in order
self.__requests = []
self.request_running = False
# we don't actually use this "private" instance variable anywhere
self.__current_thread = None
tc = TimeoutWatcher(self, timeout)
tc.daemon = True

View File

@ -6,8 +6,7 @@ from media.monitor.organizer import Organizer
from media.monitor.events import PathChannel
from media.monitor.watchersyncer import WatchSyncer
from media.monitor.handler import ProblemFileHandler
from media.monitor.bootstrap import Bootstrapper
from media.monitor.airtime import DBDumper, Connection
#from media.monitor.bootstrap import Bootstrapper
channels = {
# note that org channel still has a 'watch' path because that is the path
@ -22,11 +21,11 @@ org = Organizer(channel=channels['org'],target_path=channels['watch'].path)
watch = WatchSyncer(channel=channels['watch'])
problem_files = ProblemFileHandler(channel=channels['badfile'])
# do the bootstrapping before any listening is going one
conn = Connection('localhost', 'more', 'shit', 'here')
db = DBDumper(conn).dump_block()
bs = Bootstrapper(db, [channels['org']], [channels['watch']])
bs.flush_organize()
bs.flush_watch()
#conn = Connection('localhost', 'more', 'shit', 'here')
#db = DBDumper(conn).dump_block()
#bs = Bootstrapper(db, [channels['org']], [channels['watch']])
#bs.flush_organize()
#bs.flush_watch()
wm = pyinotify.WatchManager()

@ -1 +1 @@
Subproject commit 0653ec0b89362921f075af96ee8772538b801a7c
Subproject commit 492242f4bb7367afebbf2f096067cb5a5d3c0449