refac: split on other files

This commit is contained in:
Dmitriy Pleshevskiy 2019-08-20 13:47:23 +03:00
parent 8109f03309
commit 942d8d7782
5 changed files with 155 additions and 125 deletions

View file

@ -1,129 +1,18 @@
import mimetypes
from lxml.etree import Element, CDATA, tostring
from typing import Optional, List, TypeVar, Dict, Any, Union
from datetime import datetime from datetime import datetime
from typing import Optional, List, Dict
import pytz from lxml.etree import Element, CDATA, tostring
from .utils import create_element, ElementT
from .item import Item
from .enclosure import Enclosure
__all__ = ('GenRSS', 'Item', 'Enclosure') __all__ = ('GenRSS', 'Item', 'Enclosure')
ElementT = TypeVar('ElementT')
RSS_DEFAULT_GENERATOR = f'Generated by genrss for python' RSS_DEFAULT_GENERATOR = f'Generated by genrss for python'
def create_element(name: str, text: Any = None, children: List[ElementT] = None,
**kwargs) -> ElementT:
"""Creates xml node with text or children elements.
:param name: Tag name of node with namespace
:param text: Text of node
:param children: Appends elements as child nodes
"""
el = Element(name, **kwargs)
if text:
if isinstance(text, datetime):
text = text.replace(tzinfo=pytz.timezone('GMT')). \
strftime("%a, %d %b %Y %H:%M:%S %Z")
el.text = text
elif isinstance(children, (list, tuple)):
for child in children:
el.append(child)
return el
class Enclosure:
"""Data for enclosure tag for rss.
:param url: Absolute url to file
:param size: File size
:param type: File mime type
"""
def __init__(self, url: str, size=None, type=None):
self.url = url
self.size = size or 0
self.type = type or mimetypes.guess_type(self.url)[0]
def to_element(self) -> ElementT:
"""Returns item element for xml."""
return create_element('enclosure', url=self.url, length=str(self.size),
type=self.type)
@staticmethod
def from_dict(data: Dict[str, Union[str, int]]):
"""Makes enclosure data from dict."""
return Enclosure(data.get('url'), data.get('size'), data.get('type'))
class Item:
"""Data for item tag for rss.
:param title: Title of this particular item
:param description: Content for the item. Can contain html but
link and image urls must be absolute path including hostname
:param url: Url to the item. This could be a blog entry
:param guid: A unique string feed readers use to know if an item
is new or has already been seen. If you use a guid never change
it. If you don't provide a guid then your item urls must be unique
:param author: If included it is the name of the item's creator.
If not provided the item author will be the same as the feed
author. This is typical except on multi-author blogs
:param categories: If provided, each array item will be added as a
category element
:param enclosure: An enclosure object
:param pub_date: The date and time of when the item was created.
Feed readers use this to determine the sort order. Some readers
will also use it to determine if the content should be presented
as unread
"""
def __init__(self, title: str, **kwargs):
self.title: str = title
self.description: str = kwargs.pop('description', '')
self.url: Optional[str] = kwargs.pop('url', None)
self.guid: Optional[str] = kwargs.pop('guid', None)
self.author: Optional[str] = kwargs.pop('author', None)
self.categories: List[str] = kwargs.pop('categories', [])
self.enclosure: Optional[Enclosure] = kwargs.pop('enclosure', None)
self.pub_date: Optional[datetime] = kwargs.pop('pub_date', None)
if isinstance(self.enclosure, dict):
self.enclosure = Enclosure.from_dict(self.enclosure)
def to_element(self) -> ElementT:
"""Returns item element for xml."""
item = create_element('item', children=[
create_element('title', CDATA(self.title)),
create_element('description', CDATA(self.description)),
])
if self.url:
item.append(create_element('link', self.url))
item.append(create_element(
'guid',
attrib={
'isPermaLink': str(bool(not self.guid and self.url)).lower()
},
text=(self.guid or self.url or CDATA(self.title))
))
if self.author:
item.append(create_element(
'{http://purl.org/dc/elements/1.1/}creator',
CDATA(self.author)
))
for category in self.categories:
item.append(create_element('category', CDATA(category)))
if self.enclosure:
item.append(self.enclosure.to_element())
if self.pub_date:
item.append(create_element('pubDate', self.pub_date))
return item
class GenRSS: class GenRSS:
"""Generates RSS feed of channel. """Generates RSS feed of channel.

32
genrss/enclosure.py Normal file
View file

@ -0,0 +1,32 @@
import mimetypes
from typing import Dict, Union
from .utils import ElementT, create_element
__all__ = ('Enclosure',)
class Enclosure:
"""Data for enclosure tag for rss.
:param url: Absolute url to file
:param size: File size
:param type: File mime type
"""
def __init__(self, url: str, size=None, type=None):
self.url = url
self.size = size or 0
self.type = type or mimetypes.guess_type(self.url)[0]
def to_element(self) -> ElementT:
"""Returns item element for xml."""
return create_element('enclosure', url=self.url, length=str(self.size),
type=self.type)
@staticmethod
def from_dict(data: Dict[str, Union[str, int]]):
"""Makes enclosure data from dict."""
return Enclosure(data.get('url'), data.get('size'), data.get('type'))

