Friedrich Weber avatar Friedrich Weber committed 25fae9d

some liblet changes, added jamendo liblets

Comments (0)

Files changed (9)

     token HYPHEN: "-"
     token DHYPHEN: "--"
     token GT: ">"
-    token STRING: '[rR]?\'([^\\n\'\\\\]|\\\\.)*\'|[rR]?"([^\\n"\\\\]|\\\\.)*"'
+    token STRING: '\'([^\\n\'\\\\]|\\\\.)*\'|"([^\\n"\\\\]|\\\\.)*"'
 
     rule goal: subject [parameters<<[], {}>>] [additional<<{}>>] END
                 {{ try: parameters }}
             yapps2-generated parser in :mod:`jambalah.command`.
         """
         if not line:
-            return self.emptyline()        
+            return self.emptyline()
         cmd, positional, keywords, additional = parse_line(line)
         self.lastcmd = line
         try:
-            func = self.jam.bindings[cmd] 
+            func = self.jam.bindings[cmd]
         except KeyError:
             try:
                 func = getattr(self, 'do_%s' % cmd)
         if arg:
             # XXX check arg syntax
             try:
-                func = self.jam.help_bindings[positional[0]] 
+                func = self.jam.help_bindings[positional[0]]
             except KeyError:
                 try:
                     try:

jambalah/command.py

         ('HYPHEN', re.compile('-')),
         ('DHYPHEN', re.compile('--')),
         ('GT', re.compile('>')),
-        ('STRING', re.compile('[rR]?\'([^\\n\'\\\\]|\\\\.)*\'|[rR]?"([^\\n"\\\\]|\\\\.)*"')),
+        ('STRING', re.compile('\'([^\\n\'\\\\]|\\\\.)*\'|"([^\\n"\\\\]|\\\\.)*"')),
     ]
     def __init__(self, str):
         Scanner.__init__(self,None,['\\s+'],str)
             liblet (or None)
         """
         # split the path into components and throw away empty strings
-        # (that's done using `bool`, because 
+        # (that's done using `bool`, because
         # bool('') -> False, bool('a') -> True)
         splitted = filter(bool, path.split(os.path.sep))
         # get the root node we'll use (root for absolute paths)
                 continue
             # is a real component. get the liblet's child.
             else:
+                node.use()
                 node = node.get_child(next)
         if node is None:
             raise CLIError('You are going beyond the top-level node.') # TODO: just ignore this?
             raise CLISyntaxError(
                     'You have to specify at least one filename or track')
         # list of atomic liblets to add
