Merge pull request #652 from lbryio/arg-parsing

Parse cli args from docstrings and add positional arguments
This commit is contained in:
Jack Robison 2017-06-01 16:03:37 -04:00 committed by GitHub
commit 84bac4bab7
9 changed files with 600 additions and 318 deletions

View file

@ -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

View file

@ -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
*

View file

@ -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,
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}}
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
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=<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=<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,15 +2408,20 @@ 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:
'json'
'payment_rate_manager'(optional): if not given the default payment rate manager
will be used. supported alternative rate managers:
'only-free'
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=<payment_rate_manager> : if not given the default payment rate
manager will be used.
supported alternative rate managers:
'only-free'
Returns
(str) Success/Fail message or (dict) decoded data
@ -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

View file

@ -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,94 +89,43 @@ 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]))
)
try:
result = api.call(method, **kwargs)
if isinstance(result, basestring):
# printing the undumped string is prettier
print result
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("'" + method + "' is not a valid command.")
print_error(error_data['error']['message'] + "\n", suggest_help=False)
else:
print_error("Something went wrong\n", suggest_help=False)
else:
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_help_for_command(method)
print_error(error_data['error']['message'] + "\n", suggest_help=False)
else:
print_error("Something went wrong\n", suggest_help=False)
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
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 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=''):

View file

@ -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})
if hasattr(method, '_auth_required'):
self.authorized_functions.append(methodname.split("jsonrpc_")[1])
if hasattr(method, '_queued'):
self._queued_methods.append(methodname.split("jsonrpc_")[1])
method = getattr(klass, methodname)
if not hasattr(method, '_deprecated'):
klass.callable_methods.update({methodname.split("jsonrpc_")[1]: method})
if hasattr(method, '_auth_required'):
klass.authorized_functions.append(methodname.split("jsonrpc_")[1])
if hasattr(method, '_queued'):
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)

View file

@ -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

View file

@ -31,6 +31,7 @@ requires = [
'seccure',
'txJSON-RPC',
'zope.interface',
'docopt'
]
console_scripts = [

View 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()

View file

@ -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))