From 4c6a68f8f1cc7e8df6b710a447f130f4b15b49b1 Mon Sep 17 00:00:00 2001 From: Kirill Minovsky Date: Fri, 4 Sep 2026 19:13:35 +0300 Subject: [PATCH] fix(compat): fix Python 3.8 compatibility for TLS nullcontext and ASN removeprefix --- cli/runners.py | 8 ++++++-- core/tls_scanner.py | 11 ++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cli/runners.py b/cli/runners.py index faa7ae5..d9ed1a3 100644 --- a/cli/runners.py +++ b/cli/runners.py @@ -391,10 +391,14 @@ async def run_whitelist_sni_test(semaphore: asyncio.Semaphore, tcp_items: list, clean_sni_list.append(s) from collections import defaultdict + def _strip_as_prefix(s: str) -> str: + u = s.upper() + return u[2:] if u.startswith("AS") else u + asn_to_items: dict = defaultdict(list) for item in port443_items: asn_raw = str(item.get("asn", "")).strip() - asn_key = asn_raw.upper().removeprefix("AS") if asn_raw else item["ip"] + asn_key = _strip_as_prefix(asn_raw) if asn_raw else item["ip"] asn_to_items[asn_key].append(item) batch_size_initial = getattr(config, "SNI_BATCH_SIZE", _DEFAULT_SNI_BATCH_SIZE) @@ -414,7 +418,7 @@ async def run_whitelist_sni_test(semaphore: asyncio.Semaphore, tcp_items: list, if asn_raw and not asn_raw.upper().startswith("AS") else asn_raw.upper() ) or "-" - asn_key = asn_raw.upper().removeprefix("AS") if asn_raw else ip + asn_key = _strip_as_prefix(asn_raw) if asn_raw else ip alive_str, status, detail, rtt = await check_tcp_16_20_with_rtt(ip, 443, sni, semaphore) return { "item": item, diff --git a/core/tls_scanner.py b/core/tls_scanner.py index 01b4023..eea7f77 100644 --- a/core/tls_scanner.py +++ b/core/tls_scanner.py @@ -22,6 +22,15 @@ def _strip_www(host: str) -> str: return host[4:] if host.startswith("www.") else host +class _AsyncNullContext: + """Асинхронный nullcontext для совместимости с Python 3.8 и 3.9 (в stdlib __aenter__ появился только в 3.10).""" + async def __aenter__(self): + return None + async def __aexit__(self, *args): + return None + +_ASYNC_NULLCONTEXT = _AsyncNullContext() + def create_dpi_client(tls_version: str = None) -> httpx.AsyncClient: """ Создаёт изолированного клиента для DPI-проверки. @@ -155,7 +164,7 @@ async def _check_tls_single( elif "receive_response" in event_name: connection_state["stage"] = "reading_data" - sem_ctx = semaphore if semaphore is not None else contextlib.nullcontext() + sem_ctx = semaphore if semaphore is not None else _ASYNC_NULLCONTEXT async with sem_ctx: start = time.monotonic()