feeds: remove FeedList(Descriptor)

master
pictuga 2018-10-31 22:07:16 +01:00
parent 081d560bc4
commit 8487a43c6c
1 changed files with 55 additions and 105 deletions

View File

@ -110,21 +110,12 @@ class FeedBase(object):
selection and item creation selection and item creation
""" """
def __getitem__(self, item): def iterdic(self):
return getattr(self, item)
def __setitem__(self, item, value):
setattr(self, item, value)
def __delitem__(self, item):
delattr(self, item)
def __iter__(self):
for element in self.dic: for element in self.dic:
value = self[element] value = getattr(self, element)
if isinstance(value, FeedList): if element == 'items':
value = [OrderedDict(x) for x in value] value = [OrderedDict(x.iterdic()) for x in value]
elif isinstance(value, datetime): elif isinstance(value, datetime):
value = value.isoformat() value = value.isoformat()
@ -242,67 +233,6 @@ def parse_time(value):
return False return False
class FeedList(object):
"""
Class to map a list of xml elements against a list of matching objects,
while avoiding to recreate the same matching object over and over again. So
as to avoid extra confusion, list's elements are called "children" here, so
as not to use "items", which is already in use in RSS/Atom related code.
Comes with its very own descriptor.
"""
def __init__(self, parent, getter, tag, child_class):
self.parent = parent
self.getter = getter
self.childClass = child_class
self.tag = tag
self._children = {} # id(xml) => FeedItem
def get_children(self):
children = self.getter()
out = []
for child in children:
if id(child) in self._children:
out.append(self._children[id(child)])
else:
new = self.childClass(child, self.tag)
self._children[id(child)] = new
out.append(new)
return out
def append(self, cousin=None):
new = self.childClass(tag=self.tag)
self.parent.root.append(new.xml)
self._children[id(new.xml)] = new
if cousin is None:
return new
for key in self.childClass.__dict__:
if key.startswith('set_'):
attr = key[4:]
if hasattr(cousin, attr):
setattr(new, attr, getattr(cousin, attr))
elif attr in cousin:
setattr(new, attr, cousin[attr])
return new
def __getitem__(self, key):
return self.get_children()[key]
def __delitem__(self, key):
child = self.getter()[key]
if id(child) in self._children:
self._children[id(child)].remove()
del self._children[id(child)]
else:
child.getparent().remove(child)
def __len__(self):
return len(self.getter())
class Uniq(object): class Uniq(object):
_map = {} _map = {}
_id = None _id = None
@ -322,30 +252,6 @@ class Uniq(object):
cls._map[obj._id] = obj cls._map[obj._id] = obj
return obj return obj
class FeedListDescriptor(object):
"""
Descriptor for FeedList
"""
def __init__(self, name):
self.name = name
self.items = {} # id(instance) => FeedList
def __get__(self, instance, owner=None):
key = id(instance)
if key in self.items:
return self.items[key]
else:
getter = getattr(instance, 'get_%s' % self.name)
class_name = globals()[getattr(instance, '%sClass' % self.name)]
self.items[key] = FeedList(instance, getter, instance.tag, class_name)
return self.items[key]
def __set__(self, instance, value):
feedlist = self.__get__(instance)
[x.remove() for x in list(feedlist)]
[feedlist.append(x) for x in value]
class FeedParser(FeedBase): class FeedParser(FeedBase):
itemsClass = 'FeedItem' itemsClass = 'FeedItem'
@ -360,6 +266,8 @@ class FeedParser(FeedBase):
self.root = self.xml.xpath("//atom03:feed|//atom:feed|//channel|//rssfake:channel", namespaces=NSMAP)[0] self.root = self.xml.xpath("//atom03:feed|//atom:feed|//channel|//rssfake:channel", namespaces=NSMAP)[0]
self.tag = tag self.tag = tag
self.itemsClass = globals()[self.itemsClass]
def get_title(self): def get_title(self):
return "" return ""
@ -381,6 +289,9 @@ class FeedParser(FeedBase):
def get_items(self): def get_items(self):
return [] return []
def wrap_items(self, items):
return [self.itemsClass(x, self.tag) for x in items]
title = property( title = property(
lambda f: f.get_title(), lambda f: f.get_title(),
lambda f,x: f.set_title(x), lambda f,x: f.set_title(x),
@ -389,23 +300,52 @@ class FeedParser(FeedBase):
lambda f: f.get_desc(), lambda f: f.get_desc(),
lambda f,x: f.set_desc(x), lambda f,x: f.set_desc(x),
lambda f: f.del_desc() ) lambda f: f.del_desc() )
items = FeedListDescriptor('items') items = property(
lambda f: f )
def append(self, cousin=None):
new = self.itemsClass(tag=self.tag)
self.root.append(new.xml)
if cousin is None:
return new
for attr in self.itemsClass.dic:
if hasattr(cousin, attr):
setattr(new, attr, getattr(cousin, attr))
elif attr in cousin:
setattr(new, attr, cousin[attr])
return new
def __getitem__(self, key):
return self.wrap_items(self.get_items())[key]
def __delitem__(self, key):
self[key].remove()
def __len__(self):
return len(self.get_items())
def tostring(self, **k): def tostring(self, **k):
return etree.tostring(self.xml.getroottree(), **k) return etree.tostring(self.xml.getroottree(), **k)
def tojson(self, indent=None): def tojson(self, indent=None):
return json.dumps(OrderedDict(self), indent=indent) return json.dumps(OrderedDict(self.iterdic()), indent=indent)
def tocsv(self): def tocsv(self):
out = StringIO() out = StringIO()
c = csv.writer(out, dialect=csv.excel) c = csv.writer(out, dialect=csv.excel)
for item in self.items: for item in self.items:
if sys.version_info[0] >= 3: row = [getattr(item, x) for x in item.dic]
row = [x[1] for x in item]
else: if sys.version_info[0] < 3:
row = [x[1].encode('utf-8') if isinstance(x[1], unicode) else x[1] for x in item] row = [x.encode('utf-8') if isinstance(x, unicode) else x for x in row]
c.writerow(row) c.writerow(row)
out.seek(0) out.seek(0)
return out.read() return out.read()
@ -492,7 +432,7 @@ class FeedParserAtom(FeedParser):
return self.xpath('atom:entry|atom03:entry') return self.xpath('atom:entry|atom03:entry')
class FeedItem(FeedBase): class FeedItem(FeedBase, Uniq):
timeFormat = '' timeFormat = ''
dic = ('title', 'link', 'desc', 'content', 'id', 'is_permalink', 'time', 'updated') dic = ('title', 'link', 'desc', 'content', 'id', 'is_permalink', 'time', 'updated')
@ -500,9 +440,19 @@ class FeedItem(FeedBase):
if xml is None: if xml is None:
xml = Element(tag_NS(self.base[tag])) xml = Element(tag_NS(self.base[tag]))
self._id = FeedItem._gen_id(xml)
self.root = self.xml = xml self.root = self.xml = xml
self.tag = tag self.tag = tag
@classmethod
def _gen_id(cls, xml=None, *args, **kwargs):
if xml is not None:
return id(xml)
else:
return None
def get_title(self): def get_title(self):
return "" return ""