Upgrade pytest to 9.0.0, remove pytest-subtests, and migrate to new TOML config
Pytest 9.0.0 introduced native support for unittest's self.subTest() and stricter serialization requirements for subTest parameters when using pytest-xdist parallel execution. Non-serializable objects (mocks, datetime instances, exceptions) passed to subTest() now cause DumpError failures.
Changes
- Upgraded pytest: 8.4.2 → 9.0.0 in
pyproject.toml - Removed pytest-subtests: Removed
pytest-subtests==0.14.1from dev dependencies (pytest 9.0 has native support) - Migrated to new TOML config format: Changed from
[tool.pytest.ini_options]to[tool.pytest]with list-basedaddopts - Fixed subTest serialization: Replaced non-serializable parameters with simple identifiers across 20+ test files
Pattern Applied
Before:
for case in test_cases:
with self.subTest(error=case["error"], channel=MockTextChannel()):
# test logic
After:
for i, case in enumerate(test_cases):
with self.subTest(test_case=i, has_channel=True):
# test logic
Configuration Migration
Before:
[tool.pytest.ini_options]
addopts = "-p no:nose"
After:
[tool.pytest]
addopts = ["-p", "no:nose"]
Additional Fixes
- Fixed
HTTPExceptioninitialization intest_utils.py(requiresmessageargument)
All 442 tests pass with xdist parallel execution.
Original prompt
- Update pytest to 9.0.0
- Migrate to using TOML configuration for pytest: https://docs.pytest.org/en/stable/reference/customize.html#config-file-formats
- Additionally, fix the tests so these DumpErrors related to our mocks do not occur:
[gw1] linux -- Python 3.13.9 /home/runner/work/bot/bot/.venv/bin/python3
self = <tests.bot.exts.backend.test_error_handler.IndividualErrorHandlerTests testMethod=test_handle_api_error>
log_mock = <MagicMock name='log' id='140094977313488'>
@patch("bot.exts.backend.error_handler.log")
async def test_handle_api_error(self, log_mock):
"""Should `ctx.send` on HTTP error codes, and log at correct level."""
test_cases = (
{
"error": ResponseCodeError(AsyncMock(status=400)),
"log_level": "error"
},
{
"error": ResponseCodeError(AsyncMock(status=404)),
"log_level": "debug"
},
{
"error": ResponseCodeError(AsyncMock(status=550)),
"log_level": "warning"
},
{
"error": ResponseCodeError(AsyncMock(status=1000)),
"log_level": "warning"
}
)
for case in test_cases:
> with self.subTest(error=case["error"], log_level=case["log_level"]):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
tests/bot/exts/backend/test_error_handler.py:486:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../_temp/uv-python-dir/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/contextlib.py:148: in __exit__
next(self.gen)
../../_temp/uv-python-dir/cpython-3.13.9-linux-x86_64-gnu/lib/python3.13/contextlib.py:148: in __exit__
next(self.gen)
.venv/lib/python3.13/site-packages/_pytest/unittest.py:438: in addSubTest
self.ihook.pytest_runtest_logreport(report=sub_report)
.venv/lib/python3.13/site-packages/pluggy/_hooks.py:512: in __call__
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13/site-packages/pluggy/_manager.py:120: in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13/site-packages/xdist/remote.py:255: in pytest_runtest_logreport
self.sendevent("testreport", data=data)
.venv/lib/python3.13/site-packages/xdist/remote.py:98: in sendevent
self.channel.send((name, kwargs))
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:912: in send
self.gateway._send(Message.CHANNEL_DATA, self.id, dumps_internal(item))
^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1629: in dumps_internal
return _Serializer().save(obj) # type: ignore[return-value]
^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1647: in save
self._save(obj)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1667: in _save
dispatch(self, obj)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1744: in save_tuple
self._save(item)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1667: in _save
dispatch(self, obj)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1740: in save_dict
self._write_setitem(key, value)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1734: in _write_setitem
self._save(value)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1667: in _save
dispatch(self, obj)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1740: in save_dict
self._write_setitem(key, value)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1734: in _write_setitem
self._save(value)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1667: in _save
dispatch(self, obj)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1740: in save_dict
self._write_setitem(key, value)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1734: in _write_setitem
self._save(value)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1667: in _save
dispatch(self, obj)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1740: in save_dict
self._write_setitem(key, value)
.venv/lib/python3.13/site-packages/execnet/gateway_base.py:1734: in _write_setitem
self._save(value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <execnet.gateway_base._Serializer object at 0x7f6a67591eb0>
obj = ResponseCodeError(<AsyncMock id='140094976712656'>)
def _save(self, obj: object) -> None:
tp = type(obj)
try:
dispatch = self._dispatch[tp]
except KeyError:
methodname = "save_" + tp.__name__
...
</details>
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.
@copilot - pytest 9.0 supports subtests natively now, and these should be used (and the pytest-subtests removed from the pyproject and project), they are documented here: https://docs.pytest.org/en/stable/how-to/subtests.html#subtests
- The directive about moving to TOML specifically means the new non-INI method: https://docs.pytest.org/en/stable/changelog.html#new-features
@copilot - pytest 9.0 supports subtests natively now, and these should be used (and the pytest-subtests removed from the pyproject and project), they are documented here: https://docs.pytest.org/en/st...
Removed pytest-subtests plugin and migrated to the new TOML configuration format in commit e6029ef.
Changes:
- Removed
pytest-subtests==0.14.1from dev dependencies (pytest 9.0 has native support for unittest'sself.subTest()) - Changed
[tool.pytest.ini_options]to[tool.pytest] - Updated
addoptsfrom string to list format:["-p", "no:nose"]
All 442 tests passing with the new configuration.
[!WARNING]
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
publicsuffix.org
- Triggering command:
/home/REDACTED/work/bot/bot/.venv/bin/python -u -c import sys;exec(eval(sys.stdin.readline()))(dns block)If you need me to access, download, or install something from one of these locations, you can either:
- Configure Actions setup steps to set up my environment, which run before the firewall is enabled
- Add the appropriate URLs or hosts to the custom allowlist in this repository's Copilot coding agent settings (admins only)
Will go through and have a proper look case-by-case, I agree there are probably ways in a lot of them where we can better serialize instead of using enumerate.