forked from LBRYCommunity/lbry-sdk
Merge pull request #652 from lbryio/arg-parsing
Parse cli args from docstrings and add positional arguments
This commit is contained in:
commit
84bac4bab7
9 changed files with 600 additions and 318 deletions
|
@ -40,4 +40,5 @@ script:
|
|||
- pip install mock pylint unqlite
|
||||
- pylint lbrynet
|
||||
- PYTHONPATH=. trial tests
|
||||
- python -m unittest discover tests/integration
|
||||
- rvm use 2.3.1 && gem install danger --version '~> 4.0' && danger
|
||||
|
|
|
@ -12,10 +12,12 @@ at anytime.
|
|||
* Prevent publish of files with size 0
|
||||
* Add `dht_status` parameter to `status` to include bandwidth and peer info
|
||||
*
|
||||
* Positional and flag arguments in lbrynet-cli
|
||||
*
|
||||
|
||||
### Changed
|
||||
*
|
||||
*
|
||||
* Changed keyword arguments in lbrynet-cli to use a -- prefix
|
||||
* Using the help function in lbrynet-cli no longer requires lbrynet-daemon to be running
|
||||
|
||||
### Fixed
|
||||
*
|
||||
|
|
|
@ -990,18 +990,19 @@ class Daemon(AuthJSONRPCServer):
|
|||
############################################################################
|
||||
|
||||
@defer.inlineCallbacks
|
||||
@AuthJSONRPCServer.flags(session_status="-s", dht_status="-d")
|
||||
def jsonrpc_status(self, session_status=False, dht_status=False):
|
||||
"""
|
||||
Return daemon status
|
||||
Get daemon status
|
||||
|
||||
Args:
|
||||
'session_status' (optional): (bool) true to return session status,
|
||||
default is false
|
||||
'dht_status' (optional): (bool) true to return dht status,
|
||||
default is false
|
||||
Returns:
|
||||
(dict) Daemon status dictionary
|
||||
Usage:
|
||||
status [-s] [-d]
|
||||
|
||||
Options:
|
||||
-s : include session status in results
|
||||
-d : include dht network and peer status
|
||||
"""
|
||||
|
||||
# on startup, the wallet or network won't be available but we still need this call to work
|
||||
has_wallet = self.session and self.session.wallet and self.session.wallet.network
|
||||
local_height = self.session.wallet.network.get_local_height() if has_wallet else 0
|
||||
|
@ -1042,23 +1043,28 @@ class Daemon(AuthJSONRPCServer):
|
|||
response['dht_status'] = self.session.dht_node.get_bandwidth_stats()
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('status')
|
||||
def jsonrpc_get_best_blockhash(self):
|
||||
"""
|
||||
DEPRECATED. Use `status blockchain_status=True` instead
|
||||
"""
|
||||
|
||||
d = self.jsonrpc_status()
|
||||
d.addCallback(lambda x: self._render_response(
|
||||
x['blockchain_status']['best_blockhash']))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('status')
|
||||
def jsonrpc_is_running(self):
|
||||
"""
|
||||
DEPRECATED. Use `status` instead
|
||||
"""
|
||||
|
||||
d = self.jsonrpc_status()
|
||||
d.addCallback(lambda x: self._render_response(x['is_running']))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('status')
|
||||
def jsonrpc_daemon_status(self):
|
||||
"""
|
||||
DEPRECATED. Use `status` instead
|
||||
|
@ -1094,14 +1100,17 @@ class Daemon(AuthJSONRPCServer):
|
|||
d.addCallback(lambda x: self._render_response(x)) # is this necessary?
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('status')
|
||||
def jsonrpc_is_first_run(self):
|
||||
"""
|
||||
DEPRECATED. Use `status` instead
|
||||
"""
|
||||
|
||||
d = self.jsonrpc_status()
|
||||
d.addCallback(lambda x: self._render_response(x['is_first_run']))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('status')
|
||||
def jsonrpc_get_lbry_session_info(self):
|
||||
"""
|
||||
DEPRECATED. Use `status` instead
|
||||
|
@ -1115,10 +1124,12 @@ class Daemon(AuthJSONRPCServer):
|
|||
}))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('status')
|
||||
def jsonrpc_get_time_behind_blockchain(self):
|
||||
"""
|
||||
DEPRECATED. Use `status` instead
|
||||
"""
|
||||
|
||||
d = self.jsonrpc_status()
|
||||
d.addCallback(lambda x: self._render_response(x['blockchain_status']['blocks_behind']))
|
||||
return d
|
||||
|
@ -1127,8 +1138,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get lbry version information
|
||||
|
||||
Args:
|
||||
None
|
||||
Usage:
|
||||
version
|
||||
|
||||
Returns:
|
||||
(dict) Dictionary of lbry version information
|
||||
{
|
||||
|
@ -1153,8 +1165,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Report a bug to slack
|
||||
|
||||
Args:
|
||||
'message': (str) message to send
|
||||
Usage:
|
||||
report_bug (<message> | --message=<message>)
|
||||
|
||||
Returns:
|
||||
(bool) true if successful
|
||||
"""
|
||||
|
@ -1168,6 +1181,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
)
|
||||
return self._render_response(True)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('settings_get')
|
||||
def jsonrpc_get_settings(self):
|
||||
"""
|
||||
DEPRECATED. Use `settings_get` instead.
|
||||
|
@ -1178,12 +1192,16 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get daemon settings
|
||||
|
||||
Usage:
|
||||
settings_get
|
||||
|
||||
Returns:
|
||||
(dict) Dictionary of daemon settings
|
||||
See ADJUSTABLE_SETTINGS in lbrynet/conf.py for full list of settings
|
||||
"""
|
||||
return self._render_response(conf.settings.get_adjustable_settings_dict())
|
||||
|
||||
@AuthJSONRPCServer.deprecated('settings_set')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_set_settings(self, **kwargs):
|
||||
"""
|
||||
|
@ -1218,11 +1236,11 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Return a useful message for an API command
|
||||
|
||||
Args:
|
||||
'command'(optional): (str) command to retrieve documentation for
|
||||
Returns:
|
||||
(str) if given a command, returns documentation about that command
|
||||
otherwise returns general help message
|
||||
Usage:
|
||||
help [<command> | --command=<command>]
|
||||
|
||||
Options:
|
||||
<command>, --command=<command> : command to retrieve documentation for
|
||||
"""
|
||||
|
||||
if command is None:
|
||||
|
@ -1248,28 +1266,32 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Return a list of available commands
|
||||
|
||||
Usage:
|
||||
commands
|
||||
|
||||
Returns:
|
||||
(list) list of available commands
|
||||
"""
|
||||
return self._render_response(sorted(
|
||||
[command for command in self.callable_methods.keys()
|
||||
if 'DEPRECATED' not in getattr(self, "jsonrpc_" + command).__doc__]
|
||||
))
|
||||
return self._render_response(sorted([command for command in self.callable_methods.keys()]))
|
||||
|
||||
@AuthJSONRPCServer.deprecated('wallet_balance')
|
||||
def jsonrpc_get_balance(self, address=None, include_unconfirmed=False):
|
||||
"""
|
||||
DEPRECATED. Use `wallet_balance` instead.
|
||||
"""
|
||||
return self.jsonrpc_wallet_balance(address, include_unconfirmed)
|
||||
|
||||
@AuthJSONRPCServer.flags(include_unconfirmed='-u')
|
||||
def jsonrpc_wallet_balance(self, address=None, include_unconfirmed=False):
|
||||
"""
|
||||
Return the balance of the wallet
|
||||
|
||||
Args:
|
||||
'address' (optional): If address is provided only that balance will be given
|
||||
'include_unconfirmed' (optional): If set unconfirmed balance will be included in
|
||||
the only takes effect when address is also provided.
|
||||
Usage:
|
||||
wallet_balance [<address> | --address=<address>] [-u]
|
||||
|
||||
Options:
|
||||
<address> : If provided only the balance for this address will be given
|
||||
-u : Include unconfirmed
|
||||
|
||||
Returns:
|
||||
(float) amount of lbry credits in wallet
|
||||
|
@ -1280,6 +1302,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
return self._render_response(float(
|
||||
self.session.wallet.get_address_balance(address, include_unconfirmed)))
|
||||
|
||||
@AuthJSONRPCServer.deprecated('daemon_stop')
|
||||
def jsonrpc_stop(self):
|
||||
"""
|
||||
DEPRECATED. Use `daemon_stop` instead.
|
||||
|
@ -1291,6 +1314,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Stop lbrynet-daemon
|
||||
|
||||
Usage:
|
||||
daemon_stop
|
||||
|
||||
Returns:
|
||||
(string) Shutdown message
|
||||
"""
|
||||
|
@ -1301,19 +1327,27 @@ class Daemon(AuthJSONRPCServer):
|
|||
defer.returnValue(response)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
@AuthJSONRPCServer.flags(full_status='-f')
|
||||
def jsonrpc_file_list(self, **kwargs):
|
||||
"""
|
||||
List files limited by optional filters
|
||||
|
||||
Args:
|
||||
'name' (optional): (str) filter files by lbry name,
|
||||
'sd_hash' (optional): (str) filter files by sd hash,
|
||||
'file_name' (optional): (str) filter files by the name in the downloads folder,
|
||||
'stream_hash' (optional): (str) filter files by stream hash,
|
||||
'claim_id' (optional): (str) filter files by claim id,
|
||||
'outpoint' (optional): (str) filter files by claim outpoint,
|
||||
'rowid' (optional): (int) filter files by internal row id,
|
||||
'full_status': (optional): (bool) if true populate the 'message' and 'size' fields
|
||||
Usage:
|
||||
file_list [--sd_hash=<sd_hash>] [--file_name=<file_name>] [--stream_hash=<stream_hash>]
|
||||
[--claim_id=<claim_id>] [--outpoint=<outpoint>] [--rowid=<rowid>]
|
||||
[--name=<name>]
|
||||
[-f]
|
||||
|
||||
Options:
|
||||
--sd_hash=<sd_hash> : get file with matching sd hash
|
||||
--file_name=<file_name> : get file with matching file name in the
|
||||
downloads folder
|
||||
--stream_hash=<stream_hash> : get file with matching stream hash
|
||||
--claim_id=<claim_id> : get file with matching claim id
|
||||
--outpoint=<outpoint> : get file with matching claim outpoint
|
||||
--rowid=<rowid> : get file with matching row id
|
||||
--name=<name> : get file with matching associated name claim
|
||||
-f : full status, populate the 'message' and 'size' fields
|
||||
|
||||
Returns:
|
||||
(list) List of files
|
||||
|
@ -1348,12 +1382,17 @@ class Daemon(AuthJSONRPCServer):
|
|||
defer.returnValue(response)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
@AuthJSONRPCServer.flags(force='-f')
|
||||
def jsonrpc_resolve_name(self, name, force=False):
|
||||
"""
|
||||
Resolve stream info from a LBRY name
|
||||
|
||||
Args:
|
||||
'name': (str) name to look up, do not include lbry:// prefix
|
||||
Usage:
|
||||
resolve_name <name> [-f]
|
||||
|
||||
Options:
|
||||
-f : force refresh and do not check cache
|
||||
|
||||
Returns:
|
||||
(dict) Metadata dictionary from name claim, None if the name is not
|
||||
resolvable
|
||||
|
@ -1367,6 +1406,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
else:
|
||||
defer.returnValue(metadata)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('claim_show')
|
||||
def jsonrpc_get_claim_info(self, **kwargs):
|
||||
"""
|
||||
DEPRECATED. Use `claim_show` instead.
|
||||
|
@ -1375,15 +1415,18 @@ class Daemon(AuthJSONRPCServer):
|
|||
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_claim_show(self, name=None, txid=None, nout=None, claim_id=None):
|
||||
|
||||
"""
|
||||
Resolve claim info from a LBRY name
|
||||
|
||||
Args:
|
||||
'name': (str) name to look up, do not include lbry:// prefix
|
||||
'txid'(optional): (str) if specified, look for claim with this txid
|
||||
'nout'(optional): (int) if specified, look for claim with this nout
|
||||
'claim_id'(optional): (str) if specified, look for claim with this claim_id
|
||||
Usage:
|
||||
claim_show <name> [<txid> | --txid=<txid>] [<nout> | --nout=<nout>]
|
||||
[<claim_id> | --claim_id=<claim_id>]
|
||||
|
||||
Options:
|
||||
<txid>, --txid=<txid> : look for claim with this txid
|
||||
<nout>, --nout=<nout> : look for claim with this nout
|
||||
<claim_id>, --claim_id=<claim_id> : look for claim with this claim id
|
||||
|
||||
Returns:
|
||||
(dict) Dictionary contaning claim info, (bool) false if claim is not
|
||||
resolvable
|
||||
|
@ -1414,13 +1457,17 @@ class Daemon(AuthJSONRPCServer):
|
|||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
@defer.inlineCallbacks
|
||||
@AuthJSONRPCServer.flags(force='-f')
|
||||
def jsonrpc_resolve(self, uri, force=False):
|
||||
"""
|
||||
Resolve a LBRY URI
|
||||
|
||||
Args:
|
||||
'uri': (str) uri to download
|
||||
'force'(optional): (boolean) set to true to ignore cache and force refresh
|
||||
Usage:
|
||||
resolve <uri> [-f]
|
||||
|
||||
Options:
|
||||
-f : force refresh and ignore cache
|
||||
|
||||
Returns:
|
||||
None if nothing can be resolved, otherwise:
|
||||
If uri resolves to a channel or a claim in a channel:
|
||||
|
@ -1502,11 +1549,15 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Download stream from a LBRY name.
|
||||
|
||||
Args:
|
||||
'uri': (str) lbry uri to download
|
||||
'file_name'(optional): (str) a user specified name for the downloaded file
|
||||
'timeout'(optional): (int) download timeout in number of seconds
|
||||
'download_directory'(optional): (str) path to directory where file will be saved
|
||||
Usage:
|
||||
get <uri> [<file_name> | --file_name=<file_name>] [<timeout> | --timeout=<timeout>]
|
||||
[<download_directory> | --download_directory=<download_directory>]
|
||||
|
||||
Options:
|
||||
<file_name> : specified name for the downloaded file
|
||||
<timeout> : download timeout in number of seconds
|
||||
<download_directory> : path to directory where file will be saved
|
||||
|
||||
Returns:
|
||||
(dict) Dictionary contaning information about the stream
|
||||
{
|
||||
|
@ -1568,6 +1619,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
response = yield self._render_response(result)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('file_set_status')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_stop_lbry_file(self, **kwargs):
|
||||
"""
|
||||
|
@ -1575,6 +1627,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
return self.jsonrpc_file_set_status(status='stop', **kwargs)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('file_set_status')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_start_lbry_file(self, **kwargs):
|
||||
"""
|
||||
|
@ -1588,11 +1641,22 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Start or stop downloading a file
|
||||
|
||||
Args:
|
||||
'status': (str) "start" or "stop"
|
||||
'name' (optional): (str) start file by lbry name,
|
||||
'sd_hash' (optional): (str) start file by the hash in the name claim,
|
||||
'file_name' (optional): (str) start file by its name in the downloads folder,
|
||||
Usage:
|
||||
file_set_status <status> [--sd_hash=<sd_hash>] [--file_name=<file_name>]
|
||||
[--stream_hash=<stream_hash>] [--claim_id=<claim_id>]
|
||||
[--outpoint=<outpoint>] [--rowid=<rowid>]
|
||||
[--name=<name>]
|
||||
|
||||
Options:
|
||||
--sd_hash=<sd_hash> : set status of file with matching sd hash
|
||||
--file_name=<file_name> : set status of file with matching file name in the
|
||||
downloads folder
|
||||
--stream_hash=<stream_hash> : set status of file with matching stream hash
|
||||
--claim_id=<claim_id> : set status of file with matching claim id
|
||||
--outpoint=<outpoint> : set status of file with matching claim outpoint
|
||||
--rowid=<rowid> : set status of file with matching row id
|
||||
--name=<name> : set status of file with matching associated name claim
|
||||
|
||||
Returns:
|
||||
(str) Confirmation message
|
||||
"""
|
||||
|
@ -1618,21 +1682,28 @@ class Daemon(AuthJSONRPCServer):
|
|||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
@defer.inlineCallbacks
|
||||
@AuthJSONRPCServer.flags(delete_target_file='-f', delete_all='-a')
|
||||
def jsonrpc_file_delete(self, delete_target_file=True, delete_all=False, **kwargs):
|
||||
"""
|
||||
Delete a lbry file
|
||||
Delete a LBRY file
|
||||
|
||||
Usage:
|
||||
file_delete [-a | -f] [--sd_hash=<sd_hash>] [--file_name=<file_name>]
|
||||
[--stream_hash=<stream_hash>] [--claim_id=<claim_id>]
|
||||
[--outpoint=<outpoint>] [--rowid=<rowid>]
|
||||
[--name=<name>]
|
||||
|
||||
Options:
|
||||
-a : delete file from downloads and delete stored blobs
|
||||
-f : delete only from downloads, do not delete blobs
|
||||
--sd_hash=<sd_hash> : delete by file sd hash
|
||||
--file_name<file_name> : delete by file name in downloads folder
|
||||
--stream_hash=<stream_hash> : delete by file stream hash
|
||||
--claim_id=<claim_id> : delete by file claim id
|
||||
--outpoint=<outpoint> : delete by file claim outpoint
|
||||
--rowid=<rowid> : delete by file row id
|
||||
--name=<name> : delete by associated name claim of file
|
||||
|
||||
Args:
|
||||
'name' (optional): (str) delete file by lbry name,
|
||||
'sd_hash' (optional): (str) delete file by sd hash,
|
||||
'file_name' (optional): (str) delete file by the name in the downloads folder,
|
||||
'stream_hash' (optional): (str) delete file by stream hash,
|
||||
'claim_id' (optional): (str) delete file by claim ID,
|
||||
'outpoint' (optional): (str) delete file by claim outpoint,
|
||||
'rowid': (optional): (int) delete file by rowid in the file manager
|
||||
'delete_target_file' (optional): (bool) delete file from downloads folder,
|
||||
defaults to true if false only the blobs and
|
||||
db entries will be deleted
|
||||
Returns:
|
||||
(bool) true if deletion was successful
|
||||
"""
|
||||
|
@ -1663,6 +1734,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
response = yield self._render_response(result)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('stream_cost_estimate')
|
||||
def jsonrpc_get_est_cost(self, **kwargs):
|
||||
"""
|
||||
DEPRECATED. Use `stream_cost_estimate` instead
|
||||
|
@ -1674,10 +1746,13 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get estimated cost for a lbry stream
|
||||
|
||||
Args:
|
||||
'name': (str) lbry name
|
||||
'size' (optional): (int) stream size, in bytes. if provided an sd blob
|
||||
won't be downloaded.
|
||||
Usage:
|
||||
stream_cost_estimate <uri> [<size> | --size=<size>]
|
||||
|
||||
Options:
|
||||
<size>, --size=<size> : stream size in bytes. if provided an sd blob won't be
|
||||
downloaded.
|
||||
|
||||
Returns:
|
||||
(float) Estimated cost in lbry credits, returns None if uri is not
|
||||
resolveable
|
||||
|
@ -1690,11 +1765,11 @@ class Daemon(AuthJSONRPCServer):
|
|||
@defer.inlineCallbacks
|
||||
def jsonrpc_channel_new(self, channel_name, amount):
|
||||
"""
|
||||
Generate a publisher key and create a new certificate claim
|
||||
Generate a publisher key and create a new '@' prefixed certificate claim
|
||||
|
||||
Args:
|
||||
'channel_name': (str) '@' prefixed name
|
||||
'amount': (float) amount to claim name
|
||||
Usage:
|
||||
channel_new (<channel_name> | --channel_name=<channel_name>)
|
||||
(<amount> | --amount=<amount>)
|
||||
|
||||
Returns:
|
||||
(dict) Dictionary containing result of the claim
|
||||
|
@ -1732,6 +1807,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get my channels
|
||||
|
||||
Usage:
|
||||
channel_list_mine
|
||||
|
||||
Returns:
|
||||
(list) ClaimDict
|
||||
"""
|
||||
|
@ -1756,38 +1834,48 @@ class Daemon(AuthJSONRPCServer):
|
|||
'description'
|
||||
'author'
|
||||
'language'
|
||||
'license',
|
||||
'license'
|
||||
'nsfw'
|
||||
|
||||
Metadata can be set by either using the metadata argument or by setting individual arguments
|
||||
fee, title, description, author, language, license, license_url, thumbnail, preview, nsfw,
|
||||
or sources. Individual arguments will overwrite the fields specified in metadata argument.
|
||||
|
||||
Args:
|
||||
'name': (str) name to be claimed
|
||||
'bid': (float) amount of credits to commit in this claim,
|
||||
'metadata'(optional): (dict) Metadata to associate with the claim.
|
||||
'file_path'(optional): (str) path to file to be associated with name. If provided,
|
||||
Usage:
|
||||
publish (<name> | --name=<name>) (<bid> | --bid=<bid>) [--metadata=<metadata>]
|
||||
[--file_path=<file_path>] [--fee=<fee>] [--title=<title>]
|
||||
[--description=<description>] [--author=<author>] [--language=<language>]
|
||||
[--license=<license>] [--license_url=<license_url>] [--thumbnail=<thumbnail>]
|
||||
[--preview=<preview>] [--nsfw=<nsfw>] [--sources=<sources>]
|
||||
[--channel_name=<channel_name>] [--channel_id=<channel_id>]
|
||||
|
||||
Options:
|
||||
--metadata=<metadata> : ClaimDict to associate with the claim.
|
||||
--file_path=<file_path> : path to file to be associated with name. If provided,
|
||||
a lbry stream of this file will be used in 'sources'.
|
||||
If no path is given but a metadata dict is provided, the source
|
||||
from the given metadata will be used.
|
||||
'fee'(optional): (dict) Dictionary representing key fee to download content:
|
||||
{currency_symbol: {'amount': float, 'address': str, optional}}
|
||||
If no path is given but a metadata dict is provided,
|
||||
the source from the given metadata will be used.
|
||||
--fee=<fee> : Dictionary representing key fee to download content:
|
||||
{currency_symbol: {'amount': float,
|
||||
'address': str, optional}}
|
||||
supported currencies: LBC, USD, BTC
|
||||
If an address is not provided a new one will be automatically
|
||||
generated. Default fee is zero.
|
||||
'title'(optional): (str) title of the file
|
||||
'description'(optional): (str) description of the file
|
||||
'author'(optional): (str) author of the file
|
||||
'language'(optional): (str), language code
|
||||
'license'(optional): (str) license for the file
|
||||
'license_url'(optional): (str) URL to license
|
||||
'thumbnail'(optional): (str) thumbnail URL for the file
|
||||
'preview'(optional): (str) preview URL for the file
|
||||
'nsfw'(optional): (bool) True if not safe for work
|
||||
'sources'(optional): (dict){'lbry_sd_hash':sd_hash} specifies sd hash of file
|
||||
'channel_name' (optional): (str) name of the publisher channel
|
||||
'channel_id' (optional): (str) claim id of the publisher channel
|
||||
If an address is not provided a new one will be
|
||||
automatically generated. Default fee is zero.
|
||||
--title=<title> : title of the publication
|
||||
--description=<description> : description of the publication
|
||||
--author=<author> : author of the publication
|
||||
--language=<language> : language of the publication
|
||||
--license=<license> : publication license
|
||||
--license_url=<license_url> : publication license url
|
||||
--thumbnail=<thumbnail> : thumbnail url
|
||||
--preview=<preview> : preview url
|
||||
--nsfw=<nsfw> : title of the publication
|
||||
--sources=<sources> : {'lbry_sd_hash':sd_hash} specifies sd hash of file
|
||||
--channel_name=<channel_name> : name of the publisher channel name in the wallet
|
||||
--channel_id=<channel_id> : claim id of the publisher channel, does not check
|
||||
for channel claim being in the wallet. This allows
|
||||
publishing to a channel where only the certificate
|
||||
private key is in the wallet.
|
||||
|
||||
Returns:
|
||||
(dict) Dictionary containing result of the claim
|
||||
|
@ -1883,6 +1971,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
response = yield self._render_response(result)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('claim_abandon')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_abandon_claim(self, **kwargs):
|
||||
"""
|
||||
|
@ -1896,8 +1985,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Abandon a name and reclaim credits from the claim
|
||||
|
||||
Args:
|
||||
'claim_id': (str) claim_id of claim
|
||||
Usage:
|
||||
claim_abandon (<claim_id> | --claim_id=<claim_id>)
|
||||
|
||||
Return:
|
||||
(dict) Dictionary containing result of the claim
|
||||
{
|
||||
|
@ -1919,18 +2009,22 @@ class Daemon(AuthJSONRPCServer):
|
|||
response = yield self._render_response(err)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('claim_abandon')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_abandon_name(self, **kwargs):
|
||||
"""
|
||||
DEPRECATED. Use `claim_abandon` instead
|
||||
"""
|
||||
|
||||
return self.jsonrpc_claim_abandon(**kwargs)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('claim_support_new')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_support_claim(self, **kwargs):
|
||||
"""
|
||||
DEPRECATED. Use `claim_abandon` instead
|
||||
DEPRECATED. Use `claim_support_new` instead
|
||||
"""
|
||||
|
||||
return self.jsonrpc_claim_new_support(**kwargs)
|
||||
|
||||
@AuthJSONRPCServer.auth_required
|
||||
|
@ -1939,10 +2033,10 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Support a name claim
|
||||
|
||||
Args:
|
||||
'name': (str) name
|
||||
'claim_id': (str) claim ID of claim to support
|
||||
'amount': (float) amount to support by
|
||||
Usage:
|
||||
claim_new_support (<name> | --name=<name>) (<claim_id> | --claim_id=<claim_id>)
|
||||
(<amount> | --amount<amount>)
|
||||
|
||||
Return:
|
||||
(dict) Dictionary containing result of the claim
|
||||
{
|
||||
|
@ -1956,7 +2050,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
self.analytics_manager.send_claim_action('new_support')
|
||||
defer.returnValue(result)
|
||||
|
||||
# TODO: merge this into claim_list
|
||||
@AuthJSONRPCServer.deprecated('claim_list_mine')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_get_my_claim(self, name):
|
||||
"""
|
||||
|
@ -1974,6 +2068,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
d.addCallback(lambda r: self._render_response(r))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('claim_list_mine')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_get_name_claims(self):
|
||||
"""
|
||||
|
@ -1987,8 +2082,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
List my name claims
|
||||
|
||||
Args:
|
||||
None
|
||||
Usage:
|
||||
claim_list_mine
|
||||
|
||||
Returns
|
||||
(list) List of name claims owned by user
|
||||
[
|
||||
|
@ -2016,12 +2112,14 @@ class Daemon(AuthJSONRPCServer):
|
|||
d.addCallback(lambda claims: self._render_response(claims))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('claim_list')
|
||||
def jsonrpc_get_claims_for_name(self, **kwargs):
|
||||
"""
|
||||
DEPRECATED. Use `claim_list` instead.
|
||||
"""
|
||||
return self.jsonrpc_claim_list(**kwargs)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('claim_list')
|
||||
def jsonrpc_get_claims_for_tx(self, **kwargs):
|
||||
"""
|
||||
DEPRECATED. Use `claim_list` instead.
|
||||
|
@ -2033,8 +2131,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get claims for a name
|
||||
|
||||
Args:
|
||||
'name': (str) search for claims on this name
|
||||
Usage:
|
||||
claim_list (<name> | --name=<name>)
|
||||
|
||||
Returns
|
||||
(dict) State of claims assigned for the name
|
||||
{
|
||||
|
@ -2060,6 +2159,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
claims = yield self.session.wallet.get_claims_for_name(name)
|
||||
defer.returnValue(claims)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('transaction_list')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_get_transaction_history(self):
|
||||
"""
|
||||
|
@ -2072,8 +2172,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
List transactions belonging to wallet
|
||||
|
||||
Args:
|
||||
None
|
||||
Usage:
|
||||
transaction_list
|
||||
|
||||
Returns:
|
||||
(list) List of transactions
|
||||
"""
|
||||
|
@ -2082,6 +2183,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
d.addCallback(lambda r: self._render_response(r))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('transaction_show')
|
||||
def jsonrpc_get_transaction(self, txid):
|
||||
"""
|
||||
DEPRECATED. Use `transaction_show` instead
|
||||
|
@ -2092,8 +2194,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get a decoded transaction from a txid
|
||||
|
||||
Args:
|
||||
'txid': (str) txid of transaction
|
||||
Usage:
|
||||
transaction_show (<txid> | --txid=<txid>)
|
||||
|
||||
Returns:
|
||||
(dict) JSON formatted transaction
|
||||
"""
|
||||
|
@ -2102,6 +2205,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
d.addCallback(lambda r: self._render_response(r))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('wallet_is_address_mine')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_address_is_mine(self, address):
|
||||
"""
|
||||
|
@ -2114,8 +2218,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Checks if an address is associated with the current wallet.
|
||||
|
||||
Args:
|
||||
'address': (str) address to check in base58
|
||||
Usage:
|
||||
wallet_is_address_mine (<address> | --address=<address>)
|
||||
|
||||
Returns:
|
||||
(bool) true, if address is associated with current wallet
|
||||
"""
|
||||
|
@ -2124,10 +2229,11 @@ class Daemon(AuthJSONRPCServer):
|
|||
d.addCallback(lambda is_mine: self._render_response(is_mine))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('wallet_public_key')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_get_public_key_from_wallet(self, wallet):
|
||||
"""
|
||||
DEPRECATED. Use `wallet_is_address_mine` instead
|
||||
DEPRECATED. Use `wallet_public_key` instead
|
||||
"""
|
||||
return self.jsonrpc_wallet_public_key(wallet)
|
||||
|
||||
|
@ -2136,8 +2242,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get public key from wallet address
|
||||
|
||||
Args:
|
||||
'address': (str) wallet address in base58
|
||||
Usage:
|
||||
wallet_public_key (<address> | --address=<address>)
|
||||
|
||||
Returns:
|
||||
(list) list of public keys associated with address.
|
||||
Could contain more than one public key if multisig.
|
||||
|
@ -2153,8 +2260,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
List wallet addresses
|
||||
|
||||
Args:
|
||||
None
|
||||
Usage:
|
||||
wallet_list
|
||||
|
||||
Returns:
|
||||
List of wallet addresses
|
||||
"""
|
||||
|
@ -2163,6 +2271,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
response = yield self._render_response(addresses)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('wallet_new_address')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_get_new_address(self):
|
||||
"""
|
||||
|
@ -2175,8 +2284,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Generate a new wallet address
|
||||
|
||||
Args:
|
||||
None
|
||||
Usage:
|
||||
wallet_new_address
|
||||
|
||||
Returns:
|
||||
(str) New wallet address in base58
|
||||
"""
|
||||
|
@ -2196,8 +2306,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
Return an address containing no balance, will create
|
||||
a new address if there is none.
|
||||
|
||||
Args:
|
||||
None
|
||||
Usage:
|
||||
wallet_unused_address
|
||||
|
||||
Returns:
|
||||
(str) Unused wallet address in base58
|
||||
"""
|
||||
|
@ -2215,11 +2326,11 @@ class Daemon(AuthJSONRPCServer):
|
|||
@defer.inlineCallbacks
|
||||
def jsonrpc_send_amount_to_address(self, amount, address):
|
||||
"""
|
||||
Send credits to an address
|
||||
Queue a payment of credits to an address
|
||||
|
||||
Usage:
|
||||
send_amount_to_address (<amount> | --amount=<amount>) (<address> | --address=<address>)
|
||||
|
||||
Args:
|
||||
'amount': (float) the amount to send
|
||||
'address': (str) the address of the recipient in base58
|
||||
Returns:
|
||||
(bool) true if payment successfully scheduled
|
||||
"""
|
||||
|
@ -2231,6 +2342,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
self.analytics_manager.send_credits_sent()
|
||||
defer.returnValue(True)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('block_show')
|
||||
def jsonrpc_get_block(self, **kwargs):
|
||||
"""
|
||||
DEPRECATED. Use `block_show` instead
|
||||
|
@ -2241,8 +2353,13 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get contents of a block
|
||||
|
||||
Args:
|
||||
'blockhash': (str) hash of the block to look up
|
||||
Usage:
|
||||
block_show (<blockhash> | --blockhash=<blockhash>) | (<height> | --height=<height>)
|
||||
|
||||
Options:
|
||||
<blockhash>, --blockhash=<blockhash> : hash of the block to look up
|
||||
<height>, --height=<height> : height of the block to look up
|
||||
|
||||
Returns:
|
||||
(dict) Requested block
|
||||
"""
|
||||
|
@ -2259,6 +2376,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
d.addCallback(lambda r: self._render_response(r))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('descriptor_get')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_download_descriptor(self, **kwargs):
|
||||
"""
|
||||
|
@ -2266,6 +2384,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
return self.jsonrpc_descriptor_get(**kwargs)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('blob_get')
|
||||
@AuthJSONRPCServer.auth_required
|
||||
def jsonrpc_descriptor_get(self, sd_hash, timeout=None, payment_rate_manager=None):
|
||||
"""
|
||||
|
@ -2289,14 +2408,19 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Download and return a blob
|
||||
|
||||
Args:
|
||||
'blob_hash': (str) blob hash of blob to get
|
||||
'timeout'(optional): (int) timeout in number of seconds
|
||||
'encoding'(optional): (str) by default no attempt at decoding is made,
|
||||
can be set to one of the following decoders:
|
||||
Usage:
|
||||
blob_get (<blob_hash> | --blob_hash=<blob_hash>) [--timeout=<timeout>]
|
||||
[--encoding=<encoding>] [--payment_rate_manager=<payment_rate_manager>]
|
||||
|
||||
Options:
|
||||
--timeout=<timeout> : timeout in number of seconds
|
||||
--encoding=<encoding> : by default no attempt at decoding is made,
|
||||
can be set to one of the
|
||||
following decoders:
|
||||
'json'
|
||||
'payment_rate_manager'(optional): if not given the default payment rate manager
|
||||
will be used. supported alternative rate managers:
|
||||
--payment_rate_manager=<payment_rate_manager> : if not given the default payment rate
|
||||
manager will be used.
|
||||
supported alternative rate managers:
|
||||
'only-free'
|
||||
|
||||
Returns
|
||||
|
@ -2327,8 +2451,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Delete a blob
|
||||
|
||||
Args:
|
||||
'blob_hash': (str) hash of blob to get
|
||||
Usage:
|
||||
blob_delete (<blob_hash> | --blob_hash=<blob_hash)
|
||||
t
|
||||
Returns:
|
||||
(str) Success/fail message
|
||||
"""
|
||||
|
@ -2345,6 +2470,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
response = yield self._render_response("Deleted %s" % blob_hash)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('peer_list')
|
||||
def jsonrpc_get_peers_for_hash(self, blob_hash):
|
||||
"""
|
||||
DEPRECATED. Use `peer_list` instead
|
||||
|
@ -2355,9 +2481,12 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get peers for blob hash
|
||||
|
||||
Args:
|
||||
'blob_hash': (str) blob hash
|
||||
'timeout'(optional): (int) peer search timeout in seconds
|
||||
Usage:
|
||||
peer_list (<blob_hash> | --blob_hash=<blob_hash>) [<timeout> | --timeout=<timeout>]
|
||||
|
||||
Options:
|
||||
<timeout>, --timeout=<timeout> : peer search timeout in seconds
|
||||
|
||||
Returns:
|
||||
(list) List of contacts
|
||||
"""
|
||||
|
@ -2369,6 +2498,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
d.addCallback(lambda r: self._render_response(r))
|
||||
return d
|
||||
|
||||
@AuthJSONRPCServer.deprecated('blob_announce_all')
|
||||
def jsonrpc_announce_all_blobs_to_dht(self):
|
||||
"""
|
||||
DEPRECATED. Use `blob_announce_all` instead.
|
||||
|
@ -2379,8 +2509,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Announce all blobs to the DHT
|
||||
|
||||
Args:
|
||||
None
|
||||
Usage:
|
||||
blob_announce_all
|
||||
|
||||
Returns:
|
||||
(str) Success/fail message
|
||||
"""
|
||||
|
@ -2394,8 +2525,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Reflect a stream
|
||||
|
||||
Args:
|
||||
'sd_hash': (str) sd_hash of lbry file
|
||||
Usage:
|
||||
reflect (<sd_hash> | --sd_hash=<sd_hash>)
|
||||
|
||||
Returns:
|
||||
(bool) true if successful
|
||||
"""
|
||||
|
@ -2406,6 +2538,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
yield reupload.reflect_stream(lbry_file)
|
||||
defer.returnValue("Reflect success")
|
||||
|
||||
@AuthJSONRPCServer.deprecated('blob_list')
|
||||
def jsonrpc_get_blob_hashes(self):
|
||||
"""
|
||||
DEPRECATED. Use `blob_list` instead
|
||||
|
@ -2461,6 +2594,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
response = yield self._render_response(blob_hashes_for_return)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('blob_reflect_all')
|
||||
def jsonrpc_reflect_all_blobs(self):
|
||||
"""
|
||||
DEPRECATED. Use `blob_reflect_all` instead
|
||||
|
@ -2471,8 +2605,9 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Reflects all saved blobs
|
||||
|
||||
Args:
|
||||
None
|
||||
Usage:
|
||||
blob_reflect_all
|
||||
|
||||
Returns:
|
||||
(bool) true if successful
|
||||
"""
|
||||
|
@ -2487,10 +2622,13 @@ class Daemon(AuthJSONRPCServer):
|
|||
"""
|
||||
Get stream availability for lbry uri
|
||||
|
||||
Args:
|
||||
'uri' : (str) lbry uri
|
||||
'sd_timeout' (optional): (int) sd blob download timeout
|
||||
'peer_timeout' (optional): (int) how long to look for peers
|
||||
Usage:
|
||||
get_availability (<uri> | --uri=<uri>) [<sd_timeout> | --sd_timeout=<sd_timeout>]
|
||||
[<peer_timeout> | --peer_timeout=<peer_timeout>]
|
||||
|
||||
Options:
|
||||
<sd_timeout>, --sd_timeout=<sd_timeout> : sd blob download timeout
|
||||
<peer_timeout>, --peer_timeout=<peer_timeout> : how long to look for peers
|
||||
|
||||
Returns:
|
||||
(float) Peers per blob / total blobs
|
||||
|
@ -2554,6 +2692,7 @@ class Daemon(AuthJSONRPCServer):
|
|||
response = yield self._render_response(mean_availability)
|
||||
defer.returnValue(response)
|
||||
|
||||
@AuthJSONRPCServer.deprecated('status')
|
||||
def jsonrpc_get_start_notice(self):
|
||||
"""
|
||||
DEPRECATED.
|
||||
|
@ -2576,6 +2715,28 @@ class Daemon(AuthJSONRPCServer):
|
|||
return d
|
||||
|
||||
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def jsonrpc_cli_test_command(self, pos_arg, pos_args=[], pos_arg2=None, pos_arg3=None):
|
||||
"""
|
||||
This command is only for testing the CLI argument parsing
|
||||
Usage:
|
||||
cli_test_command (<pos_arg> | --pos_arg=<pos_arg>)
|
||||
[<pos_args>...] [--pos_arg2=<pos_arg2>]
|
||||
[--pos_arg3=<pos_arg3>]
|
||||
|
||||
Options:
|
||||
<pos_arg2>, --pos_arg2=<pos_arg2> : pos arg 2
|
||||
<pos_arg3>, --pos_arg3=<pos_arg3> : pos arg 3
|
||||
|
||||
Returns:
|
||||
pos args
|
||||
"""
|
||||
out = (pos_arg, pos_args, pos_arg2, pos_arg3)
|
||||
response = yield self._render_response(out)
|
||||
defer.returnValue(response)
|
||||
|
||||
|
||||
class _ResolveNameHelper(object):
|
||||
def __init__(self, daemon, name, force_refresh):
|
||||
self.daemon = daemon
|
||||
|
|
|
@ -1,23 +1,68 @@
|
|||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import colorama
|
||||
|
||||
from docopt import docopt
|
||||
from collections import OrderedDict
|
||||
from lbrynet import conf
|
||||
from lbrynet.core import utils
|
||||
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 urllib2 import URLError, HTTPError
|
||||
from httplib import UNAUTHORIZED
|
||||
|
||||
|
||||
def main():
|
||||
colorama.init()
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
_, arguments = parser.parse_known_args()
|
||||
def remove_brackets(key):
|
||||
if key.startswith("<") and key.endswith(">"):
|
||||
return str(key[1:-1])
|
||||
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()
|
||||
api = LBRYAPIClient.get_client()
|
||||
|
||||
|
@ -44,36 +89,10 @@ def main():
|
|||
print " Status: " + message
|
||||
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
|
||||
|
||||
if method in ['--help', '-h', 'help']:
|
||||
if len(params) == 0:
|
||||
print_help(api)
|
||||
elif 'command' not in params:
|
||||
print_error(
|
||||
'To get help on a specific command, use `{} help command=COMMAND_NAME`'.format(
|
||||
os.path.basename(sys.argv[0]))
|
||||
)
|
||||
else:
|
||||
print_help_for_command(api, params['command'])
|
||||
|
||||
elif method not in api.commands():
|
||||
print_error("'" + method + "' is not a valid command.")
|
||||
|
||||
else:
|
||||
try:
|
||||
result = api.call(method, params)
|
||||
result = api.call(method, **kwargs)
|
||||
if isinstance(result, basestring):
|
||||
# printing the undumped string is prettier
|
||||
print result
|
||||
|
@ -96,42 +115,17 @@ def main():
|
|||
else:
|
||||
print_error("Something went wrong\n", suggest_help=False)
|
||||
|
||||
print_help_for_command(api, method)
|
||||
print_help_for_command(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):
|
||||
if not isinstance(x, (unicode, str)):
|
||||
return x
|
||||
if x in ('true', 'True', 'TRUE'):
|
||||
return True
|
||||
if x in ('false', 'False', 'FALSE'):
|
||||
|
@ -159,7 +153,8 @@ def print_error(message, suggest_help=True):
|
|||
print_help_suggestion()
|
||||
|
||||
|
||||
def print_help(api):
|
||||
def print_help():
|
||||
commands = Daemon.callable_methods.keys()
|
||||
print "\n".join([
|
||||
"NAME",
|
||||
" lbrynet-cli - LBRY command line client.",
|
||||
|
@ -170,20 +165,18 @@ def print_help(api):
|
|||
"EXAMPLES",
|
||||
" lbrynet-cli commands # list available commands",
|
||||
" lbrynet-cli status # get daemon status",
|
||||
" lbrynet-cli resolve_name name=what # resolve a name",
|
||||
" lbrynet-cli help command=resolve_name # get help for a command",
|
||||
" lbrynet-cli resolve_name what # resolve a name",
|
||||
" lbrynet-cli help resolve_name # get help for a command",
|
||||
"",
|
||||
"COMMANDS",
|
||||
wrap_list_to_term_width(api.commands(), prefix=' ')
|
||||
wrap_list_to_term_width(commands, prefix=' ')
|
||||
])
|
||||
|
||||
|
||||
def print_help_for_command(api, command):
|
||||
help_response = api.call('help', {'command': command})
|
||||
print "Help for %s method:" % command
|
||||
message = help_response['help'] if 'help' in help_response else help_response
|
||||
message = "\n".join([' ' + line for line in message.split("\n")])
|
||||
print message
|
||||
def print_help_for_command(command):
|
||||
fn = Daemon.callable_methods.get(command)
|
||||
if fn:
|
||||
print "Help for %s method:\n%s" % (command, fn.__doc__)
|
||||
|
||||
|
||||
def wrap_list_to_term_width(l, width=None, separator=', ', prefix=''):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import logging
|
||||
import urlparse
|
||||
import inspect
|
||||
import json
|
||||
import inspect
|
||||
|
||||
from decimal import Decimal
|
||||
from zope.interface import implements
|
||||
|
@ -15,9 +15,9 @@ from traceback import format_exc
|
|||
from lbrynet import conf
|
||||
from lbrynet.core.Error import InvalidAuthenticationToken
|
||||
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.client import LBRY_SECRET
|
||||
from lbrynet.undecorated import undecorated
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -110,21 +110,30 @@ def jsonrpc_dumps_pretty(obj, **kwargs):
|
|||
separators=(',', ': '), **kwargs) + "\n"
|
||||
|
||||
|
||||
class AuthorizedBase(object):
|
||||
def __init__(self):
|
||||
self.authorized_functions = []
|
||||
self.callable_methods = {}
|
||||
self._call_lock = {}
|
||||
self._queued_methods = []
|
||||
class JSONRPCServerType(type):
|
||||
def __new__(mcs, name, bases, newattrs):
|
||||
klass = type.__new__(mcs, name, bases, newattrs)
|
||||
klass.callable_methods = {}
|
||||
klass.deprecated_methods = {}
|
||||
klass.authorized_functions = []
|
||||
klass.queued_methods = []
|
||||
|
||||
for methodname in dir(self):
|
||||
for methodname in dir(klass):
|
||||
if methodname.startswith("jsonrpc_"):
|
||||
method = getattr(self, methodname)
|
||||
self.callable_methods.update({methodname.split("jsonrpc_")[1]: method})
|
||||
method = getattr(klass, methodname)
|
||||
if not hasattr(method, '_deprecated'):
|
||||
klass.callable_methods.update({methodname.split("jsonrpc_")[1]: method})
|
||||
if hasattr(method, '_auth_required'):
|
||||
self.authorized_functions.append(methodname.split("jsonrpc_")[1])
|
||||
klass.authorized_functions.append(methodname.split("jsonrpc_")[1])
|
||||
if hasattr(method, '_queued'):
|
||||
self._queued_methods.append(methodname.split("jsonrpc_")[1])
|
||||
klass.queued_methods.append(methodname.split("jsonrpc_")[1])
|
||||
else:
|
||||
klass.deprecated_methods.update({methodname.split("jsonrpc_")[1]: method})
|
||||
return klass
|
||||
|
||||
|
||||
class AuthorizedBase(object):
|
||||
__metaclass__ = JSONRPCServerType
|
||||
|
||||
@staticmethod
|
||||
def auth_required(f):
|
||||
|
@ -136,6 +145,23 @@ class AuthorizedBase(object):
|
|||
f._queued = True
|
||||
return f
|
||||
|
||||
@staticmethod
|
||||
def deprecated(new_command=None):
|
||||
def _deprecated_wrapper(f):
|
||||
f._new_command = new_command
|
||||
f._deprecated = True
|
||||
return f
|
||||
return _deprecated_wrapper
|
||||
|
||||
@staticmethod
|
||||
def flags(**kwargs):
|
||||
def _flag_wrapper(f):
|
||||
f._flags = {}
|
||||
for k, v in kwargs.iteritems():
|
||||
f._flags[v] = k
|
||||
return f
|
||||
return _flag_wrapper
|
||||
|
||||
|
||||
class AuthJSONRPCServer(AuthorizedBase):
|
||||
"""Authorized JSONRPC server used as the base class for the LBRY API
|
||||
|
@ -164,7 +190,7 @@ class AuthJSONRPCServer(AuthorizedBase):
|
|||
isLeaf = True
|
||||
|
||||
def __init__(self, use_authentication=None):
|
||||
AuthorizedBase.__init__(self)
|
||||
self._call_lock = {}
|
||||
self._use_authentication = (
|
||||
use_authentication if use_authentication is not None else conf.settings['use_auth_http']
|
||||
)
|
||||
|
@ -263,7 +289,7 @@ class AuthJSONRPCServer(AuthorizedBase):
|
|||
id_ = None
|
||||
try:
|
||||
function_name = parsed.get('method')
|
||||
is_queued = function_name in self._queued_methods
|
||||
is_queued = function_name in self.queued_methods
|
||||
args = parsed.get('params', {})
|
||||
id_ = parsed.get('id', None)
|
||||
token = parsed.pop('hmac', None)
|
||||
|
@ -312,12 +338,16 @@ class AuthJSONRPCServer(AuthorizedBase):
|
|||
|
||||
if args == EMPTY_PARAMS or args == []:
|
||||
args_dict = {}
|
||||
_args, _kwargs = (), {}
|
||||
elif isinstance(args, dict):
|
||||
args_dict = args
|
||||
elif len(args) == 1 and isinstance(args[0], dict):
|
||||
# TODO: this is for backwards compatibility. Remove this once API and UI are updated
|
||||
# TODO: also delete EMPTY_PARAMS then
|
||||
args_dict = args[0]
|
||||
_args, _kwargs = (), args
|
||||
elif isinstance(args, list):
|
||||
_args, _kwargs = args, {}
|
||||
else:
|
||||
# d = defer.maybeDeferred(function, *args) # if we want to support positional args too
|
||||
raise ValueError('Args must be a dict')
|
||||
|
@ -337,7 +367,7 @@ class AuthJSONRPCServer(AuthorizedBase):
|
|||
if is_queued:
|
||||
d_lock = self._call_lock.get(function_name, False)
|
||||
if not d_lock:
|
||||
d = defer.maybeDeferred(function, **args_dict)
|
||||
d = defer.maybeDeferred(function, self, **args_dict)
|
||||
self._call_lock[function_name] = finished_deferred
|
||||
|
||||
def _del_lock(*args):
|
||||
|
@ -352,9 +382,9 @@ class AuthJSONRPCServer(AuthorizedBase):
|
|||
log.info("queued %s", function_name)
|
||||
d = d_lock
|
||||
d.addBoth(lambda _: log.info("running %s from queue", function_name))
|
||||
d.addCallback(lambda _: defer.maybeDeferred(function, **args_dict))
|
||||
d.addCallback(lambda _: defer.maybeDeferred(function, self, **args_dict))
|
||||
else:
|
||||
d = defer.maybeDeferred(function, **args_dict)
|
||||
d = defer.maybeDeferred(function, self, **args_dict)
|
||||
|
||||
# finished_deferred will callback when the request is finished
|
||||
# and errback if something went wrong. If the errback is
|
||||
|
@ -374,28 +404,6 @@ class AuthJSONRPCServer(AuthorizedBase):
|
|||
(utils.now() - time_in).total_seconds()))
|
||||
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):
|
||||
"""
|
||||
Add or update a HMAC secret for a session
|
||||
|
@ -454,6 +462,16 @@ class AuthJSONRPCServer(AuthorizedBase):
|
|||
else:
|
||||
return server_port[0], 80
|
||||
|
||||
def _check_deprecated(self, function_path):
|
||||
if function_path in self.deprecated_methods:
|
||||
deprecated_fn = self.deprecated_methods[function_path]
|
||||
deprecated_function_path = function_path
|
||||
new_function_path = deprecated_fn._new_command
|
||||
log.warning("\"%s\" is deprecated, please update to use \"%s\"",
|
||||
deprecated_function_path, new_function_path)
|
||||
return new_function_path
|
||||
return function_path
|
||||
|
||||
def _verify_method_is_callable(self, function_path):
|
||||
if function_path not in self.callable_methods:
|
||||
raise UnknownAPIMethodError(function_path)
|
||||
|
@ -462,9 +480,32 @@ class AuthJSONRPCServer(AuthorizedBase):
|
|||
raise NotAllowedDuringStartupError(function_path)
|
||||
|
||||
def _get_jsonrpc_method(self, function_path):
|
||||
function_path = self._check_deprecated(function_path)
|
||||
self._verify_method_is_callable(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):
|
||||
if not self.sessions.get(session_id, False):
|
||||
self._register_user_session(session_id)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Twisted==16.6.0
|
||||
appdirs==1.4.3
|
||||
argparse==1.2.1
|
||||
docopt==0.6.2
|
||||
base58==0.2.2
|
||||
git+https://github.com/lbryio/bumpversion.git#egg=bumpversion
|
||||
colorama==0.3.7
|
||||
|
|
1
setup.py
1
setup.py
|
@ -31,6 +31,7 @@ requires = [
|
|||
'seccure',
|
||||
'txJSON-RPC',
|
||||
'zope.interface',
|
||||
'docopt'
|
||||
]
|
||||
|
||||
console_scripts = [
|
||||
|
|
102
tests/integration/test_integration.py
Normal file
102
tests/integration/test_integration.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
"""
|
||||
Start up the actual daemon and test some non blockchain commands here
|
||||
"""
|
||||
|
||||
from jsonrpc.proxy import JSONRPCProxy
|
||||
import json
|
||||
import subprocess
|
||||
import unittest
|
||||
import time
|
||||
import os
|
||||
|
||||
from urllib2 import URLError
|
||||
from httplib import BadStatusLine
|
||||
from socket import error
|
||||
|
||||
|
||||
def shell_command(command):
|
||||
FNULL = open(os.devnull, 'w')
|
||||
p = subprocess.Popen(command,shell=False,stdout=FNULL,stderr=subprocess.STDOUT)
|
||||
|
||||
def lbrynet_cli(commands):
|
||||
cli_cmd=['lbrynet-cli']
|
||||
for cmd in commands:
|
||||
cli_cmd.append(cmd)
|
||||
p = subprocess.Popen(cli_cmd,shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
||||
out,err = p.communicate()
|
||||
return out,err
|
||||
|
||||
lbrynet_rpc_port = '5279'
|
||||
lbrynet = JSONRPCProxy.from_url("http://localhost:{}/lbryapi".format(lbrynet_rpc_port))
|
||||
|
||||
|
||||
class TestIntegration(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
shell_command(['lbrynet-daemon'])
|
||||
start_time = time.time()
|
||||
STARTUP_TIMEOUT = 180
|
||||
while time.time() - start_time < STARTUP_TIMEOUT:
|
||||
try:
|
||||
status = lbrynet.status()
|
||||
except (URLError,error,BadStatusLine) as e:
|
||||
pass
|
||||
else:
|
||||
if status['is_running'] == True:
|
||||
return
|
||||
time.sleep(1)
|
||||
raise Exception('lbrynet daemon failed to start')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
shell_command(['lbrynet-cli', 'daemon_stop'])
|
||||
|
||||
def test_cli(self):
|
||||
help_out,err = lbrynet_cli(['help'])
|
||||
self.assertTrue(help_out)
|
||||
|
||||
out,err = lbrynet_cli(['-h'])
|
||||
self.assertEqual(out, help_out)
|
||||
|
||||
out,err = lbrynet_cli(['--help'])
|
||||
self.assertEqual(out, help_out)
|
||||
|
||||
out,err = lbrynet_cli(['status'])
|
||||
out = json.loads(out)
|
||||
self.assertTrue(out['is_running'])
|
||||
|
||||
|
||||
def test_cli_docopts(self):
|
||||
out,err = lbrynet_cli(['cli_test_command'])
|
||||
self.assertEqual('',out)
|
||||
self.assertTrue('Usage' in err)
|
||||
|
||||
out,err = lbrynet_cli(['cli_test_command','1','--not_a_arg=1'])
|
||||
self.assertEqual('',out)
|
||||
self.assertTrue('Usage' in err)
|
||||
|
||||
out,err = lbrynet_cli(['cli_test_command','1'])
|
||||
out = json.loads(out)
|
||||
self.assertEqual([1,[],None,None], out)
|
||||
|
||||
out,err = lbrynet_cli(['cli_test_command','1','--pos_arg2=1'])
|
||||
out = json.loads(out)
|
||||
self.assertEqual([1,[],1,None], out)
|
||||
|
||||
|
||||
out,err = lbrynet_cli(['cli_test_command','1', '--pos_arg2=2','--pos_arg3=3'])
|
||||
out = json.loads(out)
|
||||
self.assertEqual([1,[],2,3], out)
|
||||
|
||||
out,err = lbrynet_cli(['cli_test_command','1','2','3'])
|
||||
out = json.loads(out)
|
||||
# TODO: variable length arguments don't have guess_type() on them
|
||||
self.assertEqual([1,['2','3'],None,None], out)
|
||||
|
||||
|
||||
def test_status(self):
|
||||
out = lbrynet.status()
|
||||
self.assertTrue(out['is_running'])
|
||||
|
||||
if __name__ =='__main__':
|
||||
unittest.main()
|
|
@ -16,23 +16,3 @@ class DaemonCLITests(unittest.TestCase):
|
|||
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…
Reference in a new issue