fix: python versions were defined incorrectly and remove 3.9 (#3006)

* chore: update requirements to python3.10
* chore: format RUF007 rule for py310
* wrongly specified python version
python3.13 there is a greenlet build error
This commit is contained in:
dni ⚡ 2025-03-03 12:53:30 +01:00 committed by GitHub
parent 4ce14e2312
commit c5964436b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 351 additions and 382 deletions

View File

@ -226,7 +226,7 @@ Problems installing? These commands have helped us install LNbits.
sudo apt install pkg-config libffi-dev libpq-dev
# build essentials for debian/ubuntu
sudo apt install python3.9-dev gcc build-essential
sudo apt install python3.10-dev gcc build-essential
# if the secp256k1 build fails:
# if you used poetry

View File

@ -1,7 +1,6 @@
from __future__ import annotations
from datetime import datetime, timedelta, timezone
from typing import Optional
from pydantic import BaseModel, Field
@ -10,16 +9,16 @@ from lnbits.settings import settings
class AuditEntry(BaseModel):
component: Optional[str] = None
ip_address: Optional[str] = None
user_id: Optional[str] = None
path: Optional[str] = None
request_type: Optional[str] = None
request_method: Optional[str] = None
request_details: Optional[str] = None
response_code: Optional[str] = None
component: str | None = None
ip_address: str | None = None
user_id: str | None = None
path: str | None = None
request_type: str | None = None
request_method: str | None = None
request_details: str | None = None
response_code: str | None = None
duration: float
delete_at: Optional[datetime] = None
delete_at: datetime | None = None
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
def __init__(self, **data):
@ -42,12 +41,12 @@ class AuditFilters(FilterModel):
"duration",
]
ip_address: Optional[str] = None
user_id: Optional[str] = None
path: Optional[str] = None
request_method: Optional[str] = None
response_code: Optional[str] = None
component: Optional[str] = None
ip_address: str | None = None
user_id: str | None = None
path: str | None = None
request_method: str | None = None
response_code: str | None = None
component: str | None = None
class AuditCountStat(BaseModel):

View File

@ -7,7 +7,7 @@ import os
import shutil
import zipfile
from pathlib import Path
from typing import Any, Optional
from typing import Any
import httpx
from loguru import logger
@ -29,17 +29,17 @@ class ExplicitRelease(BaseModel):
archive: str
hash: str
dependencies: list[str] = []
repo: Optional[str]
icon: Optional[str]
short_description: Optional[str]
min_lnbits_version: Optional[str]
max_lnbits_version: Optional[str]
html_url: Optional[str] # todo: release_url
warning: Optional[str]
info_notification: Optional[str]
critical_notification: Optional[str]
details_link: Optional[str]
pay_link: Optional[str]
repo: str | None
icon: str | None
short_description: str | None
min_lnbits_version: str | None
max_lnbits_version: str | None
html_url: str | None # todo: release_url
warning: str | None
info_notification: str | None
critical_notification: str | None
details_link: str | None
pay_link: str | None
def is_version_compatible(self):
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
@ -77,9 +77,9 @@ class ExtensionConfig(BaseModel):
name: str
short_description: str
tile: str = ""
warning: Optional[str] = ""
min_lnbits_version: Optional[str]
max_lnbits_version: Optional[str]
warning: str | None = ""
min_lnbits_version: str | None
max_lnbits_version: str | None
def is_version_compatible(self) -> bool:
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
@ -87,7 +87,7 @@ class ExtensionConfig(BaseModel):
@classmethod
async def fetch_github_release_config(
cls, org: str, repo: str, tag_name: str
) -> Optional[ExtensionConfig]:
) -> ExtensionConfig | None:
config_url = (
f"https://raw.githubusercontent.com/{org}/{repo}/{tag_name}/config.json"
)
@ -97,28 +97,28 @@ class ExtensionConfig(BaseModel):
class ReleasePaymentInfo(BaseModel):
amount: Optional[int] = None
pay_link: Optional[str] = None
payment_hash: Optional[str] = None
payment_request: Optional[str] = None
amount: int | None = None
pay_link: str | None = None
payment_hash: str | None = None
payment_request: str | None = None
class PayToEnableInfo(BaseModel):
amount: int = 0
required: bool = False
wallet: Optional[str] = None
wallet: str | None = None
class UserExtensionInfo(BaseModel):
paid_to_enable: Optional[bool] = False
payment_hash_to_enable: Optional[str] = None
paid_to_enable: bool | None = False
payment_hash_to_enable: str | None = None
class UserExtension(BaseModel):
user: str
extension: str
active: bool
extra: Optional[UserExtensionInfo] = None
extra: UserExtensionInfo | None = None
@property
def is_paid(self) -> bool:
@ -140,10 +140,10 @@ class UserExtension(BaseModel):
class Extension(BaseModel):
code: str
is_valid: bool
name: Optional[str] = None
short_description: Optional[str] = None
tile: Optional[str] = None
upgrade_hash: Optional[str] = ""
name: str | None = None
short_description: str | None = None
tile: str | None = None
upgrade_hash: str | None = ""
@property
def module_name(self) -> str:
@ -176,21 +176,21 @@ class ExtensionRelease(BaseModel):
archive: str
source_repo: str
is_github_release: bool = False
hash: Optional[str] = None
min_lnbits_version: Optional[str] = None
max_lnbits_version: Optional[str] = None
is_version_compatible: Optional[bool] = True
html_url: Optional[str] = None
description: Optional[str] = None
warning: Optional[str] = None
repo: Optional[str] = None
icon: Optional[str] = None
details_link: Optional[str] = None
hash: str | None = None
min_lnbits_version: str | None = None
max_lnbits_version: str | None = None
is_version_compatible: bool | None = True
html_url: str | None = None
description: str | None = None
warning: str | None = None
repo: str | None = None
icon: str | None = None
details_link: str | None = None
pay_link: Optional[str] = None
cost_sats: Optional[int] = None
paid_sats: Optional[int] = 0
payment_hash: Optional[str] = None
pay_link: str | None = None
cost_sats: int | None = None
paid_sats: int | None = 0
payment_hash: str | None = None
@property
def archive_url(self) -> str:
@ -208,8 +208,8 @@ class ExtensionRelease(BaseModel):
self.cost_sats = payment_info.amount if payment_info else None
async def fetch_release_payment_info(
self, amount: Optional[int] = None
) -> Optional[ReleasePaymentInfo]:
self, amount: int | None = None
) -> ReleasePaymentInfo | None:
url = f"{self.pay_link}?amount={amount}" if amount else self.pay_link
assert url, "Missing URL for payment info."
try:
@ -281,7 +281,7 @@ class ExtensionRelease(BaseModel):
return [GitHubRepoRelease.parse_obj(r) for r in releases]
@classmethod
async def fetch_release_details(cls, details_link: str) -> Optional[dict]:
async def fetch_release_details(cls, details_link: str) -> dict | None:
try:
async with httpx.AsyncClient() as client:
@ -300,12 +300,12 @@ class ExtensionRelease(BaseModel):
class ExtensionMeta(BaseModel):
installed_release: Optional[ExtensionRelease] = None
latest_release: Optional[ExtensionRelease] = None
pay_to_enable: Optional[PayToEnableInfo] = None
installed_release: ExtensionRelease | None = None
latest_release: ExtensionRelease | None = None
pay_to_enable: PayToEnableInfo | None = None
payments: list[ReleasePaymentInfo] = []
dependencies: list[str] = []
archive: Optional[str] = None
archive: str | None = None
featured: bool = False
@ -313,11 +313,11 @@ class InstallableExtension(BaseModel):
id: str
name: str
version: str
active: Optional[bool] = False
short_description: Optional[str] = None
icon: Optional[str] = None
active: bool | None = False
short_description: str | None = None
icon: str | None = None
stars: int = 0
meta: Optional[ExtensionMeta] = None
meta: ExtensionMeta | None = None
@property
def hash(self) -> str:
@ -452,7 +452,7 @@ class InstallableExtension(BaseModel):
shutil.rmtree(self.ext_upgrade_dir, True)
def check_latest_version(self, release: Optional[ExtensionRelease]):
def check_latest_version(self, release: ExtensionRelease | None):
if not release:
return
if not self.meta or not self.meta.latest_release:
@ -465,9 +465,7 @@ class InstallableExtension(BaseModel):
):
self.meta.latest_release = release
def find_existing_payment(
self, pay_link: Optional[str]
) -> Optional[ReleasePaymentInfo]:
def find_existing_payment(self, pay_link: str | None) -> ReleasePaymentInfo | None:
if not pay_link or not self.meta or not self.meta.payments:
return None
return next(
@ -507,7 +505,7 @@ class InstallableExtension(BaseModel):
@classmethod
async def from_github_release(
cls, github_release: GitHubRelease
) -> Optional[InstallableExtension]:
) -> InstallableExtension | None:
try:
repo, latest_release, config = await cls.fetch_github_repo_info(
github_release.organisation, github_release.repository
@ -546,7 +544,7 @@ class InstallableExtension(BaseModel):
)
@classmethod
def from_ext_dir(cls, ext_id: str) -> Optional[InstallableExtension]:
def from_ext_dir(cls, ext_id: str) -> InstallableExtension | None:
try:
conf_path = Path(
settings.lnbits_extensions_path, "extensions", ext_id, "config.json"
@ -657,7 +655,7 @@ class InstallableExtension(BaseModel):
@classmethod
async def get_extension_release(
cls, ext_id: str, source_repo: str, archive: str, version: str
) -> Optional[ExtensionRelease]:
) -> ExtensionRelease | None:
all_releases: list[ExtensionRelease] = (
await InstallableExtension.get_extension_releases(ext_id)
)
@ -708,8 +706,8 @@ class CreateExtension(BaseModel):
archive: str
source_repo: str
version: str
cost_sats: Optional[int] = 0
payment_hash: Optional[str] = None
cost_sats: int | None = 0
payment_hash: str | None = None
class ExtensionDetailsRequest(BaseModel):
@ -718,7 +716,7 @@ class ExtensionDetailsRequest(BaseModel):
version: str
async def github_api_get(url: str, error_msg: Optional[str]) -> Any:
async def github_api_get(url: str, error_msg: str | None) -> Any:
headers = {"User-Agent": settings.user_agent}
if settings.lnbits_ext_github_token:
headers["Authorization"] = f"Bearer {settings.lnbits_ext_github_token}"
@ -730,7 +728,7 @@ async def github_api_get(url: str, error_msg: Optional[str]) -> Any:
return resp.json()
def icon_to_github_url(source_repo: str, path: Optional[str]) -> str:
def icon_to_github_url(source_repo: str, path: str | None) -> str:
if not path:
return ""
_, _, *rest = path.split("/")

