From 6d5ad9e2295193812061605cf64333c57b71e2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 15 Apr 2024 09:02:21 +0200 Subject: [PATCH] chore: adhere to ruff's "N" rules (#2377) * chore: adhere to ruff's "N" rules WARN: reinstall failing extensions! bunch of more consistent variable naming. inspired by this issue. https://github.com/lnbits/lnbits/issues/2308 * fixup! chore: adhere to ruff's "N" rules * rename to funding_source * skip jmeter --------- Co-authored-by: Pavol Rusnak --- .github/workflows/ci.yml | 20 ++++---- lnbits/app.py | 30 ++++++------ lnbits/core/models.py | 8 ++-- lnbits/core/services.py | 71 +++++++++++++++-------------- lnbits/core/tasks.py | 16 +++++-- lnbits/core/views/auth_api.py | 16 +++---- lnbits/core/views/generic.py | 14 +++--- lnbits/core/views/node_api.py | 6 +-- lnbits/core/views/payment_api.py | 24 +++++----- lnbits/core/views/websocket_api.py | 12 ++--- lnbits/db.py | 4 +- lnbits/decorators.py | 4 +- lnbits/settings.py | 6 +-- lnbits/tasks.py | 6 +-- lnbits/wallets/__init__.py | 20 ++++---- lnbits/wallets/base.py | 2 +- lnbits/wallets/corelightning.py | 6 +-- lnbits/wallets/corelightningrest.py | 4 +- lnbits/wallets/eclair.py | 4 +- lnbits/wallets/lndrest.py | 6 +-- lnbits/wallets/opennode.py | 4 +- lnbits/wallets/zbd.py | 4 +- pyproject.toml | 9 +++- tests/conftest.py | 4 +- tests/core/views/test_api.py | 21 +++++---- tests/core/views/test_node_api.py | 5 +- tests/helpers.py | 12 ++--- tools/conv.py | 26 +++++------ 28 files changed, 191 insertions(+), 173 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 569f58de5..5a3446438 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,13 +53,13 @@ jobs: secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - jmeter: - needs: [ lint ] - strategy: - matrix: - python-version: ["3.9"] - poetry-version: ["1.5.1"] - uses: ./.github/workflows/jmeter.yml - with: - python-version: ${{ matrix.python-version }} - poetry-version: ${{ matrix.poetry-version }} + # jmeter: + # needs: [ lint ] + # strategy: + # matrix: + # python-version: ["3.9"] + # poetry-version: ["1.5.1"] + # uses: ./.github/workflows/jmeter.yml + # with: + # python-version: ${{ matrix.python-version }} + # poetry-version: ${{ matrix.poetry-version }} diff --git a/lnbits/app.py b/lnbits/app.py index 5062b8612..2cca3cc8b 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -25,7 +25,7 @@ from starlette.responses import JSONResponse from lnbits.core.crud import get_dbversions, get_installed_extensions from lnbits.core.helpers import migrate_extension_database -from lnbits.core.services import websocketUpdater +from lnbits.core.services import websocket_updater from lnbits.core.tasks import ( # watchdog_task killswitch_task, wait_for_paid_invoices, @@ -37,7 +37,7 @@ from lnbits.tasks import ( register_invoice_listener, ) from lnbits.utils.cache import cache -from lnbits.wallets import get_wallet_class, set_wallet_class +from lnbits.wallets import get_funding_source, set_funding_source from .commands import migrate_databases from .core import init_core_routers @@ -81,7 +81,7 @@ async def startup(app: FastAPI): # initialize WALLET try: - set_wallet_class() + set_funding_source() except Exception as e: logger.error(f"Error initializing {settings.lnbits_backend_wallet_class}: {e}") set_void_wallet_class() @@ -110,8 +110,8 @@ async def shutdown(): # wait a bit to allow them to finish, so that cleanup can run without problems await asyncio.sleep(0.1) - WALLET = get_wallet_class() - await WALLET.cleanup() + funding_source = get_funding_source() + await funding_source.cleanup() @asynccontextmanager @@ -178,33 +178,35 @@ def create_app() -> FastAPI: async def check_funding_source() -> None: - WALLET = get_wallet_class() + funding_source = get_funding_source() max_retries = settings.funding_source_max_retries retry_counter = 0 while True: try: - logger.info(f"Connecting to backend {WALLET.__class__.__name__}...") - error_message, balance = await WALLET.status() + logger.info(f"Connecting to backend {funding_source.__class__.__name__}...") + error_message, balance = await funding_source.status() if not error_message: retry_counter = 0 logger.success( - f"✔️ Backend {WALLET.__class__.__name__} connected " + f"✔️ Backend {funding_source.__class__.__name__} connected " f"and with a balance of {balance} msat." ) break logger.error( - f"The backend for {WALLET.__class__.__name__} isn't " + f"The backend for {funding_source.__class__.__name__} isn't " f"working properly: '{error_message}'", RuntimeWarning, ) except Exception as e: - logger.error(f"Error connecting to {WALLET.__class__.__name__}: {e}") + logger.error( + f"Error connecting to {funding_source.__class__.__name__}: {e}" + ) if retry_counter >= max_retries: set_void_wallet_class() - WALLET = get_wallet_class() + funding_source = get_funding_source() break retry_counter += 1 @@ -221,7 +223,7 @@ def set_void_wallet_class(): "Fallback to VoidWallet, because the backend for " f"{settings.lnbits_backend_wallet_class} isn't working properly" ) - set_wallet_class("VoidWallet") + set_funding_source("VoidWallet") async def check_installed_extensions(app: FastAPI): @@ -414,7 +416,7 @@ def initialize_server_logger(): async def update_websocket_serverlog(): while True: msg = await serverlog_queue.get() - await websocketUpdater(super_user_hash, msg) + await websocket_updater(super_user_hash, msg) create_permanent_task(update_websocket_serverlog) diff --git a/lnbits/core/models.py b/lnbits/core/models.py index 68c6c3e42..168659991 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -17,7 +17,7 @@ from lnbits.db import Connection, FilterModel, FromRowModel from lnbits.helpers import url_for from lnbits.lnurl import encode as lnurl_encode from lnbits.settings import settings -from lnbits.wallets import get_wallet_class +from lnbits.wallets import get_funding_source from lnbits.wallets.base import PaymentPendingStatus, PaymentStatus @@ -265,11 +265,11 @@ class Payment(FromRowModel): f"pending payment {self.checking_id}" ) - WALLET = get_wallet_class() + funding_source = get_funding_source() if self.is_out: - status = await WALLET.get_payment_status(self.checking_id) + status = await funding_source.get_payment_status(self.checking_id) else: - status = await WALLET.get_invoice_status(self.checking_id) + status = await funding_source.get_invoice_status(self.checking_id) logger.debug(f"Status: {status}") diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 9edeb0f47..4d2ccf1f5 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -30,7 +30,7 @@ from lnbits.settings import ( settings, ) from lnbits.utils.exchange_rates import fiat_amount_as_satoshis, satoshis_amount_as_fiat -from lnbits.wallets import FAKE_WALLET, get_wallet_class, set_wallet_class +from lnbits.wallets import fake_wallet, get_funding_source, set_funding_source from lnbits.wallets.base import ( PaymentPendingStatus, PaymentResponse, @@ -62,11 +62,11 @@ from .helpers import to_valid_user_id from .models import Payment, UserConfig, Wallet -class PaymentFailure(Exception): +class PaymentError(Exception): pass -class InvoiceFailure(Exception): +class InvoiceError(Exception): pass @@ -123,16 +123,16 @@ async def create_invoice( conn: Optional[Connection] = None, ) -> Tuple[str, str]: if not amount > 0: - raise InvoiceFailure("Amountless invoices not supported.") + raise InvoiceError("Amountless invoices not supported.") user_wallet = await get_wallet(wallet_id, conn=conn) if not user_wallet: - raise InvoiceFailure(f"Could not fetch wallet '{wallet_id}'.") + raise InvoiceError(f"Could not fetch wallet '{wallet_id}'.") invoice_memo = None if description_hash else memo # use the fake wallet if the invoice is for internal use only - wallet = FAKE_WALLET if internal else get_wallet_class() + funding_source = fake_wallet if internal else get_funding_source() amount_sat, extra = await calculate_fiat_amounts( amount, wallet_id, currency=currency, extra=extra, conn=conn @@ -141,20 +141,22 @@ async def create_invoice( if settings.is_wallet_max_balance_exceeded( user_wallet.balance_msat / 1000 + amount_sat ): - raise InvoiceFailure( + raise InvoiceError( f"Wallet balance cannot exceed " f"{settings.lnbits_wallet_limit_max_balance} sats." ) - ok, checking_id, payment_request, error_message = await wallet.create_invoice( - amount=amount_sat, - memo=invoice_memo, - description_hash=description_hash, - unhashed_description=unhashed_description, - expiry=expiry or settings.lightning_invoice_expiry, + ok, checking_id, payment_request, error_message = ( + await funding_source.create_invoice( + amount=amount_sat, + memo=invoice_memo, + description_hash=description_hash, + unhashed_description=unhashed_description, + expiry=expiry or settings.lightning_invoice_expiry, + ) ) if not ok or not payment_request or not checking_id: - raise InvoiceFailure(error_message or "unexpected backend error.") + raise InvoiceError(error_message or "unexpected backend error.") invoice = bolt11_decode(payment_request) @@ -197,12 +199,12 @@ async def pay_invoice( try: invoice = bolt11_decode(payment_request) except Exception: - raise InvoiceFailure("Bolt11 decoding failed.") + raise InvoiceError("Bolt11 decoding failed.") if not invoice.amount_msat or not invoice.amount_msat > 0: - raise InvoiceFailure("Amountless invoices not supported.") + raise InvoiceError("Amountless invoices not supported.") if max_sat and invoice.amount_msat > max_sat * 1000: - raise InvoiceFailure("Amount in invoice is too high.") + raise InvoiceError("Amount in invoice is too high.") await check_wallet_limits(wallet_id, conn, invoice.amount_msat) @@ -237,7 +239,7 @@ async def pay_invoice( # we check if an internal invoice exists that has already been paid # (not pending anymore) if not await check_internal_pending(invoice.payment_hash, conn=conn): - raise PaymentFailure("Internal invoice already paid.") + raise PaymentError("Internal invoice already paid.") # check_internal() returns the checking_id of the invoice we're waiting for # (pending only) @@ -256,7 +258,7 @@ async def pay_invoice( internal_invoice.amount != invoice.amount_msat or internal_invoice.bolt11 != payment_request.lower() ): - raise PaymentFailure("Invalid invoice.") + raise PaymentError("Invalid invoice.") logger.debug(f"creating temporary internal payment with id {internal_id}") # create a new payment from this wallet @@ -284,7 +286,7 @@ async def pay_invoice( except Exception as e: logger.error(f"could not create temporary payment: {e}") # happens if the same wallet tries to pay an invoice twice - raise PaymentFailure("Could not make payment.") + raise PaymentError("Could not make payment.") # do the balance check wallet = await get_wallet(wallet_id, conn=conn) @@ -295,7 +297,7 @@ async def pay_invoice( not internal_checking_id and wallet.balance_msat > -fee_reserve_total_msat ): - raise PaymentFailure( + raise PaymentError( f"You must reserve at least ({round(fee_reserve_total_msat/1000)}" " sat) to cover potential routing fees." ) @@ -323,8 +325,8 @@ async def pay_invoice( service_fee_msat = service_fee(invoice.amount_msat, internal=False) logger.debug(f"backend: sending payment {temp_id}") # actually pay the external invoice - WALLET = get_wallet_class() - payment: PaymentResponse = await WALLET.pay_invoice( + funding_source = get_funding_source() + payment: PaymentResponse = await funding_source.pay_invoice( payment_request, fee_reserve_msat ) @@ -363,7 +365,7 @@ async def pay_invoice( async with db.connect() as conn: logger.debug(f"deleting temporary payment {temp_id}") await delete_wallet_payment(temp_id, wallet_id, conn=conn) - raise PaymentFailure( + raise PaymentError( f"Payment failed: {payment.error_message}" or "Payment failed, but backend didn't give us an error message." ) @@ -499,7 +501,6 @@ async def redeem_lnurl_withdraw( async def perform_lnurlauth( callback: str, wallet: WalletTypeInfo = Depends(require_admin_key), - conn: Optional[Connection] = None, ) -> Optional[LnurlErrorResponse]: cb = urlparse(callback) @@ -592,7 +593,7 @@ async def check_transaction_status( # WARN: this same value must be used for balance check and passed to -# WALLET.pay_invoice(), it may cause a vulnerability if the values differ +# funding_source.pay_invoice(), it may cause a vulnerability if the values differ def fee_reserve(amount_msat: int, internal: bool = False) -> int: if internal: return 0 @@ -621,7 +622,7 @@ def fee_reserve_total(amount_msat: int, internal: bool = False) -> int: async def send_payment_notification(wallet: Wallet, payment: Payment): - await websocketUpdater( + await websocket_updater( wallet.id, json.dumps( { @@ -760,25 +761,25 @@ class WebsocketConnectionManager: await connection.send_text(message) -websocketManager = WebsocketConnectionManager() +websocket_manager = WebsocketConnectionManager() -async def websocketUpdater(item_id, data): - return await websocketManager.send_data(f"{data}", item_id) +async def websocket_updater(item_id, data): + return await websocket_manager.send_data(f"{data}", item_id) async def switch_to_voidwallet() -> None: - WALLET = get_wallet_class() - if WALLET.__class__.__name__ == "VoidWallet": + funding_source = get_funding_source() + if funding_source.__class__.__name__ == "VoidWallet": return - set_wallet_class("VoidWallet") + set_funding_source("VoidWallet") settings.lnbits_backend_wallet_class = "VoidWallet" async def get_balance_delta() -> Tuple[int, int, int]: - WALLET = get_wallet_class() + funding_source = get_funding_source() total_balance = await get_total_balance() - error_message, node_balance = await WALLET.status() + error_message, node_balance = await funding_source.status() if error_message: raise Exception(error_message) return node_balance - total_balance, node_balance, total_balance diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index c35a5ad3f..2b27de377 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -16,7 +16,7 @@ from lnbits.core.services import ( send_payment_notification, switch_to_voidwallet, ) -from lnbits.settings import get_wallet_class, settings +from lnbits.settings import get_funding_source, settings from lnbits.tasks import send_push_notification api_invoice_listeners: Dict[str, asyncio.Queue] = {} @@ -28,8 +28,11 @@ async def killswitch_task(): LNbits and will switch to VoidWallet if the killswitch is triggered. """ while True: - WALLET = get_wallet_class() - if settings.lnbits_killswitch and WALLET.__class__.__name__ != "VoidWallet": + funding_source = get_funding_source() + if ( + settings.lnbits_killswitch + and funding_source.__class__.__name__ != "VoidWallet" + ): with httpx.Client() as client: try: r = client.get(settings.lnbits_status_manifest, timeout=4) @@ -55,8 +58,11 @@ async def watchdog_task(): and will switch to VoidWallet if the watchdog delta is reached. """ while True: - WALLET = get_wallet_class() - if settings.lnbits_watchdog and WALLET.__class__.__name__ != "VoidWallet": + funding_source = get_funding_source() + if ( + settings.lnbits_watchdog + and funding_source.__class__.__name__ != "VoidWallet" + ): try: delta, *_ = await get_balance_delta() logger.debug(f"Running watchdog task. current delta: {delta}") diff --git a/lnbits/core/views/auth_api.py b/lnbits/core/views/auth_api.py index 366d69183..05d5ff443 100644 --- a/lnbits/core/views/auth_api.py +++ b/lnbits/core/views/auth_api.py @@ -316,16 +316,16 @@ def _new_sso(provider: str) -> Optional[SSOBase]: logger.warning(f"{provider} auth allowed but not configured.") return None - SSOProviderClass = _find_auth_provider_class(provider) - ssoProvider = SSOProviderClass( + sso_provider_class = _find_auth_provider_class(provider) + sso_provider = sso_provider_class( client_id, client_secret, None, allow_insecure_http=True ) if ( discovery_url - and getattr(ssoProvider, "discovery_url", discovery_url) != discovery_url + and getattr(sso_provider, "discovery_url", discovery_url) != discovery_url ): - ssoProvider.discovery_url = discovery_url - return ssoProvider + sso_provider.discovery_url = discovery_url + return sso_provider except Exception as e: logger.warning(e) @@ -337,9 +337,9 @@ def _find_auth_provider_class(provider: str) -> Callable: for module in sso_modules: try: provider_module = importlib.import_module(f"{module}.{provider}") - ProviderClass = getattr(provider_module, f"{provider.title()}SSO") - if ProviderClass: - return ProviderClass + provider_class = getattr(provider_module, f"{provider.title()}SSO") + if provider_class: + return provider_class except Exception: pass diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index b2e1370e2..03d7aae3e 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -18,7 +18,7 @@ from lnbits.core.models import User from lnbits.decorators import check_admin, check_user_exists from lnbits.helpers import template_renderer, url_for from lnbits.settings import settings -from lnbits.wallets import get_wallet_class +from lnbits.wallets import get_funding_source from ...extension_manager import InstallableExtension, get_valid_extensions from ...utils.exchange_rates import allowed_currencies, currencies @@ -452,8 +452,8 @@ async def node(request: Request, user: User = Depends(check_admin)): if not settings.lnbits_node_ui: raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE) - WALLET = get_wallet_class() - _, balance = await WALLET.status() + funding_source = get_funding_source() + _, balance = await funding_source.status() return template_renderer().TemplateResponse( request, @@ -472,8 +472,8 @@ async def node_public(request: Request): if not settings.lnbits_public_node_ui: raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE) - WALLET = get_wallet_class() - _, balance = await WALLET.status() + funding_source = get_funding_source() + _, balance = await funding_source.status() return template_renderer().TemplateResponse( request, @@ -490,8 +490,8 @@ async def index(request: Request, user: User = Depends(check_admin)): if not settings.lnbits_admin_ui: raise HTTPException(status_code=HTTPStatus.NOT_FOUND) - WALLET = get_wallet_class() - _, balance = await WALLET.status() + funding_source = get_funding_source() + _, balance = await funding_source.status() return template_renderer().TemplateResponse( request, diff --git a/lnbits/core/views/node_api.py b/lnbits/core/views/node_api.py index 917c24936..c4c1297c4 100644 --- a/lnbits/core/views/node_api.py +++ b/lnbits/core/views/node_api.py @@ -27,8 +27,8 @@ from ...utils.cache import cache def require_node(): - NODE = get_node_class() - if not NODE: + node_class = get_node_class() + if not node_class: raise HTTPException( status_code=HTTPStatus.NOT_IMPLEMENTED, detail="Active backend does not implement Node API", @@ -38,7 +38,7 @@ def require_node(): status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail="Not enabled", ) - return NODE + return node_class def check_public(): diff --git a/lnbits/core/views/payment_api.py b/lnbits/core/views/payment_api.py index 1333f70cc..06ba01f4f 100644 --- a/lnbits/core/views/payment_api.py +++ b/lnbits/core/views/payment_api.py @@ -56,8 +56,8 @@ from ..crud import ( update_pending_payments, ) from ..services import ( - InvoiceFailure, - PaymentFailure, + InvoiceError, + PaymentError, check_transaction_status, create_invoice, fee_reserve_total, @@ -171,7 +171,7 @@ 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 InvoiceFailure as e: + except InvoiceError as e: raise HTTPException(status_code=520, detail=str(e)) except Exception as exc: raise exc @@ -230,7 +230,7 @@ async def api_payments_pay_invoice( raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e)) except PermissionError as e: raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail=str(e)) - except PaymentFailure as e: + except PaymentError as e: raise HTTPException(status_code=520, detail=str(e)) except Exception as exc: raise exc @@ -257,20 +257,20 @@ async def api_payments_pay_invoice( ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData: CreateInvoice = Body(...), + invoice_data: CreateInvoice = Body(...), ): - if invoiceData.out is True and wallet.wallet_type == WalletType.admin: - if not invoiceData.bolt11: + if invoice_data.out is True and wallet.wallet_type == WalletType.admin: + if not invoice_data.bolt11: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail="BOLT11 string is invalid or not given", ) return await api_payments_pay_invoice( - invoiceData.bolt11, wallet.wallet, invoiceData.extra + invoice_data.bolt11, wallet.wallet, invoice_data.extra ) # admin key - elif not invoiceData.out: + elif not invoice_data.out: # invoice key - return await api_payments_create_invoice(invoiceData, wallet.wallet) + return await api_payments_create_invoice(invoice_data, wallet.wallet) else: raise HTTPException( status_code=HTTPStatus.UNAUTHORIZED, @@ -415,10 +415,10 @@ async def api_payments_sse( # TODO: refactor this route into a public and admin one @payment_router.get("/{payment_hash}") -async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)): +async def api_payment(payment_hash, x_api_key: Optional[str] = Header(None)): # We use X_Api_Key here because we want this call to work with and without keys # If a valid key is given, we also return the field "details", otherwise not - wallet = await get_wallet_for_key(X_Api_Key) if isinstance(X_Api_Key, str) else None + wallet = await get_wallet_for_key(x_api_key) if isinstance(x_api_key, str) else None payment = await get_standalone_payment( payment_hash, wallet_id=wallet.id if wallet else None diff --git a/lnbits/core/views/websocket_api.py b/lnbits/core/views/websocket_api.py index 577864d8d..668ce09e0 100644 --- a/lnbits/core/views/websocket_api.py +++ b/lnbits/core/views/websocket_api.py @@ -5,8 +5,8 @@ from fastapi import ( ) from ..services import ( - websocketManager, - websocketUpdater, + websocket_manager, + websocket_updater, ) websocket_router = APIRouter(prefix="/api/v1/ws", tags=["Websocket"]) @@ -14,18 +14,18 @@ websocket_router = APIRouter(prefix="/api/v1/ws", tags=["Websocket"]) @websocket_router.websocket("/{item_id}") async def websocket_connect(websocket: WebSocket, item_id: str): - await websocketManager.connect(websocket, item_id) + await websocket_manager.connect(websocket, item_id) try: while True: await websocket.receive_text() except WebSocketDisconnect: - websocketManager.disconnect(websocket) + websocket_manager.disconnect(websocket) @websocket_router.post("/{item_id}") async def websocket_update_post(item_id: str, data: str): try: - await websocketUpdater(item_id, data) + await websocket_updater(item_id, data) return {"sent": True, "data": data} except Exception: return {"sent": False, "data": data} @@ -34,7 +34,7 @@ async def websocket_update_post(item_id: str, data: str): @websocket_router.get("/{item_id}/{data}") async def websocket_update_get(item_id: str, data: str): try: - await websocketUpdater(item_id, data) + await websocket_updater(item_id, data) return {"sent": True, "data": data} except Exception: return {"sent": False, "data": data} diff --git a/lnbits/db.py b/lnbits/db.py index 652d7f4be..3bdabb42c 100644 --- a/lnbits/db.py +++ b/lnbits/db.py @@ -140,14 +140,14 @@ class Connection(Compat): def rewrite_values(self, values): # strip html - CLEANR = re.compile("<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});") + clean_regex = re.compile("<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});") # tuple to list and back to tuple raw_values = [values] if isinstance(values, str) else list(values) values = [] for raw_value in raw_values: if isinstance(raw_value, str): - values.append(re.sub(CLEANR, "", raw_value)) + values.append(re.sub(clean_regex, "", raw_value)) elif isinstance(raw_value, datetime.datetime): ts = raw_value.timestamp() if self.type == SQLITE: diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 72f4b9dff..6770f4c43 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -138,12 +138,12 @@ async def get_key_type( detail="Invoice (or Admin) key required.", ) - for wallet_type, WalletChecker in zip( + for wallet_type, wallet_checker in zip( [WalletType.admin, WalletType.invoice], [WalletAdminKeyChecker, WalletInvoiceKeyChecker], ): try: - checker = WalletChecker(api_key=token) + checker = wallet_checker(api_key=token) await checker.__call__(r) if checker.wallet is None: raise HTTPException( diff --git a/lnbits/settings.py b/lnbits/settings.py index 2388f3132..332960714 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -532,10 +532,10 @@ if not settings.lnbits_admin_ui: logger.debug(f"{key}: {value}") -def get_wallet_class(): +def get_funding_source(): """ Backwards compatibility """ - from lnbits.wallets import get_wallet_class + from lnbits.wallets import get_funding_source - return get_wallet_class() + return get_funding_source() diff --git a/lnbits/tasks.py b/lnbits/tasks.py index a4ec5caa5..284fa5d53 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -17,7 +17,7 @@ from lnbits.core.crud import ( get_standalone_payment, ) from lnbits.settings import settings -from lnbits.wallets import get_wallet_class +from lnbits.wallets import get_funding_source tasks: List[asyncio.Task] = [] unique_tasks: Dict[str, asyncio.Task] = {} @@ -119,8 +119,8 @@ async def invoice_listener(): Called by the app startup sequence. """ - WALLET = get_wallet_class() - async for checking_id in WALLET.paid_invoices_stream(): + funding_source = get_funding_source() + async for checking_id in funding_source.paid_invoices_stream(): logger.info("> got a payment notification", checking_id) create_task(invoice_callback_dispatcher(checking_id)) diff --git a/lnbits/wallets/__init__.py b/lnbits/wallets/__init__.py index 6a4bbe102..2c0ceaffd 100644 --- a/lnbits/wallets/__init__.py +++ b/lnbits/wallets/__init__.py @@ -28,21 +28,21 @@ from .void import VoidWallet from .zbd import ZBDWallet -def set_wallet_class(class_name: Optional[str] = None): +def set_funding_source(class_name: Optional[str] = None): backend_wallet_class = class_name or settings.lnbits_backend_wallet_class - wallet_class = getattr(wallets_module, backend_wallet_class) - global WALLET - WALLET = wallet_class() - if WALLET.__node_cls__: - set_node_class(WALLET.__node_cls__(WALLET)) + funding_source_constructor = getattr(wallets_module, backend_wallet_class) + global funding_source + funding_source = funding_source_constructor() + if funding_source.__node_cls__: + set_node_class(funding_source.__node_cls__(funding_source)) -def get_wallet_class() -> Wallet: - return WALLET +def get_funding_source() -> Wallet: + return funding_source wallets_module = importlib.import_module("lnbits.wallets") -FAKE_WALLET = FakeWallet() +fake_wallet = FakeWallet() # initialize as fake wallet -WALLET: Wallet = FAKE_WALLET +funding_source: Wallet = fake_wallet diff --git a/lnbits/wallets/base.py b/lnbits/wallets/base.py index 3ca38e820..421177a27 100644 --- a/lnbits/wallets/base.py +++ b/lnbits/wallets/base.py @@ -144,5 +144,5 @@ class Wallet(ABC): return endpoint -class Unsupported(Exception): +class UnsupportedError(Exception): pass diff --git a/lnbits/wallets/corelightning.py b/lnbits/wallets/corelightning.py index 8a755d3bc..527b10abd 100644 --- a/lnbits/wallets/corelightning.py +++ b/lnbits/wallets/corelightning.py @@ -18,7 +18,7 @@ from .base import ( PaymentStatus, PaymentSuccessStatus, StatusResponse, - Unsupported, + UnsupportedError, Wallet, ) @@ -73,12 +73,12 @@ class CoreLightningWallet(Wallet): msat: int = int(amount * 1000) try: if description_hash and not unhashed_description: - raise Unsupported( + raise UnsupportedError( "'description_hash' unsupported by CoreLightning, provide" " 'unhashed_description'" ) if unhashed_description and not self.supports_description_hash: - raise Unsupported("unhashed_description") + raise UnsupportedError("unhashed_description") r: dict = self.ln.invoice( # type: ignore msatoshi=msat, label=label, diff --git a/lnbits/wallets/corelightningrest.py b/lnbits/wallets/corelightningrest.py index 870e51e6c..8a173b49c 100644 --- a/lnbits/wallets/corelightningrest.py +++ b/lnbits/wallets/corelightningrest.py @@ -16,7 +16,7 @@ from .base import ( PaymentResponse, PaymentStatus, StatusResponse, - Unsupported, + UnsupportedError, Wallet, ) from .macaroon import load_macaroon @@ -104,7 +104,7 @@ class CoreLightningRestWallet(Wallet): "label": label, } if description_hash and not unhashed_description: - raise Unsupported( + raise UnsupportedError( "'description_hash' unsupported by CoreLightningRest, " "provide 'unhashed_description'" ) diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index a1578fe80..e119f571d 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -40,8 +40,8 @@ class EclairWallet(Wallet): self.ws_url = f"ws://{urllib.parse.urlsplit(self.url).netloc}/ws" password = settings.eclair_pass - encodedAuth = base64.b64encode(f":{password}".encode()) - auth = str(encodedAuth, "utf-8") + encoded_auth = base64.b64encode(f":{password}".encode()) + auth = str(encoded_auth, "utf-8") self.headers = { "Authorization": f"Basic {auth}", "User-Agent": settings.user_agent, diff --git a/lnbits/wallets/lndrest.py b/lnbits/wallets/lndrest.py index 32c3a0e1d..743abe67e 100644 --- a/lnbits/wallets/lndrest.py +++ b/lnbits/wallets/lndrest.py @@ -164,13 +164,13 @@ class LndRestWallet(Wallet): async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: # set the fee limit for the payment - lnrpcFeeLimit = {} - lnrpcFeeLimit["fixed_msat"] = f"{fee_limit_msat}" + lnrpc_fee_limit = {} + lnrpc_fee_limit["fixed_msat"] = f"{fee_limit_msat}" try: r = await self.client.post( url="/v1/channels/transactions", - json={"payment_request": bolt11, "fee_limit": lnrpcFeeLimit}, + json={"payment_request": bolt11, "fee_limit": lnrpc_fee_limit}, timeout=None, ) r.raise_for_status() diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py index 7b71f80ff..fbab27e01 100644 --- a/lnbits/wallets/opennode.py +++ b/lnbits/wallets/opennode.py @@ -12,7 +12,7 @@ from .base import ( PaymentResponse, PaymentStatus, StatusResponse, - Unsupported, + UnsupportedError, Wallet, ) @@ -74,7 +74,7 @@ class OpenNodeWallet(Wallet): **kwargs, ) -> InvoiceResponse: if description_hash or unhashed_description: - raise Unsupported("description_hash") + raise UnsupportedError("description_hash") r = await self.client.post( "/v1/charges", diff --git a/lnbits/wallets/zbd.py b/lnbits/wallets/zbd.py index 4dee4a86e..7f939cdd3 100644 --- a/lnbits/wallets/zbd.py +++ b/lnbits/wallets/zbd.py @@ -13,7 +13,7 @@ from .base import ( PaymentResponse, PaymentStatus, StatusResponse, - Unsupported, + UnsupportedError, Wallet, ) @@ -65,7 +65,7 @@ class ZBDWallet(Wallet): ) -> InvoiceResponse: # https://api.zebedee.io/v0/charges if description_hash or unhashed_description: - raise Unsupported("description_hash") + raise UnsupportedError("description_hash") msats_amount = amount * 1000 data: Dict = { diff --git a/pyproject.toml b/pyproject.toml index 95bf7d5ad..dca78e551 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -168,7 +168,8 @@ extend-exclude = [ # I - isort # A - flake8-builtins # C - mccabe -select = ["F", "E", "W", "I", "A", "C"] +# N - naming +select = ["F", "E", "W", "I", "A", "C", "N"] ignore = [] # Allow autofix for all enabled rules (when `--fix`) is provided. @@ -178,6 +179,12 @@ unfixable = [] # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +# needed for pydantic +[tool.ruff.lint.pep8-naming] +classmethod-decorators = [ + "root_validator", +] + # Ignore unused imports in __init__.py files. [tool.ruff.lint.extend-per-file-ignores] "__init__.py" = ["F401", "F403"] diff --git a/tests/conftest.py b/tests/conftest.py index 2d210f2f3..d98956053 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -176,8 +176,8 @@ async def adminkey_headers_to(to_wallet): @pytest_asyncio.fixture(scope="session") async def invoice(to_wallet): data = await get_random_invoice_data() - invoiceData = CreateInvoice(**data) - invoice = await api_payments_create_invoice(invoiceData, to_wallet) + invoice_data = CreateInvoice(**data) + invoice = await api_payments_create_invoice(invoice_data, to_wallet) yield invoice del invoice diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 5cb88f6f0..7a1cda89c 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -10,7 +10,7 @@ from lnbits.core.services import fee_reserve_total from lnbits.core.views.admin_api import api_auditor from lnbits.core.views.payment_api import api_payment from lnbits.settings import settings -from lnbits.wallets import get_wallet_class +from lnbits.wallets import get_funding_source from ...helpers import ( cancel_invoice, @@ -22,7 +22,7 @@ from ...helpers import ( settle_invoice, ) -WALLET = get_wallet_class() +funding_source = get_funding_source() # create account POST /api/v1/account @@ -407,7 +407,8 @@ async def test_api_payment_with_key(invoice, inkey_headers_from): # check POST /api/v1/payments: invoice creation with a description hash @pytest.mark.skipif( - WALLET.__class__.__name__ in ["CoreLightningWallet", "CoreLightningRestWallet"], + funding_source.__class__.__name__ + in ["CoreLightningWallet", "CoreLightningRestWallet"], reason="wallet does not support description_hash", ) @pytest.mark.asyncio @@ -428,7 +429,7 @@ async def test_create_invoice_with_description_hash(client, inkey_headers_to): @pytest.mark.skipif( - WALLET.__class__.__name__ in ["CoreLightningRestWallet"], + funding_source.__class__.__name__ in ["CoreLightningRestWallet"], reason="wallet does not support unhashed_description", ) @pytest.mark.asyncio @@ -540,8 +541,8 @@ async def test_pay_real_invoice( payment_status = response.json() assert payment_status["paid"] - WALLET = get_wallet_class() - status = await WALLET.get_payment_status(invoice["payment_hash"]) + funding_source = get_funding_source() + status = await funding_source.get_payment_status(invoice["payment_hash"]) assert status.paid await asyncio.sleep(1) @@ -571,7 +572,7 @@ async def test_create_real_invoice(client, adminkey_headers_from, inkey_headers_ async def listen(): found_checking_id = False - async for checking_id in get_wallet_class().paid_invoices_stream(): + async for checking_id in get_funding_source().paid_invoices_stream(): if checking_id == invoice["checking_id"]: found_checking_id = True return @@ -622,8 +623,8 @@ async def test_pay_real_invoice_set_pending_and_check_state( assert response["paid"] # make sure that the backend also thinks it's paid - WALLET = get_wallet_class() - status = await WALLET.get_payment_status(invoice["payment_hash"]) + funding_source = get_funding_source() + status = await funding_source.get_payment_status(invoice["payment_hash"]) assert status.paid # get the outgoing payment from the db @@ -800,7 +801,7 @@ async def test_receive_real_invoice_set_pending_and_check_state( async def listen(): found_checking_id = False - async for checking_id in get_wallet_class().paid_invoices_stream(): + async for checking_id in get_funding_source().paid_invoices_stream(): if checking_id == invoice["checking_id"]: found_checking_id = True return diff --git a/tests/core/views/test_node_api.py b/tests/core/views/test_node_api.py index 1799c5ec0..c314e4d63 100644 --- a/tests/core/views/test_node_api.py +++ b/tests/core/views/test_node_api.py @@ -10,14 +10,15 @@ from lnbits.nodes.base import ChannelPoint, ChannelState, NodeChannel from tests.conftest import pytest_asyncio, settings from ...helpers import ( - WALLET, + funding_source, get_random_invoice_data, get_unconnected_node_uri, mine_blocks, ) pytestmark = pytest.mark.skipif( - WALLET.__node_cls__ is None, reason="Cant test if node implementation isnt avilable" + funding_source.__node_cls__ is None, + reason="Cant test if node implementation isnt available", ) diff --git a/tests/helpers.py b/tests/helpers.py index 0719119c4..7286932af 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -14,7 +14,7 @@ from pydantic import BaseModel from lnbits import core from lnbits.db import DB_TYPE, POSTGRES, FromRowModel -from lnbits.wallets import get_wallet_class, set_wallet_class +from lnbits.wallets import get_funding_source, set_funding_source class DbTestModel(FromRowModel): @@ -23,10 +23,10 @@ class DbTestModel(FromRowModel): value: Optional[str] = None -def get_random_string(N: int = 10): +def get_random_string(iterations: int = 10): return "".join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) - for _ in range(N) + for _ in range(iterations) ) @@ -34,9 +34,9 @@ async def get_random_invoice_data(): return {"out": False, "amount": 10, "memo": f"test_memo_{get_random_string(10)}"} -set_wallet_class() -WALLET = get_wallet_class() -is_fake: bool = WALLET.__class__.__name__ == "FakeWallet" +set_funding_source() +funding_source = get_funding_source() +is_fake: bool = funding_source.__class__.__name__ == "FakeWallet" is_regtest: bool = not is_fake diff --git a/tools/conv.py b/tools/conv.py index 381dc378d..7595861ad 100644 --- a/tools/conv.py +++ b/tools/conv.py @@ -131,39 +131,39 @@ def migrate_db(file: str, schema: str, exclude_tables: List[str] = []): ).fetchall() for table in tables: - tableName = table[0] - print(f"Migrating table {tableName}") + table_name = table[0] + print(f"Migrating table {table_name}") # hard coded skip for dbversions (already produced during startup) - if tableName == "dbversions": + if table_name == "dbversions": continue - if exclude_tables and tableName in exclude_tables: + if exclude_tables and table_name in exclude_tables: continue - columns = cursor.execute(f"PRAGMA table_info({tableName})").fetchall() - q = build_insert_query(schema, tableName, columns) + columns = cursor.execute(f"PRAGMA table_info({table_name})").fetchall() + q = build_insert_query(schema, table_name, columns) - data = cursor.execute(f"SELECT * FROM {tableName};").fetchall() + data = cursor.execute(f"SELECT * FROM {table_name};").fetchall() if len(data) == 0: - print(f"🛑 You sneaky dev! Table {tableName} is empty!") + print(f"🛑 You sneaky dev! Table {table_name} is empty!") insert_to_pg(q, data) cursor.close() -def build_insert_query(schema, tableName, columns): +def build_insert_query(schema, table_name, columns): to_columns = ", ".join([f'"{column[1].lower()}"' for column in columns]) values = ", ".join([to_column_type(column[2]) for column in columns]) return f""" - INSERT INTO {schema}.{tableName}({to_columns}) + INSERT INTO {schema}.{table_name}({to_columns}) VALUES ({values}); """ -def to_column_type(columnType): - if columnType == "TIMESTAMP": +def to_column_type(column_type): + if column_type == "TIMESTAMP": return "to_timestamp(%s)" - if columnType in ["BOOLEAN", "BOOL"]: + if column_type in ["BOOLEAN", "BOOL"]: return "%s::boolean" return "%s"