Skip to content

Commit

Permalink
Add Python 3.12 to CI now that p4p is updated (#655)
Browse files Browse the repository at this point in the history
* Add Python 3.12 to CI and pyproject.toml now that p4p is updated

* skipped tango tests on 3.12 - this will go in a seperate issue #681

---------

Co-authored-by: Eva Lott <[email protected]>
  • Loading branch information
OCopping and evalott100 authored Dec 4, 2024
1 parent 04da787 commit 4ee4eb1
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
strategy:
matrix:
runs-on: ["ubuntu-latest", "windows-latest"] # can add macos-latest
python-version: ["3.10", "3.11"] # 3.12 should be added when p4p is updated
python-version: ["3.10","3.11","3.12"]
include:
# Include one that runs in the dev environment
- runs-on: "ubuntu-latest"
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ classifiers = [
"License :: OSI Approved :: BSD License",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
description = "Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango"
dependencies = [
Expand Down
3 changes: 2 additions & 1 deletion src/ophyd_async/epics/adcore/_core_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ async def complete_acquisition() -> None:
state = await driver.detector_state.get_value()
if state not in good_states:
raise ValueError(
f"Final detector state {state} not in valid end states: {good_states}"
f"Final detector state {state.value} not in valid end "
f"states: {good_states}"
)

return AsyncStatus(complete_acquisition())
34 changes: 25 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ def _error_and_kill_pending_tasks(
unfinished_tasks = {
task
for task in asyncio.all_tasks(loop)
if task.get_coro().__name__ not in _ALLOWED_PYTEST_TASKS and not task.done()
if (coro := task.get_coro()) is not None
and coro.__name__ not in _ALLOWED_PYTEST_TASKS
and not task.done()
}
for task in unfinished_tasks:
task.cancel()
Expand All @@ -113,15 +115,22 @@ def fail_test_on_unclosed_tasks(request: FixtureRequest):
by the end of the test.
"""

fail_count = request.session.testsfailed
loop = asyncio.get_event_loop()
loop.set_debug(True)
try:
fail_count = request.session.testsfailed
loop = asyncio.get_running_loop()

loop.set_debug(True)

request.addfinalizer(
lambda: _error_and_kill_pending_tasks(
loop, request.node.name, request.session.testsfailed == fail_count
request.addfinalizer(
lambda: _error_and_kill_pending_tasks(
loop, request.node.name, request.session.testsfailed == fail_count
)
)
)
# Once https://github.com/bluesky/ophyd-async/issues/683
# is finished we can remove this try, except.
except RuntimeError as error:
if str(error) != "no running event loop":
raise error


@pytest.fixture(scope="function")
Expand Down Expand Up @@ -242,4 +251,11 @@ def pytest_collection_modifyitems(config, items):

for item in items:
if tango_dir in str(item.fspath):
item.add_marker(pytest.mark.forked)
if sys.version_info >= (3, 12):
item.add_marker(
pytest.mark.skip(
reason="Tango is currently not supported on Python 3.12: https://github.com/bluesky/ophyd-async/issues/681"
)
)
else:
item.add_marker(pytest.mark.forked)
1 change: 1 addition & 0 deletions tests/core/test_mock_signal_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ async def test_blocks_during_put(mock_signals):
with mock_puts_blocked(signal1, signal2):
status1 = signal1.set("second_value", wait=True, timeout=None)
status2 = signal2.set("second_value", wait=True, timeout=None)
await asyncio.sleep(0.1)
assert await signal1.get_value() == "second_value"
assert await signal2.get_value() == "second_value"
assert not status1.done
Expand Down
4 changes: 2 additions & 2 deletions tests/core/test_observe.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ async def tick():

async def watch():
async for val in observe_value(sig, done_timeout=0.2):
time.sleep(0.15)
await asyncio.sleep(0.1)
recv.append(val)

t = asyncio.create_task(tick())
Expand All @@ -95,7 +95,7 @@ async def watch():
with pytest.raises(asyncio.TimeoutError):
await watch()
assert recv == [0, 1]
assert time.time() - start == pytest.approx(0.3, abs=0.05)
assert time.time() - start == pytest.approx(0.2, abs=0.05)
finally:
t.cancel()

Expand Down
16 changes: 8 additions & 8 deletions tests/core/test_readable.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ def test_standard_readable_hints():

assert sr.hints == {}

hint1 = MagicMock()
hint1 = MagicMock(spec=HasHints)
hint1.hints = {"fields": ["abc"], "dimensions": [(["f1", "f2"], "s1")]}

hint2 = MagicMock()
hint2 = MagicMock(spec=HasHints)
hint2.hints = {"fields": ["def", "ghi"]}

hint3 = MagicMock()
hint3 = MagicMock(spec=HasHints)
hint3.hints = {"fields": ["jkl"], "gridding": "rectilinear_nonsequential"}

sr.add_readables([hint1, hint2, hint3])
Expand All @@ -53,10 +53,10 @@ def test_standard_readable_hints():
def test_standard_readable_hints_raises_when_overriding_string_literal():
sr = StandardReadable()

hint1 = MagicMock()
hint1 = MagicMock(spec=HasHints)
hint1.hints = {"gridding": "rectilinear_nonsequential"}

hint2 = MagicMock()
hint2 = MagicMock(spec=HasHints)
hint2.hints = {"gridding": "a different string"}

sr._has_hints = (
Expand All @@ -71,10 +71,10 @@ def test_standard_readable_hints_raises_when_overriding_string_literal():
def test_standard_readable_hints_raises_when_overriding_sequence():
sr = StandardReadable()

hint1 = MagicMock()
hint1 = MagicMock(spec=HasHints)
hint1.hints = {"fields": ["field1", "field2"]}

hint2 = MagicMock()
hint2 = MagicMock(spec=HasHints)
hint2.hints = {"fields": ["field2"]}

sr._has_hints = (
Expand All @@ -90,7 +90,7 @@ def test_standard_readable_hints_raises_when_overriding_sequence():
def test_standard_readable_hints_invalid_types(invalid_type):
sr = StandardReadable()

hint1 = MagicMock()
hint1 = MagicMock(spec=HasHints)
hint1.hints = {"test": invalid_type}

sr._has_hints = (hint1,)
Expand Down
12 changes: 7 additions & 5 deletions tests/epics/adcore/test_drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,15 @@ async def test_start_acquiring_driver_and_ensure_status_fails_after_some_time(
set_mock_value(driver.detector_state, adcore.DetectorState.IDLE)

async def wait_then_fail():
await asyncio.sleep(0)
await asyncio.sleep(0.1)
set_mock_value(driver.detector_state, adcore.DetectorState.DISCONNECTED)

acquiring = await adcore.start_acquiring_driver_and_ensure_status(
driver, timeout=0.1
)
await wait_then_fail()

with pytest.raises(ValueError):
acquiring = await adcore.start_acquiring_driver_and_ensure_status(
driver, timeout=0.2
)
with pytest.raises(
ValueError, match="Final detector state Disconnected not in valid end states:"
):
await acquiring
6 changes: 4 additions & 2 deletions tests/sim/demo/test_sim_motor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import time

import pytest
from bluesky.plans import spiral_square
from bluesky.run_engine import RunEngine

Expand Down Expand Up @@ -49,6 +50,7 @@ async def test_stop():
new_pos = await m1.user_readback.get_value()
assert new_pos < 10
assert new_pos >= 0.1
# move should not be successful as we stopped it
assert move_status.done

assert not move_status.success
with pytest.raises(RuntimeError, match="Motor was stopped"):
await move_status

0 comments on commit 4ee4eb1

Please sign in to comment.