Skip to content

Commit

Permalink
Add OnlyList type, make even 1 item RSS parse as a list
Browse files Browse the repository at this point in the history
  • Loading branch information
dhvcc committed Oct 3, 2023
1 parent 37e7e47 commit 99cb961
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 6 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "rss-parser"
version = "1.1.0"
version = "1.1.1"
description = "Typed pythonic RSS parser"
authors = ["dhvcc <[email protected]>"]
license = "GPL-3.0"
Expand Down
5 changes: 3 additions & 2 deletions rss_parser/models/channel.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Optional
from typing import Optional

from pydantic import Field

Expand All @@ -7,6 +7,7 @@
from rss_parser.models.item import Item
from rss_parser.models.text_input import TextInput
from rss_parser.models.types.date import DateTimeOrStr
from rss_parser.models.types.only_list import OnlyList
from rss_parser.models.types.tag import Tag


Expand All @@ -26,7 +27,7 @@ class RequiredChannelElementsMixin(XMLBaseModel):
class OptionalChannelElementsMixin(XMLBaseModel):
"""https://www.rssboard.org/rss-specification#optionalChannelElements."""

items: Optional[List[Tag[Item]]] = Field(alias="item", default=[])
items: Optional[OnlyList[Tag[Item]]] = Field(alias="item", default=[])

language: Optional[Tag[str]] = None # en-us
"The language the channel is written in. This allows aggregators to group all Italian language sites, " "for example, on a single page." # noqa
Expand Down
19 changes: 19 additions & 0 deletions rss_parser/models/types/only_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Union

from pydantic.validators import list_validator


class OnlyList(list):
@classmethod
def __get_validators__(cls):
yield cls.validate
yield list_validator

@classmethod
def validate(cls, v: Union[dict, list]):
if isinstance(v, dict):
return [v]
return v

