chore: update python packages + formatting + cleanup (#3221)

This commit is contained in:
dni ⚡ 2025-06-30 14:53:11 +02:00
parent c4c03d96a3
commit a16078a6ba
No known key found for this signature in database
GPG key ID: D1F416F29AD26E87
53 changed files with 1159 additions and 576 deletions

View file

@ -9,16 +9,13 @@ from .decorators import (
from .exceptions import InvoiceError, PaymentError
__all__ = [
# decorators
"require_admin_key",
"require_invoice_key",
"InvoiceError",
"PaymentError",
"check_admin",
"check_super_user",
"check_user_exists",
# services
"pay_invoice",
"create_invoice",
# exceptions
"PaymentError",
"InvoiceError",
"pay_invoice",
"require_admin_key",
"require_invoice_key",
]

View file

@ -65,7 +65,6 @@ from .middleware import (
add_ip_block_middleware,
add_ratelimit_middleware,
)
from .requestvars import g
from .tasks import (
check_pending_payments,
internal_invoice_listener,
@ -171,8 +170,6 @@ def create_app() -> FastAPI:
name="library",
)
g().base_url = f"http://{settings.host}:{settings.port}"
app.add_middleware(
CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]
)

View file

@ -85,87 +85,78 @@ from .webpush import (
)
__all__ = [
# audit
"create_audit_entry",
# db_versions
"get_db_version",
"get_db_versions",
"update_migration_version",
"delete_dbversion",
# extensions
"create_installed_extension",
"create_user_extension",
"delete_installed_extension",
"drop_extension_db",
"get_installed_extension",
"get_installed_extensions",
"get_user_active_extensions_ids",
"get_user_extension",
"update_installed_extension",
"update_installed_extension_state",
"update_user_extension",
"get_user_extensions",
# payments
"DateTrunc",
"check_internal",
"create_payment",
"delete_expired_invoices",
"delete_wallet_payment",
"get_latest_payments_by_extension",
"get_payment",
"get_payments",
"get_payments_history",
"get_payments_paginated",
"get_standalone_payment",
"get_wallet_payment",
"is_internal_status_success",
"mark_webhook_sent",
"update_payment",
"update_payment_checking_id",
"update_payment_extra",
# settings
"create_admin_settings",
"delete_admin_settings",
"get_admin_settings",
"get_super_settings",
"update_admin_settings",
"update_super_user",
"reset_core_settings",
# tinyurl
"create_tinyurl",
"delete_tinyurl",
"get_tinyurl",
"get_tinyurl_by_url",
# users
"create_account",
"create_admin_settings",
"create_audit_entry",
"create_installed_extension",
"create_payment",
"create_tinyurl",
"create_user_extension",
"create_wallet",
"create_webpush_subscription",
"delete_account",
"delete_accounts_no_wallets",
"delete_admin_settings",
"delete_dbversion",
"delete_expired_invoices",
"delete_installed_extension",
"delete_tinyurl",
"delete_unused_wallets",
"delete_wallet",
"delete_wallet_by_id",
"delete_wallet_payment",
"delete_webpush_subscription",
"delete_webpush_subscriptions",
"drop_extension_db",
"force_delete_wallet",
"get_account",
"get_account_by_email",
"get_account_by_pubkey",
"get_account_by_username",
"get_account_by_username_or_email",
"get_accounts",
"get_user",
"get_user_from_account",
"get_user_access_control_lists",
"update_account",
# wallets
"create_wallet",
"delete_unused_wallets",
"delete_wallet",
"delete_wallet_by_id",
"force_delete_wallet",
"get_admin_settings",
"get_db_version",
"get_db_versions",
"get_installed_extension",
"get_installed_extensions",
"get_latest_payments_by_extension",
"get_payment",
"get_payments",
"get_payments_history",
"get_payments_paginated",
"get_standalone_payment",
"get_super_settings",
"get_tinyurl",
"get_tinyurl_by_url",
"get_total_balance",
"get_user",
"get_user_access_control_lists",
"get_user_active_extensions_ids",
"get_user_extension",
"get_user_extensions",
"get_user_from_account",
"get_wallet",
"get_wallet_for_key",
"get_wallet_payment",
"get_wallets",
"remove_deleted_wallets",
"update_wallet",
# webpush
"create_webpush_subscription",
"delete_webpush_subscription",
"delete_webpush_subscriptions",
"get_webpush_subscription",
"get_webpush_subscriptions_for_user",
"is_internal_status_success",
"mark_webhook_sent",
"remove_deleted_wallets",
"reset_core_settings",
"update_account",
"update_admin_settings",
"update_installed_extension",
"update_installed_extension_state",
"update_migration_version",
"update_payment",
"update_payment_checking_id",
"update_payment_extra",
"update_super_user",
"update_user_extension",
"update_wallet",
]

View file

@ -1,5 +1,5 @@
from time import time
from typing import Any, Optional, Tuple
from typing import Any, Optional
from lnbits.core.crud.wallets import get_total_balance, get_wallet, get_wallets_ids
from lnbits.core.db import db
@ -394,7 +394,7 @@ async def get_daily_stats(
filters: Optional[Filters[PaymentFilters]] = None,
user_id: Optional[str] = None,
conn: Optional[Connection] = None,
) -> Tuple[list[PaymentDailyStats], list[PaymentDailyStats]]:
) -> tuple[list[PaymentDailyStats], list[PaymentDailyStats]]:
if not filters:
filters = Filters()

View file

