9509acc490
When using `lbrynet get URL`, if the URL is not a valid URL the function `url.URL.parse` will raise a `ValueError` exception which will produce a whole backtrace. For example, this is the case if we provide a channel name with a forward slash but without a stream name. ``` lbrynet get @Non-existing/ ``` ``` Traceback (most recent call last): File "/opt/git/lbry-sdk/lbry/file/file_manager.py", line 84, in download_from_uri if not URL.parse(uri).has_stream: File "/opt/git/lbry-sdk/lbry/schema/url.py", line 114, in parse raise ValueError('Invalid LBRY URL') ValueError: Invalid LBRY URL WARNING lbry.extras.daemon.daemon:1110: Error downloading Non-existing/: Invalid LBRY URL ``` Now we raise a new `InvalidStreamURLError` which can be trapped in the upper functions that use `url.URL.parse` such as `FileManager.download_from_uri`. If we do this the traceback won't be shown. ``` WARNING lbry.file.file_manager:252: Failed to download Non-existing/: Invalid LBRY stream URL: '@Non-existing/' WARNING lbry.extras.daemon.daemon:1110: Error downloading Non-existing/: Invalid LBRY stream URL: '@Non-existing/' ``` This handles the case when trying to download only "channel" parts without the claim part. ``` lbrynet get @Non-existing lbrynet get @Non-existing/ lbrynet get Non-existing/ ```
494 lines
12 KiB
Python
494 lines
12 KiB
Python
from .base import BaseError, claim_id
|
|
|
|
|
|
class UserInputError(BaseError):
|
|
"""
|
|
User input errors.
|
|
"""
|
|
|
|
|
|
class CommandError(UserInputError):
|
|
"""
|
|
Errors preparing to execute commands.
|
|
"""
|
|
|
|
|
|
class CommandDoesNotExistError(CommandError):
|
|
|
|
def __init__(self, command):
|
|
self.command = command
|
|
super().__init__(f"Command '{command}' does not exist.")
|
|
|
|
|
|
class CommandDeprecatedError(CommandError):
|
|
|
|
def __init__(self, command):
|
|
self.command = command
|
|
super().__init__(f"Command '{command}' is deprecated.")
|
|
|
|
|
|
class CommandInvalidArgumentError(CommandError):
|
|
|
|
def __init__(self, argument, command):
|
|
self.argument = argument
|
|
self.command = command
|
|
super().__init__(f"Invalid argument '{argument}' to command '{command}'.")
|
|
|
|
|
|
class CommandTemporarilyUnavailableError(CommandError):
|
|
"""
|
|
Such as waiting for required components to start.
|
|
"""
|
|
|
|
def __init__(self, command):
|
|
self.command = command
|
|
super().__init__(f"Command '{command}' is temporarily unavailable.")
|
|
|
|
|
|
class CommandPermanentlyUnavailableError(CommandError):
|
|
"""
|
|
such as when required component was intentionally configured not to start.
|
|
"""
|
|
|
|
def __init__(self, command):
|
|
self.command = command
|
|
super().__init__(f"Command '{command}' is permanently unavailable.")
|
|
|
|
|
|
class InputValueError(UserInputError, ValueError):
|
|
"""
|
|
Invalid argument value provided to command.
|
|
"""
|
|
|
|
|
|
class GenericInputValueError(InputValueError):
|
|
|
|
def __init__(self, value, argument):
|
|
self.value = value
|
|
self.argument = argument
|
|
super().__init__(f"The value '{value}' for argument '{argument}' is not valid.")
|
|
|
|
|
|
class InputValueIsNoneError(InputValueError):
|
|
|
|
def __init__(self, argument):
|
|
self.argument = argument
|
|
super().__init__(f"None or null is not valid value for argument '{argument}'.")
|
|
|
|
|
|
class ConflictingInputValueError(InputValueError):
|
|
|
|
def __init__(self, first_argument, second_argument):
|
|
self.first_argument = first_argument
|
|
self.second_argument = second_argument
|
|
super().__init__(f"Only '{first_argument}' or '{second_argument}' is allowed, not both.")
|
|
|
|
|
|
class InputStringIsBlankError(InputValueError):
|
|
|
|
def __init__(self, argument):
|
|
self.argument = argument
|
|
super().__init__(f"{argument} cannot be blank.")
|
|
|
|
|
|
class EmptyPublishedFileError(InputValueError):
|
|
|
|
def __init__(self, file_path):
|
|
self.file_path = file_path
|
|
super().__init__(f"Cannot publish empty file: {file_path}")
|
|
|
|
|
|
class MissingPublishedFileError(InputValueError):
|
|
|
|
def __init__(self, file_path):
|
|
self.file_path = file_path
|
|
super().__init__(f"File does not exist: {file_path}")
|
|
|
|
|
|
class InvalidStreamURLError(InputValueError):
|
|
"""
|
|
When an URL cannot be downloaded, such as '@Channel/' or a collection
|
|
"""
|
|
|
|
def __init__(self, url):
|
|
self.url = url
|
|
super().__init__(f"Invalid LBRY stream URL: '{url}'")
|
|
|
|
|
|
class ConfigurationError(BaseError):
|
|
"""
|
|
Configuration errors.
|
|
"""
|
|
|
|
|
|
class ConfigWriteError(ConfigurationError):
|
|
"""
|
|
When writing the default config fails on startup, such as due to permission issues.
|
|
"""
|
|
|
|
def __init__(self, path):
|
|
self.path = path
|
|
super().__init__(f"Cannot write configuration file '{path}'.")
|
|
|
|
|
|
class ConfigReadError(ConfigurationError):
|
|
"""
|
|
Can't open the config file user provided via command line args.
|
|
"""
|
|
|
|
def __init__(self, path):
|
|
self.path = path
|
|
super().__init__(f"Cannot find provided configuration file '{path}'.")
|
|
|
|
|
|
class ConfigParseError(ConfigurationError):
|
|
"""
|
|
Includes the syntax error / line number to help user fix it.
|
|
"""
|
|
|
|
def __init__(self, path):
|
|
self.path = path
|
|
super().__init__(f"Failed to parse the configuration file '{path}'.")
|
|
|
|
|
|
class ConfigMissingError(ConfigurationError):
|
|
|
|
def __init__(self, path):
|
|
self.path = path
|
|
super().__init__(f"Configuration file '{path}' is missing setting that has no default / fallback.")
|
|
|
|
|
|
class ConfigInvalidError(ConfigurationError):
|
|
|
|
def __init__(self, path):
|
|
self.path = path
|
|
super().__init__(f"Configuration file '{path}' has setting with invalid value.")
|
|
|
|
|
|
class NetworkError(BaseError):
|
|
"""
|
|
**Networking**
|
|
"""
|
|
|
|
|
|
class NoInternetError(NetworkError):
|
|
|
|
def __init__(self):
|
|
super().__init__("No internet connection.")
|
|
|
|
|
|
class NoUPnPSupportError(NetworkError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Router does not support UPnP.")
|
|
|
|
|
|
class WalletError(BaseError):
|
|
"""
|
|
**Wallet Errors**
|
|
"""
|
|
|
|
|
|
class TransactionRejectedError(WalletError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Transaction rejected, unknown reason.")
|
|
|
|
|
|
class TransactionFeeTooLowError(WalletError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Fee too low.")
|
|
|
|
|
|
class TransactionInvalidSignatureError(WalletError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Invalid signature.")
|
|
|
|
|
|
class InsufficientFundsError(WalletError):
|
|
"""
|
|
determined by wallet prior to attempting to broadcast a tx; this is different for example from a TX
|
|
being created and sent but then rejected by lbrycrd for unspendable utxos.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__("Not enough funds to cover this transaction.")
|
|
|
|
|
|
class ChannelKeyNotFoundError(WalletError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Channel signing key not found.")
|
|
|
|
|
|
class ChannelKeyInvalidError(WalletError):
|
|
"""
|
|
For example, channel was updated but you don't have the updated key.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__("Channel signing key is out of date.")
|
|
|
|
|
|
class DataDownloadError(WalletError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Failed to download blob. *generic*")
|
|
|
|
|
|
class PrivateKeyNotFoundError(WalletError):
|
|
|
|
def __init__(self, key, value):
|
|
self.key = key
|
|
self.value = value
|
|
super().__init__(f"Couldn't find private key for {key} '{value}'.")
|
|
|
|
|
|
class ResolveError(WalletError):
|
|
|
|
def __init__(self, url):
|
|
self.url = url
|
|
super().__init__(f"Failed to resolve '{url}'.")
|
|
|
|
|
|
class ResolveTimeoutError(WalletError):
|
|
|
|
def __init__(self, url):
|
|
self.url = url
|
|
super().__init__(f"Failed to resolve '{url}' within the timeout.")
|
|
|
|
|
|
class ResolveCensoredError(WalletError):
|
|
|
|
def __init__(self, url, censor_id, censor_row):
|
|
self.url = url
|
|
self.censor_id = censor_id
|
|
self.censor_row = censor_row
|
|
super().__init__(f"Resolve of '{url}' was censored by channel with claim id '{censor_id}'.")
|
|
|
|
|
|
class KeyFeeAboveMaxAllowedError(WalletError):
|
|
|
|
def __init__(self, message):
|
|
self.message = message
|
|
super().__init__(f"{message}")
|
|
|
|
|
|
class InvalidPasswordError(WalletError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Password is invalid.")
|
|
|
|
|
|
class IncompatibleWalletServerError(WalletError):
|
|
|
|
def __init__(self, server, port):
|
|
self.server = server
|
|
self.port = port
|
|
super().__init__(f"'{server}:{port}' has an incompatibly old version.")
|
|
|
|
|
|
class TooManyClaimSearchParametersError(WalletError):
|
|
|
|
def __init__(self, key, limit):
|
|
self.key = key
|
|
self.limit = limit
|
|
super().__init__(f"{key} cant have more than {limit} items.")
|
|
|
|
|
|
class AlreadyPurchasedError(WalletError):
|
|
"""
|
|
allow-duplicate-purchase flag to override.
|
|
"""
|
|
|
|
def __init__(self, claim_id_hex):
|
|
self.claim_id_hex = claim_id_hex
|
|
super().__init__(f"You already have a purchase for claim_id '{claim_id_hex}'. Use")
|
|
|
|
|
|
class ServerPaymentInvalidAddressError(WalletError):
|
|
|
|
def __init__(self, address):
|
|
self.address = address
|
|
super().__init__(f"Invalid address from wallet server: '{address}' - skipping payment round.")
|
|
|
|
|
|
class ServerPaymentWalletLockedError(WalletError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Cannot spend funds with locked wallet, skipping payment round.")
|
|
|
|
|
|
class ServerPaymentFeeAboveMaxAllowedError(WalletError):
|
|
|
|
def __init__(self, daily_fee, max_fee):
|
|
self.daily_fee = daily_fee
|
|
self.max_fee = max_fee
|
|
super().__init__(f"Daily server fee of {daily_fee} exceeds maximum configured of {max_fee} LBC.")
|
|
|
|
|
|
class WalletNotLoadedError(WalletError):
|
|
|
|
def __init__(self, wallet_id):
|
|
self.wallet_id = wallet_id
|
|
super().__init__(f"Wallet {wallet_id} is not loaded.")
|
|
|
|
|
|
class WalletAlreadyLoadedError(WalletError):
|
|
|
|
def __init__(self, wallet_path):
|
|
self.wallet_path = wallet_path
|
|
super().__init__(f"Wallet {wallet_path} is already loaded.")
|
|
|
|
|
|
class WalletNotFoundError(WalletError):
|
|
|
|
def __init__(self, wallet_path):
|
|
self.wallet_path = wallet_path
|
|
super().__init__(f"Wallet not found at {wallet_path}.")
|
|
|
|
|
|
class WalletAlreadyExistsError(WalletError):
|
|
|
|
def __init__(self, wallet_path):
|
|
self.wallet_path = wallet_path
|
|
super().__init__(f"Wallet {wallet_path} already exists, use `wallet_add` to load it.")
|
|
|
|
|
|
class BlobError(BaseError):
|
|
"""
|
|
**Blobs**
|
|
"""
|
|
|
|
|
|
class BlobNotFoundError(BlobError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Blob not found.")
|
|
|
|
|
|
class BlobPermissionDeniedError(BlobError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Permission denied to read blob.")
|
|
|
|
|
|
class BlobTooBigError(BlobError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Blob is too big.")
|
|
|
|
|
|
class BlobEmptyError(BlobError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Blob is empty.")
|
|
|
|
|
|
class BlobFailedDecryptionError(BlobError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Failed to decrypt blob.")
|
|
|
|
|
|
class CorruptBlobError(BlobError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Blobs is corrupted.")
|
|
|
|
|
|
class BlobFailedEncryptionError(BlobError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Failed to encrypt blob.")
|
|
|
|
|
|
class DownloadCancelledError(BlobError):
|
|
|
|
def __init__(self):
|
|
super().__init__("Download was canceled.")
|
|
|
|
|
|
class DownloadSDTimeoutError(BlobError):
|
|
|
|
def __init__(self, download):
|
|
self.download = download
|
|
super().__init__(f"Failed to download sd blob {download} within timeout.")
|
|
|
|
|
|
class DownloadDataTimeoutError(BlobError):
|
|
|
|
def __init__(self, download):
|
|
self.download = download
|
|
super().__init__(f"Failed to download data blobs for sd hash {download} within timeout.")
|
|
|
|
|
|
class InvalidStreamDescriptorError(BlobError):
|
|
|
|
def __init__(self, message):
|
|
self.message = message
|
|
super().__init__(f"{message}")
|
|
|
|
|
|
class InvalidDataError(BlobError):
|
|
|
|
def __init__(self, message):
|
|
self.message = message
|
|
super().__init__(f"{message}")
|
|
|
|
|
|
class InvalidBlobHashError(BlobError):
|
|
|
|
def __init__(self, message):
|
|
self.message = message
|
|
super().__init__(f"{message}")
|
|
|
|
|
|
class ComponentError(BaseError):
|
|
"""
|
|
**Components**
|
|
"""
|
|
|
|
|
|
class ComponentStartConditionNotMetError(ComponentError):
|
|
|
|
def __init__(self, components):
|
|
self.components = components
|
|
super().__init__(f"Unresolved dependencies for: {components}")
|
|
|
|
|
|
class ComponentsNotStartedError(ComponentError):
|
|
|
|
def __init__(self, message):
|
|
self.message = message
|
|
super().__init__(f"{message}")
|
|
|
|
|
|
class CurrencyExchangeError(BaseError):
|
|
"""
|
|
**Currency Exchange**
|
|
"""
|
|
|
|
|
|
class InvalidExchangeRateResponseError(CurrencyExchangeError):
|
|
|
|
def __init__(self, source, reason):
|
|
self.source = source
|
|
self.reason = reason
|
|
super().__init__(f"Failed to get exchange rate from {source}: {reason}")
|
|
|
|
|
|
class CurrencyConversionError(CurrencyExchangeError):
|
|
|
|
def __init__(self, message):
|
|
self.message = message
|
|
super().__init__(f"{message}")
|
|
|
|
|
|
class InvalidCurrencyError(CurrencyExchangeError):
|
|
|
|
def __init__(self, currency):
|
|
self.currency = currency
|
|
super().__init__(f"Invalid currency: {currency} is not a supported currency.")
|