-        liblets = []  
+        liblets = []
         for path in positional:
             liblet = self.parse_path(path)
             # only move atomic (top-level) liblets for now.
         """
         dest = positional[0]
         self.liblet = self.parse_path(dest)
+        self.liblet.use()
         self.update_prompt()
 
     def do_ls(self, positional, keywords, additional):
             liblet = self.parse_path(dest)
         else:
             liblet = self.liblet
+        liblet.use()
         self.cli.columnize(list(liblet.get_children_names()))
 
     def do_play(self, positional, keywords, additional):
             add all default bindings
 
             Currently:
-            
+
              * quit
              * cd
              * ls
 
     def add_to_playlist(self, playlist, urls):
         self.playlists.setdefault(playlist, []).extend(urls)
-    
+
     def play_playlist(self, playlist):
         assert playlist in self.playlists
         self.current_playlist = playlist

jambalah/jamendo.py

+/home/fred/dev/jambalah/jamendo/jamendo.py

jambalah/jamendolib.py

+from string import ascii_lowercase
+
+from . import jamendo
+from .liblet import Liblet, SimpleLiblet, AtomicLiblet
+
+def nice_name(name):
+    if isinstance(name, unicode):
+        name = name.encode('ascii', 'replace') # TODO
+    return name
+
+class RootLiblet(SimpleLiblet):
+    def __init__(self, jam, parent, name):
+        SimpleLiblet.__init__(self, jam, parent, name)
+        self['artists'] = ArtistsLiblet(jam, self, 'artists')
+
+class ArtistsLiblet(SimpleLiblet):
+    def __init__(self, jam, parent, name):
+        SimpleLiblet.__init__(self, jam, parent, name)
+        for letter in ascii_lowercase:
+            self[letter] = ArtistsLiblet2(jam, self, letter)
+
+class ArtistsLiblet2(SimpleLiblet):
+    def __init__(self, jam, parent, letter):
+        SimpleLiblet.__init__(self, jam, parent, letter)
+        self.name = letter
+        self.artists = {} # nice name: artist
+
+    def use(self):
+        if not self:
+            self._fill_cache()
+    
+    def _fill_cache(self):
+        for obj in jamendo.iterate_objects(
+                jamendo.Artist.by_firstletter, self.name, hasalbums=1):
+            nn = nice_name(obj.name)
+            obj.nice_name = nn
+            self[nn] = ArtistLiblet(self.jam, self, obj)
+
+class ArtistLiblet(SimpleLiblet):
+    def __init__(self, jam, parent, artist):
+        SimpleLiblet.__init__(self, jam, parent)
+        self.artist = artist
+        self.name = artist.nice_name
+        self.albums = {}
+
+    def use(self):
+        if not self:
+            self._fill_cache()
+
+    def _fill_cache(self):
+        for album in self.artist.albums:
+            nn = nice_name(album.name)
+            album.nice_name = nn
+            self[nn] = AlbumLiblet(self.jam, self, album) 
+
+class AlbumLiblet(SimpleLiblet):
+    def __init__(self, jam, parent, album):
+        SimpleLiblet.__init__(self, jam, parent)
+        self.album = album
+        self.name = album.nice_name
+
+    def use(self):
+        if not self:
+            self._fill_cache()
+
+    def _fill_cache(self):
+        for track in self.album.tracks:
+            self[nice_name(track.name)] = AtomicLiblet(self.jam, self, track.stream) # TODO

jambalah/liblet.py

 import os
+from .odict import odict
 
 class UnknownLiblet(Exception):
     pass
         self.jam = jam
         self.parent = parent
 
-    @property
-    def name(self):
-        raise NotImplementedError()
+    def use(self):
+        """
+            a hook called when the liblet is used somehow.
+        """
+        pass
 
     @property
     def full_name(self):
         else:
             return self.get_child_by_name(stuff)
 
+class SimpleLiblet(Liblet, odict):
+    """
+        a simple liblet you can use like an ordered. it will always
+        use its value as its children list.
+
+        See :mod:`jambalah.odict` for more information on the odict
+        implementation.
+    """
+    def __init__(self, jam, parent, name='<unnamed>'):
+        Liblet.__init__(self, jam, parent)
+        odict.__init__(self)
+        self.name = name
+
+    def get_children_names(self):
+        return self.iterkeys()
+
+    def get_child_by_name(self, name):
+        return self[name]
+
+    def get_child_by_no(self, no):
+        return self.byindex(no - 1)[1]
+
 class AtomicLiblet(Liblet):
     """
         an atomic liblet is a liblet without children, e.g.
 from .liblet import Liblet, UnknownLiblet, make_named_liblet
 from .local import LocalLiblet
+from .jamendolib import RootLiblet as JamendoLiblet
 
 class MetaLiblet(Liblet):
     def __init__(self, jam):
         Liblet.__init__(self, jam, None)
-        self.liblets = {'local': 
-                make_named_liblet(LocalLiblet, 'local', self.jam, self, '/')
-                }
+        self.liblets = {
+                'local':
+                make_named_liblet(LocalLiblet, 'local', self.jam, self, '/'),
+                'jamendo':
+                JamendoLiblet(self.jam, self, 'jamendo')
+            }
 
     @property
     def name(self):

jambalah/odict.py