@ -48,62 +48,54 @@ from .wallets import BaseWallet, CreateWallet, KeyType, Wallet, WalletTypeInfo
from .webpush import CreateWebPushSubscription, WebPushSubscription
__all__ = [
# audit
"AuditEntry",
"AuditFilters",
# lnurl
"CreateLnurl",
"CreateLnurlAuth",
"PayLnurlWData",
# misc
"BalanceDelta",
"Callback",
"ConversionData",
"CoreAppExtra",
"DbVersion",
"SimpleStatus",
# payments
"CreateInvoice",
"CreatePayment",
"DecodePayment",
"PayInvoice",
"Payment",
"PaymentCountField",
"PaymentCountStat",
"PaymentDailyStats",
"PaymentsStatusCount",
"PaymentWalletStats",
"PaymentExtra",
"PaymentFilters",
"PaymentHistoryPoint",
"PaymentState",
# tinyurl
"TinyURL",
# users
"AccessTokenPayload",
"Account",
"AccountFilters",
"AccountOverview",
"UserAcls",
"AuditEntry",
"AuditFilters",
"BalanceDelta",
"BaseWallet",
"Callback",
"ConversionData",
"CoreAppExtra",
"CreateInvoice",
"CreateLnurl",
"CreateLnurlAuth",
"CreatePayment",
"CreateUser",
"RegisterUser",
"CreateWallet",
"CreateWebPushSubscription",
"DbVersion",
"DecodePayment",
"KeyType",
"LoginUsernamePassword",
"LoginUsr",
"PayInvoice",
"PayLnurlWData",
"Payment",
"PaymentCountField",
"PaymentCountStat",
"PaymentDailyStats",
"PaymentExtra",
"PaymentFilters",
"PaymentHistoryPoint",
"PaymentState",
"PaymentWalletStats",
"PaymentsStatusCount",
"RegisterUser",
"ResetUserPassword",
"SimpleStatus",
"TinyURL",
"UpdateBalance",
"UpdateSuperuserPassword",
"UpdateUser",
"UpdateUserPassword",
"UpdateUserPubkey",
"User",
"UserAcls",
"UserExtra",
# wallets
"BaseWallet",
"CreateWallet",
"KeyType",
"Wallet",
"WalletTypeInfo",
# webpush
"CreateWebPushSubscription",
"WebPushSubscription",
]

View file

@ -1,5 +1,4 @@
"""Keycloak SSO Login Helper
"""
"""Keycloak SSO Login Helper"""
from typing import Optional

View file

