Repository navigation
feat: add update_attributes() to merge user attributes - #139
vazarkevych wants to merge 1 commit into
Conversation
|
| this does not replace the whole map. Passing None (or an empty dict) is | ||
| a no-op. Sticky buckets and, in remote-eval mode, the feature refetch | ||
| are handled by delegating to set_attributes.""" | ||
| self.set_attributes({**self._attributes, **(attributes or {})}) |
There was a problem hiding this comment.
None and {} still call set_attributes. In remote-eval mode, an expired cache makes this no-change call send a blocking HTTP request. Return before calling set_attributes when attributes is empty.
| self.set_attributes({**self._attributes, **(attributes or {})}) | |
| if not attributes: | |
| return | |
| self.set_attributes({**self._attributes, **attributes}) |
Prompt To Fix With AI
This is a comment left during a code review.
Path: growthbook/growthbook.py
Line: 1144
Comment:
`None` and `{}` still call `set_attributes`. In remote-eval mode, an expired cache makes this no-change call send a blocking HTTP request. Return before calling `set_attributes` when `attributes` is empty.
```suggestion
if not attributes:
return
self.set_attributes({**self._attributes, **attributes})
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.325be99 to
acf3ce0
Compare
|
confirmed and fixed. Reproduced it on the PR code: two no-op
The existing Also rebased onto current main — the branch was 127 commits behind. |
Shallow-merges into the existing attributes (parity with the JS SDK's updateAttributes): new keys are added, existing keys overwritten, untouched keys preserved. Unlike set_attributes it does not replace the whole map. A None value is stored rather than treated as key removal. Sticky bucket refresh and, in remote-eval mode, the feature refetch come from delegating to set_attributes — except for an empty update, which returns first. Merging nothing produces an equal map, but delegating would still refresh sticky buckets and issue a blocking refetch, so a no-change call cost an HTTP request whenever the cache had expired.
acf3ce0 to
c645d9c
Compare
|
Maintainer decision needed: confirm the new synchronous update_attributes API and its contract: shallow merge, supplied values overwrite existing keys, None values remain values, and None or an empty dictionary does nothing. Nonempty updates use set_attributes and retain its sticky-bucket and remote-evaluation effects. Please record acceptance so API and regression review can proceed; align the empty-input behavior with growthbook-ruby#41. |
|
Recording the contract:
On aligning empty-input with growthbook-ruby#41: the observable behavior Ruby's Happy to drop the early return and match Ruby literally if you'd rather have Tests assert on the side effects (no refresh, no refetch) rather than the |
Summary
Adds
GrowthBook.update_attributes(), which shallow-merges the given attributesinto the existing ones instead of replacing them — parity with the TS SDK's
updateAttributes().Previously the only way to change attributes was
set_attributes(), which fullyreplaces the map. Adding a single attribute meant rebuilding the whole dict, a
frequent source of bugs where previously set attributes get silently lost.
What Changed
update_attributes(attributes)to the syncGrowthBookclass:shallow merge — new keys added, existing overwritten, untouched preserved.
set_attributes(), so sticky-bucket refresh and remote-evalcache invalidation (refetch) are reused rather than duplicated.
Noneis a no-op; aNonevalue is stored, not treated as key removal(parity with the Ruby and C# internal SDKs).
set_attributes) vs merge(
update_attributes).Scope
Only the sync
GrowthBookclient is affected. The asyncGrowthBookClientpasses attributes per-request via
UserContextand has no persistent globalattribute state to merge into, so no change applies there.
Test plan
test_update_attributes_merges_into_existing— new keys are addedtest_update_attributes_overwrites_and_preserves— existing overwritten, others kepttest_update_attributes_merges_not_replaces— merge, unlikeset_attributestest_update_attributes_none_is_noop—Noneargument changes nothingtest_update_attributes_stores_none_value— aNonevalue is stored, not removedAll 790 tests pass.