From fae4470101a762cd69c68698037e525f45ca275c Mon Sep 17 00:00:00 2001 From: Laurent Bachelier Date: Tue, 13 Mar 2012 13:25:29 +0100 Subject: [PATCH] Prepare for objects that are also Collections Don't force objects to have an id, especially since that id was incorrect. "plap/plop" would have the same id as "plop", i.e. "plop". We don't rely on the id for handling Collections anymore. Change to use more unique property names. Change the display, instead of using '*', we use '~', and there is no "Collection" header anymore. Updated formatters could use that way of showing the object is also a collection too. refs #774 --- modules/canalplus/browser.py | 4 +- modules/redmine/backend.py | 4 +- weboob/capabilities/collection.py | 74 ++++++++++++++++++------------- weboob/tools/application/repl.py | 27 ++++++----- 4 files changed, 61 insertions(+), 48 deletions(-) diff --git a/modules/canalplus/browser.py b/modules/canalplus/browser.py index 48f5c12d..b77c2e67 100644 --- a/modules/canalplus/browser.py +++ b/modules/canalplus/browser.py @@ -82,11 +82,11 @@ class CanalplusBrowser(BaseBrowser): if len(split_path) == 0: for channel in channels: - if channel.level == 1: + if channel.path_level == 1: yield channel elif len(split_path) == 1: for channel in channels: - if channel.level == 2 and split_path == channel.parent: + if channel.path_level == 2 and split_path == channel.parent_path: yield channel elif len(split_path) == 2: subchannels = self.iter_resources(split_path[0:1]) diff --git a/modules/redmine/backend.py b/modules/redmine/backend.py index ce9a84dc..aec26b95 100644 --- a/modules/redmine/backend.py +++ b/modules/redmine/backend.py @@ -106,9 +106,9 @@ class RedmineBackend(BaseBackend, ICapContent, ICapBugTracker, ICapCollection): return self.iter_issues(query) def validate_collection(self, objs, collection): - if collection.level == 0: + if collection.path_level == 0: return - if Issue in objs and collection.level == 1: + if Issue in objs and collection.path_level == 1: for project in self.iter_projects(): if collection.basename == project.id: return Collection([project.id], project.name) diff --git a/weboob/capabilities/collection.py b/weboob/capabilities/collection.py index 6ef8c112..51cfe5e0 100644 --- a/weboob/capabilities/collection.py +++ b/weboob/capabilities/collection.py @@ -19,7 +19,7 @@ from .base import IBaseCap, CapBaseObject -__all__ = ['ICapCollection', 'Collection', 'CollectionNotFound'] +__all__ = ['ICapCollection', 'BaseCollection', 'Collection', 'CollectionNotFound'] class CollectionNotFound(Exception): @@ -31,39 +31,49 @@ class CollectionNotFound(Exception): Exception.__init__(self, msg) -class Collection(CapBaseObject): +class BaseCollection(CapBaseObject): + """ + Inherit from this if you want to create an object that is *also* a Collection. + However, this probably will not work properly for now. + """ + def __init__(self, split_path): + self.split_path = split_path + + @property + def basename(self): + return self.split_path[-1] if self.path_level else None + + @property + def parent_path(self): + return self.split_path[0:-1] if self.path_level else None + + @property + def path_level(self): + return len(self.split_path) + + +class Collection(BaseCollection): """ A Collection is a "fake" object returned in results, which shows you can get more results if you go into its path. It is a dumb object, it must not contain callbacks to a backend. + + Do not inherit from this class if you want to make a regular CapBaseObject + a Collection, use BaseCollection instead. """ - def __init__(self, split_path, title=None, backend=None): - self.split_path = split_path + def __init__(self, split_path, title=None): self.title = title - _id = self.basename - CapBaseObject.__init__(self, _id, backend) + BaseCollection.__init__(self, split_path) def __unicode__(self): - if self.title and self.id: - return u'%s (%s)' % (self.id, self.title) - elif self.id: - return u'%s' % self.id + if self.title and self.basename: + return u'%s (%s)' % (self.basename, self.title) + elif self.basename: + return u'%s' % self.basename else: return u'Unknown collection' - @property - def basename(self): - return self.split_path[-1] if self.level else None - - @property - def parent(self): - return self.split_path[0:-1] if self.level else None - - @property - def level(self): - return len(self.split_path) - class ICapCollection(IBaseCap): def iter_resources_flat(self, objs, split_path, clean_only=False): @@ -93,7 +103,7 @@ class ICapCollection(IBaseCap): If the path is invalid (i.e. can't be handled by this module), it should return None. """ - collection = Collection(split_path, None, self.name) + collection = Collection(split_path, None) return self.validate_collection(objs, collection) or collection def validate_collection(self, objs, collection): @@ -105,7 +115,7 @@ class ICapCollection(IBaseCap): You can replace the collection object entirely by returning a new one. """ # Root - if collection.level == 0: + if collection.path_level == 0: return try: i = self.iter_resources(objs, collection.split_path) @@ -121,20 +131,20 @@ class ICapCollection(IBaseCap): def test(): c = Collection([]) assert c.basename is None - assert c.parent is None - assert c.level == 0 + assert c.parent_path is None + assert c.path_level == 0 c = Collection([u'lol']) assert c.basename == u'lol' - assert c.parent == [] - assert c.level == 1 + assert c.parent_path == [] + assert c.path_level == 1 c = Collection([u'lol', u'cat']) assert c.basename == u'cat' - assert c.parent == [u'lol'] - assert c.level == 2 + assert c.parent_path == [u'lol'] + assert c.path_level == 2 c = Collection([u'w', u'e', u'e', u'b', u'o', u'o', u'b']) assert c.basename == u'b' - assert c.parent == [u'w', u'e', u'e', u'b', u'o', u'o'] - assert c.level == 7 + assert c.parent_path == [u'w', u'e', u'e', u'b', u'o', u'o'] + assert c.path_level == 7 diff --git a/weboob/tools/application/repl.py b/weboob/tools/application/repl.py index d89185d8..1f6a8bab 100644 --- a/weboob/tools/application/repl.py +++ b/weboob/tools/application/repl.py @@ -32,7 +32,7 @@ from weboob.tools.application.formatters.iformatter import MandatoryFieldsNotFou from weboob.tools.misc import to_unicode from weboob.tools.path import WorkingPath from weboob.tools.ordereddict import OrderedDict -from weboob.capabilities.collection import Collection, ICapCollection, CollectionNotFound +from weboob.capabilities.collection import Collection, BaseCollection, ICapCollection, CollectionNotFound from .console import BackendNotGiven, ConsoleApplication from .formatters.load import FormattersLoader, FormatterLoadError @@ -864,19 +864,13 @@ class ReplApplication(Cmd, ConsoleApplication): print obj if self.collections: - print - print 'Collections:' for collection in self.collections: if collection.basename and collection.title: - print u'%s* (%s) %s (%s)%s' % \ + print u'%s~ (%s) %s (%s)%s' % \ (self.BOLD, collection.basename, collection.title, collection.backend, self.NC) - elif collection.basename: - print u'%s* (%s) (%s)%s' % \ - (self.BOLD, collection.basename, collection.backend, self.NC) else: - print collection - - self.flush() + print u'%s~ (%s) (%s)%s' % \ + (self.BOLD, collection.basename, collection.backend, self.NC) def do_cd(self, line): """ @@ -938,6 +932,13 @@ class ReplApplication(Cmd, ConsoleApplication): return (objects, collections) + def all_collections(self): + """ + Get all objects that are collections: regular objects and fake dumb objects. + """ + obj_collections = [obj for obj in self.objects if isinstance(obj, BaseCollection)] + return obj_collections + self.collections + def complete_cd(self, text, line, begidx, endidx): directories = set() if len(self.working_path.get()): @@ -945,7 +946,8 @@ class ReplApplication(Cmd, ConsoleApplication): mline = line.partition(' ')[2] offs = len(mline) - len(text) - if len(self.collections) == 0: + # refresh only if needed + if len(self.objects) == 0 and len(self.collections) == 0: try: self.objects, self.collections = self._fetch_objects(objs=self.COLLECTION_OBJECTS) except CallErrors, errors: @@ -955,7 +957,8 @@ class ReplApplication(Cmd, ConsoleApplication): else: self.bcall_error_handler(backend, error, backtrace) - for collection in self.collections: + collections = self.all_collections() + for collection in collections: directories.add(collection.basename.encode(sys.stdout.encoding or locale.getpreferredencoding())) return [s[offs:] for s in directories if s.startswith(mline)]