feat: adhere to ruff's B rules (#2423)

* feat: adhere to ruff's `B` rules
last of the ruff checks.
closes #2308
* B904
* B008
* B005
* B025
* cleanup on fake
This commit is contained in:
dni ⚡ 2024-04-17 13:11:51 +02:00 committed by GitHub
parent e13a37c193
commit 98ec59df96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 226 additions and 169 deletions

View file

@ -137,8 +137,8 @@ def create_app() -> FastAPI:
)
# Allow registering new extensions routes without direct access to the `app` object
setattr(core_app_extra, "register_new_ext_routes", register_new_ext_routes(app))
setattr(core_app_extra, "register_new_ratelimiter", register_new_ratelimiter(app))
core_app_extra.register_new_ext_routes = register_new_ext_routes(app)
core_app_extra.register_new_ratelimiter = register_new_ratelimiter(app)
# register static files
static_path = Path("lnbits", "static")

View file

@ -18,11 +18,11 @@ async def migrate_extension_database(ext: Extension, current_version):
try:
ext_migrations = importlib.import_module(f"{ext.module_name}.migrations")
ext_db = importlib.import_module(ext.module_name).db
except ImportError as e:
logger.error(e)
except ImportError as exc:
logger.error(exc)
raise ImportError(
f"Please make sure that the extension `{ext.code}` has a migrations file."
)
) from exc
async with ext_db.connect() as ext_conn:
await run_migration(ext_conn, ext_migrations, ext.code, current_version)
@ -113,7 +113,7 @@ def to_valid_user_id(user_id: str) -> UUID:
raise ValueError("User ID must have at least 128 bits")
try:
int(user_id, 16)
except Exception:
raise ValueError("Invalid hex string for User ID.")
except Exception as exc:
raise ValueError("Invalid hex string for User ID.") from exc
return UUID(hex=user_id[:32], version=4)

View file

