Replace deprecated optparse with argparse

This commit is contained in:
Ivan Habunek 2017-04-16 14:06:16 +02:00
parent 86f4e1beac
commit d7701bd2e6
No known key found for this signature in database
GPG Key ID: CDBD63C43A30BB95
5 changed files with 154 additions and 74 deletions

4
.gitignore vendored
View File

@ -5,4 +5,6 @@ build/
dist/
tmp/
.pypirc
/.env
/.env
/.coverage
/htmlcov

View File

@ -12,7 +12,10 @@ dist :
@echo "\nDone."
clean :
rm -rf build dist *.egg-info MANIFEST
rm -rf build dist *.egg-info MANIFEST htmlcov
publish :
twine upload dist/*
coverage:
py.test --cov=toot --cov-report html tests/

View File

@ -1,3 +1,4 @@
pytest>=3.0.0
twine>=1.8.1
wheel>=0.29.0
pytest-cov~=2.4.0
pytest~=3.0.0
twine~=1.8.1
wheel~=0.29.0

View File

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
import pytest
import requests
import sys
from toot import User, App
from toot.console import cmd_post_status, ConsoleError
from toot.console import print_usage, cmd_post_status, cmd_timeline, cmd_upload
from tests.utils import MockResponse
@ -11,12 +11,19 @@ app = App('https://habunek.com', 'foo', 'bar')
user = User('ivan@habunek.com', 'xxx')
def test_post_status_defaults(monkeypatch):
def test_print_usagecap(capsys):
print_usage()
out, err = capsys.readouterr()
assert "toot - interact with Mastodon from the command line" in out
def test_post_status_defaults(monkeypatch, capsys):
def mock_prepare(request):
assert request.method == 'POST'
assert request.url == 'https://habunek.com/api/v1/statuses'
assert request.headers == {'Authorization': 'Bearer xxx'}
assert request.data == {
'status': '"Hello world"',
'status': 'Hello world',
'visibility': 'public',
'media_ids[]': None,
}
@ -29,14 +36,17 @@ def test_post_status_defaults(monkeypatch):
monkeypatch.setattr(requests.Request, 'prepare', mock_prepare)
monkeypatch.setattr(requests.Session, 'send', mock_send)
sys.argv = ['toot', 'post', '"Hello world"']
cmd_post_status(app, user)
cmd_post_status(app, user, ['Hello world'])
out, err = capsys.readouterr()
assert "Toot posted" in out
def test_post_status_with_options(monkeypatch):
def test_post_status_with_options(monkeypatch, capsys):
def mock_prepare(request):
assert request.method == 'POST'
assert request.url == 'https://habunek.com/api/v1/statuses'
assert request.headers == {'Authorization': 'Bearer xxx'}
assert request.data == {
'status': '"Hello world"',
'visibility': 'unlisted',
@ -51,25 +61,80 @@ def test_post_status_with_options(monkeypatch):
monkeypatch.setattr(requests.Request, 'prepare', mock_prepare)
monkeypatch.setattr(requests.Session, 'send', mock_send)
sys.argv = ['toot', 'post', '"Hello world"',
'--visibility', 'unlisted']
args = ['"Hello world"', '--visibility', 'unlisted']
cmd_post_status(app, user)
cmd_post_status(app, user, args)
out, err = capsys.readouterr()
assert "Toot posted" in out
def test_post_status_invalid_visibility(monkeypatch):
sys.argv = ['toot', 'post', '"Hello world"',
'--visibility', 'foo']
def test_post_status_invalid_visibility(monkeypatch, capsys):
args = ['Hello world', '--visibility', 'foo']
with pytest.raises(ConsoleError) as ex:
cmd_post_status(app, user)
assert str(ex.value) == "Invalid visibility value given: 'foo'"
with pytest.raises(SystemExit):
cmd_post_status(app, user, args)
out, err = capsys.readouterr()
assert "invalid visibility value: 'foo'" in err
def test_post_status_invalid_media(monkeypatch):
sys.argv = ['toot', 'post', '"Hello world"',
'--media', 'does_not_exist.jpg']
def test_post_status_invalid_media(monkeypatch, capsys):
args = ['Hello world', '--media', 'does_not_exist.jpg']
with pytest.raises(ConsoleError) as ex:
cmd_post_status(app, user)
assert str(ex.value) == "File does not exist: does_not_exist.jpg"
with pytest.raises(SystemExit):
cmd_post_status(app, user, args)
out, err = capsys.readouterr()
assert "can't open 'does_not_exist.jpg'" in err
def test_timeline(monkeypatch, capsys):
def mock_get(url, params, headers=None):
assert url == 'https://habunek.com/api/v1/timelines/home'
assert headers == {'Authorization': 'Bearer xxx'}
assert params is None
return MockResponse([{
'account': {
'display_name': 'Frank Zappa',
'username': 'fz'
},
'created_at': '2017-04-12T15:53:18.174Z',
'content': "<p>The computer can't tell you the emotional story. It can give you the exact mathematical design, but what's missing is the eyebrows.</p>",
'reblog': None,
}])
monkeypatch.setattr(requests, 'get', mock_get)
cmd_timeline(app, user, [])
out, err = capsys.readouterr()
assert "The computer can't tell you the emotional story." in out
assert "Frank Zappa @fz" in out
def test_upload(monkeypatch, capsys):
def mock_prepare(request):
assert request.method == 'POST'
assert request.url == 'https://habunek.com/api/v1/media'
assert request.headers == {'Authorization': 'Bearer xxx'}
assert request.files.get('file') is not None
def mock_send(*args):
return MockResponse({
'id': 123,
'url': 'https://bigfish.software/123/456',
'preview_url': 'https://bigfish.software/789/012',
'text_url': 'https://bigfish.software/345/678',
'type': 'image',
})
monkeypatch.setattr(requests.Request, 'prepare', mock_prepare)
monkeypatch.setattr(requests.Session, 'send', mock_send)
cmd_upload(app, user, [__file__])
out, err = capsys.readouterr()
assert "Uploading media" in out
assert __file__ in out

View File

@ -12,7 +12,7 @@ from datetime import datetime
from future.moves.itertools import zip_longest
from getpass import getpass
from itertools import chain
from optparse import OptionParser
from argparse import ArgumentParser, FileType
from textwrap import TextWrapper
from .config import save_user, load_user, load_app, save_app, CONFIG_APP_FILE, CONFIG_USER_FILE
@ -131,7 +131,13 @@ def parse_timeline(item):
}
def cmd_timeline(app, user):
def cmd_timeline(app, user, args):
parser = ArgumentParser(prog="toot timeline",
description="Show recent items in your public timeline",
epilog="https://github.com/ihabunek/toot")
args = parser.parse_args(args)
items = timeline_home(app, user)
parsed_items = [parse_timeline(t) for t in items]
@ -141,39 +147,41 @@ def cmd_timeline(app, user):
print("" * 31 + "" + "" * 88)
def cmd_post_status(app, user):
parser = OptionParser(usage="toot post [options] TEXT")
def visibility(value):
if value not in ['public', 'unlisted', 'private', 'direct']:
raise ValueError("Invalid visibility value")
parser.add_option("-m", "--media", dest="media", type="string",
help="path to the media file to attach")
return value
parser.add_option("-v", "--visibility", dest="visibility", type="string", default="public",
help='post visibility, either "public" (default), "direct", "private", or "unlisted"')
(options, args) = parser.parse_args()
def cmd_post_status(app, user, args):
parser = ArgumentParser(prog="toot post",
description="Post a status text to the timeline",
epilog="https://github.com/ihabunek/toot")
parser.add_argument("text", help="The status text to post.")
parser.add_argument("-m", "--media", type=FileType('rb'),
help="path to the media file to attach")
parser.add_argument("-v", "--visibility", type=visibility, default="public",
help='post visibility, either "public" (default), "direct", "private", or "unlisted"')
if len(args) < 2:
parser.print_help()
raise ConsoleError("No text given")
args = parser.parse_args(args)
if options.visibility not in ['public', 'unlisted', 'private', 'direct']:
raise ConsoleError("Invalid visibility value given: '{}'".format(options.visibility))
if options.media:
media = do_upload(app, user, options.media)
if args.media:
media = do_upload(app, user, args.media)
media_ids = [media['id']]
else:
media_ids = None
response = post_status(
app, user, args[1], media_ids=media_ids, visibility=options.visibility)
response = post_status(app, user, args.text, media_ids=media_ids, visibility=args.visibility)
print("Toot posted: " + green(response.get('url')))
def cmd_auth(app, user):
parser = OptionParser(usage='%prog auth')
parser.parse_args()
def cmd_auth(app, user, args):
parser = ArgumentParser(prog="toot auth",
description="Show login details",
epilog="https://github.com/ihabunek/toot")
parser.parse_args(args)
if app and user:
print("You are logged in to " + green(app.base_url))
@ -185,7 +193,9 @@ def cmd_auth(app, user):
def cmd_login():
parser = OptionParser(usage='%prog login')
parser = ArgumentParser(prog="toot login",
description="Log into a Mastodon instance",
epilog="https://github.com/ihabunek/toot")
parser.parse_args()
app = create_app_interactive()
@ -194,24 +204,26 @@ def cmd_login():
return app, user
def cmd_logout(app, user):
parser = OptionParser(usage='%prog logout')
parser.parse_args()
def cmd_logout(app, user, args):
parser = ArgumentParser(prog="toot logout",
description="Log out, delete stored access keys",
epilog="https://github.com/ihabunek/toot")
parser.parse_args(args)
os.unlink(CONFIG_APP_FILE)
os.unlink(CONFIG_USER_FILE)
print("You are now logged out")
def cmd_upload(app, user):
parser = OptionParser(usage='%prog upload <path_to_media>')
parser.parse_args()
def cmd_upload(app, user, args):
parser = ArgumentParser(prog="toot upload",
description="Upload an image or video file",
epilog="https://github.com/ihabunek/toot")
parser.add_argument("file", help="Path to the file to upload", type=FileType('rb'))
if len(sys.argv) < 3:
print_error("No status text given")
return
args = parser.parse_args(args)
response = do_upload(sys.argv[2])
response = do_upload(app, user, args.file)
print("\nSuccessfully uploaded media ID {}, type '{}'".format(
yellow(response['id']), yellow(response['type'])))
@ -220,16 +232,12 @@ def cmd_upload(app, user):
print("Text URL: " + green(response['text_url']))
def do_upload(app, user, path):
if not os.path.exists(path):
raise ConsoleError("File does not exist: " + path)
with open(path, 'rb') as f:
print("Uploading media: {}".format(green(f.name)))
return upload_media(app, user, f)
def do_upload(app, user, file):
print("Uploading media: {}".format(green(file.name)))
return upload_media(app, user, file)
def run_command(command):
def run_command(command, args):
app = load_app()
user = load_user()
@ -238,7 +246,7 @@ def run_command(command):
return cmd_login()
if command == 'auth':
return cmd_auth(app, user)
return cmd_auth(app, user, args)
# Commands which require user to be logged in
if not app or not user:
@ -247,16 +255,16 @@ def run_command(command):
return
if command == 'logout':
return cmd_logout(app, user)
return cmd_logout(app, user, args)
if command == 'post':
return cmd_post_status(app, user)
return cmd_post_status(app, user, args)
if command == 'timeline':
return cmd_timeline(app, user)
return cmd_timeline(app, user, args)
if command == 'upload':
return cmd_upload(app, user)
return cmd_upload(app, user, args)
print(red("Unknown command '{}'\n".format(command)))
print_usage()
@ -267,11 +275,12 @@ def main():
logging.basicConfig(level=logging.DEBUG)
command = sys.argv[1] if len(sys.argv) > 1 else None
args = sys.argv[2:]
if not command:
return print_usage()
try:
run_command(command)
run_command(command, args)
except ConsoleError as e:
print_error(str(e))