def __repr__(self):
return f"OnlyList({super().__repr__()})"
133 changes: 133 additions & 0 deletions tests/samples/rss_2_with_1_item.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{
"channel": {
"attributes": {},
"content": {
"category": {
"attributes": {
"domain": "www.dmoz.com"
},
"content": "Computers/Software/Internet/Site Management/Content Management"
},
"cloud": null,
"copyright": {
"attributes": {},
"content": "Copyright 2004 NotePage, Inc."
},
"description": {
"attributes": {},
"content": "RSS is a fascinating technology. The uses for RSS are expanding daily. Take a closer look at how various industries are using the benefits of RSS in their businesses."
},
"docs": {
"attributes": {},
"content": "http://blogs.law.harvard.edu/tech/rss"
},
"generator": {
"attributes": {},
"content": "FeedForAll Beta1 (0.0.1.8)"
},
"image": {
"attributes": {},
"content": {
"description": {
"attributes": {},
"content": "FeedForAll Sample Feed"
},
"height": {
"attributes": {},
"content": 48
},
"link": {
"attributes": {},
"content": "http://www.feedforall.com/industry-solutions.htm"
},
"title": {
"attributes": {},
"content": "FeedForAll Sample Feed"
},
"url": {
"attributes": {},
"content": "http://www.feedforall.com/ffalogo48x48.gif"
},
"width": {
"attributes": {},
"content": 48
}
}
},
"items": [
{
"attributes": {},
"content": {
"author": null,
"category": {
"attributes": {
"domain": "www.dmoz.com"
},
"content": "Computers/Software/Internet/Site Management/Content Management"
},
"comments": {
"attributes": {},
"content": "http://www.feedforall.com/forum"
},
"description": {
"attributes": {},
"content": "<b>FeedForAll </b>helps Restaurant's communicate with customers. Let your customers know the latest specials or events.<br>\n<br>\nRSS feed uses include:<br>\n<i><font color=\"#FF0000\">Daily Specials <br>\nEntertainment <br>\nCalendar of Events </i></font>"
},
"enclosure": null,
"guid": null,
"link": {
"attributes": {},
"content": "http://www.feedforall.com/restaurant.htm"
},
"pub_date": {
"attributes": {},
"content": "Tue, 19 Oct 2004 11:09:11 -0400"
},
"source": null,
"title": {
"attributes": {},
"content": "RSS Solutions for Restaurants"
}
}
}
],
"language": {
"attributes": {},
"content": "en-us"
},
"last_build_date": {
"attributes": {},
"content": "2004-10-19T13:39:14-04:00"
},
"link": {
"attributes": {},
"content": "http://www.feedforall.com/industry-solutions.htm"
},
"managing_editor": {
"attributes": {},
"content": "[email protected]"
},
"pub_date": {
"attributes": {},
"content": "2004-10-19T13:38:55-04:00"
},
"rating": null,
"skip_days": null,
"skip_hours": null,
"text_input": null,
"title": {
"attributes": {},
"content": "FeedForAll Sample Feed"
},
"ttl": null,
"web_master": {
"attributes": {},
"content": "[email protected]"
}
}
},
"version": {
"attributes": {},
"content": "2.0"
}
}
38 changes: 38 additions & 0 deletions tests/samples/rss_2_with_1_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="windows-1252"?>
<rss version="2.0">
<channel>
<title>FeedForAll Sample Feed</title>
<description>RSS is a fascinating technology. The uses for RSS are expanding daily. Take a closer look at how various industries are using the benefits of RSS in their businesses.</description>
<link>http://www.feedforall.com/industry-solutions.htm</link>
<category domain="www.dmoz.com">Computers/Software/Internet/Site Management/Content Management</category>
<copyright>Copyright 2004 NotePage, Inc.</copyright>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<language>en-us</language>
<lastBuildDate>Tue, 19 Oct 2004 13:39:14 -0400</lastBuildDate>
<managingEditor>[email protected]</managingEditor>
<pubDate>Tue, 19 Oct 2004 13:38:55 -0400</pubDate>
<webMaster>[email protected]</webMaster>
<generator>FeedForAll Beta1 (0.0.1.8)</generator>
<image>
<url>http://www.feedforall.com/ffalogo48x48.gif</url>
<title>FeedForAll Sample Feed</title>
<link>http://www.feedforall.com/industry-solutions.htm</link>
<description>FeedForAll Sample Feed</description>
<width>48</width>
<height>48</height>
</image>
<item>
<title>RSS Solutions for Restaurants</title>
<description>&lt;b&gt;FeedForAll &lt;/b&gt;helps Restaurant&apos;s communicate with customers. Let your customers know the latest specials or events.&lt;br&gt;
&lt;br&gt;
RSS feed uses include:&lt;br&gt;
&lt;i&gt;&lt;font color=&quot;#FF0000&quot;&gt;Daily Specials &lt;br&gt;
Entertainment &lt;br&gt;
Calendar of Events &lt;/i&gt;&lt;/font&gt;</description>
<link>http://www.feedforall.com/restaurant.htm</link>
<category domain="www.dmoz.com">Computers/Software/Internet/Site Management/Content Management</category>
<comments>http://www.feedforall.com/forum</comments>
<pubDate>Tue, 19 Oct 2004 11:09:11 -0400</pubDate>
</item>
</channel>
</rss>
9 changes: 6 additions & 3 deletions tests/test_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
from rss_parser import Parser


@pytest.mark.parametrize("sample_and_result", [["rss_2"], ["rss_2_no_category_attr"], ["apology_line"]], indirect=True)
def test_parses_rss_version_2(sample_and_result):
# Expect basic RSSv2 to be parsed
@pytest.mark.parametrize(
"sample_and_result",
[["rss_2"], ["rss_2_no_category_attr"], ["apology_line"], ["rss_2_with_1_item"]],
indirect=True,
)
def test_parses_all_samples(sample_and_result):
sample, result = sample_and_result
rss = Parser.parse(sample)

Expand Down

0 comments on commit 99cb961

Please sign in to comment.