3232minLimitAttrKeyCache = {}
3333maxLimitAttrKeyCache = {}
3434resistanceCache = {}
35+ resAttrNameCache = {}
3536
3637
3738def getAttrDefault (key , fallback = None ):
@@ -48,6 +49,19 @@ def getAttrDefault(key, fallback=None):
4849 return default
4950
5051
52+ def getAttrNameByID (attrID ):
53+ # Resolve a resistance attribute ID to its name. Attribute metadata is
54+ # immutable gamedata, so memoize it: gamedata query caching is disabled
55+ # globally, and this runs in the hot getExtended() loop used by the graph
56+ # projected-effect calculations (see minLimitAttrKeyCache above).
57+ try :
58+ return resAttrNameCache [attrID ]
59+ except KeyError :
60+ attrInfo = getAttributeInfo (attrID )
61+ name = resAttrNameCache [attrID ] = None if attrInfo is None else attrInfo .attributeName
62+ return name
63+
64+
5165def getResistanceAttrID (modifyingItem , effect ):
5266 # If it doesn't exist on the effect, check the modifying module's attributes.
5367 # If it's there, cache it and return
@@ -314,16 +328,16 @@ def __calculateValue(self, key, extraMultipliers=None, preIncAdj=None, multAdj=N
314328 minLimitKey = minLimitAttrKeyCache [key ]
315329 except KeyError :
316330 attrInfo = getAttributeInfo (key )
317- if attrInfo is None :
318- cappingId = minLimitAttrKeyCache [key ] = None
319- else :
320- cappingId = attrInfo .minAttributeID
331+ cappingId = None if attrInfo is None else attrInfo .minAttributeID
321332 if cappingId is None :
322333 minLimitKey = None
323334 else :
324335 cappingAttrInfo = getAttributeInfo (cappingId )
325336 minLimitKey = None if cappingAttrInfo is None else cappingAttrInfo .name
326- minLimitAttrKeyCache [key ] = minLimitKey
337+ # Always memoize the result (including None): attribute metadata is
338+ # immutable, and getExtended() recomputes this in a hot loop. Missing
339+ # the None case here re-queried the DB on every extended attr lookup.
340+ minLimitAttrKeyCache [key ] = minLimitKey
327341 if minLimitKey :
328342 minLimitValue = self [minLimitKey ]
329343 minLimitValue = minLimitValue .value if hasattr (minLimitValue , "value" ) else minLimitValue
@@ -334,16 +348,13 @@ def __calculateValue(self, key, extraMultipliers=None, preIncAdj=None, multAdj=N
334348 maxLimitKey = maxLimitAttrKeyCache [key ]
335349 except KeyError :
336350 attrInfo = getAttributeInfo (key )
337- if attrInfo is None :
338- cappingId = maxLimitAttrKeyCache [key ] = None
339- else :
340- cappingId = attrInfo .maxAttributeID
351+ cappingId = None if attrInfo is None else attrInfo .maxAttributeID
341352 if cappingId is None :
342353 maxLimitKey = None
343354 else :
344355 cappingAttrInfo = getAttributeInfo (cappingId )
345356 maxLimitKey = None if cappingAttrInfo is None else cappingAttrInfo .name
346- maxLimitAttrKeyCache [key ] = maxLimitKey
357+ maxLimitAttrKeyCache [key ] = maxLimitKey
347358
348359 if maxLimitKey :
349360 maxLimitValue = self [maxLimitKey ]
@@ -375,11 +386,11 @@ def __calculateValue(self, key, extraMultipliers=None, preIncAdj=None, multAdj=N
375386 if not resAttrID :
376387 multipliers .append (mult )
377388 continue
378- resAttrInfo = getAttributeInfo (resAttrID )
379- if not resAttrInfo :
389+ resAttrName = getAttrNameByID (resAttrID )
390+ if not resAttrName :
380391 multipliers .append (mult )
381392 continue
382- resMult = self .fit .ship .itemModifiedAttributes [resAttrInfo . attributeName ]
393+ resMult = self .fit .ship .itemModifiedAttributes [resAttrName ]
383394 if resMult is None or resMult == 1 :
384395 multipliers .append (mult )
385396 continue
0 commit comments