33## Task
44
55Nested properties on generated resource dataclasses have ** no types, no docs, and
6- no autocomplete** , and the declared annotation * contradicts * the runtime object.
6+ no autocomplete** , and the declared annotation _ contradicts _ the runtime object.
77Fix this by generating a dataclass per nested object, so nested properties get
88real types, real docstrings, and completion — and so the annotation matches what
99` from_dict ` actually assigns.
@@ -30,26 +30,26 @@ properties=DeepAttrDict(d.get("properties", None)),
3030
3131Consequences:
3232
33- | Expression | Runtime | Type checker |
34- | ---| ---| ---|
35- | ` device.properties.locked ` | works | ** error** — ` Dict[str, Any] ` has no attribute ` locked ` |
36- | ` device.properties["locked"] ` | works | passes, returns ` Any ` — no completion, no docs |
33+ | Expression | Runtime | Type checker |
34+ | ----------------------------- | ------- | ------------------------------------------------------ |
35+ | ` device.properties.locked ` | works | ** error** — ` Dict[str, Any] ` has no attribute ` locked ` |
36+ | ` device.properties["locked"] ` | works | passes, returns ` Any ` — no completion, no docs |
3737
3838So the access pattern the SDK is designed for does not type-check, and the one
3939that type-checks tells you nothing.
4040
4141** Docs are top-level only.** ` codegen/layouts/partials/resource-dataclass.hbs `
4242iterates ` properties ` exactly once and emits a flat ` :ivar ` list, so nested
4343shapes are undocumented. The only mention of any nested field name in all of
44- ` device.py ` is incidental prose inside a * different * field's docstring:
44+ ` device.py ` is incidental prose inside a _ different _ field's docstring:
4545
4646```
4747:ivar display_name: Display name of the device, defaults to nickname (if it is
4848 set) or ``properties.appearance.name``, otherwise.
4949```
5050
5151** ` errors ` /` warnings ` are worse than nested objects.** They are
52- ` List[Dict[str, Any]] ` and, unlike ` properties ` , are * not * wrapped at all
52+ ` List[Dict[str, Any]] ` and, unlike ` properties ` , are _ not _ wrapped at all
5353(` errors=d.get("errors", None) ` , line 169). So ` device.errors[0].error_code `
5454raises ` AttributeError ` — subscript access is mandatory. Compare Ruby, which
5555coerces these into real ` ResourceError ` objects where ` .error_code ` works.
@@ -157,8 +157,8 @@ That keeps both access styles working. Only `dict(...)` and
157157narrowing.
158158
159159** One intentional behavior change to call out in the changelog:** today
160- ` device.properties.typo ` silently returns an empty ` DeepAttrDict ` * and mutates the
161- object * , inserting the typo'd path (see ` seam/utils/deep_attr_dict.py:19-25 ` ).
160+ ` device.properties.typo ` silently returns an empty ` DeepAttrDict ` _ and mutates the
161+ object _ , inserting the typo'd path (see ` seam/utils/deep_attr_dict.py:19-25 ` ).
162162With dataclasses it raises ` AttributeError ` . That is strictly better — it matches
163163Ruby, and it turns silent typos into errors — but it is a behavior change and
164164someone may be relying on the falsy-empty-dict result.
0 commit comments