@ -201,8 +201,8 @@ async def pay_invoice(
"""
try:
invoice = bolt11_decode(payment_request)
except Exception:
raise InvoiceError("Bolt11 decoding failed.")
except Exception as exc:
raise InvoiceError("Bolt11 decoding failed.") from exc
if not invoice.amount_msat or not invoice.amount_msat > 0:
raise InvoiceError("Amountless invoices not supported.")
@ -286,10 +286,10 @@ async def pay_invoice(
conn=conn,
**payment_kwargs,
)
except Exception as e:
logger.error(f"could not create temporary payment: {e}")
except Exception as exc:
logger.error(f"could not create temporary payment: {exc}")
# happens if the same wallet tries to pay an invoice twice
raise PaymentError("Could not make payment.")
raise PaymentError("Could not make payment.") from exc
# do the balance check
wallet = await get_wallet(wallet_id, conn=conn)
@ -727,7 +727,7 @@ def update_cached_settings(sets_dict: dict):
except Exception:
logger.warning(f"Failed overriding setting: {key}, value: {value}")
if "super_user" in sets_dict:
setattr(settings, "super_user", sets_dict["super_user"])
settings.super_user = sets_dict["super_user"]
async def init_admin_settings(super_user: Optional[str] = None) -> SuperSettings:

View file

@ -43,11 +43,11 @@ async def api_auditor():
"node_balance_msats": int(node_balance),
"lnbits_balance_msats": int(total_balance),
}
except Exception:
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="Could not audit balance.",
)
) from exc
@admin_router.get(
@ -113,10 +113,10 @@ async def api_restart_server() -> dict[str, str]:
async def api_topup_balance(data: CreateTopup) -> dict[str, str]:
try:
await get_wallet(data.id)
except Exception:
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="wallet does not exist."
)
) from exc
if settings.lnbits_backend_wallet_class == "VoidWallet":
raise HTTPException(

View file

@ -79,7 +79,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
try:
url = str(lnurl_decode(code))
domain = urlparse(url).netloc
except Exception:
except Exception as exc:
# parse internet identifier (user@domain.com)
name_domain = code.split("@")
if len(name_domain) == 2 and len(name_domain[1].split(".")) >= 2:
@ -94,7 +94,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
else:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl"
)
) from exc
# params is what will be returned to the client
params: Dict = {"domain": domain}
@ -119,14 +119,14 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
try:
data = json.loads(r.text)
except json.decoder.JSONDecodeError:
except json.decoder.JSONDecodeError as exc:
raise HTTPException(
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
detail={
"domain": domain,
"message": f"got invalid response '{r.text[:200]}'",
},
)
) from exc
try:
tag: str = data.get("tag")
@ -185,7 +185,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
"domain": domain,
"message": f"lnurl JSON response invalid: {exc}",
},
)
) from exc
return params

View file

@ -68,11 +68,11 @@ async def login(data: LoginUsernamePassword) -> JSONResponse:
raise HTTPException(HTTP_401_UNAUTHORIZED, "Invalid credentials.")
return _auth_success_response(user.username, user.id)
except HTTPException as e:
raise e
except Exception as e:
logger.debug(e)
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot login.")
except HTTPException as exc:
raise exc
except Exception as exc:
logger.debug(exc)
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot login.") from exc
@auth_router.post("/usr", description="Login via the User ID")
@ -86,11 +86,11 @@ async def login_usr(data: LoginUsr) -> JSONResponse:
raise HTTPException(HTTP_401_UNAUTHORIZED, "User ID does not exist.")
return _auth_success_response(user.username or "", user.id)
except HTTPException as e:
raise e
except Exception as e:
logger.debug(e)
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot login.")
except HTTPException as exc:
raise exc
except Exception as exc:
logger.debug(exc)
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot login.") from exc
@auth_router.get("/{provider}", description="SSO Provider")
@ -124,16 +124,16 @@ async def handle_oauth_token(request: Request, provider: str) -> RedirectRespons
user_id = decrypt_internal_message(provider_sso.state)
request.session.pop("user", None)
return await _handle_sso_login(userinfo, user_id)
except HTTPException as e:
raise e
except ValueError as e:
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
except Exception as e:
logger.debug(e)
except HTTPException as exc:
raise exc
except ValueError as exc:
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
except Exception as exc:
logger.debug(exc)
raise HTTPException(
HTTP_500_INTERNAL_SERVER_ERROR,
f"Cannot authenticate user with {provider} Auth.",
)
) from exc
@auth_router.post("/logout")
@ -169,11 +169,13 @@ async def register(data: CreateUser) -> JSONResponse:
user = await create_user(data)
return _auth_success_response(user.username)
except ValueError as e:
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
except Exception as e:
logger.debug(e)
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot create user.")
except ValueError as exc:
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
except Exception as exc:
logger.debug(exc)
raise HTTPException(
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot create user."
) from exc
@auth_router.put("/password")
@ -189,13 +191,13 @@ async def update_password(
try:
return await update_user_password(data)
except AssertionError as e:
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
except Exception as e:
logger.debug(e)
except AssertionError as exc:
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
except Exception as exc:
logger.debug(exc)
raise HTTPException(
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user password."
)
) from exc
@auth_router.put("/update")
@ -211,11 +213,13 @@ async def update(
try:
return await update_account(user.id, data.username, None, data.config)
except AssertionError as e:
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
except Exception as e:
logger.debug(e)
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user.")
except AssertionError as exc:
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
except Exception as exc:
logger.debug(exc)
raise HTTPException(
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user."
) from exc
@auth_router.put("/first_install")
@ -237,13 +241,13 @@ async def first_install(data: UpdateSuperuserPassword) -> JSONResponse:
await update_user_password(super_user)
settings.first_install = False
return _auth_success_response(username=super_user.username)
except AssertionError as e:
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
except Exception as e:
logger.debug(e)
except AssertionError as exc:
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
except Exception as exc:
logger.debug(exc)
raise HTTPException(
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user password."
)
) from exc
async def _handle_sso_login(userinfo: OpenID, verified_user_id: Optional[str] = None):

View file

@ -104,10 +104,10 @@ async def api_install_extension(
ext_info.notify_upgrade()
return extension
except AssertionError as e:
raise HTTPException(HTTPStatus.BAD_REQUEST, str(e))
except Exception as ex:
logger.warning(ex)
except AssertionError as exc:
raise HTTPException(HTTPStatus.BAD_REQUEST, str(exc)) from exc
except Exception as exc:
logger.warning(exc)
ext_info.clean_extension_files()
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
@ -115,7 +115,7 @@ async def api_install_extension(
f"Failed to install extension {ext_info.id} "
f"({ext_info.installed_version})."
),
)
) from exc
@extension_router.delete("/{ext_id}")
@ -159,10 +159,10 @@ async def api_uninstall_extension(
await delete_installed_extension(ext_id=ext_info.id)
logger.success(f"Extension '{ext_id}' uninstalled.")
except Exception as ex:
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(ex)
)
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
) from exc
@extension_router.get("/{ext_id}/releases", dependencies=[Depends(check_admin)])
@ -183,10 +183,10 @@ async def get_extension_releases(ext_id: str):
return extension_releases
except Exception as ex:
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(ex)
)
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
) from exc
@extension_router.put("/invoice", dependencies=[Depends(check_admin)])
@ -216,11 +216,13 @@ async def get_extension_invoice(data: CreateExtension):
return payment_info
except AssertionError as e:
raise HTTPException(HTTPStatus.BAD_REQUEST, str(e))
except Exception as ex:
logger.warning(ex)
raise HTTPException(HTTPStatus.INTERNAL_SERVER_ERROR, "Cannot request invoice")
except AssertionError as exc:
raise HTTPException(HTTPStatus.BAD_REQUEST, str(exc)) from exc
except Exception as exc:
logger.warning(exc)
raise HTTPException(
HTTPStatus.INTERNAL_SERVER_ERROR, "Cannot request invoice"
) from exc
@extension_router.get(
@ -238,10 +240,10 @@ async def get_extension_release(org: str, repo: str, tag_name: str):
"is_version_compatible": config.is_version_compatible(),
"warning": config.warning,
}
except Exception as ex:
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(ex)
)
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
) from exc
@extension_router.delete(
@ -262,9 +264,9 @@ async def delete_extension_db(ext_id: str):
except HTTPException as ex:
logger.error(ex)
raise ex
except Exception as ex:
logger.error(ex)
except Exception as exc:
logger.error(exc)
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail=f"Cannot delete data for extension '{ext_id}'",
)
) from exc

View file

@ -171,9 +171,11 @@ async def extensions_install(
"extensions": extensions,
},
)
except Exception as e:
logger.warning(e)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
except Exception as exc:
logger.warning(exc)
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
) from exc
@generic_router.get(
@ -396,8 +398,10 @@ async def hex_to_uuid4(hex_value: str):
try:
user_id = to_valid_user_id(hex_value).hex
return RedirectResponse(url=f"/wallet?usr={user_id}")
except Exception as e:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail=str(exc)
) from exc
async def toggle_extension(extension_to_enable, extension_to_disable, user_id):

View file

@ -195,5 +195,7 @@ async def api_get_1ml_stats(node: Node = Depends(require_node)) -> Optional[Node
try:
r.raise_for_status()
return r.json()["noderank"]
except httpx.HTTPStatusError:
raise HTTPException(status_code=404, detail="Node not found on 1ml.com")
except httpx.HTTPStatusError as exc:
raise HTTPException(
status_code=404, detail="Node not found on 1ml.com"
) from exc

View file

@ -13,6 +13,7 @@ from fastapi import (
Depends,
Header,
HTTPException,
Query,
Request,
)
from fastapi.responses import JSONResponse
@ -28,7 +29,6 @@ from lnbits.core.models import (
Payment,
PaymentFilters,
PaymentHistoryPoint,
Query,
Wallet,
WalletType,
)
@ -133,19 +133,19 @@ async def api_payments_create_invoice(data: CreateInvoice, wallet: Wallet):
if data.description_hash:
try:
description_hash = bytes.fromhex(data.description_hash)
except ValueError:
except ValueError as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="'description_hash' must be a valid hex string",
)
) from exc
if data.unhashed_description:
try:
unhashed_description = bytes.fromhex(data.unhashed_description)
except ValueError:
except ValueError as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="'unhashed_description' must be a valid hex string",
)
) from exc
# do not save memo if description_hash or unhashed_description is set
memo = ""
@ -170,8 +170,8 @@ async def api_payments_create_invoice(data: CreateInvoice, wallet: Wallet):
payment_db = await get_standalone_payment(payment_hash, conn=conn)
assert payment_db is not None, "payment not found"
checking_id = payment_db.checking_id
except InvoiceError as e:
raise HTTPException(status_code=520, detail=str(e))
except InvoiceError as exc:
raise HTTPException(status_code=520, detail=str(exc)) from exc
except Exception as exc:
raise exc
@ -192,12 +192,14 @@ async def api_payments_pay_invoice(
payment_hash = await pay_invoice(
wallet_id=wallet.id, payment_request=bolt11, extra=extra
)
except ValueError as e:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
except PermissionError as e:
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail=str(e))
except PaymentError as e:
raise HTTPException(status_code=520, detail=str(e))
except ValueError as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail=str(exc)
) from exc
except PermissionError as exc:
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail=str(exc)) from exc
except PaymentError as exc:
raise HTTPException(status_code=520, detail=str(exc)) from exc
except Exception as exc:
raise exc
@ -282,11 +284,11 @@ async def api_payments_pay_lnurl(
if r.is_error:
raise httpx.ConnectError("LNURL callback connection error")
r.raise_for_status()
except (httpx.ConnectError, httpx.RequestError):
except (httpx.ConnectError, httpx.RequestError) as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=f"Failed to connect to {domain}.",
)
) from exc
params = json.loads(r.text)
if params.get("status") == "ERROR":

View file

@ -27,10 +27,10 @@ async def api_public_payment_longpolling(payment_hash):
invoice = bolt11.decode(payment.bolt11)
if invoice.has_expired():
return {"status": "expired"}
except Exception:
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Invalid bolt11 invoice."
)
) from exc
payment_queue = asyncio.Queue(0)

View file

@ -38,10 +38,10 @@ async def api_create_tinyurl(
if tinyurl.wallet == wallet.wallet.id:
return tinyurl
return await create_tinyurl(url, endless, wallet.wallet.id)
except Exception:
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Unable to create tinyurl"
)
) from exc
@tinyurl_router.get(
@ -60,10 +60,10 @@ async def api_get_tinyurl(
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Wrong key provided."
)
except Exception:
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Unable to fetch tinyurl"
)
) from exc
@tinyurl_router.delete(
@ -83,10 +83,10 @@ async def api_delete_tinyurl(
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Wrong key provided."
)
except Exception:
except Exception as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Unable to delete"
)
) from exc
@tinyurl_router.get(

View file

@ -69,10 +69,10 @@ class KeyChecker(SecurityBase):
detail="Invalid key or wallet.",
)
self.wallet = wallet
except KeyError:
except KeyError as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="`X-API-KEY` header missing."
)
) from exc
class WalletInvoiceKeyChecker(KeyChecker):
@ -324,10 +324,10 @@ async def _get_account_from_token(access_token):
return await get_account_by_email(str(payload.get("email")))
raise HTTPException(HTTPStatus.UNAUTHORIZED, "Data missing for access token.")
except ExpiredSignatureError:
except ExpiredSignatureError as exc:
raise HTTPException(
HTTPStatus.UNAUTHORIZED, "Session expired.", {"token-expired": "true"}
)
except JWTError as e:
logger.debug(e)
raise HTTPException(HTTPStatus.UNAUTHORIZED, "Invalid access token.")
) from exc
except JWTError as exc:
logger.debug(exc)
raise HTTPException(HTTPStatus.UNAUTHORIZED, "Invalid access token.") from exc

View file

@ -428,9 +428,9 @@ class InstallableExtension(BaseModel):
self._remember_payment_info()
except Exception as ex:
logger.warning(ex)
raise AssertionError("Cannot fetch extension archive file")
except Exception as exc:
logger.warning(exc)
raise AssertionError("Cannot fetch extension archive file") from exc
archive_hash = file_hash(ext_zip_file)
if self.installed_release.hash and self.installed_release.hash != archive_hash:
@ -560,7 +560,11 @@ class InstallableExtension(BaseModel):
return ext
@classmethod
def from_rows(cls, rows: List[Any] = []) -> List["InstallableExtension"]:
def from_rows(
cls, rows: Optional[List[Any]] = None
) -> List["InstallableExtension"]:
if rows is None:
rows = []
return [InstallableExtension.from_row(row) for row in rows]
@classmethod

View file

@ -44,11 +44,12 @@ def catch_rpc_errors(f):
async def wrapper(*args, **kwargs):
try:
return await f(*args, **kwargs)
except RpcError as e:
if e.error["code"] == -32602:
raise HTTPException(status_code=400, detail=e.error["message"])
except RpcError as exc:
msg = exc.error["message"]
if exc.error["code"] == -32602:
raise HTTPException(status_code=400, detail=msg) from exc
else:
raise HTTPException(status_code=500, detail=e.error["message"])
raise HTTPException(status_code=500, detail=msg) from exc
return wrapper
@ -66,9 +67,11 @@ class CoreLightningNode(Node):
# https://docs.corelightning.org/reference/lightning-connect
try:
await self.ln_rpc("connect", uri)
except RpcError as e:
if e.error["code"] == 400:
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=e.error["message"])
except RpcError as exc:
if exc.error["code"] == 400:
raise HTTPException(
HTTPStatus.BAD_REQUEST, detail=exc.error["message"]
) from exc
else:
raise
@ -76,12 +79,12 @@ class CoreLightningNode(Node):
async def disconnect_peer(self, peer_id: str):
try:
await self.ln_rpc("disconnect", peer_id)
except RpcError as e:
if e.error["code"] == -1:
except RpcError as exc:
if exc.error["code"] == -1:
raise HTTPException(
HTTPStatus.BAD_REQUEST,
detail=e.error["message"],
)
detail=exc.error["message"],
) from exc
else:
raise
@ -105,14 +108,14 @@ class CoreLightningNode(Node):
funding_txid=result["txid"],
output_index=result["outnum"],
)
except RpcError as e:
message = e.error["message"]
except RpcError as exc:
message = exc.error["message"]
if "amount: should be a satoshi amount" in message:
raise HTTPException(
HTTPStatus.BAD_REQUEST,
detail="The amount is not a valid satoshi amount.",
)
) from exc
if "Unknown peer" in message:
raise HTTPException(
@ -121,7 +124,7 @@ class CoreLightningNode(Node):
"We where able to connect to the peer but CLN "
"can't find it when opening a channel."
),
)
) from exc
if "Owning subdaemon openingd died" in message:
# https://github.com/ElementsProject/lightning/issues/2798#issuecomment-511205719
@ -131,14 +134,14 @@ class CoreLightningNode(Node):
"Likely the peer didn't like our channel opening "
"proposal and disconnected from us."
),
)
) from exc
if (
"Number of pending channels exceed maximum" in message
or "exceeds maximum chan size of 10 BTC" in message
or "Could not afford" in message
):
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=message)
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=message) from exc
raise
@catch_rpc_errors
@ -152,13 +155,13 @@ class CoreLightningNode(Node):
raise HTTPException(status_code=400, detail="Short id required")
try:
await self.ln_rpc("close", short_id)
except RpcError as e:
message = e.error["message"]
except RpcError as exc:
message = exc.error["message"]
if (
"Short channel ID not active:" in message
or "Short channel ID not found" in message
):
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=message)
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=message) from exc
else:
raise

View file

@ -60,11 +60,13 @@ class LndRestNode(Node):
)
try:
response.raise_for_status()
except HTTPStatusError as e:
json = e.response.json()
except HTTPStatusError as exc:
json = exc.response.json()
if json:
error = json.get("error") or json
raise HTTPException(e.response.status_code, detail=error.get("message"))
raise HTTPException(
exc.response.status_code, detail=error.get("message")
) from exc
return response.json()
def get(self, path: str, **kwargs):
@ -81,8 +83,8 @@ class LndRestNode(Node):
async def connect_peer(self, uri: str):
try:
pubkey, host = uri.split("@")
except ValueError:
raise HTTPException(400, detail="Invalid peer URI")
except ValueError as exc:
raise HTTPException(400, detail="Invalid peer URI") from exc
await self.request(
"POST",
"/v1/peers",
@ -96,11 +98,11 @@ class LndRestNode(Node):
async def disconnect_peer(self, peer_id: str):
try:
await self.request("DELETE", "/v1/peers/" + peer_id)
except HTTPException as e:
if "unable to disconnect" in e.detail:
except HTTPException as exc:
if "unable to disconnect" in exc.detail:
raise HTTPException(
HTTPStatus.BAD_REQUEST, detail="Peer is not connected"
)
) from exc
raise
async def _get_peer_info(self, peer_id: str) -> NodePeerInfo:

View file

@ -48,18 +48,20 @@ def main(
# this beautiful beast parses all command line arguments and
# passes them to the uvicorn server
# TODO: why is this needed? it would be better only to rely on the commands options
d = {}
for a in ctx.args:
item = a.split("=")
if len(item) > 1: # argument like --key=value
print(a, item)
d[item[0].strip("--").replace("-", "_")] = (
d[item[0].strip("--").replace("-", "_")] = ( # noqa: B005
int(item[1]) # need to convert to int if it's a number
if item[1].isdigit()
else item[1]
)
else:
d[a.strip("--")] = True # argument like --key
# argument like --key
d[a.strip("--")] = True # noqa: B005
while True:
config = uvicorn.Config(

View file

@ -60,8 +60,8 @@ class AESCipher:
aes = AES.new(key, AES.MODE_CBC, iv)
try:
return self.unpad(aes.decrypt(encrypted[16:])).decode() # type: ignore
except UnicodeDecodeError:
raise ValueError("Wrong passphrase")
except UnicodeDecodeError as exc:
raise ValueError("Wrong passphrase") from exc
def encrypt(self, message: bytes) -> str:
passphrase = self.passphrase

View file

@ -93,11 +93,13 @@ class PaymentPendingStatus(PaymentStatus):
class Wallet(ABC):
async def cleanup(self):
pass
__node_cls__: Optional[type[Node]] = None
@abstractmethod
async def cleanup(self):
pass
@abstractmethod
def status(self) -> Coroutine[None, None, StatusResponse]:
pass

View file

@ -27,6 +27,9 @@ class ClicheWallet(Wallet):
self.endpoint = self.normalize_endpoint(settings.cliche_endpoint)
async def cleanup(self):
pass
async def status(self) -> StatusResponse:
try:
ws = create_connection(self.endpoint)

View file

@ -31,6 +31,9 @@ async def run_sync(func) -> Any:
class CoreLightningWallet(Wallet):
__node_cls__ = CoreLightningNode
async def cleanup(self):
pass
def __init__(self):
rpc = settings.corelightning_rpc or settings.clightning_rpc
if not rpc:

View file

@ -42,6 +42,9 @@ class FakeWallet(Wallet):
32,
).hex()
async def cleanup(self):
pass
async def status(self) -> StatusResponse:
logger.info(
"FakeWallet funding source is for using LNbits as a centralised,"

View file

@ -109,6 +109,9 @@ class LndWallet(Wallet):
def metadata_callback(self, _, callback):
callback([("macaroon", self.macaroon)], None)
async def cleanup(self):
pass
async def status(self) -> StatusResponse:
try:
resp = await self.rpc.ChannelBalance(ln.ChannelBalanceRequest())

View file

@ -77,12 +77,12 @@ class SparkWallet(Wallet):
httpx.HTTPError,
httpx.TimeoutException,
) as exc:
raise UnknownError(f"error connecting to spark: {exc}")
raise UnknownError(f"error connecting to spark: {exc}") from exc
try:
data = r.json()
except Exception:
raise UnknownError(r.text)
except Exception as exc:
raise UnknownError(r.text) from exc
if r.is_error:
if r.status_code == 401:
@ -171,7 +171,7 @@ class SparkWallet(Wallet):
raise SparkError(
f"listpays({payment_hash}) returned an unexpected response:"
f" {listpays}"
)
) from exc
if pay["status"] == "failed":
return PaymentResponse(False, None, None, None, str(exc))

View file

@ -13,6 +13,10 @@ from .base import (
class VoidWallet(Wallet):
async def cleanup(self):
pass
async def create_invoice(self, *_, **__) -> InvoiceResponse:
return InvoiceResponse(
ok=False, error_message="VoidWallet cannot create invoices."

View file

@ -181,7 +181,8 @@ extend-exclude = [
# N - naming
# UP - pyupgrade
# RUF - ruff specific rules
select = ["F", "E", "W", "I", "A", "C", "N", "UP", "RUF"]
# B - bugbear
select = ["F", "E", "W", "I", "A", "C", "N", "UP", "RUF", "B"]
# UP007: pyupgrade: use X | Y instead of Optional. (python3.10)
# RUF012: mutable-class-default
ignore = ["UP007", "RUF012"]
@ -206,3 +207,12 @@ classmethod-decorators = [
[tool.ruff.lint.mccabe]
# TODO: Decrease this to 10.
max-complexity = 16
[tool.ruff.lint.flake8-bugbear]
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.
extend-immutable-calls = [
"fastapi.Depends",
"fastapi.Query",
"fastapi.Body",
"lnbits.decorators.parse_filters"
]

View file

@ -62,7 +62,7 @@ def load_funding_source(funding_source: FundingSourceConfig) -> BaseWallet:
custom_settings = funding_source.settings
original_settings = {}
settings = getattr(wallets_module, "settings")
settings = wallets_module.settings
for s in custom_settings:
original_settings[s] = getattr(settings, s)
@ -93,7 +93,7 @@ async def check_assertions(wallet, _test_data: WalletTest):
elif "expect_error" in test_data:
await _assert_error(wallet, tested_func, call_params, _test_data.expect_error)
else:
assert False, "Expected outcome not specified"
raise AssertionError("Expected outcome not specified")
async def _assert_data(wallet, tested_func, call_params, expect):

View file

@ -7,7 +7,7 @@ import argparse
import os
import sqlite3
import sys
from typing import List
from typing import List, Optional
import psycopg2
@ -90,21 +90,23 @@ def insert_to_pg(query, data):
for d in data:
try:
cursor.execute(query, d)
except Exception as e:
except Exception as exc:
if args.ignore_errors:
print(e)
print(exc)
print(f"Failed to insert {d}")
else:
print("query:", query)
print("data:", d)
raise ValueError(f"Failed to insert {d}")
raise ValueError(f"Failed to insert {d}") from exc
connection.commit()
cursor.close()
connection.close()
def migrate_core(file: str, exclude_tables: List[str] = []):
def migrate_core(file: str, exclude_tables: Optional[List[str]] = None):
if exclude_tables is None:
exclude_tables = []
print(f"Migrating core: {file}")
migrate_db(file, "public", exclude_tables)
print("✅ Migrated core")
@ -118,8 +120,10 @@ def migrate_ext(file: str):
print(f"✅ Migrated ext: {schema}")
def migrate_db(file: str, schema: str, exclude_tables: List[str] = []):
def migrate_db(file: str, schema: str, exclude_tables: Optional[List[str]] = None):
# first we check if this file exists:
if exclude_tables is None:
exclude_tables = []
assert os.path.isfile(file), f"{file} does not exist!"
cursor = get_sqlite_cursor(file)