-
Notifications
You must be signed in to change notification settings - Fork 15
Support recent zigpy and Python 3.14 #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
23b483f
d717a93
24687e5
bef69fa
978ddb6
40638df
8a3e22f
d9328c2
1a8706a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,4 +77,6 @@ ENV/ | |
|
|
||
| TI Z-Stack/ | ||
|
|
||
| zigpy-znp-*.*.*/ | ||
| zigpy-znp-*.*.*/ | ||
| # uv | ||
| uv.lock | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,31 +12,41 @@ authors = [ | |
| ] | ||
| readme = "README.md" | ||
| license = {text = "GPL-3.0"} | ||
| requires-python = ">=3.8" | ||
| requires-python = ">=3.11" | ||
| dependencies = [ | ||
| "click", | ||
| "coloredlogs", | ||
| "scapy", | ||
| "zigpy>=0.75.0", | ||
| "bellows>=0.43.0", | ||
| "zigpy-deconz>=0.21.0", | ||
| "zigpy-xbee>=0.18.0", | ||
| "zigpy-zboss>=1.1.0", | ||
| "zigpy-zigate>=0.11.0", | ||
| "zigpy-znp>=0.11.1" | ||
| "click>=8.1", | ||
| "coloredlogs>=15.0", | ||
| "scapy>=2.5.0", | ||
| "zigpy>=2.0.0", | ||
| "bellows>=0.47.0", | ||
| "zigpy-deconz>=0.25.0", | ||
| "zigpy-xbee>=0.22.0", | ||
| "zigpy-zigate>=0.14.0", | ||
| "zigpy-znp>=1.0.0" | ||
| ] | ||
|
|
||
| [tool.setuptools.packages.find] | ||
| exclude = ["tests", "tests.*"] | ||
|
|
||
| [project.optional-dependencies] | ||
| [dependency-groups] | ||
| testing = [ | ||
| "pytest>=7.1.2", | ||
| "pytest-asyncio>=0.19.0", | ||
| "pytest-timeout>=2.1.0", | ||
| "pytest-mock>=3.8.2", | ||
| "pytest-cov>=3.0.0", | ||
| "coverage[toml]>=7.0", | ||
| "pre-commit>=3.5", | ||
| "pytest>=8.4", | ||
| "pytest-asyncio>=1.0", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional, for consistency with zigpy: zigpy's own [tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"and this PR adds the dependency without either. There is no correctness risk in leaving it out — I checked that both pytest 8.4.0 (the declared floor) and 9.1.1 fail an unmarked
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't matter now but I think we can add it for consistency. Did so with: 1a8706a (though the second part of the commit message with |
||
| "pytest-cov>=5.0", | ||
| "pytest-mock>=3.14", | ||
| "pytest-timeout>=2.3", | ||
| ] | ||
| ci = [ | ||
| {include-group = "testing"}, | ||
| "pytest-github-actions-annotate-failures>=0.2", | ||
| "pytest-xdist>=3.5", | ||
| ] | ||
|
|
||
| [tool.pytest.ini_options] | ||
| asyncio_mode = "auto" | ||
| asyncio_default_fixture_loop_scope = "function" | ||
|
|
||
| [tool.setuptools-git-versioning] | ||
| enabled = true | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import asyncio | ||
|
|
||
| import pytest | ||
|
|
||
| from zigpy_cli import cli as cli_module | ||
| from zigpy_cli.cli import click_coroutine, get_or_create_event_loop | ||
|
|
||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def reset_loop(): | ||
| """Keep the module-level loop from leaking between tests.""" | ||
| cli_module._LOOP = None | ||
| asyncio.set_event_loop(None) | ||
|
|
||
| yield | ||
|
|
||
| if cli_module._LOOP is not None and not cli_module._LOOP.is_closed(): | ||
| cli_module._LOOP.close() | ||
|
|
||
| cli_module._LOOP = None | ||
| asyncio.set_event_loop(None) | ||
|
|
||
|
|
||
| def test_click_coroutine_without_running_loop(): | ||
| """`click_coroutine` works when no event loop exists yet. | ||
|
|
||
| Python 3.14's `asyncio.get_event_loop()` raises instead of creating one. | ||
| """ | ||
| asyncio.set_event_loop(None) | ||
|
|
||
| @click_coroutine | ||
| async def cmd(value): | ||
| await asyncio.sleep(0) | ||
| return value * 2 | ||
|
|
||
| assert cmd(21) == 42 | ||
|
|
||
|
|
||
| def test_click_coroutine_reuses_the_same_loop(): | ||
| """All callbacks must share a loop: the group creates the app, the | ||
| subcommand uses it, and the cleanup callback shuts it down.""" | ||
| loops = [] | ||
|
|
||
| @click_coroutine | ||
| async def cmd(): | ||
| loops.append(asyncio.get_running_loop()) | ||
|
|
||
| cmd() | ||
| cmd() | ||
|
|
||
| assert loops[0] is loops[1] | ||
| assert loops[0] is get_or_create_event_loop() | ||
|
|
||
|
|
||
| def test_get_or_create_event_loop_replaces_closed_loop(): | ||
| loop = get_or_create_event_loop() | ||
| loop.close() | ||
|
|
||
| new_loop = get_or_create_event_loop() | ||
|
|
||
| assert new_loop is not loop | ||
| assert not new_loop.is_closed() | ||
|
|
||
|
|
||
| def test_get_or_create_event_loop_reregisters_cleared_loop(): | ||
| """The loop stays registered as current even if something else clears it. | ||
|
|
||
| Radio libraries call `asyncio.get_event_loop()` at runtime, which fails on | ||
| Python 3.14 when no loop is set. | ||
| """ | ||
| loop = get_or_create_event_loop() | ||
| asyncio.set_event_loop(None) | ||
|
|
||
| assert get_or_create_event_loop() is loop | ||
| assert asyncio.get_event_loop() is loop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note:
uv.lockis added to.gitignorefor now, but we may want to track that in the future. We can look at that in a future PR.