Skip to content
This repository was archived by the owner on Jan 13, 2024. It is now read-only.

Commit 97ba72a

Browse files
committed
add typing
1 parent 0de0580 commit 97ba72a

22 files changed

+174
-101
lines changed

Makefile

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ include Makefile.config
22
-include Makefile.custom.config
33
.SILENT:
44

5-
check: lint test
5+
check: type-check lint test
66

77
clean:
8+
rm -rf .mypy_cache
89
rm -rf .pytest_cache
910

1011
clean-all: clean
@@ -47,3 +48,7 @@ venv:
4748

4849
test:
4950
$(PYTEST) $(FLASK_APP) --cov $(FLASK_APP) --cov-report term-missing $(PYTEST_ARGS)
51+
52+
type-check:
53+
echo 'Running mypy...'
54+
$(MYPY) $(FLASK_APP)

Makefile.config

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ FLASK = $(VENV)/bin/flask
1818
PYTEST = $(VENV)/bin/py.test
1919
GUNICORN = $(VENV)/bin/gunicorn
2020
BLACK = $(VENV)/bin/black
21+
MYPY = $(VENV)/bin/mypy
2122

2223
#Sphinx Docs
2324
SPHINXOPTS ?=

mypy.ini

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Global options:
2+
3+
[mypy]
4+
python_version = 3.10
5+
disallow_untyped_defs = True
6+
ignore_missing_imports = True
7+
8+
9+
# Per-module options:

setup.cfg

+6
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,24 @@ install_requires =
3838
PyYAML==6.0.0
3939
tweepy==4.10.0
4040
tests_require =
41+
mypy
4142
pytest-cov
4243
pytest-flake8
4344
pytest-isort
4445
pytest-black
46+
types-pytz
47+
types-PyYAML
4548
include_package_data = True
4649

4750
[options.extras_require]
4851
test =
52+
mypy
4953
pytest-cov
5054
pytest-flake8
5155
pytest-isort
5256
pytest-black
57+
types-pytz
58+
types-PyYAML
5359
doc =
5460
sphinx
5561
sphinx_rtd_theme

twootfeed/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
mastodon_api = get_mastodon_api(param, app_log)
2727

2828

29-
def create_app():
29+
def create_app() -> Flask:
3030
app = Flask(__name__)
3131
app_log.setLevel(logging.DEBUG if app.debug else logging.INFO)
3232

@@ -37,7 +37,7 @@ def create_app():
3737
app.register_blueprint(twitter_bp)
3838

