Repository navigation
Fixes #812: Prevent field type markers from shadowing missing fields - #818
Conversation
|
@claude /review Only report on issues of potential concern; we don't need commentary on anything that doesn't require further consideration. |
|
Claude finished @jnovinger's task in 6m 18s —— View job Review
|
This matches the three
The old probe did not have this bug. After hydration,
The #808 tests in this file use the same mock pattern, and a second fetch still fails the test. I am keeping it consistent with those.
Split into two bullets in ddfb71a. |
Record subclasses declare field type markers as class attributes (e.g. Devices.device_type = DeviceTypes). When a payload omitted the field, normal attribute lookup found the class attribute, so the marker class came back as the value and Record.__getattr__ never ran to lazy-load it. Record.__init_subclass__ now wraps each marker in a non-data descriptor. Class access still returns the marked type, so _parse_values() is unchanged. Instance access raises AttributeError, so an omitted field lazy-loads like any other and raises if the server omits it too. The descriptor has no __set__, so values that _parse_values() assigns on the instance take precedence over it. The post-hydration probe in __getattr__ now uses object.__getattribute__ instead of getattr()/hasattr(). It no longer re-enters __getattr__, and it still returns falsy values and re-runs properties.
ddfb71a to
00ac00e
Compare
Fixes: #812
Field type markers (
Devices.device_type = DeviceTypes,Permissions.users = [Users],Devices.config_context = JsonField) are plain class attributes. When a payload omits one of those fields, normal attribute lookup finds the class attribute, sodevice.device_typereturns theDeviceTypesclass andRecord.__getattr__never runs to lazy-load it. The post-hydration probe in__getattr__that the issue points at is never reached for these keys.Record.__init_subclass__now wraps each marker in a non-data descriptor. Class access still returns the marked type, so_parse_values()and any external code readingDevices.device_typesee no change. Instance access raisesAttributeError, so an omitted field lazy-loads like any other field and raises if the server omits it too.The probe also changes from
getattr()/hasattr()toobject.__getattribute__. It no longer re-enters__getattr__twice for a key that is still missing after hydration, and it still returns falsy values and re-runs properties.Behavior changes for the release notes:
AttributeError) instead of returning the class.hasattr(record, "device_type")on an unhydrated record fetches like any other missing field.dir()driven introspection (inspect.getmembers, debugger panes) can trigger the same fetch.record.field is SomeClassto detect absence needshasattrortry/except AttributeErrorinstead.Recordsubclass, aJsonFieldstyle class, or[RecordSubclass]is treated as a marker. Plugin code that keeps a non-field reference like that should read it throughtype(self).docs/extensions.mdnow says so.Devices.has_details = TrueandInterfaces.interface_connectionare dead:__init__always overrideshas_details, and no NetBox serializer emitsinterface_connection. I left both in place because removing them saves no requests and changes class-level surface. I can remove them in a follow-up, but I am leaning against it.This touches
pynetbox/core/response.pyalongside #794, so whichever lands second will need a rebase.