ucapi-framework 1.2.2__tar.gz → 1.6.4__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.
- {ucapi_framework-1.2.2/ucapi_framework.egg-info → ucapi_framework-1.6.4}/PKG-INFO +51 -3
- ucapi_framework-1.2.2/PKG-INFO → ucapi_framework-1.6.4/README.md +49 -11
- ucapi_framework-1.6.4/pyproject.toml +102 -0
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/tests/test_device.py +6 -65
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/tests/test_driver.py +849 -147
- ucapi_framework-1.6.4/tests/test_entity.py +538 -0
- ucapi_framework-1.6.4/tests/test_factory_pattern.py +440 -0
- ucapi_framework-1.6.4/tests/test_helpers.py +412 -0
- ucapi_framework-1.6.4/tests/test_migration.py +1430 -0
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/tests/test_setup.py +646 -10
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework/__init__.py +39 -2
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework/config.py +23 -10
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework/device.py +125 -17
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework/discovery.py +2 -2
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework/driver.py +541 -283
- ucapi_framework-1.6.4/ucapi_framework/entity.py +324 -0
- ucapi_framework-1.6.4/ucapi_framework/helpers.py +278 -0
- ucapi_framework-1.6.4/ucapi_framework/migration.py +864 -0
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework/setup.py +822 -52
- ucapi_framework-1.2.2/README.md → ucapi_framework-1.6.4/ucapi_framework.egg-info/PKG-INFO +59 -1
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework.egg-info/SOURCES.txt +7 -0
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework.egg-info/requires.txt +1 -1
- ucapi_framework-1.2.2/pyproject.toml +0 -34
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/setup.cfg +0 -0
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/tests/test_config.py +0 -0
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/tests/test_discovery.py +0 -0
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework.egg-info/dependency_links.txt +0 -0
- {ucapi_framework-1.2.2 → ucapi_framework-1.6.4}/ucapi_framework.egg-info/top_level.txt +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ucapi-framework
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.4
|
|
4
4
|
Summary: ucapi framework that provides core functionality for building integrations.
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
Requires-Dist: pyee>=9.0.0
|
|
8
|
-
Requires-Dist: ucapi>=0.
|
|
8
|
+
Requires-Dist: ucapi>=0.5.1
|
|
9
9
|
Requires-Dist: aiohttp>=3.9.0
|
|
10
10
|
|
|
11
11
|
[](https://github.com/jackjpowell/ucapi-framework/actions/workflows/test.yml)
|
|
@@ -14,7 +14,7 @@ Requires-Dist: aiohttp>=3.9.0
|
|
|
14
14
|
|
|
15
15
|
# UCAPI Framework
|
|
16
16
|
|
|
17
|
-
A framework for building Unfolded Circle Remote integrations that handles the repetitive parts of integration development so you can focus on what's important.
|
|
17
|
+
A framework for building Unfolded Circle Remote integrations that handles the repetitive parts of integration development so you can focus on what's important. Full documentation is available at [https://jackjpowell.github.io/ucapi-framework/](https://jackjpowell.github.io/ucapi-framework/)
|
|
18
18
|
|
|
19
19
|
## What This Solves
|
|
20
20
|
|
|
@@ -191,6 +191,9 @@ Total: ~295 lines of integration code vs ~885 lines previously. And the new code
|
|
|
191
191
|
|
|
192
192
|
If you have an existing integration, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) for step-by-step instructions with before/after examples.
|
|
193
193
|
|
|
194
|
+
For upgrading from a previous version of ucapi-framework:
|
|
195
|
+
- **[Upgrading to 1.6.0](https://jackjpowell.github.io/ucapi-framework/upgrade-to-1.6.0/)** - New dynamic entity management features
|
|
196
|
+
|
|
194
197
|
## Requirements
|
|
195
198
|
|
|
196
199
|
- Python 3.11+
|
|
@@ -219,6 +222,51 @@ mkdocs serve
|
|
|
219
222
|
|
|
220
223
|
Visit <http://127.0.0.1:8000> to view the docs.
|
|
221
224
|
|
|
225
|
+
## Development
|
|
226
|
+
|
|
227
|
+
### Setup
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
# Install development dependencies (includes ruff)
|
|
231
|
+
uv sync --group dev
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Git hooks are automatically active from the `git-hooks/` directory:
|
|
235
|
+
|
|
236
|
+
- **pre-commit**: Runs `ruff check --fix` and `ruff format` via `uv run`
|
|
237
|
+
|
|
238
|
+
All development tools run through `uv` and are configured in `pyproject.toml`.
|
|
239
|
+
|
|
240
|
+
### Code Quality
|
|
241
|
+
|
|
242
|
+
This project uses [Ruff](https://github.com/astral-sh/ruff) for both linting and formatting.
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Run linter
|
|
246
|
+
ruff check
|
|
247
|
+
|
|
248
|
+
# Run linter with auto-fix
|
|
249
|
+
ruff check --fix
|
|
250
|
+
|
|
251
|
+
# Run formatter
|
|
252
|
+
ruff format
|
|
253
|
+
|
|
254
|
+
# Check formatting without making changes
|
|
255
|
+
ruff format --check
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Ruff is configured in `pyproject.toml` to match Black's formatting style and includes Flake8-compatible rules.
|
|
259
|
+
|
|
260
|
+
### Testing
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# Run all tests
|
|
264
|
+
pytest
|
|
265
|
+
|
|
266
|
+
# Run with coverage
|
|
267
|
+
pytest --cov=ucapi_framework --cov-report=term-missing
|
|
268
|
+
```
|
|
269
|
+
|
|
222
270
|
## License
|
|
223
271
|
|
|
224
272
|
Mozilla Public License Version 2.0
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ucapi-framework
|
|
3
|
-
Version: 1.2.2
|
|
4
|
-
Summary: ucapi framework that provides core functionality for building integrations.
|
|
5
|
-
Requires-Python: >=3.11
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: pyee>=9.0.0
|
|
8
|
-
Requires-Dist: ucapi>=0.4.0
|
|
9
|
-
Requires-Dist: aiohttp>=3.9.0
|
|
10
|
-
|
|
11
1
|
[](https://github.com/jackjpowell/ucapi-framework/actions/workflows/test.yml)
|
|
12
2
|
[](https://discord.gg/zGVYf58)
|
|
13
3
|
[](https://buymeacoffee.com/jackpowell)
|
|
14
4
|
|
|
15
5
|
# UCAPI Framework
|
|
16
6
|
|
|
17
|
-
A framework for building Unfolded Circle Remote integrations that handles the repetitive parts of integration development so you can focus on what's important.
|
|
7
|
+
A framework for building Unfolded Circle Remote integrations that handles the repetitive parts of integration development so you can focus on what's important. Full documentation is available at [https://jackjpowell.github.io/ucapi-framework/](https://jackjpowell.github.io/ucapi-framework/)
|
|
18
8
|
|
|
19
9
|
## What This Solves
|
|
20
10
|
|
|
@@ -191,6 +181,9 @@ Total: ~295 lines of integration code vs ~885 lines previously. And the new code
|
|
|
191
181
|
|
|
192
182
|
If you have an existing integration, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) for step-by-step instructions with before/after examples.
|
|
193
183
|
|
|
184
|
+
For upgrading from a previous version of ucapi-framework:
|
|
185
|
+
- **[Upgrading to 1.6.0](https://jackjpowell.github.io/ucapi-framework/upgrade-to-1.6.0/)** - New dynamic entity management features
|
|
186
|
+
|
|
194
187
|
## Requirements
|
|
195
188
|
|
|
196
189
|
- Python 3.11+
|
|
@@ -219,6 +212,51 @@ mkdocs serve
|
|
|
219
212
|
|
|
220
213
|
Visit <http://127.0.0.1:8000> to view the docs.
|
|
221
214
|
|
|
215
|
+
## Development
|
|
216
|
+
|
|
217
|
+
### Setup
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# Install development dependencies (includes ruff)
|
|
221
|
+
uv sync --group dev
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Git hooks are automatically active from the `git-hooks/` directory:
|
|
225
|
+
|
|
226
|
+
- **pre-commit**: Runs `ruff check --fix` and `ruff format` via `uv run`
|
|
227
|
+
|
|
228
|
+
All development tools run through `uv` and are configured in `pyproject.toml`.
|
|
229
|
+
|
|
230
|
+
### Code Quality
|
|
231
|
+
|
|
232
|
+
This project uses [Ruff](https://github.com/astral-sh/ruff) for both linting and formatting.
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Run linter
|
|
236
|
+
ruff check
|
|
237
|
+
|
|
238
|
+
# Run linter with auto-fix
|
|
239
|
+
ruff check --fix
|
|
240
|
+
|
|
241
|
+
# Run formatter
|
|
242
|
+
ruff format
|
|
243
|
+
|
|
244
|
+
# Check formatting without making changes
|
|
245
|
+
ruff format --check
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Ruff is configured in `pyproject.toml` to match Black's formatting style and includes Flake8-compatible rules.
|
|
249
|
+
|
|
250
|
+
### Testing
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Run all tests
|
|
254
|
+
pytest
|
|
255
|
+
|
|
256
|
+
# Run with coverage
|
|
257
|
+
pytest --cov=ucapi_framework --cov-report=term-missing
|
|
258
|
+
```
|
|
259
|
+
|
|
222
260
|
## License
|
|
223
261
|
|
|
224
262
|
Mozilla Public License Version 2.0
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ucapi-framework"
|
|
3
|
+
version = "1.6.4"
|
|
4
|
+
description = "ucapi framework that provides core functionality for building integrations."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"pyee>=9.0.0",
|
|
9
|
+
"ucapi>=0.5.1",
|
|
10
|
+
"aiohttp>=3.9.0",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[dependency-groups]
|
|
14
|
+
dev = [
|
|
15
|
+
"pytest>=9.0.1",
|
|
16
|
+
"pytest-asyncio>=0.23.0",
|
|
17
|
+
"pytest-cov>=4.1.0",
|
|
18
|
+
"ruff>=0.8.0",
|
|
19
|
+
"ty>=0.0.4",
|
|
20
|
+
]
|
|
21
|
+
docs = [
|
|
22
|
+
"mkdocs-material>=9.5.0",
|
|
23
|
+
"mkdocstrings[python]>=0.24.0",
|
|
24
|
+
"mkdocstrings-python>=1.0.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[tool.pytest.ini_options]
|
|
28
|
+
testpaths = ["tests"]
|
|
29
|
+
python_files = ["test_*.py"]
|
|
30
|
+
python_classes = ["Test*"]
|
|
31
|
+
python_functions = ["test_*"]
|
|
32
|
+
asyncio_mode = "auto"
|
|
33
|
+
addopts = [
|
|
34
|
+
"-v",
|
|
35
|
+
"--tb=short",
|
|
36
|
+
"--strict-markers",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
# Exclude a variety of commonly ignored directories.
|
|
41
|
+
exclude = [
|
|
42
|
+
".bzr",
|
|
43
|
+
".direnv",
|
|
44
|
+
".eggs",
|
|
45
|
+
".git",
|
|
46
|
+
".git-rewrite",
|
|
47
|
+
".hg",
|
|
48
|
+
".ipynb_checkpoints",
|
|
49
|
+
".mypy_cache",
|
|
50
|
+
".nox",
|
|
51
|
+
".pants.d",
|
|
52
|
+
".pyenv",
|
|
53
|
+
".pytest_cache",
|
|
54
|
+
".pytype",
|
|
55
|
+
".ruff_cache",
|
|
56
|
+
".svn",
|
|
57
|
+
".tox",
|
|
58
|
+
".venv",
|
|
59
|
+
".vscode",
|
|
60
|
+
"__pypackages__",
|
|
61
|
+
"_build",
|
|
62
|
+
"buck-out",
|
|
63
|
+
"build",
|
|
64
|
+
"dist",
|
|
65
|
+
"node_modules",
|
|
66
|
+
"site-packages",
|
|
67
|
+
"venv",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
# Same as Black.
|
|
71
|
+
line-length = 88
|
|
72
|
+
indent-width = 4
|
|
73
|
+
|
|
74
|
+
# Assume Python 3.11
|
|
75
|
+
target-version = "py311"
|
|
76
|
+
|
|
77
|
+
[tool.ruff.lint]
|
|
78
|
+
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
|
79
|
+
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
|
80
|
+
# McCabe complexity (`C901`) by default.
|
|
81
|
+
select = ["E4", "E7", "E9", "F"]
|
|
82
|
+
ignore = []
|
|
83
|
+
|
|
84
|
+
# Allow fix for all enabled rules (when `--fix`) is provided.
|
|
85
|
+
fixable = ["ALL"]
|
|
86
|
+
unfixable = []
|
|
87
|
+
|
|
88
|
+
# Allow unused variables when underscore-prefixed.
|
|
89
|
+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
90
|
+
|
|
91
|
+
[tool.ruff.format]
|
|
92
|
+
# Like Black, use double quotes for strings.
|
|
93
|
+
quote-style = "double"
|
|
94
|
+
|
|
95
|
+
# Like Black, indent with spaces, rather than tabs.
|
|
96
|
+
indent-style = "space"
|
|
97
|
+
|
|
98
|
+
# Like Black, respect magic trailing commas.
|
|
99
|
+
skip-magic-trailing-comma = false
|
|
100
|
+
|
|
101
|
+
# Like Black, automatically detect the appropriate line ending.
|
|
102
|
+
line-ending = "auto"
|
|
@@ -536,39 +536,6 @@ class TestStatelessHTTPDevice:
|
|
|
536
536
|
assert device._is_connected is False
|
|
537
537
|
assert len(events_emitted) == 1
|
|
538
538
|
|
|
539
|
-
@pytest.mark.asyncio
|
|
540
|
-
@pytest.mark.asyncio
|
|
541
|
-
@pytest.mark.skip(
|
|
542
|
-
reason="Complex async context manager mocking - implementation verified manually"
|
|
543
|
-
)
|
|
544
|
-
async def test_http_request(self, mock_device_config, event_loop):
|
|
545
|
-
"""Test HTTP request method."""
|
|
546
|
-
device = ConcreteStatelessHTTPDevice(mock_device_config, loop=event_loop)
|
|
547
|
-
|
|
548
|
-
mock_response = AsyncMock()
|
|
549
|
-
mock_response.raise_for_status = Mock()
|
|
550
|
-
mock_response.status = 200
|
|
551
|
-
|
|
552
|
-
with patch("aiohttp.ClientSession") as mock_session_class:
|
|
553
|
-
mock_session = AsyncMock()
|
|
554
|
-
mock_session.__aenter__.return_value = mock_session
|
|
555
|
-
mock_session.__aexit__.return_value = AsyncMock()
|
|
556
|
-
|
|
557
|
-
# Create a proper async context manager for the request
|
|
558
|
-
mock_request_ctx = AsyncMock()
|
|
559
|
-
mock_request_ctx.__aenter__.return_value = mock_response
|
|
560
|
-
mock_request_ctx.__aexit__.return_value = AsyncMock()
|
|
561
|
-
mock_session.request.return_value = mock_request_ctx
|
|
562
|
-
|
|
563
|
-
mock_session_class.return_value = mock_session
|
|
564
|
-
|
|
565
|
-
# Just verify the request completes without error
|
|
566
|
-
# Note: The response object can't be tested directly since it's used within a context manager
|
|
567
|
-
mock_session.request.assert_not_called() # Before call
|
|
568
|
-
await device._http_request("GET", "http://test.com")
|
|
569
|
-
mock_session.request.assert_called_once_with("GET", "http://test.com")
|
|
570
|
-
mock_response.raise_for_status.assert_called_once()
|
|
571
|
-
|
|
572
539
|
|
|
573
540
|
class TestPollingDevice:
|
|
574
541
|
"""Tests for PollingDevice."""
|
|
@@ -1001,32 +968,6 @@ class TestPersistentConnectionDevice:
|
|
|
1001
968
|
|
|
1002
969
|
assert device.connection_closed is True
|
|
1003
970
|
|
|
1004
|
-
@pytest.mark.asyncio
|
|
1005
|
-
@pytest.mark.skip(reason="Timing-sensitive test - reconnection backoff varies")
|
|
1006
|
-
async def test_reconnection_with_backoff(self, mock_device_config, event_loop):
|
|
1007
|
-
"""Test reconnection with exponential backoff."""
|
|
1008
|
-
|
|
1009
|
-
class FailingDevice(ConcretePersistentConnectionDevice):
|
|
1010
|
-
def __init__(self, *args, **kwargs):
|
|
1011
|
-
super().__init__(*args, **kwargs)
|
|
1012
|
-
self.connection_attempts = 0
|
|
1013
|
-
|
|
1014
|
-
async def establish_connection(self):
|
|
1015
|
-
self.connection_attempts += 1
|
|
1016
|
-
if self.connection_attempts < 3:
|
|
1017
|
-
raise Exception("Connection failed")
|
|
1018
|
-
return await super().establish_connection()
|
|
1019
|
-
|
|
1020
|
-
device = FailingDevice(mock_device_config, loop=event_loop, backoff_max=1)
|
|
1021
|
-
|
|
1022
|
-
await device.connect()
|
|
1023
|
-
await asyncio.sleep(1.5) # Wait for reconnection attempts with backoff
|
|
1024
|
-
|
|
1025
|
-
# Should have made multiple connection attempts
|
|
1026
|
-
assert device.connection_attempts >= 2
|
|
1027
|
-
|
|
1028
|
-
await device.disconnect()
|
|
1029
|
-
|
|
1030
971
|
@pytest.mark.asyncio
|
|
1031
972
|
async def test_maintain_connection_called(self, mock_device_config, event_loop):
|
|
1032
973
|
"""Test that maintain_connection is called after connection."""
|
|
@@ -1455,12 +1396,12 @@ class TestDeviceEvents:
|
|
|
1455
1396
|
|
|
1456
1397
|
def test_device_events_values(self):
|
|
1457
1398
|
"""Test that DeviceEvents enum has expected values."""
|
|
1458
|
-
assert DeviceEvents.CONNECTING ==
|
|
1459
|
-
assert DeviceEvents.CONNECTED ==
|
|
1460
|
-
assert DeviceEvents.DISCONNECTED ==
|
|
1461
|
-
assert DeviceEvents.PAIRED ==
|
|
1462
|
-
assert DeviceEvents.ERROR ==
|
|
1463
|
-
assert DeviceEvents.UPDATE ==
|
|
1399
|
+
assert DeviceEvents.CONNECTING == "DEVICE_CONNECTING"
|
|
1400
|
+
assert DeviceEvents.CONNECTED == "DEVICE_CONNECTED"
|
|
1401
|
+
assert DeviceEvents.DISCONNECTED == "DEVICE_DISCONNECTED"
|
|
1402
|
+
assert DeviceEvents.PAIRED == "DEVICE_PAIRED"
|
|
1403
|
+
assert DeviceEvents.ERROR == "DEVICE_ERROR"
|
|
1404
|
+
assert DeviceEvents.UPDATE == "DEVICE_UPDATE"
|
|
1464
1405
|
|
|
1465
1406
|
|
|
1466
1407
|
class ConcreteExternalClientDevice(ExternalClientDevice):
|