123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
- # Copyright (C) 2003-2017 Nominum, Inc.
- #
- # Permission to use, copy, modify, and distribute this software and its
- # documentation for any purpose with or without fee is hereby granted,
- # provided that the above copyright notice and this permission notice
- # appear in all copies.
- #
- # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
- # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
- # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- import itertools
- class Set:
- """A simple set class.
- This class was originally used to deal with python not having a set class, and
- originally the class used lists in its implementation. The ordered and indexable
- nature of RRsets and Rdatasets is unfortunately widely used in dnspython
- applications, so for backwards compatibility sets continue to be a custom class, now
- based on an ordered dictionary.
- """
- __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:
- # This is safe for how we use set, but if other code
- # subclasses it could be a legitimate issue.
- self.add(item) # lgtm[py/init-calls-subclass]
- def __repr__(self):
- return f"dns.set.Set({repr(list(self.items.keys()))})" # pragma: no cover
- 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 # type: ignore
- 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: # lgtm[py/comparison-using-is]
- 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: # lgtm[py/comparison-using-is]
- return
- # we make a copy of the list so that we can remove items from
- # the list without breaking the iterator.
- 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: # lgtm[py/comparison-using-is]
- 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: # lgtm[py/comparison-using-is]
- 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
|