+# -*- coding: utf-8 -*-
+"""
+    odict
+    ~~~~~
+    
+    (from http://dev.pocoo.org/hg/sandbox/raw-file/tip/odict.py)
+
+    This module is an example implementation of an ordered dict for the
+    collections module.  It's not written for performance (it actually
+    performs pretty bad) but to show how the API works.
+
+
+    Questions and Answers
+    =====================
+
+    Why would anyone need ordered dicts?
+
+        Dicts in python are unordered which means that the order of items when
+        iterating over dicts is undefined.  As a matter of fact it is most of
+        the time useless and differs from implementation to implementation.
+
+        Many developers stumble upon that problem sooner or later when
+        comparing the output of doctests which often does not match the order
+        the developer thought it would.
+
+        Also XML systems such as Genshi have their problems with unordered
+        dicts as the input and output ordering of tag attributes is often
+        mixed up because the ordering is lost when converting the data into
+        a dict.  Switching to lists is often not possible because the
+        complexity of a lookup is too high.
+
+        Another very common case is metaprogramming.  The default namespace
+        of a class in python is a dict.  With Python 3 it becomes possible
+        to replace it with a different object which could be an ordered dict.
+        Django is already doing something similar with a hack that assigns
+        numbers to some descriptors initialized in the class body of a
+        specific subclass to restore the ordering after class creation.
+
+        When porting code from programming languages such as PHP and Ruby
+        where the item-order in a dict is guaranteed it's also a great help
+        to have an equivalent data structure in Python to ease the transition.
+
+    Where are new keys added?
+
+        At the end.  This behavior is consistent with Ruby 1.9 Hashmaps
+        and PHP Arrays.  It also matches what common ordered dict
+        implementations do currently.
+
+    What happens if an existing key is reassigned?
+
+        The key is *not* moved.  This is consitent with existing
+        implementations and can be changed by a subclass very easily::
+
+            class movingodict(odict):
+                def __setitem__(self, key, value):
+                    self.pop(key, None)
+                    odict.__setitem__(self, key, value)
+
+        Moving keys to the end of a ordered dict on reassignment is not
+        very useful for most applications.
+
+    Does it mean the dict keys are sorted by a sort expression?
+
+        That's not the case.  The odict only guarantees that there is an order
+        and that newly inserted keys are inserted at the end of the dict.  If
+        you want to sort it you can do so, but newly added keys are again added
+        at the end of the dict.
+
+    I initializes the odict with a dict literal but the keys are not
+    ordered like they should!
+
+        Dict literals in Python generate dict objects and as such the order of
+        their items is not guaranteed.  Before they are passed to the odict
+        constructor they are already unordered.
+
+    What happens if keys appear multiple times in the list passed to the
+    constructor?
+
+        The same as for the dict.  The latter item overrides the former.  This
+        has the side-effect that the position of the first key is used because
+        the key is actually overwritten:
+
+        >>> odict([('a', 1), ('b', 2), ('a', 3)])
+        odict.odict([('a', 3), ('b', 2)])
+
+        This behavor is consistent with existing implementation in Python
+        and the PHP array and the hashmap in Ruby 1.9.
+
+    This odict doesn't scale!
+
+        Yes it doesn't.  The delitem operation is O(n).  This is file is a
+        mockup of a real odict that could be implemented for collections
+        based on an linked list.
+
+    Why is there no .insert()?
+
+        There are few situations where you really want to insert a key at
+        an specified index.  To now make the API too complex the proposed
+        solution for this situation is creating a list of items, manipulating
+        that and converting it back into an odict:
+
+        >>> d = odict([('a', 42), ('b', 23), ('c', 19)])
+        >>> l = d.items()
+        >>> l.insert(1, ('x', 0))
+        >>> odict(l)
+        odict.odict([('a', 42), ('x', 0), ('b', 23), ('c', 19)])
+"""
+from itertools import izip, imap
+from copy import deepcopy
+
+missing = object()
+
+
+class odict(dict):
+    """
+    Ordered dict example implementation.
+
+    This is the proposed interface for a an ordered dict as proposed on the
+    Python mailinglist (proposal_).
+
+    It's a dict subclass and provides some list functions.  The implementation
+    of this class is inspired by the implementation of Babel but incorporates
+    some ideas from the `ordereddict`_ and Django's ordered dict.
+
+    The constructor and `update()` both accept iterables of tuples as well as
+    mappings:
+
+    >>> d = odict([('a', 'b'), ('c', 'd')])
+    >>> d.update({'foo': 'bar'})
+    >>> d
+    odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
+
+    Keep in mind that when updating from dict-literals the order is not
+    preserved as these dicts are unsorted!
+
+    You can copy an odict like a dict by using the constructor, `copy.copy`
+    or the `copy` method and make deep copies with `copy.deepcopy`:
+
+    >>> from copy import copy, deepcopy
+    >>> copy(d)
+    odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
+    >>> d.copy()
+    odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
+    >>> odict(d)
+    odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
+    >>> d['spam'] = []
+    >>> d2 = deepcopy(d)
+    >>> d2['spam'].append('eggs')
+    >>> d
+    odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])])
+    >>> d2
+    odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', ['eggs'])])
+
+    All iteration methods as well as `keys`, `values` and `items` return
+    the values ordered by the the time the key-value pair is inserted:
+
+    >>> d.keys()
+    ['a', 'c', 'foo', 'spam']
+    >>> d.values()
+    ['b', 'd', 'bar', []]
+    >>> d.items()
+    [('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])]
+    >>> list(d.iterkeys())
+    ['a', 'c', 'foo', 'spam']
+    >>> list(d.itervalues())
+    ['b', 'd', 'bar', []]
+    >>> list(d.iteritems())
+    [('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])]
+
+    Index based lookup is supported too by `byindex` which returns the
+    key/value pair for an index:
+
+    >>> d.byindex(2)
+    ('foo', 'bar')
+
+    You can reverse the odict as well:
+
+    >>> d.reverse()
+    >>> d
+    odict.odict([('spam', []), ('foo', 'bar'), ('c', 'd'), ('a', 'b')])
+
+    And sort it like a list:
+
+    >>> d.sort(key=lambda x: x[0].lower())
+    >>> d
+    odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])])
+
+    .. _proposal: http://thread.gmane.org/gmane.comp.python.devel/95316
+    .. _ordereddict: http://www.xs4all.nl/~anthon/Python/ordereddict/
+    """
+
+    def __init__(self, *args, **kwargs):
+        dict.__init__(self)
+        self._keys = []
+        self.update(*args, **kwargs)
+
+    def __delitem__(self, key):
+        dict.__delitem__(self, key)
+        self._keys.remove(key)
+
+    def __setitem__(self, key, item):
+        if key not in self:
+            self._keys.append(key)
+        dict.__setitem__(self, key, item)
+
+    def __deepcopy__(self, memo=None):
+        if memo is None:
+            memo = {}
+        d = memo.get(id(self), missing)
+        if d is not missing:
+            return d
+        memo[id(self)] = d = self.__class__()
+        dict.__init__(d, deepcopy(self.items(), memo))
+        d._keys = self._keys[:]
+        return d
+
+    def __getstate__(self):
+        return {'items': dict(self), 'keys': self._keys}
+
+    def __setstate__(self, d):
+        self._keys = d['keys']
+        dict.update(d['items'])
+
+    def __reversed__(self):
+        return reversed(self._keys)
+
+    def __eq__(self, other):
+        if isinstance(other, odict):
+            if not dict.__eq__(self, other):
+                return False
+            return self.items() == other.items()
+        return dict.__eq__(self, other)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __cmp__(self, other):
+        if isinstance(other, odict):
+            return cmp(self.items(), other.items())
+        elif isinstance(other, dict):
+            return dict.__cmp__(self, other)
+        return NotImplemented
+
+    @classmethod
+    def fromkeys(cls, iterable, default=None):
+        return cls((key, default) for key in iterable)
+
+    def clear(self):
+        del self._keys[:]
+        dict.clear(self)
+
+    def copy(self):
+        return self.__class__(self)
+
+    def items(self):
+        return zip(self._keys, self.values())
+
+    def iteritems(self):
+        return izip(self._keys, self.itervalues())
+
+    def keys(self):
+        return self._keys[:]
+
+    def iterkeys(self):
+        return iter(self._keys)
+
+    def pop(self, key, default=missing):
+        if default is missing:
+            return dict.pop(self, key)
+        elif key not in self:
+            return default
+        self._keys.remove(key)
+        return dict.pop(self, key, default)
+
+    def popitem(self, key):
+        self._keys.remove(key)
+        return dict.popitem(key)
+
+    def setdefault(self, key, default=None):
+        if key not in self:
+            self._keys.append(key)
+        dict.setdefault(self, key, default)
+
+    def update(self, *args, **kwargs):
+        sources = []
+        if len(args) == 1:
+            if hasattr(args[0], 'iteritems'):
+                sources.append(args[0].iteritems())
+            else:
+                sources.append(iter(args[0]))
+        elif args:
+            raise TypeError('expected at most one positional argument')
+        if kwargs:
+            sources.append(kwargs.iteritems())
+        for iterable in sources:
+            for key, val in iterable:
+                self[key] = val
+
+    def values(self):
+        return map(self.get, self._keys)
+
+    def itervalues(self):
+        return imap(self.get, self._keys)
+
+    def index(self, item):
+        return self._keys.index(item)
+
+    def byindex(self, item):
+        key = self._keys[item]
+        return (key, dict.__getitem__(self, key))
+
+    def reverse(self):
+        self._keys.reverse()
+
+    def sort(self, *args, **kwargs):
+        self._keys.sort(*args, **kwargs)
+
+    def __repr__(self):
+        return 'odict.odict(%r)' % self.items()
+
+    __copy__ = copy
+    __iter__ = iterkeys
+
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.