From 5fb17da1d5d8dc18d0090f43e06cd22f26c459e7 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 9 May 2010 13:04:26 -0400 Subject: [PATCH] Oops, I'd forgotten to version the restored hooke.playlist module. --- hooke/playlist.py | 230 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 hooke/playlist.py diff --git a/hooke/playlist.py b/hooke/playlist.py new file mode 100644 index 0000000..de58e9f --- /dev/null +++ b/hooke/playlist.py @@ -0,0 +1,230 @@ +"""The playlist module provides :class:`Playlist` its subclass +:class:`FilePlaylist` for manipulating lists of +:class:`hooke.curve.Curve`\s. +""" + +import copy +import hashlib +import os.path +import xml.dom.minidom + +from . import curve as curve + +class Playlist (list): + """A list of :class:`hooke.curve.Curve`\s. + + Keeps a list of :attr:`drivers` for loading curves, the + :attr:`index` (i.e. "bookmark") of the currently active curve, and + a :class:`dict` of additional informtion (:attr:`info`). + """ + def __init__(self, drivers, name=None): + super(Playlist, self).__init__() + self.drivers = drivers + self.name = name + self.info = {} + self._index = 0 + + def append_curve_by_path(self, path, info=None, identify=True): + if self.path != None: + path = os.path.join(self.path, path) + path = os.path.normpath(path) + c = curve.Curve(path, info=info) + if identify == True: + c.identify(self.drivers) + self.append(c) + return c + + def active_curve(self): + return self[self._index] + + def has_curves(self): + return len(self) > 0 + + def jump(self, index): + if len(self) == 0: + self._index = 0 + else: + self._index = index % len(self) + + def next(self): + self.jump(self._index + 1) + + def previous(self): + self.jump(self._index - 1) + + def filter(self, keeper_fn=lambda curve:True): + playlist = copy.deepcopy(self) + for curve in reversed(playlist.curves): + if keeper_fn(curve) != True: + playlist.curves.remove(curve) + try: # attempt to maintain the same active curve + playlist._index = playlist.index(self.active_curve()) + except ValueError: + playlist._index = 0 + return playlist + +class FilePlaylist (Playlist): + version = '0.1' + + def __init__(self, drivers, name=None, path=None): + if name == None and path != None: + name = os.path.basename(path) + super(FilePlaylist, self).__init__(drivers, name) + self.path = path + self._digest = None + + def is_saved(self): + return self.digest() == self._digest + + def digest(self): + r"""Compute the sha1 digest of the flattened playlist + representation. + + Examples + -------- + + >>> root_path = os.path.sep + 'path' + >>> p = FilePlaylist(drivers=[], + ... path=os.path.join(root_path, 'to','playlist')) + >>> p.info['note'] = 'An example playlist' + >>> c = curve.Curve(os.path.join(root_path, 'to', 'curve', 'one')) + >>> c.info['note'] = 'The first curve' + >>> p.append(c) + >>> c = curve.Curve(os.path.join(root_path, 'to', 'curve', 'two')) + >>> c.info['note'] = 'The second curve' + >>> p.append(c) + >>> p.digest() + "\xa1\x99\x8a\x99\xed\xad\x13'\xa7w\x12\x00\x07Z\xb3\xd0zN\xa2\xe1" + """ + string = self.flatten() + return hashlib.sha1(string).digest() + + def flatten(self, absolute_paths=False): + """Create a string representation of the playlist. + + A playlist is an XML document with the following syntax:: + + + + + + + + Relative paths are interpreted relative to the location of the + playlist file. + + Examples + -------- + + >>> root_path = os.path.sep + 'path' + >>> p = FilePlaylist(drivers=[], + ... path=os.path.join(root_path, 'to','playlist')) + >>> p.info['note'] = 'An example playlist' + >>> c = curve.Curve(os.path.join(root_path, 'to', 'curve', 'one')) + >>> c.info['note'] = 'The first curve' + >>> p.append(c) + >>> c = curve.Curve(os.path.join(root_path, 'to', 'curve', 'two')) + >>> c.info['note'] = 'The second curve' + >>> p.append(c) + >>> print p.flatten() # doctest: +NORMALIZE_WHITESPACE +REPORT_UDIFF + + + + + + + >>> print p.flatten(absolute_paths=True) # doctest: +NORMALIZE_WHITESPACE +REPORT_UDIFF + + + + + + + """ + implementation = xml.dom.minidom.getDOMImplementation() + # create the document DOM object and the root element + doc = implementation.createDocument(None, 'playlist', None) + root = doc.documentElement + root.setAttribute('version', self.version) # store playlist version + root.setAttribute('index', str(self._index)) + for key,value in self.info.items(): # save info variables + root.setAttribute(key, str(value)) + for curve in self: # save curves and their attributes + curve_element = doc.createElement('curve') + root.appendChild(curve_element) + path = os.path.abspath(os.path.expanduser(curve.path)) + if absolute_paths == False: + path = os.path.relpath( + path, + os.path.abspath(os.path.expanduser(self.path))) + curve_element.setAttribute('path', path) + for key,value in curve.info.items(): + curve_element.setAttribute(key, str(value)) + string = doc.toprettyxml(encoding='utf-8') + root.unlink() # break circular references for garbage collection + return string + + def _from_xml_doc(self, doc): + """Load a playlist from an :class:`xml.dom.minidom.Document` + instance. + """ + root = doc.documentElement + for attribute,value in root.attributes.items(): + if attribute == 'version': + assert value == self.version, \ + 'Cannot read v%s playlist with a v%s reader' \ + % (value, self.version) + elif attribute == 'index': + self._index = int(value) + else: + self.info[attribute] = value + for curve_element in doc.getElementsByTagName('curve'): + path = curve_element.getAttribute('path') + info = dict(curve_element.attributes.items()) + info.pop('path') + self.append_curve_by_path(path, info, identify=False) + self.jump(self._index) # ensure valid index + + def from_string(self, string): + """Load a playlist from a string. + + Examples + -------- + + >>> string = ''' + ... + ... + ... + ... + ... ''' + >>> p = FilePlaylist(drivers=[], + ... path=os.path.join('path', 'to','playlist')) + >>> p.from_string(string) + >>> p._index + 1 + >>> p.info + {u'note': u'An example playlist'} + >>> for curve in p: + ... print curve.path + path/to/curve/one + path/to/curve/two + """ + doc = xml.dom.minidom.parseString(string) + return self._from_xml_doc(doc) + + def load(self, path=None): + """Load a playlist from a file. + """ + if path != None: + self.path = path + if self.name == None: + self.name = os.path.basename(self.path) + doc = xml.dom.minidom.parse(path) + return self._from_xml_doc(doc) + + def save(self, path): + """Saves the playlist in a XML file. + """ + f = file(path, 'w') + f.write(self.flatten()) + f.close() -- 2.26.2