View File

@ -2,7 +2,7 @@ from __future__ import annotations
from datetime import datetime, timezone
from enum import Enum
from typing import Literal, Optional
from typing import Literal
from fastapi import Query
from pydantic import BaseModel, Field, validator
@ -28,16 +28,16 @@ class PaymentState(str, Enum):
class PaymentExtra(BaseModel):
comment: Optional[str] = None
success_action: Optional[str] = None
lnurl_response: Optional[str] = None
comment: str | None = None
success_action: str | None = None
lnurl_response: str | None = None
class PayInvoice(BaseModel):
payment_request: str
description: Optional[str] = None
max_sat: Optional[int] = None
extra: Optional[dict] = {}
description: str | None = None
max_sat: int | None = None
extra: dict | None = {}
class CreatePayment(BaseModel):
@ -46,10 +46,10 @@ class CreatePayment(BaseModel):
bolt11: str
amount_msat: int
memo: str
extra: Optional[dict] = {}
preimage: Optional[str] = None
expiry: Optional[datetime] = None
webhook: Optional[str] = None
extra: dict | None = {}
preimage: str | None = None
expiry: datetime | None = None
webhook: str | None = None
fee: int = 0
@ -61,13 +61,13 @@ class Payment(BaseModel):
fee: int
bolt11: str
status: str = PaymentState.PENDING
memo: Optional[str] = None
expiry: Optional[datetime] = None
webhook: Optional[str] = None
webhook_status: Optional[int] = None
preimage: Optional[str] = None
tag: Optional[str] = None
extension: Optional[str] = None
memo: str | None = None
expiry: datetime | None = None
webhook: str | None = None
webhook_status: int | None = None
preimage: str | None = None
tag: str | None = None
extension: str | None = None
time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
@ -129,16 +129,16 @@ class PaymentFilters(FilterModel):
__sort_fields__ = ["created_at", "amount", "fee", "memo", "time", "tag"]
status: Optional[str]
tag: Optional[str]
checking_id: Optional[str]
status: str | None
tag: str | None
checking_id: str | None
amount: int
fee: int
memo: Optional[str]
memo: str | None
time: datetime
preimage: Optional[str]
payment_hash: Optional[str]
wallet_id: Optional[str]
preimage: str | None
payment_hash: str | None
wallet_id: str | None
class PaymentDataPoint(BaseModel):
@ -173,11 +173,11 @@ class PaymentWalletStats(BaseModel):
class PaymentDailyStats(BaseModel):
date: datetime
balance: float = 0
balance_in: Optional[float] = 0
balance_out: Optional[float] = 0
balance_in: float | None = 0
balance_out: float | None = 0
payments_count: int = 0
count_in: Optional[int] = 0
count_out: Optional[int] = 0
count_in: int | None = 0
count_out: int | None = 0
fee: float = 0
@ -190,7 +190,7 @@ class PaymentHistoryPoint(BaseModel):
class DecodePayment(BaseModel):
data: str
filter_fields: Optional[list[str]] = []
filter_fields: list[str] | None = []
class CreateInvoice(BaseModel):
@ -198,14 +198,14 @@ class CreateInvoice(BaseModel):
internal: bool = False
out: bool = True
amount: float = Query(None, ge=0)
memo: Optional[str] = None
description_hash: Optional[str] = None
unhashed_description: Optional[str] = None
expiry: Optional[int] = None
extra: Optional[dict] = None
webhook: Optional[str] = None
bolt11: Optional[str] = None
lnurl_callback: Optional[str] = None
memo: str | None = None
description_hash: str | None = None
unhashed_description: str | None = None
expiry: int | None = None
extra: dict | None = None
webhook: str | None = None
bolt11: str | None = None
lnurl_callback: str | None = None
@validator("unit")
@classmethod