@ -34,40 +34,33 @@ from .users import (
from .websockets import websocket_manager, websocket_updater
__all__ = [
# funding source
"get_balance_delta",
"switch_to_voidwallet",
# lnurl
"redeem_lnurl_withdraw",
"perform_lnurlauth",
# notifications
"enqueue_notification",
"send_payment_notification",
# payments
"calculate_fiat_amounts",
"check_admin_settings",
"check_transaction_status",
"check_wallet_limits",
"create_invoice",
"create_wallet_invoice",
"create_fiat_invoice",
"fee_reserve",
"fee_reserve_total",
"get_payments_daily_stats",
"pay_invoice",
"service_fee",
"update_pending_payment",
"update_pending_payments",
"update_wallet_balance",
# settings
"check_webpush_settings",
"update_cached_settings",
# users
"check_admin_settings",
"create_fiat_invoice",
"create_invoice",
"create_user_account",
"create_user_account_no_ckeck",
"create_wallet_invoice",
"enqueue_notification",
"fee_reserve",
"fee_reserve_total",
"get_balance_delta",
"get_payments_daily_stats",
"pay_invoice",
"perform_lnurlauth",
"redeem_lnurl_withdraw",
"send_payment_notification",
"service_fee",
"switch_to_voidwallet",
"update_cached_settings",
"update_pending_payment",
"update_pending_payments",
"update_user_account",
"update_user_extensions",
# websockets
"update_wallet_balance",
"websocket_manager",
"websocket_updater",
]

View file

@ -1,5 +1,4 @@
import asyncio
from typing import Tuple
import httpx
from loguru import logger
@ -48,7 +47,7 @@ async def send_nostr_dm(
return dm_event.to_dict()
async def fetch_nip5_details(identifier: str) -> Tuple[str, list[str]]:
async def fetch_nip5_details(identifier: str) -> tuple[str, list[str]]:
identifier, domain = identifier.split("@")
if not identifier or not domain:
raise ValueError("Invalid NIP5 identifier")

View file

@ -4,7 +4,7 @@ import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from http import HTTPStatus
from typing import Optional, Tuple
from typing import Optional
import httpx
from loguru import logger
@ -186,7 +186,7 @@ def is_message_type_enabled(message_type: NotificationType) -> bool:
def _notification_message_to_text(
notification_message: NotificationMessage,
) -> Tuple[str, str]:
) -> tuple[str, str]:
message_type = notification_message.message_type.value
meesage_value = NOTIFICATION_TEMPLATES.get(message_type, message_type)
try:

View file

@ -1,6 +1,7 @@
import asyncio
import traceback
from typing import Callable, Coroutine
from collections.abc import Coroutine
from typing import Callable
from loguru import logger

View file

@ -1,5 +1,5 @@
from http import HTTPStatus
from typing import Annotated, List, Optional, Union
from typing import Annotated, Optional, Union
from urllib.parse import urlencode, urlparse
import httpx
@ -70,7 +70,7 @@ async def robots():
@generic_router.get("/extensions", name="extensions", response_class=HTMLResponse)
async def extensions(request: Request, user: User = Depends(check_user_exists)):
installed_exts: List[InstallableExtension] = await get_installed_extensions()
installed_exts: list[InstallableExtension] = await get_installed_extensions()
installed_exts_ids = [e.id for e in installed_exts]
installable_exts = await InstallableExtension.get_installable_extensions()

View file

@ -1,5 +1,5 @@
from http import HTTPStatus
from typing import List, Optional
from typing import Optional
import httpx
from fastapi import APIRouter, Body, Depends, HTTPException
@ -91,7 +91,7 @@ async def api_get_info(
@node_router.get("/channels")
async def api_get_channels(
node: Node = Depends(require_node),
) -> Optional[List[NodeChannel]]:
) -> Optional[list[NodeChannel]]:
return await node.get_channels()
@ -121,7 +121,7 @@ async def api_delete_channel(
output_index: Optional[int],
force: bool = False,
node: Node = Depends(require_node),
) -> Optional[List[NodeChannel]]:
) -> Optional[list[NodeChannel]]:
return await node.close_channel(
short_id,
(
@ -170,7 +170,7 @@ async def api_get_invoices(
@node_router.get("/peers")
async def api_get_peers(node: Node = Depends(require_node)) -> List[NodePeerInfo]:
async def api_get_peers(node: Node = Depends(require_node)) -> list[NodePeerInfo]:
return await node.get_peers()

View file

@ -2,7 +2,7 @@ import json
import ssl
from http import HTTPStatus
from math import ceil
from typing import List, Optional
from typing import Optional
from urllib.parse import urlparse
import httpx
@ -79,7 +79,7 @@ payment_router = APIRouter(prefix="/api/v1/payments", tags=["Payments"])
name="Payment List",
summary="get list of payments",
response_description="list of payments",
response_model=List[Payment],
response_model=list[Payment],
openapi_extra=generate_filter_params_openapi(PaymentFilters),
)
async def api_payments(
@ -98,7 +98,7 @@ async def api_payments(
@payment_router.get(
"/history",
name="Get payments history",
response_model=List[PaymentHistoryPoint],
response_model=list[PaymentHistoryPoint],
openapi_extra=generate_filter_params_openapi(PaymentFilters),
)
async def api_payments_history(
@ -113,7 +113,7 @@ async def api_payments_history(
@payment_router.get(
"/stats/count",
name="Get payments history for all users",
response_model=List[PaymentCountStat],
response_model=list[PaymentCountStat],
openapi_extra=generate_filter_params_openapi(PaymentFilters),
)
async def api_payments_counting_stats(
@ -135,7 +135,7 @@ async def api_payments_counting_stats(
@payment_router.get(
"/stats/wallets",
name="Get payments history for all users",
response_model=List[PaymentWalletStats],
response_model=list[PaymentWalletStats],
openapi_extra=generate_filter_params_openapi(PaymentFilters),
)
async def api_payments_wallets_stats(
@ -156,7 +156,7 @@ async def api_payments_wallets_stats(
@payment_router.get(
"/stats/daily",
name="Get payments history per day",
response_model=List[PaymentDailyStats],
response_model=list[PaymentDailyStats],
openapi_extra=generate_filter_params_openapi(PaymentFilters),
)
async def api_payments_daily_stats(

View file

@ -2,7 +2,7 @@ import base64
import json
import time
from http import HTTPStatus
from typing import List, Optional
from typing import Optional
from uuid import uuid4
import shortuuid
@ -216,7 +216,7 @@ async def api_users_toggle_admin(user_id: str) -> SimpleStatus:
@users_router.get("/user/{user_id}/wallet", name="Get wallets for user")
async def api_users_get_user_wallet(user_id: str) -> List[Wallet]:
async def api_users_get_user_wallet(user_id: str) -> list[Wallet]:
return await get_wallets(user_id)

View file

@ -1,5 +1,5 @@
from http import HTTPStatus
from typing import Annotated, Literal, Optional, Type, Union
from typing import Annotated, Literal, Optional, Union
import jwt
from fastapi import Cookie, Depends, Query, Request, Security
@ -223,7 +223,7 @@ async def check_super_user(user: Annotated[User, Depends(check_user_exists)]) ->
return user
def parse_filters(model: Type[TFilterModel]):
def parse_filters(model: type[TFilterModel]):
"""
Parses the query params as filters.
:param model: model used for validation of filter values

View file

@ -1,7 +1,8 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, AsyncGenerator, Coroutine, NamedTuple
from collections.abc import AsyncGenerator, Coroutine
from typing import TYPE_CHECKING, NamedTuple
if TYPE_CHECKING:
pass

View file

@ -1,7 +1,8 @@
import asyncio
import json
from collections.abc import AsyncGenerator
from datetime import datetime, timedelta, timezone
from typing import AsyncGenerator, Optional
from typing import Optional
from urllib.parse import urlencode
import httpx

View file

@ -3,7 +3,7 @@ import json
import re
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any, Optional, Type
from typing import Any, Optional
from urllib import request
from urllib.parse import urlparse
@ -17,7 +17,6 @@ from pydantic.schema import field_schema
from lnbits.jinja2_templating import Jinja2Templates
from lnbits.nodes import get_node_class
from lnbits.requestvars import g
from lnbits.settings import settings
from lnbits.utils.crypto import AESCipher
@ -42,7 +41,7 @@ def urlsafe_short_hash() -> str:
def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> str:
base = g().base_url if external else ""
base = f"http://{settings.host}:{settings.port}" if external else ""
url_params = "?"
for key, value in params.items():
url_params += f"{key}={value}&"
@ -154,7 +153,7 @@ def get_current_extension_name() -> str:
return ext_name
def generate_filter_params_openapi(model: Type[FilterModel], keep_optional=False):
def generate_filter_params_openapi(model: type[FilterModel], keep_optional=False):
"""
Generate openapi documentation for Filters. This is intended to be used along
parse_filters (see example)

View file

@ -58,9 +58,9 @@ class LnurlErrorResponseHandler(APIRoute):
__all__ = [
"LnurlErrorResponse",
"LnurlErrorResponseHandler",
"decode",
"encode",
"handle",
"LnurlErrorResponse",
"LnurlErrorResponseHandler",
]

View file

@ -2,7 +2,7 @@ import asyncio
import json
from datetime import datetime, timezone
from http import HTTPStatus
from typing import Any, List, Optional, Union
from typing import Any, Optional, Union
from fastapi import FastAPI, Request, Response
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
@ -61,7 +61,7 @@ class InstalledExtensionMiddleware:
await self.app(scope, receive, send)
def _response_by_accepted_type(
self, scope: Scope, headers: List[Any], msg: str, status_code: HTTPStatus
self, scope: Scope, headers: list[Any], msg: str, status_code: HTTPStatus
) -> Union[HTMLResponse, JSONResponse]:
"""
Build an HTTP response containing the `msg` as HTTP body and the `status_code`

View file

@ -1,10 +0,0 @@
import contextvars
import types
request_global = contextvars.ContextVar(
"request_global", default=types.SimpleNamespace()
)
def g() -> types.SimpleNamespace:
return request_global.get()

View file

@ -2,11 +2,9 @@ import asyncio
import time
import traceback
import uuid
from collections.abc import Coroutine
from typing import (
Callable,
Coroutine,
Dict,
List,
Optional,
)
@ -22,8 +20,8 @@ from lnbits.core.services.fiat_providers import handle_fiat_payment_confirmation
from lnbits.settings import settings
from lnbits.wallets import get_funding_source
tasks: List[asyncio.Task] = []
unique_tasks: Dict[str, asyncio.Task] = {}
tasks: list[asyncio.Task] = []
unique_tasks: dict[str, asyncio.Task] = {}
def create_task(coro: Coroutine) -> asyncio.Task:
@ -83,7 +81,7 @@ async def catch_everything_and_restart(
return await catch_everything_and_restart(func, name)
invoice_listeners: Dict[str, asyncio.Queue] = {}
invoice_listeners: dict[str, asyncio.Queue] = {}
# TODO: name should not be optional

View file

@ -2,7 +2,7 @@ import base64
import hashlib
import json
import re
from typing import Dict, Tuple, Union
from typing import Union
from urllib.parse import urlparse
import secp256k1
@ -13,7 +13,7 @@ from Cryptodome.Util.Padding import pad, unpad
from pynostr.key import PrivateKey
def generate_keypair() -> Tuple[str, str]:
def generate_keypair() -> tuple[str, str]:
private_key = PrivateKey()
public_key = private_key.public_key
return private_key.hex(), public_key.hex()
@ -82,7 +82,7 @@ def decrypt_content(
return decrypted
def verify_event(event: Dict) -> bool:
def verify_event(event: dict) -> bool:
"""
Verify the event signature
@ -115,8 +115,8 @@ def verify_event(event: Dict) -> bool:
def sign_event(
event: Dict, account_public_key_hex: str, account_private_key: secp256k1.PrivateKey
) -> Dict:
event: dict, account_public_key_hex: str, account_private_key: secp256k1.PrivateKey
) -> dict:
"""
Signs the event (in place) with the service secret
@ -149,7 +149,7 @@ def sign_event(
return event
def json_dumps(data: Union[Dict, list]) -> str:
def json_dumps(data: Union[dict, list]) -> str:
"""
Converts a Python dictionary to a JSON string with compact encoding.
@ -159,7 +159,7 @@ def json_dumps(data: Union[Dict, list]) -> str:
Returns:
str: The compact JSON string.
"""
if isinstance(data, Dict):
if isinstance(data, dict):
data = {k: v for k, v in data.items() if v is not None}
return json.dumps(data, separators=(",", ":"), ensure_ascii=False)

View file

@ -58,17 +58,17 @@ __all__ = [
"BlinkWallet",
"BoltzWallet",
"BreezSdkWallet",
"ClicheWallet",
"CoreLightningWallet",
"CLightningWallet",
"ClicheWallet",
"CoreLightningRestWallet",
"CoreLightningWallet",
"EclairWallet",
"FakeWallet",
"LNbitsWallet",
"LndWallet",
"LndRestWallet",
"LNPayWallet",
"LNbitsWallet",
"LnTipsWallet",
"LndRestWallet",
"LndWallet",
"NWCWallet",
"OpenNodeWallet",
"PhoenixdWallet",

View file

@ -1,7 +1,8 @@
import asyncio
import hashlib
import json
from typing import AsyncGenerator, Optional
from collections.abc import AsyncGenerator
from typing import Optional
import httpx
from loguru import logger

View file

@ -2,7 +2,8 @@ from __future__ import annotations
import asyncio
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, AsyncGenerator, Coroutine, NamedTuple
from collections.abc import AsyncGenerator, Coroutine
from typing import TYPE_CHECKING, NamedTuple
from loguru import logger

View file

@ -1,12 +1,13 @@
import asyncio
import hashlib
import json
from typing import AsyncGenerator, Optional
from collections.abc import AsyncGenerator
from typing import Optional
import httpx
from loguru import logger
from pydantic import BaseModel
from websockets.client import WebSocketClientProtocol, connect
from websockets.legacy.client import WebSocketClientProtocol, connect
from websockets.typing import Subprotocol
from lnbits import bolt11

View file

@ -1,5 +1,6 @@
import asyncio
from typing import AsyncGenerator, Optional
from collections.abc import AsyncGenerator
from typing import Optional
from bolt11.decode import decode
from grpc.aio import AioRpcError

View file

@ -20,8 +20,9 @@ if not BREEZ_SDK_INSTALLED:
else:
import asyncio
from collections.abc import AsyncGenerator
from pathlib import Path
from typing import AsyncGenerator, Optional
from typing import Optional
from loguru import logger

View file

@ -1,7 +1,8 @@
import asyncio
import hashlib
import json
from typing import AsyncGenerator, Optional
from collections.abc import AsyncGenerator
from typing import Optional
from loguru import logger
from websocket import create_connection

View file

@ -9,7 +9,7 @@ from typing import Any, Optional
import httpx
from loguru import logger
from websockets.client import connect
from websockets.legacy.client import connect
from lnbits.helpers import normalize_endpoint
from lnbits.settings import settings

View file

@ -1,8 +1,9 @@
import asyncio
from collections.abc import AsyncGenerator
from datetime import datetime
from hashlib import sha256
from os import urandom
from typing import AsyncGenerator, Optional
from typing import Optional
from bolt11 import (
Bolt11,

View file

@ -1,10 +1,11 @@
import asyncio
import json
from typing import AsyncGenerator, Dict, Optional
from collections.abc import AsyncGenerator
from typing import Optional
import httpx
from loguru import logger
from websockets.client import connect
from websockets.legacy.client import connect
from lnbits.helpers import normalize_endpoint
from lnbits.settings import settings
@ -75,7 +76,7 @@ class LNbitsWallet(Wallet):
unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse:
data: Dict = {"out": False, "amount": amount, "memo": memo or ""}
data: dict = {"out": False, "amount": amount, "memo": memo or ""}
if kwargs.get("expiry"):
data["expiry"] = kwargs["expiry"]
if description_hash:

View file

@ -1,8 +1,9 @@
import asyncio
import base64
from collections.abc import AsyncGenerator
from hashlib import sha256
from os import environ
from typing import AsyncGenerator, Dict, Optional
from typing import Optional
import grpc
from loguru import logger
@ -121,7 +122,7 @@ class LndWallet(Wallet):
unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse:
data: Dict = {
data: dict = {
"description_hash": b"",
"value": amount,
"private": True,

View file

@ -2,7 +2,8 @@ import asyncio
import base64
import hashlib
import json
from typing import Any, AsyncGenerator, Dict, Optional
from collections.abc import AsyncGenerator
from typing import Any, Optional
import httpx
from loguru import logger
@ -105,7 +106,7 @@ class LndRestWallet(Wallet):
unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse:
_data: Dict = {
_data: dict = {
"value": amount,
"private": settings.lnd_rest_route_hints,
"memo": memo or "",

View file

@ -1,6 +1,7 @@
import asyncio
import hashlib
from typing import AsyncGenerator, Dict, Optional
from collections.abc import AsyncGenerator
from typing import Optional
import httpx
from loguru import logger
@ -79,7 +80,7 @@ class LNPayWallet(Wallet):
unhashed_description: Optional[bytes] = None,
**_,
) -> InvoiceResponse:
data: Dict = {"num_satoshis": f"{amount}"}
data: dict = {"num_satoshis": f"{amount}"}
if description_hash:
data["description_hash"] = description_hash.hex()
elif unhashed_description:

View file

@ -2,7 +2,8 @@ import asyncio
import hashlib
import json
import time
from typing import AsyncGenerator, Dict, Optional
from collections.abc import AsyncGenerator
from typing import Optional
import httpx
from loguru import logger
@ -73,7 +74,7 @@ class LnTipsWallet(Wallet):
unhashed_description: Optional[bytes] = None,
**_,
) -> InvoiceResponse:
data: Dict = {"amount": amount, "description_hash": "", "memo": memo or ""}
data: dict = {"amount": amount, "description_hash": "", "memo": memo or ""}
if description_hash:
data["description_hash"] = description_hash.hex()
elif unhashed_description:

View file

@ -3,13 +3,14 @@ import hashlib
import json
import random
import time
from typing import AsyncGenerator, Dict, List, Optional, Union, cast
from collections.abc import AsyncGenerator
from typing import Optional, Union, cast
from urllib.parse import parse_qs, unquote, urlparse
import secp256k1
from bolt11 import decode as bolt11_decode
from loguru import logger
from websockets.client import connect as ws_connect
from websockets.legacy.client import connect as ws_connect
from lnbits.settings import settings
from lnbits.utils.nostr import (
@ -358,7 +359,7 @@ class NWCConnection:
"""
return self.shutdown or not settings.lnbits_running
async def _send(self, data: List[Union[str, Dict]]):
async def _send(self, data: list[Union[str, dict]]):
"""
Sends data to the NWC relay.
@ -394,7 +395,7 @@ class NWCConnection:
async def _close_subscription_by_subid(
self, sub_id: str, send_event: bool = True
) -> Optional[Dict]:
) -> Optional[dict]:
"""
Closes a subscription by its sub_id.
@ -425,7 +426,7 @@ class NWCConnection:
async def _close_subscription_by_eventid(
self, event_id, send_event=True
) -> Optional[Dict]:
) -> Optional[dict]:
"""
Closes a subscription associated to an event_id.
@ -498,7 +499,7 @@ class NWCConnection:
except Exception as e:
logger.error("Error handling subscription timeout: " + str(e))
async def _on_ok_message(self, msg: List[str]):
async def _on_ok_message(self, msg: list[str]):
"""
Handles OK messages from the relay.
"""
@ -512,12 +513,12 @@ class NWCConnection:
if subscription: # Check if the subscription exists first
subscription["future"].set_exception(Exception(info))
async def _on_event_message(self, msg: List[Union[str, Dict]]):
async def _on_event_message(self, msg: list[Union[str, dict]]):
"""
Handles EVENT messages from the relay.
"""
sub_id = cast(str, msg[1])
event = cast(Dict, msg[2])
event = cast(dict, msg[2])
if not verify_event(event): # Ensure the event is valid (do not trust relays)
raise Exception("Invalid event signature")
tags = event["tags"]
@ -571,7 +572,7 @@ class NWCConnection:
else:
subscription["future"].set_result(result)
async def _on_closed_message(self, msg: List[str]):
async def _on_closed_message(self, msg: list[str]):
"""
Handles CLOSED messages from the relay.
"""
@ -646,7 +647,7 @@ class NWCConnection:
logger.debug("Reconnecting to NWC relay in 5 seconds...")
await asyncio.sleep(5)
async def call(self, method: str, params: Dict) -> Dict:
async def call(self, method: str, params: dict) -> dict:
"""
Call a NWC method.
@ -708,7 +709,7 @@ class NWCConnection:
# Wait for the response
return await future
async def get_info(self) -> Dict:
async def get_info(self) -> dict:
"""
Get the info about the service provider and cache it.
@ -793,7 +794,7 @@ class NWCConnection:
logger.warning("Error closing connection: " + str(e))
def parse_nwc(nwc) -> Dict:
def parse_nwc(nwc) -> dict:
"""
Parses a NWC URL (nostr+walletconnect://...) and extracts relevant information.

View file

@ -1,5 +1,6 @@
import asyncio
from typing import AsyncGenerator, Optional
from collections.abc import AsyncGenerator
from typing import Optional
import httpx
from loguru import logger

View file

@ -3,11 +3,12 @@ import base64
import hashlib
import json
import urllib.parse
from typing import Any, AsyncGenerator, Dict, Optional
from collections.abc import AsyncGenerator
from typing import Any, Optional
import httpx
from loguru import logger
from websockets.client import connect
from websockets.legacy.client import connect
from lnbits.helpers import normalize_endpoint
from lnbits.settings import settings
@ -101,7 +102,7 @@ class PhoenixdWallet(Wallet):
try:
msats_amount = amount
data: Dict[str, Any] = {
data: dict[str, Any] = {
"amountSat": f"{msats_amount}",
"externalId": "",
}

View file

@ -2,7 +2,8 @@ import asyncio
import hashlib
import json
import random
from typing import AsyncGenerator, Optional
from collections.abc import AsyncGenerator
from typing import Optional
import httpx
from loguru import logger

View file

@ -1,7 +1,8 @@
import asyncio
import time
from collections.abc import AsyncGenerator
from decimal import Decimal
from typing import Any, AsyncGenerator, Dict, Optional
from typing import Any, Optional
import httpx
from loguru import logger
@ -111,8 +112,8 @@ class StrikeWallet(Wallet):
# runtime state
self.pending_invoices: list[str] = [] # Keep it as a list
self.pending_payments: Dict[str, str] = {}
self.failed_payments: Dict[str, str] = {}
self.pending_payments: dict[str, str] = {}
self.failed_payments: dict[str, str] = {}
# balance cache
self._cached_balance: Optional[int] = None
@ -183,7 +184,7 @@ class StrikeWallet(Wallet):
if btc and "available" in btc:
available_btc = Decimal(btc["available"]) # Get available BTC amount.
msats = int(
available_btc * Decimal(1e11)
available_btc * Decimal("1e11")
) # Convert BTC to millisatoshis.
self._cached_balance = msats
self._cached_balance_ts = now
@ -204,10 +205,10 @@ class StrikeWallet(Wallet):
**kwargs,
) -> InvoiceResponse:
try:
btc_amt = (Decimal(amount) / Decimal(1e8)).quantize(
btc_amt = (Decimal(amount) / Decimal("1e8")).quantize(
Decimal("0.00000001")
) # Convert amount from millisatoshis to BTC.
payload: Dict[str, Any] = {
payload: dict[str, Any] = {
"bolt11": {
"amount": {
"currency": "BTC",
@ -270,7 +271,7 @@ class StrikeWallet(Wallet):
# Network fee → msat.
fee_obj = data.get("lightningNetworkFee") or data.get("totalFee") or {}
fee_btc = Decimal(fee_obj.get("amount", "0"))
fee_msat = int(fee_btc * Decimal(1e11)) # millisatoshis.
fee_msat = int(fee_btc * Decimal("1e11")) # millisatoshis.
if state in {"SUCCEEDED", "COMPLETED"}:
preimage = data.get("preimage") or data.get("preImage")
@ -427,9 +428,9 @@ class StrikeWallet(Wallet):
orderby: Optional[str] = None,
skip: Optional[int] = None,
top: Optional[int] = None,
) -> Dict[str, Any]:
) -> dict[str, Any]:
try:
params: Dict[str, Any] = {}
params: dict[str, Any] = {}
if filters:
params["$filter"] = filters
if orderby:
@ -465,7 +466,7 @@ class StrikeWallet(Wallet):
try:
if currency_str == "BTC":
fee_btc_decimal = Decimal(amount_str)
fee_msat = int(fee_btc_decimal * Decimal(1e11))
fee_msat = int(fee_btc_decimal * Decimal("1e11"))
elif currency_str == "SAT":
fee_sat_decimal = Decimal(amount_str)
fee_msat = int(fee_sat_decimal * 1000)

View file

@ -1,4 +1,4 @@
from typing import AsyncGenerator
from collections.abc import AsyncGenerator
from loguru import logger

View file

@ -1,6 +1,7 @@
import asyncio
import hashlib
from typing import AsyncGenerator, Dict, Optional
from collections.abc import AsyncGenerator
from typing import Optional
import httpx
from bolt11 import decode as bolt11_decode
@ -67,7 +68,7 @@ class ZBDWallet(Wallet):
# https://api.zebedee.io/v0/charges
msats_amount = amount * 1000
data: Dict = {
data: dict = {
"amount": f"{msats_amount}",
"expiresIn": 3600,
"callbackUrl": "",

1137
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -14,49 +14,49 @@ packages = [
[tool.poetry.dependencies]
python = "~3.12 | ~3.11 | ~3.10"
bech32 = "1.2.0"
click = "8.1.7"
ecdsa = "0.19.0"
fastapi = "0.115.2"
click = "8.2.1"
ecdsa = "0.19.1"
fastapi = "0.115.13"
httpx = "0.27.0"
jinja2 = "3.1.4"
jinja2 = "3.1.6"
lnurl = "0.5.3"
pydantic = "1.10.18"
pyqrcode = "1.2.1"
shortuuid = "1.0.13"
sse-starlette = "1.8.2"
typing-extensions = "4.12.2"
uvicorn = "0.34.2"
sse-starlette = "2.3.6"
typing-extensions = "4.14.0"
uvicorn = "0.34.3"
sqlalchemy = "1.4.54"
aiosqlite = "0.20.0"
aiosqlite = "0.21.0"
asyncpg = "0.30.0"
uvloop = "0.21.0"
websockets = "11.0.3"
loguru = "0.7.2"
websockets = "15.0.1"
loguru = "0.7.3"
grpcio = "1.69.0"
protobuf = "5.29.1"
pyln-client = "24.11"
pywebpush = "1.14.1"
pyln-client = "25.5"
pywebpush = "2.0.3"
slowapi = "0.1.9"
websocket-client = "1.8.0"
pycryptodomex = "3.20.0"
packaging = "24.0"
bolt11 = "2.1.0"
pyjwt = "2.9.0"
pycryptodomex = "3.23.0"
packaging = "25.0"
bolt11 = "2.1.1"
pyjwt = "2.10.1"
passlib = "1.7.4"
itsdangerous = "2.2.0"
fastapi-sso = "0.15.0"
fastapi-sso = "0.18.0"
# needed for boltz, lnurldevice, watchonly extensions
embit = "0.8.0"
# needed for cashu, lnurlp, nostrclient, nostrmarket, nostrrelay extensions
secp256k1 = "0.14.0"
# keep for backwards compatibility with lnurlp and cashu
environs = "9.5.0"
environs = "14.2.0"
# needed for scheduler extension
python-crontab = "3.2.0"
# needed for liquid support boltz
wallycore = {version = "1.4.0", optional = true}
# needed for breez funding source
breez-sdk = {version = "0.6.6", optional = true}
breez-sdk = {version = "0.8.0", optional = true}
jsonpath-ng = "^1.7.0"
pynostr = "^0.6.2"
@ -69,15 +69,15 @@ breez = ["breez-sdk"]
liquid = ["wallycore"]
[tool.poetry.group.dev.dependencies]
black = "^24.8.0"
black = "^25.1.0"
mypy = "^1.11.2"
types-protobuf = "^5.27.0.20240626"
pre-commit = "^3.8.0"
types-protobuf = "^6.30.2.20250516"
pre-commit = "^4.2.0"
openapi-spec-validator = "^0.7.1"
ruff = "^0.6.4"
ruff = "^0.12.0"
types-passlib = "^1.7.7.20240327"
openai = "^1.39.0"
json5 = "^0.9.25"
json5 = "^0.12.0"
asgi-lifespan = "^2.1.0"
anyio = "^4.7.0"
pytest = "^8.3.4"

View file

@ -3,7 +3,6 @@ import json
import os
import time
from subprocess import PIPE, Popen, TimeoutExpired
from typing import Tuple
from loguru import logger
@ -84,7 +83,7 @@ def run_cmd_json(cmd: list) -> dict:
raise
def get_hold_invoice(sats: int) -> Tuple[str, dict]:
def get_hold_invoice(sats: int) -> tuple[str, dict]:
preimage = os.urandom(32)
preimage_hash = hashlib.sha256(preimage).hexdigest()
cmd = docker_lightning_cli.copy()

View file

@ -1,4 +1,4 @@
from typing import Dict, List, Optional, Union
from typing import Optional, Union
from pydantic import BaseModel
@ -41,24 +41,24 @@ class Mock(FunctionMock, TestMock):
class FunctionMocks(BaseModel):
mocks: Dict[str, FunctionMock]
mocks: dict[str, FunctionMock]
class FunctionTest(BaseModel):
description: str
call_params: dict
expect: dict
mocks: Dict[str, List[Dict[str, TestMock]]]
mocks: dict[str, list[dict[str, TestMock]]]
class FunctionData(BaseModel):
"""Data required for testing this function"""
"Function level mocks that apply for all tests of this function"
mocks: List[FunctionMock] = []
mocks: list[FunctionMock] = []
"All the tests for this function"
tests: List[FunctionTest] = []
tests: list[FunctionTest] = []
class WalletTest(BaseModel):
@ -69,7 +69,7 @@ class WalletTest(BaseModel):
call_params: Optional[dict] = {}
expect: Optional[dict]
expect_error: Optional[dict]
mocks: List[Mock] = []
mocks: list[Mock] = []
@staticmethod
def tests_for_funding_source(
@ -77,7 +77,7 @@ class WalletTest(BaseModel):
fn_name: str,
fn,
test,
) -> List["WalletTest"]:
) -> list["WalletTest"]:
t = WalletTest(
**{
"funding_source": fs,
@ -96,7 +96,7 @@ class WalletTest(BaseModel):
return [t]
def _tests_from_fs_mocks(self, fn, test, fs_name: str) -> List["WalletTest"]:
def _tests_from_fs_mocks(self, fn, test, fs_name: str) -> list["WalletTest"]:
fs_mocks = fn["mocks"][fs_name]
test_mocks = test["mocks"][fs_name]
@ -132,7 +132,7 @@ class WalletTest(BaseModel):
def _tests_from_mock(self, mock_obj) -> "WalletTest":
test_mocks: List[Mock] = [
test_mocks: list[Mock] = [
Mock.combine_mocks(
mock_name,
mock_obj[mock_name]["fs_mock"],

View file

@ -2,7 +2,6 @@ import functools
import importlib
import json
import operator
from typing import Dict, List
import pytest
@ -12,7 +11,7 @@ from tests.wallets.fixtures.models import FundingSourceConfig, WalletTest
wallets_module = importlib.import_module("lnbits.wallets")
def wallet_fixtures_from_json(path) -> List["WalletTest"]:
def wallet_fixtures_from_json(path) -> list["WalletTest"]:
with open(path) as f:
data = json.load(f)
@ -20,7 +19,7 @@ def wallet_fixtures_from_json(path) -> List["WalletTest"]:
FundingSourceConfig(name=fs_name, **data["funding_sources"][fs_name])
for fs_name in data["funding_sources"]
]
tests: Dict[str, List[WalletTest]] = {}
tests: dict[str, list[WalletTest]] = {}
for fn_name in data["functions"]:
fn = data["functions"][fn_name]
fn_tests = _tests_for_function(funding_sources, fn_name, fn)
@ -33,9 +32,9 @@ def wallet_fixtures_from_json(path) -> List["WalletTest"]:
def _tests_for_function(
funding_sources: List[FundingSourceConfig], fn_name: str, fn
) -> Dict[str, List[WalletTest]]:
tests: Dict[str, List[WalletTest]] = {}
funding_sources: list[FundingSourceConfig], fn_name: str, fn
) -> dict[str, list[WalletTest]]:
tests: dict[str, list[WalletTest]] = {}
for test in fn["tests"]:
"""create an unit test for each funding source"""
@ -46,9 +45,9 @@ def _tests_for_function(
def _tests_for_funding_source(
funding_sources: List[FundingSourceConfig], fn_name: str, fn, test
) -> Dict[str, List[WalletTest]]:
tests: Dict[str, List[WalletTest]] = {fs.name: [] for fs in funding_sources}
funding_sources: list[FundingSourceConfig], fn_name: str, fn, test
) -> dict[str, list[WalletTest]]:
tests: dict[str, list[WalletTest]] = {fs.name: [] for fs in funding_sources}
for fs in funding_sources:
tests[fs.name] += WalletTest.tests_for_funding_source(fs, fn_name, fn, test)
return tests
@ -132,7 +131,7 @@ async def _assert_error(wallet, tested_func, call_params, expect_error):
assert e_info.match(expect_error["message"])
def _merge_dict_of_lists(v1: Dict[str, List], v2: Dict[str, List]):
def _merge_dict_of_lists(v1: dict[str, list], v2: dict[str, list]):
"""Merge v2 into v1"""
for k in v2:
v1[k] = v2[k] if k not in v1 else v1[k] + v2[k]

View file

@ -2,14 +2,14 @@ import base64
import hashlib
import json
import time
from typing import Dict, cast
from typing import cast
import pytest
import secp256k1
from Cryptodome import Random
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad, unpad
from websockets.server import serve as ws_serve
from websockets.legacy.server import serve as ws_serve
from lnbits.wallets.nwc import NWCWallet
from tests.wallets.helpers import (
@ -49,7 +49,7 @@ def decrypt_content(priv_key, source_pub_key, content):
def json_dumps(data):
if isinstance(data, Dict):
if isinstance(data, dict):
data = {k: v for k, v in data.items() if v is not None}
return json.dumps(data, separators=(",", ":"), ensure_ascii=False)

View file

@ -1,5 +1,5 @@
import json
from typing import Dict, Union
from typing import Union
from urllib.parse import urlencode
import pytest
@ -59,7 +59,7 @@ async def test_rest_wallet(httpserver: HTTPServer, test_data: WalletTest):
def _apply_mock(httpserver: HTTPServer, mock: Mock):
request_data: Dict[str, Union[str, dict, list]] = {}
request_data: dict[str, Union[str, dict, list]] = {}
request_type = getattr(mock.dict(), "request_type", None)
# request_type = mock.request_type <--- this des not work for whatever reason!!!

View file

@ -1,5 +1,5 @@
import importlib
from typing import Dict, List, Optional
from typing import Optional
from unittest.mock import AsyncMock, Mock
import pytest
@ -93,7 +93,7 @@ def _check_calls(expected_calls):
def _spy_mocks(mocker: MockerFixture, test_data: WalletTest, wallet: BaseWallet):
expected_calls: Dict[str, List] = {}
expected_calls: dict[str, list] = {}
for mock in test_data.mocks:
client_field = getattr(wallet, mock.name)
spy = _spy_mock(mocker, mock, client_field)
@ -104,7 +104,7 @@ def _spy_mocks(mocker: MockerFixture, test_data: WalletTest, wallet: BaseWallet)
def _spy_mock(mocker: MockerFixture, mock: RpcMock, client_field):
expected_calls: Dict[str, List] = {}
expected_calls: dict[str, list] = {}
assert isinstance(mock.response, dict), "Expected data RPC response"
for field_name in mock.response:
value = mock.response[field_name]

View file

@ -7,7 +7,7 @@ import argparse
import os
import sqlite3
import sys
from typing import List, Optional
from typing import Optional
from lnbits.settings import settings
@ -108,7 +108,7 @@ def insert_to_pg(query, data):
connection.close()
def migrate_core(file: str, exclude_tables: Optional[List[str]] = None):
def migrate_core(file: str, exclude_tables: Optional[list[str]] = None):
if exclude_tables is None:
exclude_tables = []
print(f"Migrating core: {file}")
@ -124,7 +124,7 @@ def migrate_ext(file: str):
print(f"✅ Migrated ext: {schema}")
def migrate_db(file: str, schema: str, exclude_tables: Optional[List[str]] = None):
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 = []