Skip to content

Commit f67309e

Browse files
dcjclaude
andcommitted
Simplify find_program_by_name now that openadr3 0.2.0 returns coerced models
Upstream python-oa3 POA3-o0j landed: find_program_by_name and find_ven_by_name now return coerced Pydantic models instead of raw dicts. Remove the explicit coerce() workaround in VenClient, fix register() to use attribute access (.id) instead of dict access (["id"]), and bump the openadr3 dependency to >=0.2.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 74dc81d commit f67309e

3 files changed

Lines changed: 21 additions & 34 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ classifiers = [
2525
"Topic :: Software Development :: Libraries",
2626
]
2727
dependencies = [
28-
"openadr3>=0.1.0",
28+
"openadr3>=0.2.0",
2929
]
3030

3131
[project.optional-dependencies]

src/openadr3_client/ven.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import httpx
1010
from openadr3.api import success
11-
from openadr3.entities import coerce
1211
from openadr3.entities.models import Event, Program
1312

1413
from openadr3_client.base import BaseClient
@@ -82,7 +81,7 @@ def register(self, ven_name: str) -> VenClient:
8281
with self._lock:
8382
existing = self.api.find_ven_by_name(ven_name)
8483
if existing:
85-
vid = existing["id"]
84+
vid = existing.id
8685
log.info("VEN found, reusing: name=%s id=%s", ven_name, vid)
8786
else:
8887
resp = self.api.create_ven(
@@ -103,20 +102,9 @@ def register(self, ven_name: str) -> VenClient:
103102
# -- Program lookup --
104103

105104
def find_program_by_name(self, name: str) -> Program | None:
106-
"""Query VTN for a program by programName. Caches the ID on success.
107-
108-
Returns a coerced Program model (or None), matching the shape of
109-
programs() and poll_events() which also return coerced models.
110-
"""
111-
raw = self.api.find_program_by_name(name)
112-
if not raw:
113-
return None
114-
program = coerce(raw)
115-
if not isinstance(program, Program):
116-
raise TypeError(
117-
f"Expected Program from find_program_by_name, got {type(program).__name__}"
118-
)
119-
if program.id:
105+
"""Query VTN for a program by programName. Caches the ID on success."""
106+
program = self.api.find_program_by_name(name)
107+
if program and program.id:
120108
self._program_cache[name] = program.id
121109
return program
122110

tests/test_ven.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import httpx
66
import pytest
7+
from openadr3.entities import coerce
78
from openadr3.entities.models import Program
89

910
from openadr3_client.ven import VenClient, extract_topics
@@ -41,7 +42,9 @@ class TestVenClientRegistration:
4142
@patch("openadr3_client.base.create_ven_client")
4243
def test_register_existing_ven(self, mock_create):
4344
mock_api = MagicMock()
44-
mock_api.find_ven_by_name.return_value = {"id": "ven-123", "venName": "my-ven"}
45+
mock_api.find_ven_by_name.return_value = coerce(
46+
{"objectType": "VEN", "id": "ven-123", "venName": "my-ven"}
47+
)
4548
mock_create.return_value = mock_api
4649

4750
with VenClient(url="http://test", token="tok") as ven:
@@ -80,11 +83,9 @@ class TestVenClientProgramLookup:
8083
@patch("openadr3_client.base.create_ven_client")
8184
def test_find_program_by_name(self, mock_create):
8285
mock_api = MagicMock()
83-
mock_api.find_program_by_name.return_value = {
84-
"objectType": "PROGRAM",
85-
"id": "prog-1",
86-
"programName": "pricing",
87-
}
86+
mock_api.find_program_by_name.return_value = coerce(
87+
{"objectType": "PROGRAM", "id": "prog-1", "programName": "pricing"}
88+
)
8889
mock_create.return_value = mock_api
8990

9091
with VenClient(url="http://test", token="tok") as ven:
@@ -119,11 +120,9 @@ def test_resolve_program_id_cached(self, mock_create):
119120
@patch("openadr3_client.base.create_ven_client")
120121
def test_resolve_program_id_queries(self, mock_create):
121122
mock_api = MagicMock()
122-
mock_api.find_program_by_name.return_value = {
123-
"objectType": "PROGRAM",
124-
"id": "prog-2",
125-
"programName": "dr-program",
126-
}
123+
mock_api.find_program_by_name.return_value = coerce(
124+
{"objectType": "PROGRAM", "id": "prog-2", "programName": "dr-program"}
125+
)
127126
mock_create.return_value = mock_api
128127

129128
with VenClient(url="http://test", token="tok") as ven:
@@ -202,7 +201,9 @@ class TestVenClientMqttTopics:
202201
@patch("openadr3_client.base.create_ven_client")
203202
def test_ven_scoped_defaults_to_registered(self, mock_create):
204203
mock_api = MagicMock()
205-
mock_api.find_ven_by_name.return_value = {"id": "ven-99", "venName": "v"}
204+
mock_api.find_ven_by_name.return_value = coerce(
205+
{"objectType": "VEN", "id": "ven-99", "venName": "v"}
206+
)
206207
mock_create.return_value = mock_api
207208

208209
with VenClient(url="http://test", token="tok") as ven:
@@ -251,11 +252,9 @@ class TestVenClientSubscribe:
251252
@patch("openadr3_client.base.create_ven_client")
252253
def test_subscribe_mqtt(self, mock_create):
253254
mock_api = MagicMock()
254-
mock_api.find_program_by_name.return_value = {
255-
"objectType": "PROGRAM",
256-
"id": "prog-1",
257-
"programName": "pricing",
258-
}
255+
mock_api.find_program_by_name.return_value = coerce(
256+
{"objectType": "PROGRAM", "id": "prog-1", "programName": "pricing"}
257+
)
259258
mock_api.get_mqtt_topics_program_events.return_value = _make_response(
260259
200, {"topics": {"a": "openadr3/programs/prog-1/events"}}
261260
)

0 commit comments

Comments
 (0)