Cached properties can belong to a replaced owner
Current public source; version-sensitive. The client's
cached_property stores the first
successful getter result on its instance. It does not subscribe to the getter's inputs.
Reading the property again can return the same mutable list or set even after those inputs
change. Check who replaces or invalidates the owning object before treating a property as a
live query.
In the current conversion-rule service,
getConfig() returns the actual configuration object. The controller refreshes
cached properties by constructing another configuration and rebuilding its derived vehicle
cache. A retained old configuration or collection does not adopt that replacement. Reacquire
the current owner after the relevant service update and copy collections for mod-owned work.
Illustrative Python 2.7 example: called with the current, ready service, this takes collection snapshots without changing its cached lists or sets:
def read_rule_snapshot(controller):
config = controller.getConfig()
if config is None:
return None
return (tuple(config.tradeInInfoList),
frozenset(config.allAccessTokenSet))
The helper calls getConfig() once and returns None only for
that literal result. It builds the tuple first, then the frozenset. Their cached source
collections can have different first-read times; this is no lock or revision check.
Empty collections still return a pair and do not establish readiness. Entries remain
borrowed, and attribute, iteration or hashability failures propagate. The caller owns
service readiness, current context and refresh; this helper supplies no subscription,
validation, persistence or receipt.
These copies retain their entries; they are not a deep copy or an automatically refreshed subscription. The service's token handler also refreshes conditionally: it first requires an enabled controller, then an intersection with its configured access-token keys. Other settings and vehicle-sync paths have their own gates. Do not assume every update replaces the owner or that token arrival alone recovers a disabled service.
The rule-list filter tests token-key membership, whereas the
token requester's
isTokenAvailable() separately tests positive count and future expiry.
Cached membership is therefore insufficient evidence for that availability predicate or a
completed purchase. Source inspection establishes these ownership and predicate boundaries;
live update ordering, token changes and purchases were not exercised.
Confirmed snapshots for temporarily unavailable caches
Some client caches are valid only after a lobby synchronization event and may be unavailable after entering battle. When a feature needs one small value across that transition, persist a confirmed snapshot instead of copying the whole cache.
- Read the value only after the normal cache reports that it is ready.
- Validate that the record is meaningfully populated, not merely present.
- Store only the stable key, required fields, account identity, and a schema version.
- Choose precedence explicitly: a live-first policy uses the snapshot when the live source is unavailable; the saved-first illustration below uses a different policy.
- Replace it only with another confirmed value; an empty placeholder must not erase it.
Illustrative Python 2.7 example; saved-first precedence.
is_confirmed, is_populated and normalize are
unspecified helpers. A truthy first predicate returns the original saved object without
inspecting the native record. Only a false-like first result reaches the second predicate.
A truthy second result forwards the normalizer's return unchecked; otherwise the result
is None. Exceptions propagate without fallback.
def choose_baseline(saved, native_record):
if is_confirmed(saved):
return saved
if is_populated(native_record):
return normalize(native_record)
return None # Show loading or unavailable; do not invent zero.
This body does not implement the full checklist: it supplies no confirmation criteria,
schema, account identity, timestamp, freshness check, copy or storage. A confirmed saved
value can win over a newer populated native value. Normalized ownership is helper-defined,
and returning None does not itself render loading or unavailable UI.
Keep this pattern narrow. A snapshot is not a second implementation of the client cache and should not be used to bypass server authority.
A copied raw value can describe a different revision
Copy isolation and source ownership are separate contracts. In CollectionsConfig, construction stores raw keyword data on the class while parsed fields belong to each tuple instance. getRawData() deep-copies that class field. Mutating the returned tree is isolated, but constructing another instance changes what older instances' raw getters read without replacing their parsed fields. The constructor input itself is not frozen by the later copy.
replace() builds a parsed tuple replacement without refreshing that raw field. The server-settings wrapper uses this replacement path; a controller consumer rebuilds a derived ID lookup through the raw getter. Selected-class controls changed a parsed ID while that raw-fed lookup retained the earlier ID. Do not treat the raw copy as serialization of the receiving tuple's current fields or as proof of per-instance provenance.
Choose the representation and owner before making a defensive copy. Controls used readable classes and synthetic configuration under Python 3. Separate static inspection of CPython 2.7.18's namedtuple template confirmed that replacement routes through _make and tuple.__new__, bypassing the subclass constructor. This is not installed-client, server-diff, event-dispatch or native-runtime verification, and establishes no observed live lookup defect.
A getter can establish an acknowledgement baseline
Check whether an observation helper writes history before using it for passive inspection. After account/vehicle validation, getResearchInfo() invokes its mark-and-save path when no per-vehicle viewed entry exists. For an eligible non-elite vehicle, this seeds the calculated viewed baseline before returning. getVehicleResearchInfo() reads the projection without invoking that viewed-history write path. An empty saved set still counts as an existing entry; later calls to the first getter do not automatically acknowledge newly available items.
The hangar menu uses the first getter for its counter, while the modules presenter explicitly calls updateViewedItems() after filling its models. Keep passive observation and intentional acknowledgement separate in an owned feature. The recorded set is this helper's calculated history, not proof that the user saw every item, that a badge disappeared or that bytes were flushed to disk. Explicit updating can also clear an elite vehicle's existing history; reading through getResearchInfo() does not clear an already-saved elite entry.
Selected readable info/getter/mark methods with an inert projection producer and history store confirmed first-read seeding, later-read retention, explicit marking and the elite read/clear distinction. Actual availability calculation, preferences, GUI events, rendering, server research and native/client APIs were not exercised.