forked from LBRYCommunity/lbry-sdk
update arg parsing to use docopt
This commit is contained in:
parent
0eac1e6ed0
commit
57c3d2590c
5 changed files with 119 additions and 140 deletions
|
@ -1,23 +1,68 @@
|
||||||
import argparse
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import colorama
|
import colorama
|
||||||
|
from docopt import docopt
|
||||||
|
from collections import OrderedDict
|
||||||
from lbrynet import conf
|
from lbrynet import conf
|
||||||
from lbrynet.core import utils
|
from lbrynet.core import utils
|
||||||
from lbrynet.lbrynet_daemon.auth.client import JSONRPCException, LBRYAPIClient
|
from lbrynet.lbrynet_daemon.auth.client import JSONRPCException, LBRYAPIClient
|
||||||
from lbrynet.lbrynet_daemon.Daemon import LOADING_WALLET_CODE
|
from lbrynet.lbrynet_daemon.Daemon import LOADING_WALLET_CODE, Daemon
|
||||||
from jsonrpc.common import RPCError
|
from jsonrpc.common import RPCError
|
||||||
from urllib2 import URLError, HTTPError
|
from urllib2 import URLError, HTTPError
|
||||||
from httplib import UNAUTHORIZED
|
from httplib import UNAUTHORIZED
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def remove_brackets(key):
|
||||||
colorama.init()
|
if key.startswith("<") and key.endswith(">"):
|
||||||
parser = argparse.ArgumentParser(add_help=False)
|
return str(key[1:-1])
|
||||||
_, arguments = parser.parse_known_args()
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
def set_flag_vals(flag_names, parsed_args):
|
||||||
|
kwargs = OrderedDict()
|
||||||
|
for key, arg in parsed_args.iteritems():
|
||||||
|
if arg is None:
|
||||||
|
continue
|
||||||
|
elif key.startswith("--"):
|
||||||
|
if remove_brackets(key[2:]) not in kwargs:
|
||||||
|
k = remove_brackets(key[2:])
|
||||||
|
kwargs[k] = guess_type(arg)
|
||||||
|
elif key in flag_names:
|
||||||
|
if remove_brackets(flag_names[key]) not in kwargs:
|
||||||
|
kwargs[remove_brackets(flag_names[key])] = guess_type(arg)
|
||||||
|
elif remove_brackets(key) not in kwargs:
|
||||||
|
kwargs[remove_brackets(key)] = guess_type(arg)
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv[1:]):
|
||||||
|
method, args = sys.argv[1], sys.argv[2:]
|
||||||
|
else:
|
||||||
|
print_help()
|
||||||
|
return
|
||||||
|
|
||||||
|
if method in ['help', '--help', '-h']:
|
||||||
|
if len(args) == 1:
|
||||||
|
print_help_for_command(args[0])
|
||||||
|
else:
|
||||||
|
print_help()
|
||||||
|
return
|
||||||
|
|
||||||
|
if method not in Daemon.callable_methods:
|
||||||
|
print_error("\"%s\" is not a valid command." % method)
|
||||||
|
return
|
||||||
|
|
||||||
|
fn = Daemon.callable_methods[method]
|
||||||
|
if hasattr(fn, "_flags"):
|
||||||
|
flag_names = fn._flags
|
||||||
|
else:
|
||||||
|
flag_names = {}
|
||||||
|
|
||||||
|
parsed = docopt(fn.__doc__, args)
|
||||||
|
kwargs = set_flag_vals(flag_names, parsed)
|
||||||
|
colorama.init()
|
||||||
conf.initialize_settings()
|
conf.initialize_settings()
|
||||||
api = LBRYAPIClient.get_client()
|
api = LBRYAPIClient.get_client()
|
||||||
|
|
||||||
|
@ -44,94 +89,43 @@ def main():
|
||||||
print " Status: " + message
|
print " Status: " + message
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if len(arguments) < 1:
|
|
||||||
print_help(api)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
method = arguments[0]
|
|
||||||
try:
|
|
||||||
params = parse_params(arguments[1:])
|
|
||||||
except InvalidParameters as e:
|
|
||||||
print_error(e.message)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# TODO: check if port is bound. Error if its not
|
# TODO: check if port is bound. Error if its not
|
||||||
|
|
||||||
if method in ['--help', '-h', 'help']:
|
try:
|
||||||
if len(params) == 0:
|
result = api.call(method, **kwargs)
|
||||||
print_help(api)
|
if isinstance(result, basestring):
|
||||||
elif 'command' not in params:
|
# printing the undumped string is prettier
|
||||||
print_error(
|
print result
|
||||||
'To get help on a specific command, use `{} help command=COMMAND_NAME`'.format(
|
|
||||||
os.path.basename(sys.argv[0]))
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
print_help_for_command(api, params['command'])
|
print utils.json_dumps_pretty(result)
|
||||||
|
except (RPCError, KeyError, JSONRPCException, HTTPError) as err:
|
||||||
|
error_data = None
|
||||||
|
if isinstance(err, HTTPError):
|
||||||
|
error_body = err.read()
|
||||||
|
try:
|
||||||
|
error_data = json.loads(error_body)
|
||||||
|
except ValueError:
|
||||||
|
print (
|
||||||
|
"There was an error, and the response was not valid JSON.\n" +
|
||||||
|
"Raw JSONRPC response:\n" + error_body
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
elif method not in api.commands():
|
print_error(error_data['error']['message'] + "\n", suggest_help=False)
|
||||||
print_error("'" + method + "' is not a valid command.")
|
else:
|
||||||
|
print_error("Something went wrong\n", suggest_help=False)
|
||||||
|
|
||||||
else:
|
print_help_for_command(method)
|
||||||
try:
|
|
||||||
result = api.call(method, params)
|
|
||||||
if isinstance(result, basestring):
|
|
||||||
# printing the undumped string is prettier
|
|
||||||
print result
|
|
||||||
else:
|
|
||||||
print utils.json_dumps_pretty(result)
|
|
||||||
except (RPCError, KeyError, JSONRPCException, HTTPError) as err:
|
|
||||||
error_data = None
|
|
||||||
if isinstance(err, HTTPError):
|
|
||||||
error_body = err.read()
|
|
||||||
try:
|
|
||||||
error_data = json.loads(error_body)
|
|
||||||
except ValueError:
|
|
||||||
print (
|
|
||||||
"There was an error, and the response was not valid JSON.\n" +
|
|
||||||
"Raw JSONRPC response:\n" + error_body
|
|
||||||
)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
print_error(error_data['error']['message'] + "\n", suggest_help=False)
|
if 'data' in error_data['error'] and 'traceback' in error_data['error']['data']:
|
||||||
else:
|
print "Here's the traceback for the error you encountered:"
|
||||||
print_error("Something went wrong\n", suggest_help=False)
|
print "\n".join(error_data['error']['data']['traceback'])
|
||||||
|
return 1
|
||||||
print_help_for_command(api, method)
|
|
||||||
if 'data' in error_data['error'] and 'traceback' in error_data['error']['data']:
|
|
||||||
print "Here's the traceback for the error you encountered:"
|
|
||||||
print "\n".join(error_data['error']['data']['traceback'])
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
def parse_params(params):
|
|
||||||
if len(params) > 1:
|
|
||||||
return get_params_from_kwargs(params)
|
|
||||||
elif len(params) == 1:
|
|
||||||
try:
|
|
||||||
return json.loads(params[0])
|
|
||||||
except ValueError:
|
|
||||||
return get_params_from_kwargs(params)
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidParameters(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def get_params_from_kwargs(params):
|
|
||||||
params_for_return = {}
|
|
||||||
for i in params:
|
|
||||||
try:
|
|
||||||
eq_pos = i.index('=')
|
|
||||||
except ValueError:
|
|
||||||
raise InvalidParameters('{} is not in <key>=<value> format'.format(i))
|
|
||||||
k, v = i[:eq_pos], i[eq_pos + 1:]
|
|
||||||
params_for_return[k] = guess_type(v)
|
|
||||||
return params_for_return
|
|
||||||
|
|
||||||
|
|
||||||
def guess_type(x):
|
def guess_type(x):
|
||||||
|
if not isinstance(x, (unicode, str)):
|
||||||
|
return x
|
||||||
if x in ('true', 'True', 'TRUE'):
|
if x in ('true', 'True', 'TRUE'):
|
||||||
return True
|
return True
|
||||||
if x in ('false', 'False', 'FALSE'):
|
if x in ('false', 'False', 'FALSE'):
|
||||||
|
@ -159,7 +153,8 @@ def print_error(message, suggest_help=True):
|
||||||
print_help_suggestion()
|
print_help_suggestion()
|
||||||
|
|
||||||
|
|
||||||
def print_help(api):
|
def print_help():
|
||||||
|
commands = Daemon.callable_methods.keys()
|
||||||
print "\n".join([
|
print "\n".join([
|
||||||
"NAME",
|
"NAME",
|
||||||
" lbrynet-cli - LBRY command line client.",
|
" lbrynet-cli - LBRY command line client.",
|
||||||
|
@ -170,20 +165,18 @@ def print_help(api):
|
||||||
"EXAMPLES",
|
"EXAMPLES",
|
||||||
" lbrynet-cli commands # list available commands",
|
" lbrynet-cli commands # list available commands",
|
||||||
" lbrynet-cli status # get daemon status",
|
" lbrynet-cli status # get daemon status",
|
||||||
" lbrynet-cli resolve_name name=what # resolve a name",
|
" lbrynet-cli resolve_name what # resolve a name",
|
||||||
" lbrynet-cli help command=resolve_name # get help for a command",
|
" lbrynet-cli help resolve_name # get help for a command",
|
||||||
"",
|
"",
|
||||||
"COMMANDS",
|
"COMMANDS",
|
||||||
wrap_list_to_term_width(api.commands(), prefix=' ')
|
wrap_list_to_term_width(commands, prefix=' ')
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def print_help_for_command(api, command):
|
def print_help_for_command(command):
|
||||||
help_response = api.call('help', {'command': command})
|
fn = Daemon.callable_methods.get(command)
|
||||||
print "Help for %s method:" % command
|
if fn:
|
||||||
message = help_response['help'] if 'help' in help_response else help_response
|
print "Help for %s method:\n%s" % (command, fn.__doc__)
|
||||||
message = "\n".join([' ' + line for line in message.split("\n")])
|
|
||||||
print message
|
|
||||||
|
|
||||||
|
|
||||||
def wrap_list_to_term_width(l, width=None, separator=', ', prefix=''):
|
def wrap_list_to_term_width(l, width=None, separator=', ', prefix=''):
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
import urlparse
|
import urlparse
|
||||||
import inspect
|
|
||||||
import json
|
import json
|
||||||
|
import inspect
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
|
@ -15,9 +15,9 @@ from traceback import format_exc
|
||||||
from lbrynet import conf
|
from lbrynet import conf
|
||||||
from lbrynet.core.Error import InvalidAuthenticationToken
|
from lbrynet.core.Error import InvalidAuthenticationToken
|
||||||
from lbrynet.core import utils
|
from lbrynet.core import utils
|
||||||
from lbrynet.undecorated import undecorated
|
|
||||||
from lbrynet.lbrynet_daemon.auth.util import APIKey, get_auth_message
|
from lbrynet.lbrynet_daemon.auth.util import APIKey, get_auth_message
|
||||||
from lbrynet.lbrynet_daemon.auth.client import LBRY_SECRET
|
from lbrynet.lbrynet_daemon.auth.client import LBRY_SECRET
|
||||||
|
from lbrynet.undecorated import undecorated
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -338,12 +338,16 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
|
|
||||||
if args == EMPTY_PARAMS or args == []:
|
if args == EMPTY_PARAMS or args == []:
|
||||||
args_dict = {}
|
args_dict = {}
|
||||||
|
_args, _kwargs = (), {}
|
||||||
elif isinstance(args, dict):
|
elif isinstance(args, dict):
|
||||||
args_dict = args
|
args_dict = args
|
||||||
elif len(args) == 1 and isinstance(args[0], dict):
|
elif len(args) == 1 and isinstance(args[0], dict):
|
||||||
# TODO: this is for backwards compatibility. Remove this once API and UI are updated
|
# TODO: this is for backwards compatibility. Remove this once API and UI are updated
|
||||||
# TODO: also delete EMPTY_PARAMS then
|
# TODO: also delete EMPTY_PARAMS then
|
||||||
args_dict = args[0]
|
args_dict = args[0]
|
||||||
|
_args, _kwargs = (), args
|
||||||
|
elif isinstance(args, list):
|
||||||
|
_args, _kwargs = args, {}
|
||||||
else:
|
else:
|
||||||
# d = defer.maybeDeferred(function, *args) # if we want to support positional args too
|
# d = defer.maybeDeferred(function, *args) # if we want to support positional args too
|
||||||
raise ValueError('Args must be a dict')
|
raise ValueError('Args must be a dict')
|
||||||
|
@ -400,28 +404,6 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
(utils.now() - time_in).total_seconds()))
|
(utils.now() - time_in).total_seconds()))
|
||||||
return server.NOT_DONE_YET
|
return server.NOT_DONE_YET
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _check_params(function, args_dict):
|
|
||||||
argspec = inspect.getargspec(undecorated(function))
|
|
||||||
num_optional_params = 0 if argspec.defaults is None else len(argspec.defaults)
|
|
||||||
missing_required_params = [
|
|
||||||
required_param
|
|
||||||
for required_param in argspec.args[1:-num_optional_params]
|
|
||||||
if required_param not in args_dict
|
|
||||||
]
|
|
||||||
if len(missing_required_params):
|
|
||||||
return 'Missing required parameters', missing_required_params
|
|
||||||
|
|
||||||
extraneous_params = [] if argspec.keywords is not None else [
|
|
||||||
extra_param
|
|
||||||
for extra_param in args_dict
|
|
||||||
if extra_param not in argspec.args[1:]
|
|
||||||
]
|
|
||||||
if len(extraneous_params):
|
|
||||||
return 'Extraneous parameters', extraneous_params
|
|
||||||
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
def _register_user_session(self, session_id):
|
def _register_user_session(self, session_id):
|
||||||
"""
|
"""
|
||||||
Add or update a HMAC secret for a session
|
Add or update a HMAC secret for a session
|
||||||
|
@ -502,6 +484,28 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
self._verify_method_is_callable(function_path)
|
self._verify_method_is_callable(function_path)
|
||||||
return self.callable_methods.get(function_path)
|
return self.callable_methods.get(function_path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _check_params(function, args_dict):
|
||||||
|
argspec = inspect.getargspec(undecorated(function))
|
||||||
|
num_optional_params = 0 if argspec.defaults is None else len(argspec.defaults)
|
||||||
|
missing_required_params = [
|
||||||
|
required_param
|
||||||
|
for required_param in argspec.args[1:-num_optional_params]
|
||||||
|
if required_param not in args_dict
|
||||||
|
]
|
||||||
|
if len(missing_required_params):
|
||||||
|
return 'Missing required parameters', missing_required_params
|
||||||
|
|
||||||
|
extraneous_params = [] if argspec.keywords is not None else [
|
||||||
|
extra_param
|
||||||
|
for extra_param in args_dict
|
||||||
|
if extra_param not in argspec.args[1:]
|
||||||
|
]
|
||||||
|
if len(extraneous_params):
|
||||||
|
return 'Extraneous parameters', extraneous_params
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
def _initialize_session(self, session_id):
|
def _initialize_session(self, session_id):
|
||||||
if not self.sessions.get(session_id, False):
|
if not self.sessions.get(session_id, False):
|
||||||
self._register_user_session(session_id)
|
self._register_user_session(session_id)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
Twisted==16.6.0
|
Twisted==16.6.0
|
||||||
appdirs==1.4.3
|
appdirs==1.4.3
|
||||||
argparse==1.2.1
|
argparse==1.2.1
|
||||||
|
docopt==0.6.2
|
||||||
base58==0.2.2
|
base58==0.2.2
|
||||||
git+https://github.com/lbryio/bumpversion.git#egg=bumpversion
|
git+https://github.com/lbryio/bumpversion.git#egg=bumpversion
|
||||||
colorama==0.3.7
|
colorama==0.3.7
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -31,6 +31,7 @@ requires = [
|
||||||
'seccure',
|
'seccure',
|
||||||
'txJSON-RPC',
|
'txJSON-RPC',
|
||||||
'zope.interface',
|
'zope.interface',
|
||||||
|
'docopt'
|
||||||
]
|
]
|
||||||
|
|
||||||
console_scripts = [
|
console_scripts = [
|
||||||
|
|
|
@ -16,23 +16,3 @@ class DaemonCLITests(unittest.TestCase):
|
||||||
self.assertEqual(False, DaemonCLI.guess_type('false'))
|
self.assertEqual(False, DaemonCLI.guess_type('false'))
|
||||||
self.assertEqual(False, DaemonCLI.guess_type('False'))
|
self.assertEqual(False, DaemonCLI.guess_type('False'))
|
||||||
|
|
||||||
def test_get_params(self):
|
|
||||||
test_params = [
|
|
||||||
'b64address=VdNmakxFORPSyfCprAD/eDDPk5TY9QYtSA==',
|
|
||||||
'name=test',
|
|
||||||
'amount=5.3',
|
|
||||||
'n=5',
|
|
||||||
'address=bY13xeAjLrsjP4KGETwStK2a9UgKgXVTXu',
|
|
||||||
't=true',
|
|
||||||
'f=False',
|
|
||||||
]
|
|
||||||
test_r = {
|
|
||||||
'b64address': 'VdNmakxFORPSyfCprAD/eDDPk5TY9QYtSA==',
|
|
||||||
'name': 'test',
|
|
||||||
'amount': 5.3,
|
|
||||||
'n': 5,
|
|
||||||
'address': 'bY13xeAjLrsjP4KGETwStK2a9UgKgXVTXu',
|
|
||||||
't': True,
|
|
||||||
'f': False,
|
|
||||||
}
|
|
||||||
self.assertDictEqual(test_r, DaemonCLI.get_params_from_kwargs(test_params))
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue