lifx-async 4.3.8__tar.gz → 4.3.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. {lifx_async-4.3.8 → lifx_async-4.3.9}/.github/workflows/ci.yml +2 -0
  2. {lifx_async-4.3.8 → lifx_async-4.3.9}/CLAUDE.md +14 -26
  3. {lifx_async-4.3.8 → lifx_async-4.3.9}/PKG-INFO +1 -1
  4. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/changelog.md +8 -0
  5. {lifx_async-4.3.8 → lifx_async-4.3.9}/pyproject.toml +5 -3
  6. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/network/connection.py +5 -6
  7. lifx_async-4.3.9/tests/conftest.py +530 -0
  8. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_api/test_api_apply_theme.py +0 -3
  9. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_api/test_api_batch_errors.py +100 -77
  10. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_api/test_api_batch_operations.py +3 -0
  11. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_api/test_api_discovery.py +60 -41
  12. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_api/test_api_organization.py +5 -0
  13. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_color.py +4 -0
  14. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_devices/test_base.py +26 -18
  15. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_effects/test_colorloop.py +187 -0
  16. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_network/test_concurrent_requests.py +49 -30
  17. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_network/test_connection.py +1 -0
  18. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_network/test_discovery_devices.py +12 -16
  19. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_network/test_discovery_errors.py +6 -4
  20. lifx_async-4.3.9/tests/test_network/test_transport.py +382 -0
  21. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_protocol/test_serializer.py +224 -0
  22. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_theme/test_apply_theme.py +3 -0
  23. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_theme/test_canvas.py +64 -0
  24. {lifx_async-4.3.8 → lifx_async-4.3.9}/uv.lock +11 -205
  25. lifx_async-4.3.8/tests/conftest.py +0 -470
  26. lifx_async-4.3.8/tests/test_network/test_transport.py +0 -67
  27. {lifx_async-4.3.8 → lifx_async-4.3.9}/.claude/settings.json +0 -0
  28. {lifx_async-4.3.8 → lifx_async-4.3.9}/.github/dependabot.yml +0 -0
  29. {lifx_async-4.3.8 → lifx_async-4.3.9}/.github/labeler.yml +0 -0
  30. {lifx_async-4.3.8 → lifx_async-4.3.9}/.github/workflows/docs.yml +0 -0
  31. {lifx_async-4.3.8 → lifx_async-4.3.9}/.github/workflows/pr-automation.yml +0 -0
  32. {lifx_async-4.3.8 → lifx_async-4.3.9}/.gitignore +0 -0
  33. {lifx_async-4.3.8 → lifx_async-4.3.9}/.pre-commit-config.yaml +0 -0
  34. {lifx_async-4.3.8 → lifx_async-4.3.9}/LICENSE +0 -0
  35. {lifx_async-4.3.8 → lifx_async-4.3.9}/README.md +0 -0
  36. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/api/colors.md +0 -0
  37. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/api/devices.md +0 -0
  38. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/api/effects.md +0 -0
  39. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/api/exceptions.md +0 -0
  40. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/api/high-level.md +0 -0
  41. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/api/index.md +0 -0
  42. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/api/network.md +0 -0
  43. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/api/protocol.md +0 -0
  44. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/api/themes.md +0 -0
  45. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/architecture/effects-architecture.md +0 -0
  46. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/architecture/overview.md +0 -0
  47. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/faq.md +0 -0
  48. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/getting-started/effects.md +0 -0
  49. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/getting-started/installation.md +0 -0
  50. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/getting-started/quickstart.md +0 -0
  51. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/getting-started/themes.md +0 -0
  52. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/index.md +0 -0
  53. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/migration/effect-api-changes.md +0 -0
  54. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/stylesheets/extra.css +0 -0
  55. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/user-guide/advanced-usage.md +0 -0
  56. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/user-guide/effects-custom.md +0 -0
  57. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/user-guide/effects-troubleshooting.md +0 -0
  58. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/user-guide/protocol-deep-dive.md +0 -0
  59. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/user-guide/themes.md +0 -0
  60. {lifx_async-4.3.8 → lifx_async-4.3.9}/docs/user-guide/troubleshooting.md +0 -0
  61. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/01_simple_discovery.py +0 -0
  62. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/02_simple_control.py +0 -0
  63. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/03_waveforms.py +0 -0
  64. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/04_logging.py +0 -0
  65. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/06_pulse_effect.py +0 -0
  66. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/07_colorloop_effect.py +0 -0
  67. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/08_custom_effect.py +0 -0
  68. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/09_background_effect.py +0 -0
  69. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/10_find_specific_devices.py +0 -0
  70. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/11_matrix_basic.py +0 -0
  71. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/12_matrix_effects.py +0 -0
  72. {lifx_async-4.3.8 → lifx_async-4.3.9}/examples/13_matrix_large.py +0 -0
  73. {lifx_async-4.3.8 → lifx_async-4.3.9}/mkdocs.yml +0 -0
  74. {lifx_async-4.3.8 → lifx_async-4.3.9}/renovate.json +0 -0
  75. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/__init__.py +0 -0
  76. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/api.py +0 -0
  77. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/color.py +0 -0
  78. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/const.py +0 -0
  79. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/devices/__init__.py +0 -0
  80. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/devices/base.py +0 -0
  81. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/devices/hev.py +0 -0
  82. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/devices/infrared.py +0 -0
  83. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/devices/light.py +0 -0
  84. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/devices/matrix.py +0 -0
  85. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/devices/multizone.py +0 -0
  86. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/effects/__init__.py +0 -0
  87. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/effects/base.py +0 -0
  88. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/effects/colorloop.py +0 -0
  89. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/effects/conductor.py +0 -0
  90. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/effects/const.py +0 -0
  91. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/effects/models.py +0 -0
  92. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/effects/pulse.py +0 -0
  93. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/effects/state_manager.py +0 -0
  94. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/exceptions.py +0 -0
  95. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/network/__init__.py +0 -0
  96. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/network/discovery.py +0 -0
  97. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/network/message.py +0 -0
  98. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/network/transport.py +0 -0
  99. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/products/__init__.py +0 -0
  100. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/products/generator.py +0 -0
  101. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/products/registry.py +0 -0
  102. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/protocol/__init__.py +0 -0
  103. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/protocol/base.py +0 -0
  104. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/protocol/generator.py +0 -0
  105. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/protocol/header.py +0 -0
  106. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/protocol/models.py +0 -0
  107. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/protocol/packets.py +0 -0
  108. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/protocol/protocol_types.py +0 -0
  109. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/protocol/serializer.py +0 -0
  110. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/py.typed +0 -0
  111. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/theme/__init__.py +0 -0
  112. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/theme/canvas.py +0 -0
  113. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/theme/generators.py +0 -0
  114. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/theme/library.py +0 -0
  115. {lifx_async-4.3.8 → lifx_async-4.3.9}/src/lifx/theme/theme.py +0 -0
  116. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/__init__.py +0 -0
  117. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_api/__init__.py +0 -0
  118. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_devices/__init__.py +0 -0
  119. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_devices/conftest.py +0 -0
  120. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_devices/test_hev.py +0 -0
  121. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_devices/test_infrared.py +0 -0
  122. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_devices/test_light.py +0 -0
  123. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_devices/test_mac_address.py +0 -0
  124. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_devices/test_matrix.py +0 -0
  125. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_devices/test_multizone.py +0 -0
  126. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_effects/__init__.py +0 -0
  127. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_effects/test_base.py +0 -0
  128. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_effects/test_capability_filtering.py +0 -0
  129. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_effects/test_integration.py +0 -0
  130. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_effects/test_models.py +0 -0
  131. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_effects/test_pulse.py +0 -0
  132. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_effects/test_state_manager.py +0 -0
  133. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_network/__init__.py +0 -0
  134. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_network/test_message.py +0 -0
  135. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_network/test_message_advanced.py +0 -0
  136. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_products/test_product_generator.py +0 -0
  137. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_products/test_registry.py +0 -0
  138. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_protocol/test_generated.py +0 -0
  139. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_protocol/test_header.py +0 -0
  140. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_protocol/test_protocol_generator.py +0 -0
  141. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_theme/__init__.py +0 -0
  142. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_theme/conftest.py +0 -0
  143. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_theme/test_generators.py +0 -0
  144. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_theme/test_library.py +0 -0
  145. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_theme/test_theme.py +0 -0
  146. {lifx_async-4.3.8 → lifx_async-4.3.9}/tests/test_utils.py +0 -0
@@ -95,6 +95,7 @@ jobs:
95
95
  run: uv run --frozen pytest
96
96
 
97
97
  - name: Upload coverage to Codecov
98
+ if: matrix.os == 'ubuntu-latest'
98
99
  uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5
99
100
  with:
100
101
  env_vars: OS,PYTHON
@@ -105,6 +106,7 @@ jobs:
105
106
  verbose: true
106
107
 
107
108
  - name: Upload test results to Codecov
109
+ if: matrix.os == 'ubuntu-latest'
108
110
  uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5
109
111
  with:
110
112
  env_vars: OS,PYTHON
@@ -12,7 +12,7 @@ structures from a YAML specification.
12
12
  **Python Versions**: 3.11, 3.12, 3.13, 3.14 (tested on all versions via CI)
13
13
  **Runtime Dependencies**: Zero - completely dependency-free!
14
14
  **Async Framework**: Python's built-in `asyncio` (no external async library required)
15
- **Test Isolation**: lifx-emulator runs as subprocess, not a dependency
15
+ **Test Isolation**: lifx-emulator-core runs embedded in-process for fast, cross-platform testing
16
16
 
17
17
  ## Essential Commands
18
18
 
@@ -610,38 +610,27 @@ The `discover_devices()` function implements DoS protection through:
610
610
  Test files mirror source structure: `tests/test_devices/test_light.py` tests
611
611
  `src/lifx/devices/light.py`
612
612
 
613
- ### Integration Tests with lifx-emulator
613
+ ### Integration Tests with lifx-emulator-core
614
614
 
615
- Some tests require the `lifx-emulator` to run integration tests against real protocol implementations.
616
- The emulator runs as a **separate subprocess** and is **not** a dependency of lifx.
615
+ Some tests require `lifx-emulator-core` to run integration tests against real protocol implementations.
616
+ The emulator runs **embedded in-process** as a dev dependency, providing:
617
+ - Fast startup (~5-10ms vs 500ms+ for subprocess)
618
+ - Cross-platform support (Windows, macOS, Linux)
619
+ - Direct access to emulator internals for scenario testing
617
620
 
618
- **Setup Options**:
619
-
620
- 1. **Development setup** (recommended): Clone lifx-emulator as a sibling directory
621
- ```bash
622
- cd ..
623
- git clone https://github.com/Djelibeybi/lifx-emulator.git
624
- cd lifx-emulator
625
- uv sync
626
- cd ../lifx
627
- ```
628
-
629
- 2. **System install**: Install lifx-emulator globally (requires Python 3.13+)
630
- ```bash
631
- uv tool install lifx-emulator
632
- ```
621
+ **Setup**: The emulator is automatically installed as a dev dependency:
622
+ ```bash
623
+ uv sync # Installs lifx-emulator-core automatically
624
+ ```
633
625
 
634
626
  **Running Integration Tests**:
627
+ - Tests marked with `@pytest.mark.emulator` use the embedded emulator
635
628
  - If emulator is not available, these tests are automatically skipped
636
- - No code changes needed - pytest plugin handles everything
637
- - **Works on all Python versions (3.11+)** since emulator runs as separate process
638
-
639
- **Note**: The emulator itself requires Python 3.13+, but it runs as a subprocess so your
640
- lifx tests can run on any supported Python version (3.11-3.14).
629
+ - **Works on all Python versions (3.11+)**
641
630
 
642
631
  **External Emulator Management**:
643
632
 
644
- For cases where you want to manage the emulator separately (or test against actual hardware), you can skip the automatic emulator subprocess startup:
633
+ For cases where you want to manage the emulator separately (or test against actual hardware):
645
634
 
646
635
  ```bash
647
636
  # Use an externally managed emulator instance
@@ -654,7 +643,6 @@ LIFX_EMULATOR_EXTERNAL=1 pytest
654
643
  This is useful when:
655
644
  - Testing against actual LIFX hardware on your network
656
645
  - Running the emulator with custom configuration or device setup
657
- - Using a shared emulator instance across multiple test runs
658
646
  - Debugging emulator behavior separately from the test suite
659
647
 
660
648
  **Key Test Files:**
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lifx-async
3
- Version: 4.3.8
3
+ Version: 4.3.9
4
4
  Summary: A modern, type-safe, async Python library for controlling LIFX lights
5
5
  Author-email: Avi Miller <me@dje.li>
6
6
  Maintainer-email: Avi Miller <me@dje.li>
@@ -2,6 +2,14 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v4.3.9 (2025-11-27)
6
+
7
+ ### Bug Fixes
8
+
9
+ - **network**: Propagate timeout from request() to internal methods
10
+ ([`b35ebea`](https://github.com/Djelibeybi/lifx-async/commit/b35ebea46120bfd4ad9ce149f5e25125d3694b30))
11
+
12
+
5
13
  ## v4.3.8 (2025-11-25)
6
14
 
7
15
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lifx-async"
3
- version = "4.3.8"
3
+ version = "4.3.9"
4
4
  description = "A modern, type-safe, async Python library for controlling LIFX lights"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -31,7 +31,7 @@ classifiers = [
31
31
  [dependency-groups]
32
32
  dev = [
33
33
  "hatchling>=1.27.0",
34
- "lifx-emulator>=2.4.0",
34
+ "lifx-emulator-core>=3.0.3",
35
35
  "mkdocs-git-revision-date-localized-plugin>=1.4.7",
36
36
  "mkdocs-llmstxt>=0.4.0",
37
37
  "mkdocs-material>=9.6.22",
@@ -41,7 +41,6 @@ dev = [
41
41
  "pytest-asyncio>=0.24.0",
42
42
  "pytest-cov>=7.0.0",
43
43
  "pytest-sugar>=1.1.1",
44
- "pytest-xprocess>=1.0.2",
45
44
  "pyyaml>=6.0.3",
46
45
  "ruff>=0.14.2",
47
46
  ]
@@ -102,6 +101,9 @@ addopts = """\
102
101
  """
103
102
  asyncio_mode = "auto"
104
103
  asyncio_default_fixture_loop_scope = "function"
104
+ markers = [
105
+ "emulator: tests that require the lifx-emulator-core embedded emulator",
106
+ ]
105
107
 
106
108
  [tool.coverage.run]
107
109
  omit = [
@@ -760,7 +760,7 @@ class DeviceConnection:
760
760
  async def request_stream(
761
761
  self,
762
762
  packet: Any,
763
- timeout: float = DEFAULT_REQUEST_TIMEOUT,
763
+ timeout: float | None = None,
764
764
  ) -> AsyncGenerator[Any, None]:
765
765
  """Send request and yield unpacked responses.
766
766
 
@@ -823,6 +823,9 @@ class DeviceConnection:
823
823
  # Ensure connection is open (lazy opening)
824
824
  await self._ensure_open()
825
825
 
826
+ if timeout is None:
827
+ timeout = self.timeout
828
+
826
829
  # Get packet metadata
827
830
  packet_kind = getattr(packet, "_packet_kind", "OTHER")
828
831
 
@@ -942,11 +945,7 @@ class DeviceConnection:
942
945
  f"Packet missing PKT_TYPE: {type(packet).__name__}"
943
946
  )
944
947
 
945
- async def request(
946
- self,
947
- packet: Any,
948
- timeout: float = DEFAULT_REQUEST_TIMEOUT,
949
- ) -> Any:
948
+ async def request(self, packet: Any, timeout: float | None = None) -> Any:
950
949
  """Send request and get single response (convenience wrapper).
951
950
 
952
951
  This is a convenience method that returns the first response from