mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-24 02:24:23 -05:00
Add Python 3.14 support (#20195)
* First pass add 3.14 * Update bitarray * Update markupsafe * Update zstd * Update tach to version with 3.14 support * Changes for 3.14 related things * Refactor shutdown handling in sync method * Ignore unclosed database ResourceWarning Added a new warning to ignore for unclosed database resources. * Update pytest.ini to ignore additional warnings * ignore resourcewarnings about tempdir cleanup * Minor cleanup for tests running under python 3.14 * Reduce consensus tests for test_ban_for_mismatched_tx_cost_fee * make sure to close RPC client at end of test * different idea for TempFile * oh, no cleanup function * small fixes for tests detected by 3.14 * update pytest.ini * ignore sqlite errors during closing in __del__ * add code to ignore deprecatewarnings for loop policy * Pass in ClientSession to wschiaconnection for proper cleanup * Some more resource cleanup * Trying to find unclosed sessions * One more time needing a close * wait for harvesters to connect to the farmer * properly teardown vdf_server using context manager * Testing cleanup for simulator tests * Add in a sleep to allow sockets time to cleanup * Revert "Testing cleanup for simulator tests" This reverts commitc5a4ef0cc3. * Revert "properly teardown vdf_server using context manager" This reverts commit18b456a7b6. * Revert "Revert "Testing cleanup for simulator tests"" This reverts commit8c8efc2e03. * vdf_server cleanup * attempt 47 of timelord cleanup * Some other cleanup fixes in tests * yet another attempt at timelord shutdown * Some more adjustments for 3.14 * ignore unclosed socket resource warnings * some more adjustments and cleanup * remove commented code * correct poetry.lock file * Address some review comments * fix merge indentation * fix install script print * update requests * fix problems with timelord tests on 3.14 * Make sure to close dummy session on any exception * Use different cleanup pattern to help with flakes on Windows * better shutdown handling * hopefully better awaiting for cleanup * yet more better timelord cleanup * some test adjustments * more time for cleanup
This commit is contained in:
@@ -43,7 +43,7 @@ jobs:
|
||||
matrix: arm
|
||||
- name: Intel
|
||||
matrix: intel
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
exclude:
|
||||
- os:
|
||||
matrix: windows
|
||||
|
||||
@@ -45,6 +45,7 @@ jobs:
|
||||
- major_dot_minor: "3.11"
|
||||
- major_dot_minor: "3.12"
|
||||
- major_dot_minor: "3.13"
|
||||
- major_dot_minor: "3.14"
|
||||
exclude:
|
||||
- os:
|
||||
matrix: windows
|
||||
|
||||
@@ -103,6 +103,16 @@ jobs:
|
||||
exclude_from:
|
||||
limited: True
|
||||
main: True
|
||||
- name: "3.14"
|
||||
file_name: "3.14"
|
||||
action: "3.14"
|
||||
apt: "3.14"
|
||||
install_sh: "3.14"
|
||||
matrix: "3.14"
|
||||
exclude_from:
|
||||
limited: True
|
||||
main: True
|
||||
|
||||
exclude:
|
||||
- arch:
|
||||
matrix: arm
|
||||
|
||||
@@ -63,6 +63,7 @@ jobs:
|
||||
- major_dot_minor: "3.11"
|
||||
- major_dot_minor: "3.12"
|
||||
- major_dot_minor: "3.13"
|
||||
- major_dot_minor: "3.14"
|
||||
check:
|
||||
- name: mypy
|
||||
command: |
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ if ($null -eq (Get-Command py -ErrorAction SilentlyContinue))
|
||||
Exit 1
|
||||
}
|
||||
|
||||
$supportedPythonVersions = "3.13", "3.12", "3.11", "3.10"
|
||||
$supportedPythonVersions = "3.14", "3.13", "3.12", "3.11", "3.10"
|
||||
if ("$env:INSTALL_PYTHON_VERSION" -ne "")
|
||||
{
|
||||
$pythonVersion = $env:INSTALL_PYTHON_VERSION
|
||||
|
||||
@@ -79,6 +79,7 @@ async def add_dummy_connection_wsc(
|
||||
]
|
||||
timeout = aiohttp.ClientTimeout(total=10)
|
||||
session = aiohttp.ClientSession(timeout=timeout)
|
||||
try:
|
||||
config = load_config(server.root_path, "config.yaml")
|
||||
|
||||
ca_crt_path: Path
|
||||
@@ -115,8 +116,12 @@ async def add_dummy_connection_wsc(
|
||||
30,
|
||||
local_capabilities_for_handshake=default_capabilities[type] + additional_capabilities,
|
||||
stub_metadata_for_type=StubMetadataRegistry,
|
||||
session=session,
|
||||
)
|
||||
await wsc.perform_handshake(server._network_id, dummy_port, type)
|
||||
except Exception:
|
||||
await session.close()
|
||||
raise
|
||||
if wait_for_peer_added:
|
||||
await time_out_assert(5, lambda: peer_id in server.all_connections)
|
||||
if wsc.incoming_message_task is not None:
|
||||
|
||||
@@ -96,6 +96,7 @@ class TestDos:
|
||||
await session.ws_connect(
|
||||
url, autoclose=True, autoping=True, ssl=ssl_context, max_msg_size=100 * 1024 * 1024
|
||||
)
|
||||
await session.close()
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_large_message_disconnect_and_ban(
|
||||
@@ -110,15 +111,15 @@ class TestDos:
|
||||
|
||||
# Use the server_2 ssl information to connect to server_1, and send a huge message
|
||||
timeout = ClientTimeout(total=10)
|
||||
session = ClientSession(timeout=timeout)
|
||||
url = f"wss://{self_hostname}:{server_1._port}/ws"
|
||||
|
||||
ssl_context = server_2.ssl_client_context
|
||||
ws = await session.ws_connect(
|
||||
url, autoclose=True, autoping=True, ssl=ssl_context, max_msg_size=100 * 1024 * 1024
|
||||
)
|
||||
assert not ws.closed
|
||||
|
||||
async with (
|
||||
ClientSession(timeout=timeout) as session,
|
||||
session.ws_connect(
|
||||
url, autoclose=True, autoping=True, ssl=ssl_context, max_msg_size=100 * 1024 * 1024
|
||||
) as ws,
|
||||
):
|
||||
large_msg: bytes = bytes([0] * (60 * 1024 * 1024))
|
||||
with monkeypatch.context() as monkey_patch_context:
|
||||
monkey_patch_context.setattr(chia.server.server, "is_localhost", not_localhost)
|
||||
@@ -130,7 +131,6 @@ class TestDos:
|
||||
print(response)
|
||||
assert response.type == WSMsgType.CLOSE
|
||||
assert response.data == WSCloseCode.MESSAGE_TOO_BIG
|
||||
await ws.close()
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_bad_handshake_and_ban(
|
||||
@@ -146,13 +146,15 @@ class TestDos:
|
||||
server_1.invalid_protocol_ban_seconds = int(10 + adjusted_timeout(1))
|
||||
# Use the server_2 ssl information to connect to server_1, and send a huge message
|
||||
timeout = ClientTimeout(total=10)
|
||||
session = ClientSession(timeout=timeout)
|
||||
url = f"wss://{self_hostname}:{server_1._port}/ws"
|
||||
|
||||
ssl_context = server_2.ssl_client_context
|
||||
ws = await session.ws_connect(
|
||||
|
||||
async with (
|
||||
ClientSession(timeout=timeout) as session,
|
||||
session.ws_connect(
|
||||
url, autoclose=True, autoping=True, ssl=ssl_context, max_msg_size=100 * 1024 * 1024
|
||||
)
|
||||
) as ws,
|
||||
):
|
||||
with monkeypatch.context() as monkey_patch_context:
|
||||
monkey_patch_context.setattr(chia.server.server, "is_localhost", not_localhost)
|
||||
await ws.send_bytes(bytes([1] * 1024))
|
||||
@@ -163,7 +165,6 @@ class TestDos:
|
||||
print(response)
|
||||
assert response.type == WSMsgType.CLOSE
|
||||
assert response.data == WSCloseCode.PROTOCOL_ERROR
|
||||
await ws.close()
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_invalid_protocol_handshake(
|
||||
|
||||
@@ -114,4 +114,7 @@ def test_base_event_loop_has_methods() -> None:
|
||||
assert str(inspect.signature(method)) == "()"
|
||||
|
||||
finally:
|
||||
if sys.platform != "win32":
|
||||
if pausable_server is not None:
|
||||
pausable_server.close()
|
||||
selector_event_loop.close()
|
||||
|
||||
@@ -8,6 +8,7 @@ import random
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import warnings
|
||||
from collections.abc import AsyncIterator
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
@@ -115,6 +116,8 @@ class ServeInThread:
|
||||
|
||||
def _run(self) -> None:
|
||||
# TODO: yuck yuck, messes with a single global
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
original_event_loop_policy = asyncio.get_event_loop_policy()
|
||||
asyncio.set_event_loop_policy(chia_policy.ChiaPolicy())
|
||||
try:
|
||||
|
||||
@@ -88,7 +88,7 @@ async def test_empty_request(setup_node_and_rpc: tuple[FullNodeRpcClient, FullNo
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_empty_peak(one_node_no_blocks: tuple[FullNodeRpcClient, FullNodeRpcApi]) -> None:
|
||||
_client, full_node_rpc_api = one_node_no_blocks
|
||||
client, full_node_rpc_api = one_node_no_blocks
|
||||
response = await full_node_rpc_api.get_fee_estimate({"target_times": [], "cost": 1})
|
||||
del response["node_time_utc"]
|
||||
assert response == {
|
||||
@@ -108,6 +108,10 @@ async def test_empty_peak(one_node_no_blocks: tuple[FullNodeRpcClient, FullNodeR
|
||||
"num_spends": 0,
|
||||
}
|
||||
|
||||
# one_node_no_blocks is not an async generator, so we have to close the client ourselves
|
||||
client.close()
|
||||
await client.await_closed()
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_no_target_times(setup_node_and_rpc: tuple[FullNodeRpcClient, FullNodeRpcApi]) -> None:
|
||||
|
||||
@@ -67,6 +67,7 @@ class FakeDNSResolver:
|
||||
return []
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning")
|
||||
class TestSimulation:
|
||||
@pytest.mark.limit_consensus_modes(reason="This test only supports one running at a time.")
|
||||
@pytest.mark.anyio
|
||||
|
||||
@@ -20,6 +20,7 @@ from chia.wallet.wallet_node import WalletNode
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning")
|
||||
@pytest.mark.parametrize(argnames="count", argvalues=[0, 1, 2, 5, 10])
|
||||
@pytest.mark.parametrize(argnames="guarantee_transaction_blocks", argvalues=[False, True])
|
||||
async def test_simulation_farm_blocks_to_puzzlehash(
|
||||
@@ -176,7 +177,7 @@ async def test_process_transaction_records(
|
||||
)
|
||||
|
||||
await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions)
|
||||
assert full_node_api.full_node.coin_store.get_coin_record(coin.name()) is not None
|
||||
assert await full_node_api.full_node.coin_store.get_coin_record(coin.name()) is not None
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
||||
@@ -22,11 +22,16 @@ async def test_timelord_has_no_server(timelord_service: TimelordService) -> None
|
||||
|
||||
|
||||
class _NullTransport(asyncio.Transport):
|
||||
_closing = False
|
||||
|
||||
def write(self, data: bytes) -> None:
|
||||
pass
|
||||
|
||||
def close(self) -> None:
|
||||
self._closing = True
|
||||
|
||||
def is_closing(self) -> bool:
|
||||
return False
|
||||
return self._closing
|
||||
|
||||
|
||||
def _make_null_writer() -> asyncio.StreamWriter:
|
||||
@@ -72,6 +77,7 @@ async def test_invalid_vdf_proof_is_ignored_in_process_communication(
|
||||
|
||||
writer = _make_null_writer()
|
||||
await timelord._do_process_communication(chain, challenge, initial_form, "127.0.0.1", reader, writer, proof_label=1)
|
||||
writer.close()
|
||||
|
||||
assert timelord.proofs_finished == []
|
||||
assert state_changed_calls == []
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import cProfile
|
||||
import gc
|
||||
import logging
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
from collections.abc import Callable, Iterator
|
||||
@@ -131,7 +134,8 @@ async def run_sync_test(
|
||||
check_log = ExitOnError()
|
||||
logger.addHandler(check_log)
|
||||
|
||||
with tempfile.TemporaryDirectory() as root_dir:
|
||||
root_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
root_path = Path(root_dir, "root")
|
||||
if start_at_checkpoint is not None:
|
||||
shutil.copytree(start_at_checkpoint, root_path)
|
||||
@@ -252,3 +256,18 @@ async def run_sync_test(
|
||||
logger.warning(f"end-height: {height}")
|
||||
if node_profiler:
|
||||
(root_path / "profile-node").rename("./profile-node")
|
||||
finally:
|
||||
# On Windows, SQLite WAL/SHM handles may not be released immediately
|
||||
# after closing, causing PermissionError on cleanup. Retry with
|
||||
# backoff — same pattern CPython uses in its own test infrastructure:
|
||||
# https://github.com/python/cpython/issues/59701
|
||||
# https://github.com/python/cpython/issues/98219
|
||||
gc.collect()
|
||||
for attempt in range(20):
|
||||
try:
|
||||
shutil.rmtree(root_dir)
|
||||
break
|
||||
except PermissionError:
|
||||
if attempt == 19 or sys.platform != "win32":
|
||||
raise
|
||||
await asyncio.sleep(2)
|
||||
|
||||
@@ -23,6 +23,7 @@ from chia.full_node.full_node_service import FullNodeService
|
||||
from chia.harvester.harvester import Harvester
|
||||
from chia.harvester.harvester_service import HarvesterService
|
||||
from chia.introducer.introducer_api import IntroducerAPI
|
||||
from chia.protocols.outbound_message import NodeType
|
||||
from chia.protocols.shared_protocol import Capability
|
||||
from chia.server.server import ChiaServer
|
||||
from chia.simulator.block_tools import BlockTools, create_block_tools_async
|
||||
@@ -360,6 +361,19 @@ async def setup_farmer_solver_multi_harvester(
|
||||
for i in range(harvester_count)
|
||||
]
|
||||
|
||||
# Ensure all harvesters are connected to the farmer
|
||||
# this helps with proper test setup and with proper teardown
|
||||
if start_services:
|
||||
with anyio.fail_after(delay=adjusted_timeout(10)):
|
||||
for backoff in backoff_times():
|
||||
all_connected = all(
|
||||
len(harvester_service._node.server.get_connections(NodeType.FARMER)) > 0
|
||||
for harvester_service in harvester_services
|
||||
)
|
||||
if all_connected:
|
||||
break
|
||||
|
||||
await asyncio.sleep(backoff)
|
||||
yield harvester_services, farmer_service, block_tools
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ from pathlib import Path
|
||||
|
||||
@contextlib.contextmanager
|
||||
def TempFile() -> Iterator[Path]:
|
||||
path = Path(tempfile.NamedTemporaryFile().name)
|
||||
t = tempfile.NamedTemporaryFile(delete=False)
|
||||
path = Path(t.name)
|
||||
t.close()
|
||||
yield path
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from chia_rs import ConsensusConstants
|
||||
from chia_rs.sized_bytes import bytes32
|
||||
@@ -139,7 +141,11 @@ def test_replace_str_to_bytes_deprecated_field(caplog: pytest.LogCaptureFixture)
|
||||
|
||||
def test_replace_str_to_bytes_invalid_value() -> None:
|
||||
# invalid value
|
||||
with pytest.raises(ValueError, match="non-hexadecimal number found in"):
|
||||
if sys.version_info >= (3, 14):
|
||||
matchstr = "arg must contain an even number of hexadecimal digits"
|
||||
else:
|
||||
matchstr = "non-hexadecimal number found in"
|
||||
with pytest.raises(ValueError, match=matchstr):
|
||||
replace_str_to_bytes(
|
||||
test_constants,
|
||||
GENESIS_PRE_FARM_FARMER_PUZZLE_HASH="fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
|
||||
@@ -394,13 +394,12 @@ class TestWalletRpc:
|
||||
wallet_node.config["trusted_peers"] = {}
|
||||
|
||||
assert wallet_service.rpc_server is not None
|
||||
client = await WalletRpcClient.create(
|
||||
async with WalletRpcClient.create_as_context(
|
||||
self_hostname,
|
||||
wallet_service.rpc_server.listen_port,
|
||||
wallet_service.root_path,
|
||||
wallet_service.config,
|
||||
)
|
||||
|
||||
) as client:
|
||||
with pytest.raises(ValueError, match="No peer connected"):
|
||||
await wallet_service.rpc_server.rpc_api.dl_verify_proof(fake_gpr.to_json_dict())
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from asyncio import Queue
|
||||
from collections import OrderedDict
|
||||
from collections.abc import AsyncGenerator
|
||||
from contextlib import asynccontextmanager
|
||||
from dataclasses import dataclass
|
||||
from random import Random
|
||||
|
||||
@@ -11,7 +12,7 @@ from chia_rs import AugSchemeMPL, Coin, CoinRecord, CoinSpend, CoinState, Progra
|
||||
from chia_rs.sized_bytes import bytes32
|
||||
from chia_rs.sized_ints import uint8, uint16, uint32, uint64
|
||||
|
||||
from chia._tests.connection_utils import add_dummy_connection
|
||||
from chia._tests.connection_utils import add_dummy_connection, add_dummy_connection_wsc
|
||||
from chia._tests.util.coin_store import add_coin_records_to_db
|
||||
from chia.full_node.coin_store import CoinStore
|
||||
from chia.full_node.full_node import FullNode
|
||||
@@ -38,6 +39,33 @@ Mpu = tuple[FullNodeSimulator, Queue[Message], WSChiaConnection]
|
||||
ALL_FILTER = wallet_protocol.CoinStateFilters(True, True, True, uint64(0))
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def connect_to_simulator_context(
|
||||
one_node: OneNode, self_hostname: str, mempool_updates: bool = True
|
||||
) -> AsyncGenerator[tuple[FullNodeSimulator, Queue[Message], WSChiaConnection], None]:
|
||||
[full_node_service], _, _ = one_node
|
||||
|
||||
full_node_api = full_node_service._api
|
||||
fn_server = full_node_api.server
|
||||
|
||||
wsc, peer_id = await add_dummy_connection_wsc(
|
||||
fn_server,
|
||||
self_hostname,
|
||||
41723,
|
||||
NodeType.WALLET,
|
||||
additional_capabilities=[(uint16(Capability.MEMPOOL_UPDATES), "1")] if mempool_updates else [],
|
||||
)
|
||||
peer = fn_server.all_connections[peer_id]
|
||||
incoming_queue = wsc.incoming_queue
|
||||
|
||||
try:
|
||||
yield full_node_api, incoming_queue, peer
|
||||
finally:
|
||||
# closing the client side of the dummy connection
|
||||
await wsc.close()
|
||||
await wsc.wait_until_closed()
|
||||
|
||||
|
||||
async def connect_to_simulator(
|
||||
one_node: OneNode, self_hostname: str, mempool_updates: bool = True
|
||||
) -> tuple[FullNodeSimulator, Queue[Message], WSChiaConnection]:
|
||||
@@ -546,8 +574,7 @@ async def test_request_puzzle_state_reorg(one_node: OneNode, self_hostname: str)
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_request_puzzle_state_limit(one_node: OneNode, self_hostname: str) -> None:
|
||||
simulator, _, peer = await connect_to_simulator(one_node, self_hostname)
|
||||
|
||||
async with connect_to_simulator_context(one_node, self_hostname) as (simulator, _, peer):
|
||||
# Farm blocks 0-11 and make sure the last one is farmed
|
||||
await simulator.farm_blocks_to_puzzlehash(12)
|
||||
|
||||
|
||||
@@ -682,6 +682,15 @@ async def test_transaction_send_cache(self_hostname: str, simulator_and_wallet:
|
||||
|
||||
await time_out_assert(5, check_wallet_cache_empty, True)
|
||||
|
||||
# Wait for the rejection to be persisted to sent_to before testing resend behavior
|
||||
async def check_sent_to_has_failed() -> bool:
|
||||
record = await wallet.wallet_state_manager.tx_store.get_transaction_record(tx.name)
|
||||
return record is not None and any(
|
||||
status == MempoolInclusionStatus.FAILED.value for _, status, _ in record.sent_to
|
||||
)
|
||||
|
||||
await time_out_assert(10, check_sent_to_has_failed, True)
|
||||
|
||||
# Re-process the queue — the peer already rejected it, so it should NOT be resent
|
||||
await wallet_node._resend_queue()
|
||||
with pytest.raises(AssertionError):
|
||||
|
||||
@@ -878,3 +878,6 @@ async def test_cat_wallet_conversion(
|
||||
await time_out_assert_not_none(
|
||||
15, check_length, 0, wallet_node_0.wallet_state_manager.get_all_wallet_info_entries, WalletType.CAT
|
||||
)
|
||||
|
||||
client_0.close()
|
||||
await client_0.await_closed()
|
||||
|
||||
@@ -1431,7 +1431,8 @@ def launch_plotter(
|
||||
plotter_path.unlink()
|
||||
else:
|
||||
plotter_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
outfile = open(plotter_path.resolve(), "w")
|
||||
|
||||
with open(plotter_path.resolve(), "w") as outfile:
|
||||
log.info(f"Service array: {service_array}") # lgtm [py/clear-text-logging-sensitive-data]
|
||||
process = subprocess.Popen(
|
||||
service_array,
|
||||
|
||||
@@ -1099,8 +1099,7 @@ class FullNode:
|
||||
tb = traceback.format_exc()
|
||||
self.log.error(f"Error with syncing: {type(e)}{tb}")
|
||||
finally:
|
||||
if self._shut_down:
|
||||
return None
|
||||
if not self._shut_down:
|
||||
await self._finish_sync(fork_point)
|
||||
|
||||
async def request_validate_wp(
|
||||
|
||||
@@ -464,9 +464,9 @@ class DNSServer:
|
||||
try:
|
||||
validated_peer = ip_address(peer)
|
||||
if validated_peer.version == 4:
|
||||
self.reliable_peers_v4.append(validated_peer)
|
||||
self.reliable_peers_v4.append(IPv4Address(validated_peer))
|
||||
elif validated_peer.version == 6:
|
||||
self.reliable_peers_v6.append(validated_peer)
|
||||
self.reliable_peers_v6.append(IPv6Address(validated_peer))
|
||||
except ValueError:
|
||||
log.error(f"Invalid peer: {peer}")
|
||||
continue
|
||||
|
||||
@@ -348,7 +348,21 @@ if sys.platform == "win32":
|
||||
self._proactor.disable_connections()
|
||||
|
||||
|
||||
class ChiaPolicy(asyncio.DefaultEventLoopPolicy):
|
||||
if sys.version_info >= (3, 14):
|
||||
# DefaultEventLoopPolicy is deprecated in 3.14 and will be removed in 3.16
|
||||
# Need to inherit from _BaseDefaultEventLoopPolicy instead
|
||||
class ChiaPolicy(asyncio.events._BaseDefaultEventLoopPolicy):
|
||||
def new_event_loop(self) -> asyncio.AbstractEventLoop:
|
||||
# overriding https://github.com/python/cpython/blob/v3.14.0/Lib/asyncio/events.py#L726-L732
|
||||
if sys.platform == "win32":
|
||||
loop_factory = ChiaProactorEventLoop
|
||||
else:
|
||||
loop_factory = ChiaSelectorEventLoop
|
||||
|
||||
return loop_factory()
|
||||
else:
|
||||
|
||||
class ChiaPolicy(asyncio.DefaultEventLoopPolicy):
|
||||
def new_event_loop(self) -> asyncio.AbstractEventLoop:
|
||||
# overriding https://github.com/python/cpython/blob/v3.11.0/Lib/asyncio/events.py#L689-L695
|
||||
if sys.platform == "win32":
|
||||
|
||||
@@ -104,6 +104,9 @@ class FullNodeDiscovery:
|
||||
cancel_task_safe(self.connect_peers_task, self.log)
|
||||
cancel_task_safe(self.serialize_task, self.log)
|
||||
cancel_task_safe(self.cleanup_task, self.log)
|
||||
tasks_to_await = {t for t in (self.connect_peers_task, self.serialize_task, self.cleanup_task) if t is not None}
|
||||
if len(tasks_to_await) > 0:
|
||||
await asyncio.wait(tasks_to_await)
|
||||
for t in self.pending_tasks:
|
||||
cancel_task_safe(t, self.log)
|
||||
if len(self.pending_tasks) > 0:
|
||||
|
||||
@@ -190,10 +190,21 @@ class Timelord:
|
||||
self.main_loop.cancel()
|
||||
if self.bluebox_pool is not None:
|
||||
self.bluebox_pool.shutdown()
|
||||
for _, _, writer in self.free_clients:
|
||||
await self._shutdown_vdf_clients()
|
||||
if self.vdf_server is not None:
|
||||
self.vdf_server.close()
|
||||
|
||||
async def _shutdown_vdf_clients(self) -> None:
|
||||
"""Send stop signal and close all VDF client writers, suppressing errors."""
|
||||
for _, _, writer in [*self.free_clients, *self.chain_type_to_stream.values()]:
|
||||
with contextlib.suppress(Exception):
|
||||
writer.write(b"010")
|
||||
await writer.drain()
|
||||
with contextlib.suppress(Exception):
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
self.free_clients.clear()
|
||||
self.chain_type_to_stream.clear()
|
||||
|
||||
def get_connections(self, request_node_type: NodeType | None) -> list[dict[str, Any]]:
|
||||
return default_get_connections(server=self.server, request_node_type=request_node_type)
|
||||
|
||||
+3
-3
@@ -71,7 +71,7 @@ OPENSSL_VERSION_INT=
|
||||
find_python() {
|
||||
set +e
|
||||
unset BEST_VERSION
|
||||
for V in 313 3.13 312 3.12 311 3.11 310 3.10 3; do
|
||||
for V in 314 3.14 313 3.13 312 3.12 311 3.11 310 3.10 3; do
|
||||
if command -v python$V >/dev/null; then
|
||||
if [ "$BEST_VERSION" = "" ]; then
|
||||
BEST_VERSION=$V
|
||||
@@ -134,8 +134,8 @@ if ! command -v "$INSTALL_PYTHON_PATH" >/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$PYTHON_MAJOR_VER" -ne "3" ] || [ "$PYTHON_MINOR_VER" -lt "10" ] || [ "$PYTHON_MINOR_VER" -ge "14" ]; then
|
||||
echo "Chia requires Python version >= 3.10 and < 3.14.0" >&2
|
||||
if [ "$PYTHON_MAJOR_VER" -ne "3" ] || [ "$PYTHON_MINOR_VER" -lt "10" ] || [ "$PYTHON_MINOR_VER" -ge "15" ]; then
|
||||
echo "Chia requires Python version >= 3.10 and < 3.15.0" >&2
|
||||
echo "Current Python version = $INSTALL_PYTHON_VERSION" >&2
|
||||
# If Arch, direct to Arch Wiki
|
||||
if type pacman >/dev/null 2>&1 && [ -f "/etc/arch-release" ]; then
|
||||
|
||||
+1169
-892
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -95,7 +95,7 @@ pytest-mock = { version = ">=3.14.0", optional = true }
|
||||
pytest-monitor = { version = ">=1.6.6", platform = "linux", optional = true }
|
||||
pytest-xdist = { version = ">=3.6.1", optional = true }
|
||||
pytest-rerunfailures = { version = ">=16.1, <17.0", optional = true }
|
||||
tach = { version = ">=0.29.0", optional = true }
|
||||
tach = { version = ">=0.32.0", optional = true }
|
||||
types-aiofiles = { version = ">=24.1.0.20240626", optional = true }
|
||||
types-pyyaml = { version = ">=6.0.12.20240917", optional = true }
|
||||
types-setuptools = { version = ">=75.5.0.20241122", optional = true }
|
||||
|
||||
@@ -17,8 +17,14 @@ markers =
|
||||
testpaths = chia/_tests/
|
||||
filterwarnings =
|
||||
error
|
||||
ignore:unclosed database:ResourceWarning
|
||||
ignore:Exception ignored in:pytest.PytestUnraisableExceptionWarning
|
||||
ignore:cannot collect test class:pytest.PytestCollectionWarning
|
||||
ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning
|
||||
ignore:record_property is incompatible with junit_family:pytest.PytestWarning
|
||||
ignore:Implicit None on return values is deprecated and will raise
|
||||
ignore:'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16:DeprecationWarning
|
||||
ignore:Implicitly cleaning up <TemporaryDirectory:ResourceWarning
|
||||
ignore:'asyncio.set_event_loop_policy' is deprecated and slated for removal in Python 3.16:DeprecationWarning
|
||||
ignore:'asyncio.WindowsProactorEventLoopPolicy' is deprecated and slated for removal in Python 3.16:DeprecationWarning
|
||||
ignore:unclosed <socket.socket:ResourceWarning
|
||||
|
||||
Reference in New Issue
Block a user