From 942d8d77826e53d65573a951b7dbf5a9190b7b58 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Tue, 20 Aug 2019 13:47:23 +0300 Subject: [PATCH] refac: split on other files --- genrss/__init__.py | 127 +++-------------------------------------- genrss/enclosure.py | 32 +++++++++++ genrss/item.py | 78 +++++++++++++++++++++++++ genrss/utils.py | 30 ++++++++++ tests/test_rss_item.py | 13 +++-- 5 files changed, 155 insertions(+), 125 deletions(-) create mode 100644 genrss/enclosure.py create mode 100644 genrss/item.py create mode 100644 genrss/utils.py diff --git a/genrss/__init__.py b/genrss/__init__.py index 7ebff80..0aaa610 100644 --- a/genrss/__init__.py +++ b/genrss/__init__.py @@ -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 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') -ElementT = TypeVar('ElementT') + 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: """Generates RSS feed of channel. @@ -240,5 +129,5 @@ class GenRSS: root = self.to_element() return tostring(root, pretty_print=pretty, xml_declaration=True, - encoding='UTF-8').\ + encoding='UTF-8'). \ decode('utf-8') diff --git a/genrss/enclosure.py b/genrss/enclosure.py new file mode 100644 index 0000000..cdb698d --- /dev/null +++ b/genrss/enclosure.py @@ -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')) + + diff --git a/genrss/item.py b/genrss/item.py new file mode 100644 index 0000000..8c8820d --- /dev/null +++ b/genrss/item.py @@ -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 diff --git a/genrss/utils.py b/genrss/utils.py new file mode 100644 index 0000000..a28daaf --- /dev/null +++ b/genrss/utils.py @@ -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 diff --git a/tests/test_rss_item.py b/tests/test_rss_item.py index 6a8fd12..28fca2a 100644 --- a/tests/test_rss_item.py +++ b/tests/test_rss_item.py @@ -92,15 +92,16 @@ def test_item_enclosure(feed): create_item(feed, enclosure=enclosure) xml = feed.xml() assert xml - assert '' \ - ''.format(0, 'image/jpeg', enclosure.url) in xml + assert '' \ + ''.format(length=0, type='image/jpeg', + url=enclosure.url) in xml def test_item_enclosure_from_dict(feed, enclosure_dict): create_item(feed, enclosure=enclosure_dict) xml = feed.xml() assert xml - assert '' \ - ''.format(enclosure_dict.get('size', 0), - enclosure_dict.get('type', 'image/jpeg'), - enclosure_dict.get('url')) in xml + assert '' \ + ''.format(length=enclosure_dict.get('size', 0), + type=enclosure_dict.get('type', 'image/jpeg'), + url=enclosure_dict.get('url')) in xml