Skip to content

Commit f23926c

Browse files
authored
Merge pull request #9 from realpython/config-file
Release v0.2.0
2 parents 3ab9c12 + 769d382 commit f23926c

File tree

9 files changed

+62
-44
lines changed

9 files changed

+62
-44
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.1.1
2+
current_version = 0.2.0
33
commit = False
44
tag = False
55

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include reader/*.cfg

reader/__init__.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@
88
99
See https://github.com/realpython/reader/ for more information
1010
"""
11+
import importlib_resources as _resources
12+
try:
13+
from configparser import ConfigParser as _ConfigParser
14+
except ImportError: # Python 2
15+
from ConfigParser import ConfigParser as _ConfigParser
16+
17+
1118
# Version of realpython-reader package
12-
__version__ = "0.1.1"
19+
__version__ = "0.2.0"
1320

14-
# URL of Real Python feed
15-
URL = "https://realpython.com/atom.xml"
21+
# Read URL of feed from config file
22+
_cfg = _ConfigParser()
23+
with _resources.path("reader", "config.cfg") as _path:
24+
_cfg.read(str(_path))
25+
URL = _cfg.get("feed", "url")

reader/__main__.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,13 @@
4040
Version:
4141
--------
4242
43-
- realpython-reader v0.1.1
43+
- realpython-reader v0.2.0
4444
"""
4545
# Standard library imports
4646
import sys
4747

4848
# Reader imports
49+
import reader
4950
from reader import feed
5051
from reader import viewer
5152

@@ -63,16 +64,19 @@ def main(): # type: () -> None
6364
# Should links be shown in the text
6465
show_links = "-l" in opts or "--show-links" in opts
6566

67+
# Get URL from config file
68+
url = reader.URL
69+
6670
# An article ID is given, show article
6771
if args:
6872
for article_id in args:
69-
article = feed.get_article(article_id, show_links)
73+
article = feed.get_article(article_id, links=show_links, url=url)
7074
viewer.show(article)
7175

7276
# No ID is given, show list of articles
7377
else:
74-
site = feed.get_site()
75-
titles = feed.get_titles()
78+
site = feed.get_site(url=url)
79+
titles = feed.get_titles(url=url)
7680
viewer.show_list(site, titles)
7781

7882

reader/config.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[feed]
2+
url = https://realpython.com/atom.xml

reader/feed.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,23 @@
1212
_CACHED_FEEDS = dict() # type: Dict[str, feedparser.FeedParserDict]
1313

1414

15-
def _feed(): # type: () -> feedparser.FeedParserDict
15+
def _feed(url=URL): # type: (str) -> feedparser.FeedParserDict
1616
"""Cache contents of the feed, so it's only read once"""
17-
if URL not in _CACHED_FEEDS:
18-
_CACHED_FEEDS[URL] = feedparser.parse(URL)
19-
return _CACHED_FEEDS[URL]
17+
if url not in _CACHED_FEEDS:
18+
_CACHED_FEEDS[url] = feedparser.parse(url)
19+
return _CACHED_FEEDS[url]
2020

2121

22-
def get_site(): # type: () -> str
22+
def get_site(url=URL): # type: (str) -> str
2323
"""Get name and link to web site of the feed"""
24-
info = _feed().feed
25-
return "{info.title} ({info.link})".format(info=info)
24+
info = _feed(url).feed
25+
return u"{info.title} ({info.link})".format(info=info)
2626

2727

28-
def get_article(article_id, links=False): # type: (str, bool) -> str
28+
def get_article(article_id, links=False, url=URL):
29+
# type: (str, bool, str) -> str
2930
"""Get article from feed with the given ID"""
30-
articles = _feed().entries
31+
articles = _feed(url).entries
3132
try:
3233
article = articles[int(article_id)]
3334
except (IndexError, ValueError):
@@ -49,7 +50,7 @@ def get_article(article_id, links=False): # type: (str, bool) -> str
4950
return u"# {}\n\n{}".format(article.title, text)
5051

5152

52-
def get_titles(): # type: () -> List[str]
53+
def get_titles(url=URL): # type: (str) -> List[str]
5354
"""List titles in feed"""
54-
articles = _feed().entries
55+
articles = _feed(url).entries
5556
return [a.title for a in articles]

reader/viewer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ def show(article): # type: (str) -> None
1414

1515
def show_list(site, titles): # type: (str, List[str]) -> None
1616
"""Show list of articles"""
17-
print("The latest tutorials from {}".format(site))
17+
print(u"The latest tutorials from {}".format(site))
1818
for article_id, title in enumerate(titles):
19-
print("{:>3} {}".format(article_id, title))
19+
print(u"{:>3} {}".format(article_id, title))

setup.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# This call to setup() does all the work
1414
setup(
1515
name="realpython-reader",
16-
version="0.1.1",
16+
version="0.2.0",
1717
description="Read Real Python tutorials",
1818
long_description=README,
1919
long_description_content_type="text/markdown",
@@ -28,6 +28,10 @@
2828
"Programming Language :: Python :: 3",
2929
],
3030
packages=["reader"],
31-
install_requires=["feedparser", "html2text", "typing"],
31+
package_data={"reader": ["reader/config.cfg"]},
32+
include_package_data=True,
33+
install_requires=[
34+
"feedparser", "html2text", "importlib_resources", "typing"
35+
],
3236
entry_points={"console_scripts": ["realpython=reader.__main__:main"]},
3337
)

tests/test_feed.py

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,77 +13,73 @@
1313

1414

1515
@pytest.fixture
16-
def monkeypatch_feed(monkeypatch):
16+
def local_feed():
1717
"""Use local file instead of downloading feed from web"""
18-
local_path = os.path.join(HERE, "realpython_20180919.xml")
19-
monkeypatch.setattr(feed, "URL", local_path)
20-
return local_path
18+
return os.path.join(HERE, "realpython_20180919.xml")
2119

2220

2321
@pytest.fixture
24-
def monkeypatch_summary_feed(monkeypatch):
22+
def local_summary_feed():
2523
"""Use local file instead of downloading feed from web"""
26-
local_path = os.path.join(HERE, "realpython_descriptions_20180919.xml")
27-
monkeypatch.setattr(feed, "URL", local_path)
28-
return local_path
24+
return os.path.join(HERE, "realpython_descriptions_20180919.xml")
2925

3026

3127
#
3228
# Tests
3329
#
34-
def test_site(monkeypatch_feed):
30+
def test_site(local_feed):
3531
"""Test that we can read the site title and link"""
3632
expected = "Real Python (https://realpython.com/)"
37-
assert feed.get_site() == expected
33+
assert feed.get_site(url=local_feed) == expected
3834

3935

40-
def test_article_title(monkeypatch_feed):
36+
def test_article_title(local_feed):
4137
"""Test that title is added at top of article"""
4238
article_id = 0
43-
title = feed.get_titles()[article_id]
44-
article = feed.get_article(article_id)
39+
title = feed.get_titles(url=local_feed)[article_id]
40+
article = feed.get_article(article_id, url=local_feed)
4541

4642
assert article.strip("# ").startswith(title)
4743

4844

49-
def test_article(monkeypatch_feed):
45+
def test_article(local_feed):
5046
"""Test that article is returned"""
5147
article_id = 2
5248
article_phrases = [
5349
"logging.info('This is an info message')",
5450
"By using the `level` parameter",
5551
" * `level`: The root logger",
5652
]
57-
article = feed.get_article(article_id)
53+
article = feed.get_article(article_id, url=local_feed)
5854

5955
for phrase in article_phrases:
6056
assert phrase in article
6157

6258

63-
def test_titles(monkeypatch_feed):
59+
def test_titles(local_feed):
6460
"""Test that titles are found"""
65-
titles = feed.get_titles()
61+
titles = feed.get_titles(url=local_feed)
6662

6763
assert len(titles) == 20
6864
assert titles[0] == "Absolute vs Relative Imports in Python"
6965
assert titles[9] == "Primer on Python Decorators"
7066

7167

72-
def test_summary(monkeypatch_summary_feed):
68+
def test_summary(local_summary_feed):
7369
"""Test that summary feeds can be read"""
7470
article_id = 1
7571
summary_phrases = [
7672
"Get the inside scoop",
7773
"this list of\ninformative videos",
7874
]
79-
summary = feed.get_article(article_id)
75+
summary = feed.get_article(article_id, url=local_summary_feed)
8076

8177
for phrase in summary_phrases:
8278
assert phrase in summary
8379

8480

85-
def test_invalid_article_id(monkeypatch_feed):
81+
def test_invalid_article_id(local_feed):
8682
"""Test that invalid article ids are handled gracefully"""
8783
article_id = "wrong"
8884
with pytest.raises(SystemExit):
89-
feed.get_article(article_id)
85+
feed.get_article(article_id, url=local_feed)

0 commit comments

Comments
 (0)