From 3703efe7268059d91fe625e457d35b644dc0504a Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 14:00:42 -0600 Subject: [PATCH 1/2] Fail hung tests after five minutes instead of stalling CI Add pytest-timeout with a 300 second limit to the root project and the standalone samples, so a stuck test fails with a stack dump and the job finishes instead of sitting at the 30 minute limit with no trace. --- bedrock_agentcore/strands_agent/pyproject.toml | 3 +++ bedrock_agentcore/strands_agent/uv.lock | 16 +++++++++++++++- lambda_worker/pyproject.toml | 3 +++ lambda_worker/uv.lock | 14 ++++++++++++++ openai_agents/mcp_v2/pyproject.toml | 3 +++ openai_agents/mcp_v2/uv.lock | 14 ++++++++++++++ pyproject.toml | 3 +++ uv.lock | 16 +++++++++++++++- 8 files changed, 70 insertions(+), 2 deletions(-) diff --git a/bedrock_agentcore/strands_agent/pyproject.toml b/bedrock_agentcore/strands_agent/pyproject.toml index 9cb221ce..4d06a4f5 100644 --- a/bedrock_agentcore/strands_agent/pyproject.toml +++ b/bedrock_agentcore/strands_agent/pyproject.toml @@ -21,6 +21,7 @@ dev = [ "poethepoet>=0.36.0", "pytest>=7.1.2,<8", "pytest-asyncio>=0.18.3,<0.19", + "pytest-timeout>=2.2,<3", ] [build-system] @@ -47,6 +48,8 @@ lint-types = "uv run --all-groups mypy --check-untyped-defs --namespace-packages test = "uv run pytest" [tool.pytest.ini_options] +# Fail a test that hangs, with a stack dump, instead of stalling the job. +timeout = 300 asyncio_mode = "auto" [tool.ruff] diff --git a/bedrock_agentcore/strands_agent/uv.lock b/bedrock_agentcore/strands_agent/uv.lock index fe937474..7149383f 100644 --- a/bedrock_agentcore/strands_agent/uv.lock +++ b/bedrock_agentcore/strands_agent/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 5 +revision = 3 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.15'", @@ -1885,6 +1885,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/4b/7c400506ec484ec999b10133aa8e31af39dfc727042dc6944cd45fd927d0/pytest_asyncio-0.18.3-py3-none-any.whl", hash = "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84", size = 14597, upload-time = "2022-03-25T09:43:57.106Z" }, ] +[[package]] +name = "pytest-timeout" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -2509,6 +2521,7 @@ dev = [ { name = "poethepoet" }, { name = "pytest" }, { name = "pytest-asyncio" }, + { name = "pytest-timeout" }, { name = "ruff" }, ] @@ -2527,6 +2540,7 @@ dev = [ { name = "poethepoet", specifier = ">=0.36.0" }, { name = "pytest", specifier = ">=7.1.2,<8" }, { name = "pytest-asyncio", specifier = ">=0.18.3,<0.19" }, + { name = "pytest-timeout", specifier = ">=2.2,<3" }, { name = "ruff", specifier = ">=0.5.0,<0.6" }, ] diff --git a/lambda_worker/pyproject.toml b/lambda_worker/pyproject.toml index bf473842..ab510556 100644 --- a/lambda_worker/pyproject.toml +++ b/lambda_worker/pyproject.toml @@ -15,6 +15,7 @@ dev = [ "poethepoet>=0.36.0", "pytest>=7.1.2,<8", "pytest-asyncio>=0.18.3,<0.19", + "pytest-timeout>=2.2,<3", ] [build-system] @@ -42,6 +43,8 @@ test = "uv run pytest" [tool.pytest.ini_options] asyncio_mode = "auto" +# Fail a test that hangs, with a stack dump, instead of stalling the job. +timeout = 300 [tool.ruff] target-version = "py310" diff --git a/lambda_worker/uv.lock b/lambda_worker/uv.lock index 7452a2ab..7068d705 100644 --- a/lambda_worker/uv.lock +++ b/lambda_worker/uv.lock @@ -475,6 +475,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/4b/7c400506ec484ec999b10133aa8e31af39dfc727042dc6944cd45fd927d0/pytest_asyncio-0.18.3-py3-none-any.whl", hash = "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84", size = 14597, upload-time = "2022-03-25T09:43:57.106Z" }, ] +[[package]] +name = "pytest-timeout" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -630,6 +642,7 @@ dev = [ { name = "poethepoet" }, { name = "pytest" }, { name = "pytest-asyncio" }, + { name = "pytest-timeout" }, { name = "ruff" }, ] @@ -642,6 +655,7 @@ dev = [ { name = "poethepoet", specifier = ">=0.36.0" }, { name = "pytest", specifier = ">=7.1.2,<8" }, { name = "pytest-asyncio", specifier = ">=0.18.3,<0.19" }, + { name = "pytest-timeout", specifier = ">=2.2,<3" }, { name = "ruff", specifier = ">=0.5.0,<0.6" }, ] diff --git a/openai_agents/mcp_v2/pyproject.toml b/openai_agents/mcp_v2/pyproject.toml index 5f8a1fe4..c496ffc7 100644 --- a/openai_agents/mcp_v2/pyproject.toml +++ b/openai_agents/mcp_v2/pyproject.toml @@ -13,6 +13,7 @@ dev = [ "mypy>=1.4.1,<2", "poethepoet>=0.36.0", "pytest>=9.0.3,<10", + "pytest-timeout>=2.2,<3", ] [tool.poe.tasks] @@ -35,5 +36,7 @@ target-version = "py310" ignore_missing_imports = true [tool.pytest.ini_options] +# Fail a test that hangs, with a stack dump, instead of stalling the job. +timeout = 300 pythonpath = ["."] testpaths = ["tests"] diff --git a/openai_agents/mcp_v2/uv.lock b/openai_agents/mcp_v2/uv.lock index ba27f40d..3da9f78d 100644 --- a/openai_agents/mcp_v2/uv.lock +++ b/openai_agents/mcp_v2/uv.lock @@ -1248,6 +1248,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] +[[package]] +name = "pytest-timeout" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1795,6 +1807,7 @@ dev = [ { name = "mypy" }, { name = "poethepoet" }, { name = "pytest" }, + { name = "pytest-timeout" }, { name = "ruff" }, ] @@ -1809,6 +1822,7 @@ dev = [ { name = "mypy", specifier = ">=1.4.1,<2" }, { name = "poethepoet", specifier = ">=0.36.0" }, { name = "pytest", specifier = ">=9.0.3,<10" }, + { name = "pytest-timeout", specifier = ">=2.2,<3" }, { name = "ruff", specifier = ">=0.5.0,<0.6" }, ] diff --git a/pyproject.toml b/pyproject.toml index 744e0eb3..78449a50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ dev = [ "mypy>=1.4.1,<2", "pytest>=7.1.2,<8", "pytest-asyncio>=0.18.3,<0.19", + "pytest-timeout>=2.2,<3", "fakeredis>=2,<3", "frozenlist>=1.4.0,<2", "pyright>=1.1.394", @@ -135,6 +136,8 @@ test = "uv run --all-groups --no-group cloud-export-to-parquet pytest" [tool.pytest.ini_options] asyncio_mode = "auto" +# Fail a test that hangs, with a stack dump, instead of stalling the job. +timeout = 300 log_cli = true log_cli_level = "INFO" log_cli_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)" diff --git a/uv.lock b/uv.lock index 4a075220..a670f212 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 5 +revision = 3 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.15'", @@ -3935,6 +3935,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/85/2f97a1b65178b0f11c9c77c35417a4cc5b99a80db90dad4734a129844ea5/pytest_pretty-1.3.0-py3-none-any.whl", hash = "sha256:074b9d5783cef9571494543de07e768a4dda92a3e85118d6c7458c67297159b7", size = 5620, upload-time = "2025-06-04T12:54:36.229Z" }, ] +[[package]] +name = "pytest-timeout" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -4803,6 +4815,7 @@ dev = [ { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-pretty" }, + { name = "pytest-timeout" }, { name = "ruff" }, { name = "types-pyyaml" }, ] @@ -4930,6 +4943,7 @@ dev = [ { name = "pytest", specifier = ">=7.1.2,<8" }, { name = "pytest-asyncio", specifier = ">=0.18.3,<0.19" }, { name = "pytest-pretty", specifier = ">=1.3.0" }, + { name = "pytest-timeout", specifier = ">=2.2,<3" }, { name = "ruff", specifier = ">=0.5.0,<0.6" }, { name = "types-pyyaml", specifier = ">=6.0.12.20241230,<7" }, ] From 493ccadcf1cafdc784e20954607c5c4f8c727288 Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 15:57:08 -0600 Subject: [PATCH 2/2] Let every root matrix job finish With fail-fast on, one flaky job cancels the rest and hides whether they would have passed, which has made this week's stalls harder to read. --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88ba116a..c3c57a37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,7 +101,8 @@ jobs: # Build and test the project build-lint-test: strategy: - fail-fast: true + # Let every matrix job finish so one flaky job does not hide the others. + fail-fast: false matrix: python: ["3.10", "3.14"] os: [ubuntu-latest, macos-intel, macos-arm, windows-latest]