Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions source/baseObject.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,11 @@ class AutoPropertyObject(with_metaclass(AutoPropertyType, object)):
"""

#: Tracks the instances of this class; used by L{invalidateCaches}.
#: @type: weakref.WeakKeyDictionary
__instances=weakref.WeakKeyDictionary()
#: @type: weakref.WeakValueDictionary
# We really just want to store the instances as a set of weak references,
# but as there is no easy way to have NVDAObjects and TextInfos remain hashable in Python3, as they override equality,
# We store them on a WeakValueDictionary, keyed by their id (address).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment suggest that we could theoretically use a WeakSet, but that's not going to work as in sets, only hashable types are allowed. Doesn't that mean we theoretically can not use a set?

__instances=weakref.WeakValueDictionary()
#: Specifies whether properties are cached by default;
#: can be overridden for individual properties by setting _cache_propertyName.
#: @type: bool
Expand All @@ -135,7 +138,7 @@ def __new__(cls, *args, **kwargs):
#: Maps properties to cached values.
#: @type: dict
self._propertyCache={}
self.__instances[self]=None
self.__instances[id(self)]=self
return self

def _getPropertyViaCache(self,getterMethod=None):
Expand All @@ -158,7 +161,7 @@ def invalidateCaches(cls):
# We use keys() here instead of iterkeys(), as invalidating the cache on an object may cause instances to disappear,
# which would in turn cause an exception due to the dictionary changing size during iteration.
# #9067 (Py3 review required): because of this, wrap this in a list, as dict.keys() in Python 3 returns iterators.
for instance in list(cls.__instances.keys()):
for instance in list(cls.__instances.values()):
instance.invalidateCache()

class ScriptableType(AutoPropertyType):
Expand Down
26 changes: 14 additions & 12 deletions source/eventHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@

#Some dicts to store event counts by name and or obj
_pendingEventCountsByName={}
_pendingEventCountsByObj={}
_pendingEventCountsByNameAndObj={}
# Note that due to the fact it is hard to keep NVDAObjects remaining hashable in Python3 due to their equality method being overridden,
# the id (address) of the NVDAObject will be used as the key in the dict, not the NVDAObject itself.
_pendingEventCountsByObjID={}
_pendingEventCountsByNameAndObjID={}
# Needed to ensure updates are atomic, as these might be updated from multiple threads simultaneously.
_pendingEventCountsLock=threading.RLock()

Expand All @@ -38,8 +40,8 @@ def queueEvent(eventName,obj,**kwargs):
lastQueuedFocusObject=obj
with _pendingEventCountsLock:
_pendingEventCountsByName[eventName]=_pendingEventCountsByName.get(eventName,0)+1
_pendingEventCountsByObj[obj]=_pendingEventCountsByObj.get(obj,0)+1
_pendingEventCountsByNameAndObj[(eventName,obj)]=_pendingEventCountsByNameAndObj.get((eventName,obj),0)+1
_pendingEventCountsByObjID[id(obj)]=_pendingEventCountsByObjID.get(id(obj),0)+1
_pendingEventCountsByNameAndObjID[(eventName,id(obj))]=_pendingEventCountsByNameAndObjID.get((eventName,id(obj)),0)+1
queueHandler.queueFunction(queueHandler.eventQueue,_queueEventCallback,eventName,obj,kwargs)

def _queueEventCallback(eventName,obj,kwargs):
Expand All @@ -49,16 +51,16 @@ def _queueEventCallback(eventName,obj,kwargs):
_pendingEventCountsByName[eventName]=(curCount-1)
elif curCount==1:
del _pendingEventCountsByName[eventName]
curCount=_pendingEventCountsByObj.get(obj,0)
curCount=_pendingEventCountsByObjID.get(id(obj),0)
if curCount>1:
_pendingEventCountsByObj[obj]=(curCount-1)
_pendingEventCountsByObjID[id(obj)]=(curCount-1)
elif curCount==1:
del _pendingEventCountsByObj[obj]
curCount=_pendingEventCountsByNameAndObj.get((eventName,obj),0)
del _pendingEventCountsByObjID[id(obj)]
curCount=_pendingEventCountsByNameAndObjID.get((eventName,id(obj)),0)
if curCount>1:
_pendingEventCountsByNameAndObj[(eventName,obj)]=(curCount-1)
_pendingEventCountsByNameAndObjID[(eventName,id(obj))]=(curCount-1)
elif curCount==1:
del _pendingEventCountsByNameAndObj[(eventName,obj)]
del _pendingEventCountsByNameAndObjID[(eventName,id(obj))]
executeEvent(eventName,obj,**kwargs)

def isPendingEvents(eventName=None,obj=None):
Expand All @@ -73,11 +75,11 @@ def isPendingEvents(eventName=None,obj=None):
if not eventName and not obj:
return bool(len(_pendingEventCountsByName))
elif not eventName and obj:
return obj in _pendingEventCountsByObj
return id(obj) in _pendingEventCountsByObjID
elif eventName and not obj:
return eventName in _pendingEventCountsByName
elif eventName and obj:
return (eventName,obj) in _pendingEventCountsByNameAndObj
return (eventName,id(obj)) in _pendingEventCountsByNameAndObjID

class _EventExecuter(object):
"""Facilitates execution of a chain of event functions.
Expand Down