78
genrss/item.py Normal file
View file

@ -0,0 +1,78 @@
from datetime import datetime
from typing import Optional, List
from lxml.etree import CDATA
from .utils import ElementT, create_element
from .enclosure import Enclosure
__all__ = ('Item',)
class Item:
"""Data for item tag for rss.
:param title: Title of this particular item
:param description: Content for the item. Can contain html but
link and image urls must be absolute path including hostname
:param url: Url to the item. This could be a blog entry
:param guid: A unique string feed readers use to know if an item
is new or has already been seen. If you use a guid never change
it. If you don't provide a guid then your item urls must be unique
:param author: If included it is the name of the item's creator.
If not provided the item author will be the same as the feed
author. This is typical except on multi-author blogs
:param categories: If provided, each array item will be added as a
category element
:param enclosure: An enclosure object
:param pub_date: The date and time of when the item was created.
Feed readers use this to determine the sort order. Some readers
will also use it to determine if the content should be presented
as unread
"""
def __init__(self, title: str, **kwargs):
self.title: str = title
self.description: str = kwargs.pop('description', '')
self.url: Optional[str] = kwargs.pop('url', None)
self.guid: Optional[str] = kwargs.pop('guid', None)
self.author: Optional[str] = kwargs.pop('author', None)
self.categories: List[str] = kwargs.pop('categories', [])
self.enclosure: Optional[Enclosure] = kwargs.pop('enclosure', None)
self.pub_date: Optional[datetime] = kwargs.pop('pub_date', None)
if isinstance(self.enclosure, dict):
self.enclosure = Enclosure.from_dict(self.enclosure)
def to_element(self) -> ElementT:
"""Returns item element for xml."""
item = create_element('item', children=[
create_element('title', CDATA(self.title)),
create_element('description', CDATA(self.description)),
])
if self.url:
item.append(create_element('link', self.url))
item.append(create_element(
'guid',
attrib={
'isPermaLink': str(bool(not self.guid and self.url)).lower()
},
text=(self.guid or self.url or CDATA(self.title))
))
if self.author:
item.append(create_element(
'{http://purl.org/dc/elements/1.1/}creator',
CDATA(self.author)
))
for category in self.categories:
item.append(create_element('category', CDATA(category)))
if self.enclosure:
item.append(self.enclosure.to_element())
if self.pub_date:
item.append(create_element('pubDate', self.pub_date))
return item

30
genrss/utils.py Normal file
View file

@ -0,0 +1,30 @@
from datetime import datetime
from typing import TypeVar, Any, List
import pytz
from lxml.etree import Element
__all__ = ('ElementT', 'create_element',)
ElementT = TypeVar('ElementT')
def create_element(name: str, text: Any = None, children: List[ElementT] = None,
**kwargs) -> ElementT:
"""Creates xml node with text or children elements.
:param name: Tag name of node with namespace
:param text: Text of node
:param children: Appends elements as child nodes
"""
el = Element(name, **kwargs)
if text:
if isinstance(text, datetime):
text = text.replace(tzinfo=pytz.timezone('GMT')). \
strftime("%a, %d %b %Y %H:%M:%S %Z")
el.text = text
elif isinstance(children, (list, tuple)):
for child in children:
el.append(child)
return el

View file

@ -92,15 +92,16 @@ def test_item_enclosure(feed):
create_item(feed, enclosure=enclosure) create_item(feed, enclosure=enclosure)
xml = feed.xml() xml = feed.xml()
assert xml assert xml
assert '<enclosure length="{}" type="{}" url="{}"/>' \ assert '<enclosure url="{url}" length="{length}" type="{type}"/>' \
'</item>'.format(0, 'image/jpeg', enclosure.url) in xml '</item>'.format(length=0, type='image/jpeg',
url=enclosure.url) in xml
def test_item_enclosure_from_dict(feed, enclosure_dict): def test_item_enclosure_from_dict(feed, enclosure_dict):
create_item(feed, enclosure=enclosure_dict) create_item(feed, enclosure=enclosure_dict)
xml = feed.xml() xml = feed.xml()
assert xml assert xml
assert '<enclosure length="{}" type="{}" url="{}"/>' \ assert '<enclosure url="{url}" length="{length}" type="{type}"/>' \
'</item>'.format(enclosure_dict.get('size', 0), '</item>'.format(length=enclosure_dict.get('size', 0),
enclosure_dict.get('type', 'image/jpeg'), type=enclosure_dict.get('type', 'image/jpeg'),
enclosure_dict.get('url')) in xml url=enclosure_dict.get('url')) in xml