View File

@ -1,7 +1,6 @@
from __future__ import annotations
from datetime import datetime, timezone
from typing import Optional
from uuid import UUID
from fastapi import Query
@ -17,16 +16,16 @@ from .wallets import Wallet
class UserExtra(BaseModel):
email_verified: Optional[bool] = False
first_name: Optional[str] = None
last_name: Optional[str] = None
display_name: Optional[str] = None
picture: Optional[str] = None
email_verified: bool | None = False
first_name: str | None = None
last_name: str | None = None
display_name: str | None = None
picture: str | None = None
# Auth provider, possible values:
# - "env": the user was created automatically by the system
# - "lnbits": the user was created via register form (username/pass or user_id only)
# - "google | github | ...": the user was created using an SSO provider
provider: Optional[str] = "lnbits" # auth provider
provider: str | None = "lnbits" # auth provider
class EndpointAccess(BaseModel):
@ -50,13 +49,13 @@ class AccessControlList(BaseModel):
endpoints: list[EndpointAccess] = []
token_id_list: list[SimpleItem] = []
def get_endpoint(self, path: str) -> Optional[EndpointAccess]:
def get_endpoint(self, path: str) -> EndpointAccess | None:
for e in self.endpoints:
if e.path == path:
return e
return None
def get_token_by_id(self, token_id: str) -> Optional[SimpleItem]:
def get_token_by_id(self, token_id: str) -> SimpleItem | None:
for t in self.token_id_list:
if t.id == token_id:
return t
@ -71,7 +70,7 @@ class UserAcls(BaseModel):
access_control_list: list[AccessControlList] = []
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
def get_acl_by_id(self, acl_id: str) -> Optional[AccessControlList]:
def get_acl_by_id(self, acl_id: str) -> AccessControlList | None:
for acl in self.access_control_list:
if acl.id == acl_id:
return acl
@ -82,7 +81,7 @@ class UserAcls(BaseModel):
acl for acl in self.access_control_list if acl.id != acl_id
]
def get_acl_by_token_id(self, token_id: str) -> Optional[AccessControlList]:
def get_acl_by_token_id(self, token_id: str) -> AccessControlList | None:
for acl in self.access_control_list:
if acl.get_token_by_id(token_id):
return acl
@ -91,10 +90,10 @@ class UserAcls(BaseModel):
class Account(BaseModel):
id: str
username: Optional[str] = None
password_hash: Optional[str] = None
pubkey: Optional[str] = None
email: Optional[str] = None
username: str | None = None
password_hash: str | None = None
pubkey: str | None = None
email: str | None = None
extra: UserExtra = UserExtra()
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
@ -134,10 +133,10 @@ class Account(BaseModel):
class AccountOverview(Account):
transaction_count: Optional[int] = 0
wallet_count: Optional[int] = 0
balance_msat: Optional[int] = 0
last_payment: Optional[datetime] = None
transaction_count: int | None = 0
wallet_count: int | None = 0
balance_msat: int | None = 0
last_payment: datetime | None = None
class AccountFilters(FilterModel):
@ -151,20 +150,20 @@ class AccountFilters(FilterModel):
"last_payment",
]
email: Optional[str] = None
user: Optional[str] = None
username: Optional[str] = None
pubkey: Optional[str] = None
wallet_id: Optional[str] = None
email: str | None = None
user: str | None = None
username: str | None = None
pubkey: str | None = None
wallet_id: str | None = None
class User(BaseModel):
id: str
created_at: datetime
updated_at: datetime
email: Optional[str] = None
username: Optional[str] = None
pubkey: Optional[str] = None
email: str | None = None
username: str | None = None
pubkey: str | None = None
extensions: list[str] = []
wallets: list[Wallet] = []
admin: bool = False
@ -176,7 +175,7 @@ class User(BaseModel):
def wallet_ids(self) -> list[str]:
return [wallet.id for wallet in self.wallets]
def get_wallet(self, wallet_id: str) -> Optional[Wallet]:
def get_wallet(self, wallet_id: str) -> Wallet | None:
w = [wallet for wallet in self.wallets if wallet.id == wallet_id]
return w[0] if w else None
@ -192,33 +191,33 @@ class User(BaseModel):
class RegisterUser(BaseModel):
email: Optional[str] = Query(default=None)
email: str | None = Query(default=None)
username: str = Query(default=..., min_length=2, max_length=20)
password: str = Query(default=..., min_length=8, max_length=50)
password_repeat: str = Query(default=..., min_length=8, max_length=50)
class CreateUser(BaseModel):
id: Optional[str] = Query(default=None)
email: Optional[str] = Query(default=None)
username: Optional[str] = Query(default=None, min_length=2, max_length=20)
password: Optional[str] = Query(default=None, min_length=8, max_length=50)
password_repeat: Optional[str] = Query(default=None, min_length=8, max_length=50)
id: str | None = Query(default=None)
email: str | None = Query(default=None)
username: str | None = Query(default=None, min_length=2, max_length=20)
password: str | None = Query(default=None, min_length=8, max_length=50)
password_repeat: str | None = Query(default=None, min_length=8, max_length=50)
pubkey: str = Query(default=None, max_length=64)
extensions: Optional[list[str]] = None
extra: Optional[UserExtra] = None
extensions: list[str] | None = None
extra: UserExtra | None = None
class UpdateUser(BaseModel):
user_id: str
email: Optional[str] = Query(default=None)
username: Optional[str] = Query(default=..., min_length=2, max_length=20)
extra: Optional[UserExtra] = None
email: str | None = Query(default=None)
username: str | None = Query(default=..., min_length=2, max_length=20)
extra: UserExtra | None = None
class UpdateUserPassword(BaseModel):
user_id: str
password_old: Optional[str] = None
password_old: str | None = None
password: str = Query(default=..., min_length=8, max_length=50)
password_repeat: str = Query(default=..., min_length=8, max_length=50)
username: str = Query(default=..., min_length=2, max_length=20)
@ -252,10 +251,10 @@ class LoginUsernamePassword(BaseModel):
class AccessTokenPayload(BaseModel):
sub: str
usr: Optional[str] = None
email: Optional[str] = None
auth_time: Optional[int] = 0
api_token_id: Optional[str] = None
usr: str | None = None
email: str | None = None
auth_time: int | None = 0
api_token_id: str | None = None
class UpdateBalance(BaseModel):

