Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/util/_collections.py: 36%
656 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
1# util/_collections.py
2# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
8"""Collection classes and helpers."""
10from __future__ import absolute_import
12import operator
13import types
14import weakref
16from .compat import binary_types
17from .compat import collections_abc
18from .compat import itertools_filterfalse
19from .compat import py2k
20from .compat import py37
21from .compat import string_types
22from .compat import threading
25EMPTY_SET = frozenset()
28class ImmutableContainer(object):
29 def _immutable(self, *arg, **kw):
30 raise TypeError("%s object is immutable" % self.__class__.__name__)
32 __delitem__ = __setitem__ = __setattr__ = _immutable
35def _immutabledict_py_fallback():
36 class immutabledict(ImmutableContainer, dict):
38 clear = (
39 pop
40 ) = popitem = setdefault = update = ImmutableContainer._immutable
42 def __new__(cls, *args):
43 new = dict.__new__(cls)
44 dict.__init__(new, *args)
45 return new
47 def __init__(self, *args):
48 pass
50 def __reduce__(self):
51 return _immutabledict_reconstructor, (dict(self),)
53 def union(self, __d=None):
54 if not __d:
55 return self
57 new = dict.__new__(self.__class__)
58 dict.__init__(new, self)
59 dict.update(new, __d)
60 return new
62 def _union_w_kw(self, __d=None, **kw):
63 # not sure if C version works correctly w/ this yet
64 if not __d and not kw:
65 return self
67 new = dict.__new__(self.__class__)
68 dict.__init__(new, self)
69 if __d:
70 dict.update(new, __d)
71 dict.update(new, kw)
72 return new
74 def merge_with(self, *dicts):
75 new = None
76 for d in dicts:
77 if d:
78 if new is None:
79 new = dict.__new__(self.__class__)
80 dict.__init__(new, self)
81 dict.update(new, d)
82 if new is None:
83 return self
85 return new
87 def __repr__(self):
88 return "immutabledict(%s)" % dict.__repr__(self)
90 return immutabledict
93try:
94 from sqlalchemy.cimmutabledict import immutabledict
96 collections_abc.Mapping.register(immutabledict)
98except ImportError:
99 immutabledict = _immutabledict_py_fallback()
101 def _immutabledict_reconstructor(*arg):
102 """do the pickle dance"""
103 return immutabledict(*arg)
106def coerce_to_immutabledict(d):
107 if not d:
108 return EMPTY_DICT
109 elif isinstance(d, immutabledict):
110 return d
111 else:
112 return immutabledict(d)
115EMPTY_DICT = immutabledict()
118class FacadeDict(ImmutableContainer, dict):
119 """A dictionary that is not publicly mutable."""
121 clear = pop = popitem = setdefault = update = ImmutableContainer._immutable
123 def __new__(cls, *args):
124 new = dict.__new__(cls)
125 return new
127 def copy(self):
128 raise NotImplementedError(
129 "an immutabledict shouldn't need to be copied. use dict(d) "
130 "if you need a mutable dictionary."
131 )
133 def __reduce__(self):
134 return FacadeDict, (dict(self),)
136 def _insert_item(self, key, value):
137 """insert an item into the dictionary directly."""
138 dict.__setitem__(self, key, value)
140 def __repr__(self):
141 return "FacadeDict(%s)" % dict.__repr__(self)
144class Properties(object):
145 """Provide a __getattr__/__setattr__ interface over a dict."""
147 __slots__ = ("_data",)
149 def __init__(self, data):
150 object.__setattr__(self, "_data", data)
152 def __len__(self):
153 return len(self._data)
155 def __iter__(self):
156 return iter(list(self._data.values()))
158 def __dir__(self):
159 return dir(super(Properties, self)) + [
160 str(k) for k in self._data.keys()
161 ]
163 def __add__(self, other):
164 return list(self) + list(other)
166 def __setitem__(self, key, obj):
167 self._data[key] = obj
169 def __getitem__(self, key):
170 return self._data[key]
172 def __delitem__(self, key):
173 del self._data[key]
175 def __setattr__(self, key, obj):
176 self._data[key] = obj
178 def __getstate__(self):
179 return {"_data": self._data}
181 def __setstate__(self, state):
182 object.__setattr__(self, "_data", state["_data"])
184 def __getattr__(self, key):
185 try:
186 return self._data[key]
187 except KeyError:
188 raise AttributeError(key)
190 def __contains__(self, key):
191 return key in self._data
193 def as_immutable(self):
194 """Return an immutable proxy for this :class:`.Properties`."""
196 return ImmutableProperties(self._data)
198 def update(self, value):
199 self._data.update(value)
201 def get(self, key, default=None):
202 if key in self:
203 return self[key]
204 else:
205 return default
207 def keys(self):
208 return list(self._data)
210 def values(self):
211 return list(self._data.values())
213 def items(self):
214 return list(self._data.items())
216 def has_key(self, key):
217 return key in self._data
219 def clear(self):
220 self._data.clear()
223class OrderedProperties(Properties):
224 """Provide a __getattr__/__setattr__ interface with an OrderedDict
225 as backing store."""
227 __slots__ = ()
229 def __init__(self):
230 Properties.__init__(self, OrderedDict())
233class ImmutableProperties(ImmutableContainer, Properties):
234 """Provide immutable dict/object attribute to an underlying dictionary."""
236 __slots__ = ()
239def _ordered_dictionary_sort(d, key=None):
240 """Sort an OrderedDict in-place."""
242 items = [(k, d[k]) for k in sorted(d, key=key)]
244 d.clear()
246 d.update(items)
249if py37:
250 OrderedDict = dict
251 sort_dictionary = _ordered_dictionary_sort
253else:
254 # prevent sort_dictionary from being used against a plain dictionary
255 # for Python < 3.7
257 def sort_dictionary(d, key=None):
258 """Sort an OrderedDict in place."""
260 d._ordered_dictionary_sort(key=key)
262 class OrderedDict(dict):
263 """Dictionary that maintains insertion order.
265 Superseded by Python dict as of Python 3.7
267 """
269 __slots__ = ("_list",)
271 def _ordered_dictionary_sort(self, key=None):
272 _ordered_dictionary_sort(self, key=key)
274 def __reduce__(self):
275 return OrderedDict, (self.items(),)
277 def __init__(self, ____sequence=None, **kwargs):
278 self._list = []
279 if ____sequence is None:
280 if kwargs:
281 self.update(**kwargs)
282 else:
283 self.update(____sequence, **kwargs)
285 def clear(self):
286 self._list = []
287 dict.clear(self)
289 def copy(self):
290 return self.__copy__()
292 def __copy__(self):
293 return OrderedDict(self)
295 def update(self, ____sequence=None, **kwargs):
296 if ____sequence is not None:
297 if hasattr(____sequence, "keys"):
298 for key in ____sequence.keys():
299 self.__setitem__(key, ____sequence[key])
300 else:
301 for key, value in ____sequence:
302 self[key] = value
303 if kwargs:
304 self.update(kwargs)
306 def setdefault(self, key, value):
307 if key not in self:
308 self.__setitem__(key, value)
309 return value
310 else:
311 return self.__getitem__(key)
313 def __iter__(self):
314 return iter(self._list)
316 def keys(self):
317 return list(self)
319 def values(self):
320 return [self[key] for key in self._list]
322 def items(self):
323 return [(key, self[key]) for key in self._list]
325 if py2k:
327 def itervalues(self):
328 return iter(self.values())
330 def iterkeys(self):
331 return iter(self)
333 def iteritems(self):
334 return iter(self.items())
336 def __setitem__(self, key, obj):
337 if key not in self:
338 try:
339 self._list.append(key)
340 except AttributeError:
341 # work around Python pickle loads() with
342 # dict subclass (seems to ignore __setstate__?)
343 self._list = [key]
344 dict.__setitem__(self, key, obj)
346 def __delitem__(self, key):
347 dict.__delitem__(self, key)
348 self._list.remove(key)
350 def pop(self, key, *default):
351 present = key in self
352 value = dict.pop(self, key, *default)
353 if present:
354 self._list.remove(key)
355 return value
357 def popitem(self):
358 item = dict.popitem(self)
359 self._list.remove(item[0])
360 return item
363class OrderedSet(set):
364 def __init__(self, d=None):
365 set.__init__(self)
366 if d is not None:
367 self._list = unique_list(d)
368 set.update(self, self._list)
369 else:
370 self._list = []
372 def add(self, element):
373 if element not in self:
374 self._list.append(element)
375 set.add(self, element)
377 def remove(self, element):
378 set.remove(self, element)
379 self._list.remove(element)
381 def insert(self, pos, element):
382 if element not in self:
383 self._list.insert(pos, element)
384 set.add(self, element)
386 def discard(self, element):
387 if element in self:
388 self._list.remove(element)
389 set.remove(self, element)
391 def clear(self):
392 set.clear(self)
393 self._list = []
395 def __getitem__(self, key):
396 return self._list[key]
398 def __iter__(self):
399 return iter(self._list)
401 def __add__(self, other):
402 return self.union(other)
404 def __repr__(self):
405 return "%s(%r)" % (self.__class__.__name__, self._list)
407 __str__ = __repr__
409 def update(self, iterable):
410 for e in iterable:
411 if e not in self:
412 self._list.append(e)
413 set.add(self, e)
414 return self
416 __ior__ = update
418 def union(self, other):
419 result = self.__class__(self)
420 result.update(other)
421 return result
423 __or__ = union
425 def intersection(self, other):
426 other = set(other)
427 return self.__class__(a for a in self if a in other)
429 __and__ = intersection
431 def symmetric_difference(self, other):
432 other = set(other)
433 result = self.__class__(a for a in self if a not in other)
434 result.update(a for a in other if a not in self)
435 return result
437 __xor__ = symmetric_difference
439 def difference(self, other):
440 other = set(other)
441 return self.__class__(a for a in self if a not in other)
443 __sub__ = difference
445 def intersection_update(self, other):
446 other = set(other)
447 set.intersection_update(self, other)
448 self._list = [a for a in self._list if a in other]
449 return self
451 __iand__ = intersection_update
453 def symmetric_difference_update(self, other):
454 set.symmetric_difference_update(self, other)
455 self._list = [a for a in self._list if a in self]
456 self._list += [a for a in other._list if a in self]
457 return self
459 __ixor__ = symmetric_difference_update
461 def difference_update(self, other):
462 set.difference_update(self, other)
463 self._list = [a for a in self._list if a in self]
464 return self
466 __isub__ = difference_update
469class IdentitySet(object):
470 """A set that considers only object id() for uniqueness.
472 This strategy has edge cases for builtin types- it's possible to have
473 two 'foo' strings in one of these sets, for example. Use sparingly.
475 """
477 def __init__(self, iterable=None):
478 self._members = dict()
479 if iterable:
480 self.update(iterable)
482 def add(self, value):
483 self._members[id(value)] = value
485 def __contains__(self, value):
486 return id(value) in self._members
488 def remove(self, value):
489 del self._members[id(value)]
491 def discard(self, value):
492 try:
493 self.remove(value)
494 except KeyError:
495 pass
497 def pop(self):
498 try:
499 pair = self._members.popitem()
500 return pair[1]
501 except KeyError:
502 raise KeyError("pop from an empty set")
504 def clear(self):
505 self._members.clear()
507 def __cmp__(self, other):
508 raise TypeError("cannot compare sets using cmp()")
510 def __eq__(self, other):
511 if isinstance(other, IdentitySet):
512 return self._members == other._members
513 else:
514 return False
516 def __ne__(self, other):
517 if isinstance(other, IdentitySet):
518 return self._members != other._members
519 else:
520 return True
522 def issubset(self, iterable):
523 if isinstance(iterable, self.__class__):
524 other = iterable
525 else:
526 other = self.__class__(iterable)
528 if len(self) > len(other):
529 return False
530 for m in itertools_filterfalse(
531 other._members.__contains__, iter(self._members.keys())
532 ):
533 return False
534 return True
536 def __le__(self, other):
537 if not isinstance(other, IdentitySet):
538 return NotImplemented
539 return self.issubset(other)
541 def __lt__(self, other):
542 if not isinstance(other, IdentitySet):
543 return NotImplemented
544 return len(self) < len(other) and self.issubset(other)
546 def issuperset(self, iterable):
547 if isinstance(iterable, self.__class__):
548 other = iterable
549 else:
550 other = self.__class__(iterable)
552 if len(self) < len(other):
553 return False
555 for m in itertools_filterfalse(
556 self._members.__contains__, iter(other._members.keys())
557 ):
558 return False
559 return True
561 def __ge__(self, other):
562 if not isinstance(other, IdentitySet):
563 return NotImplemented
564 return self.issuperset(other)
566 def __gt__(self, other):
567 if not isinstance(other, IdentitySet):
568 return NotImplemented
569 return len(self) > len(other) and self.issuperset(other)
571 def union(self, iterable):
572 result = self.__class__()
573 members = self._members
574 result._members.update(members)
575 result._members.update((id(obj), obj) for obj in iterable)
576 return result
578 def __or__(self, other):
579 if not isinstance(other, IdentitySet):
580 return NotImplemented
581 return self.union(other)
583 def update(self, iterable):
584 self._members.update((id(obj), obj) for obj in iterable)
586 def __ior__(self, other):
587 if not isinstance(other, IdentitySet):
588 return NotImplemented
589 self.update(other)
590 return self
592 def difference(self, iterable):
593 result = self.__class__()
594 members = self._members
595 if isinstance(iterable, self.__class__):
596 other = set(iterable._members.keys())
597 else:
598 other = {id(obj) for obj in iterable}
599 result._members.update(
600 ((k, v) for k, v in members.items() if k not in other)
601 )
602 return result
604 def __sub__(self, other):
605 if not isinstance(other, IdentitySet):
606 return NotImplemented
607 return self.difference(other)
609 def difference_update(self, iterable):
610 self._members = self.difference(iterable)._members
612 def __isub__(self, other):
613 if not isinstance(other, IdentitySet):
614 return NotImplemented
615 self.difference_update(other)
616 return self
618 def intersection(self, iterable):
619 result = self.__class__()
620 members = self._members
621 if isinstance(iterable, self.__class__):
622 other = set(iterable._members.keys())
623 else:
624 other = {id(obj) for obj in iterable}
625 result._members.update(
626 (k, v) for k, v in members.items() if k in other
627 )
628 return result
630 def __and__(self, other):
631 if not isinstance(other, IdentitySet):
632 return NotImplemented
633 return self.intersection(other)
635 def intersection_update(self, iterable):
636 self._members = self.intersection(iterable)._members
638 def __iand__(self, other):
639 if not isinstance(other, IdentitySet):
640 return NotImplemented
641 self.intersection_update(other)
642 return self
644 def symmetric_difference(self, iterable):
645 result = self.__class__()
646 members = self._members
647 if isinstance(iterable, self.__class__):
648 other = iterable._members
649 else:
650 other = {id(obj): obj for obj in iterable}
651 result._members.update(
652 ((k, v) for k, v in members.items() if k not in other)
653 )
654 result._members.update(
655 ((k, v) for k, v in other.items() if k not in members)
656 )
657 return result
659 def __xor__(self, other):
660 if not isinstance(other, IdentitySet):
661 return NotImplemented
662 return self.symmetric_difference(other)
664 def symmetric_difference_update(self, iterable):
665 self._members = self.symmetric_difference(iterable)._members
667 def __ixor__(self, other):
668 if not isinstance(other, IdentitySet):
669 return NotImplemented
670 self.symmetric_difference(other)
671 return self
673 def copy(self):
674 return type(self)(iter(self._members.values()))
676 __copy__ = copy
678 def __len__(self):
679 return len(self._members)
681 def __iter__(self):
682 return iter(self._members.values())
684 def __hash__(self):
685 raise TypeError("set objects are unhashable")
687 def __repr__(self):
688 return "%s(%r)" % (type(self).__name__, list(self._members.values()))
691class WeakSequence(object):
692 def __init__(self, __elements=()):
693 # adapted from weakref.WeakKeyDictionary, prevent reference
694 # cycles in the collection itself
695 def _remove(item, selfref=weakref.ref(self)):
696 self = selfref()
697 if self is not None:
698 self._storage.remove(item)
700 self._remove = _remove
701 self._storage = [
702 weakref.ref(element, _remove) for element in __elements
703 ]
705 def append(self, item):
706 self._storage.append(weakref.ref(item, self._remove))
708 def __len__(self):
709 return len(self._storage)
711 def __iter__(self):
712 return (
713 obj for obj in (ref() for ref in self._storage) if obj is not None
714 )
716 def __getitem__(self, index):
717 try:
718 obj = self._storage[index]
719 except KeyError:
720 raise IndexError("Index %s out of range" % index)
721 else:
722 return obj()
725class OrderedIdentitySet(IdentitySet):
726 def __init__(self, iterable=None):
727 IdentitySet.__init__(self)
728 self._members = OrderedDict()
729 if iterable:
730 for o in iterable:
731 self.add(o)
734class PopulateDict(dict):
735 """A dict which populates missing values via a creation function.
737 Note the creation function takes a key, unlike
738 collections.defaultdict.
740 """
742 def __init__(self, creator):
743 self.creator = creator
745 def __missing__(self, key):
746 self[key] = val = self.creator(key)
747 return val
750class WeakPopulateDict(dict):
751 """Like PopulateDict, but assumes a self + a method and does not create
752 a reference cycle.
754 """
756 def __init__(self, creator_method):
757 self.creator = creator_method.__func__
758 weakself = creator_method.__self__
759 self.weakself = weakref.ref(weakself)
761 def __missing__(self, key):
762 self[key] = val = self.creator(self.weakself(), key)
763 return val
766# Define collections that are capable of storing
767# ColumnElement objects as hashable keys/elements.
768# At this point, these are mostly historical, things
769# used to be more complicated.
770column_set = set
771column_dict = dict
772ordered_column_set = OrderedSet
775_getters = PopulateDict(operator.itemgetter)
777_property_getters = PopulateDict(
778 lambda idx: property(operator.itemgetter(idx))
779)
782def unique_list(seq, hashfunc=None):
783 seen = set()
784 seen_add = seen.add
785 if not hashfunc:
786 return [x for x in seq if x not in seen and not seen_add(x)]
787 else:
788 return [
789 x
790 for x in seq
791 if hashfunc(x) not in seen and not seen_add(hashfunc(x))
792 ]
795class UniqueAppender(object):
796 """Appends items to a collection ensuring uniqueness.
798 Additional appends() of the same object are ignored. Membership is
799 determined by identity (``is a``) not equality (``==``).
800 """
802 def __init__(self, data, via=None):
803 self.data = data
804 self._unique = {}
805 if via:
806 self._data_appender = getattr(data, via)
807 elif hasattr(data, "append"):
808 self._data_appender = data.append
809 elif hasattr(data, "add"):
810 self._data_appender = data.add
812 def append(self, item):
813 id_ = id(item)
814 if id_ not in self._unique:
815 self._data_appender(item)
816 self._unique[id_] = True
818 def __iter__(self):
819 return iter(self.data)
822def coerce_generator_arg(arg):
823 if len(arg) == 1 and isinstance(arg[0], types.GeneratorType):
824 return list(arg[0])
825 else:
826 return arg
829def to_list(x, default=None):
830 if x is None:
831 return default
832 if not isinstance(x, collections_abc.Iterable) or isinstance(
833 x, string_types + binary_types
834 ):
835 return [x]
836 elif isinstance(x, list):
837 return x
838 else:
839 return list(x)
842def has_intersection(set_, iterable):
843 r"""return True if any items of set\_ are present in iterable.
845 Goes through special effort to ensure __hash__ is not called
846 on items in iterable that don't support it.
848 """
849 # TODO: optimize, write in C, etc.
850 return bool(set_.intersection([i for i in iterable if i.__hash__]))
853def to_set(x):
854 if x is None:
855 return set()
856 if not isinstance(x, set):
857 return set(to_list(x))
858 else:
859 return x
862def to_column_set(x):
863 if x is None:
864 return column_set()
865 if not isinstance(x, column_set):
866 return column_set(to_list(x))
867 else:
868 return x
871def update_copy(d, _new=None, **kw):
872 """Copy the given dict and update with the given values."""
874 d = d.copy()
875 if _new:
876 d.update(_new)
877 d.update(**kw)
878 return d
881def flatten_iterator(x):
882 """Given an iterator of which further sub-elements may also be
883 iterators, flatten the sub-elements into a single iterator.
885 """
886 for elem in x:
887 if not isinstance(elem, str) and hasattr(elem, "__iter__"):
888 for y in flatten_iterator(elem):
889 yield y
890 else:
891 yield elem
894class LRUCache(dict):
895 """Dictionary with 'squishy' removal of least
896 recently used items.
898 Note that either get() or [] should be used here, but
899 generally its not safe to do an "in" check first as the dictionary
900 can change subsequent to that call.
902 """
904 __slots__ = "capacity", "threshold", "size_alert", "_counter", "_mutex"
906 def __init__(self, capacity=100, threshold=0.5, size_alert=None):
907 self.capacity = capacity
908 self.threshold = threshold
909 self.size_alert = size_alert
910 self._counter = 0
911 self._mutex = threading.Lock()
913 def _inc_counter(self):
914 self._counter += 1
915 return self._counter
917 def get(self, key, default=None):
918 item = dict.get(self, key, default)
919 if item is not default:
920 item[2] = self._inc_counter()
921 return item[1]
922 else:
923 return default
925 def __getitem__(self, key):
926 item = dict.__getitem__(self, key)
927 item[2] = self._inc_counter()
928 return item[1]
930 def values(self):
931 return [i[1] for i in dict.values(self)]
933 def setdefault(self, key, value):
934 if key in self:
935 return self[key]
936 else:
937 self[key] = value
938 return value
940 def __setitem__(self, key, value):
941 item = dict.get(self, key)
942 if item is None:
943 item = [key, value, self._inc_counter()]
944 dict.__setitem__(self, key, item)
945 else:
946 item[1] = value
947 self._manage_size()
949 @property
950 def size_threshold(self):
951 return self.capacity + self.capacity * self.threshold
953 def _manage_size(self):
954 if not self._mutex.acquire(False):
955 return
956 try:
957 size_alert = bool(self.size_alert)
958 while len(self) > self.capacity + self.capacity * self.threshold:
959 if size_alert:
960 size_alert = False
961 self.size_alert(self)
962 by_counter = sorted(
963 dict.values(self), key=operator.itemgetter(2), reverse=True
964 )
965 for item in by_counter[self.capacity :]:
966 try:
967 del self[item[0]]
968 except KeyError:
969 # deleted elsewhere; skip
970 continue
971 finally:
972 self._mutex.release()
975class ScopedRegistry(object):
976 """A Registry that can store one or multiple instances of a single
977 class on the basis of a "scope" function.
979 The object implements ``__call__`` as the "getter", so by
980 calling ``myregistry()`` the contained object is returned
981 for the current scope.
983 :param createfunc:
984 a callable that returns a new object to be placed in the registry
986 :param scopefunc:
987 a callable that will return a key to store/retrieve an object.
988 """
990 def __init__(self, createfunc, scopefunc):
991 """Construct a new :class:`.ScopedRegistry`.
993 :param createfunc: A creation function that will generate
994 a new value for the current scope, if none is present.
996 :param scopefunc: A function that returns a hashable
997 token representing the current scope (such as, current
998 thread identifier).
1000 """
1001 self.createfunc = createfunc
1002 self.scopefunc = scopefunc
1003 self.registry = {}
1005 def __call__(self):
1006 key = self.scopefunc()
1007 try:
1008 return self.registry[key]
1009 except KeyError:
1010 return self.registry.setdefault(key, self.createfunc())
1012 def has(self):
1013 """Return True if an object is present in the current scope."""
1015 return self.scopefunc() in self.registry
1017 def set(self, obj):
1018 """Set the value for the current scope."""
1020 self.registry[self.scopefunc()] = obj
1022 def clear(self):
1023 """Clear the current scope, if any."""
1025 try:
1026 del self.registry[self.scopefunc()]
1027 except KeyError:
1028 pass
1031class ThreadLocalRegistry(ScopedRegistry):
1032 """A :class:`.ScopedRegistry` that uses a ``threading.local()``
1033 variable for storage.
1035 """
1037 def __init__(self, createfunc):
1038 self.createfunc = createfunc
1039 self.registry = threading.local()
1041 def __call__(self):
1042 try:
1043 return self.registry.value
1044 except AttributeError:
1045 val = self.registry.value = self.createfunc()
1046 return val
1048 def has(self):
1049 return hasattr(self.registry, "value")
1051 def set(self, obj):
1052 self.registry.value = obj
1054 def clear(self):
1055 try:
1056 del self.registry.value
1057 except AttributeError:
1058 pass
1061def has_dupes(sequence, target):
1062 """Given a sequence and search object, return True if there's more
1063 than one, False if zero or one of them.
1066 """
1067 # compare to .index version below, this version introduces less function
1068 # overhead and is usually the same speed. At 15000 items (way bigger than
1069 # a relationship-bound collection in memory usually is) it begins to
1070 # fall behind the other version only by microseconds.
1071 c = 0
1072 for item in sequence:
1073 if item is target:
1074 c += 1
1075 if c > 1:
1076 return True
1077 return False
1080# .index version. the two __contains__ calls as well
1081# as .index() and isinstance() slow this down.
1082# def has_dupes(sequence, target):
1083# if target not in sequence:
1084# return False
1085# elif not isinstance(sequence, collections_abc.Sequence):
1086# return False
1087#
1088# idx = sequence.index(target)
1089# return target in sequence[idx + 1:]