3939
@app.route('/')
40-
def index_page():
40+
def index_page() -> str:
4141
message = (
4242
'The RSS feeds are available on these urls : \r\n'
4343
'for Twitter : http://localhost:5000/_keywords_ or '

twootfeed/__main__.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
# source: http://docs.gunicorn.org/en/stable/custom.html
2-
from __future__ import unicode_literals
3-
42
import multiprocessing
3+
from typing import Dict, Optional
54

65
import gunicorn.app.base
6+
from flask import Flask
77
from twootfeed import create_app, param
88

99

1010
class StandaloneApplication(gunicorn.app.base.BaseApplication):
11-
def __init__(self, current_app, options=None):
11+
def __init__(
12+
self, current_app: Flask, options: Optional[Dict] = None
13+
) -> None:
1214
self.options = options or {}
1315
self.application = current_app
1416
super().__init__()
1517

16-
def load_config(self):
18+
def load_config(self) -> None:
1719
config = {
1820
key: value
1921
for key, value in self.options.items()
@@ -22,15 +24,15 @@ def load_config(self):
2224
for key, value in config.items():
2325
self.cfg.set(key.lower(), value)
2426

25-
def load(self):
27+
def load(self) -> Flask:
2628
return self.application
2729

2830

29-
def number_of_workers():
31+
def number_of_workers() -> int:
3032
return (multiprocessing.cpu_count() * 2) + 1
3133

3234

33-
def main():
35+
def main() -> None:
3436
app = create_app()
3537
options = {
3638
'bind': f"{param['app']['host']}:{param['app']['port']}",

twootfeed/mastodon/generate_toots_feed.py

+19-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from html import unescape
2+
from typing import Dict, List, Optional, Tuple
23

34
import pytz
45
from bs4 import BeautifulSoup
6+
from mastodon import Mastodon
57
from twootfeed.utils.feed_generation import generate_feed
68

79

8-
def format_toot(toot, text_length_limit):
10+
def format_toot(toot: Dict, text_length_limit: int) -> Dict:
911
rss_toot = {
1012
'display_name': toot['account']['display_name'],
1113
'screen_name': toot['account']['username'],
@@ -25,7 +27,7 @@ def format_toot(toot, text_length_limit):
2527
if source:
2628
rss_toot['htmltext'] += '<i>Source: {}</i>'.format(source.get('name'))
2729

28-
medialist = toot.get('media_attachments')
30+
medialist = toot.get('media_attachments', [])
2931
if len(medialist) > 0:
3032
rss_toot['htmltext'] += '<br>'
3133
for media in medialist:
@@ -53,8 +55,12 @@ def format_toot(toot, text_length_limit):
5355

5456

5557
def generate_mastodon_feed(
56-
result, param, feed_title, feed_link, feed_desc=None
57-
):
58+
result: List[Dict],
59+
param: Dict,
60+
feed_title: str,
61+
feed_link: str,
62+
feed_desc: Optional[str] = None,
63+
) -> str:
5864
text_length_limit = int(param['feed'].get('text_length_limit', 100))
5965
f = generate_feed(feed_title, feed_link, param, feed_desc)
6066

@@ -86,7 +92,9 @@ def generate_mastodon_feed(
8692
return xml
8793

8894

89-
def get_next_toots(api, first_toots, max_items):
95+
def get_next_toots(
96+
api: Mastodon, first_toots: List[Dict], max_items: int
97+
) -> List[Dict]:
9098
if len(first_toots) == 0:
9199
return first_toots
92100
result = first_toots
@@ -104,7 +112,12 @@ def get_next_toots(api, first_toots, max_items):
104112
return result
105113

106114

107-
def generate_xml(api, param, query_feed=None, favorites=False):
115+
def generate_xml(
116+
api: Mastodon,
117+
param: Dict,
118+
query_feed: Optional[Dict] = None,
119+
favorites: bool = False,
120+
) -> Tuple[str, int]:
108121
if api:
109122
max_items = param['feed']['max_items']
110123
if query_feed:

twootfeed/mastodon/get_api.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import os
2+
from logging import Logger
3+
from typing import Dict
24

35
from mastodon import Mastodon
46
from twootfeed.utils.config import default_directory
57

68

7-
def get_mastodon_api(param, app_log):
9+
def get_mastodon_api(param: Dict, app_log: Logger) -> Mastodon:
810
mastodon_api = None
911

1012
try:

twootfeed/mastodon/routes.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Tuple
2+
13
from flask import Blueprint
24
from twootfeed import mastodon_api, param as mastodon_param
35
from twootfeed.mastodon.generate_toots_feed import generate_xml
@@ -6,26 +8,26 @@
68

79

810
@mastodon_bp.route('/toots/<hashtag>', methods=['GET'])
9-
def tootfeed_hashtag(hashtag):
11+
def tootfeed_hashtag(hashtag: str) -> Tuple[str, int]:
1012
"""generate a rss feed from parsed mastodon search"""
1113
return generate_xml(mastodon_api, mastodon_param, {'hashtag': hashtag})
1214

1315

1416
@mastodon_bp.route('/toots/search/<query_feed>', methods=['GET'])
1517
@mastodon_bp.route('/toot_search/<query_feed>', methods=['GET'])
16-
def tootfeed(query_feed):
18+
def tootfeed(query_feed: str) -> Tuple[str, int]:
1719
"""generate a rss feed from parsed mastodon search"""
1820
return generate_xml(mastodon_api, mastodon_param, {'query': query_feed})
1921

2022

2123
@mastodon_bp.route('/toots/favorites', methods=['GET'])
2224
@mastodon_bp.route('/toot_favorites', methods=['GET'])
23-
def toot_favorites_feed():
25+
def toot_favorites_feed() -> Tuple[str, int]:
2426
"""generate an rss feed authenticated user's favorites"""
2527
return generate_xml(mastodon_api, mastodon_param, favorites=True)
2628

2729

2830
@mastodon_bp.route('/toots/bookmarks', methods=['GET'])
29-
def toot_bookmarks_feed():
31+
def toot_bookmarks_feed() -> Tuple[str, int]:
3032
"""generate an rss feed authenticated user's bookmarks"""
3133
return generate_xml(mastodon_api, mastodon_param)

twootfeed/tests/conftest.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1+
from typing import Any, Dict, List
12
from unittest.mock import Mock
23

34
import pytest
5+
from flask import Flask
46

57
from .. import create_app
68
from .data import retweet, tweet_1, tweet_no_full_text
79
from .utils import Tweepy
810

911

10-
def mock_api(tweets):
12+
def mock_api(tweets: List[Dict]) -> Mock:
1113
mock_response = Mock()
1214
mock_response.return_value = Tweepy(tweets)
1315
return mock_response
1416

1517

1618
@pytest.fixture
17-
def app(monkeypatch, tmpdir):
19+
def app(monkeypatch: pytest.MonkeyPatch, tmpdir: Any) -> Flask:
1820
test_dir = str(tmpdir)
1921
monkeypatch.setenv('TWOOTFEED_CONFIG', test_dir)
2022
monkeypatch.setenv('TWOOTFEED_CONFIG_FILE', test_dir + '/config.yml')
@@ -23,32 +25,32 @@ def app(monkeypatch, tmpdir):
2325

2426

2527
@pytest.fixture()
26-
def fake_tweepy_ok():
28+
def fake_tweepy_ok() -> Mock:
2729
return mock_api(tweets=[tweet_1])
2830

2931

3032
@pytest.fixture()
31-
def fake_tweepy_retweet():
33+
def fake_tweepy_retweet() -> Mock:
3234
return mock_api(tweets=[retweet])
3335

3436

3537
@pytest.fixture()
36-
def fake_tweepy_no_full_text():
38+
def fake_tweepy_no_full_text() -> Mock:
3739
return mock_api(tweets=[tweet_no_full_text])
3840

3941

4042
@pytest.fixture()
41-
def fake_tweepy_no_tweets():
43+
def fake_tweepy_no_tweets() -> Mock:
4244
return mock_api(tweets=[])
4345

4446

4547
@pytest.fixture()
46-
def fake_tweepy_200_ok():
48+
def fake_tweepy_200_ok() -> Mock:
4749
return mock_api(tweets=[tweet_1] * 200)
4850

4951

5052
@pytest.fixture()
51-
def fake_tweepy_220_ok():
53+
def fake_tweepy_220_ok() -> Mock:
5254
return mock_api(
5355
tweets=[tweet_1] * 200 + [tweet_no_full_text] * 10 + [retweet] * 10
5456
)

twootfeed/tests/data.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from copy import deepcopy
22
from datetime import datetime
3+
from typing import Dict
34

45
import pytz
56

@@ -32,11 +33,11 @@
3233
'app': {'host': '0.0.0.0', 'port': '8080'},
3334
}
3435

35-
invalid_param = deepcopy(init_param)
36+
invalid_param: Dict = deepcopy(init_param)
3637
invalid_param['mastodon']['client_id_file'] = 'tootrss_clientcred_invalid.txt'
3738
invalid_param['mastodon']['access_token_file'] = 'tootrss_usercred_invalid.txt'
3839

39-
invalid_param_api = deepcopy(invalid_param)
40+
invalid_param_api: Dict = deepcopy(invalid_param)
4041
invalid_param_api['twitter'] = None
4142
invalid_param_api['mastodon'] = None
4243

twootfeed/tests/test_default_route.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
def test_default_route(app):
1+
from flask import Flask
2+
3+
4+
def test_default_route(app: Flask) -> None:
25
client = app.test_client()
36
response = client.get('/')
47
assert response.status_code == 200

0 commit comments

Comments
 (0)