View File

@ -5,7 +5,6 @@ import hmac
from dataclasses import dataclass
from datetime import datetime, timezone
from enum import Enum
from typing import Optional
from ecdsa import SECP256k1, SigningKey
from pydantic import BaseModel, Field
@ -37,7 +36,7 @@ class Wallet(BaseModel):
deleted: bool = False
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
currency: Optional[str] = None
currency: str | None = None
balance_msat: int = Field(default=0, no_database=True)
extra: WalletExtra = WalletExtra()
@ -67,7 +66,7 @@ class Wallet(BaseModel):
class CreateWallet(BaseModel):
name: Optional[str] = None
name: str | None = None
class KeyType(Enum):

View File

@ -8,7 +8,7 @@ import time
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Generic, Literal, Optional, TypeVar, Union, get_origin
from typing import Any, Generic, Literal, TypeVar, get_origin
from loguru import logger
from pydantic import BaseModel, ValidationError, root_validator
@ -65,8 +65,8 @@ def get_placeholder(model: Any, field: str) -> str:
class Compat:
type: Optional[str] = "<inherited>"
schema: Optional[str] = "<inherited>"
type: str | None = "<inherited>"
schema: str | None = "<inherited>"
def interval_seconds(self, seconds: int) -> str:
if self.type in {POSTGRES, COCKROACH}:
@ -167,8 +167,8 @@ class Connection(Compat):
async def fetchall(
self,
query: str,
values: Optional[dict] = None,
model: Optional[type[TModel]] = None,
values: dict | None = None,
model: type[TModel] | None = None,
) -> list[TModel]:
params = self.rewrite_values(values) if values else {}
result = await self.conn.execute(text(self.rewrite_query(query)), params)
@ -183,8 +183,8 @@ class Connection(Compat):
async def fetchone(
self,
query: str,
values: Optional[dict] = None,
model: Optional[type[TModel]] = None,
values: dict | None = None,
model: type[TModel] | None = None,
) -> TModel:
params = self.rewrite_values(values) if values else {}
result = await self.conn.execute(text(self.rewrite_query(query)), params)
@ -211,11 +211,11 @@ class Connection(Compat):
async def fetch_page(
self,
query: str,
where: Optional[list[str]] = None,
values: Optional[dict] = None,
filters: Optional[Filters] = None,
model: Optional[type[TModel]] = None,
group_by: Optional[list[str]] = None,
where: list[str] | None = None,
values: dict | None = None,
filters: Filters | None = None,
model: type[TModel] | None = None,
group_by: list[str] | None = None,
) -> Page[TModel]:
if not filters:
filters = Filters()
@ -268,7 +268,7 @@ class Connection(Compat):
total=count,
)
async def execute(self, query: str, values: Optional[dict] = None):
async def execute(self, query: str, values: dict | None = None):
params = self.rewrite_values(values) if values else {}
result = await self.conn.execute(text(self.rewrite_query(query)), params)
await self.conn.commit()
@ -350,8 +350,8 @@ class Database(Compat):
async def fetchall(
self,
query: str,
values: Optional[dict] = None,
model: Optional[type[TModel]] = None,
values: dict | None = None,
model: type[TModel] | None = None,
) -> list[TModel]:
async with self.connect() as conn:
return await conn.fetchall(query, values, model)
@ -359,8 +359,8 @@ class Database(Compat):
async def fetchone(
self,
query: str,
values: Optional[dict] = None,
model: Optional[type[TModel]] = None,
values: dict | None = None,
model: type[TModel] | None = None,
) -> TModel:
async with self.connect() as conn:
return await conn.fetchone(query, values, model)
@ -378,16 +378,16 @@ class Database(Compat):
async def fetch_page(
self,
query: str,
where: Optional[list[str]] = None,
values: Optional[dict] = None,
filters: Optional[Filters] = None,
model: Optional[type[TModel]] = None,
group_by: Optional[list[str]] = None,
where: list[str] | None = None,
values: dict | None = None,
filters: Filters | None = None,
model: type[TModel] | None = None,
group_by: list[str] | None = None,
) -> Page[TModel]:
async with self.connect() as conn:
return await conn.fetch_page(query, where, values, filters, model, group_by)
async def execute(self, query: str, values: Optional[dict] = None):
async def execute(self, query: str, values: dict | None = None):
async with self.connect() as conn:
return await conn.execute(query, values)
@ -445,7 +445,7 @@ class Operator(Enum):
class FilterModel(BaseModel):
__search_fields__: list[str] = []
__sort_fields__: Optional[list[str]] = None
__sort_fields__: list[str] | None = None
T = TypeVar("T")
@ -461,8 +461,8 @@ class Page(BaseModel, Generic[T]):
class Filter(BaseModel, Generic[TFilterModel]):
field: str
op: Operator = Operator.EQ
model: Optional[type[TFilterModel]]
values: Optional[dict] = None
model: type[TFilterModel] | None
values: dict | None = None
@classmethod
def parse_query(
@ -517,15 +517,15 @@ class Filters(BaseModel, Generic[TFilterModel]):
"""
filters: list[Filter[TFilterModel]] = []
search: Optional[str] = None
search: str | None = None
offset: Optional[int] = None
limit: Optional[int] = None
offset: int | None = None
limit: int | None = None
sortby: Optional[str] = None
direction: Optional[Literal["asc", "desc"]] = None
sortby: str | None = None
direction: Literal["asc", "desc"] | None = None
model: Optional[type[TFilterModel]] = None
model: type[TFilterModel] | None = None
@root_validator(pre=True)
def validate_sortby(cls, values):
@ -547,7 +547,7 @@ class Filters(BaseModel, Generic[TFilterModel]):
stmt += f"OFFSET {self.offset}"
return stmt
def where(self, where_stmts: Optional[list[str]] = None) -> str:
def where(self, where_stmts: list[str] | None = None) -> str:
if not where_stmts:
where_stmts = []
if self.filters:
@ -567,7 +567,7 @@ class Filters(BaseModel, Generic[TFilterModel]):
return f"ORDER BY {self.sortby} {self.direction or 'asc'}"
return ""
def values(self, values: Optional[dict] = None) -> dict:
def values(self, values: dict | None = None) -> dict:
if not values:
values = {}
if self.filters:
@ -641,7 +641,7 @@ def model_to_dict(model: BaseModel) -> dict:
return _dict
def dict_to_submodel(model: type[TModel], value: Union[dict, str]) -> Optional[TModel]:
def dict_to_submodel(model: type[TModel], value: dict | str) -> TModel | None:
"""convert a dictionary or JSON string to a Pydantic model"""
if isinstance(value, str):
if value == "null":

View File

@ -2,7 +2,7 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from enum import Enum
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING
from pydantic import BaseModel
@ -15,10 +15,10 @@ if TYPE_CHECKING:
class NodePeerInfo(BaseModel):
id: str
alias: Optional[str] = None
color: Optional[str] = None
last_timestamp: Optional[int] = None
addresses: Optional[list[str]] = None
alias: str | None = None
color: str | None = None
last_timestamp: int | None = None
addresses: list[str] | None = None
class ChannelState(Enum):
@ -47,20 +47,20 @@ class NodeChannel(BaseModel):
balance: ChannelBalance
state: ChannelState
# could be optional for closing/pending channels on lndrest
id: Optional[str] = None
short_id: Optional[str] = None
point: Optional[ChannelPoint] = None
name: Optional[str] = None
color: Optional[str] = None
fee_ppm: Optional[int] = None
fee_base_msat: Optional[int] = None
id: str | None = None
short_id: str | None = None
point: ChannelPoint | None = None
name: str | None = None
color: str | None = None
fee_ppm: int | None = None
fee_base_msat: int | None = None
class ChannelStats(BaseModel):
counts: dict[ChannelState, int]
avg_size: int
biggest_size: Optional[int]
smallest_size: Optional[int]
biggest_size: int | None
smallest_size: int | None
total_capacity: int
@classmethod
@ -95,9 +95,9 @@ class ChannelStats(BaseModel):
class NodeFees(BaseModel):
total_msat: int
daily_msat: Optional[int] = None
weekly_msat: Optional[int] = None
monthly_msat: Optional[int] = None
daily_msat: int | None = None
weekly_msat: int | None = None
monthly_msat: int | None = None
class PublicNodeInfo(BaseModel):
@ -121,25 +121,25 @@ class NodeInfoResponse(PublicNodeInfo):
class NodePayment(BaseModel):
pending: bool
amount: int
fee: Optional[int] = None
memo: Optional[str] = None
fee: int | None = None
memo: str | None = None
time: int
bolt11: Optional[str] = None
preimage: Optional[str]
bolt11: str | None = None
preimage: str | None
payment_hash: str
expiry: Optional[float] = None
destination: Optional[NodePeerInfo] = None
expiry: float | None = None
destination: NodePeerInfo | None = None
class NodeInvoice(BaseModel):
pending: bool
amount: int
memo: Optional[str]
memo: str | None
bolt11: str
preimage: Optional[str]
preimage: str | None
payment_hash: str
paid_at: Optional[int] = None
expiry: Optional[int] = None
paid_at: int | None = None
expiry: int | None = None
class NodeInvoiceFilters(FilterModel):
@ -154,7 +154,7 @@ class Node(ABC):
def __init__(self, wallet: Wallet):
self.wallet = wallet
self.id: Optional[str] = None
self.id: str | None = None
@property
def name(self):
@ -203,22 +203,22 @@ class Node(ABC):
self,
peer_id: str,
local_amount: int,
push_amount: Optional[int] = None,
fee_rate: Optional[int] = None,
push_amount: int | None = None,
fee_rate: int | None = None,
) -> ChannelPoint:
raise NotImplementedError
@abstractmethod
async def close_channel(
self,
short_id: Optional[str] = None,
point: Optional[ChannelPoint] = None,
short_id: str | None = None,
point: ChannelPoint | None = None,
force: bool = False,
):
raise NotImplementedError
@abstractmethod
async def get_channel(self, channel_id: str) -> Optional[NodeChannel]:
async def get_channel(self, channel_id: str) -> NodeChannel | None:
raise NotImplementedError
@abstractmethod

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import asyncio
from http import HTTPStatus
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING
from fastapi import HTTPException
@ -119,8 +119,8 @@ class CoreLightningNode(Node):
self,
peer_id: str,
local_amount: int,
push_amount: Optional[int] = None,
fee_rate: Optional[int] = None,
push_amount: int | None = None,
fee_rate: int | None = None,
) -> ChannelPoint:
try:
result = await self.ln_rpc(
@ -173,8 +173,8 @@ class CoreLightningNode(Node):
@catch_rpc_errors
async def close_channel(
self,
short_id: Optional[str] = None,
point: Optional[ChannelPoint] = None,
short_id: str | None = None,
point: ChannelPoint | None = None,
force: bool = False,
):
if not short_id:
@ -229,7 +229,7 @@ class CoreLightningNode(Node):
await self.ln_rpc("setchannel", channel_id, feebase=base_msat, feeppm=ppm)
@catch_rpc_errors
async def get_channel(self, channel_id: str) -> Optional[NodeChannel]:
async def get_channel(self, channel_id: str) -> NodeChannel | None:
channels = await self.get_channels()
for channel in channels:
if channel.id == channel_id:

View File

@ -4,7 +4,7 @@ import asyncio
import base64
import json
from http import HTTPStatus
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING
from fastapi import HTTPException
from httpx import HTTPStatusError
@ -60,9 +60,7 @@ def _parse_channel_point(raw: str) -> ChannelPoint:
class LndRestNode(Node):
wallet: LndRestWallet
async def request(
self, method: str, path: str, json: Optional[dict] = None, **kwargs
):
async def request(self, method: str, path: str, json: dict | None = None, **kwargs):
response = await self.wallet.client.request(
method, f"{self.wallet.endpoint}{path}", json=json, **kwargs
)
@ -131,8 +129,8 @@ class LndRestNode(Node):
self,
peer_id: str,
local_amount: int,
push_amount: Optional[int] = None,
fee_rate: Optional[int] = None,
push_amount: int | None = None,
fee_rate: int | None = None,
) -> ChannelPoint:
response = await self.request(
"POST",
@ -176,8 +174,8 @@ class LndRestNode(Node):
async def close_channel(
self,
short_id: Optional[str] = None,
point: Optional[ChannelPoint] = None,
short_id: str | None = None,
point: ChannelPoint | None = None,
force: bool = False,
):
if short_id:
@ -218,7 +216,7 @@ class LndRestNode(Node):
},
)
async def get_channel(self, channel_id: str) -> Optional[NodeChannel]:
async def get_channel(self, channel_id: str) -> NodeChannel | None:
channel_info = await self.get(f"/v1/graph/edge/{channel_id}")
info = await self.get("/v1/getinfo")
if info["identity_pubkey"] == channel_info["node1_pub"]:

View File

@ -11,7 +11,7 @@ from hashlib import sha256
from os import path
from pathlib import Path
from time import gmtime, strftime, time
from typing import Any, Optional
from typing import Any
import httpx
from loguru import logger
@ -77,7 +77,7 @@ class RedirectPath(BaseModel):
other.from_path, list(other.header_filters.items())
) or other.redirect_matches(self.from_path, list(self.header_filters.items()))
def find_in_conflict(self, others: list[RedirectPath]) -> Optional[RedirectPath]:
def find_in_conflict(self, others: list[RedirectPath]) -> RedirectPath | None:
for other in others:
if self.in_conflict(other):
return other
@ -153,7 +153,7 @@ class InstalledExtensionsSettings(LNbitsSettings):
def find_extension_redirect(
self, path: str, req_headers: list[tuple[bytes, bytes]]
) -> Optional[RedirectPath]:
) -> RedirectPath | None:
headers = [(k.decode(), v.decode()) for k, v in req_headers]
return next(
(
@ -167,8 +167,8 @@ class InstalledExtensionsSettings(LNbitsSettings):
def activate_extension_paths(
self,
ext_id: str,
upgrade_hash: Optional[str] = None,
ext_redirects: Optional[list[dict]] = None,
upgrade_hash: str | None = None,
ext_redirects: list[dict] | None = None,
):
self.lnbits_deactivated_extensions.discard(ext_id)
@ -231,12 +231,12 @@ class ExchangeHistorySettings(LNbitsSettings):
class ThemesSettings(LNbitsSettings):
lnbits_site_title: str = Field(default="LNbits")
lnbits_site_tagline: str = Field(default="free and open-source lightning wallet")
lnbits_site_description: Optional[str] = Field(
lnbits_site_description: str | None = Field(
default="The world's most powerful suite of bitcoin tools."
)
lnbits_show_home_page_elements: bool = Field(default=True)
lnbits_default_wallet_name: str = Field(default="LNbits wallet")
lnbits_custom_badge: Optional[str] = Field(default=None)
lnbits_custom_badge: str | None = Field(default=None)
lnbits_custom_badge_color: str = Field(default="warning")
lnbits_theme_options: list[str] = Field(
default=[
@ -251,17 +251,15 @@ class ThemesSettings(LNbitsSettings):
"bitcoin",
]
)
lnbits_custom_logo: Optional[str] = Field(default=None)
lnbits_custom_image: Optional[str] = Field(
default="/static/images/logos/lnbits.svg"
)
lnbits_custom_logo: str | None = Field(default=None)
lnbits_custom_image: str | None = Field(default="/static/images/logos/lnbits.svg")
lnbits_ad_space_title: str = Field(default="Supported by")
lnbits_ad_space: str = Field(
default="https://shop.lnbits.com/;/static/images/bitcoin-shop-banner.png;/static/images/bitcoin-shop-banner.png,https://affil.trezor.io/aff_c?offer_id=169&aff_id=33845;/static/images/bitcoin-hardware-wallet.png;/static/images/bitcoin-hardware-wallet.png,https://opensats.org/;/static/images/open-sats.png;/static/images/open-sats.png"
) # sneaky sneaky
lnbits_ad_space_enabled: bool = Field(default=False)
lnbits_allowed_currencies: list[str] = Field(default=[])
lnbits_default_accounting_currency: Optional[str] = Field(default=None)
lnbits_default_accounting_currency: str | None = Field(default=None)
lnbits_qr_logo: str = Field(default="/static/images/logos/lnbits.png")
lnbits_default_reaction: str = Field(default="confettiBothSides")
lnbits_default_theme: str = Field(default="salvador")
@ -282,7 +280,7 @@ class FeeSettings(LNbitsSettings):
lnbits_service_fee: float = Field(default=0)
lnbits_service_fee_ignore_internal: bool = Field(default=True)
lnbits_service_fee_max: int = Field(default=0)
lnbits_service_fee_wallet: Optional[str] = Field(default=None)
lnbits_service_fee_wallet: str | None = Field(default=None)
# WARN: this same value must be used for balance check and passed to
# funding_source.pay_invoice(), it may cause a vulnerability if the values differ
@ -413,121 +411,121 @@ class FakeWalletFundingSource(LNbitsSettings):
class LNbitsFundingSource(LNbitsSettings):
lnbits_endpoint: str = Field(default="https://demo.lnbits.com")
lnbits_key: Optional[str] = Field(default=None)
lnbits_admin_key: Optional[str] = Field(default=None)
lnbits_invoice_key: Optional[str] = Field(default=None)
lnbits_key: str | None = Field(default=None)
lnbits_admin_key: str | None = Field(default=None)
lnbits_invoice_key: str | None = Field(default=None)
class ClicheFundingSource(LNbitsSettings):
cliche_endpoint: Optional[str] = Field(default=None)
cliche_endpoint: str | None = Field(default=None)
class CoreLightningFundingSource(LNbitsSettings):
corelightning_rpc: Optional[str] = Field(default=None)
corelightning_rpc: str | None = Field(default=None)
corelightning_pay_command: str = Field(default="pay")
clightning_rpc: Optional[str] = Field(default=None)
clightning_rpc: str | None = Field(default=None)
class CoreLightningRestFundingSource(LNbitsSettings):
corelightning_rest_url: Optional[str] = Field(default=None)
corelightning_rest_macaroon: Optional[str] = Field(default=None)
corelightning_rest_cert: Optional[str] = Field(default=None)
corelightning_rest_url: str | None = Field(default=None)
corelightning_rest_macaroon: str | None = Field(default=None)
corelightning_rest_cert: str | None = Field(default=None)
class EclairFundingSource(LNbitsSettings):
eclair_url: Optional[str] = Field(default=None)
eclair_pass: Optional[str] = Field(default=None)
eclair_url: str | None = Field(default=None)
eclair_pass: str | None = Field(default=None)
class LndRestFundingSource(LNbitsSettings):
lnd_rest_endpoint: Optional[str] = Field(default=None)
lnd_rest_cert: Optional[str] = Field(default=None)
lnd_rest_macaroon: Optional[str] = Field(default=None)
lnd_rest_macaroon_encrypted: Optional[str] = Field(default=None)
lnd_rest_endpoint: str | None = Field(default=None)
lnd_rest_cert: str | None = Field(default=None)
lnd_rest_macaroon: str | None = Field(default=None)
lnd_rest_macaroon_encrypted: str | None = Field(default=None)
lnd_rest_route_hints: bool = Field(default=True)
lnd_rest_allow_self_payment: bool = Field(default=False)
lnd_cert: Optional[str] = Field(default=None)
lnd_admin_macaroon: Optional[str] = Field(default=None)
lnd_invoice_macaroon: Optional[str] = Field(default=None)
lnd_rest_admin_macaroon: Optional[str] = Field(default=None)
lnd_rest_invoice_macaroon: Optional[str] = Field(default=None)
lnd_cert: str | None = Field(default=None)
lnd_admin_macaroon: str | None = Field(default=None)
lnd_invoice_macaroon: str | None = Field(default=None)
lnd_rest_admin_macaroon: str | None = Field(default=None)
lnd_rest_invoice_macaroon: str | None = Field(default=None)
class LndGrpcFundingSource(LNbitsSettings):
lnd_grpc_endpoint: Optional[str] = Field(default=None)
lnd_grpc_cert: Optional[str] = Field(default=None)
lnd_grpc_port: Optional[int] = Field(default=None)
lnd_grpc_admin_macaroon: Optional[str] = Field(default=None)
lnd_grpc_invoice_macaroon: Optional[str] = Field(default=None)
lnd_grpc_macaroon: Optional[str] = Field(default=None)
lnd_grpc_macaroon_encrypted: Optional[str] = Field(default=None)
lnd_grpc_endpoint: str | None = Field(default=None)
lnd_grpc_cert: str | None = Field(default=None)
lnd_grpc_port: int | None = Field(default=None)
lnd_grpc_admin_macaroon: str | None = Field(default=None)
lnd_grpc_invoice_macaroon: str | None = Field(default=None)
lnd_grpc_macaroon: str | None = Field(default=None)
lnd_grpc_macaroon_encrypted: str | None = Field(default=None)
class LnPayFundingSource(LNbitsSettings):
lnpay_api_endpoint: Optional[str] = Field(default=None)
lnpay_api_key: Optional[str] = Field(default=None)
lnpay_wallet_key: Optional[str] = Field(default=None)
lnpay_admin_key: Optional[str] = Field(default=None)
lnpay_api_endpoint: str | None = Field(default=None)
lnpay_api_key: str | None = Field(default=None)
lnpay_wallet_key: str | None = Field(default=None)
lnpay_admin_key: str | None = Field(default=None)
class BlinkFundingSource(LNbitsSettings):
blink_api_endpoint: Optional[str] = Field(default="https://api.blink.sv/graphql")
blink_ws_endpoint: Optional[str] = Field(default="wss://ws.blink.sv/graphql")
blink_token: Optional[str] = Field(default=None)
blink_api_endpoint: str | None = Field(default="https://api.blink.sv/graphql")
blink_ws_endpoint: str | None = Field(default="wss://ws.blink.sv/graphql")
blink_token: str | None = Field(default=None)
class ZBDFundingSource(LNbitsSettings):
zbd_api_endpoint: Optional[str] = Field(default="https://api.zebedee.io/v0/")
zbd_api_key: Optional[str] = Field(default=None)
zbd_api_endpoint: str | None = Field(default="https://api.zebedee.io/v0/")
zbd_api_key: str | None = Field(default=None)
class PhoenixdFundingSource(LNbitsSettings):
phoenixd_api_endpoint: Optional[str] = Field(default="http://localhost:9740/")
phoenixd_api_password: Optional[str] = Field(default=None)
phoenixd_api_endpoint: str | None = Field(default="http://localhost:9740/")
phoenixd_api_password: str | None = Field(default=None)
class AlbyFundingSource(LNbitsSettings):
alby_api_endpoint: Optional[str] = Field(default="https://api.getalby.com/")
alby_access_token: Optional[str] = Field(default=None)
alby_api_endpoint: str | None = Field(default="https://api.getalby.com/")
alby_access_token: str | None = Field(default=None)
class OpenNodeFundingSource(LNbitsSettings):
opennode_api_endpoint: Optional[str] = Field(default=None)
opennode_key: Optional[str] = Field(default=None)
opennode_admin_key: Optional[str] = Field(default=None)
opennode_invoice_key: Optional[str] = Field(default=None)
opennode_api_endpoint: str | None = Field(default=None)
opennode_key: str | None = Field(default=None)
opennode_admin_key: str | None = Field(default=None)
opennode_invoice_key: str | None = Field(default=None)
class SparkFundingSource(LNbitsSettings):
spark_url: Optional[str] = Field(default=None)
spark_token: Optional[str] = Field(default=None)
spark_url: str | None = Field(default=None)
spark_token: str | None = Field(default=None)
class LnTipsFundingSource(LNbitsSettings):
lntips_api_endpoint: Optional[str] = Field(default=None)
lntips_api_key: Optional[str] = Field(default=None)
lntips_admin_key: Optional[str] = Field(default=None)
lntips_invoice_key: Optional[str] = Field(default=None)
lntips_api_endpoint: str | None = Field(default=None)
lntips_api_key: str | None = Field(default=None)
lntips_admin_key: str | None = Field(default=None)
lntips_invoice_key: str | None = Field(default=None)
class NWCFundingSource(LNbitsSettings):
nwc_pairing_url: Optional[str] = Field(default=None)
nwc_pairing_url: str | None = Field(default=None)
class BreezSdkFundingSource(LNbitsSettings):
breez_api_key: Optional[str] = Field(default=None)
breez_greenlight_seed: Optional[str] = Field(default=None)
breez_greenlight_invite_code: Optional[str] = Field(default=None)
breez_greenlight_device_key: Optional[str] = Field(default=None)
breez_greenlight_device_cert: Optional[str] = Field(default=None)
breez_api_key: str | None = Field(default=None)
breez_greenlight_seed: str | None = Field(default=None)
breez_greenlight_invite_code: str | None = Field(default=None)
breez_greenlight_device_key: str | None = Field(default=None)
breez_greenlight_device_cert: str | None = Field(default=None)
breez_use_trampoline: bool = Field(default=True)
class BoltzFundingSource(LNbitsSettings):
boltz_client_endpoint: Optional[str] = Field(default="127.0.0.1:9002")
boltz_client_macaroon: Optional[str] = Field(default=None)
boltz_client_wallet: Optional[str] = Field(default="lnbits")
boltz_client_cert: Optional[str] = Field(default=None)
boltz_client_endpoint: str | None = Field(default="127.0.0.1:9002")
boltz_client_macaroon: str | None = Field(default=None)
boltz_client_wallet: str | None = Field(default="lnbits")
boltz_client_cert: str | None = Field(default=None)
class LightningSettings(LNbitsSettings):
@ -562,8 +560,8 @@ class FundingSourcesSettings(
class WebPushSettings(LNbitsSettings):
lnbits_webpush_pubkey: Optional[str] = Field(default=None)
lnbits_webpush_privkey: Optional[str] = Field(default=None)
lnbits_webpush_pubkey: str | None = Field(default=None)
lnbits_webpush_privkey: str | None = Field(default=None)
class NodeUISettings(LNbitsSettings):
@ -669,9 +667,9 @@ class AuditSettings(LNbitsSettings):
def audit_http_request(
self,
http_method: Optional[str] = None,
path: Optional[str] = None,
http_response_code: Optional[str] = None,
http_method: str | None = None,
path: str | None = None,
http_response_code: str | None = None,
) -> bool:
if not self.lnbits_audit_enabled:
return False
@ -689,7 +687,7 @@ class AuditSettings(LNbitsSettings):
return True
def _is_http_request_path_auditable(self, path: Optional[str]):
def _is_http_request_path_auditable(self, path: str | None):
if len(self.lnbits_audit_exclude_paths) != 0 and path:
for exclude_path in self.lnbits_audit_exclude_paths:
if _re_fullmatch_safe(exclude_path, path):
@ -706,9 +704,7 @@ class AuditSettings(LNbitsSettings):
return False
def _is_http_response_code_auditable(
self, http_response_code: Optional[str]
) -> bool:
def _is_http_response_code_auditable(self, http_response_code: str | None) -> bool:
if not http_response_code:
# No response code means only request filters should apply
return True
@ -805,9 +801,9 @@ class EnvSettings(LNbitsSettings):
class SaaSSettings(LNbitsSettings):
lnbits_saas_callback: Optional[str] = Field(default=None)
lnbits_saas_secret: Optional[str] = Field(default=None)
lnbits_saas_instance_id: Optional[str] = Field(default=None)
lnbits_saas_callback: str | None = Field(default=None)
lnbits_saas_secret: str | None = Field(default=None)
lnbits_saas_instance_id: str | None = Field(default=None)
class PersistenceSettings(LNbitsSettings):
@ -905,7 +901,7 @@ class Settings(EditableSettings, ReadOnlySettings, TransientSettings, BaseSettin
or user_id == self.super_user
)
def is_super_user(self, user_id: Optional[str] = None) -> bool:
def is_super_user(self, user_id: str | None = None) -> bool:
return user_id == self.super_user
def is_admin_user(self, user_id: str) -> bool:
@ -924,12 +920,12 @@ class SuperSettings(EditableSettings):
class AdminSettings(EditableSettings):
is_super_user: bool
lnbits_allowed_funding_sources: Optional[list[str]]
lnbits_allowed_funding_sources: list[str] | None
class SettingsField(BaseModel):
id: str
value: Optional[Any]
value: Any | None
tag: str = "core"

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import asyncio
from time import time
from typing import Any, NamedTuple, Optional
from typing import Any, NamedTuple
from loguru import logger
@ -23,7 +23,7 @@ class Cache:
self.interval = interval
self._values: dict[Any, Cached] = {}
def get(self, key: str, default=None) -> Optional[Any]:
def get(self, key: str, default=None) -> Any | None:
cached = self._values.get(key)
if cached is not None:
if cached.expiry > time():
@ -35,7 +35,7 @@ class Cache:
def set(self, key: str, value: Any, expiry: float = 10):
self._values[key] = Cached(value, time() + expiry)
def pop(self, key: str, default=None) -> Optional[Any]:
def pop(self, key: str, default=None) -> Any | None:
cached = self._values.pop(key, None)
if cached and cached.expiry > time():
return cached.value

View File

@ -1,7 +1,6 @@
from __future__ import annotations
import importlib
from typing import Optional
from lnbits.nodes import set_node_class
from lnbits.settings import settings
@ -33,7 +32,7 @@ from .void import VoidWallet
from .zbd import ZBDWallet
def set_funding_source(class_name: Optional[str] = None):
def set_funding_source(class_name: str | None = None):
backend_wallet_class = class_name or settings.lnbits_backend_wallet_class
funding_source_constructor = getattr(wallets_module, backend_wallet_class)
global funding_source

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import asyncio
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, AsyncGenerator, Coroutine, NamedTuple, Optional
from typing import TYPE_CHECKING, AsyncGenerator, Coroutine, NamedTuple
from loguru import logger
@ -11,15 +11,15 @@ if TYPE_CHECKING:
class StatusResponse(NamedTuple):
error_message: Optional[str]
error_message: str | None
balance_msat: int
class InvoiceResponse(NamedTuple):
ok: bool
checking_id: Optional[str] = None # payment_hash, rpc_id
payment_request: Optional[str] = None
error_message: Optional[str] = None
checking_id: str | None = None # payment_hash, rpc_id
payment_request: str | None = None
error_message: str | None = None
@property
def success(self) -> bool:
@ -36,11 +36,11 @@ class InvoiceResponse(NamedTuple):
class PaymentResponse(NamedTuple):
# when ok is None it means we don't know if this succeeded
ok: Optional[bool] = None
checking_id: Optional[str] = None # payment_hash, rcp_id
fee_msat: Optional[int] = None
preimage: Optional[str] = None
error_message: Optional[str] = None
ok: bool | None = None
checking_id: str | None = None # payment_hash, rcp_id
fee_msat: int | None = None
preimage: str | None = None
error_message: str | None = None
@property
def success(self) -> bool:
@ -56,9 +56,9 @@ class PaymentResponse(NamedTuple):
class PaymentStatus(NamedTuple):
paid: Optional[bool] = None
fee_msat: Optional[int] = None
preimage: Optional[str] = None
paid: bool | None = None
fee_msat: int | None = None
preimage: str | None = None
@property
def success(self) -> bool:
@ -94,7 +94,7 @@ class PaymentPendingStatus(PaymentStatus):
class Wallet(ABC):
__node_cls__: Optional[type[Node]] = None
__node_cls__: type[Node] | None = None
def __init__(self) -> None:
self.pending_invoices: list[str] = []
@ -111,9 +111,9 @@ class Wallet(ABC):
def create_invoice(
self,
amount: int,
memo: Optional[str] = None,
description_hash: Optional[bytes] = None,
unhashed_description: Optional[bytes] = None,
memo: str | None = None,
description_hash: bytes | None = None,
unhashed_description: bytes | None = None,
**kwargs,
) -> Coroutine[None, None, InvoiceResponse]:
pass

23
poetry.lock generated
View File

@ -1395,9 +1395,6 @@ files = [
{file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"},
]
[package.dependencies]
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"]
@ -2916,7 +2913,6 @@ files = [
[package.dependencies]
anyio = ">=3.4.0,<5"
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
[package.extras]
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
@ -3400,26 +3396,11 @@ files = [
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
]
[[package]]
name = "zipp"
version = "3.19.2"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
files = [
{file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"},
{file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"},
]
[package.extras]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
[extras]
breez = ["breez-sdk"]
liquid = ["wallycore"]
[metadata]
lock-version = "2.0"
python-versions = "^3.13 | ^3.12 | ^3.11 | ^3.10 | ^3.9"
content-hash = "e263865649975ea7e977b3cbf6cb453c3653de115523d026e3863605ab48a463"
python-versions = "~3.12 | ~3.11 | ~3.10"
content-hash = "96dd180aaa4fbfeb34fa6f9647c8684fce183a72b1b41d22101a9dd4b962fa2e"

View File

@ -12,7 +12,7 @@ packages = [
]
[tool.poetry.dependencies]
python = "^3.13 | ^3.12 | ^3.11 | ^3.10 | ^3.9"
python = "~3.12 | ~3.11 | ~3.10"
bech32 = "1.2.0"
click = "8.1.7"
ecdsa = "0.19.0"
@ -192,7 +192,7 @@ extend-exclude = [
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"]
ignore = ["RUF012"]
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]