Show More
Commit Description:
Merge pull request #17 from nattee/master...
Commit Description:
Merge pull request #17 from nattee/master
upgrade to current working snapshot
References:
File last commit:
Show/Diff file:
Action:
lib/assets/Lib/weakref.py
| 385 lines
| 11.6 KiB
| text/x-python
| PythonLexer
|
r584 | """Weak reference support for Python. | |||
This module is an implementation of PEP 205: | ||||
http://www.python.org/dev/peps/pep-0205/ | ||||
""" | ||||
# Naming convention: Variables named "wr" are weak reference objects; | ||||
# they are called this instead of "ref" to avoid name collisions with | ||||
# the module-global ref() function imported from _weakref. | ||||
from _weakref import ( | ||||
getweakrefcount, | ||||
getweakrefs, | ||||
ref, | ||||
proxy, | ||||
CallableProxyType, | ||||
ProxyType, | ||||
ReferenceType) | ||||
from _weakrefset import WeakSet, _IterationGuard | ||||
import collections # Import after _weakref to avoid circular import. | ||||
ProxyTypes = (ProxyType, CallableProxyType) | ||||
__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs", | ||||
"WeakKeyDictionary", "ReferenceType", "ProxyType", | ||||
"CallableProxyType", "ProxyTypes", "WeakValueDictionary", | ||||
"WeakSet"] | ||||
class WeakValueDictionary(collections.MutableMapping): | ||||
"""Mapping class that references values weakly. | ||||
Entries in the dictionary will be discarded when no strong | ||||
reference to the value exists anymore | ||||
""" | ||||
# We inherit the constructor without worrying about the input | ||||
# dictionary; since it uses our .update() method, we get the right | ||||
# checks (if the other dictionary is a WeakValueDictionary, | ||||
# objects are unwrapped on the way out, and we always wrap on the | ||||
# way in). | ||||
def __init__(self, *args, **kw): | ||||
def remove(wr, selfref=ref(self)): | ||||
self = selfref() | ||||
if self is not None: | ||||
if self._iterating: | ||||
self._pending_removals.append(wr.key) | ||||
else: | ||||
del self.data[wr.key] | ||||
self._remove = remove | ||||
# A list of keys to be removed | ||||
self._pending_removals = [] | ||||
self._iterating = set() | ||||
self.data = d = {} | ||||
self.update(*args, **kw) | ||||
def _commit_removals(self): | ||||
l = self._pending_removals | ||||
d = self.data | ||||
# We shouldn't encounter any KeyError, because this method should | ||||
# always be called *before* mutating the dict. | ||||
while l: | ||||
del d[l.pop()] | ||||
def __getitem__(self, key): | ||||
o = self.data[key]() | ||||
if o is None: | ||||
raise KeyError(key) | ||||
else: | ||||
return o | ||||
def __delitem__(self, key): | ||||
if self._pending_removals: | ||||
self._commit_removals() | ||||
del self.data[key] | ||||
def __len__(self): | ||||
return len(self.data) - len(self._pending_removals) | ||||
def __contains__(self, key): | ||||
try: | ||||
o = self.data[key]() | ||||
except KeyError: | ||||
return False | ||||
return o is not None | ||||
def __repr__(self): | ||||
return "<WeakValueDictionary at %s>" % id(self) | ||||
def __setitem__(self, key, value): | ||||
if self._pending_removals: | ||||
self._commit_removals() | ||||
self.data[key] = KeyedRef(value, self._remove, key) | ||||
def copy(self): | ||||
new = WeakValueDictionary() | ||||
for key, wr in self.data.items(): | ||||
o = wr() | ||||
if o is not None: | ||||
new[key] = o | ||||
return new | ||||
__copy__ = copy | ||||
def __deepcopy__(self, memo): | ||||
from copy import deepcopy | ||||
new = self.__class__() | ||||
for key, wr in self.data.items(): | ||||
o = wr() | ||||
if o is not None: | ||||
new[deepcopy(key, memo)] = o | ||||
return new | ||||
def get(self, key, default=None): | ||||
try: | ||||
wr = self.data[key] | ||||
except KeyError: | ||||
return default | ||||
else: | ||||
o = wr() | ||||
if o is None: | ||||
# This should only happen | ||||
return default | ||||
else: | ||||
return o | ||||
def items(self): | ||||
with _IterationGuard(self): | ||||
for k, wr in self.data.items(): | ||||
v = wr() | ||||
if v is not None: | ||||
yield k, v | ||||
def keys(self): | ||||
with _IterationGuard(self): | ||||
for k, wr in self.data.items(): | ||||
if wr() is not None: | ||||
yield k | ||||
__iter__ = keys | ||||
def itervaluerefs(self): | ||||
"""Return an iterator that yields the weak references to the values. | ||||
The references are not guaranteed to be 'live' at the time | ||||
they are used, so the result of calling the references needs | ||||
to be checked before being used. This can be used to avoid | ||||
creating references that will cause the garbage collector to | ||||
keep the values around longer than needed. | ||||
""" | ||||
with _IterationGuard(self): | ||||
for wr in self.data.values(): | ||||
yield wr | ||||
def values(self): | ||||
with _IterationGuard(self): | ||||
for wr in self.data.values(): | ||||
obj = wr() | ||||
if obj is not None: | ||||
yield obj | ||||
def popitem(self): | ||||
if self._pending_removals: | ||||
self._commit_removals() | ||||
while True: | ||||
key, wr = self.data.popitem() | ||||
o = wr() | ||||
if o is not None: | ||||
return key, o | ||||
def pop(self, key, *args): | ||||
if self._pending_removals: | ||||
self._commit_removals() | ||||
try: | ||||
o = self.data.pop(key)() | ||||
except KeyError: | ||||
if args: | ||||
return args[0] | ||||
raise | ||||
if o is None: | ||||
raise KeyError(key) | ||||
else: | ||||
return o | ||||
def setdefault(self, key, default=None): | ||||
try: | ||||
wr = self.data[key] | ||||
except KeyError: | ||||
if self._pending_removals: | ||||
self._commit_removals() | ||||
self.data[key] = KeyedRef(default, self._remove, key) | ||||
return default | ||||
else: | ||||
return wr() | ||||
def update(self, dict=None, **kwargs): | ||||
if self._pending_removals: | ||||
self._commit_removals() | ||||
d = self.data | ||||
if dict is not None: | ||||
if not hasattr(dict, "items"): | ||||
dict = type({})(dict) | ||||
for key, o in dict.items(): | ||||
d[key] = KeyedRef(o, self._remove, key) | ||||
if len(kwargs): | ||||
self.update(kwargs) | ||||
def valuerefs(self): | ||||
"""Return a list of weak references to the values. | ||||
The references are not guaranteed to be 'live' at the time | ||||
they are used, so the result of calling the references needs | ||||
to be checked before being used. This can be used to avoid | ||||
creating references that will cause the garbage collector to | ||||
keep the values around longer than needed. | ||||
""" | ||||
return list(self.data.values()) | ||||
class KeyedRef(ref): | ||||
"""Specialized reference that includes a key corresponding to the value. | ||||
This is used in the WeakValueDictionary to avoid having to create | ||||
a function object for each key stored in the mapping. A shared | ||||
callback object can use the 'key' attribute of a KeyedRef instead | ||||
of getting a reference to the key from an enclosing scope. | ||||
""" | ||||
__slots__ = "key", | ||||
def __new__(type, ob, callback, key): | ||||
self = ref.__new__(type, ob, callback) | ||||
self.key = key | ||||
return self | ||||
def __init__(self, ob, callback, key): | ||||
super().__init__(ob, callback) | ||||
class WeakKeyDictionary(collections.MutableMapping): | ||||
""" Mapping class that references keys weakly. | ||||
Entries in the dictionary will be discarded when there is no | ||||
longer a strong reference to the key. This can be used to | ||||
associate additional data with an object owned by other parts of | ||||
an application without adding attributes to those objects. This | ||||
can be especially useful with objects that override attribute | ||||
accesses. | ||||
""" | ||||
def __init__(self, dict=None): | ||||
self.data = {} | ||||
def remove(k, selfref=ref(self)): | ||||
self = selfref() | ||||
if self is not None: | ||||
if self._iterating: | ||||
self._pending_removals.append(k) | ||||
else: | ||||
del self.data[k] | ||||
self._remove = remove | ||||
# A list of dead weakrefs (keys to be removed) | ||||
self._pending_removals = [] | ||||
self._iterating = set() | ||||
if dict is not None: | ||||
self.update(dict) | ||||
def _commit_removals(self): | ||||
# NOTE: We don't need to call this method before mutating the dict, | ||||
# because a dead weakref never compares equal to a live weakref, | ||||
# even if they happened to refer to equal objects. | ||||
# However, it means keys may already have been removed. | ||||
l = self._pending_removals | ||||
d = self.data | ||||
while l: | ||||
try: | ||||
del d[l.pop()] | ||||
except KeyError: | ||||
pass | ||||
def __delitem__(self, key): | ||||
del self.data[ref(key)] | ||||
def __getitem__(self, key): | ||||
return self.data[ref(key)] | ||||
def __len__(self): | ||||
return len(self.data) - len(self._pending_removals) | ||||
def __repr__(self): | ||||
return "<WeakKeyDictionary at %s>" % id(self) | ||||
def __setitem__(self, key, value): | ||||
self.data[ref(key, self._remove)] = value | ||||
def copy(self): | ||||
new = WeakKeyDictionary() | ||||
for key, value in self.data.items(): | ||||
o = key() | ||||
if o is not None: | ||||
new[o] = value | ||||
return new | ||||
__copy__ = copy | ||||
def __deepcopy__(self, memo): | ||||
from copy import deepcopy | ||||
new = self.__class__() | ||||
for key, value in self.data.items(): | ||||
o = key() | ||||
if o is not None: | ||||
new[o] = deepcopy(value, memo) | ||||
return new | ||||
def get(self, key, default=None): | ||||
return self.data.get(ref(key),default) | ||||
def __contains__(self, key): | ||||
try: | ||||
wr = ref(key) | ||||
except TypeError: | ||||
return False | ||||
return wr in self.data | ||||
def items(self): | ||||
with _IterationGuard(self): | ||||
for wr, value in self.data.items(): | ||||
key = wr() | ||||
if key is not None: | ||||
yield key, value | ||||
def keys(self): | ||||
with _IterationGuard(self): | ||||
for wr in self.data: | ||||
obj = wr() | ||||
if obj is not None: | ||||
yield obj | ||||
__iter__ = keys | ||||
def values(self): | ||||
with _IterationGuard(self): | ||||
for wr, value in self.data.items(): | ||||
if wr() is not None: | ||||
yield value | ||||
def keyrefs(self): | ||||
"""Return a list of weak references to the keys. | ||||
The references are not guaranteed to be 'live' at the time | ||||
they are used, so the result of calling the references needs | ||||
to be checked before being used. This can be used to avoid | ||||
creating references that will cause the garbage collector to | ||||
keep the keys around longer than needed. | ||||
""" | ||||
return list(self.data) | ||||
def popitem(self): | ||||
while True: | ||||
key, value = self.data.popitem() | ||||
o = key() | ||||
if o is not None: | ||||
return o, value | ||||
def pop(self, key, *args): | ||||
return self.data.pop(ref(key), *args) | ||||
def setdefault(self, key, default=None): | ||||
return self.data.setdefault(ref(key, self._remove),default) | ||||
def update(self, dict=None, **kwargs): | ||||
d = self.data | ||||
if dict is not None: | ||||
if not hasattr(dict, "items"): | ||||
dict = type({})(dict) | ||||
for key, value in dict.items(): | ||||
d[ref(key, self._remove)] = value | ||||
if len(kwargs): | ||||
self.update(kwargs) | ||||