diff --git a/.github/workflows/check_wheel_availability.yaml b/.github/workflows/check_wheel_availability.yaml index 75dc4264c5f0..faa419560bce 100644 --- a/.github/workflows/check_wheel_availability.yaml +++ b/.github/workflows/check_wheel_availability.yaml @@ -43,7 +43,7 @@ jobs: matrix: arm - name: Intel matrix: intel - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] exclude: - os: matrix: windows diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 6d0b7f78b11c..2323d5cc5cc6 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -45,6 +45,7 @@ jobs: - major_dot_minor: "3.10" - major_dot_minor: "3.11" - major_dot_minor: "3.12" + - major_dot_minor: "3.13" exclude: - os: matrix: windows diff --git a/.github/workflows/test-install-scripts.yml b/.github/workflows/test-install-scripts.yml index e91dffec10f4..d97b3178028b 100644 --- a/.github/workflows/test-install-scripts.yml +++ b/.github/workflows/test-install-scripts.yml @@ -224,7 +224,7 @@ jobs: # The behavior we follow in install.sh is unique with Arch in that # we leave it to the user to install the appropriate version of python, # so we need to install python here in order for the test to succeed. - pacman --noconfirm -U --needed https://archive.archlinux.org/packages/p/python/python-3.12.7-1-x86_64.pkg.tar.zst + pacman --noconfirm -U --needed https://archive.archlinux.org/packages/p/python/python-3.13.5-1-x86_64.pkg.tar.zst - name: Prepare Debian if: ${{ matrix.distribution.type == 'debian' }} diff --git a/.github/workflows/test-single.yml b/.github/workflows/test-single.yml index f60e2990e680..c55522612047 100644 --- a/.github/workflows/test-single.yml +++ b/.github/workflows/test-single.yml @@ -105,6 +105,15 @@ jobs: exclude_from: limited: True main: True + - name: "3.13" + file_name: "3.13" + action: "3.13" + apt: "3.13" + install_sh: "3.13" + matrix: "3.13" + exclude_from: + limited: True + main: True exclude: - arch: matrix: arm diff --git a/.github/workflows/upload-pypi-source.yml b/.github/workflows/upload-pypi-source.yml index 94a563349484..db70ae892955 100644 --- a/.github/workflows/upload-pypi-source.yml +++ b/.github/workflows/upload-pypi-source.yml @@ -63,6 +63,7 @@ jobs: - major_dot_minor: "3.10" - major_dot_minor: "3.11" - major_dot_minor: "3.12" + - major_dot_minor: "3.13" check: - name: mypy command: | diff --git a/Install.ps1 b/Install.ps1 index 0fd2f96555e9..028be90105c1 100644 --- a/Install.ps1 +++ b/Install.ps1 @@ -46,7 +46,7 @@ if ($null -eq (Get-Command py -ErrorAction SilentlyContinue)) Exit 1 } -$supportedPythonVersions = "3.12", "3.11", "3.10", "3.9" +$supportedPythonVersions = "3.13", "3.12", "3.11", "3.10", "3.9" if ("$env:INSTALL_PYTHON_VERSION" -ne "") { $pythonVersion = $env:INSTALL_PYTHON_VERSION diff --git a/chia/_tests/core/server/test_event_loop.py b/chia/_tests/core/server/test_event_loop.py index 19af2456c631..51e7168e1b15 100644 --- a/chia/_tests/core/server/test_event_loop.py +++ b/chia/_tests/core/server/test_event_loop.py @@ -30,7 +30,9 @@ def test_base_event_loop_has_methods() -> None: assert hasattr(base_selector_event_loop, "create_server") method = getattr(base_selector_event_loop, "create_server") assert inspect.ismethod(method) - if sys.version_info >= (3, 11): + if sys.version_info >= (3, 13): + expected_signature = "(protocol_factory, host=None, port=None, *, family=, flags=, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, keep_alive=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, start_serving=True)" # noqa: E501 + elif sys.version_info >= (3, 11): expected_signature = "(protocol_factory, host=None, port=None, *, family=, flags=, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, start_serving=True)" # noqa: E501 else: expected_signature = "(protocol_factory, host=None, port=None, *, family=, flags=, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, start_serving=True)" # noqa: E501 @@ -103,7 +105,13 @@ async def main() -> None: for func in ("_attach", "_detach"): assert hasattr(base_server, func) - method = getattr(base_server, func) - assert str(inspect.signature(method)) == "()" + if sys.version_info >= (3, 13): + # https://github.com/python/cpython/blob/bcee1c322115c581da27600f2ae55e5439c027eb/Lib/asyncio/base_events.py#L296 + method = getattr(base_server, func) + assert str(inspect.signature(method)) == "(transport)" + else: + method = getattr(base_server, func) + assert str(inspect.signature(method)) == "()" + finally: selector_event_loop.close() diff --git a/chia/server/chia_policy.py b/chia/server/chia_policy.py index 36ed1c0cd0c1..fec3242936bf 100644 --- a/chia/server/chia_policy.py +++ b/chia/server/chia_policy.py @@ -6,6 +6,7 @@ import ssl import struct import sys +import weakref if sys.platform == "win32": import _overlapped # type: ignore[import-not-found] @@ -57,15 +58,26 @@ class BaseEventsServer(asyncio.base_events.Server): else: _loop: EventsAbstractEventLoop _sockets: Iterable[socket.socket] - _active_count: int + if sys.version_info >= (3, 13): + # https://github.com/python/cpython/blob/v3.13.7/Lib/asyncio/base_events.py#L283 + _clients: weakref.WeakSet[object] + else: + _active_count: int _protocol_factory: _ProtocolFactory _backlog: int _ssl_context: _SSLContext _ssl_handshake_timeout: Optional[float] - def _attach(self) -> None: ... + if sys.version_info >= (3, 13): + # https://github.com/python/cpython/blob/bcee1c322115c581da27600f2ae55e5439c027eb/Lib/asyncio/base_events.py#L296 + def _attach(self, transport: object) -> None: ... + + def _detach(self, transport: object) -> None: ... + else: + + def _attach(self) -> None: ... - def _detach(self) -> None: ... + def _detach(self) -> None: ... def _start_serving(self) -> None: ... @@ -132,20 +144,22 @@ def __init__( max_concurrent_connections if max_concurrent_connections is not None else global_max_concurrent_connections ) - def _attach(self) -> None: - super()._attach() - logging.getLogger(__name__).debug(f"New connection. Total connections: {self._active_count}") - if not self._paused and self._active_count >= self.max_concurrent_connections: + def _attach(self, *args: object, **kwargs: object) -> None: + super()._attach(*args, **kwargs) + active_connections = self._chia_active_connections() + logging.getLogger(__name__).debug(f"New connection. Total connections: {active_connections}") + if not self._paused and active_connections >= self.max_concurrent_connections: self._chia_pause() - def _detach(self) -> None: - super()._detach() - logging.getLogger(__name__).debug(f"Connection lost. Total connections: {self._active_count}") + def _detach(self, *args: object, **kwargs: object) -> None: + super()._detach(*args, **kwargs) + active_connections = self._chia_active_connections() + logging.getLogger(__name__).debug(f"Connection lost. Total connections: {active_connections}") if ( - self._active_count > 0 + active_connections > 0 and self._sockets is not None and self._paused - and self._active_count < self.max_concurrent_connections + and active_connections < self.max_concurrent_connections ): self._chia_resume() @@ -180,6 +194,12 @@ def _chia_resume(self) -> None: ) logging.getLogger(__name__).debug("Resumed accepting connections.") + def _chia_active_connections(self) -> int: + if sys.version_info >= (3, 13): + return len(self._clients) + else: + return self._active_count + async def _chia_create_server( cls: Any, diff --git a/install.sh b/install.sh index 5fe03c41ee02..230c04f5bd4a 100755 --- a/install.sh +++ b/install.sh @@ -72,7 +72,7 @@ OPENSSL_VERSION_INT= find_python() { set +e unset BEST_VERSION - for V in 312 3.12 311 3.11 310 3.10 39 3.9 3; do + for V in 313 3.13 312 3.12 311 3.11 310 3.10 39 3.9 3; do if command -v python$V >/dev/null; then if [ "$BEST_VERSION" = "" ]; then BEST_VERSION=$V @@ -135,8 +135,8 @@ if ! command -v "$INSTALL_PYTHON_PATH" >/dev/null; then exit 1 fi -if [ "$PYTHON_MAJOR_VER" -ne "3" ] || [ "$PYTHON_MINOR_VER" -lt "7" ] || [ "$PYTHON_MINOR_VER" -ge "13" ]; then - echo "Chia requires Python version >= 3.9 and < 3.13.0" >&2 +if [ "$PYTHON_MAJOR_VER" -ne "3" ] || [ "$PYTHON_MINOR_VER" -lt "7" ] || [ "$PYTHON_MINOR_VER" -ge "14" ]; then + echo "Chia requires Python version >= 3.9 and < 3.14.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 diff --git a/poetry.lock b/poetry.lock index e62adf751da5..c1d916f296f2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -190,7 +190,7 @@ description = "Python graph (network) package" optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"dev\" and python_version <= \"3.12\"" +markers = "extra == \"dev\" and python_version < \"3.14\"" files = [ {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, @@ -1921,7 +1921,7 @@ description = "Mach-O header analysis and editing" optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"dev\" and sys_platform == \"darwin\" and python_version <= \"3.12\"" +markers = "extra == \"dev\" and sys_platform == \"darwin\" and python_version < \"3.14\"" files = [ {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, @@ -2385,7 +2385,7 @@ description = "Python PE parsing module" optional = true python-versions = ">=3.6.0" groups = ["main"] -markers = "extra == \"dev\" and sys_platform == \"win32\" and python_version <= \"3.12\"" +markers = "extra == \"dev\" and sys_platform == \"win32\" and python_version < \"3.14\"" files = [ {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, @@ -2716,7 +2716,7 @@ description = "PyInstaller bundles a Python application and all its dependencies optional = true python-versions = "<3.15,>=3.8" groups = ["main"] -markers = "extra == \"dev\" and python_version <= \"3.12\"" +markers = "extra == \"dev\" and python_version < \"3.14\"" files = [ {file = "pyinstaller-6.15.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:9f00c71c40148cd1e61695b2c6f1e086693d3bcf9bfa22ab513aa4254c3b966f"}, {file = "pyinstaller-6.15.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cbcc8eb77320c60722030ac875883b564e00768fe3ff1721c7ba3ad0e0a277e9"}, @@ -2753,7 +2753,7 @@ description = "Community maintained hooks for PyInstaller" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"dev\" and python_version <= \"3.12\"" +markers = "extra == \"dev\" and python_version < \"3.14\"" files = [ {file = "pyinstaller_hooks_contrib-2025.8-py3-none-any.whl", hash = "sha256:8d0b8cfa0cb689a619294ae200497374234bd4e3994b3ace2a4442274c899064"}, {file = "pyinstaller_hooks_contrib-2025.8.tar.gz", hash = "sha256:3402ad41dfe9b5110af134422e37fc5d421ba342c6cb980bd67cb30b7415641c"}, @@ -3807,7 +3807,7 @@ description = "ZSTD Bindings for Python" optional = false python-versions = "*" groups = ["main"] -markers = "python_version == \"3.12\"" +markers = "python_version >= \"3.12\"" files = [ {file = "zstd-1.5.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5cd20afab8d13c52d2b2219bf18cc765eae87b8219343bce20647007890adab"}, {file = "zstd-1.5.5.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:0f467ab9b57ab8b4b874e6974d38b802f20406803bb7ec9308df923553cd48f7"}, @@ -3870,4 +3870,4 @@ upnp = ["miniupnpc"] [metadata] lock-version = "2.1" python-versions = ">=3.9, <4, !=3.9.0, !=3.9.1" -content-hash = "650f0c98ff03cf897ee793ebb1e26bee8ae7b3c99bab5295fee7736bc19e346f" +content-hash = "b7d446b58d0bc30a0f35c407a90e167c2f79550d5216399c090db6fe2cc802f4" diff --git a/pyproject.toml b/pyproject.toml index bd0d1f297330..b3f0e3605e17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,7 +79,7 @@ typing-extensions = ">=4.12.2" # typing backports like Protocol and TypedDict watchdog = ">=4.0.1" # Filesystem event watching - watches keyring.yaml zstd = [ {version=">=1.5.5.1", python = "<3.12"}, - {version=">=1.5.5.1", python = "3.12", source="chia"}, + {version=">=1.5.5.1", python = ">=3.12", source="chia"}, ] importlib-resources = ">=6.4.5" hsms = ">=0.3.1" @@ -92,7 +92,7 @@ diff-cover = { version = ">=9.2.0", optional = true } mypy = { version = ">=1.11.1", optional = true } pre-commit = { version = ">=3.7.1", optional = true } py3createtorrent = { version = ">=1.2.1", optional = true } -pyinstaller = { version = ">=6.9.0", python = "<3.13", optional = true } +pyinstaller = { version = ">=6.9.0", python = "<3.14", optional = true } pytest = { version = ">=8.3.3", optional = true } pytest-cov = { version = ">=5.0.0", optional = true } pytest-mock = { version = ">=3.14.0", optional = true }