From 120305f383d2e831ca74920d0de4fb9a1f0a7d22 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Fri, 13 Jul 2012 11:28:16 -0400 Subject: [PATCH] cleared up a lot of boilerplate with lazy properties --- .../media-monitor2/media/monitor/events.py | 34 +++++++------------ .../media-monitor2/media/monitor/handler.py | 6 ++++ .../media-monitor2/media/monitor/log.py | 4 ++- .../media-monitor2/media/monitor/pure.py | 15 ++++++++ .../media/monitor/watchersyncer.py | 13 +++++-- python_apps/media-monitor2/mm2.py | 13 ++++--- python_apps/pypo/liquidsoap_bin | 2 +- 7 files changed, 53 insertions(+), 34 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/events.py b/python_apps/media-monitor2/media/monitor/events.py index ccd6bdb71..4538db3ea 100644 --- a/python_apps/media-monitor2/media/monitor/events.py +++ b/python_apps/media-monitor2/media/monitor/events.py @@ -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 diff --git a/python_apps/media-monitor2/media/monitor/handler.py b/python_apps/media-monitor2/media/monitor/handler.py index 259c9659d..7a1067bfc 100644 --- a/python_apps/media-monitor2/media/monitor/handler.py +++ b/python_apps/media-monitor2/media/monitor/handler.py @@ -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): diff --git a/python_apps/media-monitor2/media/monitor/log.py b/python_apps/media-monitor2/media/monitor/log.py index bae437b1e..5f1c11597 100644 --- a/python_apps/media-monitor2/media/monitor/log.py +++ b/python_apps/media-monitor2/media/monitor/log.py @@ -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 diff --git a/python_apps/media-monitor2/media/monitor/pure.py b/python_apps/media-monitor2/media/monitor/pure.py index aa0c962b9..01aaac33c 100644 --- a/python_apps/media-monitor2/media/monitor/pure.py +++ b/python_apps/media-monitor2/media/monitor/pure.py @@ -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 diff --git a/python_apps/media-monitor2/media/monitor/watchersyncer.py b/python_apps/media-monitor2/media/monitor/watchersyncer.py index 202a04a63..e2dac9f7c 100644 --- a/python_apps/media-monitor2/media/monitor/watchersyncer.py +++ b/python_apps/media-monitor2/media/monitor/watchersyncer.py @@ -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 diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 4fc4b3fbb..7f8b43bd0 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -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() diff --git a/python_apps/pypo/liquidsoap_bin b/python_apps/pypo/liquidsoap_bin index 0653ec0b8..492242f4b 160000 --- a/python_apps/pypo/liquidsoap_bin +++ b/python_apps/pypo/liquidsoap_bin @@ -1 +1 @@ -Subproject commit 0653ec0b89362921f075af96ee8772538b801a7c +Subproject commit 492242f4bb7367afebbf2f096067cb5a5d3c0449