mirror of
https://github.com/ihabunek/toot.git
synced 2024-09-22 04:25:55 -04:00
Replace deprecated optparse with argparse
This commit is contained in:
parent
86f4e1beac
commit
d7701bd2e6
4
.gitignore
vendored
4
.gitignore
vendored
@ -5,4 +5,6 @@ build/
|
|||||||
dist/
|
dist/
|
||||||
tmp/
|
tmp/
|
||||||
.pypirc
|
.pypirc
|
||||||
/.env
|
/.env
|
||||||
|
/.coverage
|
||||||
|
/htmlcov
|
||||||
|
5
Makefile
5
Makefile
@ -12,7 +12,10 @@ dist :
|
|||||||
@echo "\nDone."
|
@echo "\nDone."
|
||||||
|
|
||||||
clean :
|
clean :
|
||||||
rm -rf build dist *.egg-info MANIFEST
|
rm -rf build dist *.egg-info MANIFEST htmlcov
|
||||||
|
|
||||||
publish :
|
publish :
|
||||||
twine upload dist/*
|
twine upload dist/*
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
py.test --cov=toot --cov-report html tests/
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pytest>=3.0.0
|
pytest-cov~=2.4.0
|
||||||
twine>=1.8.1
|
pytest~=3.0.0
|
||||||
wheel>=0.29.0
|
twine~=1.8.1
|
||||||
|
wheel~=0.29.0
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
import sys
|
|
||||||
|
|
||||||
from toot import User, App
|
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
|
from tests.utils import MockResponse
|
||||||
|
|
||||||
@ -11,12 +11,19 @@ app = App('https://habunek.com', 'foo', 'bar')
|
|||||||
user = User('ivan@habunek.com', 'xxx')
|
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):
|
def mock_prepare(request):
|
||||||
assert request.method == 'POST'
|
assert request.method == 'POST'
|
||||||
assert request.url == 'https://habunek.com/api/v1/statuses'
|
assert request.url == 'https://habunek.com/api/v1/statuses'
|
||||||
|
assert request.headers == {'Authorization': 'Bearer xxx'}
|
||||||
assert request.data == {
|
assert request.data == {
|
||||||
'status': '"Hello world"',
|
'status': 'Hello world',
|
||||||
'visibility': 'public',
|
'visibility': 'public',
|
||||||
'media_ids[]': None,
|
'media_ids[]': None,
|
||||||
}
|
}
|
||||||
@ -29,14 +36,17 @@ def test_post_status_defaults(monkeypatch):
|
|||||||
monkeypatch.setattr(requests.Request, 'prepare', mock_prepare)
|
monkeypatch.setattr(requests.Request, 'prepare', mock_prepare)
|
||||||
monkeypatch.setattr(requests.Session, 'send', mock_send)
|
monkeypatch.setattr(requests.Session, 'send', mock_send)
|
||||||
|
|
||||||
sys.argv = ['toot', 'post', '"Hello world"']
|
cmd_post_status(app, user, ['Hello world'])
|
||||||
cmd_post_status(app, user)
|
|
||||||
|
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):
|
def mock_prepare(request):
|
||||||
assert request.method == 'POST'
|
assert request.method == 'POST'
|
||||||
assert request.url == 'https://habunek.com/api/v1/statuses'
|
assert request.url == 'https://habunek.com/api/v1/statuses'
|
||||||
|
assert request.headers == {'Authorization': 'Bearer xxx'}
|
||||||
assert request.data == {
|
assert request.data == {
|
||||||
'status': '"Hello world"',
|
'status': '"Hello world"',
|
||||||
'visibility': 'unlisted',
|
'visibility': 'unlisted',
|
||||||
@ -51,25 +61,80 @@ def test_post_status_with_options(monkeypatch):
|
|||||||
monkeypatch.setattr(requests.Request, 'prepare', mock_prepare)
|
monkeypatch.setattr(requests.Request, 'prepare', mock_prepare)
|
||||||
monkeypatch.setattr(requests.Session, 'send', mock_send)
|
monkeypatch.setattr(requests.Session, 'send', mock_send)
|
||||||
|
|
||||||
sys.argv = ['toot', 'post', '"Hello world"',
|
args = ['"Hello world"', '--visibility', 'unlisted']
|
||||||
'--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):
|
def test_post_status_invalid_visibility(monkeypatch, capsys):
|
||||||
sys.argv = ['toot', 'post', '"Hello world"',
|
args = ['Hello world', '--visibility', 'foo']
|
||||||
'--visibility', 'foo']
|
|
||||||
|
|
||||||
with pytest.raises(ConsoleError) as ex:
|
with pytest.raises(SystemExit):
|
||||||
cmd_post_status(app, user)
|
cmd_post_status(app, user, args)
|
||||||
assert str(ex.value) == "Invalid visibility value given: 'foo'"
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert "invalid visibility value: 'foo'" in err
|
||||||
|
|
||||||
|
|
||||||
def test_post_status_invalid_media(monkeypatch):
|
def test_post_status_invalid_media(monkeypatch, capsys):
|
||||||
sys.argv = ['toot', 'post', '"Hello world"',
|
args = ['Hello world', '--media', 'does_not_exist.jpg']
|
||||||
'--media', 'does_not_exist.jpg']
|
|
||||||
|
|
||||||
with pytest.raises(ConsoleError) as ex:
|
with pytest.raises(SystemExit):
|
||||||
cmd_post_status(app, user)
|
cmd_post_status(app, user, args)
|
||||||
assert str(ex.value) == "File does not exist: does_not_exist.jpg"
|
|
||||||
|
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
|
||||||
|
103
toot/console.py
103
toot/console.py
@ -12,7 +12,7 @@ from datetime import datetime
|
|||||||
from future.moves.itertools import zip_longest
|
from future.moves.itertools import zip_longest
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from optparse import OptionParser
|
from argparse import ArgumentParser, FileType
|
||||||
from textwrap import TextWrapper
|
from textwrap import TextWrapper
|
||||||
|
|
||||||
from .config import save_user, load_user, load_app, save_app, CONFIG_APP_FILE, CONFIG_USER_FILE
|
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)
|
items = timeline_home(app, user)
|
||||||
parsed_items = [parse_timeline(t) for t in items]
|
parsed_items = [parse_timeline(t) for t in items]
|
||||||
|
|
||||||
@ -141,39 +147,41 @@ def cmd_timeline(app, user):
|
|||||||
print("─" * 31 + "┼" + "─" * 88)
|
print("─" * 31 + "┼" + "─" * 88)
|
||||||
|
|
||||||
|
|
||||||
def cmd_post_status(app, user):
|
def visibility(value):
|
||||||
parser = OptionParser(usage="toot post [options] TEXT")
|
if value not in ['public', 'unlisted', 'private', 'direct']:
|
||||||
|
raise ValueError("Invalid visibility value")
|
||||||
|
|
||||||
parser.add_option("-m", "--media", dest="media", type="string",
|
return value
|
||||||
help="path to the media file to attach")
|
|
||||||
|
|
||||||
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:
|
args = parser.parse_args(args)
|
||||||
parser.print_help()
|
|
||||||
raise ConsoleError("No text given")
|
|
||||||
|
|
||||||
if options.visibility not in ['public', 'unlisted', 'private', 'direct']:
|
if args.media:
|
||||||
raise ConsoleError("Invalid visibility value given: '{}'".format(options.visibility))
|
media = do_upload(app, user, args.media)
|
||||||
|
|
||||||
if options.media:
|
|
||||||
media = do_upload(app, user, options.media)
|
|
||||||
media_ids = [media['id']]
|
media_ids = [media['id']]
|
||||||
else:
|
else:
|
||||||
media_ids = None
|
media_ids = None
|
||||||
|
|
||||||
response = post_status(
|
response = post_status(app, user, args.text, media_ids=media_ids, visibility=args.visibility)
|
||||||
app, user, args[1], media_ids=media_ids, visibility=options.visibility)
|
|
||||||
|
|
||||||
print("Toot posted: " + green(response.get('url')))
|
print("Toot posted: " + green(response.get('url')))
|
||||||
|
|
||||||
|
|
||||||
def cmd_auth(app, user):
|
def cmd_auth(app, user, args):
|
||||||
parser = OptionParser(usage='%prog auth')
|
parser = ArgumentParser(prog="toot auth",
|
||||||
parser.parse_args()
|
description="Show login details",
|
||||||
|
epilog="https://github.com/ihabunek/toot")
|
||||||
|
parser.parse_args(args)
|
||||||
|
|
||||||
if app and user:
|
if app and user:
|
||||||
print("You are logged in to " + green(app.base_url))
|
print("You are logged in to " + green(app.base_url))
|
||||||
@ -185,7 +193,9 @@ def cmd_auth(app, user):
|
|||||||
|
|
||||||
|
|
||||||
def cmd_login():
|
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()
|
parser.parse_args()
|
||||||
|
|
||||||
app = create_app_interactive()
|
app = create_app_interactive()
|
||||||
@ -194,24 +204,26 @@ def cmd_login():
|
|||||||
return app, user
|
return app, user
|
||||||
|
|
||||||
|
|
||||||
def cmd_logout(app, user):
|
def cmd_logout(app, user, args):
|
||||||
parser = OptionParser(usage='%prog logout')
|
parser = ArgumentParser(prog="toot logout",
|
||||||
parser.parse_args()
|
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_APP_FILE)
|
||||||
os.unlink(CONFIG_USER_FILE)
|
os.unlink(CONFIG_USER_FILE)
|
||||||
print("You are now logged out")
|
print("You are now logged out")
|
||||||
|
|
||||||
|
|
||||||
def cmd_upload(app, user):
|
def cmd_upload(app, user, args):
|
||||||
parser = OptionParser(usage='%prog upload <path_to_media>')
|
parser = ArgumentParser(prog="toot upload",
|
||||||
parser.parse_args()
|
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:
|
args = parser.parse_args(args)
|
||||||
print_error("No status text given")
|
|
||||||
return
|
|
||||||
|
|
||||||
response = do_upload(sys.argv[2])
|
response = do_upload(app, user, args.file)
|
||||||
|
|
||||||
print("\nSuccessfully uploaded media ID {}, type '{}'".format(
|
print("\nSuccessfully uploaded media ID {}, type '{}'".format(
|
||||||
yellow(response['id']), yellow(response['type'])))
|
yellow(response['id']), yellow(response['type'])))
|
||||||
@ -220,16 +232,12 @@ def cmd_upload(app, user):
|
|||||||
print("Text URL: " + green(response['text_url']))
|
print("Text URL: " + green(response['text_url']))
|
||||||
|
|
||||||
|
|
||||||
def do_upload(app, user, path):
|
def do_upload(app, user, file):
|
||||||
if not os.path.exists(path):
|
print("Uploading media: {}".format(green(file.name)))
|
||||||
raise ConsoleError("File does not exist: " + path)
|
return upload_media(app, user, file)
|
||||||
|
|
||||||
with open(path, 'rb') as f:
|
|
||||||
print("Uploading media: {}".format(green(f.name)))
|
|
||||||
return upload_media(app, user, f)
|
|
||||||
|
|
||||||
|
|
||||||
def run_command(command):
|
def run_command(command, args):
|
||||||
app = load_app()
|
app = load_app()
|
||||||
user = load_user()
|
user = load_user()
|
||||||
|
|
||||||
@ -238,7 +246,7 @@ def run_command(command):
|
|||||||
return cmd_login()
|
return cmd_login()
|
||||||
|
|
||||||
if command == 'auth':
|
if command == 'auth':
|
||||||
return cmd_auth(app, user)
|
return cmd_auth(app, user, args)
|
||||||
|
|
||||||
# Commands which require user to be logged in
|
# Commands which require user to be logged in
|
||||||
if not app or not user:
|
if not app or not user:
|
||||||
@ -247,16 +255,16 @@ def run_command(command):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if command == 'logout':
|
if command == 'logout':
|
||||||
return cmd_logout(app, user)
|
return cmd_logout(app, user, args)
|
||||||
|
|
||||||
if command == 'post':
|
if command == 'post':
|
||||||
return cmd_post_status(app, user)
|
return cmd_post_status(app, user, args)
|
||||||
|
|
||||||
if command == 'timeline':
|
if command == 'timeline':
|
||||||
return cmd_timeline(app, user)
|
return cmd_timeline(app, user, args)
|
||||||
|
|
||||||
if command == 'upload':
|
if command == 'upload':
|
||||||
return cmd_upload(app, user)
|
return cmd_upload(app, user, args)
|
||||||
|
|
||||||
print(red("Unknown command '{}'\n".format(command)))
|
print(red("Unknown command '{}'\n".format(command)))
|
||||||
print_usage()
|
print_usage()
|
||||||
@ -267,11 +275,12 @@ def main():
|
|||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
command = sys.argv[1] if len(sys.argv) > 1 else None
|
command = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
|
args = sys.argv[2:]
|
||||||
|
|
||||||
if not command:
|
if not command:
|
||||||
return print_usage()
|
return print_usage()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
run_command(command)
|
run_command(command, args)
|
||||||
except ConsoleError as e:
|
except ConsoleError as e:
|
||||||
print_error(str(e))
|
print_error(str(e))
|
||||||
|
Loading…
Reference in New Issue
Block a user