| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import itertools |
|
|
|
|
| class Set: |
| """A simple set class. |
| |
| This class was originally used to deal with sets being missing in |
| ancient versions of python, but dnspython will continue to use it |
| as these sets are based on lists and are thus indexable, and this |
| ability is widely used in dnspython applications. |
| """ |
|
|
| __slots__ = ["items"] |
|
|
| def __init__(self, items=None): |
| """Initialize the set. |
| |
| *items*, an iterable or ``None``, the initial set of items. |
| """ |
|
|
| self.items = dict() |
| if items is not None: |
| for item in items: |
| |
| |
| self.add(item) |
|
|
| def __repr__(self): |
| return "dns.set.Set(%s)" % repr(list(self.items.keys())) |
|
|
| def add(self, item): |
| """Add an item to the set.""" |
|
|
| if item not in self.items: |
| self.items[item] = None |
|
|
| def remove(self, item): |
| """Remove an item from the set.""" |
|
|
| try: |
| del self.items[item] |
| except KeyError: |
| raise ValueError |
|
|
| def discard(self, item): |
| """Remove an item from the set if present.""" |
|
|
| self.items.pop(item, None) |
|
|
| def pop(self): |
| """Remove an arbitrary item from the set.""" |
| (k, _) = self.items.popitem() |
| return k |
|
|
| def _clone(self) -> "Set": |
| """Make a (shallow) copy of the set. |
| |
| There is a 'clone protocol' that subclasses of this class |
| should use. To make a copy, first call your super's _clone() |
| method, and use the object returned as the new instance. Then |
| make shallow copies of the attributes defined in the subclass. |
| |
| This protocol allows us to write the set algorithms that |
| return new instances (e.g. union) once, and keep using them in |
| subclasses. |
| """ |
|
|
| if hasattr(self, "_clone_class"): |
| cls = self._clone_class |
| else: |
| cls = self.__class__ |
| obj = cls.__new__(cls) |
| obj.items = dict() |
| obj.items.update(self.items) |
| return obj |
|
|
| def __copy__(self): |
| """Make a (shallow) copy of the set.""" |
|
|
| return self._clone() |
|
|
| def copy(self): |
| """Make a (shallow) copy of the set.""" |
|
|
| return self._clone() |
|
|
| def union_update(self, other): |
| """Update the set, adding any elements from other which are not |
| already in the set. |
| """ |
|
|
| if not isinstance(other, Set): |
| raise ValueError("other must be a Set instance") |
| if self is other: |
| return |
| for item in other.items: |
| self.add(item) |
|
|
| def intersection_update(self, other): |
| """Update the set, removing any elements from other which are not |
| in both sets. |
| """ |
|
|
| if not isinstance(other, Set): |
| raise ValueError("other must be a Set instance") |
| if self is other: |
| return |
| |
| |
| for item in list(self.items): |
| if item not in other.items: |
| del self.items[item] |
|
|
| def difference_update(self, other): |
| """Update the set, removing any elements from other which are in |
| the set. |
| """ |
|
|
| if not isinstance(other, Set): |
| raise ValueError("other must be a Set instance") |
| if self is other: |
| self.items.clear() |
| else: |
| for item in other.items: |
| self.discard(item) |
|
|
| def symmetric_difference_update(self, other): |
| """Update the set, retaining only elements unique to both sets.""" |
|
|
| if not isinstance(other, Set): |
| raise ValueError("other must be a Set instance") |
| if self is other: |
| self.items.clear() |
| else: |
| overlap = self.intersection(other) |
| self.union_update(other) |
| self.difference_update(overlap) |
|
|
| def union(self, other): |
| """Return a new set which is the union of ``self`` and ``other``. |
| |
| Returns the same Set type as this set. |
| """ |
|
|
| obj = self._clone() |
| obj.union_update(other) |
| return obj |
|
|
| def intersection(self, other): |
| """Return a new set which is the intersection of ``self`` and |
| ``other``. |
| |
| Returns the same Set type as this set. |
| """ |
|
|
| obj = self._clone() |
| obj.intersection_update(other) |
| return obj |
|
|
| def difference(self, other): |
| """Return a new set which ``self`` - ``other``, i.e. the items |
| in ``self`` which are not also in ``other``. |
| |
| Returns the same Set type as this set. |
| """ |
|
|
| obj = self._clone() |
| obj.difference_update(other) |
| return obj |
|
|
| def symmetric_difference(self, other): |
| """Return a new set which (``self`` - ``other``) | (``other`` |
| - ``self), ie: the items in either ``self`` or ``other`` which |
| are not contained in their intersection. |
| |
| Returns the same Set type as this set. |
| """ |
|
|
| obj = self._clone() |
| obj.symmetric_difference_update(other) |
| return obj |
|
|
| def __or__(self, other): |
| return self.union(other) |
|
|
| def __and__(self, other): |
| return self.intersection(other) |
|
|
| def __add__(self, other): |
| return self.union(other) |
|
|
| def __sub__(self, other): |
| return self.difference(other) |
|
|
| def __xor__(self, other): |
| return self.symmetric_difference(other) |
|
|
| def __ior__(self, other): |
| self.union_update(other) |
| return self |
|
|
| def __iand__(self, other): |
| self.intersection_update(other) |
| return self |
|
|
| def __iadd__(self, other): |
| self.union_update(other) |
| return self |
|
|
| def __isub__(self, other): |
| self.difference_update(other) |
| return self |
|
|
| def __ixor__(self, other): |
| self.symmetric_difference_update(other) |
| return self |
|
|
| def update(self, other): |
| """Update the set, adding any elements from other which are not |
| already in the set. |
| |
| *other*, the collection of items with which to update the set, which |
| may be any iterable type. |
| """ |
|
|
| for item in other: |
| self.add(item) |
|
|
| def clear(self): |
| """Make the set empty.""" |
| self.items.clear() |
|
|
| def __eq__(self, other): |
| return self.items == other.items |
|
|
| def __ne__(self, other): |
| return not self.__eq__(other) |
|
|
| def __len__(self): |
| return len(self.items) |
|
|
| def __iter__(self): |
| return iter(self.items) |
|
|
| def __getitem__(self, i): |
| if isinstance(i, slice): |
| return list(itertools.islice(self.items, i.start, i.stop, i.step)) |
| else: |
| return next(itertools.islice(self.items, i, i + 1)) |
|
|
| def __delitem__(self, i): |
| if isinstance(i, slice): |
| for elt in list(self[i]): |
| del self.items[elt] |
| else: |
| del self.items[self[i]] |
|
|
| def issubset(self, other): |
| """Is this set a subset of *other*? |
| |
| Returns a ``bool``. |
| """ |
|
|
| if not isinstance(other, Set): |
| raise ValueError("other must be a Set instance") |
| for item in self.items: |
| if item not in other.items: |
| return False |
| return True |
|
|
| def issuperset(self, other): |
| """Is this set a superset of *other*? |
| |
| Returns a ``bool``. |
| """ |
|
|
| if not isinstance(other, Set): |
| raise ValueError("other must be a Set instance") |
| for item in other.items: |
| if item not in self.items: |
| return False |
| return True |
|
|
| def isdisjoint(self, other): |
| if not isinstance(other, Set): |
| raise ValueError("other must be a Set instance") |
| for item in other.items: |
| if item in self.items: |
| return False |
| return True |
|
|