comparison env/lib/python3.9/site-packages/boltons/setutils.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 # -*- coding: utf-8 -*-
2 """\
3
4 The :class:`set` type brings the practical expressiveness of
5 set theory to Python. It has a very rich API overall, but lacks a
6 couple of fundamental features. For one, sets are not ordered. On top
7 of this, sets are not indexable, i.e, ``my_set[8]`` will raise an
8 :exc:`TypeError`. The :class:`IndexedSet` type remedies both of these
9 issues without compromising on the excellent complexity
10 characteristics of Python's built-in set implementation.
11 """
12
13 from __future__ import print_function
14
15 from bisect import bisect_left
16 from itertools import chain, islice
17 import operator
18
19 try:
20 from collections.abc import MutableSet
21 except ImportError:
22 from collections import MutableSet
23
24 try:
25 from typeutils import make_sentinel
26 _MISSING = make_sentinel(var_name='_MISSING')
27 except ImportError:
28 _MISSING = object()
29
30
31 __all__ = ['IndexedSet', 'complement']
32
33
34 _COMPACTION_FACTOR = 8
35
36 # TODO: inherit from set()
37 # TODO: .discard_many(), .remove_many()
38 # TODO: raise exception on non-set params?
39 # TODO: technically reverse operators should probably reverse the
40 # order of the 'other' inputs and put self last (to try and maintain
41 # insertion order)
42
43
44 class IndexedSet(MutableSet):
45 """``IndexedSet`` is a :class:`collections.MutableSet` that maintains
46 insertion order and uniqueness of inserted elements. It's a hybrid
47 type, mostly like an OrderedSet, but also :class:`list`-like, in
48 that it supports indexing and slicing.
49
50 Args:
51 other (iterable): An optional iterable used to initialize the set.
52
53 >>> x = IndexedSet(list(range(4)) + list(range(8)))
54 >>> x
55 IndexedSet([0, 1, 2, 3, 4, 5, 6, 7])
56 >>> x - set(range(2))
57 IndexedSet([2, 3, 4, 5, 6, 7])
58 >>> x[-1]
59 7
60 >>> fcr = IndexedSet('freecreditreport.com')
61 >>> ''.join(fcr[:fcr.index('.')])
62 'frecditpo'
63
64 Standard set operators and interoperation with :class:`set` are
65 all supported:
66
67 >>> fcr & set('cash4gold.com')
68 IndexedSet(['c', 'd', 'o', '.', 'm'])
69
70 As you can see, the ``IndexedSet`` is almost like a ``UniqueList``,
71 retaining only one copy of a given value, in the order it was
72 first added. For the curious, the reason why IndexedSet does not
73 support setting items based on index (i.e, ``__setitem__()``),
74 consider the following dilemma::
75
76 my_indexed_set = [A, B, C, D]
77 my_indexed_set[2] = A
78
79 At this point, a set requires only one *A*, but a :class:`list` would
80 overwrite *C*. Overwriting *C* would change the length of the list,
81 meaning that ``my_indexed_set[2]`` would not be *A*, as expected with a
82 list, but rather *D*. So, no ``__setitem__()``.
83
84 Otherwise, the API strives to be as complete a union of the
85 :class:`list` and :class:`set` APIs as possible.
86 """
87 def __init__(self, other=None):
88 self.item_index_map = dict()
89 self.item_list = []
90 self.dead_indices = []
91 self._compactions = 0
92 self._c_max_size = 0
93 if other:
94 self.update(other)
95
96 # internal functions
97 @property
98 def _dead_index_count(self):
99 return len(self.item_list) - len(self.item_index_map)
100
101 def _compact(self):
102 if not self.dead_indices:
103 return
104 self._compactions += 1
105 dead_index_count = self._dead_index_count
106 items, index_map = self.item_list, self.item_index_map
107 self._c_max_size = max(self._c_max_size, len(items))
108 for i, item in enumerate(self):
109 items[i] = item
110 index_map[item] = i
111 del items[-dead_index_count:]
112 del self.dead_indices[:]
113
114 def _cull(self):
115 ded = self.dead_indices
116 if not ded:
117 return
118 items, ii_map = self.item_list, self.item_index_map
119 if not ii_map:
120 del items[:]
121 del ded[:]
122 elif len(ded) > 384:
123 self._compact()
124 elif self._dead_index_count > (len(items) / _COMPACTION_FACTOR):
125 self._compact()
126 elif items[-1] is _MISSING: # get rid of dead right hand side
127 num_dead = 1
128 while items[-(num_dead + 1)] is _MISSING:
129 num_dead += 1
130 if ded and ded[-1][1] == len(items):
131 del ded[-1]
132 del items[-num_dead:]
133
134 def _get_real_index(self, index):
135 if index < 0:
136 index += len(self)
137 if not self.dead_indices:
138 return index
139 real_index = index
140 for d_start, d_stop in self.dead_indices:
141 if real_index < d_start:
142 break
143 real_index += d_stop - d_start
144 return real_index
145
146 def _get_apparent_index(self, index):
147 if index < 0:
148 index += len(self)
149 if not self.dead_indices:
150 return index
151 apparent_index = index
152 for d_start, d_stop in self.dead_indices:
153 if index < d_start:
154 break
155 apparent_index -= d_stop - d_start
156 return apparent_index
157
158 def _add_dead(self, start, stop=None):
159 # TODO: does not handle when the new interval subsumes
160 # multiple existing intervals
161 dints = self.dead_indices
162 if stop is None:
163 stop = start + 1
164 cand_int = [start, stop]
165 if not dints:
166 dints.append(cand_int)
167 return
168 int_idx = bisect_left(dints, cand_int)
169 dint = dints[int_idx - 1]
170 d_start, d_stop = dint
171 if start <= d_start <= stop:
172 dint[0] = start
173 elif start <= d_stop <= stop:
174 dint[1] = stop
175 else:
176 dints.insert(int_idx, cand_int)
177 return
178
179 # common operations (shared by set and list)
180 def __len__(self):
181 return len(self.item_index_map)
182
183 def __contains__(self, item):
184 return item in self.item_index_map
185
186 def __iter__(self):
187 return (item for item in self.item_list if item is not _MISSING)
188
189 def __reversed__(self):
190 item_list = self.item_list
191 return (item for item in reversed(item_list) if item is not _MISSING)
192
193 def __repr__(self):
194 return '%s(%r)' % (self.__class__.__name__, list(self))
195
196 def __eq__(self, other):
197 if isinstance(other, IndexedSet):
198 return len(self) == len(other) and list(self) == list(other)
199 return set(self) == set(other)
200
201 @classmethod
202 def from_iterable(cls, it):
203 "from_iterable(it) -> create a set from an iterable"
204 return cls(it)
205
206 # set operations
207 def add(self, item):
208 "add(item) -> add item to the set"
209 if item not in self.item_index_map:
210 self.item_index_map[item] = len(self.item_list)
211 self.item_list.append(item)
212
213 def remove(self, item):
214 "remove(item) -> remove item from the set, raises if not present"
215 try:
216 didx = self.item_index_map.pop(item)
217 except KeyError:
218 raise KeyError(item)
219 self.item_list[didx] = _MISSING
220 self._add_dead(didx)
221 self._cull()
222
223 def discard(self, item):
224 "discard(item) -> discard item from the set (does not raise)"
225 try:
226 self.remove(item)
227 except KeyError:
228 pass
229
230 def clear(self):
231 "clear() -> empty the set"
232 del self.item_list[:]
233 del self.dead_indices[:]
234 self.item_index_map.clear()
235
236 def isdisjoint(self, other):
237 "isdisjoint(other) -> return True if no overlap with other"
238 iim = self.item_index_map
239 for k in other:
240 if k in iim:
241 return False
242 return True
243
244 def issubset(self, other):
245 "issubset(other) -> return True if other contains this set"
246 if len(other) < len(self):
247 return False
248 for k in self.item_index_map:
249 if k not in other:
250 return False
251 return True
252
253 def issuperset(self, other):
254 "issuperset(other) -> return True if set contains other"
255 if len(other) > len(self):
256 return False
257 iim = self.item_index_map
258 for k in other:
259 if k not in iim:
260 return False
261 return True
262
263 def union(self, *others):
264 "union(*others) -> return a new set containing this set and others"
265 return self.from_iterable(chain(self, *others))
266
267 def iter_intersection(self, *others):
268 "iter_intersection(*others) -> iterate over elements also in others"
269 for k in self:
270 for other in others:
271 if k not in other:
272 break
273 else:
274 yield k
275 return
276
277 def intersection(self, *others):
278 "intersection(*others) -> get a set with overlap of this and others"
279 if len(others) == 1:
280 other = others[0]
281 return self.from_iterable(k for k in self if k in other)
282 return self.from_iterable(self.iter_intersection(*others))
283
284 def iter_difference(self, *others):
285 "iter_difference(*others) -> iterate over elements not in others"
286 for k in self:
287 for other in others:
288 if k in other:
289 break
290 else:
291 yield k
292 return
293
294 def difference(self, *others):
295 "difference(*others) -> get a new set with elements not in others"
296 if len(others) == 1:
297 other = others[0]
298 return self.from_iterable(k for k in self if k not in other)
299 return self.from_iterable(self.iter_difference(*others))
300
301 def symmetric_difference(self, *others):
302 "symmetric_difference(*others) -> XOR set of this and others"
303 ret = self.union(*others)
304 return ret.difference(self.intersection(*others))
305
306 __or__ = __ror__ = union
307 __and__ = __rand__ = intersection
308 __sub__ = difference
309 __xor__ = __rxor__ = symmetric_difference
310
311 def __rsub__(self, other):
312 vals = [x for x in other if x not in self]
313 return type(other)(vals)
314
315 # in-place set operations
316 def update(self, *others):
317 "update(*others) -> add values from one or more iterables"
318 if not others:
319 return # raise?
320 elif len(others) == 1:
321 other = others[0]
322 else:
323 other = chain(others)
324 for o in other:
325 self.add(o)
326
327 def intersection_update(self, *others):
328 "intersection_update(*others) -> discard self.difference(*others)"
329 for val in self.difference(*others):
330 self.discard(val)
331
332 def difference_update(self, *others):
333 "difference_update(*others) -> discard self.intersection(*others)"
334 if self in others:
335 self.clear()
336 for val in self.intersection(*others):
337 self.discard(val)
338
339 def symmetric_difference_update(self, other): # note singular 'other'
340 "symmetric_difference_update(other) -> in-place XOR with other"
341 if self is other:
342 self.clear()
343 for val in other:
344 if val in self:
345 self.discard(val)
346 else:
347 self.add(val)
348
349 def __ior__(self, *others):
350 self.update(*others)
351 return self
352
353 def __iand__(self, *others):
354 self.intersection_update(*others)
355 return self
356
357 def __isub__(self, *others):
358 self.difference_update(*others)
359 return self
360
361 def __ixor__(self, *others):
362 self.symmetric_difference_update(*others)
363 return self
364
365 def iter_slice(self, start, stop, step=None):
366 "iterate over a slice of the set"
367 iterable = self
368 if start is not None:
369 start = self._get_real_index(start)
370 if stop is not None:
371 stop = self._get_real_index(stop)
372 if step is not None and step < 0:
373 step = -step
374 iterable = reversed(self)
375 return islice(iterable, start, stop, step)
376
377 # list operations
378 def __getitem__(self, index):
379 try:
380 start, stop, step = index.start, index.stop, index.step
381 except AttributeError:
382 index = operator.index(index)
383 else:
384 iter_slice = self.iter_slice(start, stop, step)
385 return self.from_iterable(iter_slice)
386 if index < 0:
387 index += len(self)
388 real_index = self._get_real_index(index)
389 try:
390 ret = self.item_list[real_index]
391 except IndexError:
392 raise IndexError('IndexedSet index out of range')
393 return ret
394
395 def pop(self, index=None):
396 "pop(index) -> remove the item at a given index (-1 by default)"
397 item_index_map = self.item_index_map
398 len_self = len(item_index_map)
399 if index is None or index == -1 or index == len_self - 1:
400 ret = self.item_list.pop()
401 del item_index_map[ret]
402 else:
403 real_index = self._get_real_index(index)
404 ret = self.item_list[real_index]
405 self.item_list[real_index] = _MISSING
406 del item_index_map[ret]
407 self._add_dead(real_index)
408 self._cull()
409 return ret
410
411 def count(self, val):
412 "count(val) -> count number of instances of value (0 or 1)"
413 if val in self.item_index_map:
414 return 1
415 return 0
416
417 def reverse(self):
418 "reverse() -> reverse the contents of the set in-place"
419 reversed_list = list(reversed(self))
420 self.item_list[:] = reversed_list
421 for i, item in enumerate(self.item_list):
422 self.item_index_map[item] = i
423 del self.dead_indices[:]
424
425 def sort(self, **kwargs):
426 "sort() -> sort the contents of the set in-place"
427 sorted_list = sorted(self, **kwargs)
428 if sorted_list == self.item_list:
429 return
430 self.item_list[:] = sorted_list
431 for i, item in enumerate(self.item_list):
432 self.item_index_map[item] = i
433 del self.dead_indices[:]
434
435 def index(self, val):
436 "index(val) -> get the index of a value, raises if not present"
437 try:
438 return self._get_apparent_index(self.item_index_map[val])
439 except KeyError:
440 cn = self.__class__.__name__
441 raise ValueError('%r is not in %s' % (val, cn))
442
443
444 def complement(wrapped):
445 """Given a :class:`set`, convert it to a **complement set**.
446
447 Whereas a :class:`set` keeps track of what it contains, a
448 `complement set
449 <https://en.wikipedia.org/wiki/Complement_(set_theory)>`_ keeps
450 track of what it does *not* contain. For example, look what
451 happens when we intersect a normal set with a complement set::
452
453 >>> list(set(range(5)) & complement(set([2, 3])))
454 [0, 1, 4]
455
456 We get the everything in the left that wasn't in the right,
457 because intersecting with a complement is the same as subtracting
458 a normal set.
459
460 Args:
461 wrapped (set): A set or any other iterable which should be
462 turned into a complement set.
463
464 All set methods and operators are supported by complement sets,
465 between other :func:`complement`-wrapped sets and/or regular
466 :class:`set` objects.
467
468 Because a complement set only tracks what elements are *not* in
469 the set, functionality based on set contents is unavailable:
470 :func:`len`, :func:`iter` (and for loops), and ``.pop()``. But a
471 complement set can always be turned back into a regular set by
472 complementing it again:
473
474 >>> s = set(range(5))
475 >>> complement(complement(s)) == s
476 True
477
478 .. note::
479
480 An empty complement set corresponds to the concept of a
481 `universal set <https://en.wikipedia.org/wiki/Universal_set>`_
482 from mathematics.
483
484 Complement sets by example
485 ^^^^^^^^^^^^^^^^^^^^^^^^^^
486
487 Many uses of sets can be expressed more simply by using a
488 complement. Rather than trying to work out in your head the proper
489 way to invert an expression, you can just throw a complement on
490 the set. Consider this example of a name filter::
491
492 >>> class NamesFilter(object):
493 ... def __init__(self, allowed):
494 ... self._allowed = allowed
495 ...
496 ... def filter(self, names):
497 ... return [name for name in names if name in self._allowed]
498 >>> NamesFilter(set(['alice', 'bob'])).filter(['alice', 'bob', 'carol'])
499 ['alice', 'bob']
500
501 What if we want to just express "let all the names through"?
502
503 We could try to enumerate all of the expected names::
504
505 ``NamesFilter({'alice', 'bob', 'carol'})``
506
507 But this is very brittle -- what if at some point over this
508 object is changed to filter ``['alice', 'bob', 'carol', 'dan']``?
509
510 Even worse, what about the poor programmer who next works
511 on this piece of code? They cannot tell whether the purpose
512 of the large allowed set was "allow everything", or if 'dan'
513 was excluded for some subtle reason.
514
515 A complement set lets the programmer intention be expressed
516 succinctly and directly::
517
518 NamesFilter(complement(set()))
519
520 Not only is this code short and robust, it is easy to understand
521 the intention.
522
523 """
524 if type(wrapped) is _ComplementSet:
525 return wrapped.complemented()
526 if type(wrapped) is frozenset:
527 return _ComplementSet(excluded=wrapped)
528 return _ComplementSet(excluded=set(wrapped))
529
530
531 def _norm_args_typeerror(other):
532 '''normalize args and raise type-error if there is a problem'''
533 if type(other) in (set, frozenset):
534 inc, exc = other, None
535 elif type(other) is _ComplementSet:
536 inc, exc = other._included, other._excluded
537 else:
538 raise TypeError('argument must be another set or complement(set)')
539 return inc, exc
540
541
542 def _norm_args_notimplemented(other):
543 '''normalize args and return NotImplemented (for overloaded operators)'''
544 if type(other) in (set, frozenset):
545 inc, exc = other, None
546 elif type(other) is _ComplementSet:
547 inc, exc = other._included, other._excluded
548 else:
549 return NotImplemented, None
550 return inc, exc
551
552
553 class _ComplementSet(object):
554 """
555 helper class for complement() that implements the set methods
556 """
557 __slots__ = ('_included', '_excluded')
558
559 def __init__(self, included=None, excluded=None):
560 if included is None:
561 assert type(excluded) in (set, frozenset)
562 elif excluded is None:
563 assert type(included) in (set, frozenset)
564 else:
565 raise ValueError('one of included or excluded must be a set')
566 self._included, self._excluded = included, excluded
567
568 def __repr__(self):
569 if self._included is None:
570 return 'complement({0})'.format(repr(self._excluded))
571 return 'complement(complement({0}))'.format(repr(self._included))
572
573 def complemented(self):
574 '''return a complement of the current set'''
575 if type(self._included) is frozenset or type(self._excluded) is frozenset:
576 return _ComplementSet(included=self._excluded, excluded=self._included)
577 return _ComplementSet(
578 included=None if self._excluded is None else set(self._excluded),
579 excluded=None if self._included is None else set(self._included))
580
581 __invert__ = complemented
582
583 def complement(self):
584 '''convert the current set to its complement in-place'''
585 self._included, self._excluded = self._excluded, self._included
586
587 def __contains__(self, item):
588 if self._included is None:
589 return not item in self._excluded
590 return item in self._included
591
592 def add(self, item):
593 if self._included is None:
594 if item in self._excluded:
595 self._excluded.remove(item)
596 else:
597 self._included.add(item)
598
599 def remove(self, item):
600 if self._included is None:
601 self._excluded.add(item)
602 else:
603 self._included.remove(item)
604
605 def pop(self):
606 if self._included is None:
607 raise NotImplementedError # self.missing.add(random.choice(gc.objects()))
608 return self._included.pop()
609
610 def intersection(self, other):
611 try:
612 return self & other
613 except NotImplementedError:
614 raise TypeError('argument must be another set or complement(set)')
615
616 def __and__(self, other):
617 inc, exc = _norm_args_notimplemented(other)
618 if inc is NotImplemented:
619 return NotImplemented
620 if self._included is None:
621 if exc is None: # - +
622 return _ComplementSet(included=inc - self._excluded)
623 else: # - -
624 return _ComplementSet(excluded=self._excluded.union(other._excluded))
625 else:
626 if inc is None: # + -
627 return _ComplementSet(included=exc - self._included)
628 else: # + +
629 return _ComplementSet(included=self._included.intersection(inc))
630
631 __rand__ = __and__
632
633 def __iand__(self, other):
634 inc, exc = _norm_args_notimplemented(other)
635 if inc is NotImplemented:
636 return NotImplemented
637 if self._included is None:
638 if exc is None: # - +
639 self._excluded = inc - self._excluded # TODO: do this in place?
640 else: # - -
641 self._excluded |= exc
642 else:
643 if inc is None: # + -
644 self._included -= exc
645 self._included, self._excluded = None, self._included
646 else: # + +
647 self._included &= inc
648 return self
649
650 def union(self, other):
651 try:
652 return self | other
653 except NotImplementedError:
654 raise TypeError('argument must be another set or complement(set)')
655
656 def __or__(self, other):
657 inc, exc = _norm_args_notimplemented(other)
658 if inc is NotImplemented:
659 return NotImplemented
660 if self._included is None:
661 if exc is None: # - +
662 return _ComplementSet(excluded=self._excluded - inc)
663 else: # - -
664 return _ComplementSet(excluded=self._excluded.intersection(exc))
665 else:
666 if inc is None: # + -
667 return _ComplementSet(excluded=exc - self._included)
668 else: # + +
669 return _ComplementSet(included=self._included.union(inc))
670
671 __ror__ = __or__
672
673 def __ior__(self, other):
674 inc, exc = _norm_args_notimplemented(other)
675 if inc is NotImplemented:
676 return NotImplemented
677 if self._included is None:
678 if exc is None: # - +
679 self._excluded -= inc
680 else: # - -
681 self._excluded &= exc
682 else:
683 if inc is None: # + -
684 self._included, self._excluded = None, exc - self._included # TODO: do this in place?
685 else: # + +
686 self._included |= inc
687 return self
688
689 def update(self, items):
690 if type(items) in (set, frozenset):
691 inc, exc = items, None
692 elif type(items) is _ComplementSet:
693 inc, exc = items._included, items._excluded
694 else:
695 inc, exc = frozenset(items), None
696 if self._included is None:
697 if exc is None: # - +
698 self._excluded &= inc
699 else: # - -
700 self._excluded.discard(exc)
701 else:
702 if inc is None: # + -
703 self._included &= exc
704 self._included, self._excluded = None, self._excluded
705 else: # + +
706 self._included.update(inc)
707
708 def discard(self, items):
709 if type(items) in (set, frozenset):
710 inc, exc = items, None
711 elif type(items) is _ComplementSet:
712 inc, exc = items._included, items._excluded
713 else:
714 inc, exc = frozenset(items), None
715 if self._included is None:
716 if exc is None: # - +
717 self._excluded.update(inc)
718 else: # - -
719 self._included, self._excluded = exc - self._excluded, None
720 else:
721 if inc is None: # + -
722 self._included &= exc
723 else: # + +
724 self._included.discard(inc)
725
726 def symmetric_difference(self, other):
727 try:
728 return self ^ other
729 except NotImplementedError:
730 raise TypeError('argument must be another set or complement(set)')
731
732 def __xor__(self, other):
733 inc, exc = _norm_args_notimplemented(other)
734 if inc is NotImplemented:
735 return NotImplemented
736 if inc is NotImplemented:
737 return NotImplemented
738 if self._included is None:
739 if exc is None: # - +
740 return _ComplementSet(excluded=self._excluded - inc)
741 else: # - -
742 return _ComplementSet(included=self._excluded.symmetric_difference(exc))
743 else:
744 if inc is None: # + -
745 return _ComplementSet(excluded=exc - self._included)
746 else: # + +
747 return _ComplementSet(included=self._included.symmetric_difference(inc))
748
749 __rxor__ = __xor__
750
751 def symmetric_difference_update(self, other):
752 inc, exc = _norm_args_typeerror(other)
753 if self._included is None:
754 if exc is None: # - +
755 self._excluded |= inc
756 else: # - -
757 self._excluded.symmetric_difference_update(exc)
758 self._included, self._excluded = self._excluded, None
759 else:
760 if inc is None: # + -
761 self._included |= exc
762 self._included, self._excluded = None, self._included
763 else: # + +
764 self._included.symmetric_difference_update(inc)
765
766 def isdisjoint(self, other):
767 inc, exc = _norm_args_typeerror(other)
768 if inc is NotImplemented:
769 return NotImplemented
770 if self._included is None:
771 if exc is None: # - +
772 return inc.issubset(self._excluded)
773 else: # - -
774 return False
775 else:
776 if inc is None: # + -
777 return self._included.issubset(exc)
778 else: # + +
779 return self._included.isdisjoint(inc)
780
781 def issubset(self, other):
782 '''everything missing from other is also missing from self'''
783 try:
784 return self <= other
785 except NotImplementedError:
786 raise TypeError('argument must be another set or complement(set)')
787
788 def __le__(self, other):
789 inc, exc = _norm_args_notimplemented(other)
790 if inc is NotImplemented:
791 return NotImplemented
792 if inc is NotImplemented:
793 return NotImplemented
794 if self._included is None:
795 if exc is None: # - +
796 return False
797 else: # - -
798 return self._excluded.issupserset(exc)
799 else:
800 if inc is None: # + -
801 return self._included.isdisjoint(exc)
802 else: # + +
803 return self._included.issubset(inc)
804
805 def __lt__(self, other):
806 inc, exc = _norm_args_notimplemented(other)
807 if inc is NotImplemented:
808 return NotImplemented
809 if inc is NotImplemented:
810 return NotImplemented
811 if self._included is None:
812 if exc is None: # - +
813 return False
814 else: # - -
815 return self._excluded > exc
816 else:
817 if inc is None: # + -
818 return self._included.isdisjoint(exc)
819 else: # + +
820 return self._included < inc
821
822 def issuperset(self, other):
823 '''everything missing from self is also missing from super'''
824 try:
825 return self >= other
826 except NotImplementedError:
827 raise TypeError('argument must be another set or complement(set)')
828
829 def __ge__(self, other):
830 inc, exc = _norm_args_notimplemented(other)
831 if inc is NotImplemented:
832 return NotImplemented
833 if self._included is None:
834 if exc is None: # - +
835 return not self._excluded.intersection(inc)
836 else: # - -
837 return self._excluded.issubset(exc)
838 else:
839 if inc is None: # + -
840 return False
841 else: # + +
842 return self._included.issupserset(inc)
843
844 def __gt__(self, other):
845 inc, exc = _norm_args_notimplemented(other)
846 if inc is NotImplemented:
847 return NotImplemented
848 if self._included is None:
849 if exc is None: # - +
850 return not self._excluded.intersection(inc)
851 else: # - -
852 return self._excluded < exc
853 else:
854 if inc is None: # + -
855 return False
856 else: # + +
857 return self._included > inc
858
859 def difference(self, other):
860 try:
861 return self - other
862 except NotImplementedError:
863 raise TypeError('argument must be another set or complement(set)')
864
865 def __sub__(self, other):
866 inc, exc = _norm_args_notimplemented(other)
867 if inc is NotImplemented:
868 return NotImplemented
869 if self._included is None:
870 if exc is None: # - +
871 return _ComplementSet(excluded=self._excluded | inc)
872 else: # - -
873 return _ComplementSet(included=exc - self._excluded)
874 else:
875 if inc is None: # + -
876 return _ComplementSet(included=self._included & exc)
877 else: # + +
878 return _ComplementSet(included=self._included.difference(inc))
879
880 def __rsub__(self, other):
881 inc, exc = _norm_args_notimplemented(other)
882 if inc is NotImplemented:
883 return NotImplemented
884 # rsub, so the expression being evaluated is "other - self"
885 if self._included is None:
886 if exc is None: # - +
887 return _ComplementSet(included=inc & self._excluded)
888 else: # - -
889 return _ComplementSet(included=self._excluded - exc)
890 else:
891 if inc is None: # + -
892 return _ComplementSet(excluded=exc | self._included)
893 else: # + +
894 return _ComplementSet(included=inc.difference(self._included))
895
896 def difference_update(self, other):
897 try:
898 self -= other
899 except NotImplementedError:
900 raise TypeError('argument must be another set or complement(set)')
901
902 def __isub__(self, other):
903 inc, exc = _norm_args_notimplemented(other)
904 if inc is NotImplemented:
905 return NotImplemented
906 if self._included is None:
907 if exc is None: # - +
908 self._excluded |= inc
909 else: # - -
910 self._included, self._excluded = exc - self._excluded, None
911 else:
912 if inc is None: # + -
913 self._included &= exc
914 else: # + +
915 self._included.difference_update(inc)
916 return self
917
918 def __eq__(self, other):
919 return (
920 type(self) is type(other)
921 and self._included == other._included
922 and self._excluded == other._excluded) or (
923 type(other) in (set, frozenset) and self._included == other)
924
925 def __hash__(self):
926 return hash(self._included) ^ hash(self._excluded)
927
928 def __len__(self):
929 if self._included is not None:
930 return len(self._included)
931 raise NotImplementedError('complemented sets have undefined length')
932
933 def __iter__(self):
934 if self._included is not None:
935 return iter(self._included)
936 raise NotImplementedError('complemented sets have undefined contents')
937
938 def __bool__(self):
939 if self._included is not None:
940 return bool(self._included)
941 return True
942
943 __nonzero__ = __bool__ # py2 compat