feeds: remove FeedList(Descriptor)
parent
081d560bc4
commit
8487a43c6c
160
morss/feeds.py
160
morss/feeds.py
|
@ -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 ""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue