From 1ff8bf2c6b8dcbef4ae837e656a2718cecdb5d7a Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 20 Jun 2016 17:03:45 +0300 Subject: [PATCH 001/112] - --- .../python_toolbox/misc_tools/misc_tools.py | 22 +++++++++++++++++++ .../test_add_extension_if_plain.py | 18 +++++++++------ .../test_phrase_iterable_in_english.py | 17 ++++++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 source_py3/test_python_toolbox/test_misc_tools/test_phrase_iterable_in_english.py diff --git a/source_py3/python_toolbox/misc_tools/misc_tools.py b/source_py3/python_toolbox/misc_tools/misc_tools.py index 90a90fa90..c77e3fa44 100644 --- a/source_py3/python_toolbox/misc_tools/misc_tools.py +++ b/source_py3/python_toolbox/misc_tools/misc_tools.py @@ -353,3 +353,25 @@ def __bool__(self): from python_toolbox import sequence_tools return bool(sequence_tools.get_length(self)) +def phrase_iterable_in_english(iterable): + from python_toolbox import sequence_tools + from python_toolbox import combi + sequence = combi.MapSpace(str, iterable) + if len(sequence) == 0: + return '' + elif len(sequence) == 1: + (item,) = sequence + return item + elif len(sequence) == 2: + return ' and '.join(sequence) + else: + assert len(sequence) >= 3 + return '%s and %s' % (', '.join(sequence[:-1]), sequence[-1]) + + + + + + + + \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_misc_tools/test_add_extension_if_plain.py b/source_py3/test_python_toolbox/test_misc_tools/test_add_extension_if_plain.py index 1306b5d19..9a5aaf6f9 100644 --- a/source_py3/test_python_toolbox/test_misc_tools/test_add_extension_if_plain.py +++ b/source_py3/test_python_toolbox/test_misc_tools/test_add_extension_if_plain.py @@ -5,13 +5,17 @@ from python_toolbox import temp_file_tools -from python_toolbox.misc_tools import add_extension_if_plain +from python_toolbox.misc_tools import phrase_iterable_in_english def test(): - assert str(add_extension_if_plain(r'''c:\hello.zip''', '.ogg')) == \ - r'''c:\hello.zip''' - assert str(add_extension_if_plain(r'''c:\hello''', '.ogg')) == \ - r'''c:\hello.ogg''' - assert str(add_extension_if_plain(r'''c:\hello''', '.mkv')) == \ - r'''c:\hello.mkv''' \ No newline at end of file + iterables_and_results = ( + ((), ''), + (['foo'], 'foo'), + ((1, 2), '1 and 2'), + ((1, 2, 'meow'), '1, 2 and meow'), + (iter((1, 2, 'meow')), '1, 2 and meow'), + ([{'a'}, {'b'}, {'c'}, {'d'}], "{'a'}, {'b'}, {'c'} and {'d'}"), + ) + for iterable, result in iterables_and_results: + assert phrase_iterable_in_english(iterable) == result \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_misc_tools/test_phrase_iterable_in_english.py b/source_py3/test_python_toolbox/test_misc_tools/test_phrase_iterable_in_english.py new file mode 100644 index 000000000..1306b5d19 --- /dev/null +++ b/source_py3/test_python_toolbox/test_misc_tools/test_phrase_iterable_in_english.py @@ -0,0 +1,17 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import nose.tools + +from python_toolbox import temp_file_tools + +from python_toolbox.misc_tools import add_extension_if_plain + + +def test(): + assert str(add_extension_if_plain(r'''c:\hello.zip''', '.ogg')) == \ + r'''c:\hello.zip''' + assert str(add_extension_if_plain(r'''c:\hello''', '.ogg')) == \ + r'''c:\hello.ogg''' + assert str(add_extension_if_plain(r'''c:\hello''', '.mkv')) == \ + r'''c:\hello.mkv''' \ No newline at end of file From f7912dbdb367e25f0d55792d6d878e781390557d Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 20 Jun 2016 17:07:58 +0300 Subject: [PATCH 002/112] - --- source_py3/python_toolbox/misc_tools/misc_tools.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source_py3/python_toolbox/misc_tools/misc_tools.py b/source_py3/python_toolbox/misc_tools/misc_tools.py index c77e3fa44..97a0f980f 100644 --- a/source_py3/python_toolbox/misc_tools/misc_tools.py +++ b/source_py3/python_toolbox/misc_tools/misc_tools.py @@ -354,6 +354,16 @@ def __bool__(self): return bool(sequence_tools.get_length(self)) def phrase_iterable_in_english(iterable): + ''' + Get a nice textual output for a bunch of items. + + Example: + + >>> phrase_iterable_in_english(('foo', 'bar', 'baz)) + 'foo, bar and baz' + + ''' + from python_toolbox import sequence_tools from python_toolbox import combi sequence = combi.MapSpace(str, iterable) From f60128faad78bb86b0064de83dabbf9d40897bdb Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 20 Jun 2016 17:56:50 +0300 Subject: [PATCH 003/112] - --- misc/IDE files/Wing/python_toolbox_py3.wpr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/IDE files/Wing/python_toolbox_py3.wpr b/misc/IDE files/Wing/python_toolbox_py3.wpr index a14503e2f..0a104a906 100644 --- a/misc/IDE files/Wing/python_toolbox_py3.wpr +++ b/misc/IDE files/Wing/python_toolbox_py3.wpr @@ -1,5 +1,5 @@ #!wing -#!version=5.0 +#!version=6.0 ################################################################## # Wing IDE project file # ################################################################## From 43acd62298facac86b0f7dbc3e01471403184269 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 20 Jun 2016 17:58:29 +0300 Subject: [PATCH 004/112] - --- source_py3/python_toolbox/combi/selection_space.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source_py3/python_toolbox/combi/selection_space.py b/source_py3/python_toolbox/combi/selection_space.py index a5d7f5700..9feb5fa63 100644 --- a/source_py3/python_toolbox/combi/selection_space.py +++ b/source_py3/python_toolbox/combi/selection_space.py @@ -7,7 +7,7 @@ class SelectionSpace(sequence_tools.CuteSequenceMixin, - collections.Sequence): + collections.abc.Sequence): ''' Space of possible selections of any number of items from `sequence`. @@ -74,7 +74,7 @@ def __getitem__(self, i): def index(self, selection): '''Find the index number of `selection` in this `SelectionSpace`.''' - if not isinstance(selection, collections.Iterable): + if not isinstance(selection, collections.abc.Iterable): raise ValueError selection_set = set(selection) From 2e73944f3c7ec9247e76e97175bdf42bd764acfc Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 20 Jun 2016 18:27:02 +0300 Subject: [PATCH 005/112] - --- .../test_nifty_collections/test_bagging.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py b/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py index c418243e5..8b6d7752c 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py @@ -87,7 +87,7 @@ def test_bool(self): assert bag assert bool(self.bag_type()) is bool(self.bag_type('')) is \ bool(self.bag_type({'d': 0,})) is False - if not isinstance(bag, collections.Hashable): + if not isinstance(bag, collections.abc.Hashable): bag.clear() assert bool(bag) is False assert not bag @@ -98,7 +98,7 @@ def test_n_elements(self): assert bag.n_elements == 4 assert bag.n_elements == 4 # Testing again because now it's a data # attribute. - if not isinstance(bag, collections.Hashable): + if not isinstance(bag, collections.abc.Hashable): bag['x'] = 1 assert bag.n_elements == 5 assert bag.n_elements == 5 @@ -108,7 +108,7 @@ def test_frozen_bag_bag(self): bag = self.bag_type('meeeow') assert bag.frozen_bag_bag == \ nifty_collections.FrozenBagBag({3: 1, 1: 3,}) - if not isinstance(bag, collections.Hashable): + if not isinstance(bag, collections.abc.Hashable): bag['o'] += 2 assert bag.frozen_bag_bag == \ nifty_collections.FrozenBagBag({3: 2, 1: 2,}) @@ -219,7 +219,7 @@ def test_ignores_zero(self): bag_1 = self.bag_type() assert bag_0 == bag_1 - if issubclass(self.bag_type, collections.Hashable): + if issubclass(self.bag_type, collections.abc.Hashable): assert hash(bag_0) == hash(bag_1) assert {bag_0, bag_1} == {bag_0} == {bag_1} @@ -227,7 +227,7 @@ def test_ignores_zero(self): self.bag_type({'a': 0.0, 'b': 2, 'c': decimal_module.Decimal('0.0'),}) bag_3 = self.bag_type('bb') - if issubclass(self.bag_type, collections.Hashable): + if issubclass(self.bag_type, collections.abc.Hashable): assert hash(bag_2) == hash(bag_3) assert {bag_2, bag_3} == {bag_2} == {bag_3} @@ -283,7 +283,7 @@ def test_operations_with_foreign_operands(self): with cute_testing.RaiseAssertor(TypeError): 'foo' ** bag with cute_testing.RaiseAssertor(TypeError): divmod(bag, 'foo') with cute_testing.RaiseAssertor(TypeError): divmod('foo', bag) - if not isinstance(bag, collections.Hashable): + if not isinstance(bag, collections.abc.Hashable): with cute_testing.RaiseAssertor(TypeError): bag |= 'foo' with cute_testing.RaiseAssertor(TypeError): bag &= 'foo' with cute_testing.RaiseAssertor(TypeError): bag += 'foo' @@ -417,7 +417,7 @@ def test_get_mutable(self): def test_get_frozen(self): bag = self.bag_type('abracadabra') frozen_bag = bag.get_frozen() - assert isinstance(frozen_bag, collections.Hashable) + assert isinstance(frozen_bag, collections.abc.Hashable) if isinstance(bag, nifty_collections.Ordered): assert tuple(bag.items()) == tuple(frozen_bag.items()) else: @@ -427,8 +427,8 @@ def test_get_frozen(self): def test_hash(self): bag = self.bag_type('abracadabra') - assert not isinstance(bag, collections.Hashable) - assert not issubclass(self.bag_type, collections.Hashable) + assert not isinstance(bag, collections.abc.Hashable) + assert not issubclass(self.bag_type, collections.abc.Hashable) with cute_testing.RaiseAssertor(TypeError): {bag} with cute_testing.RaiseAssertor(TypeError): @@ -593,7 +593,7 @@ class BaseFrozenBagTestCase(BaseBagTestCase): def test_get_mutable(self): bag = self.bag_type('abracadabra') mutable_bag = bag.get_mutable() - assert not isinstance(mutable_bag, collections.Hashable) + assert not isinstance(mutable_bag, collections.abc.Hashable) if isinstance(bag, nifty_collections.Ordered): assert tuple(bag.items()) == tuple(mutable_bag.items()) else: @@ -611,8 +611,8 @@ def test_get_frozen(self): def test_hash(self): bag = self.bag_type('abracadabra') - assert isinstance(bag, collections.Hashable) - assert issubclass(self.bag_type, collections.Hashable) + assert isinstance(bag, collections.abc.Hashable) + assert issubclass(self.bag_type, collections.abc.Hashable) assert {bag, bag} == {bag} assert {bag: bag} == {bag: bag} assert isinstance(hash(bag), int) @@ -721,7 +721,7 @@ def test_reversed(self): # Cached only for a frozen type: assert (bag.reversed is bag.reversed) == \ (bag.reversed.reversed is bag.reversed.reversed) == \ - isinstance(bag, collections.Hashable) + isinstance(bag, collections.abc.Hashable) assert bag.reversed == bag.reversed assert bag.reversed.reversed == bag.reversed.reversed @@ -741,10 +741,10 @@ def test_ordering(self): ordered_bag_0 = self.bag_type('ababb') ordered_bag_1 = self.bag_type('bbbaa') assert ordered_bag_0 == ordered_bag_0 - if issubclass(self.bag_type, collections.Hashable): + if issubclass(self.bag_type, collections.abc.Hashable): assert hash(ordered_bag_0) == hash(ordered_bag_0) assert ordered_bag_1 == ordered_bag_1 - if issubclass(self.bag_type, collections.Hashable): + if issubclass(self.bag_type, collections.abc.Hashable): assert hash(ordered_bag_1) == hash(ordered_bag_1) assert ordered_bag_0 != ordered_bag_1 assert ordered_bag_0 <= ordered_bag_1 @@ -758,7 +758,7 @@ def test_builtin_reversed(self): def test_index(self): bag = self.bag_type('aaabbc') - if not isinstance(bag, collections.Hashable): + if not isinstance(bag, collections.abc.Hashable): bag['d'] = 0 assert bag.index('a') == 0 assert bag.index('b') == 1 @@ -784,7 +784,7 @@ def test_ordering(self): bag_0 = self.bag_type('ababb') bag_1 = self.bag_type('bbbaa') assert bag_0 == bag_1 - if issubclass(self.bag_type, collections.Hashable): + if issubclass(self.bag_type, collections.abc.Hashable): assert hash(bag_0) == hash(bag_1) @@ -796,7 +796,7 @@ def test_builtin_reversed(self): def test_index(self): bag = self.bag_type('aaabbc') - if not isinstance(bag, collections.Hashable): + if not isinstance(bag, collections.abc.Hashable): bag['d'] = 0 with cute_testing.RaiseAssertor(AttributeError): bag.index('a') From d8e3116e1df3adc70b533c367f721f4abf31af58 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 20 Jun 2016 18:27:24 +0300 Subject: [PATCH 006/112] - --- source_py3/python_toolbox/combi/chain_space.py | 2 +- source_py3/python_toolbox/combi/map_space.py | 2 +- .../python_toolbox/combi/perming/perm.py | 8 ++++---- .../python_toolbox/combi/perming/perm_space.py | 10 +++++----- .../python_toolbox/combi/product_space.py | 4 ++-- source_py3/python_toolbox/dict_tools.py | 6 +++--- source_py3/python_toolbox/emitting/emitter.py | 6 +++--- source_py3/python_toolbox/logic_tools.py | 4 ++-- .../python_toolbox/math_tools/factorials.py | 2 +- .../nifty_collections/abstract.py | 2 +- .../nifty_collections/bagging.py | 4 ++-- .../nifty_collections/lazy_tuple.py | 8 +++++--- .../nifty_collections/various_frozen_dicts.py | 6 +++--- .../nifty_collections/various_ordered_sets.py | 4 ++-- .../nifty_collections/weak_key_default_dict.py | 2 +- .../weak_key_identity_dict.py | 2 +- .../sequence_tools/canonical_slice.py | 4 ++-- .../python_toolbox/sequence_tools/misc.py | 18 +++++++++--------- .../cute_window/accelerator_savvy_window.py | 2 +- .../test_combi/test_extensive.py | 2 +- .../test_iterate_overlapping_subsequences.py | 2 +- .../test_lazy_tuple/test_lazy_tuple.py | 2 +- 22 files changed, 52 insertions(+), 50 deletions(-) diff --git a/source_py3/python_toolbox/combi/chain_space.py b/source_py3/python_toolbox/combi/chain_space.py index 24b5bf0c8..30ed6064b 100644 --- a/source_py3/python_toolbox/combi/chain_space.py +++ b/source_py3/python_toolbox/combi/chain_space.py @@ -14,7 +14,7 @@ -class ChainSpace(sequence_tools.CuteSequenceMixin, collections.Sequence): +class ChainSpace(sequence_tools.CuteSequenceMixin, collections.abc.Sequence): ''' A space of sequences chained together. diff --git a/source_py3/python_toolbox/combi/map_space.py b/source_py3/python_toolbox/combi/map_space.py index 2ee42527c..14450f073 100644 --- a/source_py3/python_toolbox/combi/map_space.py +++ b/source_py3/python_toolbox/combi/map_space.py @@ -11,7 +11,7 @@ -class MapSpace(sequence_tools.CuteSequenceMixin, collections.Sequence): +class MapSpace(sequence_tools.CuteSequenceMixin, collections.abc.Sequence): ''' A space of a function applied to a sequence. diff --git a/source_py3/python_toolbox/combi/perming/perm.py b/source_py3/python_toolbox/combi/perming/perm.py index fe71b9fab..70bc566b2 100644 --- a/source_py3/python_toolbox/combi/perming/perm.py +++ b/source_py3/python_toolbox/combi/perming/perm.py @@ -31,7 +31,7 @@ def __getitem__(self, i): pass class PermItems(sequence_tools.CuteSequenceMixin, _BasePermView, - collections.Sequence): + collections.abc.Sequence): ''' A viewer of a perm's items, similar to `dict.items()`. @@ -46,7 +46,7 @@ def __getitem__(self, i): class PermAsDictoid(sequence_tools.CuteSequenceMixin, _BasePermView, - collections.Mapping): + collections.abc.Mapping): '''A dict-like interface to a `Perm`.''' def __getitem__(self, key): return self.perm[key] @@ -69,7 +69,7 @@ def __call__(cls, item, perm_space=None): @functools.total_ordering -class Perm(sequence_tools.CuteSequenceMixin, collections.Sequence, +class Perm(sequence_tools.CuteSequenceMixin, collections.abc.Sequence, metaclass=PermType): ''' A permutation of items from a `PermSpace`. @@ -107,7 +107,7 @@ def __init__(self, perm_sequence, perm_space=None): ''' perm_space = None if perm_space is None \ else PermSpace.coerce(perm_space) - assert isinstance(perm_sequence, collections.Iterable) + assert isinstance(perm_sequence, collections.abc.Iterable) perm_sequence = sequence_tools. \ ensure_iterable_is_immutable_sequence(perm_sequence) diff --git a/source_py3/python_toolbox/combi/perming/perm_space.py b/source_py3/python_toolbox/combi/perming/perm_space.py index 82e86eeb6..4e4891817 100644 --- a/source_py3/python_toolbox/combi/perming/perm_space.py +++ b/source_py3/python_toolbox/combi/perming/perm_space.py @@ -59,7 +59,7 @@ def __call__(cls, *args, **kwargs): class PermSpace(_VariationRemovingMixin, _VariationAddingMixin, _FixedMapManagingMixin, sequence_tools.CuteSequenceMixin, - collections.Sequence, metaclass=PermSpaceType): + collections.abc.Sequence, metaclass=PermSpaceType): ''' A space of permutations on a sequence. @@ -165,7 +165,7 @@ def __init__(self, iterable_or_length, n_elements=None, *, domain=None, # # assert isinstance( iterable_or_length, - (collections.Iterable, numbers.Integral) + (collections.abc.Iterable, numbers.Integral) ) if isinstance(iterable_or_length, numbers.Integral): assert iterable_or_length >= 0 @@ -186,7 +186,7 @@ def __init__(self, iterable_or_length, n_elements=None, *, domain=None, self.sequence = sequence_tools.CuteRange(iterable_or_length) self.sequence_length = iterable_or_length else: - assert isinstance(iterable_or_length, collections.Iterable) + assert isinstance(iterable_or_length, collections.abc.Iterable) self.sequence = sequence_tools. \ ensure_iterable_is_immutable_sequence(iterable_or_length) range_candidate = sequence_tools.CuteRange(len(self.sequence)) @@ -268,7 +268,7 @@ def __init__(self, iterable_or_length, n_elements=None, *, domain=None, if fixed_map is None: fixed_map = {} if not isinstance(fixed_map, dict): - if isinstance(fixed_map, collections.Callable): + if isinstance(fixed_map, collections.abc.Callable): fixed_map = {item: fixed_map(item) for item in self.sequence} else: fixed_map = dict(fixed_map) @@ -754,7 +754,7 @@ def __getitem__(self, i): def index(self, perm): '''Get the index number of permutation `perm` in this space.''' - if not isinstance(perm, collections.Iterable): + if not isinstance(perm, collections.abc.Iterable): raise ValueError try: diff --git a/source_py3/python_toolbox/combi/product_space.py b/source_py3/python_toolbox/combi/product_space.py index a88e88dd7..9da04d935 100644 --- a/source_py3/python_toolbox/combi/product_space.py +++ b/source_py3/python_toolbox/combi/product_space.py @@ -7,7 +7,7 @@ from python_toolbox import sequence_tools -class ProductSpace(sequence_tools.CuteSequenceMixin, collections.Sequence): +class ProductSpace(sequence_tools.CuteSequenceMixin, collections.abc.Sequence): ''' A product space between sequences. @@ -70,7 +70,7 @@ def __getitem__(self, i): def index(self, given_sequence): '''Get the index number of `given_sequence` in this product space.''' - if not isinstance(given_sequence, collections.Sequence) or \ + if not isinstance(given_sequence, collections.abc.Sequence) or \ not len(given_sequence) == len(self.sequences): raise ValueError diff --git a/source_py3/python_toolbox/dict_tools.py b/source_py3/python_toolbox/dict_tools.py index b2201e4dc..8089f5583 100644 --- a/source_py3/python_toolbox/dict_tools.py +++ b/source_py3/python_toolbox/dict_tools.py @@ -118,17 +118,17 @@ def remove_keys(d, keys_to_remove): If key doesn't exist, doesn't raise an exception. ''' - if isinstance(keys_to_remove, collections.Iterable): + if isinstance(keys_to_remove, collections.abc.Iterable): for key in keys_to_remove: try: del d[key] except KeyError: pass else: - if isinstance(keys_to_remove, collections.Container): + if isinstance(keys_to_remove, collections.abc.Container): filter_function = lambda value: value in keys_to_remove else: - assert isinstance(keys_to_remove, collections.Callable) + assert isinstance(keys_to_remove, collections.abc.Callable) filter_function = keys_to_remove for key in list(d.keys()): if filter_function(key): diff --git a/source_py3/python_toolbox/emitting/emitter.py b/source_py3/python_toolbox/emitting/emitter.py index 4957a02b4..689b1c391 100644 --- a/source_py3/python_toolbox/emitting/emitter.py +++ b/source_py3/python_toolbox/emitting/emitter.py @@ -72,7 +72,7 @@ def __init__(self, inputs=(), outputs=(), name=None): inputs = sequence_tools.to_tuple(inputs, item_type=Emitter) outputs = sequence_tools.to_tuple(outputs, - item_type=(collections.Callable, + item_type=(collections.abc.Callable, Emitter)) self._inputs = set() @@ -220,7 +220,7 @@ def add_output(self, thing): If adding an emitter, every time this emitter will emit the output emitter will emit as well. ''' - assert isinstance(thing, (Emitter, collections.Callable)) + assert isinstance(thing, (Emitter, collections.abc.Callable)) self._outputs.add(thing) if isinstance(thing, Emitter): thing._inputs.add(self) @@ -228,7 +228,7 @@ def add_output(self, thing): def remove_output(self, thing): '''Remove an output from this emitter.''' - assert isinstance(thing, (Emitter, collections.Callable)) + assert isinstance(thing, (Emitter, collections.abc.Callable)) self._outputs.remove(thing) if isinstance(thing, Emitter): thing._inputs.remove(self) diff --git a/source_py3/python_toolbox/logic_tools.py b/source_py3/python_toolbox/logic_tools.py index bd8556fdd..0154755a6 100644 --- a/source_py3/python_toolbox/logic_tools.py +++ b/source_py3/python_toolbox/logic_tools.py @@ -105,7 +105,7 @@ def get_equivalence_classes(iterable, key=None, container=set, *, ### Pre-processing input: ################################################# # # if key is None: - if isinstance(iterable, collections.Mapping): + if isinstance(iterable, collections.abc.Mapping): d = iterable else: try: @@ -137,7 +137,7 @@ def get_equivalence_classes(iterable, key=None, container=set, *, new_dict[key] = container(value) if sort_ordered_dict: - if isinstance(sort_ordered_dict, (collections.Callable, str)): + if isinstance(sort_ordered_dict, (collections.abc.Callable, str)): key_function = comparison_tools. \ process_key_function_or_attribute_name(sort_ordered_dict) new_dict.sort(key_function) diff --git a/source_py3/python_toolbox/math_tools/factorials.py b/source_py3/python_toolbox/math_tools/factorials.py index 22be704bb..18acdff27 100644 --- a/source_py3/python_toolbox/math_tools/factorials.py +++ b/source_py3/python_toolbox/math_tools/factorials.py @@ -76,7 +76,7 @@ def from_factoradic(factoradic_number): ''' from python_toolbox import sequence_tools - assert isinstance(factoradic_number, collections.Iterable) + assert isinstance(factoradic_number, collections.abc.Iterable) factoradic_number = \ sequence_tools.ensure_iterable_is_sequence(factoradic_number) number = 0 diff --git a/source_py3/python_toolbox/nifty_collections/abstract.py b/source_py3/python_toolbox/nifty_collections/abstract.py index b39b823ca..56c8ac46c 100644 --- a/source_py3/python_toolbox/nifty_collections/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/abstract.py @@ -20,7 +20,7 @@ class Ordered(metaclass=abc.ABCMeta): __slots__ = () -Ordered.register(collections.Sequence) +Ordered.register(collections.abc.Sequence) Ordered.register(collections.OrderedDict) Ordered.register(collections.deque) Ordered.register(queue.Queue) diff --git a/source_py3/python_toolbox/nifty_collections/bagging.py b/source_py3/python_toolbox/nifty_collections/bagging.py index 11ebd20f5..2cded1779 100644 --- a/source_py3/python_toolbox/nifty_collections/bagging.py +++ b/source_py3/python_toolbox/nifty_collections/bagging.py @@ -148,7 +148,7 @@ class _BaseBagMixin: def __init__(self, iterable={}): super().__init__() - if isinstance(iterable, collections.Mapping): + if isinstance(iterable, collections.abc.Mapping): for key, value, in iterable.items(): try: self._dict[key] = _process_count(value) @@ -824,7 +824,7 @@ def get_contained_bags(self): -class _BaseDictDelegator(collections.MutableMapping): +class _BaseDictDelegator(collections.abc.MutableMapping): ''' Base class for a dict-like object. diff --git a/source_py3/python_toolbox/nifty_collections/lazy_tuple.py b/source_py3/python_toolbox/nifty_collections/lazy_tuple.py index 86469e056..1bf2593c5 100644 --- a/source_py3/python_toolbox/nifty_collections/lazy_tuple.py +++ b/source_py3/python_toolbox/nifty_collections/lazy_tuple.py @@ -45,7 +45,7 @@ def _with_lock(method, *args, **kwargs): @functools.total_ordering -class LazyTuple(collections.Sequence): +class LazyTuple(collections.abc.Sequence): ''' A lazy tuple which requests as few values as possible from its iterator. @@ -75,8 +75,10 @@ def my_generator(): ''' def __init__(self, iterable, definitely_infinite=False): - was_given_a_sequence = isinstance(iterable, collections.Sequence) and \ - not isinstance(iterable, LazyTuple) + was_given_a_sequence = ( + isinstance(iterable, collections.abc.Sequence) and + not isinstance(iterable, LazyTuple) + ) self.is_exhausted = True if was_given_a_sequence else False '''Flag saying whether the internal iterator is tobag exhausted.''' diff --git a/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py b/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py index 5f7c96559..33f3f0e9a 100644 --- a/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py @@ -10,7 +10,7 @@ from .ordered_dict import OrderedDict -class _AbstractFrozenDict(collections.Mapping): +class _AbstractFrozenDict(collections.abc.Mapping): _hash = None # Overridden by instance when calculating hash. def __init__(self, *args, **kwargs): @@ -70,9 +70,9 @@ class FrozenOrderedDict(Ordered, _AbstractFrozenDict): def __eq__(self, other): if isinstance(other, (OrderedDict, FrozenOrderedDict)): - return collections.Mapping.__eq__(self, other) and \ + return collections.abc.Mapping.__eq__(self, other) and \ all(map(operator.eq, self, other)) - return collections.Mapping.__eq__(self, other) + return collections.abc.Mapping.__eq__(self, other) __hash__ = _AbstractFrozenDict.__hash__ # (Gotta manually carry `__hash__` over from the base class because setting diff --git a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py index fbc7a423a..f1f64fe5f 100644 --- a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py +++ b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py @@ -15,7 +15,7 @@ KEY, PREV, NEXT = range(3) -class BaseOrderedSet(collections.Set, collections.Sequence): +class BaseOrderedSet(collections.abc.Set, collections.abc.Sequence): ''' Base class for `OrderedSet` and `FrozenOrderedSet`, i.e. set with an order. @@ -109,7 +109,7 @@ def __hash__(self): -class OrderedSet(BaseOrderedSet, collections.MutableSet): +class OrderedSet(BaseOrderedSet, collections.abc.MutableSet): ''' A `set` with an order. diff --git a/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py b/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py index 81f63e642..e0d270608 100644 --- a/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py +++ b/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py @@ -13,7 +13,7 @@ #todo: needs testing -class WeakKeyDefaultDict(collections.MutableMapping): +class WeakKeyDefaultDict(collections.abc.MutableMapping): ''' A weak key dictionary which can use a default factory. diff --git a/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py b/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py index 7722b27eb..508f1346e 100644 --- a/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py +++ b/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py @@ -27,7 +27,7 @@ def __hash__(self): return self._hash -class WeakKeyIdentityDict(collections.MutableMapping): +class WeakKeyIdentityDict(collections.abc.MutableMapping): """ A weak key dictionary which cares about the keys' identities. diff --git a/source_py3/python_toolbox/sequence_tools/canonical_slice.py b/source_py3/python_toolbox/sequence_tools/canonical_slice.py index 3ed36d063..d2a26152d 100644 --- a/source_py3/python_toolbox/sequence_tools/canonical_slice.py +++ b/source_py3/python_toolbox/sequence_tools/canonical_slice.py @@ -47,10 +47,10 @@ def __init__(self, slice_, iterable_or_length=None, offset=0): if isinstance(iterable_or_length, math_tools.PossiblyInfiniteIntegral): self.length = iterable_or_length - elif isinstance(iterable_or_length, collections.Sequence): + elif isinstance(iterable_or_length, collections.abc.Sequence): self.length = sequence_tools.get_length(iterable_or_length) else: - assert isinstance(iterable_or_length, collections.Iterable) + assert isinstance(iterable_or_length, collections.abc.Iterable) self.length = cute_iter_tools.get_length(iterable_or_length) else: self.length = None diff --git a/source_py3/python_toolbox/sequence_tools/misc.py b/source_py3/python_toolbox/sequence_tools/misc.py index 6cdad1e6f..ef6213a30 100644 --- a/source_py3/python_toolbox/sequence_tools/misc.py +++ b/source_py3/python_toolbox/sequence_tools/misc.py @@ -155,8 +155,8 @@ def partitions(sequence, partition_size=None, *, n_partitions=None, def is_immutable_sequence(thing): '''Is `thing` an immutable sequence, like `tuple`?''' - return isinstance(thing, collections.Sequence) and not \ - isinstance(thing, collections.MutableSequence) + return isinstance(thing, collections.abc.Sequence) and not \ + isinstance(thing, collections.abc.MutableSequence) @@ -192,7 +192,7 @@ def to_tuple(single_or_sequence, item_type=None, item_test=None): actual_item_test = None if actual_item_test is None: - if isinstance(single_or_sequence, collections.Sequence): + if isinstance(single_or_sequence, collections.abc.Sequence): return tuple(single_or_sequence) elif single_or_sequence is None: return tuple() @@ -241,13 +241,13 @@ def ensure_iterable_is_immutable_sequence(iterable, default_type=tuple, specified in `default_type`. ''' from python_toolbox import nifty_collections - assert isinstance(iterable, collections.Iterable) + assert isinstance(iterable, collections.abc.Iterable) if not allow_unordered and \ isinstance(iterable, nifty_collections.DefinitelyUnordered): raise UnorderedIterableException - if isinstance(iterable, collections.MutableSequence) or \ + if isinstance(iterable, collections.abc.MutableSequence) or \ isinstance(iterable, unallowed_types) or \ - not isinstance(iterable, collections.Sequence): + not isinstance(iterable, collections.abc.Sequence): return default_type(iterable) else: return iterable @@ -263,10 +263,10 @@ def ensure_iterable_is_sequence(iterable, default_type=tuple, makes it into a `tuple`, or into any other data type specified in `default_type`. ''' - assert isinstance(iterable, collections.Iterable) + assert isinstance(iterable, collections.abc.Iterable) if not allow_unordered and isinstance(iterable, (set, frozenset)): raise UnorderedIterableException - if isinstance(iterable, collections.Sequence) and \ + if isinstance(iterable, collections.abc.Sequence) and \ not isinstance(iterable, unallowed_types): return iterable else: @@ -285,7 +285,7 @@ def __contains__(self, item): -class CuteSequence(CuteSequenceMixin, collections.Sequence): +class CuteSequence(CuteSequenceMixin, collections.abc.Sequence): '''A sequence type that adds extra functionality.''' diff --git a/source_py3/python_toolbox/wx_tools/widgets/cute_window/accelerator_savvy_window.py b/source_py3/python_toolbox/wx_tools/widgets/cute_window/accelerator_savvy_window.py index 1fea691f9..06daca5f0 100644 --- a/source_py3/python_toolbox/wx_tools/widgets/cute_window/accelerator_savvy_window.py +++ b/source_py3/python_toolbox/wx_tools/widgets/cute_window/accelerator_savvy_window.py @@ -43,7 +43,7 @@ def _key_dict_to_accelerators(key_dict): ### Breaking down key tuples to individual entries: ####################### # # for key, id in original_key_dict.items(): - if isinstance(key, collections.Sequence): + if isinstance(key, collections.abc.Sequence): key_sequence = key for actual_key in key_sequence: key_dict[actual_key] = id diff --git a/source_py3/test_python_toolbox/test_combi/test_extensive.py b/source_py3/test_python_toolbox/test_combi/test_extensive.py index c3a4f7dba..2be1482dc 100644 --- a/source_py3/test_python_toolbox/test_combi/test_extensive.py +++ b/source_py3/test_python_toolbox/test_combi/test_extensive.py @@ -48,7 +48,7 @@ def __init__(self, iterable_or_length, domain=None, n_elements=None, fixed_map={}, degrees=None, is_combination=False, slice_=None, perm_type=None): self.sequence = tuple(iterable_or_length) if \ - isinstance(iterable_or_length, collections.Iterable) else \ + isinstance(iterable_or_length, collections.abc.Iterable) else \ sequence_tools.CuteRange(iterable_or_length) self.sequence_length = len(self.sequence) self._sequence_frozen_bag = \ diff --git a/source_py3/test_python_toolbox/test_cute_iter_tools/test_iterate_overlapping_subsequences.py b/source_py3/test_python_toolbox/test_cute_iter_tools/test_iterate_overlapping_subsequences.py index 347fa243e..c2647b3ed 100644 --- a/source_py3/test_python_toolbox/test_cute_iter_tools/test_iterate_overlapping_subsequences.py +++ b/source_py3/test_python_toolbox/test_cute_iter_tools/test_iterate_overlapping_subsequences.py @@ -18,7 +18,7 @@ def test_length_2(): # `iterate_overlapping_subsequences` returns an iterator, not a sequence: assert not isinstance( iterate_overlapping_subsequences(list(range(4))), - collections.Sequence + collections.abc.Sequence ) assert tuple(iterate_overlapping_subsequences(list(range(4)))) == \ diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py b/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py index 6a90365c6..5fed1cd23 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py @@ -15,7 +15,7 @@ from python_toolbox.nifty_collections import LazyTuple -class SelfAwareUuidIterator(collections.Iterator): +class SelfAwareUuidIterator(collections.abc.Iterator): '''Iterator that gives UUIDs and keeps them all in an internal list.''' def __init__(self): self.data = [] From 2f8e6843905e8037bc655d72d8640c167b79e82c Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 20 Jun 2016 18:27:52 +0300 Subject: [PATCH 007/112] - (reverted from commit f60128faad78bb86b0064de83dabbf9d40897bdb) --- misc/IDE files/Wing/python_toolbox_py3.wpr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/IDE files/Wing/python_toolbox_py3.wpr b/misc/IDE files/Wing/python_toolbox_py3.wpr index 0a104a906..a14503e2f 100644 --- a/misc/IDE files/Wing/python_toolbox_py3.wpr +++ b/misc/IDE files/Wing/python_toolbox_py3.wpr @@ -1,5 +1,5 @@ #!wing -#!version=6.0 +#!version=5.0 ################################################################## # Wing IDE project file # ################################################################## From 34be9f3a0a9148e0c8233e1401c8ceb45eb2a895 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Thu, 23 Jun 2016 00:15:29 +0300 Subject: [PATCH 008/112] - --- .../python_toolbox/caching/cached_type.py | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/source_py3/python_toolbox/caching/cached_type.py b/source_py3/python_toolbox/caching/cached_type.py index be2f75a52..d106857d5 100644 --- a/source_py3/python_toolbox/caching/cached_type.py +++ b/source_py3/python_toolbox/caching/cached_type.py @@ -7,8 +7,21 @@ See its documentation for more details. ''' +import threading +import contextlib + from python_toolbox.sleek_reffing import SleekCallArgs +from .cached_property import CachedProperty + + +class InConstructionMarker: + @caching.CachedProperty + def condition(self): + import threading + return threading.Condition() + + class SelfPlaceholder: '''Placeholder for `self` when storing call-args.''' @@ -42,9 +55,11 @@ def __init__(self, a, b=2): ''' def __new__(mcls, *args, **kwargs): - result = super().__new__(mcls, *args, **kwargs) - result.__cache = {} - return result + cls = super().__new__(mcls, *args, **kwargs) + cls.__cache = {} + cls.__quick_lock = threading.Lock() + cls.__construction_lock = threading.Lock() + return cls def __call__(cls, *args, **kwargs): @@ -55,8 +70,19 @@ def __call__(cls, *args, **kwargs): **kwargs ) try: - return cls.__cache[sleek_call_args] - except KeyError: - cls.__cache[sleek_call_args] = value = \ - super().__call__(*args, **kwargs) - return value + quick_locked = True + cls.__quick_lock.acquire() + try: + cached_value = cls.__cache[sleek_call_args] + except KeyError: + cls.__construction_lock. + exit_stack.pop_all + cls.__cache[sleek_call_args] = _IN_CONSTRUCTION + cls.__cache[sleek_call_args] = value = \ + super().__call__(*args, **kwargs) + return value + finally: + if construction_locked: + cls.__construction_lock.release() + if quick_locked: + cls.__quick_lock.release() From cb5ecbe853e14ccce0efdb9e2aa82ef8cbe0ef8c Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Thu, 23 Jun 2016 00:42:44 +0300 Subject: [PATCH 009/112] - --- source_py3/python_toolbox/caching/cached_type.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/source_py3/python_toolbox/caching/cached_type.py b/source_py3/python_toolbox/caching/cached_type.py index d106857d5..02222cf52 100644 --- a/source_py3/python_toolbox/caching/cached_type.py +++ b/source_py3/python_toolbox/caching/cached_type.py @@ -18,10 +18,8 @@ class InConstructionMarker: @caching.CachedProperty def condition(self): - import threading return threading.Condition() - class SelfPlaceholder: '''Placeholder for `self` when storing call-args.''' From 0e0e42631128b33fa49304abb2ca6af52ebdb627 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 29 Jun 2016 08:47:12 +0300 Subject: [PATCH 010/112] - --- source_py3/python_toolbox/cute_testing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/source_py3/python_toolbox/cute_testing.py b/source_py3/python_toolbox/cute_testing.py index a84ed4c3f..0e1ec1dc2 100644 --- a/source_py3/python_toolbox/cute_testing.py +++ b/source_py3/python_toolbox/cute_testing.py @@ -3,7 +3,6 @@ '''This module defines tools for testing.''' -import nose import sys from python_toolbox.third_party import unittest2 From 575aca10deadc09ccb80dc66206af8f90406cacd Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 29 Jun 2016 08:55:53 +0300 Subject: [PATCH 011/112] - --- .../python_toolbox/caching/cached_type.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/source_py3/python_toolbox/caching/cached_type.py b/source_py3/python_toolbox/caching/cached_type.py index 02222cf52..9f042a4c8 100644 --- a/source_py3/python_toolbox/caching/cached_type.py +++ b/source_py3/python_toolbox/caching/cached_type.py @@ -16,9 +16,8 @@ class InConstructionMarker: - @caching.CachedProperty - def condition(self): - return threading.Condition() + def __init__(self): + self.lock = threading.Lock() class SelfPlaceholder: @@ -67,20 +66,26 @@ def __call__(cls, *args, **kwargs): *((SelfPlaceholder,) + args), **kwargs ) - try: - quick_locked = True + + while True: cls.__quick_lock.acquire() try: cached_value = cls.__cache[sleek_call_args] except KeyError: - cls.__construction_lock. - exit_stack.pop_all - cls.__cache[sleek_call_args] = _IN_CONSTRUCTION - cls.__cache[sleek_call_args] = value = \ - super().__call__(*args, **kwargs) - return value - finally: - if construction_locked: - cls.__construction_lock.release() - if quick_locked: + cls.__cache[sleek_call_args] = in_construction_marker = \ + InConstructionMarker() cls.__quick_lock.release() + with in_construction_marker.lock: + cls.__cache[sleek_call_args] = new_instance = \ + super().__call__(*args, **kwargs) + return new_instance + else: + if isinstance(cached_value, InConstructionMarker): + in_construction_marker = cached_value + with in_construction_marker.lock: + continue + + return value + + + From d1de081f13131731c694ea464a5e2199c80488ad Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Fri, 1 Jul 2016 22:55:29 +0300 Subject: [PATCH 012/112] - --- source_py3/python_toolbox/caching/cached_type.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source_py3/python_toolbox/caching/cached_type.py b/source_py3/python_toolbox/caching/cached_type.py index 9f042a4c8..ebb124888 100644 --- a/source_py3/python_toolbox/caching/cached_type.py +++ b/source_py3/python_toolbox/caching/cached_type.py @@ -80,12 +80,12 @@ def __call__(cls, *args, **kwargs): super().__call__(*args, **kwargs) return new_instance else: + cls.__quick_lock.release() if isinstance(cached_value, InConstructionMarker): in_construction_marker = cached_value with in_construction_marker.lock: continue - - return value - + else: + return cached_value From fab74bfb6c70fd8009b4fed2548d99e579d4c523 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Fri, 1 Jul 2016 23:14:39 +0300 Subject: [PATCH 013/112] - --- .../nifty_collections/condition_list.py | 59 +++++++++++++++++++ .../test_caching/test_cached_type.py | 15 ++++- 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 source_py3/python_toolbox/nifty_collections/condition_list.py diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py new file mode 100644 index 000000000..636f4a2f1 --- /dev/null +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -0,0 +1,59 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import threading + +import collections.abc + + +class ConditionList(collections.abc.MutableSequence): + + def __init__(self, iterable=None): + self.__list = list(iterable) if iterable is not None else [] + self.__condition = threading.Condition() + self.__new_items = [] + + + def __setitem__(self, index, value): + if isinstance(index, slice): + raise NotImplementedError("Setting a slice isn't implemented yet.") + assert isinstance(index, int) + with self.__condition: + assert not self.__new_items + old_value = self.__list[index] + self.__list[index] = value + if value != old_value: + self.__new_items.append(value) + self.__notify_all() + + + def __delitem__(self, index): + with self.__condition: + del self.__list[index] + + def insert(self, index, value): + assert isinstance(index, int) + with self.__condition: + assert not self.__new_items + self.__list.insert(index, value) + if value != old_value: + self.__new_items.append(value) + self.__notify_all() + + def __getitem__(self, index): + with self.__condition: + return self.__list[index] + + + def __len__(self, ): + with self.__condition: + return len(self.__list) + + + def notify_all(self): + pass + + def wait_for(self, *items): + + + \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_caching/test_cached_type.py b/source_py3/test_python_toolbox/test_caching/test_cached_type.py index 5dcb287fc..0ad9309f8 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cached_type.py +++ b/source_py3/test_python_toolbox/test_caching/test_cached_type.py @@ -1,7 +1,7 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -'''Testing module for `python_toolbox.caching.CachedType`.''' +import uuid as uuid_module from python_toolbox.caching import CachedType @@ -14,4 +14,15 @@ def __init__(self, a=1, b=2, *args, **kwargs): assert A() is A(1) is A(b=2) is A(1, 2) is A(1, b=2) assert A() is not A(3) is not A(b=7) is not A(1, 2, 'meow') is not A(x=9) - \ No newline at end of file + + +class Feline(metaclass=CachedType): + def __init__(self, name): + self.name = name + self.uuid = uuid_module.uuid4().hex + self.creation_hook() + + creation_hook = lambda self: None + +def test_thread_safe(): + f1 = \ No newline at end of file From 67814a56bef3a3b636447227801c2a6b95e8b3c2 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Fri, 1 Jul 2016 23:28:51 +0300 Subject: [PATCH 014/112] - --- .../nifty_collections/condition_list.py | 19 +++++++++++-------- .../python_toolbox/sequence_tools/misc.py | 8 ++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py index 636f4a2f1..256aea21e 100644 --- a/source_py3/python_toolbox/nifty_collections/condition_list.py +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -5,13 +5,14 @@ import collections.abc +from python_toolbox import context_management + class ConditionList(collections.abc.MutableSequence): def __init__(self, iterable=None): self.__list = list(iterable) if iterable is not None else [] self.__condition = threading.Condition() - self.__new_items = [] def __setitem__(self, index, value): @@ -19,11 +20,9 @@ def __setitem__(self, index, value): raise NotImplementedError("Setting a slice isn't implemented yet.") assert isinstance(index, int) with self.__condition: - assert not self.__new_items old_value = self.__list[index] self.__list[index] = value if value != old_value: - self.__new_items.append(value) self.__notify_all() @@ -34,10 +33,8 @@ def __delitem__(self, index): def insert(self, index, value): assert isinstance(index, int) with self.__condition: - assert not self.__new_items self.__list.insert(index, value) if value != old_value: - self.__new_items.append(value) self.__notify_all() def __getitem__(self, index): @@ -50,10 +47,16 @@ def __len__(self, ): return len(self.__list) - def notify_all(self): - pass + def __notify_all(self): + with self.__condition: + self.__condition.notify_all() + @context_management.ContextManagerType def wait_for(self, *items): - + from python_toolbox import sequence_tools + with self.__condition: + self.__condition.wait_for( + lambda: sequence_tools.is_contained_in(items, self.__list)) + yield \ No newline at end of file diff --git a/source_py3/python_toolbox/sequence_tools/misc.py b/source_py3/python_toolbox/sequence_tools/misc.py index ef6213a30..276561f2d 100644 --- a/source_py3/python_toolbox/sequence_tools/misc.py +++ b/source_py3/python_toolbox/sequence_tools/misc.py @@ -359,4 +359,12 @@ def is_subsequence(big_sequence, small_sequence): return True +def is_contained_in(iterable, container): + desired_items = list(iterable) + for item in container: + if item in desired_items: + desired_items.remove(item) + if not desired_items: + return True + return False \ No newline at end of file From 3e22b06da208f6b041ad24095ed81812b378c93b Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Fri, 1 Jul 2016 23:55:43 +0300 Subject: [PATCH 015/112] - --- .../nifty_collections/__init__.py | 1 + .../nifty_collections/condition_list.py | 19 +++-- .../python_toolbox/sequence_tools/misc.py | 18 +++++ .../test_condition_list/__init__.py | 3 + .../test_condition_list.py | 78 +++++++++++++++++++ 5 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/__init__.py create mode 100644 source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py diff --git a/source_py3/python_toolbox/nifty_collections/__init__.py b/source_py3/python_toolbox/nifty_collections/__init__.py index e7e3c758a..2af7b7d9b 100644 --- a/source_py3/python_toolbox/nifty_collections/__init__.py +++ b/source_py3/python_toolbox/nifty_collections/__init__.py @@ -11,6 +11,7 @@ from .various_frozen_dicts import FrozenDict, FrozenOrderedDict from .bagging import Bag, OrderedBag, FrozenBag, FrozenOrderedBag from .frozen_bag_bag import FrozenBagBag +from .condition_list import ConditionList from ..cute_enum import CuteEnum from .emitting_weak_key_default_dict import EmittingWeakKeyDefaultDict diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py index 256aea21e..73f577d03 100644 --- a/source_py3/python_toolbox/nifty_collections/condition_list.py +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -20,10 +20,8 @@ def __setitem__(self, index, value): raise NotImplementedError("Setting a slice isn't implemented yet.") assert isinstance(index, int) with self.__condition: - old_value = self.__list[index] self.__list[index] = value - if value != old_value: - self.__notify_all() + self.__notify_all() def __delitem__(self, index): @@ -34,15 +32,13 @@ def insert(self, index, value): assert isinstance(index, int) with self.__condition: self.__list.insert(index, value) - if value != old_value: - self.__notify_all() + self.__notify_all() def __getitem__(self, index): with self.__condition: return self.__list[index] - - def __len__(self, ): + def __len__(self): with self.__condition: return len(self.__list) @@ -51,12 +47,15 @@ def __notify_all(self): with self.__condition: self.__condition.notify_all() - @context_management.ContextManagerType - def wait_for(self, *items): + def wait_for(self, *items, remove=True): from python_toolbox import sequence_tools with self.__condition: self.__condition.wait_for( lambda: sequence_tools.is_contained_in(items, self.__list)) - yield + if remove: + sequence_tools.remove_items(items, self.__list) + return + def __repr__(self): + return '<%s: %s>' % (type(self).__name__, self.__list) \ No newline at end of file diff --git a/source_py3/python_toolbox/sequence_tools/misc.py b/source_py3/python_toolbox/sequence_tools/misc.py index 276561f2d..e30dc9a03 100644 --- a/source_py3/python_toolbox/sequence_tools/misc.py +++ b/source_py3/python_toolbox/sequence_tools/misc.py @@ -367,4 +367,22 @@ def is_contained_in(iterable, container): if not desired_items: return True return False + +def remove_items(items_to_remove, mutable_sequence, *, + ensure_contained_first=False): + assert isinstance(mutable_sequence, collections.abc.MutableSequence) + items_to_remove = list(items_to_remove) + if ensure_contained_first: + assert is_contained_in(items_to_remove, mutable_sequence) + for i, item in enumerate(mutable_sequence): + try: + items_to_remove.remove(item) + except ValueError: + pass + else: + del mutable_sequence[i] + if not items_to_remove: + return + raise ValueError('Not all items were found, possibly some items were ' + 'removed.') \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/__init__.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/__init__.py new file mode 100644 index 000000000..ed9a70da2 --- /dev/null +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py new file mode 100644 index 000000000..8e1a10968 --- /dev/null +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -0,0 +1,78 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import threading + +from python_toolbox import cute_testing + +from python_toolbox.nifty_collections import ConditionList + + +def test(): + c = ConditionList() + assert list(c) == [] + assert 7 not in c + assert len(c) == 0 + assert not c + c.append(7) + assert 7 in c + assert len(c) == 1 + assert c + c[0] = 'meow' + assert 'meow' in c + assert 7 not in c + assert len(c) == 1 + assert c + c.extend(range(3)) + assert list(c) == ['meow', 0, 1, 2] + + c = ConditionList([1, 2, 3]) + assert list(c) == [1, 2, 3] + +def test_threaded(): + + + log_list = [] + log_list_lock = threading.RLock() + + c = ConditionList() + + + class Thread(threading.Thread): + def __init__(self, number): + super().__init__() + self.number = number + + def run(self): + for i in range(10): + c.wait_for('t%sm%s' % (self.number, i)) + with log_list_lock: + log_list.append('Thread %s achieved milestone %s') + + threads = tuple(map(Thread, range(10))) + for thread in threads: + thread.start() + + timings = [0, 1, 4, 7, 9, 9, 8, 8, 3, 5, 3, 9, 5, 2, 1, 1, 9, 8, 1, 7, 3, + 4, 9, 4, 5, 0, 1, 0, 9, 5, 0, 5, 4, 4, 5, 9, 3, 9, 2, 3, 8, 7, + 2, 2, 1, 3, 0, 0, 7, 6, 6, 8, 7, 6, 4, 6, 2, 6, 0, 1, 7, 8, 6, + 2, 9, 3, 4, 3, 0, 2, 5, 1, 4, 2, 4, 3, 0, 1, 7, 5, 4, 5, 8, 6, + 5, 2, 8, 6, 1, 2, 6, 6, 3, 0, 7, 7, 9, 8, 7, 8] + # (Generated randomly, keeping static here to ensure reproducible test + # runs.) + + expected_log_list = [] + expected_thread_states = [0] * 10 + for timing in timings: + + + + assert log_list == expected_log_list + + + + + + + + \ No newline at end of file From bb35342ac9a59533f5ccbfc2d38e11404cbeff93 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 00:06:51 +0300 Subject: [PATCH 016/112] - --- .../test_condition_list.py | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index 8e1a10968..d891daf16 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -31,12 +31,19 @@ def test(): def test_threaded(): - log_list = [] log_list_lock = threading.RLock() + # This `log_list_lock` serves two purposes: + # 1. Ensuring no two threads try to write to `log_list` at the same time. + # (Though the GIL might protect from that, I'm not sure, but it's + # implementation detail anyway.) + # 2. Ensuring that only one thread runs at a time, so there won't be any + # race conditions and the log list will be in the expected order. This + # is done by acquiring this lock between releasing one milestone and the + # next. - c = ConditionList() + c = ConditionList() class Thread(threading.Thread): def __init__(self, number): @@ -48,22 +55,49 @@ def run(self): c.wait_for('t%sm%s' % (self.number, i)) with log_list_lock: log_list.append('Thread %s achieved milestone %s') + + class ManagementThread(threading.Thread): + def __init__(self): + super().__init__() + + def run(self): + for i, milestone in enumerate(milestones_release_order): + with log_list_lock: + assert len(log_list_lock) == i + c.append(milestone) threads = tuple(map(Thread, range(10))) + management_thread = ManagementThread() for thread in threads: thread.start() + management_thread.start() + # We're going to let these puppies grind in the background while we + # calculate the expected log output. - timings = [0, 1, 4, 7, 9, 9, 8, 8, 3, 5, 3, 9, 5, 2, 1, 1, 9, 8, 1, 7, 3, - 4, 9, 4, 5, 0, 1, 0, 9, 5, 0, 5, 4, 4, 5, 9, 3, 9, 2, 3, 8, 7, - 2, 2, 1, 3, 0, 0, 7, 6, 6, 8, 7, 6, 4, 6, 2, 6, 0, 1, 7, 8, 6, - 2, 9, 3, 4, 3, 0, 2, 5, 1, 4, 2, 4, 3, 0, 1, 7, 5, 4, 5, 8, 6, - 5, 2, 8, 6, 1, 2, 6, 6, 3, 0, 7, 7, 9, 8, 7, 8] + milestones_release_order = [ + 't6m3', 't9m4', 't1m4', 't1m8', 't6m0', 't7m7', 't4m4', 't5m1', 't8m8', + 't4m0', 't5m2', 't8m9', 't5m8', 't2m4', 't1m2', 't9m9', 't6m7', 't9m7', + 't1m9', 't8m3', 't6m8', 't7m5', 't5m4', 't3m6', 't0m5', 't4m2', 't6m9', + 't9m0', 't9m1', 't6m4', 't8m5', 't0m3', 't3m8', 't0m1', 't4m1', 't4m3', + 't3m3', 't1m7', 't5m0', 't8m0', 't2m1', 't6m1', 't1m1', 't9m6', 't3m7', + 't7m8', 't8m4', 't8m6', 't9m5', 't6m6', 't5m3', 't4m9', 't3m5', 't4m8', + 't0m6', 't3m1', 't0m8', 't5m5', 't4m6', 't3m0', 't7m0', 't2m5', 't5m7', + 't7m6', 't7m4', 't8m2', 't4m5', 't4m7', 't8m1', 't9m2', 't2m6', 't2m2', + 't1m6', 't2m3', 't1m5', 't2m8', 't0m0', 't6m2', 't1m3', 't7m1', 't2m0', + 't3m2', 't3m9', 't2m9', 't7m3', 't0m2', 't2m7', 't8m7', 't7m2', 't1m0', + 't0m4', 't0m9', 't0m7', 't6m5', 't9m8', 't5m6', 't3m4', 't5m9', 't7m9', + 't9m3' + ] # (Generated randomly, keeping static here to ensure reproducible test # runs.) + # First we're going to calculate the + expected_log_list = [] expected_thread_states = [0] * 10 - for timing in timings: + for thread_number_to_advance in timings: + expected_thread_state = expected_thread_state[thread_number_to_advance] + c.append('') From 776c79a3f8a9232d854905751e445ec796c2ce0d Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 00:07:33 +0300 Subject: [PATCH 017/112] - --- .../test_condition_list.py | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index d891daf16..ca97bdaea 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -56,23 +56,6 @@ def run(self): with log_list_lock: log_list.append('Thread %s achieved milestone %s') - class ManagementThread(threading.Thread): - def __init__(self): - super().__init__() - - def run(self): - for i, milestone in enumerate(milestones_release_order): - with log_list_lock: - assert len(log_list_lock) == i - c.append(milestone) - - threads = tuple(map(Thread, range(10))) - management_thread = ManagementThread() - for thread in threads: - thread.start() - management_thread.start() - # We're going to let these puppies grind in the background while we - # calculate the expected log output. milestones_release_order = [ 't6m3', 't9m4', 't1m4', 't1m8', 't6m0', 't7m7', 't4m4', 't5m1', 't8m8', @@ -99,8 +82,18 @@ def run(self): expected_thread_state = expected_thread_state[thread_number_to_advance] c.append('') + + + # And now, let the show begin! + threads = tuple(map(Thread, range(10))) + for thread in threads: + thread.start() + for i, milestone in enumerate(milestones_release_order): + with log_list_lock: + assert len(log_list_lock) == i + c.append(milestone) - + # This is the money line right here: assert log_list == expected_log_list From 352caf120250385ea2b5bde96304c9d5a86511ff Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 00:15:53 +0300 Subject: [PATCH 018/112] - --- .../test_condition_list.py | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index ca97bdaea..ecf9e544c 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -3,6 +3,7 @@ import threading +from python_toolbox import sequence_tools from python_toolbox import cute_testing from python_toolbox.nifty_collections import ConditionList @@ -77,12 +78,26 @@ def run(self): # First we're going to calculate the expected_log_list = [] - expected_thread_states = [0] * 10 - for thread_number_to_advance in timings: - expected_thread_state = expected_thread_state[thread_number_to_advance] - c.append('') - - + milestones_in_the_bank = [set() for _ in range(10)] + def get_thread_state(number): + for i in range(0, 10): + milestones_in_the_bank_for_thread = milestones_in_the_bank[number] + if i not in milestones_in_the_bank_for_thread: + return i - 1 + return 9 + + for milestone in milestones_release_order: + thread_number = int(milestone[1]) + milestone_number = int(milestone[3]) + old_thread_state = get_thread_state(thread_number) + milestones_in_the_bank[thread_number].append(milestone_number) + new_thread_state = get_thread_state(thread_number) + for milestone_accomplished in range(old_thread_state + 1, + new_thread_state + 1): + expected_log_list.append('t%sm%s' % + (thread_number, milestone_accomplished)) + assert len(expected_log_list) == 100 + assert not sequence_tools.get_recurrences(expected_log_list) # And now, let the show begin! threads = tuple(map(Thread, range(10))) From 9b82bca884a2ac2cf4dedb54dd441edb0fc251e6 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 00:24:27 +0300 Subject: [PATCH 019/112] - --- .../nifty_collections/condition_list.py | 2 +- .../test_condition_list.py | 30 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py index 73f577d03..b5ce73147 100644 --- a/source_py3/python_toolbox/nifty_collections/condition_list.py +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -47,7 +47,7 @@ def __notify_all(self): with self.__condition: self.__condition.notify_all() - def wait_for(self, *items, remove=True): + def wait_for(self, *items, remove=False): from python_toolbox import sequence_tools with self.__condition: self.__condition.wait_for( diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index ecf9e544c..acea753dd 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -2,6 +2,7 @@ # This program is distributed under the MIT license. import threading +import time from python_toolbox import sequence_tools from python_toolbox import cute_testing @@ -34,28 +35,24 @@ def test_threaded(): log_list = [] log_list_lock = threading.RLock() - # This `log_list_lock` serves two purposes: - # 1. Ensuring no two threads try to write to `log_list` at the same time. - # (Though the GIL might protect from that, I'm not sure, but it's - # implementation detail anyway.) - # 2. Ensuring that only one thread runs at a time, so there won't be any - # race conditions and the log list will be in the expected order. This - # is done by acquiring this lock between releasing one milestone and the - # next. - c = ConditionList() class Thread(threading.Thread): + is_waiting = False + def __init__(self, number): super().__init__() self.number = number def run(self): for i in range(10): - c.wait_for('t%sm%s' % (self.number, i)) + c.is_waiting = True + milestone = 't%sm%s' % (self.number, i) + c.wait_for(milestone) + c.is_waiting = False with log_list_lock: - log_list.append('Thread %s achieved milestone %s') + log_list.append(milestone) milestones_release_order = [ @@ -90,7 +87,7 @@ def get_thread_state(number): thread_number = int(milestone[1]) milestone_number = int(milestone[3]) old_thread_state = get_thread_state(thread_number) - milestones_in_the_bank[thread_number].append(milestone_number) + milestones_in_the_bank[thread_number].add(milestone_number) new_thread_state = get_thread_state(thread_number) for milestone_accomplished in range(old_thread_state + 1, new_thread_state + 1): @@ -99,14 +96,19 @@ def get_thread_state(number): assert len(expected_log_list) == 100 assert not sequence_tools.get_recurrences(expected_log_list) - # And now, let the show begin! + # And now, let the show begin! threads = tuple(map(Thread, range(10))) for thread in threads: thread.start() + threads_finished_advancing = lambda: all([not thread.is_waiting for thread + in threads]) for i, milestone in enumerate(milestones_release_order): with log_list_lock: - assert len(log_list_lock) == i + assert len(log_list) <= i c.append(milestone) + if not threads_finished_advancing(): + time.sleep(1) + assert threads_finished_advancing() # This is the money line right here: assert log_list == expected_log_list From 2eb58aca9dd4f3f68ce0ddde8aa1635c25e75d1c Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 00:30:41 +0300 Subject: [PATCH 020/112] - --- .../test_condition_list/test_condition_list.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index acea753dd..2a0845d92 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -3,7 +3,10 @@ import threading import time +import queue as queue_module +from python_toolbox import queue_tools +from python_toolbox import cute_iter_tools from python_toolbox import sequence_tools from python_toolbox import cute_testing @@ -38,6 +41,8 @@ def test_threaded(): c = ConditionList() + thread_started_queue = queue_module.Queue() + class Thread(threading.Thread): is_waiting = False @@ -46,6 +51,7 @@ def __init__(self, number): self.number = number def run(self): + thread_started_queue.put(self.number) for i in range(10): c.is_waiting = True milestone = 't%sm%s' % (self.number, i) @@ -96,10 +102,17 @@ def get_thread_state(number): assert len(expected_log_list) == 100 assert not sequence_tools.get_recurrences(expected_log_list) - # And now, let the show begin! + # Start your engines... threads = tuple(map(Thread, range(10))) for thread in threads: thread.start() + + # This waits for all the threads to start: + assert sorted( + cute_iter_tools.shorten(queue_tools.iterate(thread_started_queue), 10) + ) == list(range(10)) + + # And now, let the show begin! threads_finished_advancing = lambda: all([not thread.is_waiting for thread in threads]) for i, milestone in enumerate(milestones_release_order): From de8974cb9c04251049f6e91f5552e626b9ba991a Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 00:39:38 +0300 Subject: [PATCH 021/112] - --- .../test_condition_list.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index 2a0845d92..87df300e0 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -49,16 +49,18 @@ class Thread(threading.Thread): def __init__(self, number): super().__init__() self.number = number + self.busy_condition = threading.Condition() def run(self): thread_started_queue.put(self.number) for i in range(10): - c.is_waiting = True milestone = 't%sm%s' % (self.number, i) + c.is_waiting = True c.wait_for(milestone) - c.is_waiting = False - with log_list_lock: - log_list.append(milestone) + with self.busy_condition: + c.is_waiting = False + with log_list_lock: + log_list.append(milestone) milestones_release_order = [ @@ -113,15 +115,15 @@ def get_thread_state(number): ) == list(range(10)) # And now, let the show begin! - threads_finished_advancing = lambda: all([not thread.is_waiting for thread - in threads]) + def wait_for_threads_to_advance(): + for thread in threads: + with thread.busy_condition: + pass for i, milestone in enumerate(milestones_release_order): with log_list_lock: assert len(log_list) <= i c.append(milestone) - if not threads_finished_advancing(): - time.sleep(1) - assert threads_finished_advancing() + wait_for_threads_to_advance() # This is the money line right here: assert log_list == expected_log_list From c11dc660121c5d0e0fc95478aa18d3fd2db03ad5 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 00:48:52 +0300 Subject: [PATCH 022/112] - --- .../nifty_collections/condition_list.py | 9 ++++++--- .../test_condition_list.py | 19 +++++++------------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py index b5ce73147..fed581f95 100644 --- a/source_py3/python_toolbox/nifty_collections/condition_list.py +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -8,11 +8,13 @@ from python_toolbox import context_management -class ConditionList(collections.abc.MutableSequence): +class ConditionList(collections.abc.MutableSequence, + context_management.DelegatingContextManager): def __init__(self, iterable=None): self.__list = list(iterable) if iterable is not None else [] - self.__condition = threading.Condition() + self.__condition = \ + self.delegatee_context_manager = threading.Condition() def __setitem__(self, index, value): @@ -58,4 +60,5 @@ def wait_for(self, *items, remove=False): def __repr__(self): return '<%s: %s>' % (type(self).__name__, self.__list) - \ No newline at end of file + + \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index 87df300e0..6e7ffb781 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -44,12 +44,9 @@ def test_threaded(): thread_started_queue = queue_module.Queue() class Thread(threading.Thread): - is_waiting = False - def __init__(self, number): super().__init__() self.number = number - self.busy_condition = threading.Condition() def run(self): thread_started_queue.put(self.number) @@ -57,10 +54,8 @@ def run(self): milestone = 't%sm%s' % (self.number, i) c.is_waiting = True c.wait_for(milestone) - with self.busy_condition: - c.is_waiting = False - with log_list_lock: - log_list.append(milestone) + with log_list_lock: + log_list.append(milestone) milestones_release_order = [ @@ -115,15 +110,15 @@ def get_thread_state(number): ) == list(range(10)) # And now, let the show begin! - def wait_for_threads_to_advance(): - for thread in threads: - with thread.busy_condition: - pass for i, milestone in enumerate(milestones_release_order): with log_list_lock: assert len(log_list) <= i c.append(milestone) - wait_for_threads_to_advance() + + # Ensure that all threads advanced if they should, by ensuring that the + # condition lock is free: + with c: + pass # This is the money line right here: assert log_list == expected_log_list From 2131c4938786e9700506e9fa5dc178d48b7a80ab Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 00:53:36 +0300 Subject: [PATCH 023/112] - --- .../test_condition_list/test_condition_list.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index 6e7ffb781..50a4be720 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -109,6 +109,8 @@ def get_thread_state(number): cute_iter_tools.shorten(queue_tools.iterate(thread_started_queue), 10) ) == list(range(10)) + time.sleep(1) + # And now, let the show begin! for i, milestone in enumerate(milestones_release_order): with log_list_lock: From 80ff82416a71ded73d32c52be903c1ba947b04b5 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 01:14:20 +0300 Subject: [PATCH 024/112] - --- .../nifty_collections/condition_list.py | 13 +++++-- .../test_condition_list.py | 37 ++++++++++++------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py index fed581f95..61b387804 100644 --- a/source_py3/python_toolbox/nifty_collections/condition_list.py +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -49,14 +49,21 @@ def __notify_all(self): with self.__condition: self.__condition.notify_all() - def wait_for(self, *items, remove=False): + def wait_for(self, *items, remove=False, timeout=None, + extra_predicate=lambda: True): from python_toolbox import sequence_tools with self.__condition: self.__condition.wait_for( - lambda: sequence_tools.is_contained_in(items, self.__list)) + lambda: (extra_predicate() and + sequence_tools.is_contained_in(items, self.__list)) + ) if remove: sequence_tools.remove_items(items, self.__list) - return + + def wait(self, *, timeout=None): + from python_toolbox import sequence_tools + with self.__condition: + self.__condition.wait(timeout=timeout) def __repr__(self): return '<%s: %s>' % (type(self).__name__, self.__list) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index 50a4be720..b1c78c766 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -41,7 +41,7 @@ def test_threaded(): c = ConditionList() - thread_started_queue = queue_module.Queue() + thread_advanced_queue = queue_module.Queue() class Thread(threading.Thread): def __init__(self, number): @@ -49,11 +49,18 @@ def __init__(self, number): self.number = number def run(self): - thread_started_queue.put(self.number) + thread_advanced_queue.put(self.number) for i in range(10): milestone = 't%sm%s' % (self.number, i) c.is_waiting = True - c.wait_for(milestone) + c.wait_for( + milestone, + extra_predicate=lambda: (thread_advanced_queue.put(self.number) + or True) + ) + # (Used an extra predicate above just to let the main thread + # know that we done checked our predicate. This wouldn't be + # needed in real code.) with log_list_lock: log_list.append(milestone) @@ -99,35 +106,37 @@ def get_thread_state(number): assert len(expected_log_list) == 100 assert not sequence_tools.get_recurrences(expected_log_list) + def wait_for_threads_to_advance(): + thread_numbers_we_have_not_seen_yet = set(range(10)) + while thread_numbers_we_have_not_seen_yet: + thread_numbers_we_have_not_seen_yet.discard( + thread_advanced_queue.get() + ) + # Start your engines... threads = tuple(map(Thread, range(10))) for thread in threads: thread.start() - # This waits for all the threads to start: - assert sorted( - cute_iter_tools.shorten(queue_tools.iterate(thread_started_queue), 10) + # Wait for all the threads to start... + assert list( + cute_iter_tools.shorten( + sorted(queue_tools.iterate(thread_advanced_queue, block=True)), 10 + ) ) == list(range(10)) - time.sleep(1) # And now, let the show begin! for i, milestone in enumerate(milestones_release_order): with log_list_lock: assert len(log_list) <= i c.append(milestone) - - # Ensure that all threads advanced if they should, by ensuring that the - # condition lock is free: - with c: - pass + wait_for_threads_to_advance() # This is the money line right here: assert log_list == expected_log_list - - From c468a16c4a9ea008618a1e22b4c433e3cb6f5aa9 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 01:14:41 +0300 Subject: [PATCH 025/112] - --- .../test_condition_list/test_condition_list.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index b1c78c766..d9ecee46e 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -119,12 +119,7 @@ def wait_for_threads_to_advance(): thread.start() # Wait for all the threads to start... - assert list( - cute_iter_tools.shorten( - sorted(queue_tools.iterate(thread_advanced_queue, block=True)), 10 - ) - ) == list(range(10)) - + wait_for_threads_to_advance() # And now, let the show begin! for i, milestone in enumerate(milestones_release_order): From dbf0c81f9d69106c4c43a5dbe2ccf24e68e08b80 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 01:17:42 +0300 Subject: [PATCH 026/112] - --- .../test_python_toolbox/test_caching/test_cached_type.py | 2 +- .../test_condition_list/test_condition_list.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/source_py3/test_python_toolbox/test_caching/test_cached_type.py b/source_py3/test_python_toolbox/test_caching/test_cached_type.py index 0ad9309f8..f9b9a77a8 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cached_type.py +++ b/source_py3/test_python_toolbox/test_caching/test_cached_type.py @@ -25,4 +25,4 @@ def __init__(self, name): creation_hook = lambda self: None def test_thread_safe(): - f1 = \ No newline at end of file + pass # blocktodo use ConditionList \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py index d9ecee46e..1cd790170 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_condition_list/test_condition_list.py @@ -44,6 +44,7 @@ def test_threaded(): thread_advanced_queue = queue_module.Queue() class Thread(threading.Thread): + is_done = False def __init__(self, number): super().__init__() self.number = number @@ -64,6 +65,9 @@ def run(self): with log_list_lock: log_list.append(milestone) + self.is_done = True + thread_advanced_queue.put(self.number) + milestones_release_order = [ 't6m3', 't9m4', 't1m4', 't1m8', 't6m0', 't7m7', 't4m4', 't5m1', 't8m8', @@ -107,7 +111,8 @@ def get_thread_state(number): assert not sequence_tools.get_recurrences(expected_log_list) def wait_for_threads_to_advance(): - thread_numbers_we_have_not_seen_yet = set(range(10)) + thread_numbers_we_have_not_seen_yet = {thread.number for thread in + threads if not thread.is_done} while thread_numbers_we_have_not_seen_yet: thread_numbers_we_have_not_seen_yet.discard( thread_advanced_queue.get() From eb9f4cdb9fab69e85ef4f4c724f14d822f1fd48d Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 02:03:39 +0300 Subject: [PATCH 027/112] - --- .../nifty_collections/condition_list.py | 27 +++++++- .../test_caching/test_cached_type.py | 64 ++++++++++++++++--- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py index 61b387804..8ceba0911 100644 --- a/source_py3/python_toolbox/nifty_collections/condition_list.py +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -49,6 +49,11 @@ def __notify_all(self): with self.__condition: self.__condition.notify_all() + def wait(self, *, timeout=None): + from python_toolbox import sequence_tools + with self.__condition: + self.__condition.wait(timeout=timeout) + def wait_for(self, *items, remove=False, timeout=None, extra_predicate=lambda: True): from python_toolbox import sequence_tools @@ -60,10 +65,28 @@ def wait_for(self, *items, remove=False, timeout=None, if remove: sequence_tools.remove_items(items, self.__list) - def wait(self, *, timeout=None): + def wait_for_missing(self, *missing_items, timeout=None, + extra_predicate=lambda: True): from python_toolbox import sequence_tools with self.__condition: - self.__condition.wait(timeout=timeout) + self.__condition.wait_for( + lambda: (extra_predicate() and + not sequence_tools.is_contained_in(missing_items, + self.__list)) + ) + + def wait_for_empty(self, timeout=None, extra_predicate=lambda: True): + from python_toolbox import sequence_tools + with self.__condition: + self.__condition.wait_for( + lambda: (extra_predicate() and not self.__list) + ) + + def play_out(self, iterable): + for item in iterable: + self.append(item) + self.wait_for_missing(item) + def __repr__(self): return '<%s: %s>' % (type(self).__name__, self.__list) diff --git a/source_py3/test_python_toolbox/test_caching/test_cached_type.py b/source_py3/test_python_toolbox/test_caching/test_cached_type.py index f9b9a77a8..cca174daa 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cached_type.py +++ b/source_py3/test_python_toolbox/test_caching/test_cached_type.py @@ -1,9 +1,11 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +import threading import uuid as uuid_module from python_toolbox.caching import CachedType +from python_toolbox import nifty_collections def test(): @@ -16,13 +18,57 @@ def __init__(self, a=1, b=2, *args, **kwargs): assert A() is not A(3) is not A(b=7) is not A(1, 2, 'meow') is not A(x=9) -class Feline(metaclass=CachedType): - def __init__(self, name): - self.name = name - self.uuid = uuid_module.uuid4().hex - self.creation_hook() - - creation_hook = lambda self: None - def test_thread_safe(): - pass # blocktodo use ConditionList \ No newline at end of file + + class Feline(metaclass=CachedType): + def __init__(self, name): + self.name = name + self.uuid = uuid_module.uuid4().hex + self.creation_hook() + + def creation_hook(self): + condition_list.wait_for('%s_creation' % self.name, remove=True) + + condition_list = nifty_collections.ConditionList() + + class BaseThread(threading.Thread): + def __init__(self): + super().__init__() + + class FirstThread(BaseThread): + def run(self): + condition_list.wait_for('t1go', remove=True) + self.cat = Feline('cat') + + class SecondThread(BaseThread): + def run(self): + condition_list.wait_for('t2go', remove=True) + self.tiger = Feline('tiger') + + class ThirdThread(BaseThread): + def run(self): + condition_list.wait_for('t3go', remove=True) + self.cat = Feline('cat') + + class FourthThread(BaseThread): + def run(self): + condition_list.wait_for('t4go', remove=True) + self.tiger = Feline('tiger') + + threads = (FirstThread(), SecondThread(), ThirdThread(), FourthThread()) + for thread in threads: + thread.start() + + + condition_list.play_out(['t1go', 't2go', 'tiger_creation', 't3go', + 'cat_creation', 't4go']) + + for thread in threads: + thread.join() + + assert threads[0].cat is threads[2].cat + assert threads[0].cat.uuid == threads[2].cat.uuid + assert threads[1].tiger is threads[3].tiger + assert threads[1].tiger.uuid == threads[3].tiger.uuid + + # blocktodo be asserting the in construction marker \ No newline at end of file From 5986450b6f10b3fbcbbc543dfdd129042075091b Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 02:19:13 +0300 Subject: [PATCH 028/112] - --- .../nifty_collections/condition_list.py | 6 +- .../test_caching/test_cached_type.py | 72 +++++++++++++++++-- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py index 8ceba0911..44db17955 100644 --- a/source_py3/python_toolbox/nifty_collections/condition_list.py +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -29,6 +29,7 @@ def __setitem__(self, index, value): def __delitem__(self, index): with self.__condition: del self.__list[index] + self.__notify_all() def insert(self, index, value): assert isinstance(index, int) @@ -63,7 +64,7 @@ def wait_for(self, *items, remove=False, timeout=None, sequence_tools.is_contained_in(items, self.__list)) ) if remove: - sequence_tools.remove_items(items, self.__list) + sequence_tools.remove_items(items, self) def wait_for_missing(self, *missing_items, timeout=None, extra_predicate=lambda: True): @@ -83,9 +84,10 @@ def wait_for_empty(self, timeout=None, extra_predicate=lambda: True): ) def play_out(self, iterable): + assert not self for item in iterable: self.append(item) - self.wait_for_missing(item) + self.wait_for_empty() def __repr__(self): diff --git a/source_py3/test_python_toolbox/test_caching/test_cached_type.py b/source_py3/test_python_toolbox/test_caching/test_cached_type.py index cca174daa..5e335602c 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cached_type.py +++ b/source_py3/test_python_toolbox/test_caching/test_cached_type.py @@ -4,6 +4,7 @@ import threading import uuid as uuid_module +from python_toolbox import caching from python_toolbox.caching import CachedType from python_toolbox import nifty_collections @@ -39,29 +40,93 @@ class FirstThread(BaseThread): def run(self): condition_list.wait_for('t1go', remove=True) self.cat = Feline('cat') + condition_list.append('t1done') class SecondThread(BaseThread): def run(self): condition_list.wait_for('t2go', remove=True) self.tiger = Feline('tiger') + condition_list.append('t2done') class ThirdThread(BaseThread): def run(self): condition_list.wait_for('t3go', remove=True) self.cat = Feline('cat') + condition_list.append('t3done') class FourthThread(BaseThread): def run(self): condition_list.wait_for('t4go', remove=True) self.tiger = Feline('tiger') + condition_list.append('t4done') threads = (FirstThread(), SecondThread(), ThirdThread(), FourthThread()) for thread in threads: thread.start() - condition_list.play_out(['t1go', 't2go', 'tiger_creation', 't3go', - 'cat_creation', 't4go']) + condition_list.play_out(['t1go']) + + cache = Feline._CachedType__cache + assert len(cache) == 1 + ((cat_key, cat_value),) = cache.items() + assert isinstance(cat_value, caching.cached_type.InConstructionMarker) + assert cat_value.lock.locked() + assert not Feline._CachedType__quick_lock.locked() + + condition_list.play_out(['t2go']) + + assert len(cache) == 2 + ((key_1, value_1), (key_2, value_2)) = cache.items() + cat_value, tiger_value = ((value_1, value_2) if (key_1 is cat_key) + else (value_2, value_1)) + assert isinstance(cat_value, caching.cached_type.InConstructionMarker) + assert cat_value.lock.locked() + assert isinstance(tiger_value, caching.cached_type.InConstructionMarker) + assert tiger_value.lock.locked() + assert not Feline._CachedType__quick_lock.locked() + + condition_list.play_out(['tiger_creation']) + condition_list.wait_for('t2done', remove=True) + + assert len(cache) == 2 + ((key_1, value_1), (key_2, value_2)) = cache.items() + cat_value, tiger_value = ((value_1, value_2) if (key_1 is cat_key) + else (value_2, value_1)) + assert isinstance(tiger_value, Feline) + assert isinstance(cat_value, caching.cached_type.InConstructionMarker) + assert cat_value.lock.locked() + assert not Feline._CachedType__quick_lock.locked() + + condition_list.play_out(['t3go']) + + assert len(cache) == 2 + ((key_1, value_1), (key_2, value_2)) = cache.items() + cat_value, tiger_value = ((value_1, value_2) if (key_1 is cat_key) + else (value_2, value_1)) + assert isinstance(tiger_value, Feline) + assert isinstance(cat_value, caching.cached_type.InConstructionMarker) + assert cat_value.lock.locked() + assert not Feline._CachedType__quick_lock.locked() + + condition_list.play_out(['cat_creation']) + condition_list.wait_for('t1done', 't3done', remove=True) + + assert len(cache) == 2 + ((key_1, value_1), (key_2, value_2)) = cache.items() + cat_value, tiger_value = ((value_1, value_2) if (key_1 is cat_key) + else (value_2, value_1)) + assert isinstance(tiger_value, Feline) + assert isinstance(cat_value, Feline) + + condition_list.play_out(['t4go', 't4done']) + + assert len(cache) == 2 + ((key_1, value_1), (key_2, value_2)) = cache.items() + cat_value, tiger_value = ((value_1, value_2) if (key_1 is cat_key) + else (value_2, value_1)) + assert isinstance(tiger_value, Feline) + assert isinstance(cat_value, Feline) for thread in threads: thread.join() @@ -70,5 +135,4 @@ def run(self): assert threads[0].cat.uuid == threads[2].cat.uuid assert threads[1].tiger is threads[3].tiger assert threads[1].tiger.uuid == threads[3].tiger.uuid - - # blocktodo be asserting the in construction marker \ No newline at end of file + \ No newline at end of file From 864413126a4f12cf1628107cd0bdb67bbc61a5d6 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 2 Jul 2016 02:29:14 +0300 Subject: [PATCH 029/112] - --- .../nifty_collections/condition_list.py | 2 +- source_py3/python_toolbox/sequence_tools/misc.py | 12 ++++++++---- .../test_caching/test_cached_type.py | 4 +++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py index 44db17955..46d985abd 100644 --- a/source_py3/python_toolbox/nifty_collections/condition_list.py +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -87,7 +87,7 @@ def play_out(self, iterable): assert not self for item in iterable: self.append(item) - self.wait_for_empty() + self.wait_for_missing(item) def __repr__(self): diff --git a/source_py3/python_toolbox/sequence_tools/misc.py b/source_py3/python_toolbox/sequence_tools/misc.py index e30dc9a03..a877f81cd 100644 --- a/source_py3/python_toolbox/sequence_tools/misc.py +++ b/source_py3/python_toolbox/sequence_tools/misc.py @@ -374,15 +374,19 @@ def remove_items(items_to_remove, mutable_sequence, *, items_to_remove = list(items_to_remove) if ensure_contained_first: assert is_contained_in(items_to_remove, mutable_sequence) + indices_to_remove = [] for i, item in enumerate(mutable_sequence): try: items_to_remove.remove(item) except ValueError: pass else: - del mutable_sequence[i] + indices_to_remove.append(i) if not items_to_remove: - return - raise ValueError('Not all items were found, possibly some items were ' - 'removed.') + break + else: + raise ValueError("Not all items were found, the list wasn't changed.") + + for i in reversed(indices_to_remove): + del mutable_sequence[i] \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_caching/test_cached_type.py b/source_py3/test_python_toolbox/test_caching/test_cached_type.py index 5e335602c..cef78d265 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cached_type.py +++ b/source_py3/test_python_toolbox/test_caching/test_cached_type.py @@ -119,7 +119,9 @@ def run(self): assert isinstance(tiger_value, Feline) assert isinstance(cat_value, Feline) - condition_list.play_out(['t4go', 't4done']) + condition_list.play_out(['t4go']) + condition_list.wait_for('t4done', remove=True) + assert not condition_list assert len(cache) == 2 ((key_1, value_1), (key_2, value_2)) = cache.items() From 0249e6538339c6c1afdcc01710b864ec44f960d7 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 11 Jul 2016 21:02:35 +0300 Subject: [PATCH 030/112] - --- .../python_toolbox/context_management/context_manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source_py3/python_toolbox/context_management/context_manager.py b/source_py3/python_toolbox/context_management/context_manager.py index 06964a77e..32de7a18b 100644 --- a/source_py3/python_toolbox/context_management/context_manager.py +++ b/source_py3/python_toolbox/context_management/context_manager.py @@ -70,12 +70,12 @@ def __enter_using_manage_context(self): try: generator_return_value = next(new_generator) - return self if (generator_return_value is SelfHook) else \ - generator_return_value - except StopIteration: raise RuntimeError("The generator didn't yield even one time; it " "must yield one time exactly.") + else: + return self if (generator_return_value is SelfHook) else \ + generator_return_value def __exit_using_manage_context(self, exc_type, exc_value, exc_traceback): From 7e87d063274253766b883077c1b2394f69f7bf84 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 11 Jul 2016 21:10:51 +0300 Subject: [PATCH 031/112] - --- .../python_toolbox/context_management/context_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source_py3/python_toolbox/context_management/context_manager.py b/source_py3/python_toolbox/context_management/context_manager.py index 32de7a18b..600736d24 100644 --- a/source_py3/python_toolbox/context_management/context_manager.py +++ b/source_py3/python_toolbox/context_management/context_manager.py @@ -97,7 +97,8 @@ def __exit_using_manage_context(self, exc_type, exc_value, exc_traceback): raise RuntimeError( "The generator didn't stop after the yield; possibly you " "have more than one `yield` in the generator function? " - "The generator function must `yield` exactly one time.") + "The generator function must `yield` exactly one time." + ) else: if exc_value is None: # Need to force instantiation so we can reliably From 922b27c6821334f7ec5cd6a12386b7864629e2ef Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 12 Jul 2016 11:14:17 +0300 Subject: [PATCH 032/112] - --- .../python_toolbox/sequence_tools/misc.py | 15 +++++---- .../test_is_contained_in.py | 32 +++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 source_py3/test_python_toolbox/test_sequence_tools/test_is_contained_in.py diff --git a/source_py3/python_toolbox/sequence_tools/misc.py b/source_py3/python_toolbox/sequence_tools/misc.py index a877f81cd..bdffeab37 100644 --- a/source_py3/python_toolbox/sequence_tools/misc.py +++ b/source_py3/python_toolbox/sequence_tools/misc.py @@ -361,12 +361,15 @@ def is_subsequence(big_sequence, small_sequence): def is_contained_in(iterable, container): desired_items = list(iterable) - for item in container: - if item in desired_items: - desired_items.remove(item) - if not desired_items: - return True - return False + if isinstance(container, collections.abc.Iterable): + for item in container: + if item in desired_items: + desired_items.remove(item) + if not desired_items: + return True + return False + else: + return all(item in container for item in iterable) def remove_items(items_to_remove, mutable_sequence, *, ensure_contained_first=False): diff --git a/source_py3/test_python_toolbox/test_sequence_tools/test_is_contained_in.py b/source_py3/test_python_toolbox/test_sequence_tools/test_is_contained_in.py new file mode 100644 index 000000000..9787e18c2 --- /dev/null +++ b/source_py3/test_python_toolbox/test_sequence_tools/test_is_contained_in.py @@ -0,0 +1,32 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +from python_toolbox import cute_testing +from python_toolbox import sequence_tools + +from python_toolbox.sequence_tools import is_contained_in + +class PureContainer: + def __init__(self, inner): + self.inner = list(inner) + __contains__ = lambda self, item: item in self.inner + + +def test(): + true_examples = [ + ([1, 3, 6], range(10)), + (iter([1, 3, 6]), range(10)), + (range(4, 7), range(2, 11)), + (range(4, 7), PureContainer(range(2, 11))), + ] + false_examples = [ + ([11, 3, 6], range(10)), + ([1, 3, 6, 11], range(10)), + (iter([1, 3, 6, 11]), range(10)), + (range(4, 17), range(2, 11)), + (range(4, 17), PureContainer(range(2, 11))), + ] + for true_example in true_examples: + assert is_contained_in(*true_example) + for false_example in false_examples: + assert not is_contained_in(*false_example) \ No newline at end of file From f1f2d4ff435cb16e837dbd101ac9f05a9542ab52 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 12 Jul 2016 11:33:55 +0300 Subject: [PATCH 033/112] - --- .../Wing/python_toolbox_py3_wing6.wpr | 53 +++++++++++++++++++ .../python_toolbox/combi/chain_space.py | 2 + source_py3/python_toolbox/combi/map_space.py | 2 + .../context_management/__init__.py | 1 + .../blank_context_manager.py | 2 + .../context_management/context_manager.py | 2 + .../context_manager_type.py | 2 + .../context_manager_type_type.py | 2 + .../context_management/functions.py | 2 + .../context_management/modifiers.py | 2 + source_py3/python_toolbox/cute_iter_tools.py | 2 + source_py3/python_toolbox/cute_testing.py | 2 + source_py3/python_toolbox/dict_tools.py | 2 + source_py3/python_toolbox/file_tools.py | 2 + source_py3/python_toolbox/future_tools.py | 2 + .../emitting_weak_key_default_dict.py | 2 + .../nifty_collections/lazy_tuple.py | 2 + .../nifty_collections/various_ordered_sets.py | 2 + .../weak_key_default_dict.py | 2 + .../weak_key_identity_dict.py | 2 + source_py3/python_toolbox/queue_tools.py | 1 + .../python_toolbox/sequence_tools/misc.py | 16 +++++- .../sleek_reffing/cute_sleek_value_dict.py | 2 + source_py3/python_toolbox/sys_tools.py | 1 + source_py3/python_toolbox/temp_file_tools.py | 2 + .../test_combi/test_extensive.py | 2 + .../test_abstractness.py | 1 + .../test_as_idempotent.py | 2 + .../test_as_reentrant.py | 2 + .../test_context_manager.py | 2 + .../test_context_management/test_external.py | 2 + .../test_problematic_context_managers.py | 2 + .../test_cute_iter_tools/test_iter_with.py | 2 + .../test_cute_iter_tools/test_shorten.py | 2 + .../test_get_default_args_dict.py | 2 + .../test_nifty_collections/test_bagging.py | 2 + .../test_lazy_tuple/test_lazy_tuple.py | 2 + .../test_sequence_tools/test_remove_items.py | 16 ++++++ .../test_generic_dict_tests.py | 2 + 39 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 misc/IDE files/Wing/python_toolbox_py3_wing6.wpr create mode 100644 source_py3/test_python_toolbox/test_sequence_tools/test_remove_items.py diff --git a/misc/IDE files/Wing/python_toolbox_py3_wing6.wpr b/misc/IDE files/Wing/python_toolbox_py3_wing6.wpr new file mode 100644 index 000000000..0a104a906 --- /dev/null +++ b/misc/IDE files/Wing/python_toolbox_py3_wing6.wpr @@ -0,0 +1,53 @@ +#!wing +#!version=6.0 +################################################################## +# Wing IDE project file # +################################################################## +[project attributes] +debug.launch-configs = (1, + {'launch-OHU716PSo2P5T54y': ({}, + {'buildcmd': ('default', + None), + 'env': ('project', + [u'']), + 'name': u'Launch Config 1', + 'pyexec': ('default', + u''), + 'pypath': ('default', + ''), + 'pyrunargs': ('project', + u''), + 'runargs': u'', + 'rundir': ('default', + u'')})}) +proj.directory-list = [{'dirloc': loc('../../..'), + 'excludes': [u'nosetests.xml', + u'source_py2', + u'.coverage_html_report', + u'build', + u'source_py3/.coverage_html_report', + u'source_py3/python_toolbox.egg-info', + u'dist', + u'docs/_build', + u'python_toolbox.egg-info'], + 'filter': '*', + 'include_hidden': False, + 'recursive': True, + 'watch_for_changes': True}] +proj.file-type = 'shared' +proj.home-dir = loc('../../..') +proj.shared-attribute-names = ['proj.shared-attribute-names', + 'proj.directory-list', + 'proj.file-list', + 'proj.file-type', + 'proj.main-file', + 'proj.home-dir', + 'testing.auto-test-file-specs', + 'testing.test-file-list', + 'testing.test-framework', + 'debug.named-entry-points', + 'debug.launch-configs', + 'console.toolbox'] +testing.auto-test-file-specs = [('regex', + 'test_python_toolbox(/test[^/.]*)+[.]py')] +testing.test-framework = {None: 'nose'} diff --git a/source_py3/python_toolbox/combi/chain_space.py b/source_py3/python_toolbox/combi/chain_space.py index 30ed6064b..0f3ed7005 100644 --- a/source_py3/python_toolbox/combi/chain_space.py +++ b/source_py3/python_toolbox/combi/chain_space.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import collections from python_toolbox import binary_search diff --git a/source_py3/python_toolbox/combi/map_space.py b/source_py3/python_toolbox/combi/map_space.py index 14450f073..d682ab1af 100644 --- a/source_py3/python_toolbox/combi/map_space.py +++ b/source_py3/python_toolbox/combi/map_space.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import collections from python_toolbox import nifty_collections diff --git a/source_py3/python_toolbox/context_management/__init__.py b/source_py3/python_toolbox/context_management/__init__.py index 5a0f4aa82..6efa3edd8 100644 --- a/source_py3/python_toolbox/context_management/__init__.py +++ b/source_py3/python_toolbox/context_management/__init__.py @@ -122,6 +122,7 @@ def do_stuff(): # todo: for case of decorated generator, possibly make getstate (or whatever) # that will cause it to be pickled by reference to the decorated function +from __future__ import generator_stop from .abstract_context_manager import AbstractContextManager from .context_manager_type_type import ContextManagerTypeType diff --git a/source_py3/python_toolbox/context_management/blank_context_manager.py b/source_py3/python_toolbox/context_management/blank_context_manager.py index c7db2f823..15709e045 100644 --- a/source_py3/python_toolbox/context_management/blank_context_manager.py +++ b/source_py3/python_toolbox/context_management/blank_context_manager.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + from .context_manager import ContextManager diff --git a/source_py3/python_toolbox/context_management/context_manager.py b/source_py3/python_toolbox/context_management/context_manager.py index 600736d24..3f00a97e6 100644 --- a/source_py3/python_toolbox/context_management/context_manager.py +++ b/source_py3/python_toolbox/context_management/context_manager.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import sys import types import abc diff --git a/source_py3/python_toolbox/context_management/context_manager_type.py b/source_py3/python_toolbox/context_management/context_manager_type.py index 08ccd9e2d..219dfc610 100644 --- a/source_py3/python_toolbox/context_management/context_manager_type.py +++ b/source_py3/python_toolbox/context_management/context_manager_type.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import abc from .context_manager_type_type import ContextManagerTypeType diff --git a/source_py3/python_toolbox/context_management/context_manager_type_type.py b/source_py3/python_toolbox/context_management/context_manager_type_type.py index 9459972f8..c4b70321f 100644 --- a/source_py3/python_toolbox/context_management/context_manager_type_type.py +++ b/source_py3/python_toolbox/context_management/context_manager_type_type.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + class ContextManagerTypeType(type): ''' diff --git a/source_py3/python_toolbox/context_management/functions.py b/source_py3/python_toolbox/context_management/functions.py index b436960c1..5069c9ec1 100644 --- a/source_py3/python_toolbox/context_management/functions.py +++ b/source_py3/python_toolbox/context_management/functions.py @@ -7,6 +7,8 @@ See their documentation for more information. ''' +from __future__ import generator_stop + import sys from .context_manager_type import ContextManagerType diff --git a/source_py3/python_toolbox/context_management/modifiers.py b/source_py3/python_toolbox/context_management/modifiers.py index a4da246be..0458197b8 100644 --- a/source_py3/python_toolbox/context_management/modifiers.py +++ b/source_py3/python_toolbox/context_management/modifiers.py @@ -7,6 +7,8 @@ See their documentation for more information. ''' +from __future__ import generator_stop + import string import random diff --git a/source_py3/python_toolbox/cute_iter_tools.py b/source_py3/python_toolbox/cute_iter_tools.py index 9773c8840..ea0dc654c 100644 --- a/source_py3/python_toolbox/cute_iter_tools.py +++ b/source_py3/python_toolbox/cute_iter_tools.py @@ -5,6 +5,8 @@ # todo: make something like `filter` except it returns first found, or raises # exception +from __future__ import generator_stop + import collections import operator import itertools diff --git a/source_py3/python_toolbox/cute_testing.py b/source_py3/python_toolbox/cute_testing.py index 0e1ec1dc2..dca2aab34 100644 --- a/source_py3/python_toolbox/cute_testing.py +++ b/source_py3/python_toolbox/cute_testing.py @@ -3,6 +3,8 @@ '''This module defines tools for testing.''' +from __future__ import generator_stop + import sys from python_toolbox.third_party import unittest2 diff --git a/source_py3/python_toolbox/dict_tools.py b/source_py3/python_toolbox/dict_tools.py index 8089f5583..5bf4657ee 100644 --- a/source_py3/python_toolbox/dict_tools.py +++ b/source_py3/python_toolbox/dict_tools.py @@ -3,6 +3,8 @@ '''Defines several functions that may be useful when working with dicts.''' +from __future__ import generator_stop + import collections from python_toolbox import cute_iter_tools diff --git a/source_py3/python_toolbox/file_tools.py b/source_py3/python_toolbox/file_tools.py index 748a128aa..bc9e5696c 100644 --- a/source_py3/python_toolbox/file_tools.py +++ b/source_py3/python_toolbox/file_tools.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + try: import pathlib except: diff --git a/source_py3/python_toolbox/future_tools.py b/source_py3/python_toolbox/future_tools.py index bbca84c49..c86f09f39 100644 --- a/source_py3/python_toolbox/future_tools.py +++ b/source_py3/python_toolbox/future_tools.py @@ -5,6 +5,8 @@ Defines tools related to the `concurrent.futures` standard library package. ''' +from __future__ import generator_stop + import time import concurrent.futures diff --git a/source_py3/python_toolbox/nifty_collections/emitting_weak_key_default_dict.py b/source_py3/python_toolbox/nifty_collections/emitting_weak_key_default_dict.py index 03d356ce0..1f89ee472 100644 --- a/source_py3/python_toolbox/nifty_collections/emitting_weak_key_default_dict.py +++ b/source_py3/python_toolbox/nifty_collections/emitting_weak_key_default_dict.py @@ -7,6 +7,8 @@ See its documentation for more details. ''' +from __future__ import generator_stop + from .weak_key_default_dict import WeakKeyDefaultDict diff --git a/source_py3/python_toolbox/nifty_collections/lazy_tuple.py b/source_py3/python_toolbox/nifty_collections/lazy_tuple.py index 1bf2593c5..265bbddd3 100644 --- a/source_py3/python_toolbox/nifty_collections/lazy_tuple.py +++ b/source_py3/python_toolbox/nifty_collections/lazy_tuple.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import functools import threading import collections diff --git a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py index f1f64fe5f..795024e40 100644 --- a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py +++ b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import collections import operator import itertools diff --git a/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py b/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py index e0d270608..a90d48e5b 100644 --- a/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py +++ b/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py @@ -8,6 +8,8 @@ ''' # todo: revamp +from __future__ import generator_stop + import collections from weakref import ref diff --git a/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py b/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py index 508f1346e..10fe0a875 100644 --- a/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py +++ b/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py @@ -8,6 +8,8 @@ ''' # todo: revamp +from __future__ import generator_stop + import weakref import collections diff --git a/source_py3/python_toolbox/queue_tools.py b/source_py3/python_toolbox/queue_tools.py index 03426c877..0d3504718 100644 --- a/source_py3/python_toolbox/queue_tools.py +++ b/source_py3/python_toolbox/queue_tools.py @@ -3,6 +3,7 @@ '''Defines various functions for working with queues.''' +from __future__ import generator_stop import queue as queue_module import sys diff --git a/source_py3/python_toolbox/sequence_tools/misc.py b/source_py3/python_toolbox/sequence_tools/misc.py index bdffeab37..e3a44723d 100644 --- a/source_py3/python_toolbox/sequence_tools/misc.py +++ b/source_py3/python_toolbox/sequence_tools/misc.py @@ -360,6 +360,12 @@ def is_subsequence(big_sequence, small_sequence): def is_contained_in(iterable, container): + ''' + Check whether all the items in `iterable` are in `container`. + + Attempts an efficient implementation for the case where container might be + big and not optimized for lookups. + ''' desired_items = list(iterable) if isinstance(container, collections.abc.Iterable): for item in container: @@ -372,10 +378,16 @@ def is_contained_in(iterable, container): return all(item in container for item in iterable) def remove_items(items_to_remove, mutable_sequence, *, - ensure_contained_first=False): + assert_contained_first=False): + ''' + Remove all the items in `items_to_remove` from `mutable_sequence`. + + Attempts an efficient implementation for the case where `mutable_sequence` + might be big and not optimized for looking up an item and removing it. + ''' assert isinstance(mutable_sequence, collections.abc.MutableSequence) items_to_remove = list(items_to_remove) - if ensure_contained_first: + if assert_contained_first: assert is_contained_in(items_to_remove, mutable_sequence) indices_to_remove = [] for i, item in enumerate(mutable_sequence): diff --git a/source_py3/python_toolbox/sleek_reffing/cute_sleek_value_dict.py b/source_py3/python_toolbox/sleek_reffing/cute_sleek_value_dict.py index ea9db3db0..66fee0ab1 100644 --- a/source_py3/python_toolbox/sleek_reffing/cute_sleek_value_dict.py +++ b/source_py3/python_toolbox/sleek_reffing/cute_sleek_value_dict.py @@ -7,6 +7,8 @@ See its documentation for more details. ''' +from __future__ import generator_stop + import weakref import collections diff --git a/source_py3/python_toolbox/sys_tools.py b/source_py3/python_toolbox/sys_tools.py index 0cc844f39..f38a08bc3 100644 --- a/source_py3/python_toolbox/sys_tools.py +++ b/source_py3/python_toolbox/sys_tools.py @@ -3,6 +3,7 @@ '''Defines various `sys`-related tools.''' +from __future__ import generator_stop import sys try: diff --git a/source_py3/python_toolbox/temp_file_tools.py b/source_py3/python_toolbox/temp_file_tools.py index 52c44dc41..0c20f1543 100644 --- a/source_py3/python_toolbox/temp_file_tools.py +++ b/source_py3/python_toolbox/temp_file_tools.py @@ -3,6 +3,8 @@ '''Defines various tools related to temporary files.''' +from __future__ import generator_stop + import tempfile import shutil try: diff --git a/source_py3/test_python_toolbox/test_combi/test_extensive.py b/source_py3/test_python_toolbox/test_combi/test_extensive.py index 2be1482dc..9940e1597 100644 --- a/source_py3/test_python_toolbox/test_combi/test_extensive.py +++ b/source_py3/test_python_toolbox/test_combi/test_extensive.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import pickle import itertools import collections diff --git a/source_py3/test_python_toolbox/test_context_management/test_abstractness.py b/source_py3/test_python_toolbox/test_context_management/test_abstractness.py index f30dc88a8..7728bfd67 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_abstractness.py +++ b/source_py3/test_python_toolbox/test_context_management/test_abstractness.py @@ -3,6 +3,7 @@ '''Module for testing the abstract methods of `ContextManager`.''' +from __future__ import generator_stop import sys diff --git a/source_py3/test_python_toolbox/test_context_management/test_as_idempotent.py b/source_py3/test_python_toolbox/test_context_management/test_as_idempotent.py index de0007ba3..ace80d9a1 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_as_idempotent.py +++ b/source_py3/test_python_toolbox/test_context_management/test_as_idempotent.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import queue as queue_module from python_toolbox.context_management import (as_idempotent, ContextManager, diff --git a/source_py3/test_python_toolbox/test_context_management/test_as_reentrant.py b/source_py3/test_python_toolbox/test_context_management/test_as_reentrant.py index 104926870..b279fdfd6 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_as_reentrant.py +++ b/source_py3/test_python_toolbox/test_context_management/test_as_reentrant.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import queue as queue_module from python_toolbox.context_management import (as_reentrant, ContextManager, diff --git a/source_py3/test_python_toolbox/test_context_management/test_context_manager.py b/source_py3/test_python_toolbox/test_context_management/test_context_manager.py index d757085f3..cbcd0b71c 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_context_manager.py +++ b/source_py3/test_python_toolbox/test_context_management/test_context_manager.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + from python_toolbox import cute_testing from python_toolbox.context_management import (ContextManager, diff --git a/source_py3/test_python_toolbox/test_context_management/test_external.py b/source_py3/test_python_toolbox/test_context_management/test_external.py index 4db70ea60..bc4d6c9ea 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_external.py +++ b/source_py3/test_python_toolbox/test_context_management/test_external.py @@ -3,6 +3,8 @@ '''Tests taken from Python's `contextlib'.''' +from __future__ import generator_stop + import sys import nose diff --git a/source_py3/test_python_toolbox/test_context_management/test_problematic_context_managers.py b/source_py3/test_python_toolbox/test_context_management/test_problematic_context_managers.py index a439fadf4..99965ab81 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_problematic_context_managers.py +++ b/source_py3/test_python_toolbox/test_context_management/test_problematic_context_managers.py @@ -3,6 +3,8 @@ '''Testing module for various problematic context managers.''' +from __future__ import generator_stop + import nose from python_toolbox import cute_testing diff --git a/source_py3/test_python_toolbox/test_cute_iter_tools/test_iter_with.py b/source_py3/test_python_toolbox/test_cute_iter_tools/test_iter_with.py index 76f9c0709..ec27cff4b 100644 --- a/source_py3/test_python_toolbox/test_cute_iter_tools/test_iter_with.py +++ b/source_py3/test_python_toolbox/test_cute_iter_tools/test_iter_with.py @@ -3,6 +3,8 @@ '''Testing module for `cute_iter_tools.iter_with`.''' +from __future__ import generator_stop + import itertools from python_toolbox import nifty_collections diff --git a/source_py3/test_python_toolbox/test_cute_iter_tools/test_shorten.py b/source_py3/test_python_toolbox/test_cute_iter_tools/test_shorten.py index 342f2896a..02ab4b71c 100644 --- a/source_py3/test_python_toolbox/test_cute_iter_tools/test_shorten.py +++ b/source_py3/test_python_toolbox/test_cute_iter_tools/test_shorten.py @@ -3,6 +3,8 @@ '''Testing module for `python_toolbox.cute_iter_tools.shorten`.''' +from __future__ import generator_stop + import nose.tools from python_toolbox import nifty_collections diff --git a/source_py3/test_python_toolbox/test_introspection_tools/test_get_default_args_dict.py b/source_py3/test_python_toolbox/test_introspection_tools/test_get_default_args_dict.py index e35d732c4..3d582f80e 100644 --- a/source_py3/test_python_toolbox/test_introspection_tools/test_get_default_args_dict.py +++ b/source_py3/test_python_toolbox/test_introspection_tools/test_get_default_args_dict.py @@ -3,6 +3,8 @@ '''Testing for `python_toolbox.introspection_tools.get_default_args_dict`.''' +from __future__ import generator_stop + from python_toolbox.introspection_tools import get_default_args_dict from python_toolbox.nifty_collections import OrderedDict diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py b/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py index 8b6d7752c..854e3f9c2 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from __future__ import generator_stop + import re import pickle import abc diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py b/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py index 5fed1cd23..6e4837a70 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py @@ -3,6 +3,8 @@ '''Testing module for `python_toolbox.nifty_collections.LazyTuple`.''' +from __future__ import generator_stop + import uuid import itertools import collections diff --git a/source_py3/test_python_toolbox/test_sequence_tools/test_remove_items.py b/source_py3/test_python_toolbox/test_sequence_tools/test_remove_items.py new file mode 100644 index 000000000..e183620f6 --- /dev/null +++ b/source_py3/test_python_toolbox/test_sequence_tools/test_remove_items.py @@ -0,0 +1,16 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +from python_toolbox import cute_testing +from python_toolbox import sequence_tools + +from python_toolbox.sequence_tools import remove_items + +def test(): + x = list(range(10)) + remove_items(range(4, 8), x) + assert x == [0, 1, 2, 3, 8, 9] + + x = list(range(10)) + remove_items(range(4, 8), x, assert_contained_first=True) + assert x == [0, 1, 2, 3, 8, 9] \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/test_generic_dict_tests.py b/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/test_generic_dict_tests.py index 8467c6df1..9b916f5aa 100644 --- a/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/test_generic_dict_tests.py +++ b/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/test_generic_dict_tests.py @@ -3,6 +3,8 @@ '''Run generic `dict` tests on `CuteSleekValueDict`.''' +from __future__ import generator_stop + import sys import random import string From 63497f147a14562902c25b25e95aa349061b89da Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 12 Jul 2016 11:35:57 +0300 Subject: [PATCH 034/112] - --- source_py3/python_toolbox/temp_file_tools.py | 2 +- .../test_temp_file_tools/test_create_temp_folder.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/source_py3/python_toolbox/temp_file_tools.py b/source_py3/python_toolbox/temp_file_tools.py index 0c20f1543..e80852548 100644 --- a/source_py3/python_toolbox/temp_file_tools.py +++ b/source_py3/python_toolbox/temp_file_tools.py @@ -48,7 +48,7 @@ def create_temp_folder(*, prefix=tempfile.template, suffix='', ''' temp_folder = pathlib.Path(tempfile.mkdtemp(prefix=prefix, suffix=suffix, - dir=parent_folder)) + dir=str(parent_folder))) try: if chmod is not None: temp_folder.chmod(chmod) diff --git a/source_py3/test_python_toolbox/test_temp_file_tools/test_create_temp_folder.py b/source_py3/test_python_toolbox/test_temp_file_tools/test_create_temp_folder.py index 73477b5c5..0a3131bb2 100644 --- a/source_py3/test_python_toolbox/test_temp_file_tools/test_create_temp_folder.py +++ b/source_py3/test_python_toolbox/test_temp_file_tools/test_create_temp_folder.py @@ -116,6 +116,12 @@ def test_parent_folder(): with create_temp_folder(parent_folder=str(tf1)) as tf2: assert isinstance(tf2, pathlib.Path) assert str(tf2).startswith(str(tf1)) + +def test_parent_folder_pathlib(): + with create_temp_folder() as tf1: + with create_temp_folder(parent_folder=tf1) as tf2: + assert isinstance(tf2, pathlib.Path) + assert str(tf2).startswith(str(tf1)) def test_chmod(): with create_temp_folder(chmod=0o777) as liberal_temp_folder, \ From e627c5e750bd46858a0192244e1b0f2ff758a72e Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Fri, 15 Jul 2016 11:26:24 +0300 Subject: [PATCH 035/112] - --- source_py3/python_toolbox/temp_file_tools.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source_py3/python_toolbox/temp_file_tools.py b/source_py3/python_toolbox/temp_file_tools.py index e80852548..5e421b4d9 100644 --- a/source_py3/python_toolbox/temp_file_tools.py +++ b/source_py3/python_toolbox/temp_file_tools.py @@ -47,8 +47,13 @@ def create_temp_folder(*, prefix=tempfile.template, suffix='', create_temp_folder(chmod=0o550) ''' - temp_folder = pathlib.Path(tempfile.mkdtemp(prefix=prefix, suffix=suffix, - dir=str(parent_folder))) + temp_folder = pathlib.Path( + tempfile.mkdtemp( + prefix=prefix, + suffix=suffix, + dir=str(parent_folder) if parent_folder is not None else None, + ) + ) try: if chmod is not None: temp_folder.chmod(chmod) From b183ef2122410215742c702e67008f3975e34480 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Fri, 15 Jul 2016 11:38:38 +0300 Subject: [PATCH 036/112] - --- source_py3/python_toolbox/logic_tools.py | 32 +++++++++++------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/source_py3/python_toolbox/logic_tools.py b/source_py3/python_toolbox/logic_tools.py index 0154755a6..cc8bc9d95 100644 --- a/source_py3/python_toolbox/logic_tools.py +++ b/source_py3/python_toolbox/logic_tools.py @@ -60,8 +60,8 @@ def all_equivalent(iterable, relation=operator.eq, *, assume_reflexive=True, return all(itertools.starmap(relation, pairs)) -def get_equivalence_classes(iterable, key=None, container=set, *, - use_ordered_dict=False, sort_ordered_dict=False): +def get_equivalence_classes(iterable, key=None, *, + big_container=dict, small_container=set): ''' Divide items in `iterable` to equivalence classes, using the key function. @@ -106,32 +106,30 @@ def get_equivalence_classes(iterable, key=None, container=set, *, # # if key is None: if isinstance(iterable, collections.abc.Mapping): - d = iterable + items = d.items() else: - try: - d = dict(iterable) - except ValueError: - raise Exception( - "You can't put in a non-dict without also supplying a " - "`key` function. We need to know which key to use." - ) + raise Exception( + "You can't put in a non-dict without also supplying a " + "`key` function. We need to know which key to use." + ) else: # key is not None assert cute_iter_tools.is_iterable(iterable) key_function = comparison_tools.process_key_function_or_attribute_name( key ) - d = {key: key_function(key) for key in iterable} + items = ((key, key_function(key)) for key in iterable) # # ### Finished pre-processing input. ######################################## - if use_ordered_dict or sort_ordered_dict: - from python_toolbox import nifty_collections - new_dict = nifty_collections.OrderedDict() + if isinstance(big_container, type): + new_dict = big_container() else: - new_dict = {} - for key, value in d.items(): - new_dict.setdefault(value, []).append(key) + assert isinstance(big_container, collections.abc.Mapping) + new_dict = big_container + for key, value in items: + new_dict.setdefault(value, []).append(key) + 1 / 0 i was here # Making into desired container: for key, value in new_dict.copy().items(): new_dict[key] = container(value) From 693f0d38fba097d511318b42bb5eebcc9862f5d0 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 17:05:13 +0300 Subject: [PATCH 037/112] - --- source_py3/python_toolbox/logic_tools.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source_py3/python_toolbox/logic_tools.py b/source_py3/python_toolbox/logic_tools.py index cc8bc9d95..96140fe7a 100644 --- a/source_py3/python_toolbox/logic_tools.py +++ b/source_py3/python_toolbox/logic_tools.py @@ -65,6 +65,10 @@ def get_equivalence_classes(iterable, key=None, *, ''' Divide items in `iterable` to equivalence classes, using the key function. + blocktododoc, changed lots of arguments so change docs + + big_container can be a type or an existing instance, do tests for both + Each item will be put in a set with all other items that had the same result when put through the `key` function. @@ -99,11 +103,17 @@ def get_equivalence_classes(iterable, key=None, *, `use_ordered_dict=True`.) You can also pass in a sorting key function or attribute name as the `sort_ordered_dict` argument. ''' - + # blocktodo: Let's try to not build the small container twice (build list + # and then feed to small container), instead let's try to make multiple + # smart iterators. + from python_toolbox import comparison_tools ### Pre-processing input: ################################################# # # + assert (isinstance(big_container, collections.abc.Mapping) or + issubclass(big_container, collections.abc.Mapping)) + assert issubclass(small_container, collections.abc.Iterable) if key is None: if isinstance(iterable, collections.abc.Mapping): items = d.items() From 50482ef288c095d5bbf86be5b58f7632a16a88be Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 17:19:07 +0300 Subject: [PATCH 038/112] - --- source_py3/python_toolbox/logic_tools.py | 29 ++--------- .../test_get_equivalence_classes.py | 51 +++++++++++-------- 2 files changed, 34 insertions(+), 46 deletions(-) diff --git a/source_py3/python_toolbox/logic_tools.py b/source_py3/python_toolbox/logic_tools.py index 96140fe7a..5b924b097 100644 --- a/source_py3/python_toolbox/logic_tools.py +++ b/source_py3/python_toolbox/logic_tools.py @@ -103,20 +103,14 @@ def get_equivalence_classes(iterable, key=None, *, `use_ordered_dict=True`.) You can also pass in a sorting key function or attribute name as the `sort_ordered_dict` argument. ''' - # blocktodo: Let's try to not build the small container twice (build list - # and then feed to small container), instead let's try to make multiple - # smart iterators. - from python_toolbox import comparison_tools - ### Pre-processing input: ################################################# - # # assert (isinstance(big_container, collections.abc.Mapping) or issubclass(big_container, collections.abc.Mapping)) assert issubclass(small_container, collections.abc.Iterable) if key is None: if isinstance(iterable, collections.abc.Mapping): - items = d.items() + items = iterable.items() else: raise Exception( "You can't put in a non-dict without also supplying a " @@ -128,8 +122,6 @@ def get_equivalence_classes(iterable, key=None, *, key ) items = ((key, key_function(key)) for key in iterable) - # # - ### Finished pre-processing input. ######################################## if isinstance(big_container, type): new_dict = big_container() @@ -139,22 +131,11 @@ def get_equivalence_classes(iterable, key=None, *, for key, value in items: new_dict.setdefault(value, []).append(key) - 1 / 0 i was here - # Making into desired container: - for key, value in new_dict.copy().items(): - new_dict[key] = container(value) - - if sort_ordered_dict: - if isinstance(sort_ordered_dict, (collections.abc.Callable, str)): - key_function = comparison_tools. \ - process_key_function_or_attribute_name(sort_ordered_dict) - new_dict.sort(key_function) - elif sort_ordered_dict is True: - new_dict.sort() - return new_dict - else: - return new_dict + for key, value in tuple(new_dict.items()): + new_dict[key] = small_container(value) + + return new_dict diff --git a/source_py3/test_python_toolbox/test_logic_tools/test_get_equivalence_classes.py b/source_py3/test_python_toolbox/test_logic_tools/test_get_equivalence_classes.py index a3ee5b28e..2c0905dcd 100644 --- a/source_py3/test_python_toolbox/test_logic_tools/test_get_equivalence_classes.py +++ b/source_py3/test_python_toolbox/test_logic_tools/test_get_equivalence_classes.py @@ -30,43 +30,50 @@ def test_iterable_input(): == {0: {1, 4}, 3: {2+3j}, -6: {5-6j}} -def test_ordered_dict_output(): +def test_big_container(): # Insertion order: - assert get_equivalence_classes( - nifty_collections.OrderedDict(((1, 2), (3, 4), ('meow', 2))), - use_ordered_dict=True) == \ - nifty_collections.OrderedDict([(2, {1, 'meow'}), (4, {3})]) + assert ( + get_equivalence_classes( + nifty_collections.OrderedDict(((1, 2), (3, 4), ('meow', 2))), + big_container=nifty_collections.OrderedDict + ) == + get_equivalence_classes( + nifty_collections.OrderedDict(((1, 2), (3, 4), ('meow', 2))), + big_container=nifty_collections.OrderedDict() + ) == nifty_collections.OrderedDict([(2, {1, 'meow'}), (4, {3})]) + ) + + assert ( + get_equivalence_classes( + nifty_collections.OrderedDict(((1, 2), (3, 4), ('meow', 2))), + big_container=dict + ) == + get_equivalence_classes( + nifty_collections.OrderedDict(((1, 2), (3, 4), ('meow', 2))), + big_container={} + ) == {2: {1, 'meow'}, 4: {3}} + ) assert get_equivalence_classes( nifty_collections.OrderedDict((('meow', 2), (1, 2), (3, 4))), - use_ordered_dict=True) == \ - nifty_collections.OrderedDict([(2, {1, 'meow'}), (4, {3})]) + big_container=nifty_collections.OrderedDict((('foo', 'bar'),))) == \ + nifty_collections.OrderedDict([('foo', 'bar'), (2, {1, 'meow'}), (4, {3})]) assert get_equivalence_classes( nifty_collections.OrderedDict(((3, 4), (1, 2), ('meow', 2))), - use_ordered_dict=True) == \ + big_container=nifty_collections.OrderedDict) == \ nifty_collections.OrderedDict([(4, {3}), (2, {1, 'meow'})]) assert get_equivalence_classes( nifty_collections.OrderedDict(((1, 2), (3, 4), ('meow', 2))), - container=tuple, - use_ordered_dict=True) == \ + small_container=tuple, + big_container=nifty_collections.OrderedDict) == \ nifty_collections.OrderedDict([(2, (1, 'meow')), (4, (3,))]) assert get_equivalence_classes( nifty_collections.OrderedDict((('meow', 2), (1, 2), (3, 4))), container=tuple, - use_ordered_dict=True) == \ - nifty_collections.OrderedDict([(2, ('meow', 1)), (4, (3,))]) + big_container=nifty_collections.FrozenOrderedDict) == \ + nifty_collections.FrozenOrderedDict([(2, ('meow', 1)), (4, (3,))]) - # Sorting: - - assert get_equivalence_classes({1: 2, 3: 4, 'meow': 2}, - sort_ordered_dict=True) == \ - nifty_collections.OrderedDict([(2, {1, 'meow'}), (4, {3})]) - - assert get_equivalence_classes({1: 2, 3: 4, 'meow': 2}, - sort_ordered_dict=lambda x: -x) == \ - nifty_collections.OrderedDict([(4, {3}), (2, {1, 'meow'})]) - \ No newline at end of file From c7c212abe7b2d5c4b70ac9f31747b17f31c49402 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 18:12:36 +0300 Subject: [PATCH 039/112] - --- source_py3/python_toolbox/logic_tools.py | 72 ++++++++++++------- .../test_get_equivalence_classes.py | 6 +- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/source_py3/python_toolbox/logic_tools.py b/source_py3/python_toolbox/logic_tools.py index 5b924b097..c4585bed9 100644 --- a/source_py3/python_toolbox/logic_tools.py +++ b/source_py3/python_toolbox/logic_tools.py @@ -65,10 +65,6 @@ def get_equivalence_classes(iterable, key=None, *, ''' Divide items in `iterable` to equivalence classes, using the key function. - blocktododoc, changed lots of arguments so change docs - - big_container can be a type or an existing instance, do tests for both - Each item will be put in a set with all other items that had the same result when put through the `key` function. @@ -95,18 +91,41 @@ def get_equivalence_classes(iterable, key=None, *, >>> get_equivalence_classes({1: 2, 3: 4, 'meow': 2}) {2: {1, 'meow'}, 4: {3}} - - If you'd like the result to be in an `OrderedDict`, specify - `use_ordered_dict=True`, and the items will be ordered according to - insertion order. If you'd like that `OrderedDict` to be sorted, pass in - `sort_ordered_dict=True`. (It automatically implies - `use_ordered_dict=True`.) You can also pass in a sorting key function or - attribute name as the `sort_ordered_dict` argument. + You can use optional arguments `small_container` and `big_container` to + customize the containers used in the result. `big_container` is `dict` by + default, but you can use alternative dict types like `OrderedDict`, + `defaultdict`, `SortedDict` or any other kind of mapping. Example: + + >>> from python_toolbox.nifty_collections import OrderedDict + >>> get_equivalence_classes(range(10), lambda x: x % 3, + big_container=OrderedDict) + OrderedDict([(0, {0, 9, 3, 6}), (1, {1, 4, 7}), (2, {8, 2, 5})]) + + You can pass in either the type of mapping, or an existing instance, and + then the existing items will still be there and have the equivalence + classes added to them. + + `small_container` is the container in which the items are put inside the + mapping. By default it's `set` but you can specify any other kind of + container. If you something like `OrderedSet`, the items will be inserted + in the same order that they were in the original `iterable`. Example: + + >>> get_equivalence_classes(range(10), lambda x: x % 3, + small_container=tuple) + {0: (0, 3, 6, 9), 1: (1, 4, 7), 2: (2, 5, 8)} + ''' from python_toolbox import comparison_tools + from python_toolbox import nifty_collections - assert (isinstance(big_container, collections.abc.Mapping) or - issubclass(big_container, collections.abc.Mapping)) + if isinstance(big_container, collections.abc.Mapping): + big_container_type = type(big_container) + big_container_instance = big_container + else: + big_container_type = big_container + big_container_instance = None + assert issubclass(big_container_type, collections.abc.Mapping) + assert issubclass(small_container, collections.abc.Iterable) if key is None: if isinstance(iterable, collections.abc.Mapping): @@ -123,20 +142,23 @@ def get_equivalence_classes(iterable, key=None, *, ) items = ((key, key_function(key)) for key in iterable) - if isinstance(big_container, type): - new_dict = big_container() - else: - assert isinstance(big_container, collections.abc.Mapping) - new_dict = big_container - + # If we know our big container isn't ordered, we can save some performance + # and use a dict as our pre-dict, otherwise play it safe and use an ordered + # dict. + pre_dict = ({} if big_container_type in {dict, collections.defaultdict} + else nifty_collections.OrderedDict()) for key, value in items: - new_dict.setdefault(value, []).append(key) - - for key, value in tuple(new_dict.items()): - new_dict[key] = small_container(value) - - return new_dict + pre_dict.setdefault(value, []).append(key) + if big_container_instance is None: + big_container_instance = big_container_type( + ((key, small_container(value)) for key, value in pre_dict.items()) + ) + else: + for key, value in pre_dict.items(): + big_container_instance[key] = small_container(value) + + return big_container_instance def logic_max(iterable, relation=lambda a, b: (a >= b)): diff --git a/source_py3/test_python_toolbox/test_logic_tools/test_get_equivalence_classes.py b/source_py3/test_python_toolbox/test_logic_tools/test_get_equivalence_classes.py index 2c0905dcd..703d44f8f 100644 --- a/source_py3/test_python_toolbox/test_logic_tools/test_get_equivalence_classes.py +++ b/source_py3/test_python_toolbox/test_logic_tools/test_get_equivalence_classes.py @@ -30,9 +30,7 @@ def test_iterable_input(): == {0: {1, 4}, 3: {2+3j}, -6: {5-6j}} -def test_big_container(): - # Insertion order: - +def test_big_container_small_container(): assert ( get_equivalence_classes( nifty_collections.OrderedDict(((1, 2), (3, 4), ('meow', 2))), @@ -73,7 +71,7 @@ def test_big_container(): assert get_equivalence_classes( nifty_collections.OrderedDict((('meow', 2), (1, 2), (3, 4))), - container=tuple, + small_container=tuple, big_container=nifty_collections.FrozenOrderedDict) == \ nifty_collections.FrozenOrderedDict([(2, ('meow', 1)), (4, (3,))]) From ec6c5b4fc866f23871a550023b1de015ddc14a45 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 18:21:57 +0300 Subject: [PATCH 040/112] - --- .../nifty_collections/various_frozen_dicts.py | 14 ++++++++++++-- .../test_nifty_collections/test_frozen_dict.py | 13 ++++++++++++- .../test_frozen_ordered_dict.py | 10 ++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py b/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py index 33f3f0e9a..9071d317b 100644 --- a/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py @@ -42,7 +42,7 @@ def __hash__(self): return self._hash __repr__ = lambda self: '%s(%s)' % (type(self).__name__, - repr(self._dict)) + repr(self._dict) if self._dict else '') __reduce__ = lambda self: (self.__class__ , (self._dict,)) @@ -89,4 +89,14 @@ def reversed(self): ''' if self._reversed is None: self._reversed = type(self)(reversed(tuple(self.items()))) - return self._reversed \ No newline at end of file + return self._reversed + + def __repr__(self): + if self._dict: + inner = '[%s]' % ', '.join( + '(%s, %s)' % item for item in self._dict.items() + ) + else: + inner = '' + return '%s(%s)' % (type(self).__name__, inner) + \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_dict.py b/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_dict.py index 266985bcb..7fe105f96 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_dict.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_dict.py @@ -33,4 +33,15 @@ def test(): assert repr(frozen_dict).startswith('FrozenDict(') - assert pickle.loads(pickle.dumps(frozen_dict)) == frozen_dict \ No newline at end of file + assert pickle.loads(pickle.dumps(frozen_dict)) == frozen_dict + +def test_repr(): + d1 = FrozenDict(((4, 3), (2, 1))) + assert repr(d1) in {'FrozenDict({4: 3, 2: 1})', + 'FrozenDict({2: 1, 4: 3})'} + + d2 = FrozenDict() + assert repr(d2) == 'FrozenDict()' + + + \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_ordered_dict.py b/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_ordered_dict.py index d53991326..f71c4c9cc 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_ordered_dict.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_ordered_dict.py @@ -65,3 +65,13 @@ def test_reversed(): tuple(reversed(tuple(frozen_ordered_dict.reversed.items()))) assert type(frozen_ordered_dict.reversed) is type(frozen_ordered_dict) \ is FrozenOrderedDict + +def test_repr(): + d1 = FrozenOrderedDict(((4, 3), (2, 1))) + assert repr(d1) == 'FrozenOrderedDict([(4, 3), (2, 1)])' + + d2 = FrozenOrderedDict() + assert repr(d2) == 'FrozenOrderedDict()' + + + \ No newline at end of file From 8de6bcc64d61b65ebfb2817f0bb7ab190c3b50f4 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 18:56:44 +0300 Subject: [PATCH 041/112] - --- .../python_toolbox/function_tools/__init__.py | 5 + .../call_args.py} | 106 +++++++++++------- .../misc.py} | 2 - .../__init__.py | 0 .../test_get_default_args_dict.py | 2 +- 5 files changed, 71 insertions(+), 44 deletions(-) create mode 100644 source_py3/python_toolbox/function_tools/__init__.py rename source_py3/python_toolbox/{sleek_reffing/sleek_call_args.py => function_tools/call_args.py} (63%) rename source_py3/python_toolbox/{introspection_tools.py => function_tools/misc.py} (93%) rename source_py3/test_python_toolbox/{test_introspection_tools => test_function_tools}/__init__.py (100%) rename source_py3/test_python_toolbox/{test_introspection_tools => test_function_tools}/test_get_default_args_dict.py (93%) diff --git a/source_py3/python_toolbox/function_tools/__init__.py b/source_py3/python_toolbox/function_tools/__init__.py new file mode 100644 index 000000000..bba57046c --- /dev/null +++ b/source_py3/python_toolbox/function_tools/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +from .misc import get_default_args_dict +from .call_args import SleekCallArgs, CallArgs \ No newline at end of file diff --git a/source_py3/python_toolbox/sleek_reffing/sleek_call_args.py b/source_py3/python_toolbox/function_tools/call_args.py similarity index 63% rename from source_py3/python_toolbox/sleek_reffing/sleek_call_args.py rename to source_py3/python_toolbox/function_tools/call_args.py index 11a55f975..119566ef1 100644 --- a/source_py3/python_toolbox/sleek_reffing/sleek_call_args.py +++ b/source_py3/python_toolbox/function_tools/call_args.py @@ -1,24 +1,19 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -''' -Defines the `SleekCallArgs` class. - -See its documentation for more details. -''' +import abc from python_toolbox import cute_inspect from python_toolbox import cheat_hashing +from python_toolbox import nifty_collections from .sleek_ref import SleekRef from .cute_sleek_value_dict import CuteSleekValueDict -__all__ = ['SleekCallArgs'] - - -class SleekCallArgs: +class BaseCallArgs(metaclass=abc.ABCMeta): ''' + blocktododoc A bunch of call args with a sleekref to them. "Call args" is a mapping of which function arguments get which values. @@ -32,13 +27,18 @@ def f(a, b=2): All the argument values are sleekreffed to avoid memory leaks. (See documentation of `python_toolbox.sleek_reffing.SleekRef` for more details.) ''' + + make_ref = None + make_dict = None + hash_function = None + # What if we one of the args gets gc'ed before this SCA gets added to the # dictionary? It will render this SCA invalid, but we'll still be in the # dict. So make note to user: Always keep reference to args and kwargs # until the SCA gets added to the dict. def __init__(self, containing_dict, function, *args, **kwargs): ''' - Construct the `SleekCallArgs`. + Construct the `BaseCallArgs`. `containing_dict` is the `dict` we'll try to remove ourselves from when one of our sleekrefs dies. `function` is the function for which we @@ -51,8 +51,8 @@ def __init__(self, containing_dict, function, *args, **kwargs): ''' args_spec = cute_inspect.getargspec(function) - star_args_name, star_kwargs_name = \ - args_spec.varargs, args_spec.keywords + star_args_name, star_kwargs_name = (args_spec.varargs, + args_spec.keywords) call_args = cute_inspect.getcallargs(function, *args, **kwargs) del args, kwargs @@ -63,7 +63,7 @@ def __init__(self, containing_dict, function, *args, **kwargs): if star_args_name: star_args = call_args.pop(star_args_name, None) if star_args: - self.star_args_refs = [SleekRef(star_arg, self.destroy) for + self.star_args_refs = [self.make_ref(star_arg) for star_arg in star_args] self.star_kwargs_refs = {} @@ -71,15 +71,14 @@ def __init__(self, containing_dict, function, *args, **kwargs): if star_kwargs_name: star_kwargs = call_args.pop(star_kwargs_name, {}) if star_kwargs: - self.star_kwargs_refs = CuteSleekValueDict(self.destroy, - star_kwargs) + self.star_kwargs_refs = self.make_dict(star_kwargs) - self.args_refs = CuteSleekValueDict(self.destroy, call_args) - '''Mapping from argument name to value, sleek-style.''' + self.args_refs = self.make_dict(call_args) + '''Mapping from argument name to value.''' # In the future the `.args`, `.star_args` and `.star_kwargs` attributes # may change, so we must record the hash now: - self._hash = cheat_hashing.cheat_hash( + self._hash = self.hash_function( ( self.args, self.star_args, @@ -87,20 +86,51 @@ def __init__(self, containing_dict, function, *args, **kwargs): ) ) + args = abc.abstractproperty() + '''The arguments.''' + + star_args = abc.abstractproperty() + '''Extraneous arguments. (i.e. `*args`.)''' + + star_kwargs = abc.abstractproperty() + '''Extraneous keyword arguments. (i.e. `*kwargs`.)''' + + def __hash__(self): + return self._hash + - args = property(lambda self: dict(self.args_refs)) + def __eq__(self, other): + return ( + type(self) == type(other) and + self.args == other.args and + self.star_args == other.star_args and + self.star_kwargs == other.star_kwargs + ) + + + def __ne__(self, other): + return not self == other + + +class CallArgs(BaseCallArgs): + make_ref = lambda self, thing: thing + make_dict = lambda self, items: nifty_collections.FrozenDict(items) + hash_function = lambda self, thing: hash(thing) + + args = property(lambda self: self.args_refs) '''The arguments.''' - star_args = property( - lambda self: - tuple((star_arg_ref() for star_arg_ref in self.star_args_refs)) - ) + star_args = property(lambda self: self.star_args_refs) '''Extraneous arguments. (i.e. `*args`.)''' - star_kwargs = property(lambda self: dict(self.star_kwargs_refs)) + star_kwargs = property(lambda self: self.star_kwargs_refs) '''Extraneous keyword arguments. (i.e. `*kwargs`.)''' - + +class SleekCallArgs(BaseCallArgs): + make_ref = lambda self, thing: SleekRef(thing, self.destroy) + make_dict = lambda self, items: CuteSleekValueDict(self.destroy, items) + hash_function = lambda self, thing: cheat_hashing.cheat_hash(thing) def destroy(self, _=None): '''Delete ourselves from our containing `dict`.''' @@ -110,21 +140,15 @@ def destroy(self, _=None): except KeyError: pass + args = property(lambda self: dict(self.args_refs)) + '''The arguments.''' - def __hash__(self): - return self._hash - - - def __eq__(self, other): - if not isinstance(other, SleekCallArgs): - return NotImplemented - return self.args == other.args and \ - self.star_args == other.star_args and \ - self.star_kwargs == other.star_kwargs - - - def __ne__(self, other): - return not self == other - + star_args = property( + lambda self: + tuple((star_arg_ref() for star_arg_ref in self.star_args_refs)) + ) + '''Extraneous arguments. (i.e. `*args`.)''' - \ No newline at end of file + star_kwargs = property(lambda self: dict(self.star_kwargs_refs)) + '''Extraneous keyword arguments. (i.e. `*kwargs`.)''' + \ No newline at end of file diff --git a/source_py3/python_toolbox/introspection_tools.py b/source_py3/python_toolbox/function_tools/misc.py similarity index 93% rename from source_py3/python_toolbox/introspection_tools.py rename to source_py3/python_toolbox/function_tools/misc.py index b04456035..6efbf98d1 100644 --- a/source_py3/python_toolbox/introspection_tools.py +++ b/source_py3/python_toolbox/function_tools/misc.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -'''Defines various introspection tools, similar to the stdlib's `inspect`.''' - from python_toolbox import cute_inspect from python_toolbox.nifty_collections import OrderedDict diff --git a/source_py3/test_python_toolbox/test_introspection_tools/__init__.py b/source_py3/test_python_toolbox/test_function_tools/__init__.py similarity index 100% rename from source_py3/test_python_toolbox/test_introspection_tools/__init__.py rename to source_py3/test_python_toolbox/test_function_tools/__init__.py diff --git a/source_py3/test_python_toolbox/test_introspection_tools/test_get_default_args_dict.py b/source_py3/test_python_toolbox/test_function_tools/test_get_default_args_dict.py similarity index 93% rename from source_py3/test_python_toolbox/test_introspection_tools/test_get_default_args_dict.py rename to source_py3/test_python_toolbox/test_function_tools/test_get_default_args_dict.py index 3d582f80e..0bbd74f50 100644 --- a/source_py3/test_python_toolbox/test_introspection_tools/test_get_default_args_dict.py +++ b/source_py3/test_python_toolbox/test_function_tools/test_get_default_args_dict.py @@ -5,7 +5,7 @@ from __future__ import generator_stop -from python_toolbox.introspection_tools import get_default_args_dict +from python_toolbox.function_tools import get_default_args_dict from python_toolbox.nifty_collections import OrderedDict From 0010adcfd5e0759a16e2def1db724b2191d8dd45 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 20:58:19 +0300 Subject: [PATCH 042/112] - --- source_py3/python_toolbox/caching/__init__.py | 2 +- .../python_toolbox/caching/cached_type.py | 24 ++++++++--- .../python_toolbox/caching/decorators.py | 41 ++++++++++--------- .../python_toolbox/combi/chain_space.py | 2 - source_py3/python_toolbox/combi/map_space.py | 2 - .../context_management/__init__.py | 2 - .../blank_context_manager.py | 2 - .../context_management/context_manager.py | 2 - .../context_manager_type.py | 2 - .../context_manager_type_type.py | 2 - .../context_management/functions.py | 2 - .../context_management/modifiers.py | 2 - source_py3/python_toolbox/cute_iter_tools.py | 2 - source_py3/python_toolbox/cute_testing.py | 2 - source_py3/python_toolbox/dict_tools.py | 2 - source_py3/python_toolbox/file_tools.py | 2 - .../function_tools/call_args.py | 1 + .../python_toolbox/function_tools/misc.py | 4 +- source_py3/python_toolbox/future_tools.py | 2 - .../emitting_weak_key_default_dict.py | 2 - .../nifty_collections/lazy_tuple.py | 2 - .../nifty_collections/various_ordered_sets.py | 2 - .../weak_key_default_dict.py | 2 - .../weak_key_identity_dict.py | 2 - source_py3/python_toolbox/queue_tools.py | 2 - .../python_toolbox/sleek_reffing/__init__.py | 3 +- .../sleek_reffing/cute_sleek_value_dict.py | 2 - source_py3/python_toolbox/sys_tools.py | 2 - source_py3/python_toolbox/temp_file_tools.py | 2 - .../test_combi/test_extensive.py | 2 - .../test_abstractness.py | 2 - .../test_as_idempotent.py | 2 - .../test_as_reentrant.py | 2 - .../test_context_manager.py | 2 - .../test_context_management/test_external.py | 2 - .../test_problematic_context_managers.py | 2 - .../test_cute_iter_tools/test_iter_with.py | 4 -- .../test_cute_iter_tools/test_shorten.py | 2 - .../test_get_default_args_dict.py | 2 - .../test_nifty_collections/test_bagging.py | 2 - .../test_lazy_tuple/test_lazy_tuple.py | 2 - .../test_generic_dict_tests.py | 2 - 42 files changed, 45 insertions(+), 104 deletions(-) diff --git a/source_py3/python_toolbox/caching/__init__.py b/source_py3/python_toolbox/caching/__init__.py index 8b69b4df9..75972ceaa 100644 --- a/source_py3/python_toolbox/caching/__init__.py +++ b/source_py3/python_toolbox/caching/__init__.py @@ -6,5 +6,5 @@ # todo: examine thread-safety from .decorators import cache -from .cached_type import CachedType +from .cached_type import CachedType, StrongCachedType from .cached_property import CachedProperty \ No newline at end of file diff --git a/source_py3/python_toolbox/caching/cached_type.py b/source_py3/python_toolbox/caching/cached_type.py index ebb124888..7f18f0ebb 100644 --- a/source_py3/python_toolbox/caching/cached_type.py +++ b/source_py3/python_toolbox/caching/cached_type.py @@ -10,7 +10,7 @@ import threading import contextlib -from python_toolbox.sleek_reffing import SleekCallArgs +from python_toolbox.function_tools import CallArgs, SleekCallArgs from .cached_property import CachedProperty @@ -24,8 +24,9 @@ class SelfPlaceholder: '''Placeholder for `self` when storing call-args.''' -class CachedType(type): +class BaseCachedType(type): ''' + blocktododoc A metaclass for sharing instances. For example, if you have a class like this: @@ -51,6 +52,8 @@ def __init__(self, a, b=2): (Assuming you don't mind the memory leaks.) ''' + _BaseCachedType__call_args_type = None + def __new__(mcls, *args, **kwargs): cls = super().__new__(mcls, *args, **kwargs) cls.__cache = {} @@ -60,7 +63,7 @@ def __new__(mcls, *args, **kwargs): def __call__(cls, *args, **kwargs): - sleek_call_args = SleekCallArgs( + call_args = cls._BaseCachedType__call_args_type( cls.__cache, cls.__init__, *((SelfPlaceholder,) + args), @@ -70,13 +73,13 @@ def __call__(cls, *args, **kwargs): while True: cls.__quick_lock.acquire() try: - cached_value = cls.__cache[sleek_call_args] + cached_value = cls.__cache[call_args] except KeyError: - cls.__cache[sleek_call_args] = in_construction_marker = \ + cls.__cache[call_args] = in_construction_marker = \ InConstructionMarker() cls.__quick_lock.release() with in_construction_marker.lock: - cls.__cache[sleek_call_args] = new_instance = \ + cls.__cache[call_args] = new_instance = \ super().__call__(*args, **kwargs) return new_instance else: @@ -89,3 +92,12 @@ def __call__(cls, *args, **kwargs): return cached_value +class CachedType(BaseCachedType): + '''''' + _BaseCachedType__call_args_type = CallArgs + + +class StrongCachedType(BaseCachedType): + '''''' + _BaseCachedType__call_args_type = SleekCallArgs + \ No newline at end of file diff --git a/source_py3/python_toolbox/caching/decorators.py b/source_py3/python_toolbox/caching/decorators.py index af04d9787..f531390c2 100644 --- a/source_py3/python_toolbox/caching/decorators.py +++ b/source_py3/python_toolbox/caching/decorators.py @@ -13,7 +13,6 @@ from python_toolbox import misc_tools from python_toolbox import binary_search from python_toolbox import decorator_tools -from python_toolbox.sleek_reffing import SleekCallArgs infinity = float('inf') @@ -32,8 +31,9 @@ def _get_now(): @decorator_tools.helpful_decorator_builder -def cache(max_size=infinity, time_to_keep=None): +def cache(*, max_size=infinity, time_to_keep=None, weakref_when_possible=True): ''' + blocktododoc Cache a function, saving results so they won't have to be computed again. This decorator understands function arguments. For example, it understands @@ -69,6 +69,9 @@ def f(a, b=2): # completely argumentless function. so do one for those. from python_toolbox.nifty_collections import OrderedDict + from python_toolbox.function_tools import CallArgs, SleekCallArgs + + call_args_type = SleekCallArgs if weakref_when_possible else CallArgs if time_to_keep is not None: if max_size != infinity: @@ -94,8 +97,8 @@ def decorator(function): if time_to_keep: - sorting_key_function = lambda sleek_call_args: \ - cached._cache[sleek_call_args][1] + sorting_key_function = (lambda call_args: + cached._cache[call_args][1]) def remove_expired_entries(): @@ -114,13 +117,13 @@ def remove_expired_entries(): @misc_tools.set_attributes(_cache=OrderedDict()) def cached(function, *args, **kwargs): remove_expired_entries() - sleek_call_args = \ - SleekCallArgs(cached._cache, function, *args, **kwargs) + call_args = call_args_type(cached._cache, function, *args, + **kwargs) try: - return cached._cache[sleek_call_args][0] + return cached._cache[call_args][0] except KeyError: value = function(*args, **kwargs) - cached._cache[sleek_call_args] = ( + cached._cache[call_args] = ( value, _get_now() + time_to_keep ) @@ -131,28 +134,28 @@ def cached(function, *args, **kwargs): @misc_tools.set_attributes(_cache={}) def cached(function, *args, **kwargs): - sleek_call_args = \ - SleekCallArgs(cached._cache, function, *args, **kwargs) + call_args = call_args_type(cached._cache, function, *args, + **kwargs) try: - return cached._cache[sleek_call_args] + return cached._cache[call_args] except KeyError: - cached._cache[sleek_call_args] = value = \ - function(*args, **kwargs) + cached._cache[call_args] = value = \ + function(*args, **kwargs) return value else: # max_size < infinity @misc_tools.set_attributes(_cache=OrderedDict()) def cached(function, *args, **kwargs): - sleek_call_args = \ - SleekCallArgs(cached._cache, function, *args, **kwargs) + call_args = call_args_type(cached._cache, function, *args, + **kwargs) try: - result = cached._cache[sleek_call_args] - cached._cache.move_to_end(sleek_call_args) + result = cached._cache[call_args] + cached._cache.move_to_end(call_args) return result except KeyError: - cached._cache[sleek_call_args] = value = \ - function(*args, **kwargs) + cached._cache[call_args] = value = \ + function(*args, **kwargs) if len(cached._cache) > max_size: cached._cache.popitem(last=False) return value diff --git a/source_py3/python_toolbox/combi/chain_space.py b/source_py3/python_toolbox/combi/chain_space.py index 0f3ed7005..30ed6064b 100644 --- a/source_py3/python_toolbox/combi/chain_space.py +++ b/source_py3/python_toolbox/combi/chain_space.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import collections from python_toolbox import binary_search diff --git a/source_py3/python_toolbox/combi/map_space.py b/source_py3/python_toolbox/combi/map_space.py index d682ab1af..14450f073 100644 --- a/source_py3/python_toolbox/combi/map_space.py +++ b/source_py3/python_toolbox/combi/map_space.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import collections from python_toolbox import nifty_collections diff --git a/source_py3/python_toolbox/context_management/__init__.py b/source_py3/python_toolbox/context_management/__init__.py index 6efa3edd8..9cdee899d 100644 --- a/source_py3/python_toolbox/context_management/__init__.py +++ b/source_py3/python_toolbox/context_management/__init__.py @@ -122,8 +122,6 @@ def do_stuff(): # todo: for case of decorated generator, possibly make getstate (or whatever) # that will cause it to be pickled by reference to the decorated function -from __future__ import generator_stop - from .abstract_context_manager import AbstractContextManager from .context_manager_type_type import ContextManagerTypeType from .context_manager_type import ContextManagerType diff --git a/source_py3/python_toolbox/context_management/blank_context_manager.py b/source_py3/python_toolbox/context_management/blank_context_manager.py index 15709e045..c7db2f823 100644 --- a/source_py3/python_toolbox/context_management/blank_context_manager.py +++ b/source_py3/python_toolbox/context_management/blank_context_manager.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - from .context_manager import ContextManager diff --git a/source_py3/python_toolbox/context_management/context_manager.py b/source_py3/python_toolbox/context_management/context_manager.py index 3f00a97e6..600736d24 100644 --- a/source_py3/python_toolbox/context_management/context_manager.py +++ b/source_py3/python_toolbox/context_management/context_manager.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import sys import types import abc diff --git a/source_py3/python_toolbox/context_management/context_manager_type.py b/source_py3/python_toolbox/context_management/context_manager_type.py index 219dfc610..08ccd9e2d 100644 --- a/source_py3/python_toolbox/context_management/context_manager_type.py +++ b/source_py3/python_toolbox/context_management/context_manager_type.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import abc from .context_manager_type_type import ContextManagerTypeType diff --git a/source_py3/python_toolbox/context_management/context_manager_type_type.py b/source_py3/python_toolbox/context_management/context_manager_type_type.py index c4b70321f..9459972f8 100644 --- a/source_py3/python_toolbox/context_management/context_manager_type_type.py +++ b/source_py3/python_toolbox/context_management/context_manager_type_type.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - class ContextManagerTypeType(type): ''' diff --git a/source_py3/python_toolbox/context_management/functions.py b/source_py3/python_toolbox/context_management/functions.py index 5069c9ec1..b436960c1 100644 --- a/source_py3/python_toolbox/context_management/functions.py +++ b/source_py3/python_toolbox/context_management/functions.py @@ -7,8 +7,6 @@ See their documentation for more information. ''' -from __future__ import generator_stop - import sys from .context_manager_type import ContextManagerType diff --git a/source_py3/python_toolbox/context_management/modifiers.py b/source_py3/python_toolbox/context_management/modifiers.py index 0458197b8..a4da246be 100644 --- a/source_py3/python_toolbox/context_management/modifiers.py +++ b/source_py3/python_toolbox/context_management/modifiers.py @@ -7,8 +7,6 @@ See their documentation for more information. ''' -from __future__ import generator_stop - import string import random diff --git a/source_py3/python_toolbox/cute_iter_tools.py b/source_py3/python_toolbox/cute_iter_tools.py index ea0dc654c..9773c8840 100644 --- a/source_py3/python_toolbox/cute_iter_tools.py +++ b/source_py3/python_toolbox/cute_iter_tools.py @@ -5,8 +5,6 @@ # todo: make something like `filter` except it returns first found, or raises # exception -from __future__ import generator_stop - import collections import operator import itertools diff --git a/source_py3/python_toolbox/cute_testing.py b/source_py3/python_toolbox/cute_testing.py index dca2aab34..0e1ec1dc2 100644 --- a/source_py3/python_toolbox/cute_testing.py +++ b/source_py3/python_toolbox/cute_testing.py @@ -3,8 +3,6 @@ '''This module defines tools for testing.''' -from __future__ import generator_stop - import sys from python_toolbox.third_party import unittest2 diff --git a/source_py3/python_toolbox/dict_tools.py b/source_py3/python_toolbox/dict_tools.py index 5bf4657ee..8089f5583 100644 --- a/source_py3/python_toolbox/dict_tools.py +++ b/source_py3/python_toolbox/dict_tools.py @@ -3,8 +3,6 @@ '''Defines several functions that may be useful when working with dicts.''' -from __future__ import generator_stop - import collections from python_toolbox import cute_iter_tools diff --git a/source_py3/python_toolbox/file_tools.py b/source_py3/python_toolbox/file_tools.py index bc9e5696c..748a128aa 100644 --- a/source_py3/python_toolbox/file_tools.py +++ b/source_py3/python_toolbox/file_tools.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - try: import pathlib except: diff --git a/source_py3/python_toolbox/function_tools/call_args.py b/source_py3/python_toolbox/function_tools/call_args.py index 119566ef1..7c07de1ee 100644 --- a/source_py3/python_toolbox/function_tools/call_args.py +++ b/source_py3/python_toolbox/function_tools/call_args.py @@ -80,6 +80,7 @@ def __init__(self, containing_dict, function, *args, **kwargs): # may change, so we must record the hash now: self._hash = self.hash_function( ( + type(self), self.args, self.star_args, self.star_kwargs diff --git a/source_py3/python_toolbox/function_tools/misc.py b/source_py3/python_toolbox/function_tools/misc.py index 6efbf98d1..fb24fab4f 100644 --- a/source_py3/python_toolbox/function_tools/misc.py +++ b/source_py3/python_toolbox/function_tools/misc.py @@ -3,8 +3,6 @@ from python_toolbox import cute_inspect -from python_toolbox.nifty_collections import OrderedDict - def get_default_args_dict(function): ''' @@ -17,6 +15,8 @@ def get_default_args_dict(function): OrderedDict([('c', 1), ('d', 'meow')]) ''' + from python_toolbox.nifty_collections import OrderedDict + arg_spec = cute_inspect.getargspec(function) (s_args, s_star_args, s_star_kwargs, s_defaults) = arg_spec diff --git a/source_py3/python_toolbox/future_tools.py b/source_py3/python_toolbox/future_tools.py index c86f09f39..bbca84c49 100644 --- a/source_py3/python_toolbox/future_tools.py +++ b/source_py3/python_toolbox/future_tools.py @@ -5,8 +5,6 @@ Defines tools related to the `concurrent.futures` standard library package. ''' -from __future__ import generator_stop - import time import concurrent.futures diff --git a/source_py3/python_toolbox/nifty_collections/emitting_weak_key_default_dict.py b/source_py3/python_toolbox/nifty_collections/emitting_weak_key_default_dict.py index 1f89ee472..03d356ce0 100644 --- a/source_py3/python_toolbox/nifty_collections/emitting_weak_key_default_dict.py +++ b/source_py3/python_toolbox/nifty_collections/emitting_weak_key_default_dict.py @@ -7,8 +7,6 @@ See its documentation for more details. ''' -from __future__ import generator_stop - from .weak_key_default_dict import WeakKeyDefaultDict diff --git a/source_py3/python_toolbox/nifty_collections/lazy_tuple.py b/source_py3/python_toolbox/nifty_collections/lazy_tuple.py index 265bbddd3..1bf2593c5 100644 --- a/source_py3/python_toolbox/nifty_collections/lazy_tuple.py +++ b/source_py3/python_toolbox/nifty_collections/lazy_tuple.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import functools import threading import collections diff --git a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py index 795024e40..f1f64fe5f 100644 --- a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py +++ b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import collections import operator import itertools diff --git a/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py b/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py index a90d48e5b..e0d270608 100644 --- a/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py +++ b/source_py3/python_toolbox/nifty_collections/weak_key_default_dict.py @@ -8,8 +8,6 @@ ''' # todo: revamp -from __future__ import generator_stop - import collections from weakref import ref diff --git a/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py b/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py index 10fe0a875..508f1346e 100644 --- a/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py +++ b/source_py3/python_toolbox/nifty_collections/weak_key_identity_dict.py @@ -8,8 +8,6 @@ ''' # todo: revamp -from __future__ import generator_stop - import weakref import collections diff --git a/source_py3/python_toolbox/queue_tools.py b/source_py3/python_toolbox/queue_tools.py index 0d3504718..da598e255 100644 --- a/source_py3/python_toolbox/queue_tools.py +++ b/source_py3/python_toolbox/queue_tools.py @@ -3,8 +3,6 @@ '''Defines various functions for working with queues.''' -from __future__ import generator_stop - import queue as queue_module import sys diff --git a/source_py3/python_toolbox/sleek_reffing/__init__.py b/source_py3/python_toolbox/sleek_reffing/__init__.py index 9d6b35acc..8eef9c801 100644 --- a/source_py3/python_toolbox/sleek_reffing/__init__.py +++ b/source_py3/python_toolbox/sleek_reffing/__init__.py @@ -10,8 +10,7 @@ from .sleek_ref import SleekRef from .exceptions import SleekRefDied -from .sleek_call_args import SleekCallArgs from .cute_sleek_value_dict import CuteSleekValueDict -__all__ = ['SleekRef', 'SleekRefDied', 'SleekCallArgs', 'CuteSleekValueDict'] +__all__ = ['SleekRef', 'SleekRefDied', 'CuteSleekValueDict'] diff --git a/source_py3/python_toolbox/sleek_reffing/cute_sleek_value_dict.py b/source_py3/python_toolbox/sleek_reffing/cute_sleek_value_dict.py index 66fee0ab1..ea9db3db0 100644 --- a/source_py3/python_toolbox/sleek_reffing/cute_sleek_value_dict.py +++ b/source_py3/python_toolbox/sleek_reffing/cute_sleek_value_dict.py @@ -7,8 +7,6 @@ See its documentation for more details. ''' -from __future__ import generator_stop - import weakref import collections diff --git a/source_py3/python_toolbox/sys_tools.py b/source_py3/python_toolbox/sys_tools.py index f38a08bc3..cb2aa8ef3 100644 --- a/source_py3/python_toolbox/sys_tools.py +++ b/source_py3/python_toolbox/sys_tools.py @@ -3,8 +3,6 @@ '''Defines various `sys`-related tools.''' -from __future__ import generator_stop - import sys try: import pathlib diff --git a/source_py3/python_toolbox/temp_file_tools.py b/source_py3/python_toolbox/temp_file_tools.py index 5e421b4d9..04231e477 100644 --- a/source_py3/python_toolbox/temp_file_tools.py +++ b/source_py3/python_toolbox/temp_file_tools.py @@ -3,8 +3,6 @@ '''Defines various tools related to temporary files.''' -from __future__ import generator_stop - import tempfile import shutil try: diff --git a/source_py3/test_python_toolbox/test_combi/test_extensive.py b/source_py3/test_python_toolbox/test_combi/test_extensive.py index 9940e1597..2be1482dc 100644 --- a/source_py3/test_python_toolbox/test_combi/test_extensive.py +++ b/source_py3/test_python_toolbox/test_combi/test_extensive.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import pickle import itertools import collections diff --git a/source_py3/test_python_toolbox/test_context_management/test_abstractness.py b/source_py3/test_python_toolbox/test_context_management/test_abstractness.py index 7728bfd67..ba07fd5cb 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_abstractness.py +++ b/source_py3/test_python_toolbox/test_context_management/test_abstractness.py @@ -3,8 +3,6 @@ '''Module for testing the abstract methods of `ContextManager`.''' -from __future__ import generator_stop - import sys import nose diff --git a/source_py3/test_python_toolbox/test_context_management/test_as_idempotent.py b/source_py3/test_python_toolbox/test_context_management/test_as_idempotent.py index ace80d9a1..de0007ba3 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_as_idempotent.py +++ b/source_py3/test_python_toolbox/test_context_management/test_as_idempotent.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import queue as queue_module from python_toolbox.context_management import (as_idempotent, ContextManager, diff --git a/source_py3/test_python_toolbox/test_context_management/test_as_reentrant.py b/source_py3/test_python_toolbox/test_context_management/test_as_reentrant.py index b279fdfd6..104926870 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_as_reentrant.py +++ b/source_py3/test_python_toolbox/test_context_management/test_as_reentrant.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import queue as queue_module from python_toolbox.context_management import (as_reentrant, ContextManager, diff --git a/source_py3/test_python_toolbox/test_context_management/test_context_manager.py b/source_py3/test_python_toolbox/test_context_management/test_context_manager.py index cbcd0b71c..d757085f3 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_context_manager.py +++ b/source_py3/test_python_toolbox/test_context_management/test_context_manager.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - from python_toolbox import cute_testing from python_toolbox.context_management import (ContextManager, diff --git a/source_py3/test_python_toolbox/test_context_management/test_external.py b/source_py3/test_python_toolbox/test_context_management/test_external.py index bc4d6c9ea..4db70ea60 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_external.py +++ b/source_py3/test_python_toolbox/test_context_management/test_external.py @@ -3,8 +3,6 @@ '''Tests taken from Python's `contextlib'.''' -from __future__ import generator_stop - import sys import nose diff --git a/source_py3/test_python_toolbox/test_context_management/test_problematic_context_managers.py b/source_py3/test_python_toolbox/test_context_management/test_problematic_context_managers.py index 99965ab81..a439fadf4 100644 --- a/source_py3/test_python_toolbox/test_context_management/test_problematic_context_managers.py +++ b/source_py3/test_python_toolbox/test_context_management/test_problematic_context_managers.py @@ -3,8 +3,6 @@ '''Testing module for various problematic context managers.''' -from __future__ import generator_stop - import nose from python_toolbox import cute_testing diff --git a/source_py3/test_python_toolbox/test_cute_iter_tools/test_iter_with.py b/source_py3/test_python_toolbox/test_cute_iter_tools/test_iter_with.py index ec27cff4b..8316939f9 100644 --- a/source_py3/test_python_toolbox/test_cute_iter_tools/test_iter_with.py +++ b/source_py3/test_python_toolbox/test_cute_iter_tools/test_iter_with.py @@ -1,10 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -'''Testing module for `cute_iter_tools.iter_with`.''' - -from __future__ import generator_stop - import itertools from python_toolbox import nifty_collections diff --git a/source_py3/test_python_toolbox/test_cute_iter_tools/test_shorten.py b/source_py3/test_python_toolbox/test_cute_iter_tools/test_shorten.py index 02ab4b71c..342f2896a 100644 --- a/source_py3/test_python_toolbox/test_cute_iter_tools/test_shorten.py +++ b/source_py3/test_python_toolbox/test_cute_iter_tools/test_shorten.py @@ -3,8 +3,6 @@ '''Testing module for `python_toolbox.cute_iter_tools.shorten`.''' -from __future__ import generator_stop - import nose.tools from python_toolbox import nifty_collections diff --git a/source_py3/test_python_toolbox/test_function_tools/test_get_default_args_dict.py b/source_py3/test_python_toolbox/test_function_tools/test_get_default_args_dict.py index 0bbd74f50..6535d9c13 100644 --- a/source_py3/test_python_toolbox/test_function_tools/test_get_default_args_dict.py +++ b/source_py3/test_python_toolbox/test_function_tools/test_get_default_args_dict.py @@ -3,8 +3,6 @@ '''Testing for `python_toolbox.introspection_tools.get_default_args_dict`.''' -from __future__ import generator_stop - from python_toolbox.function_tools import get_default_args_dict from python_toolbox.nifty_collections import OrderedDict diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py b/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py index 854e3f9c2..8b6d7752c 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_bagging.py @@ -1,8 +1,6 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -from __future__ import generator_stop - import re import pickle import abc diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py b/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py index 6e4837a70..5fed1cd23 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_lazy_tuple/test_lazy_tuple.py @@ -3,8 +3,6 @@ '''Testing module for `python_toolbox.nifty_collections.LazyTuple`.''' -from __future__ import generator_stop - import uuid import itertools import collections diff --git a/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/test_generic_dict_tests.py b/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/test_generic_dict_tests.py index 9b916f5aa..8467c6df1 100644 --- a/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/test_generic_dict_tests.py +++ b/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/test_generic_dict_tests.py @@ -3,8 +3,6 @@ '''Run generic `dict` tests on `CuteSleekValueDict`.''' -from __future__ import generator_stop - import sys import random import string From c5dd15bc9ab64808df5509bfce92798af19c4ed5 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 21:08:59 +0300 Subject: [PATCH 043/112] - --- .../function_tools/call_args.py | 31 ++++++++----------- .../test_caching/test_cache.py | 10 ++---- .../test_caching/test_cached_type.py | 10 +++--- 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/source_py3/python_toolbox/function_tools/call_args.py b/source_py3/python_toolbox/function_tools/call_args.py index 7c07de1ee..c3ff6d1e8 100644 --- a/source_py3/python_toolbox/function_tools/call_args.py +++ b/source_py3/python_toolbox/function_tools/call_args.py @@ -5,10 +5,7 @@ from python_toolbox import cute_inspect from python_toolbox import cheat_hashing -from python_toolbox import nifty_collections - -from .sleek_ref import SleekRef -from .cute_sleek_value_dict import CuteSleekValueDict +from python_toolbox.sleek_reffing import SleekRef, CuteSleekValueDict class BaseCallArgs(metaclass=abc.ABCMeta): @@ -57,21 +54,15 @@ def __init__(self, containing_dict, function, *args, **kwargs): call_args = cute_inspect.getcallargs(function, *args, **kwargs) del args, kwargs - self.star_args_refs = [] - '''Sleekrefs to star-args.''' + self.star_args_refs = () - if star_args_name: - star_args = call_args.pop(star_args_name, None) - if star_args: - self.star_args_refs = [self.make_ref(star_arg) for - star_arg in star_args] + star_args = call_args.pop(star_args_name, ()) if star_args_name else () + self.star_args_refs = tuple(self.make_ref(star_arg) for + star_arg in star_args) - self.star_kwargs_refs = {} - '''Sleerefs to star-kwargs.''' - if star_kwargs_name: - star_kwargs = call_args.pop(star_kwargs_name, {}) - if star_kwargs: - self.star_kwargs_refs = self.make_dict(star_kwargs) + star_kwargs = (call_args.pop(star_kwargs_name, {}) if + star_kwargs_name else {}) + self.star_kwargs_refs = self.make_dict(star_kwargs) self.args_refs = self.make_dict(call_args) '''Mapping from argument name to value.''' @@ -116,8 +107,12 @@ def __ne__(self, other): class CallArgs(BaseCallArgs): make_ref = lambda self, thing: thing - make_dict = lambda self, items: nifty_collections.FrozenDict(items) hash_function = lambda self, thing: hash(thing) + def make_dict(self, items): + from python_toolbox import nifty_collections + return nifty_collections.FrozenDict(items) + + args = property(lambda self: self.args_refs) '''The arguments.''' diff --git a/source_py3/test_python_toolbox/test_caching/test_cache.py b/source_py3/test_python_toolbox/test_caching/test_cache.py index fd75c6511..5df3e22af 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cache.py +++ b/source_py3/test_python_toolbox/test_caching/test_cache.py @@ -127,19 +127,13 @@ def test_unhashable_arguments(): assert f(meow=y) == f(1, meow=y) -def test_helpful_message_when_forgetting_parentheses(): - '''Test user gets a helpful exception when when forgetting parentheses.''' +def test_error_when_forgetting_parentheses(): def confusedly_forget_parentheses(): @cache def f(): pass - with cute_testing.RaiseAssertor( - TypeError, - 'It seems that you forgot to add parentheses after `@cache` when ' - 'decorating the `f` function.' - ): - + with cute_testing.RaiseAssertor(TypeError,): confusedly_forget_parentheses() diff --git a/source_py3/test_python_toolbox/test_caching/test_cached_type.py b/source_py3/test_python_toolbox/test_caching/test_cached_type.py index cef78d265..8ba0657f5 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cached_type.py +++ b/source_py3/test_python_toolbox/test_caching/test_cached_type.py @@ -67,12 +67,12 @@ def run(self): condition_list.play_out(['t1go']) - cache = Feline._CachedType__cache + cache = Feline._BaseCachedType__cache assert len(cache) == 1 ((cat_key, cat_value),) = cache.items() assert isinstance(cat_value, caching.cached_type.InConstructionMarker) assert cat_value.lock.locked() - assert not Feline._CachedType__quick_lock.locked() + assert not Feline._BaseCachedType__quick_lock.locked() condition_list.play_out(['t2go']) @@ -84,7 +84,7 @@ def run(self): assert cat_value.lock.locked() assert isinstance(tiger_value, caching.cached_type.InConstructionMarker) assert tiger_value.lock.locked() - assert not Feline._CachedType__quick_lock.locked() + assert not Feline._BaseCachedType__quick_lock.locked() condition_list.play_out(['tiger_creation']) condition_list.wait_for('t2done', remove=True) @@ -96,7 +96,7 @@ def run(self): assert isinstance(tiger_value, Feline) assert isinstance(cat_value, caching.cached_type.InConstructionMarker) assert cat_value.lock.locked() - assert not Feline._CachedType__quick_lock.locked() + assert not Feline._BaseCachedType__quick_lock.locked() condition_list.play_out(['t3go']) @@ -107,7 +107,7 @@ def run(self): assert isinstance(tiger_value, Feline) assert isinstance(cat_value, caching.cached_type.InConstructionMarker) assert cat_value.lock.locked() - assert not Feline._CachedType__quick_lock.locked() + assert not Feline._BaseCachedType__quick_lock.locked() condition_list.play_out(['cat_creation']) condition_list.wait_for('t1done', 't3done', remove=True) From bdeff2557bdf82bd4729434aeed2cbebed5b44b8 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 21:17:31 +0300 Subject: [PATCH 044/112] - --- .../test_python_toolbox/test_call_args/__init__.py | 0 .../test_sleek_call_args.py | 9 +++------ .../test_cute_sleek_value_dict/tests.py | 3 +-- .../test_sleek_reffing/test_sleek_ref.py | 5 +---- 4 files changed, 5 insertions(+), 12 deletions(-) create mode 100644 source_py3/test_python_toolbox/test_call_args/__init__.py rename source_py3/test_python_toolbox/{test_sleek_reffing => test_call_args}/test_sleek_call_args.py (81%) diff --git a/source_py3/test_python_toolbox/test_call_args/__init__.py b/source_py3/test_python_toolbox/test_call_args/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_call_args.py b/source_py3/test_python_toolbox/test_call_args/test_sleek_call_args.py similarity index 81% rename from source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_call_args.py rename to source_py3/test_python_toolbox/test_call_args/test_sleek_call_args.py index 93704e752..0cd17e298 100644 --- a/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_call_args.py +++ b/source_py3/test_python_toolbox/test_call_args/test_sleek_call_args.py @@ -1,16 +1,13 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -'''Testing module for `python_toolbox.sleek_reffing.SleekCallArgs`.''' - import weakref from python_toolbox import gc_tools -from python_toolbox.sleek_reffing import (SleekCallArgs, - SleekRef, - CuteSleekValueDict) -from .shared import _is_weakreffable, A, counter +from python_toolbox.function_tools import SleekCallArgs +from python_toolbox.sleek_reffing import SleekRef, CuteSleekValueDict +from ..test_sleek_reffing.shared import _is_weakreffable, A, counter def f(*args, **kwargs): pass diff --git a/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/tests.py b/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/tests.py index bb78ccfd1..cd00b2487 100644 --- a/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/tests.py +++ b/source_py3/test_python_toolbox/test_sleek_reffing/test_cute_sleek_value_dict/tests.py @@ -9,8 +9,7 @@ from python_toolbox import gc_tools -from python_toolbox.sleek_reffing import (SleekCallArgs, - SleekRef, +from python_toolbox.sleek_reffing import (SleekRef, CuteSleekValueDict) from ..shared import _is_weakreffable, A, counter diff --git a/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py b/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py index cfefa0781..31da64a72 100644 --- a/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py +++ b/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py @@ -9,10 +9,7 @@ from python_toolbox import gc_tools -from python_toolbox.sleek_reffing import (SleekCallArgs, - SleekRef, - SleekRefDied, - CuteSleekValueDict) +from python_toolbox.sleek_reffing import SleekRef, CuteSleekValueDict from .shared import _is_weakreffable, A, counter From 081351f1c361cd2d7da5a4b636a08bece7ac08e6 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 21:20:50 +0300 Subject: [PATCH 045/112] - --- .../test_python_toolbox/test_sleek_reffing/test_sleek_ref.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py b/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py index 31da64a72..01e3a343d 100644 --- a/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py +++ b/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py @@ -9,7 +9,8 @@ from python_toolbox import gc_tools -from python_toolbox.sleek_reffing import SleekRef, CuteSleekValueDict +from python_toolbox.sleek_reffing import (SleekRef, CuteSleekValueDict, + SleekRefDied) from .shared import _is_weakreffable, A, counter From e357e87d7a82c32ecc3b250e01f3d7b34b3275d3 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 16 Jul 2016 22:25:46 +0300 Subject: [PATCH 046/112] - --- .../test_python_toolbox/test_sleek_reffing/test_sleek_ref.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py b/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py index 01e3a343d..ab2be291b 100644 --- a/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py +++ b/source_py3/test_python_toolbox/test_sleek_reffing/test_sleek_ref.py @@ -33,6 +33,7 @@ def test_sleek_ref(): gc_tools.collect() assert counter() == count + 2 nose.tools.assert_raises(SleekRefDied, sleek_ref) + # I dieded. else: count = counter() del volatile_thing From cb67ec277a73432ab8f919f90a3e07b024b02d7a Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 17 Jul 2016 20:39:55 +0300 Subject: [PATCH 047/112] - --- .../test_caching/test_cache.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source_py3/test_python_toolbox/test_caching/test_cache.py b/source_py3/test_python_toolbox/test_caching/test_cache.py index 5df3e22af..acab8df2e 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cache.py +++ b/source_py3/test_python_toolbox/test_caching/test_cache.py @@ -65,6 +65,29 @@ class A: pass assert a_ref() is None +def test_non_weakref(): + f = cache(weakref_when_possible=False)(counting_func) + i was here + + class A: pass + + a = A() + result = f(a) + assert result == f(a) == f(a) == f(a) + a_ref = weakref.ref(a) + del a + gc_tools.collect() + assert a_ref() is None + + a = A() + result = f(meow=a) + assert result == f(meow=a) == f(meow=a) == f(meow=a) + a_ref = weakref.ref(a) + del a + gc_tools.collect() + + assert a_ref() is None + def test_lru(): '''Test the least-recently-used algorithm for forgetting cached results.''' From 5ede9e1930d1bfa99525999f30d542c0c4ec1c11 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 18 Jul 2016 17:21:51 +0300 Subject: [PATCH 048/112] - --- .../test_caching/test_cache.py | 12 ++++---- .../test_caching/test_cached_type.py | 28 ++++++++++++++++++- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/source_py3/test_python_toolbox/test_caching/test_cache.py b/source_py3/test_python_toolbox/test_caching/test_cache.py index acab8df2e..ad043993e 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cache.py +++ b/source_py3/test_python_toolbox/test_caching/test_cache.py @@ -50,7 +50,7 @@ class A: pass a = A() result = f(a) - assert result == f(a) == f(a) == f(a) + assert result is f(a) is f(a) is f(a) a_ref = weakref.ref(a) del a gc_tools.collect() @@ -58,7 +58,7 @@ class A: pass a = A() result = f(meow=a) - assert result == f(meow=a) == f(meow=a) == f(meow=a) + assert result is f(meow=a) is f(meow=a) is f(meow=a) a_ref = weakref.ref(a) del a gc_tools.collect() @@ -67,7 +67,6 @@ class A: pass def test_non_weakref(): f = cache(weakref_when_possible=False)(counting_func) - i was here class A: pass @@ -77,7 +76,8 @@ class A: pass a_ref = weakref.ref(a) del a gc_tools.collect() - assert a_ref() is None + assert a_ref() is not None + assert result == f(a_ref()) == f(a_ref()) == f(a_ref()) a = A() result = f(meow=a) @@ -85,8 +85,8 @@ class A: pass a_ref = weakref.ref(a) del a gc_tools.collect() - - assert a_ref() is None + assert a_ref() is not None + assert result == f(meow=a_ref()) == f(meow=a_ref()) == f(meow=a_ref()) def test_lru(): diff --git a/source_py3/test_python_toolbox/test_caching/test_cached_type.py b/source_py3/test_python_toolbox/test_caching/test_cached_type.py index 8ba0657f5..5a7a7a5d5 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cached_type.py +++ b/source_py3/test_python_toolbox/test_caching/test_cached_type.py @@ -2,10 +2,12 @@ # This program is distributed under the MIT license. import threading +import weakref import uuid as uuid_module +from python_toolbox import gc_tools from python_toolbox import caching -from python_toolbox.caching import CachedType +from python_toolbox.caching import CachedType, StrongCachedType from python_toolbox import nifty_collections @@ -137,4 +139,28 @@ def run(self): assert threads[0].cat.uuid == threads[2].cat.uuid assert threads[1].tiger is threads[3].tiger assert threads[1].tiger.uuid == threads[3].tiger.uuid + + +def test_weakref(): + class Mouse(metaclass=CachedType): + def __init__(self, whatever): + pass + + class Cat(metaclass=StrongCachedType): + def __init__(self, whatever): + pass + + class A: pass + + assert Mouse(7) is Mouse(7) is not Mouse(8) is Mouse(8) + assert Cat(7) is Cat(7) is not Cat(8) is Cat(8) + + a = A() + a_ref = weakref.ref(a) + assert a_ref() is a + mouse = Mouse(a) + assert mouse is Mouse(a) is Mouse(a) is Mouse(a) + del a + gc_tools.collect() + assert a_ref() is None \ No newline at end of file From 1d122dc773ad22a7b979fc3168752c4147295fe1 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 18 Jul 2016 17:23:40 +0300 Subject: [PATCH 049/112] - --- .../test_caching/test_cached_type.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source_py3/test_python_toolbox/test_caching/test_cached_type.py b/source_py3/test_python_toolbox/test_caching/test_cached_type.py index 5a7a7a5d5..8bedc4e74 100644 --- a/source_py3/test_python_toolbox/test_caching/test_cached_type.py +++ b/source_py3/test_python_toolbox/test_caching/test_cached_type.py @@ -163,4 +163,15 @@ class A: pass del a gc_tools.collect() assert a_ref() is None + + a = A() + a_ref = weakref.ref(a) + assert a_ref() is a + cat = Cat(a) + assert cat is Cat(a) is Cat(a) is Cat(a) + del a + gc_tools.collect() + assert a_ref() is not None + assert cat is Cat(a_ref()) is Cat(a_ref()) is Cat(a_ref()) + \ No newline at end of file From 6083f248fe781b89d26724f470422850900f5faa Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 18 Jul 2016 17:30:07 +0300 Subject: [PATCH 050/112] - --- source_py3/python_toolbox/caching/cached_type.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source_py3/python_toolbox/caching/cached_type.py b/source_py3/python_toolbox/caching/cached_type.py index 7f18f0ebb..c5654b5ee 100644 --- a/source_py3/python_toolbox/caching/cached_type.py +++ b/source_py3/python_toolbox/caching/cached_type.py @@ -94,10 +94,10 @@ def __call__(cls, *args, **kwargs): class CachedType(BaseCachedType): '''''' - _BaseCachedType__call_args_type = CallArgs + _BaseCachedType__call_args_type = SleekCallArgs class StrongCachedType(BaseCachedType): '''''' - _BaseCachedType__call_args_type = SleekCallArgs + _BaseCachedType__call_args_type = CallArgs \ No newline at end of file From 97f6eac647607d46d63e5dd31c2b24c12168f977 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 20 Jul 2016 17:08:54 +0300 Subject: [PATCH 051/112] - --- .../python_toolbox/caching/cached_type.py | 75 ++++++++++++------- .../python_toolbox/caching/decorators.py | 19 +++-- ...t_sleek_call_args.py => test_call_args.py} | 20 ++++- 3 files changed, 75 insertions(+), 39 deletions(-) rename source_py3/test_python_toolbox/test_call_args/{test_sleek_call_args.py => test_call_args.py} (75%) diff --git a/source_py3/python_toolbox/caching/cached_type.py b/source_py3/python_toolbox/caching/cached_type.py index c5654b5ee..fdc4da23c 100644 --- a/source_py3/python_toolbox/caching/cached_type.py +++ b/source_py3/python_toolbox/caching/cached_type.py @@ -25,33 +25,6 @@ class SelfPlaceholder: class BaseCachedType(type): - ''' - blocktododoc - A metaclass for sharing instances. - - For example, if you have a class like this: - - class Grokker(object, metaclass=caching.CachedType): - def __init__(self, a, b=2): - self.a = a - self.b = b - - Then all the following calls would result in just one instance: - - Grokker(1) is Grokker(1, 2) is Grokker(b=2, a=1) is Grokker(1, **{}) - - This metaclass understands keyword arguments. - - All the arguments are sleekreffed to prevent memory leaks. Sleekref is a - variation of weakref. Sleekref is when you try to weakref an object, but if - it's non-weakreffable, like a `list` or a `dict`, you maintain a normal, - strong reference to it. (See documentation of - `python_toolbox.sleek_reffing` for more details.) Thanks to sleekreffing - you can avoid memory leaks when using weakreffable arguments, but if you - ever want to use non-weakreffable arguments you are still able to. - (Assuming you don't mind the memory leaks.) - ''' - _BaseCachedType__call_args_type = None def __new__(mcls, *args, **kwargs): @@ -93,11 +66,55 @@ def __call__(cls, *args, **kwargs): class CachedType(BaseCachedType): - '''''' + ''' + A metaclass for sharing instances. + + For example, if you have a class like this: + + class Grokker(object, metaclass=caching.CachedType): + def __init__(self, a, b=2): + self.a = a + self.b = b + + Then all the following calls would result in just one instance: + + Grokker(1) is Grokker(1, 2) is Grokker(b=2, a=1) is Grokker(1, **{}) + + This metaclass understands keyword arguments. + + All the arguments are sleekreffed to prevent memory leaks. Sleekref is a + variation of weakref. Sleekref is when you try to weakref an object, but if + it's non-weakreffable, like a `list` or a `dict`, you maintain a normal, + strong reference to it. (See documentation of + `python_toolbox.sleek_reffing` for more details.) Thanks to sleekreffing + you can avoid memory leaks when using weakreffable arguments, but if you + ever want to use non-weakreffable arguments you are still able to. + (Assuming you don't mind the memory leaks.) + + If you want a non-weakreffing version, use `StrongCachedType` instead. + ''' _BaseCachedType__call_args_type = SleekCallArgs class StrongCachedType(BaseCachedType): - '''''' + ''' + A metaclass for sharing instances. + + For example, if you have a class like this: + + class Grokker(object, metaclass=caching.CachedType): + def __init__(self, a, b=2): + self.a = a + self.b = b + + Then all the following calls would result in just one instance: + + Grokker(1) is Grokker(1, 2) is Grokker(b=2, a=1) is Grokker(1, **{}) + + This metaclass understands keyword arguments. + + The arguments will not be weakreffed. If you want a weakreffing version, + use `CachedType` instead. + ''' _BaseCachedType__call_args_type = CallArgs \ No newline at end of file diff --git a/source_py3/python_toolbox/caching/decorators.py b/source_py3/python_toolbox/caching/decorators.py index f531390c2..aa2784037 100644 --- a/source_py3/python_toolbox/caching/decorators.py +++ b/source_py3/python_toolbox/caching/decorators.py @@ -46,14 +46,17 @@ def f(a, b=2): The calls `f(1)` or `f(1, 2)` or `f(b=2, a=1)` are all identical, and a cached result saved for one of these calls will be used for the others. - All the arguments are sleekreffed to prevent memory leaks. Sleekref is a - variation of weakref. Sleekref is when you try to weakref an object, but if - it's non-weakreffable, like a `list` or a `dict`, you maintain a normal, - strong reference to it. (See documentation of - `python_toolbox.sleek_reffing` for more details.) Thanks to sleekreffing - you can avoid memory leaks when using weakreffable arguments, but if you - ever want to use non-weakreffable arguments you are still able to. - (Assuming you don't mind the memory leaks.) + If `weakref_when_possible=True` (default) all the arguments are sleekreffed + to prevent memory leaks. Sleekref is a variation of weakref. Sleekref is + when you try to weakref an object, but if it's non-weakreffable, like a + `list` or a `dict`, you maintain a normal, strong reference to it. (See + documentation of `python_toolbox.sleek_reffing` for more details.) Thanks + to sleekreffing you can avoid memory leaks when using weakreffable + arguments, but if you ever want to use non-weakreffable arguments you are + still able to. (Assuming you don't mind the memory leaks.) + + If you want to use a non-weakreffing version, pass in + `weakref_when_possible=False`. You may optionally specify a `max_size` for maximum number of cached results to store; old entries are thrown away according to a diff --git a/source_py3/test_python_toolbox/test_call_args/test_sleek_call_args.py b/source_py3/test_python_toolbox/test_call_args/test_call_args.py similarity index 75% rename from source_py3/test_python_toolbox/test_call_args/test_sleek_call_args.py rename to source_py3/test_python_toolbox/test_call_args/test_call_args.py index 0cd17e298..9ef7b5784 100644 --- a/source_py3/test_python_toolbox/test_call_args/test_sleek_call_args.py +++ b/source_py3/test_python_toolbox/test_call_args/test_call_args.py @@ -5,7 +5,7 @@ from python_toolbox import gc_tools -from python_toolbox.function_tools import SleekCallArgs +from python_toolbox.function_tools import SleekCallArgs, CallArgs from python_toolbox.sleek_reffing import SleekRef, CuteSleekValueDict from ..test_sleek_reffing.shared import _is_weakreffable, A, counter @@ -14,7 +14,7 @@ def f(*args, **kwargs): pass def test(): - '''Test the basic workings of `SleekCallArgs`.''' + '''Test the basic workings of `SleekCallArgs` and `CallArgs`.''' sca_dict = {} args = (1, 2) @@ -31,6 +31,22 @@ def test(): gc_tools.collect() assert len(sca_dict) == 1 + ca_dict = {} + + args = (1, 2) + ca1 = CallArgs(ca_dict, f, *args) + ca_dict[ca1] = 'meow' + del args + gc_tools.collect() + assert len(ca_dict) == 1 + + args = (1, A()) + ca2 = CallArgs(ca_dict, f, *args) + ca_dict[ca2] = 'meow' + del args + gc_tools.collect() + assert len(ca_dict) == 2 + def test_unhashable(): '''Test `SleekCallArgs` on unhashable arguments.''' From e4b9da3ef323cfaf7915bec8d0902cc965a1381e Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Thu, 21 Jul 2016 18:02:28 +0300 Subject: [PATCH 052/112] - --- .../multiprocessing_tools/__init__.py | 4 ++++ .../multiprocessing_tools/pipe_queueing.py | 21 ++++++++++++++++ .../test_multiprocessing_tools/__init__.py | 2 ++ .../test_pipe_queue.py | 24 +++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 source_py3/python_toolbox/multiprocessing_tools/__init__.py create mode 100644 source_py3/python_toolbox/multiprocessing_tools/pipe_queueing.py create mode 100644 source_py3/test_python_toolbox/test_multiprocessing_tools/__init__.py create mode 100644 source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py diff --git a/source_py3/python_toolbox/multiprocessing_tools/__init__.py b/source_py3/python_toolbox/multiprocessing_tools/__init__.py new file mode 100644 index 000000000..4641879a9 --- /dev/null +++ b/source_py3/python_toolbox/multiprocessing_tools/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +from .pipe_queueing import PipeQueue \ No newline at end of file diff --git a/source_py3/python_toolbox/multiprocessing_tools/pipe_queueing.py b/source_py3/python_toolbox/multiprocessing_tools/pipe_queueing.py new file mode 100644 index 000000000..da347cf65 --- /dev/null +++ b/source_py3/python_toolbox/multiprocessing_tools/pipe_queueing.py @@ -0,0 +1,21 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import multiprocessing + +class PipeQueue: + def __init__(self): + self.queue = multiprocessing.Queue() + + def create_pipe(self): + master_connection, slave_connection = multiprocessing.Pipe() + self.queue.put(slave_connection) + return master_connection + + def wait_for_connection(self): + return self.queue.get() + + + + + \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_multiprocessing_tools/__init__.py b/source_py3/test_python_toolbox/test_multiprocessing_tools/__init__.py new file mode 100644 index 000000000..39c1bbf18 --- /dev/null +++ b/source_py3/test_python_toolbox/test_multiprocessing_tools/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. diff --git a/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py b/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py new file mode 100644 index 000000000..3adb12382 --- /dev/null +++ b/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py @@ -0,0 +1,24 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import multiprocessing + +from python_toolbox import multiprocessing_tools + +class Worker(multiprocessing.Process): + def __init__(self, pipe_queue): + self.pipe_queue = pipe_queue + + def run(self): + connection = self.pipe_queue.wait_for_connection() + message = connection.recv() + assert message == 'Hello worker!' + connection.send('Why hello there master!') + message = connection.recv() + assert message == 'Please exit mister worker!' + + +def test(): + pipe_queue = multiprocessing_tools.PipeQueue() + workers = [Worker(pipe_queue) for _ in range(3)] + \ No newline at end of file From a72c10912647d2f2091bb4cc2a01bf8e38c8aa17 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Thu, 21 Jul 2016 18:03:20 +0300 Subject: [PATCH 053/112] - --- .../test_multiprocessing_tools/test_pipe_queue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py b/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py index 3adb12382..913622127 100644 --- a/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py +++ b/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py @@ -21,4 +21,4 @@ def run(self): def test(): pipe_queue = multiprocessing_tools.PipeQueue() workers = [Worker(pipe_queue) for _ in range(3)] - \ No newline at end of file + 1 / 0 was here \ No newline at end of file From bb4058d54bce626cd7a6b426a307bd4aae6c2b33 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Thu, 21 Jul 2016 18:44:17 +0300 Subject: [PATCH 054/112] -- --- .../test_pipe_queue.py | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py b/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py index 913622127..dd7a1514d 100644 --- a/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py +++ b/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py @@ -1,6 +1,7 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +import time import multiprocessing from python_toolbox import multiprocessing_tools @@ -21,4 +22,40 @@ def run(self): def test(): pipe_queue = multiprocessing_tools.PipeQueue() workers = [Worker(pipe_queue) for _ in range(3)] - 1 / 0 was here \ No newline at end of file + def get_n_alive_workers(): + return len([worker.alive() for worker in workers]) + def assert_n_live_workers(n): + for i in range(5): + if get_n_alive_workers() == n: + return + else: + time.sleep(i) + else: + raise AssertionError + + connection_1 = pipe_queue.create_pipe() + connection_1.send('Hello worker!') + for worker in workers: + worker.start() + assert_n_live_workers(3) + connection_2 = pipe_queue.create_pipe() + connection_2.send('Hello worker!') + message = connection_1.recv() + assert message == 'Why hello there master!' + message = connection_2.recv() + assert message == 'Why hello there master!' + assert_n_live_workers(3) + connection_1.send('Please exit mister worker!') + assert_n_live_workers(2) + connection_2.send('Please exit mister worker!') + connection_3 = pipe_queue.create_pipe() + assert_n_live_workers(1) + connection_3.send('Hello worker!') + assert_n_live_workers(1) + message = connection_3.recv() + assert message == 'Why hello there master!' + connection_3.send('Please exit mister worker!') + assert_n_live_workers(1) + + + \ No newline at end of file From 9bcda7261fbfba3573c3dbe645024e074835fc9a Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Thu, 21 Jul 2016 18:46:57 +0300 Subject: [PATCH 055/112] - --- .../test_multiprocessing_tools/test_pipe_queue.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py b/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py index dd7a1514d..d94466809 100644 --- a/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py +++ b/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py @@ -8,6 +8,7 @@ class Worker(multiprocessing.Process): def __init__(self, pipe_queue): + multiprocessing.Process.__init__(self) self.pipe_queue = pipe_queue def run(self): @@ -23,7 +24,7 @@ def test(): pipe_queue = multiprocessing_tools.PipeQueue() workers = [Worker(pipe_queue) for _ in range(3)] def get_n_alive_workers(): - return len([worker.alive() for worker in workers]) + return len([worker.is_alive() for worker in workers]) def assert_n_live_workers(n): for i in range(5): if get_n_alive_workers() == n: From fd4556895976e239433f079e2c6056c3991ff6e3 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 25 Jul 2016 14:56:54 +0300 Subject: [PATCH 056/112] - --- .../thread_pool_exit_stack.py | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 source_py3/python_toolbox/context_management/thread_pool_exit_stack.py diff --git a/source_py3/python_toolbox/context_management/thread_pool_exit_stack.py b/source_py3/python_toolbox/context_management/thread_pool_exit_stack.py new file mode 100644 index 000000000..505f5c0d3 --- /dev/null +++ b/source_py3/python_toolbox/context_management/thread_pool_exit_stack.py @@ -0,0 +1,182 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import contextlib + +from python_toolbox.nifty_collections import CuteEnum + +from .abstract_context_manager import AbstractContextManager +from .context_manager import ContextManager + + +def _get_enterer(enterable): + enter_function = type(exitable).__enter__ + return (lambda: enter_function(enterable)) + + +def _get_exiter(exitable): + exit_function = type(exitable).__exit__ + return ( + lambda exc_type, exc_value, exc_traceback: + exit_function(exitable, exc_type, exc_value, exc_traceback) + ) + + + +class StackState(CuteEnum): + ALIVE = 0 + EXITING = 1 + EXITED = 2 + + +class ThreadPoolExitStack(ContextManager): + """ + blocktododoc + Context manager for dynamic management of a stack of exit callbacks + + For example: + + with ExitStack() as stack: + files = [stack.enter_context(open(fname)) for fname in filenames] + # All opened files will automatically be closed at the end of + # the with statement, even if attempts to open files later + # in the list raise an exception + + """ + def __init__(self, *, executor=None, n_threads=None): + from python_toolbox.future_tools import CuteThreadPoolExecutor + self._exit_callbacks = deque() + self._state = StackState.ALIVE + if executor is None: + self._executor = \ + CuteThreadPoolExecutor(10 if n_threads is None else n_threads) + self.enter_context(self._executor) + else: + assert n_threads is None + self._executor = executor + + def pop_all(self): + """Preserve the context stack by transferring it to a new instance""" + new_stack = type(self)() + new_stack._exit_callbacks = self._exit_callbacks + self._exit_callbacks = deque() + return new_stack + + def push(self, exit): + """Registers a callback with the standard __exit__ method signature + + Can suppress exceptions the same way __exit__ methods can. + + Also accepts any object with an __exit__ method (registering a call + to the method instead of the object itself) + """ + # We use an unbound method rather than a bound method to follow + # the standard lookup behaviour for special methods + _cb_type = type(exit) + try: + exit_method = _cb_type.__exit__ + except AttributeError: + # Not a context manager, so assume its a callable + self._exit_callbacks.append(exit) + else: + self._push_context_manager_exit(exit, exit_method) + return exit # Allow use as a decorator + + def callback(self, callback, *args, **kwds): + """Registers an arbitrary callback and arguments. + + Cannot suppress exceptions. + """ + def _exit_wrapper(exc_type, exc, tb): + callback(*args, **kwds) + # We changed the signature, so using @wraps is not appropriate, but + # setting __wrapped__ may still help with introspection + _exit_wrapper.__wrapped__ = callback + self.push(_exit_wrapper) + return callback # Allow use as a decorator + + def enter_context(self, context_manager): + (result,) = self.enter_contexts((context_manager,)) + return result + + + def enter_contexts(self, context_managers): + """Enters the supplied context manager + + If successful, also pushes its __exit__ method as a callback and + returns the result of the __enter__ method. + """ + from python_toolbox import sequence_tools + # We look up the special methods on the type to match the with statement + + def enter_in_thread(context_manager): + enterer = _get_enterer(context_manager) + if StackState. + enter_value = enterer() + self.push(_get_exiter(context_manager)) + return enter_value + + return tuple(self._executor.map(enter_in_thread, context_managers)) + + def close(self): + """Immediately unwind the context stack""" + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, *exc_details): + if self._state != StackState.ALIVE: + raise RuntimeError + self._state = StackState.EXITING + + received_exc = exc_details[0] is not None + + # We manipulate the exception state so it behaves as though + # we were actually nesting multiple with statements + frame_exc = sys.exc_info()[1] + def _fix_exception_context(new_exc, old_exc): + # Context may not be correct, so find the end of the chain + while 1: + exc_context = new_exc.__context__ + if exc_context is old_exc: + # Context is already set correctly (see issue 20317) + return + if exc_context is None or exc_context is frame_exc: + break + new_exc = exc_context + # Change the end of the chain to point to the exception + # we expect it to reference + new_exc.__context__ = old_exc + + # Callbacks are invoked in LIFO order to match the behaviour of + # nested context managers + suppressed_exc = False + pending_raise = False + while self._exit_callbacks: + cb = self._exit_callbacks.pop() + try: + if cb(*exc_details): + suppressed_exc = True + pending_raise = False + exc_details = (None, None, None) + except: + new_exc_details = sys.exc_info() + # simulate the stack of exceptions by setting the context + _fix_exception_context(new_exc_details[1], exc_details[1]) + pending_raise = True + exc_details = new_exc_details + if pending_raise: + try: + # bare "raise exc_details[1]" replaces our carefully + # set-up context + fixed_ctx = exc_details[1].__context__ + raise exc_details[1] + except BaseException: + exc_details[1].__context__ = fixed_ctx + raise + + assert self._state == StackState.EXITING + self._state = StackState.EXITED + + return received_exc and suppressed_exc From 2084a859beda9623de13b0525ad984cdb48f0d1b Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 25 Jul 2016 15:04:21 +0300 Subject: [PATCH 057/112] - --- .../thread_pool_exit_stack.py | 53 ++++++++++++++----- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/source_py3/python_toolbox/context_management/thread_pool_exit_stack.py b/source_py3/python_toolbox/context_management/thread_pool_exit_stack.py index 505f5c0d3..aebb0edd2 100644 --- a/source_py3/python_toolbox/context_management/thread_pool_exit_stack.py +++ b/source_py3/python_toolbox/context_management/thread_pool_exit_stack.py @@ -2,8 +2,10 @@ # This program is distributed under the MIT license. import contextlib +import threading from python_toolbox.nifty_collections import CuteEnum +from python_toolbox import decorator_tools from .abstract_context_manager import AbstractContextManager from .context_manager import ContextManager @@ -24,11 +26,21 @@ def _get_exiter(exitable): class StackState(CuteEnum): - ALIVE = 0 - EXITING = 1 - EXITED = 2 + CREATED = 0 + ENTERED = 1 + EXITING = 2 + EXITED = 3 + +@decorator_tools.decorator +def _with_lock(function, *args, **kwargs): + thread_pool_exit_stack = args[0] + assert isinstance(thread_pool_exit_stack, ThreadPoolExitStack) + with thread_pool_exit_stack._lock: + return function(*args, **kwargs) + + class ThreadPoolExitStack(ContextManager): """ blocktododoc @@ -46,7 +58,8 @@ class ThreadPoolExitStack(ContextManager): def __init__(self, *, executor=None, n_threads=None): from python_toolbox.future_tools import CuteThreadPoolExecutor self._exit_callbacks = deque() - self._state = StackState.ALIVE + self._lock = threading.RLock() + self._state = StackState.CREATED if executor is None: self._executor = \ CuteThreadPoolExecutor(10 if n_threads is None else n_threads) @@ -55,6 +68,7 @@ def __init__(self, *, executor=None, n_threads=None): assert n_threads is None self._executor = executor + @_with_lock def pop_all(self): """Preserve the context stack by transferring it to a new instance""" new_stack = type(self)() @@ -62,6 +76,7 @@ def pop_all(self): self._exit_callbacks = deque() return new_stack + @_with_lock def push(self, exit): """Registers a callback with the standard __exit__ method signature @@ -82,6 +97,7 @@ def push(self, exit): self._push_context_manager_exit(exit, exit_method) return exit # Allow use as a decorator + @_with_lock def callback(self, callback, *args, **kwds): """Registers an arbitrary callback and arguments. @@ -99,7 +115,14 @@ def enter_context(self, context_manager): (result,) = self.enter_contexts((context_manager,)) return result - + + def __enter_in_thread(self, context_manager): + enterer = _get_enterer(context_manager) + enter_value = enterer() + self.push(_get_exiter(context_manager)) + return enter_value + + @_with_lock def enter_contexts(self, context_managers): """Enters the supplied context manager @@ -108,25 +131,27 @@ def enter_contexts(self, context_managers): """ from python_toolbox import sequence_tools # We look up the special methods on the type to match the with statement + if self._state != StackState.ENTERED: + raise RuntimeError - def enter_in_thread(context_manager): - enterer = _get_enterer(context_manager) - if StackState. - enter_value = enterer() - self.push(_get_exiter(context_manager)) - return enter_value - - return tuple(self._executor.map(enter_in_thread, context_managers)) + return tuple( + self._executor.map(self.enter_in_thread, context_managers) + ) def close(self): """Immediately unwind the context stack""" self.__exit__(None, None, None) + @_with_lock def __enter__(self): + if self._state != StackState.CREATED: + raise RuntimeError return self + @_with_lock def __exit__(self, *exc_details): - if self._state != StackState.ALIVE: + # blocktodo: add threading here too, to exit concurrently? + if self._state != StackState.ENTERED: raise RuntimeError self._state = StackState.EXITING From 6dfd24e7730692499c019e125f76a790dc8c5756 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 10 Aug 2016 01:26:49 +0300 Subject: [PATCH 058/112] dicts --- .../nifty_collections/ordered_dict.py | 43 ----------- ...rious_frozen_dicts.py => various_dicts.py} | 74 +++++++++++++++++-- 2 files changed, 66 insertions(+), 51 deletions(-) delete mode 100644 source_py3/python_toolbox/nifty_collections/ordered_dict.py rename source_py3/python_toolbox/nifty_collections/{various_frozen_dicts.py => various_dicts.py} (65%) diff --git a/source_py3/python_toolbox/nifty_collections/ordered_dict.py b/source_py3/python_toolbox/nifty_collections/ordered_dict.py deleted file mode 100644 index f188cd725..000000000 --- a/source_py3/python_toolbox/nifty_collections/ordered_dict.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2009-2017 Ram Rachum. -# This program is distributed under the MIT license. - -from python_toolbox import comparison_tools - -from collections import OrderedDict as StdlibOrderedDict - - -class OrderedDict(StdlibOrderedDict): - ''' - A dictionary with an order. - - This is a subclass of `collections.OrderedDict` with a couple of - improvements. - ''' - - def sort(self, key=None, reverse=False): - ''' - Sort the items according to their keys, changing the order in-place. - - The optional `key` argument, (not to be confused with the dictionary - keys,) will be passed to the `sorted` function as a key function. - ''' - key_function = \ - comparison_tools.process_key_function_or_attribute_name(key) - sorted_keys = sorted(self.keys(), key=key_function, reverse=reverse) - for key_ in sorted_keys[1:]: - self.move_to_end(key_) - - - def index(self, key): - '''Get the index number of `key`.''' - if key not in self: - raise ValueError - for i, key_ in enumerate(self): - if key_ == key: - return i - raise RuntimeError - - @property - def reversed(self): - '''Get a version of this `OrderedDict` with key order reversed.''' - return type(self)(reversed(tuple(self.items()))) \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py b/source_py3/python_toolbox/nifty_collections/various_dicts.py similarity index 65% rename from source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py rename to source_py3/python_toolbox/nifty_collections/various_dicts.py index 9071d317b..a14c7015a 100644 --- a/source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts.py @@ -6,24 +6,33 @@ import functools import itertools +from python_toolbox import comparison_tools + from .abstract import Ordered, DefinitelyUnordered from .ordered_dict import OrderedDict -class _AbstractFrozenDict(collections.abc.Mapping): - _hash = None # Overridden by instance when calculating hash. +class _AbstractMappingDelegator(collections.abc.Mapping): def __init__(self, *args, **kwargs): self._dict = self._dict_type(*args, **kwargs) __getitem__ = lambda self, key: self._dict[key] __len__ = lambda self: len(self._dict) __iter__ = lambda self: iter(self._dict) - + def copy(self, *args, **kwargs): base_dict = self._dict.copy() base_dict.update(*args, **kwargs) return type(self)(base_dict) + + __repr__ = lambda self: '%s(%s)' % (type(self).__name__, + repr(self._dict) if self._dict else '') + __reduce__ = lambda self: (self.__class__ , (self._dict,)) + + +class _AbstractFrozenDict(_AbstractMappingDelegator): + _hash = None # Overridden by instance when calculating hash. def __hash__(self): if self._hash is None: @@ -40,12 +49,50 @@ def __hash__(self): ) return self._hash - - __repr__ = lambda self: '%s(%s)' % (type(self).__name__, - repr(self._dict) if self._dict else '') - __reduce__ = lambda self: (self.__class__ , (self._dict,)) +class _AbstractDoubleSidedDict(collections.abc.Mapping): + def __init__(self, *args, **kwargs): + self._dict = self._dict_type(*args, **kwargs) + + +class OrderedDict(collections.OrderedDict): + ''' + A dictionary with an order. + This is a subclass of `collections.OrderedDict` with a couple of + improvements. + ''' + + def sort(self, key=None, reverse=False): + ''' + Sort the items according to their keys, changing the order in-place. + + The optional `key` argument, (not to be confused with the dictionary + keys,) will be passed to the `sorted` function as a key function. + ''' + key_function = \ + comparison_tools.process_key_function_or_attribute_name(key) + sorted_keys = sorted(self.keys(), key=key_function, reverse=reverse) + for key_ in sorted_keys[1:]: + self.move_to_end(key_) + + + def index(self, key): + '''Get the index number of `key`.''' + if key not in self: + raise ValueError + for i, key_ in enumerate(self): + if key_ == key: + return i + raise RuntimeError + + @property + def reversed(self): + '''Get a version of this `OrderedDict` with key order reversed.''' + return type(self)(reversed(tuple(self.items()))) + + + class FrozenDict(DefinitelyUnordered, _AbstractFrozenDict): ''' An immutable `dict`. @@ -99,4 +146,15 @@ def __repr__(self): else: inner = '' return '%s(%s)' % (type(self).__name__, inner) - \ No newline at end of file + + + + +class DoubleSidedFrozenDict: + 1 / 0 + +class DoubleSidedOrderedDict: + 1 / 0 + +class DoubleSidedFrozenOrderedDict: + 1 / 0 From 55cf28e7e48626779eeaa4222eb7abc94ba4f367 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 10 Aug 2016 01:26:55 +0300 Subject: [PATCH 059/112] - --- source_py3/python_toolbox/nifty_collections/various_dicts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts.py b/source_py3/python_toolbox/nifty_collections/various_dicts.py index a14c7015a..b23f8a00e 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts.py @@ -53,6 +53,7 @@ def __hash__(self): class _AbstractDoubleSidedDict(collections.abc.Mapping): def __init__(self, *args, **kwargs): self._dict = self._dict_type(*args, **kwargs) + self.inverse = class OrderedDict(collections.OrderedDict): From e25075d1aaff31ffd9d6bb302cd89378da932ef7 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 10 Aug 2016 01:48:16 +0300 Subject: [PATCH 060/112] - --- .../nifty_collections/various_dicts.py | 161 ------------------ .../various_dicts/abstract.py | 99 +++++++++++ .../various_dicts/ordered_dict.py | 43 +++++ .../various_dicts/various_dicts.py | 80 +++++++++ 4 files changed, 222 insertions(+), 161 deletions(-) delete mode 100644 source_py3/python_toolbox/nifty_collections/various_dicts.py create mode 100644 source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py create mode 100644 source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py create mode 100644 source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts.py b/source_py3/python_toolbox/nifty_collections/various_dicts.py deleted file mode 100644 index b23f8a00e..000000000 --- a/source_py3/python_toolbox/nifty_collections/various_dicts.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright 2009-2017 Ram Rachum. -# This program is distributed under the MIT license. - -import collections -import operator -import functools -import itertools - -from python_toolbox import comparison_tools - -from .abstract import Ordered, DefinitelyUnordered -from .ordered_dict import OrderedDict - - - -class _AbstractMappingDelegator(collections.abc.Mapping): - def __init__(self, *args, **kwargs): - self._dict = self._dict_type(*args, **kwargs) - - __getitem__ = lambda self, key: self._dict[key] - __len__ = lambda self: len(self._dict) - __iter__ = lambda self: iter(self._dict) - - def copy(self, *args, **kwargs): - base_dict = self._dict.copy() - base_dict.update(*args, **kwargs) - return type(self)(base_dict) - - __repr__ = lambda self: '%s(%s)' % (type(self).__name__, - repr(self._dict) if self._dict else '') - __reduce__ = lambda self: (self.__class__ , (self._dict,)) - - -class _AbstractFrozenDict(_AbstractMappingDelegator): - _hash = None # Overridden by instance when calculating hash. - - def __hash__(self): - if self._hash is None: - self._hash = functools.reduce( - operator.xor, - map( - hash, - itertools.chain( - (h for h in self.items()), - (type(self), len(self)) - ) - ), - 0 - ) - - return self._hash - -class _AbstractDoubleSidedDict(collections.abc.Mapping): - def __init__(self, *args, **kwargs): - self._dict = self._dict_type(*args, **kwargs) - self.inverse = - - -class OrderedDict(collections.OrderedDict): - ''' - A dictionary with an order. - - This is a subclass of `collections.OrderedDict` with a couple of - improvements. - ''' - - def sort(self, key=None, reverse=False): - ''' - Sort the items according to their keys, changing the order in-place. - - The optional `key` argument, (not to be confused with the dictionary - keys,) will be passed to the `sorted` function as a key function. - ''' - key_function = \ - comparison_tools.process_key_function_or_attribute_name(key) - sorted_keys = sorted(self.keys(), key=key_function, reverse=reverse) - for key_ in sorted_keys[1:]: - self.move_to_end(key_) - - - def index(self, key): - '''Get the index number of `key`.''' - if key not in self: - raise ValueError - for i, key_ in enumerate(self): - if key_ == key: - return i - raise RuntimeError - - @property - def reversed(self): - '''Get a version of this `OrderedDict` with key order reversed.''' - return type(self)(reversed(tuple(self.items()))) - - - -class FrozenDict(DefinitelyUnordered, _AbstractFrozenDict): - ''' - An immutable `dict`. - - A `dict` that can't be changed. The advantage of this over `dict` is mainly - that it's hashable, and thus can be used as a key in dicts and sets. - - In other words, `FrozenDict` is to `dict` what `frozenset` is to `set`. - ''' - _dict_type = dict - - -class FrozenOrderedDict(Ordered, _AbstractFrozenDict): - ''' - An immutable, ordered `dict`. - - A `dict` that is ordered and can't be changed. The advantage of this over - `OrderedDict` is mainly that it's hashable, and thus can be used as a key - in dicts and sets. - ''' - _dict_type = OrderedDict - - def __eq__(self, other): - if isinstance(other, (OrderedDict, FrozenOrderedDict)): - return collections.abc.Mapping.__eq__(self, other) and \ - all(map(operator.eq, self, other)) - return collections.abc.Mapping.__eq__(self, other) - - __hash__ = _AbstractFrozenDict.__hash__ - # (Gotta manually carry `__hash__` over from the base class because setting - # `__eq__` resets it. ) - - - # Poor man's caching because we can't import `CachedProperty` due to import - # loop: - _reversed = None - @property - def reversed(self): - ''' - Get a version of this `FrozenOrderedDict` with key order reversed. - ''' - if self._reversed is None: - self._reversed = type(self)(reversed(tuple(self.items()))) - return self._reversed - - def __repr__(self): - if self._dict: - inner = '[%s]' % ', '.join( - '(%s, %s)' % item for item in self._dict.items() - ) - else: - inner = '' - return '%s(%s)' % (type(self).__name__, inner) - - - - -class DoubleSidedFrozenDict: - 1 / 0 - -class DoubleSidedOrderedDict: - 1 / 0 - -class DoubleSidedFrozenOrderedDict: - 1 / 0 diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py new file mode 100644 index 000000000..2f99d3157 --- /dev/null +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py @@ -0,0 +1,99 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import collections +import operator +import functools +import itertools + +from python_toolbox import comparison_tools + +from .abstract import Ordered, DefinitelyUnordered +from .ordered_dict import OrderedDict + + +class _AbstractMappingDelegator(collections.abc.Mapping): + def __init__(self, *args, **kwargs): + self._dict = self._dict_type(*args, **kwargs) + + __getitem__ = lambda self, key: self._dict[key] + __len__ = lambda self: len(self._dict) + __iter__ = lambda self: iter(self._dict) + + def copy(self, *args, **kwargs): + base_dict = self._dict.copy() + base_dict.update(*args, **kwargs) + return type(self)(base_dict) + + __repr__ = lambda self: '%s(%s)' % (type(self).__name__, + repr(self._dict) if self._dict else '') + __reduce__ = lambda self: (self.__class__ , (self._dict,)) + + +class _AbstractFrozenDict(_AbstractMappingDelegator): + _hash = None # Overridden by instance when calculating hash. + + def __hash__(self): + if self._hash is None: + self._hash = functools.reduce( + operator.xor, + map( + hash, + itertools.chain( + (h for h in self.items()), + (type(self), len(self)) + ) + ), + 0 + ) + + return self._hash + + +class _AbstractDoubleSidedDict(_AbstractMappingDelegator): + def __init__(self, *args, **kwargs): + if hasattr(self, '_dict'): + assert self.inverse.inverse is self + else: + self._dict = self._dict_type(*args, **kwargs) + internal_inverse = self._dict_type((value, key) for key, value + in self._dict.items()) + if len(internal_inverse) <= len(self._dict): + raise Exception("There's a repeating value given to the " + "double-sided dict, that is not allowed.") # blocktodo test + assert len(internal_inverse) == len(self._dict) + self.inverse = type(self).__new__() + self.inverse._dict = internal_inverse + self.inverse.inverse = self + self.inverse.__init__() + + +class _AbstractMutableDoubleSidedDict(_AbstractDoubleSidedDict, + collections.abc.MutableMapping): + @abstractmethod + def __setitem__(self, key, value): + try: + existing_key = self.inverse[value] + except KeyError: + self._dict[key] = value + self.inverse._dict[value] = key + else: + raise Exception( + "Can't add key %s with value %s because there is already a " + "key %s with the same value." % (key, value, + self.inverse[value]) # blocktodo test + ) + + @abstractmethod + def __delitem__(self, key): + value = self[key] # Propagating possible KeyError # blocktodo test + del self._dict[key] + del self.inverse._dict[value] + + + def clear(self): + 'D.clear() -> None. Remove all items from D.' + self._dict.clear() + self.inverse._dict.clear() + + diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py b/source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py new file mode 100644 index 000000000..f188cd725 --- /dev/null +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py @@ -0,0 +1,43 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +from python_toolbox import comparison_tools + +from collections import OrderedDict as StdlibOrderedDict + + +class OrderedDict(StdlibOrderedDict): + ''' + A dictionary with an order. + + This is a subclass of `collections.OrderedDict` with a couple of + improvements. + ''' + + def sort(self, key=None, reverse=False): + ''' + Sort the items according to their keys, changing the order in-place. + + The optional `key` argument, (not to be confused with the dictionary + keys,) will be passed to the `sorted` function as a key function. + ''' + key_function = \ + comparison_tools.process_key_function_or_attribute_name(key) + sorted_keys = sorted(self.keys(), key=key_function, reverse=reverse) + for key_ in sorted_keys[1:]: + self.move_to_end(key_) + + + def index(self, key): + '''Get the index number of `key`.''' + if key not in self: + raise ValueError + for i, key_ in enumerate(self): + if key_ == key: + return i + raise RuntimeError + + @property + def reversed(self): + '''Get a version of this `OrderedDict` with key order reversed.''' + return type(self)(reversed(tuple(self.items()))) \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py new file mode 100644 index 000000000..9cf7c9854 --- /dev/null +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py @@ -0,0 +1,80 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import collections +import operator +import functools +import itertools + +from python_toolbox import comparison_tools + +from python_toolbox.nifty_collections.abstract import (Ordered, + DefinitelyUnordered) +from . import abstract + + +class FrozenDict(DefinitelyUnordered, abstract.): + ''' + An immutable `dict`. + + A `dict` that can't be changed. The advantage of this over `dict` is mainly + that it's hashable, and thus can be used as a key in dicts and sets. + + In other words, `FrozenDict` is to `dict` what `frozenset` is to `set`. + ''' + _dict_type = dict + + +class FrozenOrderedDict(Ordered, _AbstractFrozenDict): + ''' + An immutable, ordered `dict`. + + A `dict` that is ordered and can't be changed. The advantage of this over + `OrderedDict` is mainly that it's hashable, and thus can be used as a key + in dicts and sets. + ''' + _dict_type = OrderedDict + + def __eq__(self, other): + if isinstance(other, (OrderedDict, FrozenOrderedDict)): + return collections.abc.Mapping.__eq__(self, other) and \ + all(map(operator.eq, self, other)) + return collections.abc.Mapping.__eq__(self, other) + + __hash__ = _AbstractFrozenDict.__hash__ + # (Gotta manually carry `__hash__` over from the base class because setting + # `__eq__` resets it. ) + + + # Poor man's caching because we can't import `CachedProperty` due to import + # loop: + _reversed = None + @property + def reversed(self): + ''' + Get a version of this `FrozenOrderedDict` with key order reversed. + ''' + if self._reversed is None: + self._reversed = type(self)(reversed(tuple(self.items()))) + return self._reversed + + def __repr__(self): + if self._dict: + inner = '[%s]' % ', '.join( + '(%s, %s)' % item for item in self._dict.items() + ) + else: + inner = '' + return '%s(%s)' % (type(self).__name__, inner) + + + + +class DoubleSidedFrozenDict: + 1 / 0 + +class DoubleSidedOrderedDict: + 1 / 0 + +class DoubleSidedFrozenOrderedDict: + 1 / 0 From a5903d6f0bc10ba42b5f96c57ab7584f577f5086 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 10 Aug 2016 01:54:57 +0300 Subject: [PATCH 061/112] - --- .../various_dicts/abstract.py | 19 ++++++++++++++--- .../various_dicts/various_dicts.py | 21 ++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py index 2f99d3157..eb2ecc59a 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py @@ -8,7 +8,8 @@ from python_toolbox import comparison_tools -from .abstract import Ordered, DefinitelyUnordered +from python_toolbox.nifty_collections.abstract import (Ordered, + DefinitelyUnordered) from .ordered_dict import OrderedDict @@ -30,6 +31,15 @@ def copy(self, *args, **kwargs): __reduce__ = lambda self: (self.__class__ , (self._dict,)) +class _AbstractMutableMappingDelegator(_AbstractMappingDelegator, + collections.abc.MutableMapping): + def __setitem__(self, key, value): + self._dict[key] = value + + def __delitem__(self, key): + del self._dict[key] + + class _AbstractFrozenDict(_AbstractMappingDelegator): _hash = None # Overridden by instance when calculating hash. @@ -70,7 +80,6 @@ def __init__(self, *args, **kwargs): class _AbstractMutableDoubleSidedDict(_AbstractDoubleSidedDict, collections.abc.MutableMapping): - @abstractmethod def __setitem__(self, key, value): try: existing_key = self.inverse[value] @@ -84,7 +93,6 @@ def __setitem__(self, key, value): self.inverse[value]) # blocktodo test ) - @abstractmethod def __delitem__(self, key): value = self[key] # Propagating possible KeyError # blocktodo test del self._dict[key] @@ -97,3 +105,8 @@ def clear(self): self.inverse._dict.clear() +class _UnorderedDictDelegator(DefinitelyUnordered, + _AbstractMappingDelegator): + + _dict_type = dict + \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py index 9cf7c9854..db2f721ca 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py @@ -8,12 +8,18 @@ from python_toolbox import comparison_tools -from python_toolbox.nifty_collections.abstract import (Ordered, - DefinitelyUnordered) +from .ordered_dict import OrderedDict from . import abstract -class FrozenDict(DefinitelyUnordered, abstract.): +class DoubleSidedDict(abstract._AbstractUnorderedDict, + abstract._AbstractMutableDoubleSidedDict): + ''' + blocktododoc''' + + +class FrozenDict(abstract._AbstractUnorderedDict, + abstract._AbstractFrozenDict): ''' An immutable `dict`. @@ -23,6 +29,13 @@ class FrozenDict(DefinitelyUnordered, abstract.): In other words, `FrozenDict` is to `dict` what `frozenset` is to `set`. ''' _dict_type = dict + + +OrderedDict = OrderedDict + +class DoubleSidedFrozenDict: + 1 / 0 + class FrozenOrderedDict(Ordered, _AbstractFrozenDict): @@ -70,8 +83,6 @@ def __repr__(self): -class DoubleSidedFrozenDict: - 1 / 0 class DoubleSidedOrderedDict: 1 / 0 From 38ba41b2ae8eed9acd5ae1ebd3e83ab73cc0f4b2 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 10 Aug 2016 02:08:30 +0300 Subject: [PATCH 062/112] - --- .../various_dicts/abstract.py | 30 +++++++++++++++++-- .../various_dicts/various_dicts.py | 20 ++++++++----- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py index eb2ecc59a..2ae7dce4d 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py @@ -80,33 +80,59 @@ def __init__(self, *args, **kwargs): class _AbstractMutableDoubleSidedDict(_AbstractDoubleSidedDict, collections.abc.MutableMapping): + + def _assert_valid(self): + assert len(self._dict) == len(self.inverse._dict) + def __setitem__(self, key, value): + self._assert_valid() try: existing_key = self.inverse[value] except KeyError: - self._dict[key] = value - self.inverse._dict[value] = key + pass else: raise Exception( "Can't add key %s with value %s because there is already a " "key %s with the same value." % (key, value, self.inverse[value]) # blocktodo test ) + + try: + existing_value = self[key] + except KeyError: + got_existing_value = True + else: + got_existing_value = False + + self._dict[key] = value + self.inverse._dict[value] = key + if got_existing_value: + del self.inverse._dict[existing_value] + self._assert_valid() + def __delitem__(self, key): value = self[key] # Propagating possible KeyError # blocktodo test + self._assert_valid() del self._dict[key] del self.inverse._dict[value] + self._assert_valid() def clear(self): 'D.clear() -> None. Remove all items from D.' + self._assert_valid() self._dict.clear() self.inverse._dict.clear() + self._assert_valid() class _UnorderedDictDelegator(DefinitelyUnordered, _AbstractMappingDelegator): _dict_type = dict + +class _OrderedDictDelegator(Ordered, _AbstractMappingDelegator): + + _dict_type = OrderedDict \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py index db2f721ca..42ad7f967 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py @@ -12,13 +12,13 @@ from . import abstract -class DoubleSidedDict(abstract._AbstractUnorderedDict, +class DoubleSidedDict(abstract._UnorderedDictDelegator, abstract._AbstractMutableDoubleSidedDict): ''' blocktododoc''' -class FrozenDict(abstract._AbstractUnorderedDict, +class FrozenDict(abstract._UnorderedDictDelegator, abstract._AbstractFrozenDict): ''' An immutable `dict`. @@ -28,15 +28,19 @@ class FrozenDict(abstract._AbstractUnorderedDict, In other words, `FrozenDict` is to `dict` what `frozenset` is to `set`. ''' - _dict_type = dict -OrderedDict = OrderedDict - -class DoubleSidedFrozenDict: - 1 / 0 +class DoubleSidedFrozenDict(abstract._UnorderedDictDelegator, + abstract._AbstractDoubleSidedDict, + abstract._AbstractFrozenDict): + '''blocktododoc''' + - +class DoubleSidedOrderedDict(abstract._OrderedDictDelegator, + abstract._AbstractMutableDoubleSidedDict, + abstract._AbstractFrozenDict): + '''blocktododoc''' + class FrozenOrderedDict(Ordered, _AbstractFrozenDict): ''' From 2b7c30c31fbfd3bc2afe10b2f314ebbab5a7bae8 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 10 Aug 2016 02:25:43 +0300 Subject: [PATCH 063/112] - --- .../various_dicts/abstract.py | 44 ++++++++++++++++++- .../various_dicts/ordered_dict.py | 6 +-- .../various_dicts/various_dicts.py | 31 +++++++++---- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py index 2ae7dce4d..03214a98e 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py @@ -132,7 +132,49 @@ class _UnorderedDictDelegator(DefinitelyUnordered, _dict_type = dict + class _OrderedDictDelegator(Ordered, _AbstractMappingDelegator): _dict_type = OrderedDict - \ No newline at end of file + def __reversed__(self): + return reversed(self._dict) + + def index(self, key): + '''Get the index number of `key`.''' + if key not in self._dict: + raise ValueError + for i, key_ in enumerate(self._dict): + if key_ == key: + return i + raise RuntimeError + + +class _MutableOrderedDictDelegator(_OrderedDictDelegator, + _AbstractMutableMappingDelegator): + + + def sort(self, key=None, reverse=False): + ''' + Sort the items according to their keys, changing the order in-place. + + The optional `key` argument, (not to be confused with the dictionary + keys,) will be passed to the `sorted` function as a key function. + ''' + key_function = \ + comparison_tools.process_key_function_or_attribute_name(key) + sorted_keys = sorted(self._dict.keys(), key=key_function, + reverse=reverse) + for key_ in sorted_keys[1:]: + self.move_to_end(key_) + + + def move_to_end(self, key, last=True): + ''' + Move an existing element to the end (or beginning if `last is False`.) + + Raises `KeyError` if the element does not exist. + + When `last is True`, acts like a fast version of `self[key] = + self.pop(key)`. + ''' + self._dict.move_to_end(key, last=last) \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py b/source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py index f188cd725..0ade01920 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py @@ -36,8 +36,4 @@ def index(self, key): if key_ == key: return i raise RuntimeError - - @property - def reversed(self): - '''Get a version of this `OrderedDict` with key order reversed.''' - return type(self)(reversed(tuple(self.items()))) \ No newline at end of file + \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py index 42ad7f967..72ff3c191 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py @@ -41,6 +41,22 @@ class DoubleSidedOrderedDict(abstract._OrderedDictDelegator, abstract._AbstractFrozenDict): '''blocktododoc''' + + def move_to_end(self, key, last=True): + ''' + Move an existing element to the end (or beginning if `last is False`.) + + Raises `KeyError` if the element does not exist. + + When `last is True`, acts like a fast version of `self[key] = + self.pop(key)`. + ''' + self._assert_valid() + value = self._dict[key] # Propagate `KeyError`. + self._dict.move_to_end(key, last=last) + self.inverse._dict.move_to_end(value, last=last) + self._assert_valid() + class FrozenOrderedDict(Ordered, _AbstractFrozenDict): ''' @@ -66,8 +82,7 @@ def __eq__(self, other): # Poor man's caching because we can't import `CachedProperty` due to import # loop: _reversed = None - @property - def reversed(self): + def __reversed__(self): ''' Get a version of this `FrozenOrderedDict` with key order reversed. ''' @@ -85,11 +100,9 @@ def __repr__(self): return '%s(%s)' % (type(self).__name__, inner) - - -class DoubleSidedOrderedDict: - 1 / 0 - -class DoubleSidedFrozenOrderedDict: - 1 / 0 +class DoubleSidedFrozenOrderedDict(abstract._OrderedDictDelegator, + abstract._AbstractDoubleSidedDict, + abstract._AbstractFrozenDict): + '''blocktododoc''' + \ No newline at end of file From 6d6e9b3034c30ac4f918d9212d1c4ab4c00f6499 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 10 Aug 2016 02:27:03 +0300 Subject: [PATCH 064/112] - --- .../nifty_collections/various_dicts/various_dicts.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py index 72ff3c191..b8db882f3 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py @@ -105,4 +105,9 @@ class DoubleSidedFrozenOrderedDict(abstract._OrderedDictDelegator, abstract._AbstractDoubleSidedDict, abstract._AbstractFrozenDict): '''blocktododoc''' - \ No newline at end of file + + +# blocktodo: Do sophisticated tests that iterate over the dict classes, see +# their attributes (defined in the tests) and do the appropriate tests. For +# example if a dict is ordered and mutable, we should test .sort and +# .move_to_end, otherwise we should test these methods don't exist. From 1a4ebfc098870aaf814c8f93d62d136e10f5f139 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 10 Aug 2016 02:36:41 +0300 Subject: [PATCH 065/112] - --- .../nifty_collections/various_dicts/abstract.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py index 03214a98e..cafac47da 100644 --- a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py @@ -97,6 +97,12 @@ def __setitem__(self, key, value): self.inverse[value]) # blocktodo test ) + try: + hash(value) + except TypeError as hashing_error: + raise TypeError('%s is not hashable so it can\'t be used as a ' + 'value in a double-sided dict.' % value) + try: existing_value = self[key] except KeyError: From 6771379cbdd5c9a99b0e20ad4712d52786c051b3 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 12:54:24 +0300 Subject: [PATCH 066/112] - --- source_py3/python_toolbox/nifty_collections/__init__.py | 7 ++++--- .../nifty_collections/nifty_dicts/__init__.py | 4 ++++ .../{various_dicts => nifty_dicts}/abstract.py | 0 .../various_dicts.py => nifty_dicts/nifty_dicts.py} | 0 .../{various_dicts => nifty_dicts}/ordered_dict.py | 0 5 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py rename source_py3/python_toolbox/nifty_collections/{various_dicts => nifty_dicts}/abstract.py (100%) rename source_py3/python_toolbox/nifty_collections/{various_dicts/various_dicts.py => nifty_dicts/nifty_dicts.py} (100%) rename source_py3/python_toolbox/nifty_collections/{various_dicts => nifty_dicts}/ordered_dict.py (100%) diff --git a/source_py3/python_toolbox/nifty_collections/__init__.py b/source_py3/python_toolbox/nifty_collections/__init__.py index 2af7b7d9b..082e20ddf 100644 --- a/source_py3/python_toolbox/nifty_collections/__init__.py +++ b/source_py3/python_toolbox/nifty_collections/__init__.py @@ -3,17 +3,18 @@ '''Defines various data types, similarly to the stdlib's `collections`.''' -from .ordered_dict import OrderedDict from .various_ordered_sets import OrderedSet, FrozenOrderedSet, EmittingOrderedSet from .weak_key_default_dict import WeakKeyDefaultDict from .weak_key_identity_dict import WeakKeyIdentityDict from .lazy_tuple import LazyTuple -from .various_frozen_dicts import FrozenDict, FrozenOrderedDict from .bagging import Bag, OrderedBag, FrozenBag, FrozenOrderedBag from .frozen_bag_bag import FrozenBagBag from .condition_list import ConditionList from ..cute_enum import CuteEnum - from .emitting_weak_key_default_dict import EmittingWeakKeyDefaultDict +from .nifty_dicts import (DoubleSidedDict, FrozenDict, OrderedDict, + DoubleSidedFrozenDict, DoubleSidedOrderedDict, + FrozenOrderedDict, + DoubleSidedFrozenOrderedDict) from .abstract import Ordered, DefinitelyUnordered \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py new file mode 100644 index 000000000..f4eb1672a --- /dev/null +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py @@ -0,0 +1,4 @@ +from .nifty_dicts import (DoubleSidedDict, FrozenDict, OrderedDict, + DoubleSidedFrozenDict, DoubleSidedOrderedDict, + FrozenOrderedDict, + DoubleSidedFrozenOrderedDict,) \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py similarity index 100% rename from source_py3/python_toolbox/nifty_collections/various_dicts/abstract.py rename to source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py similarity index 100% rename from source_py3/python_toolbox/nifty_collections/various_dicts/various_dicts.py rename to source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py diff --git a/source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py similarity index 100% rename from source_py3/python_toolbox/nifty_collections/various_dicts/ordered_dict.py rename to source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py From f2dcfe85879339df51d264974b48ad07d749026b Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 12:56:21 +0300 Subject: [PATCH 067/112] - --- .../test_nifty_collections/test_nifty_dicts/old/__init__.py | 1 + .../{ => test_nifty_dicts/old}/test_frozen_dict.py | 0 .../{ => test_nifty_dicts/old}/test_frozen_ordered_dict.py | 0 .../{ => test_nifty_dicts/old}/test_ordered_dict/__init__.py | 0 .../{ => test_nifty_dicts/old}/test_ordered_dict/test.py | 0 .../old}/test_ordered_dict/test_with_stdlib_ordered_dict.py | 0 6 files changed, 1 insertion(+) create mode 100644 source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/__init__.py rename source_py3/test_python_toolbox/test_nifty_collections/{ => test_nifty_dicts/old}/test_frozen_dict.py (100%) rename source_py3/test_python_toolbox/test_nifty_collections/{ => test_nifty_dicts/old}/test_frozen_ordered_dict.py (100%) rename source_py3/test_python_toolbox/test_nifty_collections/{ => test_nifty_dicts/old}/test_ordered_dict/__init__.py (100%) rename source_py3/test_python_toolbox/test_nifty_collections/{ => test_nifty_dicts/old}/test_ordered_dict/test.py (100%) rename source_py3/test_python_toolbox/test_nifty_collections/{ => test_nifty_dicts/old}/test_ordered_dict/test_with_stdlib_ordered_dict.py (100%) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/__init__.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/__init__.py new file mode 100644 index 000000000..9e49b29ea --- /dev/null +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/__init__.py @@ -0,0 +1 @@ +1/0 # blocktodo all code from this folder should go organized into test_nifty_dicts \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_dict.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_frozen_dict.py similarity index 100% rename from source_py3/test_python_toolbox/test_nifty_collections/test_frozen_dict.py rename to source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_frozen_dict.py diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_frozen_ordered_dict.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_frozen_ordered_dict.py similarity index 100% rename from source_py3/test_python_toolbox/test_nifty_collections/test_frozen_ordered_dict.py rename to source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_frozen_ordered_dict.py diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_ordered_dict/__init__.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_ordered_dict/__init__.py similarity index 100% rename from source_py3/test_python_toolbox/test_nifty_collections/test_ordered_dict/__init__.py rename to source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_ordered_dict/__init__.py diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_ordered_dict/test.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_ordered_dict/test.py similarity index 100% rename from source_py3/test_python_toolbox/test_nifty_collections/test_ordered_dict/test.py rename to source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_ordered_dict/test.py diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_ordered_dict/test_with_stdlib_ordered_dict.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_ordered_dict/test_with_stdlib_ordered_dict.py similarity index 100% rename from source_py3/test_python_toolbox/test_nifty_collections/test_ordered_dict/test_with_stdlib_ordered_dict.py rename to source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/test_ordered_dict/test_with_stdlib_ordered_dict.py From eabaeae4fbdc29bec4d93983f66a75a960a51385 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 12:57:37 +0300 Subject: [PATCH 068/112] - --- .../test_nifty_collections/test_nifty_dicts/__init__.py | 3 +++ .../test_nifty_collections/test_nifty_dicts/old/__init__.py | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py new file mode 100644 index 000000000..ed9a70da2 --- /dev/null +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/__init__.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/__init__.py index 9e49b29ea..c512d1261 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/__init__.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/old/__init__.py @@ -1 +1,4 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + 1/0 # blocktodo all code from this folder should go organized into test_nifty_dicts \ No newline at end of file From 79012ab7eb3e2dc14e1bcf6be500ae9705f12356 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:06:00 +0300 Subject: [PATCH 069/112] - --- .../nifty_collections/__init__.py | 6 +-- .../nifty_collections/nifty_dicts/__init__.py | 6 +-- .../nifty_collections/nifty_dicts/abstract.py | 4 +- .../nifty_dicts/nifty_dicts.py | 16 ++++---- .../test_nifty_dicts/__init__.py | 41 +++++++++++++++++++ 5 files changed, 57 insertions(+), 16 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/__init__.py b/source_py3/python_toolbox/nifty_collections/__init__.py index 082e20ddf..5b76f164f 100644 --- a/source_py3/python_toolbox/nifty_collections/__init__.py +++ b/source_py3/python_toolbox/nifty_collections/__init__.py @@ -12,9 +12,9 @@ from .condition_list import ConditionList from ..cute_enum import CuteEnum from .emitting_weak_key_default_dict import EmittingWeakKeyDefaultDict -from .nifty_dicts import (DoubleSidedDict, FrozenDict, OrderedDict, - DoubleSidedFrozenDict, DoubleSidedOrderedDict, +from .nifty_dicts import (DoubleDict, FrozenDict, OrderedDict, + DoubleFrozenDict, DoubleOrderedDict, FrozenOrderedDict, - DoubleSidedFrozenOrderedDict) + DoubleFrozenOrderedDict) from .abstract import Ordered, DefinitelyUnordered \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py index f4eb1672a..049fd8cbc 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py @@ -1,4 +1,4 @@ -from .nifty_dicts import (DoubleSidedDict, FrozenDict, OrderedDict, - DoubleSidedFrozenDict, DoubleSidedOrderedDict, +from .nifty_dicts import (DoubleDict, FrozenDict, OrderedDict, + DoubleFrozenDict, DoubleOrderedDict, FrozenOrderedDict, - DoubleSidedFrozenOrderedDict,) \ No newline at end of file + DoubleFrozenOrderedDict,) \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index cafac47da..4033ac952 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -60,7 +60,7 @@ def __hash__(self): return self._hash -class _AbstractDoubleSidedDict(_AbstractMappingDelegator): +class _AbstractDoubleDict(_AbstractMappingDelegator): def __init__(self, *args, **kwargs): if hasattr(self, '_dict'): assert self.inverse.inverse is self @@ -78,7 +78,7 @@ def __init__(self, *args, **kwargs): self.inverse.__init__() -class _AbstractMutableDoubleSidedDict(_AbstractDoubleSidedDict, +class _AbstractMutableDoubleDict(_AbstractDoubleDict, collections.abc.MutableMapping): def _assert_valid(self): diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py index b8db882f3..8c119172e 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py @@ -12,8 +12,8 @@ from . import abstract -class DoubleSidedDict(abstract._UnorderedDictDelegator, - abstract._AbstractMutableDoubleSidedDict): +class DoubleDict(abstract._UnorderedDictDelegator, + abstract._AbstractMutableDoubleDict): ''' blocktododoc''' @@ -30,14 +30,14 @@ class FrozenDict(abstract._UnorderedDictDelegator, ''' -class DoubleSidedFrozenDict(abstract._UnorderedDictDelegator, - abstract._AbstractDoubleSidedDict, +class DoubleFrozenDict(abstract._UnorderedDictDelegator, + abstract._AbstractDoubleDict, abstract._AbstractFrozenDict): '''blocktododoc''' -class DoubleSidedOrderedDict(abstract._OrderedDictDelegator, - abstract._AbstractMutableDoubleSidedDict, +class DoubleOrderedDict(abstract._OrderedDictDelegator, + abstract._AbstractMutableDoubleDict, abstract._AbstractFrozenDict): '''blocktododoc''' @@ -101,8 +101,8 @@ def __repr__(self): -class DoubleSidedFrozenOrderedDict(abstract._OrderedDictDelegator, - abstract._AbstractDoubleSidedDict, +class DoubleFrozenOrderedDict(abstract._OrderedDictDelegator, + abstract._AbstractDoubleDict, abstract._AbstractFrozenDict): '''blocktododoc''' diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py index ed9a70da2..32600449a 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py @@ -1,3 +1,44 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +from python_toolbox.third_party import unittest2 + +import nose + +from python_toolbox import cute_iter_tools +from python_toolbox import cute_testing + +from python_toolbox import nifty_collections +from python_toolbox.nifty_collections import ( + DoubleDict, FrozenDict, OrderedDict, + DoubleFrozenDict, DoubleOrderedDict, + FrozenOrderedDict, DoubleFrozenOrderedDict +) + + +class BaseDictTestCase(cute_testing.TestCase): + __test__ = False + d_type = None # Filled in by subclasses + + def test_common(self): + d = self.d_type(((1, 2), (3, 4), (5, 6))) + assert len(d) == 3 + assert set(d.keys()) == {1, 3, 5} + assert set(d.values()) == {2, 4, 6} + assert set(d.items()) == {(1, 2), (3, 4), (5, 6)} + assert d[1] == 2 + assert d[3] == 4 + assert d[5] == 6 + with cute_testing.RaiseAssertor(exception_type=KeyError): + d[7] + with cute_testing.RaiseAssertor(exception_type=KeyError): + d[None] + with cute_testing.RaiseAssertor(exception_type=KeyError): + d['whatever'] + + assert d.get(1) == 2 + assert d.get(1, 'whatever') == 2 + assert d.get(10, 'whatever') == 'whatever' + + +class (cute_testing.TestCase): From b15d2f77f0033c0af5298888e6538c67458d403c Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:07:16 +0300 Subject: [PATCH 070/112] - --- .../python_toolbox/nifty_collections/__init__.py | 9 +++++---- .../nifty_collections/nifty_dicts/__init__.py | 9 +++++---- .../nifty_collections/nifty_dicts/abstract.py | 2 +- .../nifty_collections/nifty_dicts/nifty_dicts.py | 14 +++++++------- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/__init__.py b/source_py3/python_toolbox/nifty_collections/__init__.py index 5b76f164f..5cc24f4b9 100644 --- a/source_py3/python_toolbox/nifty_collections/__init__.py +++ b/source_py3/python_toolbox/nifty_collections/__init__.py @@ -12,9 +12,10 @@ from .condition_list import ConditionList from ..cute_enum import CuteEnum from .emitting_weak_key_default_dict import EmittingWeakKeyDefaultDict -from .nifty_dicts import (DoubleDict, FrozenDict, OrderedDict, - DoubleFrozenDict, DoubleOrderedDict, - FrozenOrderedDict, - DoubleFrozenOrderedDict) +from .nifty_dicts import ( + DoubleDict, FrozenDict, OrderedDict, + DoubleFrozenDict, DoubleOrderedDict, FrozenOrderedDict, + DoubleFrozenOrderedDict +) from .abstract import Ordered, DefinitelyUnordered \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py index 049fd8cbc..15da0d2ce 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/__init__.py @@ -1,4 +1,5 @@ -from .nifty_dicts import (DoubleDict, FrozenDict, OrderedDict, - DoubleFrozenDict, DoubleOrderedDict, - FrozenOrderedDict, - DoubleFrozenOrderedDict,) \ No newline at end of file +from .nifty_dicts import ( + DoubleDict, FrozenDict, OrderedDict, + DoubleFrozenDict, DoubleOrderedDict, FrozenOrderedDict, + DoubleFrozenOrderedDict +) \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index 4033ac952..08622db6c 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -79,7 +79,7 @@ def __init__(self, *args, **kwargs): class _AbstractMutableDoubleDict(_AbstractDoubleDict, - collections.abc.MutableMapping): + collections.abc.MutableMapping): def _assert_valid(self): assert len(self._dict) == len(self.inverse._dict) diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py index 8c119172e..ef6da5c4c 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py @@ -13,7 +13,7 @@ class DoubleDict(abstract._UnorderedDictDelegator, - abstract._AbstractMutableDoubleDict): + abstract._AbstractMutableDoubleDict): ''' blocktododoc''' @@ -31,14 +31,14 @@ class FrozenDict(abstract._UnorderedDictDelegator, class DoubleFrozenDict(abstract._UnorderedDictDelegator, - abstract._AbstractDoubleDict, - abstract._AbstractFrozenDict): + abstract._AbstractDoubleDict, + abstract._AbstractFrozenDict): '''blocktododoc''' class DoubleOrderedDict(abstract._OrderedDictDelegator, - abstract._AbstractMutableDoubleDict, - abstract._AbstractFrozenDict): + abstract._AbstractMutableDoubleDict, + abstract._AbstractFrozenDict): '''blocktododoc''' @@ -102,8 +102,8 @@ def __repr__(self): class DoubleFrozenOrderedDict(abstract._OrderedDictDelegator, - abstract._AbstractDoubleDict, - abstract._AbstractFrozenDict): + abstract._AbstractDoubleDict, + abstract._AbstractFrozenDict): '''blocktododoc''' From ce15dcfb62d198efb9ad42d7cf6eb693edd05680 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:15:01 +0300 Subject: [PATCH 071/112] - --- .../nifty_collections/nifty_dicts/abstract.py | 8 +++++-- .../nifty_dicts/nifty_dicts.py | 4 ++-- .../test_nifty_dicts/__init__.py | 21 +++++++++++++++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index 08622db6c..f33823b95 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -60,7 +60,11 @@ def __hash__(self): return self._hash -class _AbstractDoubleDict(_AbstractMappingDelegator): +class BaseDoubleDict(_AbstractMappingDelegator): + # This has a different name, and we're exposing it too, so people could do + # `isinstance(d, BaseDoubleDict)` and get `True` if it's either a + # `DoubleDict`, `DoubleFrozenDict` or `DoubleFrozenOrderedDict` or + # `DoubleFrozenOrderedDict`. def __init__(self, *args, **kwargs): if hasattr(self, '_dict'): assert self.inverse.inverse is self @@ -78,7 +82,7 @@ def __init__(self, *args, **kwargs): self.inverse.__init__() -class _AbstractMutableDoubleDict(_AbstractDoubleDict, +class _AbstractMutableDoubleDict(BaseDoubleDict, collections.abc.MutableMapping): def _assert_valid(self): diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py index ef6da5c4c..c75b679ab 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py @@ -31,7 +31,7 @@ class FrozenDict(abstract._UnorderedDictDelegator, class DoubleFrozenDict(abstract._UnorderedDictDelegator, - abstract._AbstractDoubleDict, + abstract.BaseDoubleDict, abstract._AbstractFrozenDict): '''blocktododoc''' @@ -102,7 +102,7 @@ def __repr__(self): class DoubleFrozenOrderedDict(abstract._OrderedDictDelegator, - abstract._AbstractDoubleDict, + abstract.BaseDoubleDict, abstract._AbstractFrozenDict): '''blocktododoc''' diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py index 32600449a..1f1b70136 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py @@ -16,7 +16,7 @@ ) -class BaseDictTestCase(cute_testing.TestCase): +class _AbstractDictTestCase(cute_testing.TestCase): __test__ = False d_type = None # Filled in by subclasses @@ -40,5 +40,22 @@ def test_common(self): assert d.get(1, 'whatever') == 2 assert d.get(10, 'whatever') == 'whatever' + assert d == d.copy() == d.copy() -class (cute_testing.TestCase): + +class _AbstractDoubleDictTestCase(_AbstractDictTestCase): + pass + + +def test_base_double_dict(): + from nifty_collections.nifty_dicts.abstract import BaseDoubleDict + assert isinstance(DoubleDict(), BaseDoubleDict) + assert isinstance(DoubleFrozenDict(), BaseDoubleDict) + assert isinstance(DoubleOrderedDict(), BaseDoubleDict) + assert isinstance(DoubleFrozenOrderedDict(), BaseDoubleDict) + assert not isinstance({}, BaseDoubleDict) + assert not isinstance(OrderedDict(), BaseDoubleDict) + assert not isinstance(FrozenDict(), BaseDoubleDict) + assert not isinstance(FrozenOrderedDict(), BaseDoubleDict) + assert not isinstance(["haha I'm not even related"], BaseDoubleDict) + \ No newline at end of file From 37613e09562c6535958b1d093b3681e60a05e4b2 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:21:59 +0300 Subject: [PATCH 072/112] - --- .../nifty_collections/abstract.py | 6 +++ .../test_nifty_dicts/__init__.py | 50 +++++++++++++------ .../test_nifty_dicts/test_general.py | 32 ++++++++++++ 3 files changed, 73 insertions(+), 15 deletions(-) create mode 100644 source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_general.py diff --git a/source_py3/python_toolbox/nifty_collections/abstract.py b/source_py3/python_toolbox/nifty_collections/abstract.py index 56c8ac46c..fdaefba2a 100644 --- a/source_py3/python_toolbox/nifty_collections/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/abstract.py @@ -28,6 +28,12 @@ class Ordered(metaclass=abc.ABCMeta): ############################################################################### +class OrderedMapping(Ordered, collections.abc.Mapping): + '''blocktododoc''' + __slots__ = () + +############################################################################### + class DefinitelyUnordered(metaclass=abc.ABCMeta): ''' A data structure that does not have a defined order. diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py index 1f1b70136..a878f73f8 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +import collections.abc + from python_toolbox.third_party import unittest2 import nose @@ -20,6 +22,9 @@ class _AbstractDictTestCase(cute_testing.TestCase): __test__ = False d_type = None # Filled in by subclasses + def test_mapping_base_class(self): + assert issubclass(self.d_type, collections.Mapping) + def test_common(self): d = self.d_type(((1, 2), (3, 4), (5, 6))) assert len(d) == 3 @@ -44,18 +49,33 @@ def test_common(self): class _AbstractDoubleDictTestCase(_AbstractDictTestCase): - pass - - -def test_base_double_dict(): - from nifty_collections.nifty_dicts.abstract import BaseDoubleDict - assert isinstance(DoubleDict(), BaseDoubleDict) - assert isinstance(DoubleFrozenDict(), BaseDoubleDict) - assert isinstance(DoubleOrderedDict(), BaseDoubleDict) - assert isinstance(DoubleFrozenOrderedDict(), BaseDoubleDict) - assert not isinstance({}, BaseDoubleDict) - assert not isinstance(OrderedDict(), BaseDoubleDict) - assert not isinstance(FrozenDict(), BaseDoubleDict) - assert not isinstance(FrozenOrderedDict(), BaseDoubleDict) - assert not isinstance(["haha I'm not even related"], BaseDoubleDict) - \ No newline at end of file + def test_double_dict_base_class(self): + assert issubclass( + self.d_type, + nifty_collections.nifty_dicts.abstract.BaseDoubleDict + ) + +class _AbstractFrozenDictTestCase(_AbstractDictTestCase): + def test_frozen_dict_base_class(self): + assert issubclass( + self.d_type, + collections.abc.Hashable + ) + assert not issubclass( + self.d_type, + collections.abc.MutableMapping + ) +class _AbstractOrderedDictTestCase(_AbstractDictTestCase): + def test_ordered_dict_base_class(self): + assert issubclass( + self.d_type, + nifty_collections.abstract.Ordered + ) + assert issubclass( + self.d_type, + nifty_collections.abstract.OrderedMapping + ) + assert not issubclass( + self.d_type, + nifty_collections.abstract.DefinitelyUnordered + ) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_general.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_general.py new file mode 100644 index 000000000..6ff63d3ea --- /dev/null +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_general.py @@ -0,0 +1,32 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import collections.abc + +from python_toolbox.third_party import unittest2 + +import nose + +from python_toolbox import cute_iter_tools +from python_toolbox import cute_testing + +from python_toolbox import nifty_collections +from python_toolbox.nifty_collections import ( + DoubleDict, FrozenDict, OrderedDict, + DoubleFrozenDict, DoubleOrderedDict, + FrozenOrderedDict, DoubleFrozenOrderedDict +) + + +def test_base_double_dict(): + from nifty_collections.nifty_dicts.abstract import BaseDoubleDict + assert isinstance(DoubleDict(), BaseDoubleDict) + assert isinstance(DoubleFrozenDict(), BaseDoubleDict) + assert isinstance(DoubleOrderedDict(), BaseDoubleDict) + assert isinstance(DoubleFrozenOrderedDict(), BaseDoubleDict) + assert not isinstance({}, BaseDoubleDict) + assert not isinstance(OrderedDict(), BaseDoubleDict) + assert not isinstance(FrozenDict(), BaseDoubleDict) + assert not isinstance(FrozenOrderedDict(), BaseDoubleDict) + assert not isinstance(["haha I'm not even related"], BaseDoubleDict) + \ No newline at end of file From 9a666affc4d6d5ae0157e39d8c8e3b7d1653b367 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:25:17 +0300 Subject: [PATCH 073/112] - --- ...__init__.py => abstract_dict_test_case.py} | 31 ------- .../abstract_one_base_test_cases.py | 87 +++++++++++++++++++ .../abstract_two_base_test_cases.py | 87 +++++++++++++++++++ 3 files changed, 174 insertions(+), 31 deletions(-) rename source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/{__init__.py => abstract_dict_test_case.py} (59%) create mode 100644 source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py create mode 100644 source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py similarity index 59% rename from source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py rename to source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py index a878f73f8..1548c215d 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/__init__.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py @@ -48,34 +48,3 @@ def test_common(self): assert d == d.copy() == d.copy() -class _AbstractDoubleDictTestCase(_AbstractDictTestCase): - def test_double_dict_base_class(self): - assert issubclass( - self.d_type, - nifty_collections.nifty_dicts.abstract.BaseDoubleDict - ) - -class _AbstractFrozenDictTestCase(_AbstractDictTestCase): - def test_frozen_dict_base_class(self): - assert issubclass( - self.d_type, - collections.abc.Hashable - ) - assert not issubclass( - self.d_type, - collections.abc.MutableMapping - ) -class _AbstractOrderedDictTestCase(_AbstractDictTestCase): - def test_ordered_dict_base_class(self): - assert issubclass( - self.d_type, - nifty_collections.abstract.Ordered - ) - assert issubclass( - self.d_type, - nifty_collections.abstract.OrderedMapping - ) - assert not issubclass( - self.d_type, - nifty_collections.abstract.DefinitelyUnordered - ) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py new file mode 100644 index 000000000..c0324b7c8 --- /dev/null +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -0,0 +1,87 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import collections.abc + +from python_toolbox.third_party import unittest2 + +import nose + +from python_toolbox import cute_iter_tools +from python_toolbox import cute_testing + +from python_toolbox import nifty_collections +from python_toolbox.nifty_collections import ( + DoubleDict, FrozenDict, OrderedDict, + DoubleFrozenDict, DoubleOrderedDict, + FrozenOrderedDict, DoubleFrozenOrderedDict +) + +from .abstract_dict_test_case import _AbstractDictTestCase + + +class _AbstractDoubleDictTestCase(_AbstractDictTestCase): + def test_double_dict_base_class(self): + assert issubclass( + self.d_type, + nifty_collections.nifty_dicts.abstract.BaseDoubleDict + ) + +class _AbstractFrozenDictTestCase(_AbstractDictTestCase): + def test_frozen_dict_base_class(self): + assert issubclass( + self.d_type, + collections.abc.Hashable + ) + assert not issubclass( + self.d_type, + collections.abc.MutableMapping + ) + +class _AbstractOrderedDictTestCase(_AbstractDictTestCase): + def test_ordered_dict_base_class(self): + assert issubclass( + self.d_type, + nifty_collections.abstract.Ordered + ) + assert issubclass( + self.d_type, + nifty_collections.abstract.OrderedMapping + ) + assert not issubclass( + self.d_type, + nifty_collections.abstract.DefinitelyUnordered + ) + +class _AbstractNotDoubleDictTestCase(_AbstractDictTestCase): + def test_not_double_dict_base_class(self): + assert not issubclass( + self.d_type, + nifty_collections.nifty_dicts.abstract.BaseDoubleDict + ) + +class _AbstractNotFrozenDictTestCase(_AbstractDictTestCase): + def test_not_frozen_dict_base_class(self): + assert not issubclass( + self.d_type, + collections.abc.Hashable + ) + assert issubclass( + self.d_type, + collections.abc.MutableMapping + ) + +class _AbstractNotOrderedDictTestCase(_AbstractDictTestCase): + def test_not_ordered_dict_base_class(self): + assert not issubclass( + self.d_type, + nifty_collections.abstract.Ordered + ) + assert not issubclass( + self.d_type, + nifty_collections.abstract.OrderedMapping + ) + assert issubclass( + self.d_type, + nifty_collections.abstract.DefinitelyUnordered + ) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py new file mode 100644 index 000000000..f0e824f07 --- /dev/null +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -0,0 +1,87 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import collections.abc + +from python_toolbox.third_party import unittest2 + +import nose + +from python_toolbox import cute_iter_tools +from python_toolbox import cute_testing + +from python_toolbox import nifty_collections +from python_toolbox.nifty_collections import ( + DoubleDict, FrozenDict, OrderedDict, + DoubleFrozenDict, DoubleOrderedDict, + FrozenOrderedDict, DoubleFrozenOrderedDict +) + +from .abstract_one_test_case import * + + +class _AbstractDoubleDictTestCase(_AbstractDictTestCase): + def test_double_dict_base_class(self): + assert issubclass( + self.d_type, + nifty_collections.nifty_dicts.abstract.BaseDoubleDict + ) + +class _AbstractFrozenDictTestCase(_AbstractDictTestCase): + def test_frozen_dict_base_class(self): + assert issubclass( + self.d_type, + collections.abc.Hashable + ) + assert not issubclass( + self.d_type, + collections.abc.MutableMapping + ) + +class _AbstractOrderedDictTestCase(_AbstractDictTestCase): + def test_ordered_dict_base_class(self): + assert issubclass( + self.d_type, + nifty_collections.abstract.Ordered + ) + assert issubclass( + self.d_type, + nifty_collections.abstract.OrderedMapping + ) + assert not issubclass( + self.d_type, + nifty_collections.abstract.DefinitelyUnordered + ) + +class _AbstractNotDoubleDictTestCase(_AbstractDictTestCase): + def test_not_double_dict_base_class(self): + assert not issubclass( + self.d_type, + nifty_collections.nifty_dicts.abstract.BaseDoubleDict + ) + +class _AbstractNotFrozenDictTestCase(_AbstractDictTestCase): + def test_not_frozen_dict_base_class(self): + assert not issubclass( + self.d_type, + collections.abc.Hashable + ) + assert issubclass( + self.d_type, + collections.abc.MutableMapping + ) + +class _AbstractNotOrderedDictTestCase(_AbstractDictTestCase): + def test_not_ordered_dict_base_class(self): + assert not issubclass( + self.d_type, + nifty_collections.abstract.Ordered + ) + assert not issubclass( + self.d_type, + nifty_collections.abstract.OrderedMapping + ) + assert issubclass( + self.d_type, + nifty_collections.abstract.DefinitelyUnordered + ) From 650fca4a2156d4627815b96d11c6c0630601d440 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:38:38 +0300 Subject: [PATCH 074/112] - --- .../abstract_two_base_test_cases.py | 129 +++++++++--------- .../test_nifty_dicts/test_nifty_dicts.py | 51 +++++++ 2 files changed, 112 insertions(+), 68 deletions(-) create mode 100644 source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index f0e824f07..21def0d79 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -17,71 +17,64 @@ FrozenOrderedDict, DoubleFrozenOrderedDict ) -from .abstract_one_test_case import * - - -class _AbstractDoubleDictTestCase(_AbstractDictTestCase): - def test_double_dict_base_class(self): - assert issubclass( - self.d_type, - nifty_collections.nifty_dicts.abstract.BaseDoubleDict - ) - -class _AbstractFrozenDictTestCase(_AbstractDictTestCase): - def test_frozen_dict_base_class(self): - assert issubclass( - self.d_type, - collections.abc.Hashable - ) - assert not issubclass( - self.d_type, - collections.abc.MutableMapping - ) - -class _AbstractOrderedDictTestCase(_AbstractDictTestCase): - def test_ordered_dict_base_class(self): - assert issubclass( - self.d_type, - nifty_collections.abstract.Ordered - ) - assert issubclass( - self.d_type, - nifty_collections.abstract.OrderedMapping - ) - assert not issubclass( - self.d_type, - nifty_collections.abstract.DefinitelyUnordered - ) - -class _AbstractNotDoubleDictTestCase(_AbstractDictTestCase): - def test_not_double_dict_base_class(self): - assert not issubclass( - self.d_type, - nifty_collections.nifty_dicts.abstract.BaseDoubleDict - ) - -class _AbstractNotFrozenDictTestCase(_AbstractDictTestCase): - def test_not_frozen_dict_base_class(self): - assert not issubclass( - self.d_type, - collections.abc.Hashable - ) - assert issubclass( - self.d_type, - collections.abc.MutableMapping - ) - -class _AbstractNotOrderedDictTestCase(_AbstractDictTestCase): - def test_not_ordered_dict_base_class(self): - assert not issubclass( - self.d_type, - nifty_collections.abstract.Ordered - ) - assert not issubclass( - self.d_type, - nifty_collections.abstract.OrderedMapping - ) - assert issubclass( - self.d_type, - nifty_collections.abstract.DefinitelyUnordered - ) +from .abstract_one_test_cases import * + + +class _AbstractDoubleFrozenDictTestCase(_AbstractDoubleDictTestCase, + _AbstractFrozenDictTestCase): + pass + + +class _AbstractDoubleOrderedDictTestCase(_AbstractDoubleDictTestCase, + _AbstractOrderedDictTestCase): + pass + +class _AbstractDoubleNotFrozenDictTestCase(_AbstractDoubleDictTestCase, + _AbstractNotFrozenDictTestCase): + pass + +class _AbstractDoubleNotOrderedDictTestCase(_AbstractDoubleDictTestCase, + _AbstractNotOrderedDictTestCase): + pass + + +class _AbstractFrozenOrderedDictTestCase(_AbstractFrozenDictTestCase, + _AbstractOrderedDictTestCase): + pass + + +class _AbstractFrozenNotDoubleDictTestCase(_AbstractFrozenDictTestCase, + _AbstractNotDoubleDictTestCase): + pass + + +class _AbstractFrozenNotOrderedDictTestCase(_AbstractFrozenDictTestCase, + _AbstractNotOrderedDictTestCase): + pass + + +class _AbstractOrderedNotDoubleDictTestCase(_AbstractOrderedDictTestCase, + _AbstractNotDoubleDictTestCase): + pass + + +class _AbstractOrderedNotFrozenDictTestCase(_AbstractOrderedDictTestCase, + _AbstractNotFrozenDictTestCase): + pass + + +class _AbstractNotDoubleNotFrozenDictTestCase(_AbstractNotDoubleDictTestCase, + _AbstractNotFrozenDictTestCase): + pass + + +class _AbstractNotDoubleNotOrderedDictTestCase(_AbstractNotDoubleDictTestCase, + _AbstractNotOrderedDictTestCase): + pass + + +class _AbstractNotFrozenNotOrderedDictTestCase(_AbstractNotFrozenDictTestCase, + _AbstractNotOrderedDictTestCase): + pass + + diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py new file mode 100644 index 000000000..11f378db7 --- /dev/null +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py @@ -0,0 +1,51 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +import collections.abc + +from python_toolbox.third_party import unittest2 + +import nose + +from python_toolbox import cute_iter_tools +from python_toolbox import cute_testing + +from python_toolbox import nifty_collections +from python_toolbox.nifty_collections import ( + DoubleDict, FrozenDict, OrderedDict, + DoubleFrozenDict, DoubleOrderedDict, + FrozenOrderedDict, DoubleFrozenOrderedDict +) + +from .abstract_two_test_cases import * + + +class DoubleDictTestCase(_AbstractDoubleNotFrozenDictTestCase, + _AbstractDoubleNotOrderedDictTestCase, + _AbstractNotFrozenNotOrderedDictTestCase): + d_type = DoubleDict + + +class FrozenDictTestCase(_AbstractFrozenNotDoubleDictTestCase, + _AbstractFrozenNotOrderedDictTestCase, + _AbstractNotDoubleNotOrderedDictTestCase): + d_type = FrozenDict + + +class OrderedDictTestCase(_AbstractOrderedNotDoubleDictTestCase, + _AbstractOrderedNotFrozenDictTestCase, + _AbstractNotDoubleNotFrozenDictTestCase): + d_type = OrderedDict + + +class DoubleFrozenDictTestCase(_AbstractDoubleFrozenDictTestCase, + _AbstractDoubleNotOrderedDictTestCase, + _AbstractFrozenNotOrderedDictTestCase): + d_type = DoubleFrozenDict + + +class DoubleOrderedDictTestCase(_AbstractDoubleFrozenDictTestCase, + _AbstractDoubleNotOrderedDictTestCase, + _AbstractFrozenNotOrderedDictTestCase): + d_type = DoubleOrderedDict + \ No newline at end of file From 00a14bbea357b99ee99ddaa154a508d422f7bb0b Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:41:17 +0300 Subject: [PATCH 075/112] - --- .../test_nifty_dicts/test_nifty_dicts.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py index 11f378db7..6fb396043 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py @@ -44,8 +44,19 @@ class DoubleFrozenDictTestCase(_AbstractDoubleFrozenDictTestCase, d_type = DoubleFrozenDict -class DoubleOrderedDictTestCase(_AbstractDoubleFrozenDictTestCase, - _AbstractDoubleNotOrderedDictTestCase, - _AbstractFrozenNotOrderedDictTestCase): +class DoubleOrderedDictTestCase(_AbstractDoubleOrderedDictTestCase, + _AbstractDoubleNotFrozenDictTestCase, + _AbstractOrderedNotFrozenDictTestCase): d_type = DoubleOrderedDict + + +class FrozenOrderedDictTestCase(_AbstractFrozenOrderedDictTestCase, + _AbstractFrozenNotDoubleDictTestCase, + _AbstractOrderedNotDoubleDictTestCase): + d_type = FrozenOrderedDict + +class DoubleFrozenOrderedDictTestCase(_AbstractDoubleFrozenDictTestCase, + _AbstractDoubleOrderedDictTestCase, + _AbstractFrozenOrderedDictTestCase): + d_type = DoubleFrozenOrderedDict \ No newline at end of file From a0d89948cd51fbdbbba31f6b94c5c4599f8b0209 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:45:55 +0300 Subject: [PATCH 076/112] - --- .../abstract_one_base_test_cases.py | 47 +++++++++++------- .../abstract_two_base_test_cases.py | 48 +++++++++++-------- .../test_nifty_dicts/test_nifty_dicts.py | 12 ++--- 3 files changed, 64 insertions(+), 43 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index c0324b7c8..28c7fd73d 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -27,6 +27,18 @@ def test_double_dict_base_class(self): nifty_collections.nifty_dicts.abstract.BaseDoubleDict ) + +class _AbstractNotDoubleDictTestCase(_AbstractDictTestCase): + def test_not_double_dict_base_class(self): + assert not issubclass( + self.d_type, + nifty_collections.nifty_dicts.abstract.BaseDoubleDict + ) + + +############################################################################### + + class _AbstractFrozenDictTestCase(_AbstractDictTestCase): def test_frozen_dict_base_class(self): assert issubclass( @@ -37,7 +49,23 @@ def test_frozen_dict_base_class(self): self.d_type, collections.abc.MutableMapping ) + + +class _AbstractNotFrozenDictTestCase(_AbstractDictTestCase): + def test_not_frozen_dict_base_class(self): + assert not issubclass( + self.d_type, + collections.abc.Hashable + ) + assert issubclass( + self.d_type, + collections.abc.MutableMapping + ) + +############################################################################### + + class _AbstractOrderedDictTestCase(_AbstractDictTestCase): def test_ordered_dict_base_class(self): assert issubclass( @@ -53,24 +81,7 @@ def test_ordered_dict_base_class(self): nifty_collections.abstract.DefinitelyUnordered ) -class _AbstractNotDoubleDictTestCase(_AbstractDictTestCase): - def test_not_double_dict_base_class(self): - assert not issubclass( - self.d_type, - nifty_collections.nifty_dicts.abstract.BaseDoubleDict - ) - -class _AbstractNotFrozenDictTestCase(_AbstractDictTestCase): - def test_not_frozen_dict_base_class(self): - assert not issubclass( - self.d_type, - collections.abc.Hashable - ) - assert issubclass( - self.d_type, - collections.abc.MutableMapping - ) - + class _AbstractNotOrderedDictTestCase(_AbstractDictTestCase): def test_not_ordered_dict_base_class(self): assert not issubclass( diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index 21def0d79..a87cf8816 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -20,56 +20,66 @@ from .abstract_one_test_cases import * +############################################################################### + + class _AbstractDoubleFrozenDictTestCase(_AbstractDoubleDictTestCase, _AbstractFrozenDictTestCase): pass -class _AbstractDoubleOrderedDictTestCase(_AbstractDoubleDictTestCase, - _AbstractOrderedDictTestCase): - pass - class _AbstractDoubleNotFrozenDictTestCase(_AbstractDoubleDictTestCase, _AbstractNotFrozenDictTestCase): pass -class _AbstractDoubleNotOrderedDictTestCase(_AbstractDoubleDictTestCase, - _AbstractNotOrderedDictTestCase): + +class _AbstractNotDoubleFrozenDictTestCase(_AbstractFrozenDictTestCase, + _AbstractNotDoubleDictTestCase): pass -class _AbstractFrozenOrderedDictTestCase(_AbstractFrozenDictTestCase, - _AbstractOrderedDictTestCase): +class _AbstractNotDoubleNotFrozenDictTestCase(_AbstractNotDoubleDictTestCase, + _AbstractNotFrozenDictTestCase): pass -class _AbstractFrozenNotDoubleDictTestCase(_AbstractFrozenDictTestCase, - _AbstractNotDoubleDictTestCase): - pass +############################################################################### -class _AbstractFrozenNotOrderedDictTestCase(_AbstractFrozenDictTestCase, +class _AbstractDoubleOrderedDictTestCase(_AbstractDoubleDictTestCase, + _AbstractOrderedDictTestCase): + pass + +class _AbstractDoubleNotOrderedDictTestCase(_AbstractDoubleDictTestCase, _AbstractNotOrderedDictTestCase): pass -class _AbstractOrderedNotDoubleDictTestCase(_AbstractOrderedDictTestCase, +class _AbstractNotDoubleOrderedDictTestCase(_AbstractOrderedDictTestCase, _AbstractNotDoubleDictTestCase): pass -class _AbstractOrderedNotFrozenDictTestCase(_AbstractOrderedDictTestCase, - _AbstractNotFrozenDictTestCase): +class _AbstractNotDoubleNotOrderedDictTestCase(_AbstractNotDoubleDictTestCase, + _AbstractNotOrderedDictTestCase): pass -class _AbstractNotDoubleNotFrozenDictTestCase(_AbstractNotDoubleDictTestCase, - _AbstractNotFrozenDictTestCase): +############################################################################### + + +class _AbstractFrozenOrderedDictTestCase(_AbstractFrozenDictTestCase, + _AbstractOrderedDictTestCase): pass -class _AbstractNotDoubleNotOrderedDictTestCase(_AbstractNotDoubleDictTestCase, - _AbstractNotOrderedDictTestCase): +class _AbstractFrozenNotOrderedDictTestCase(_AbstractFrozenDictTestCase, + _AbstractNotOrderedDictTestCase): + pass + + +class _AbstractNotFrozenOrderedDictTestCase(_AbstractOrderedDictTestCase, + _AbstractNotFrozenDictTestCase): pass diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py index 6fb396043..23664aa0a 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py @@ -26,14 +26,14 @@ class DoubleDictTestCase(_AbstractDoubleNotFrozenDictTestCase, d_type = DoubleDict -class FrozenDictTestCase(_AbstractFrozenNotDoubleDictTestCase, +class FrozenDictTestCase(_AbstractNotDoubleFrozenDictTestCase, _AbstractFrozenNotOrderedDictTestCase, _AbstractNotDoubleNotOrderedDictTestCase): d_type = FrozenDict -class OrderedDictTestCase(_AbstractOrderedNotDoubleDictTestCase, - _AbstractOrderedNotFrozenDictTestCase, +class OrderedDictTestCase(_AbstractNotDoubleOrderedDictTestCase, + _AbstractNotFrozenOrderedDictTestCase, _AbstractNotDoubleNotFrozenDictTestCase): d_type = OrderedDict @@ -46,13 +46,13 @@ class DoubleFrozenDictTestCase(_AbstractDoubleFrozenDictTestCase, class DoubleOrderedDictTestCase(_AbstractDoubleOrderedDictTestCase, _AbstractDoubleNotFrozenDictTestCase, - _AbstractOrderedNotFrozenDictTestCase): + _AbstractNotFrozenOrderedDictTestCase): d_type = DoubleOrderedDict class FrozenOrderedDictTestCase(_AbstractFrozenOrderedDictTestCase, - _AbstractFrozenNotDoubleDictTestCase, - _AbstractOrderedNotDoubleDictTestCase): + _AbstractNotDoubleFrozenDictTestCase, + _AbstractNotDoubleOrderedDictTestCase): d_type = FrozenOrderedDict class DoubleFrozenOrderedDictTestCase(_AbstractDoubleFrozenDictTestCase, From 6e62a50fc28a214bdf6d01331129e21f8a351312 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:46:52 +0300 Subject: [PATCH 077/112] - --- source_py3/python_toolbox/nifty_collections/bagging.py | 2 +- .../test_nifty_dicts/test_nifty_dicts.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/source_py3/python_toolbox/nifty_collections/bagging.py b/source_py3/python_toolbox/nifty_collections/bagging.py index 2cded1779..26191d24d 100644 --- a/source_py3/python_toolbox/nifty_collections/bagging.py +++ b/source_py3/python_toolbox/nifty_collections/bagging.py @@ -13,7 +13,7 @@ from python_toolbox import math_tools from .lazy_tuple import LazyTuple -from .ordered_dict import OrderedDict +from .nifty_dicts import OrderedDict from .various_ordered_sets import FrozenOrderedSet from .various_frozen_dicts import FrozenDict, FrozenOrderedDict from .abstract import Ordered, DefinitelyUnordered diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py index 23664aa0a..6bc257de8 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py @@ -23,40 +23,47 @@ class DoubleDictTestCase(_AbstractDoubleNotFrozenDictTestCase, _AbstractDoubleNotOrderedDictTestCase, _AbstractNotFrozenNotOrderedDictTestCase): + __test__ = True d_type = DoubleDict class FrozenDictTestCase(_AbstractNotDoubleFrozenDictTestCase, _AbstractFrozenNotOrderedDictTestCase, _AbstractNotDoubleNotOrderedDictTestCase): + __test__ = True d_type = FrozenDict class OrderedDictTestCase(_AbstractNotDoubleOrderedDictTestCase, _AbstractNotFrozenOrderedDictTestCase, _AbstractNotDoubleNotFrozenDictTestCase): + __test__ = True d_type = OrderedDict class DoubleFrozenDictTestCase(_AbstractDoubleFrozenDictTestCase, _AbstractDoubleNotOrderedDictTestCase, _AbstractFrozenNotOrderedDictTestCase): + __test__ = True d_type = DoubleFrozenDict class DoubleOrderedDictTestCase(_AbstractDoubleOrderedDictTestCase, _AbstractDoubleNotFrozenDictTestCase, _AbstractNotFrozenOrderedDictTestCase): + __test__ = True d_type = DoubleOrderedDict class FrozenOrderedDictTestCase(_AbstractFrozenOrderedDictTestCase, _AbstractNotDoubleFrozenDictTestCase, _AbstractNotDoubleOrderedDictTestCase): + __test__ = True d_type = FrozenOrderedDict class DoubleFrozenOrderedDictTestCase(_AbstractDoubleFrozenDictTestCase, _AbstractDoubleOrderedDictTestCase, _AbstractFrozenOrderedDictTestCase): + __test__ = True d_type = DoubleFrozenOrderedDict \ No newline at end of file From 79d7de0faa5dd068199db245a368b6fc97dcd2b7 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:49:43 +0300 Subject: [PATCH 078/112] - --- .../nifty_collections/nifty_dicts/nifty_dicts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py index c75b679ab..ada6b0884 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py @@ -58,7 +58,8 @@ def move_to_end(self, key, last=True): self._assert_valid() -class FrozenOrderedDict(Ordered, _AbstractFrozenDict): +class FrozenOrderedDict(abstract._OrderedDictDelegator, + abstract._AbstractFrozenDict): ''' An immutable, ordered `dict`. From 3d2ae0df9cad4336017f888c1aad8ff94d6b7435 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:51:53 +0300 Subject: [PATCH 079/112] - --- source_py3/python_toolbox/nifty_collections/bagging.py | 3 +-- .../nifty_collections/nifty_dicts/nifty_dicts.py | 2 +- .../test_nifty_dicts/abstract_one_base_test_cases.py | 2 +- .../test_nifty_dicts/abstract_two_base_test_cases.py | 2 +- .../test_nifty_dicts/test_nifty_dicts.py | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/bagging.py b/source_py3/python_toolbox/nifty_collections/bagging.py index 26191d24d..c66f9e642 100644 --- a/source_py3/python_toolbox/nifty_collections/bagging.py +++ b/source_py3/python_toolbox/nifty_collections/bagging.py @@ -13,9 +13,8 @@ from python_toolbox import math_tools from .lazy_tuple import LazyTuple -from .nifty_dicts import OrderedDict from .various_ordered_sets import FrozenOrderedSet -from .various_frozen_dicts import FrozenDict, FrozenOrderedDict +from .nifty_dicts import FrozenDict, FrozenOrderedDict, OrderedDict from .abstract import Ordered, DefinitelyUnordered diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py index ada6b0884..a49050526 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py @@ -75,7 +75,7 @@ def __eq__(self, other): all(map(operator.eq, self, other)) return collections.abc.Mapping.__eq__(self, other) - __hash__ = _AbstractFrozenDict.__hash__ + __hash__ = abstract._AbstractFrozenDict.__hash__ # (Gotta manually carry `__hash__` over from the base class because setting # `__eq__` resets it. ) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index 28c7fd73d..ca89d9180 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -17,7 +17,7 @@ FrozenOrderedDict, DoubleFrozenOrderedDict ) -from .abstract_dict_test_case import _AbstractDictTestCase +from abstract_dict_test_case import _AbstractDictTestCase class _AbstractDoubleDictTestCase(_AbstractDictTestCase): diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index a87cf8816..ed215513e 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -17,7 +17,7 @@ FrozenOrderedDict, DoubleFrozenOrderedDict ) -from .abstract_one_test_cases import * +from abstract_one_base_test_cases import * ############################################################################### diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py index 6bc257de8..53c5e1664 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py @@ -17,7 +17,7 @@ FrozenOrderedDict, DoubleFrozenOrderedDict ) -from .abstract_two_test_cases import * +from abstract_two_base_test_cases import * class DoubleDictTestCase(_AbstractDoubleNotFrozenDictTestCase, From 1c732e9ef792f1d396a79e9c7ac96b72384222bc Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:52:15 +0300 Subject: [PATCH 080/112] - --- .../abstract_dict_test_case.py | 2 +- .../abstract_one_base_test_cases.py | 14 +++--- .../abstract_two_base_test_cases.py | 48 +++++++++---------- .../test_nifty_dicts/test_nifty_dicts.py | 42 ++++++++-------- 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py index 1548c215d..fc1859244 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py @@ -18,7 +18,7 @@ ) -class _AbstractDictTestCase(cute_testing.TestCase): +class AbstractDictTestCase(cute_testing.TestCase): __test__ = False d_type = None # Filled in by subclasses diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index ca89d9180..d52c7893d 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -17,10 +17,10 @@ FrozenOrderedDict, DoubleFrozenOrderedDict ) -from abstract_dict_test_case import _AbstractDictTestCase +from abstract_dict_test_case import AbstractDictTestCase -class _AbstractDoubleDictTestCase(_AbstractDictTestCase): +class AbstractDoubleDictTestCase(AbstractDictTestCase): def test_double_dict_base_class(self): assert issubclass( self.d_type, @@ -28,7 +28,7 @@ def test_double_dict_base_class(self): ) -class _AbstractNotDoubleDictTestCase(_AbstractDictTestCase): +class AbstractNotDoubleDictTestCase(AbstractDictTestCase): def test_not_double_dict_base_class(self): assert not issubclass( self.d_type, @@ -39,7 +39,7 @@ def test_not_double_dict_base_class(self): ############################################################################### -class _AbstractFrozenDictTestCase(_AbstractDictTestCase): +class AbstractFrozenDictTestCase(AbstractDictTestCase): def test_frozen_dict_base_class(self): assert issubclass( self.d_type, @@ -51,7 +51,7 @@ def test_frozen_dict_base_class(self): ) -class _AbstractNotFrozenDictTestCase(_AbstractDictTestCase): +class AbstractNotFrozenDictTestCase(AbstractDictTestCase): def test_not_frozen_dict_base_class(self): assert not issubclass( self.d_type, @@ -66,7 +66,7 @@ def test_not_frozen_dict_base_class(self): ############################################################################### -class _AbstractOrderedDictTestCase(_AbstractDictTestCase): +class AbstractOrderedDictTestCase(AbstractDictTestCase): def test_ordered_dict_base_class(self): assert issubclass( self.d_type, @@ -82,7 +82,7 @@ def test_ordered_dict_base_class(self): ) -class _AbstractNotOrderedDictTestCase(_AbstractDictTestCase): +class AbstractNotOrderedDictTestCase(AbstractDictTestCase): def test_not_ordered_dict_base_class(self): assert not issubclass( self.d_type, diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index ed215513e..c4d8a1dfb 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -23,68 +23,68 @@ ############################################################################### -class _AbstractDoubleFrozenDictTestCase(_AbstractDoubleDictTestCase, - _AbstractFrozenDictTestCase): +class AbstractDoubleFrozenDictTestCase(AbstractDoubleDictTestCase, + AbstractFrozenDictTestCase): pass -class _AbstractDoubleNotFrozenDictTestCase(_AbstractDoubleDictTestCase, - _AbstractNotFrozenDictTestCase): +class AbstractDoubleNotFrozenDictTestCase(AbstractDoubleDictTestCase, + AbstractNotFrozenDictTestCase): pass -class _AbstractNotDoubleFrozenDictTestCase(_AbstractFrozenDictTestCase, - _AbstractNotDoubleDictTestCase): +class AbstractNotDoubleFrozenDictTestCase(AbstractFrozenDictTestCase, + AbstractNotDoubleDictTestCase): pass -class _AbstractNotDoubleNotFrozenDictTestCase(_AbstractNotDoubleDictTestCase, - _AbstractNotFrozenDictTestCase): +class AbstractNotDoubleNotFrozenDictTestCase(AbstractNotDoubleDictTestCase, + AbstractNotFrozenDictTestCase): pass ############################################################################### -class _AbstractDoubleOrderedDictTestCase(_AbstractDoubleDictTestCase, - _AbstractOrderedDictTestCase): +class AbstractDoubleOrderedDictTestCase(AbstractDoubleDictTestCase, + AbstractOrderedDictTestCase): pass -class _AbstractDoubleNotOrderedDictTestCase(_AbstractDoubleDictTestCase, - _AbstractNotOrderedDictTestCase): +class AbstractDoubleNotOrderedDictTestCase(AbstractDoubleDictTestCase, + AbstractNotOrderedDictTestCase): pass -class _AbstractNotDoubleOrderedDictTestCase(_AbstractOrderedDictTestCase, - _AbstractNotDoubleDictTestCase): +class AbstractNotDoubleOrderedDictTestCase(AbstractOrderedDictTestCase, + AbstractNotDoubleDictTestCase): pass -class _AbstractNotDoubleNotOrderedDictTestCase(_AbstractNotDoubleDictTestCase, - _AbstractNotOrderedDictTestCase): +class AbstractNotDoubleNotOrderedDictTestCase(AbstractNotDoubleDictTestCase, + AbstractNotOrderedDictTestCase): pass ############################################################################### -class _AbstractFrozenOrderedDictTestCase(_AbstractFrozenDictTestCase, - _AbstractOrderedDictTestCase): +class AbstractFrozenOrderedDictTestCase(AbstractFrozenDictTestCase, + AbstractOrderedDictTestCase): pass -class _AbstractFrozenNotOrderedDictTestCase(_AbstractFrozenDictTestCase, - _AbstractNotOrderedDictTestCase): +class AbstractFrozenNotOrderedDictTestCase(AbstractFrozenDictTestCase, + AbstractNotOrderedDictTestCase): pass -class _AbstractNotFrozenOrderedDictTestCase(_AbstractOrderedDictTestCase, - _AbstractNotFrozenDictTestCase): +class AbstractNotFrozenOrderedDictTestCase(AbstractOrderedDictTestCase, + AbstractNotFrozenDictTestCase): pass -class _AbstractNotFrozenNotOrderedDictTestCase(_AbstractNotFrozenDictTestCase, - _AbstractNotOrderedDictTestCase): +class AbstractNotFrozenNotOrderedDictTestCase(AbstractNotFrozenDictTestCase, + AbstractNotOrderedDictTestCase): pass diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py index 53c5e1664..c253f6c29 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py @@ -20,50 +20,50 @@ from abstract_two_base_test_cases import * -class DoubleDictTestCase(_AbstractDoubleNotFrozenDictTestCase, - _AbstractDoubleNotOrderedDictTestCase, - _AbstractNotFrozenNotOrderedDictTestCase): +class DoubleDictTestCase(AbstractDoubleNotFrozenDictTestCase, + AbstractDoubleNotOrderedDictTestCase, + AbstractNotFrozenNotOrderedDictTestCase): __test__ = True d_type = DoubleDict -class FrozenDictTestCase(_AbstractNotDoubleFrozenDictTestCase, - _AbstractFrozenNotOrderedDictTestCase, - _AbstractNotDoubleNotOrderedDictTestCase): +class FrozenDictTestCase(AbstractNotDoubleFrozenDictTestCase, + AbstractFrozenNotOrderedDictTestCase, + AbstractNotDoubleNotOrderedDictTestCase): __test__ = True d_type = FrozenDict -class OrderedDictTestCase(_AbstractNotDoubleOrderedDictTestCase, - _AbstractNotFrozenOrderedDictTestCase, - _AbstractNotDoubleNotFrozenDictTestCase): +class OrderedDictTestCase(AbstractNotDoubleOrderedDictTestCase, + AbstractNotFrozenOrderedDictTestCase, + AbstractNotDoubleNotFrozenDictTestCase): __test__ = True d_type = OrderedDict -class DoubleFrozenDictTestCase(_AbstractDoubleFrozenDictTestCase, - _AbstractDoubleNotOrderedDictTestCase, - _AbstractFrozenNotOrderedDictTestCase): +class DoubleFrozenDictTestCase(AbstractDoubleFrozenDictTestCase, + AbstractDoubleNotOrderedDictTestCase, + AbstractFrozenNotOrderedDictTestCase): __test__ = True d_type = DoubleFrozenDict -class DoubleOrderedDictTestCase(_AbstractDoubleOrderedDictTestCase, - _AbstractDoubleNotFrozenDictTestCase, - _AbstractNotFrozenOrderedDictTestCase): +class DoubleOrderedDictTestCase(AbstractDoubleOrderedDictTestCase, + AbstractDoubleNotFrozenDictTestCase, + AbstractNotFrozenOrderedDictTestCase): __test__ = True d_type = DoubleOrderedDict -class FrozenOrderedDictTestCase(_AbstractFrozenOrderedDictTestCase, - _AbstractNotDoubleFrozenDictTestCase, - _AbstractNotDoubleOrderedDictTestCase): +class FrozenOrderedDictTestCase(AbstractFrozenOrderedDictTestCase, + AbstractNotDoubleFrozenDictTestCase, + AbstractNotDoubleOrderedDictTestCase): __test__ = True d_type = FrozenOrderedDict -class DoubleFrozenOrderedDictTestCase(_AbstractDoubleFrozenDictTestCase, - _AbstractDoubleOrderedDictTestCase, - _AbstractFrozenOrderedDictTestCase): +class DoubleFrozenOrderedDictTestCase(AbstractDoubleFrozenDictTestCase, + AbstractDoubleOrderedDictTestCase, + AbstractFrozenOrderedDictTestCase): __test__ = True d_type = DoubleFrozenOrderedDict \ No newline at end of file From b7a9c3e55867b9a08e2b992d4bf9d9c3574f3688 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:53:26 +0300 Subject: [PATCH 081/112] - --- .../python_toolbox/nifty_collections/nifty_dicts/abstract.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index f33823b95..1fa68c271 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -72,11 +72,11 @@ def __init__(self, *args, **kwargs): self._dict = self._dict_type(*args, **kwargs) internal_inverse = self._dict_type((value, key) for key, value in self._dict.items()) - if len(internal_inverse) <= len(self._dict): + if len(internal_inverse) != len(self._dict): raise Exception("There's a repeating value given to the " "double-sided dict, that is not allowed.") # blocktodo test assert len(internal_inverse) == len(self._dict) - self.inverse = type(self).__new__() + self.inverse = type(self).__new__(type(self)) self.inverse._dict = internal_inverse self.inverse.inverse = self self.inverse.__init__() From 5b2a1da900da0b61875dc337ad59bea4f11379f9 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 22:54:38 +0300 Subject: [PATCH 082/112] - --- source_py3/python_toolbox/nifty_collections/abstract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_py3/python_toolbox/nifty_collections/abstract.py b/source_py3/python_toolbox/nifty_collections/abstract.py index fdaefba2a..f91eed8e6 100644 --- a/source_py3/python_toolbox/nifty_collections/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/abstract.py @@ -2,7 +2,7 @@ # This program is distributed under the MIT license. import abc -import collections +import collections.abc import queue import multiprocessing.queues From 043280103c97e0548a99f611af4e3910776b9551 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 23:02:07 +0300 Subject: [PATCH 083/112] - --- source_py3/python_toolbox/nifty_collections/abstract.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source_py3/python_toolbox/nifty_collections/abstract.py b/source_py3/python_toolbox/nifty_collections/abstract.py index f91eed8e6..66a70b6f5 100644 --- a/source_py3/python_toolbox/nifty_collections/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/abstract.py @@ -31,6 +31,14 @@ class Ordered(metaclass=abc.ABCMeta): class OrderedMapping(Ordered, collections.abc.Mapping): '''blocktododoc''' __slots__ = () + + @classmethod + def __subclasshook__(cls, subclass_candidate): + if cls is OrderedMapping: + return all(issubclass(subclass_candidate, base_class) + for base_class in cls.__mro__) + return NotImplemented + ############################################################################### From 4fefccff1734c2641a04dd80c0bbf2dd0f0d43e9 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 23:04:59 +0300 Subject: [PATCH 084/112] - --- source_py3/python_toolbox/nifty_collections/abstract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_py3/python_toolbox/nifty_collections/abstract.py b/source_py3/python_toolbox/nifty_collections/abstract.py index 66a70b6f5..7f9585cfe 100644 --- a/source_py3/python_toolbox/nifty_collections/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/abstract.py @@ -36,7 +36,7 @@ class OrderedMapping(Ordered, collections.abc.Mapping): def __subclasshook__(cls, subclass_candidate): if cls is OrderedMapping: return all(issubclass(subclass_candidate, base_class) - for base_class in cls.__mro__) + for base_class in cls.__bases__) return NotImplemented From 8c956b2512cf3b564cb1a227da9d72c236c04bef Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 23:17:50 +0300 Subject: [PATCH 085/112] - --- source_py3/python_toolbox/nifty_collections/abstract.py | 8 +------- .../nifty_collections/nifty_dicts/abstract.py | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/abstract.py b/source_py3/python_toolbox/nifty_collections/abstract.py index 7f9585cfe..9a99b95d0 100644 --- a/source_py3/python_toolbox/nifty_collections/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/abstract.py @@ -32,14 +32,8 @@ class OrderedMapping(Ordered, collections.abc.Mapping): '''blocktododoc''' __slots__ = () - @classmethod - def __subclasshook__(cls, subclass_candidate): - if cls is OrderedMapping: - return all(issubclass(subclass_candidate, base_class) - for base_class in cls.__bases__) - return NotImplemented +Ordered.register(collections.OrderedDict) - ############################################################################### class DefinitelyUnordered(metaclass=abc.ABCMeta): diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index 1fa68c271..d7026d72d 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -8,7 +8,7 @@ from python_toolbox import comparison_tools -from python_toolbox.nifty_collections.abstract import (Ordered, +from python_toolbox.nifty_collections.abstract import (Ordered, OrderedMapping, DefinitelyUnordered) from .ordered_dict import OrderedDict @@ -143,7 +143,7 @@ class _UnorderedDictDelegator(DefinitelyUnordered, _dict_type = dict -class _OrderedDictDelegator(Ordered, _AbstractMappingDelegator): +class _OrderedDictDelegator(OrderedMapping, _AbstractMappingDelegator): _dict_type = OrderedDict def __reversed__(self): From d4f0c7a3603169e228f7a97d71400b8c3d96b370 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 23:27:11 +0300 Subject: [PATCH 086/112] - --- .../nifty_collections/nifty_dicts/abstract.py | 4 +-- .../nifty_dicts/nifty_dicts.py | 3 +-- .../nifty_dicts/ordered_dict.py | 4 ++- .../abstract_one_base_test_cases.py | 27 +++++++++++++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index d7026d72d..5c5ecd03d 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -73,8 +73,8 @@ def __init__(self, *args, **kwargs): internal_inverse = self._dict_type((value, key) for key, value in self._dict.items()) if len(internal_inverse) != len(self._dict): - raise Exception("There's a repeating value given to the " - "double-sided dict, that is not allowed.") # blocktodo test + raise ValueError("There's a repeating value given to the " + "double-sided dict, that is not allowed.") # blocktodo test assert len(internal_inverse) == len(self._dict) self.inverse = type(self).__new__(type(self)) self.inverse._dict = internal_inverse diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py index a49050526..c310de79b 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py @@ -37,8 +37,7 @@ class DoubleFrozenDict(abstract._UnorderedDictDelegator, class DoubleOrderedDict(abstract._OrderedDictDelegator, - abstract._AbstractMutableDoubleDict, - abstract._AbstractFrozenDict): + abstract._AbstractMutableDoubleDict): '''blocktododoc''' diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py index 0ade01920..1746570c3 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py @@ -5,8 +5,10 @@ from collections import OrderedDict as StdlibOrderedDict +from ..abstract import OrderedMapping -class OrderedDict(StdlibOrderedDict): + +class OrderedDict(StdlibOrderedDict, OrderedMapping): ''' A dictionary with an order. diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index d52c7893d..295209c08 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -27,6 +27,25 @@ def test_double_dict_base_class(self): nifty_collections.nifty_dicts.abstract.BaseDoubleDict ) + def test_inverse_basics(self): + d = self.d_type(('a', 'b'), ('c', 'd',), ('e', 'f',)) + inverse = d.inverse + + assert inverse.inverse is d + assert type(inverse) is type(d) + assert len(inverse) == len(d) + assert dict(inverse) == {'b': 'a', 'd': 'c', 'f': 'e',} + assert inverse['b'] == 'a' + + + def test_no_value_repeats(self): + with cute_testing.RaiseAssertor(ValueError): + self.d_type(('a', 'b'), ('c', 'd',), ('e', 'b',)) + with cute_testing.RaiseAssertor(ValueError): + self.d_type(foo=7, bar=7) + with cute_testing.RaiseAssertor(ValueError): + self.d_type({1: ('meow',), 2: ('meow',),}) + class AbstractNotDoubleDictTestCase(AbstractDictTestCase): def test_not_double_dict_base_class(self): @@ -35,6 +54,9 @@ def test_not_double_dict_base_class(self): nifty_collections.nifty_dicts.abstract.BaseDoubleDict ) + def test_no_inverse(self): + assert not hasattr(self.d_type(), 'inverse') + ############################################################################### @@ -49,6 +71,11 @@ def test_frozen_dict_base_class(self): self.d_type, collections.abc.MutableMapping ) + + def test_hashable(self): + d = self.d_type(((1, 2), (3, 4))) + assert hash(d) == hash(d) + {d: 7,} class AbstractNotFrozenDictTestCase(AbstractDictTestCase): From 27f39e1a786c3769133e87e7dc72d87200ff790d Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 23:47:52 +0300 Subject: [PATCH 087/112] - --- source_py3/python_toolbox/math_tools/misc.py | 20 ++++++- .../abstract_one_base_test_cases.py | 58 ++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/source_py3/python_toolbox/math_tools/misc.py b/source_py3/python_toolbox/math_tools/misc.py index 6ab475b6a..3051cedbb 100644 --- a/source_py3/python_toolbox/math_tools/misc.py +++ b/source_py3/python_toolbox/math_tools/misc.py @@ -4,13 +4,29 @@ import numbers import math import random +import decimal as decimal_module import python_toolbox.cute_enum infinity = float('inf') infinities = (infinity, -infinity) - +pi_decimal = decimal_module.Decimal( + '3.14159265358979323846264338327950288419716939937510582097494459230781640' + '6286208998628034825342117067982148086513282306647093844609550582231725359' + '4081284811174502841027019385211055596446229489549303819644288109756659334' + '4612847564823378678316527120190914564856692346034861045432664821339360726' + '0249141273724587006606315588174881520920962829254091715364367892590360011' + '3305305488204665213841469519415116094330572703657595919530921861173819326' + '1179310511854807446237996274956735188575272489122793818301194912983367336' + '2440656643086021394946395224737190702179860943702770539217176293176752384' + '6748184676694051320005681271452635608277857713427577896091736371787214684' + '4090122495343014654958537105079227968925892354201995611212902196086403441' + '8159813629774771309960518707211349999998372978049951059731732816096318595' + '0244594553469083026425223082533446850352619311881710100031378387528865875' + '3320838142061717766914730359825349042875546873115956286388235378759375195' + '7781857780532171226806613001927876611195909216420199' +) def cute_floor_div(x, y): ''' @@ -227,4 +243,4 @@ def cute_round(x, round_mode=RoundMode.CLOSEST_OR_DOWN, *, step=1): round_up = random.random() < mod / step return (div + round_up) * step - \ No newline at end of file + diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index 295209c08..5fb5978b8 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -4,9 +4,11 @@ import collections.abc from python_toolbox.third_party import unittest2 - import nose +from python_toolbox import caching +from python_toolbox import math_tools +from python_toolbox import sequence_tools from python_toolbox import cute_iter_tools from python_toolbox import cute_testing @@ -19,6 +21,25 @@ from abstract_dict_test_case import AbstractDictTestCase +@caching.cache() +def get_pseudo_random_strings(n): + ''' + Get a list of random-like digit strings but ensure they're always the same. + + And also they're unique, i.e. no recurrences. + ''' + pi_digits = str(math_tools.pi_decimal).split('.')[-1] + partitions = sequence_tools.partitions( + some_pi_digits, partition_size=5, fill_value=0 + ) + pseudo_random_numbers = nifty_collections.OrderedSet() + for partition in partitions: + if len(pseudo_random_numbers) == n: + return pseudo_random_numbers + pseudo_random_numbers.add(partition) + else: + raise RuntimeError('Not enough unique pseudo-random numbers.') + class AbstractDoubleDictTestCase(AbstractDictTestCase): def test_double_dict_base_class(self): @@ -89,6 +110,13 @@ def test_not_frozen_dict_base_class(self): collections.abc.MutableMapping ) + def test_not_hashable(self): + d = self.d_type(((1, 2), (3, 4))) + with cute_testing.RaiseAssertor(TypeError): + hash(d) + with cute_testing.RaiseAssertor(TypeError): + {d: 7,} + ############################################################################### @@ -107,6 +135,20 @@ def test_ordered_dict_base_class(self): self.d_type, nifty_collections.abstract.DefinitelyUnordered ) + + def test_ordered_on_long(self): + pseudo_random_strings = get_pseudo_random_strings(100) + pairs = sequence_tools.partitions(pseudo_random_strings, 2) + d = self.d_type(pairs) + assert len(d) == 50 + assert tuple(d.items()) == pairs + assert d.index(pairs[7][0]) == 7 + with cute_testing.RaiseAssertor(ValueError): + d.index('meow') + + assert tuple(zip(d.keys(), d.values())) == pairs + + assert tuple(reversed(d)) == next(zip(*pairs)) class AbstractNotOrderedDictTestCase(AbstractDictTestCase): @@ -123,3 +165,17 @@ def test_not_ordered_dict_base_class(self): self.d_type, nifty_collections.abstract.DefinitelyUnordered ) + + def test_not_ordered_on_long(self): + pseudo_random_strings = get_pseudo_random_strings(100) + pairs = sequence_tools.partitions(pseudo_random_strings, 2) + d = self.d_type(pairs) + assert len(d) == 50 + assert tuple(d.items()) != pairs + assert set(d.items()) == set(pairs) + assert not hasattr(d, 'index') + assert not hasattr(d, 'move_to_end') + assert not hasattr(d, 'sort') + assert not hasattr(d, '__reversed__') + + From bcd1f4985db44eb5ab0c37984f3ff23f8780ac2e Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 23:50:23 +0300 Subject: [PATCH 088/112] - --- .../test_nifty_dicts/abstract_one_base_test_cases.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index 5fb5978b8..58b77252e 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -28,10 +28,8 @@ def get_pseudo_random_strings(n): And also they're unique, i.e. no recurrences. ''' - pi_digits = str(math_tools.pi_decimal).split('.')[-1] - partitions = sequence_tools.partitions( - some_pi_digits, partition_size=5, fill_value=0 - ) + some_pi_digits = str(math_tools.pi_decimal).split('.')[-1][:900] + partitions = sequence_tools.partitions(some_pi_digits, partition_size=5) pseudo_random_numbers = nifty_collections.OrderedSet() for partition in partitions: if len(pseudo_random_numbers) == n: @@ -49,7 +47,7 @@ def test_double_dict_base_class(self): ) def test_inverse_basics(self): - d = self.d_type(('a', 'b'), ('c', 'd',), ('e', 'f',)) + d = self.d_type((('a', 'b'), ('c', 'd',), ('e', 'f',))) inverse = d.inverse assert inverse.inverse is d @@ -61,7 +59,7 @@ def test_inverse_basics(self): def test_no_value_repeats(self): with cute_testing.RaiseAssertor(ValueError): - self.d_type(('a', 'b'), ('c', 'd',), ('e', 'b',)) + self.d_type((('a', 'b'), ('c', 'd',), ('e', 'b',))) with cute_testing.RaiseAssertor(ValueError): self.d_type(foo=7, bar=7) with cute_testing.RaiseAssertor(ValueError): From c275ffe110fcf0bf802dfa9bb5381bc195759ebf Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 13 Aug 2016 23:53:22 +0300 Subject: [PATCH 089/112] - --- .../test_nifty_dicts/abstract_one_base_test_cases.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index 58b77252e..9ec52723b 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -33,7 +33,7 @@ def get_pseudo_random_strings(n): pseudo_random_numbers = nifty_collections.OrderedSet() for partition in partitions: if len(pseudo_random_numbers) == n: - return pseudo_random_numbers + return tuple(pseudo_random_numbers) pseudo_random_numbers.add(partition) else: raise RuntimeError('Not enough unique pseudo-random numbers.') @@ -136,7 +136,7 @@ def test_ordered_dict_base_class(self): def test_ordered_on_long(self): pseudo_random_strings = get_pseudo_random_strings(100) - pairs = sequence_tools.partitions(pseudo_random_strings, 2) + pairs = tuple(sequence_tools.partitions(pseudo_random_strings, 2)) d = self.d_type(pairs) assert len(d) == 50 assert tuple(d.items()) == pairs @@ -146,7 +146,7 @@ def test_ordered_on_long(self): assert tuple(zip(d.keys(), d.values())) == pairs - assert tuple(reversed(d)) == next(zip(*pairs)) + assert tuple(reversed(d)) == next(zip(*pairs[::-1])) class AbstractNotOrderedDictTestCase(AbstractDictTestCase): From e728e7ed332da45b61ef260d78ef3cf098f079b5 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:05:07 +0300 Subject: [PATCH 090/112] - --- .../nifty_collections/nifty_dicts/abstract.py | 4 +- .../abstract_one_base_test_cases.py | 40 +++++++++++++++++++ .../abstract_two_base_test_cases.py | 24 +++++------ 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index 5c5ecd03d..2452379e4 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -110,9 +110,9 @@ def __setitem__(self, key, value): try: existing_value = self[key] except KeyError: - got_existing_value = True - else: got_existing_value = False + else: + got_existing_value = True self._dict[key] = value self.inverse._dict[value] = key diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index 9ec52723b..48090e95d 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -95,6 +95,18 @@ def test_hashable(self): d = self.d_type(((1, 2), (3, 4))) assert hash(d) == hash(d) {d: 7,} + + def test_frozen(self): + d = self.d_type(((1, 2), (3, 4))) + with cute_testing.RaiseAssertor(TypeError): + d[5] = 6 + with cute_testing.RaiseAssertor(TypeError): + del d[1] + assert not hasattr(d, 'setdefault') + assert not hasattr(d, 'pop') + assert not hasattr(d, 'popitem') + assert not hasattr(d, 'update') + class AbstractNotFrozenDictTestCase(AbstractDictTestCase): @@ -115,6 +127,34 @@ def test_not_hashable(self): with cute_testing.RaiseAssertor(TypeError): {d: 7,} + def test_notfrozen(self): + d = self.d_type(((1, 2), (3, 4))) + assert len(d) == 2 + + d[5] = 6 + assert len(d) == 3 + + del d[5] + assert len(d) == 2 + + d.setdefault(5, 6) + assert len(d) == 3 + + value = d.pop(5) + assert value == 6 + assert len(d) == 2 + + d[5] = 6 + assert len(d) == 3 + + item = d.popitem() + assert item in {(1, 2), (3, 4), (5, 6)} + assert item[0] not in d + assert len(d) == 2 + + d.update({'foo': 'bar',}) + assert len(d) == 3 + ############################################################################### diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index c4d8a1dfb..bcf2c6556 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -24,22 +24,22 @@ class AbstractDoubleFrozenDictTestCase(AbstractDoubleDictTestCase, - AbstractFrozenDictTestCase): + AbstractFrozenDictTestCase): pass class AbstractDoubleNotFrozenDictTestCase(AbstractDoubleDictTestCase, - AbstractNotFrozenDictTestCase): + AbstractNotFrozenDictTestCase): pass class AbstractNotDoubleFrozenDictTestCase(AbstractFrozenDictTestCase, - AbstractNotDoubleDictTestCase): + AbstractNotDoubleDictTestCase): pass class AbstractNotDoubleNotFrozenDictTestCase(AbstractNotDoubleDictTestCase, - AbstractNotFrozenDictTestCase): + AbstractNotFrozenDictTestCase): pass @@ -47,21 +47,21 @@ class AbstractNotDoubleNotFrozenDictTestCase(AbstractNotDoubleDictTestCase, class AbstractDoubleOrderedDictTestCase(AbstractDoubleDictTestCase, - AbstractOrderedDictTestCase): + AbstractOrderedDictTestCase): pass class AbstractDoubleNotOrderedDictTestCase(AbstractDoubleDictTestCase, - AbstractNotOrderedDictTestCase): + AbstractNotOrderedDictTestCase): pass class AbstractNotDoubleOrderedDictTestCase(AbstractOrderedDictTestCase, - AbstractNotDoubleDictTestCase): + AbstractNotDoubleDictTestCase): pass class AbstractNotDoubleNotOrderedDictTestCase(AbstractNotDoubleDictTestCase, - AbstractNotOrderedDictTestCase): + AbstractNotOrderedDictTestCase): pass @@ -69,22 +69,22 @@ class AbstractNotDoubleNotOrderedDictTestCase(AbstractNotDoubleDictTestCase, class AbstractFrozenOrderedDictTestCase(AbstractFrozenDictTestCase, - AbstractOrderedDictTestCase): + AbstractOrderedDictTestCase): pass class AbstractFrozenNotOrderedDictTestCase(AbstractFrozenDictTestCase, - AbstractNotOrderedDictTestCase): + AbstractNotOrderedDictTestCase): pass class AbstractNotFrozenOrderedDictTestCase(AbstractOrderedDictTestCase, - AbstractNotFrozenDictTestCase): + AbstractNotFrozenDictTestCase): pass class AbstractNotFrozenNotOrderedDictTestCase(AbstractNotFrozenDictTestCase, - AbstractNotOrderedDictTestCase): + AbstractNotOrderedDictTestCase): pass From 88078dbd9d3746689dc6c1afd08b4e3881d02fa2 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:08:18 +0300 Subject: [PATCH 091/112] - --- .../test_nifty_dicts/abstract_dict_test_case.py | 2 ++ .../abstract_two_base_test_cases.py | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py index fc1859244..bef163bd5 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py @@ -34,6 +34,8 @@ def test_common(self): assert d[1] == 2 assert d[3] == 4 assert d[5] == 6 + assert '1' in d + assert 'meow' not in d with cute_testing.RaiseAssertor(exception_type=KeyError): d[7] with cute_testing.RaiseAssertor(exception_type=KeyError): diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index bcf2c6556..7aede75f6 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -30,7 +30,21 @@ class AbstractDoubleFrozenDictTestCase(AbstractDoubleDictTestCase, class AbstractDoubleNotFrozenDictTestCase(AbstractDoubleDictTestCase, AbstractNotFrozenDictTestCase): - pass + def test_changing_affects_inverse(self): + d = self.d_type(((1, 2), (3, 4), (5, 6))) + inverse = d.inverse + assert len(d) == len(inverse) == 3 + + d[7] == 8 + assert inverse[8] == 7 + assert len(d) == len(inverse) == 4 + + del d[3] + assert len(d) == len(inverse) == 3 + assert '4' not in inverse + + + class AbstractNotDoubleFrozenDictTestCase(AbstractFrozenDictTestCase, From 45eb35514ee09bd62285fb4bad56486eeaf49fd6 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:13:28 +0300 Subject: [PATCH 092/112] - --- .../abstract_dict_test_case.py | 4 ++++ .../abstract_one_base_test_cases.py | 18 +-------------- .../abstract_two_base_test_cases.py | 14 +++++++---- .../test_nifty_dicts/tools.py | 23 +++++++++++++++++++ 4 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/tools.py diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py index bef163bd5..328512493 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py @@ -49,4 +49,8 @@ def test_common(self): assert d == d.copy() == d.copy() + assert set(d) == set(d.keys()) + assert tuple(map(set, zip(*d.items()))) == \ + (set(d.keys()), set(d.values())) + diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index 48090e95d..9ddddbfdd 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -19,25 +19,9 @@ FrozenOrderedDict, DoubleFrozenOrderedDict ) +from tools import get_pseudo_random_strings from abstract_dict_test_case import AbstractDictTestCase -@caching.cache() -def get_pseudo_random_strings(n): - ''' - Get a list of random-like digit strings but ensure they're always the same. - - And also they're unique, i.e. no recurrences. - ''' - some_pi_digits = str(math_tools.pi_decimal).split('.')[-1][:900] - partitions = sequence_tools.partitions(some_pi_digits, partition_size=5) - pseudo_random_numbers = nifty_collections.OrderedSet() - for partition in partitions: - if len(pseudo_random_numbers) == n: - return tuple(pseudo_random_numbers) - pseudo_random_numbers.add(partition) - else: - raise RuntimeError('Not enough unique pseudo-random numbers.') - class AbstractDoubleDictTestCase(AbstractDictTestCase): def test_double_dict_base_class(self): diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index 7aede75f6..27e7de689 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -17,7 +17,8 @@ FrozenOrderedDict, DoubleFrozenOrderedDict ) -from abstract_one_base_test_cases import * +from abstract_one_base_test_cases import * +from tools import get_pseudo_random_strings ############################################################################### @@ -43,8 +44,9 @@ def test_changing_affects_inverse(self): assert len(d) == len(inverse) == 3 assert '4' not in inverse - - + d.clear() + assert len(d) == len(inverse) == 0 + assert '2' not in inverse class AbstractNotDoubleFrozenDictTestCase(AbstractFrozenDictTestCase, @@ -62,7 +64,11 @@ class AbstractNotDoubleNotFrozenDictTestCase(AbstractNotDoubleDictTestCase, class AbstractDoubleOrderedDictTestCase(AbstractDoubleDictTestCase, AbstractOrderedDictTestCase): - pass + def test_double_ordered(self): + pseudo_random_strings = get_pseudo_random_strings(100) + pairs = sequence_tools.partitions(pseudo_random_strings, 2) + d = self.d_type(pairs) + class AbstractDoubleNotOrderedDictTestCase(AbstractDoubleDictTestCase, AbstractNotOrderedDictTestCase): diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/tools.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/tools.py new file mode 100644 index 000000000..fe11cce5b --- /dev/null +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/tools.py @@ -0,0 +1,23 @@ +from python_toolbox import caching +from python_toolbox import cute_iter_tools +from python_toolbox import cute_testing + + +@caching.cache() +def get_pseudo_random_strings(n): + ''' + Get a list of random-like digit strings but ensure they're always the same. + + And also they're unique, i.e. no recurrences. + ''' + some_pi_digits = str(math_tools.pi_decimal).split('.')[-1][:900] + partitions = sequence_tools.partitions(some_pi_digits, partition_size=5) + pseudo_random_numbers = nifty_collections.OrderedSet() + for partition in partitions: + if len(pseudo_random_numbers) == n: + return tuple(pseudo_random_numbers) + pseudo_random_numbers.add(partition) + else: + raise RuntimeError('Not enough unique pseudo-random numbers.') + + From 8cc3f269fa0b15aa0ac9a7679bdf9ac431a7769e Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:23:41 +0300 Subject: [PATCH 093/112] - --- .../abstract_one_base_test_cases.py | 10 +++-- .../abstract_two_base_test_cases.py | 13 +++--- .../test_nifty_dicts/test_nifty_dicts.py | 44 +++++++++++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index 9ddddbfdd..b943544f8 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -144,6 +144,12 @@ def test_notfrozen(self): class AbstractOrderedDictTestCase(AbstractDictTestCase): + + def make_big_dict(self): + pseudo_random_strings = get_pseudo_random_strings(100) + pairs = tuple(sequence_tools.partitions(pseudo_random_strings, 2)) + return self.d_type(pairs) + def test_ordered_dict_base_class(self): assert issubclass( self.d_type, @@ -159,9 +165,7 @@ def test_ordered_dict_base_class(self): ) def test_ordered_on_long(self): - pseudo_random_strings = get_pseudo_random_strings(100) - pairs = tuple(sequence_tools.partitions(pseudo_random_strings, 2)) - d = self.d_type(pairs) + d = self.make_big_dict() assert len(d) == 50 assert tuple(d.items()) == pairs assert d.index(pairs[7][0]) == 7 diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index 27e7de689..d01812e60 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -18,7 +18,6 @@ ) from abstract_one_base_test_cases import * -from tools import get_pseudo_random_strings ############################################################################### @@ -64,10 +63,14 @@ class AbstractNotDoubleNotFrozenDictTestCase(AbstractNotDoubleDictTestCase, class AbstractDoubleOrderedDictTestCase(AbstractDoubleDictTestCase, AbstractOrderedDictTestCase): - def test_double_ordered(self): - pseudo_random_strings = get_pseudo_random_strings(100) - pairs = sequence_tools.partitions(pseudo_random_strings, 2) - d = self.d_type(pairs) + def test_double_ordered_without_modifying(self): + d = self.make_big_dict() + assert tuple(d.inverse.keys()) == tuple(d.values()) + assert tuple(d.inverse.values()) == tuple(d.keys()) + + some_pair = tuple(d.items())[47] + assert d.index(some_pair[0]) == 47 + assert d.inverse.index(some_pair[1]) == 47 class AbstractDoubleNotOrderedDictTestCase(AbstractDoubleDictTestCase, diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py index c253f6c29..ad340bd0b 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py @@ -53,6 +53,50 @@ class DoubleOrderedDictTestCase(AbstractDoubleOrderedDictTestCase, AbstractNotFrozenOrderedDictTestCase): __test__ = True d_type = DoubleOrderedDict + + def test_changing_order_affects_double(self): + d = self.make_big_dict() + assert tuple(d.inverse.keys()) == tuple(d.values()) + assert tuple(d.inverse.values()) == tuple(d.keys()) + + old_pairs = tuple(d.items()) + d.sort() + assert set(d.items()) == set(old_pairs) + assert tuple(d.items()) != old_pairs + assert tuple(d.inverse.keys()) == tuple(d.values()) + assert tuple(d.inverse.values()) == tuple(d.keys()) + + old_pairs = tuple(d.items()) + d.inverse.sort(key=hash, reverse=True) + assert set(d.items()) == set(old_pairs) + assert tuple(d.items()) != old_pairs + assert tuple(d.inverse.keys()) == tuple(d.values()) + assert tuple(d.inverse.values()) == tuple(d.keys()) + + old_pairs = tuple(d.items()) + some_key, some_value = old_pairs[37] + d.move_to_end(some_key) + assert d.index(some_key) == len(d) - 1 + assert d.inverse.index(some_value) == len(d) - 1 + assert set(d.items()) == set(old_pairs) + assert tuple(d.items()) != old_pairs + assert tuple(d.inverse.keys()) == tuple(d.values()) + assert tuple(d.inverse.values()) == tuple(d.keys()) + + old_pairs = tuple(d.items()) + some_other_key, some_other_value = old_pairs[23] + d.inverse.move_to_end(some_other_value, last=False) + assert d.index(some_key) == len + assert d.inverse.index(some_other_value) == 0 + assert set(d.items()) == set(old_pairs) + assert tuple(d.items()) != old_pairs + assert tuple(d.inverse.keys()) == tuple(d.values()) + assert tuple(d.inverse.values()) == tuple(d.keys()) + + d['foo'] == 'bar' + assert len(d) == len(d.inverse) == len(old_pairs) + 1 + assert d.index('foo') == len(d) - 1 + assert d.inverse.index('bar') == len(d) - 1 class FrozenOrderedDictTestCase(AbstractFrozenOrderedDictTestCase, From 7e17022b06147ec702a554a73c371447138174c7 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:29:43 +0300 Subject: [PATCH 094/112] - --- .../test_nifty_dicts/abstract_dict_test_case.py | 13 +++++++++++++ .../abstract_two_base_test_cases.py | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py index 328512493..13609da96 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py @@ -7,6 +7,7 @@ import nose +from python_toolbox import sequence_tools from python_toolbox import cute_iter_tools from python_toolbox import cute_testing @@ -53,4 +54,16 @@ def test_common(self): assert tuple(map(set, zip(*d.items()))) == \ (set(d.keys()), set(d.values())) + def __init__(self, *args, **kwargs): + cute_testing.TestCase.__init__(self, *args, **kwargs) + + # Ensure no overridden test methods so no tests will go ignored: + test_method_names_from_all_base_classes = [ + [method for method in dir(base_class) if method.startswith('test_') + for base_class in type(self).__mro__] + ] + all_test_method_names = sequence_tools.flatten( + test_method_names_from_all_base_classes + ) + assert not sequence_tools.get_recurrences(all_test_method_names) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index d01812e60..fc27e8739 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -103,7 +103,8 @@ class AbstractFrozenNotOrderedDictTestCase(AbstractFrozenDictTestCase, class AbstractNotFrozenOrderedDictTestCase(AbstractOrderedDictTestCase, AbstractNotFrozenDictTestCase): - pass + def test_move_to_end(self): + class AbstractNotFrozenNotOrderedDictTestCase(AbstractNotFrozenDictTestCase, From 0cba2b8b56aa076d6395349fcfbde74129fe3c9f Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:34:35 +0300 Subject: [PATCH 095/112] - --- .../abstract_two_base_test_cases.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index fc27e8739..6a956e483 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -104,7 +104,33 @@ class AbstractFrozenNotOrderedDictTestCase(AbstractFrozenDictTestCase, class AbstractNotFrozenOrderedDictTestCase(AbstractOrderedDictTestCase, AbstractNotFrozenDictTestCase): def test_move_to_end(self): + d = self.d_type(((1, 2), (3, 4), (5, 6))) + assert tuple(d.items()) == ((1, 2), (3, 4), (5, 6)) + assert d.index(3) == 1 + assert tuple(d) == (1, 3, 5) + + d.move_to_end(3) + assert tuple(d.items()) == ((1, 2), (5, 6), (3, 4)) + assert d.index(3) == 2 + assert tuple(d) == (1, 5, 3) + + assert d.index(5) == 2 + d.move_to_end(5, end=False) + assert tuple(d.items()) == ((5, 6), (1, 2), (3, 4)) + assert d.index(5) == 1 + assert tuple(d) == (5, 1, 3) + def test_new_item_at_end(self): + d = self.d_type(((1, 2), (5, 6))) + d[3] = 4 + assert tuple(d.items()) == ((1, 2), (5, 6), (3, 4)) + + def test_new_item_at_end(self): + d = self.d_type(((1, 2), (5, 6), (3, 4))) + d.sort() + assert tuple(d.items()) == ((1, 2), (3, 4), (5, 6)) + d.sort(reverse=True) + assert tuple(d.items()) == ((5, 6), (3, 4), (1, 2)) class AbstractNotFrozenNotOrderedDictTestCase(AbstractNotFrozenDictTestCase, From ac99e15b62d45dc162ab489daeb179ff35ae2202 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:39:43 +0300 Subject: [PATCH 096/112] - --- .../abstract_dict_test_case.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py index 13609da96..ba1514048 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py @@ -58,12 +58,17 @@ def __init__(self, *args, **kwargs): cute_testing.TestCase.__init__(self, *args, **kwargs) # Ensure no overridden test methods so no tests will go ignored: - test_method_names_from_all_base_classes = [ - [method for method in dir(base_class) if method.startswith('test_') - for base_class in type(self).__mro__] - ] - all_test_method_names = sequence_tools.flatten( - test_method_names_from_all_base_classes - ) - assert not sequence_tools.get_recurrences(all_test_method_names) + base_classes = collections.deque(type(self).__bases__) + while base_classes: + base_class = base_classes.pop() + test_method_names_from_base_classes = [ + [method for method in dir(base_class_of_base_class) + if method.startswith('test_')] + for base_class_of_base_class in base_class.__bases__ + ] + all_test_method_names = sequence_tools.flatten( + test_method_names_from_base_classes + ) + assert not sequence_tools.get_recurrences(all_test_method_names) + base_classes.append(base_class.__bases__) From aeb46779b3158b169eec1e89da902785795770e3 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:43:46 +0300 Subject: [PATCH 097/112] - --- .../test_nifty_dicts/abstract_dict_test_case.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py index ba1514048..f8d0092f9 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py @@ -8,6 +8,7 @@ import nose from python_toolbox import sequence_tools +from python_toolbox import logic_tools from python_toolbox import cute_iter_tools from python_toolbox import cute_testing @@ -61,14 +62,14 @@ def __init__(self, *args, **kwargs): base_classes = collections.deque(type(self).__bases__) while base_classes: base_class = base_classes.pop() - test_method_names_from_base_classes = [ - [method for method in dir(base_class_of_base_class) - if method.startswith('test_')] - for base_class_of_base_class in base_class.__bases__ + test_methods_from_base_classes = [ + getattr(base_class_of_base_class, method) for method in + dir(base_class_of_base_class) if method.startswith('test_') + for base_class_of_base_class in base_class.__bases__ ] - all_test_method_names = sequence_tools.flatten( - test_method_names_from_base_classes + equivalence_classes = logic_tools.get_equivalence_classes( + test_methods_from_base_classes, key='__name__' ) - assert not sequence_tools.get_recurrences(all_test_method_names) + assert set(map(len, equivalence_classes.values())) == 1 base_classes.append(base_class.__bases__) From fdce00e39f50a112c7f0bd41ba383f6ca41f23e9 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:47:39 +0300 Subject: [PATCH 098/112] - --- .../test_nifty_dicts/abstract_dict_test_case.py | 17 +++++++++-------- .../abstract_two_base_test_cases.py | 3 ++- .../test_nifty_dicts/tools.py | 8 ++++++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py index f8d0092f9..aaf519060 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py @@ -36,7 +36,7 @@ def test_common(self): assert d[1] == 2 assert d[3] == 4 assert d[5] == 6 - assert '1' in d + assert 1 in d assert 'meow' not in d with cute_testing.RaiseAssertor(exception_type=KeyError): d[7] @@ -62,14 +62,15 @@ def __init__(self, *args, **kwargs): base_classes = collections.deque(type(self).__bases__) while base_classes: base_class = base_classes.pop() - test_methods_from_base_classes = [ - getattr(base_class_of_base_class, method) for method in - dir(base_class_of_base_class) if method.startswith('test_') - for base_class_of_base_class in base_class.__bases__ - ] + test_methods_from_base_classes = sequence_tools.flatten([ + [getattr(base_class_of_base_class, method) for method in + dir(base_class_of_base_class) if method.startswith('test_')] + for base_class_of_base_class in + (base_class,) + base_class.__bases__ + ]) equivalence_classes = logic_tools.get_equivalence_classes( test_methods_from_base_classes, key='__name__' ) - assert set(map(len, equivalence_classes.values())) == 1 - base_classes.append(base_class.__bases__) + assert set(map(len, equivalence_classes.values())) <= {1} + base_classes.extend(base_class.__bases__) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index 6a956e483..d7eeb3119 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -35,7 +35,7 @@ def test_changing_affects_inverse(self): inverse = d.inverse assert len(d) == len(inverse) == 3 - d[7] == 8 + d[7] = 8 assert inverse[8] == 7 assert len(d) == len(inverse) == 4 @@ -46,6 +46,7 @@ def test_changing_affects_inverse(self): d.clear() assert len(d) == len(inverse) == 0 assert '2' not in inverse + class AbstractNotDoubleFrozenDictTestCase(AbstractFrozenDictTestCase, diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/tools.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/tools.py index fe11cce5b..fd6e6b72b 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/tools.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/tools.py @@ -1,6 +1,10 @@ +# Copyright 2009-2017 Ram Rachum. +# This program is distributed under the MIT license. + +from python_toolbox import math_tools from python_toolbox import caching -from python_toolbox import cute_iter_tools -from python_toolbox import cute_testing +from python_toolbox import nifty_collections +from python_toolbox import sequence_tools @caching.cache() From fa1b3eff88dbd5ddc7044c554ee77cab30414a71 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:49:49 +0300 Subject: [PATCH 099/112] - --- .../abstract_one_base_test_cases.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index b943544f8..ea17982e8 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -148,7 +148,11 @@ class AbstractOrderedDictTestCase(AbstractDictTestCase): def make_big_dict(self): pseudo_random_strings = get_pseudo_random_strings(100) pairs = tuple(sequence_tools.partitions(pseudo_random_strings, 2)) - return self.d_type(pairs) + d = self.d_type(pairs) + assert len(d) == 50 + assert tuple(d.items()) == pairs + return d + def test_ordered_dict_base_class(self): assert issubclass( @@ -166,15 +170,13 @@ def test_ordered_dict_base_class(self): def test_ordered_on_long(self): d = self.make_big_dict() - assert len(d) == 50 - assert tuple(d.items()) == pairs - assert d.index(pairs[7][0]) == 7 + assert d.index(tuple(d.items())[7][0]) == 7 with cute_testing.RaiseAssertor(ValueError): d.index('meow') - assert tuple(zip(d.keys(), d.values())) == pairs + assert tuple(zip(d.keys(), d.values())) == tuple(d.items()) - assert tuple(reversed(d)) == next(zip(*pairs[::-1])) + assert tuple(reversed(d)) == next(zip(*tuple(d.items())[::-1])) class AbstractNotOrderedDictTestCase(AbstractDictTestCase): From 391c5c13110bf6b0cefd9cd9c0ba58fe5723f316 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:51:20 +0300 Subject: [PATCH 100/112] - --- .../nifty_dicts/nifty_dicts.py | 17 ++++++++++++++++- .../nifty_dicts/ordered_dict.py | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py index c310de79b..a1bbc5343 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py @@ -41,7 +41,22 @@ class DoubleOrderedDict(abstract._OrderedDictDelegator, '''blocktododoc''' - def move_to_end(self, key, last=True): + def sort(self, *, key=None, reverse=False): + ''' + Sort the items according to their keys, changing the order in-place. + + The optional `key` argument, (not to be confused with the dictionary + keys,) will be passed to the `sorted` function as a key function. + ''' + from python_toolbox import comparison_tools + key_function = \ + comparison_tools.process_key_function_or_attribute_name(key) + sorted_keys = sorted(self.keys(), key=key_function, reverse=reverse) + for key_ in sorted_keys[1:]: + self.move_to_end(key_) + + + def move_to_end(self, key, *, last=True): ''' Move an existing element to the end (or beginning if `last is False`.) diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py index 1746570c3..fe9d5496b 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/ordered_dict.py @@ -16,7 +16,7 @@ class OrderedDict(StdlibOrderedDict, OrderedMapping): improvements. ''' - def sort(self, key=None, reverse=False): + def sort(self, *, key=None, reverse=False): ''' Sort the items according to their keys, changing the order in-place. From 96cf8451941da260648d808cc579d1ee966e6a39 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 14 Aug 2016 00:52:57 +0300 Subject: [PATCH 101/112] - --- .../test_nifty_dicts/abstract_two_base_test_cases.py | 6 +++--- .../test_nifty_dicts/test_nifty_dicts.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index d7eeb3119..214549f3f 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -115,10 +115,10 @@ def test_move_to_end(self): assert d.index(3) == 2 assert tuple(d) == (1, 5, 3) - assert d.index(5) == 2 - d.move_to_end(5, end=False) - assert tuple(d.items()) == ((5, 6), (1, 2), (3, 4)) assert d.index(5) == 1 + d.move_to_end(5, last=False) + assert tuple(d.items()) == ((5, 6), (1, 2), (3, 4)) + assert d.index(5) == 0 assert tuple(d) == (5, 1, 3) def test_new_item_at_end(self): diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py index ad340bd0b..f2e37ea72 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/test_nifty_dicts.py @@ -86,14 +86,14 @@ def test_changing_order_affects_double(self): old_pairs = tuple(d.items()) some_other_key, some_other_value = old_pairs[23] d.inverse.move_to_end(some_other_value, last=False) - assert d.index(some_key) == len + assert d.index(some_other_key) == 0 assert d.inverse.index(some_other_value) == 0 assert set(d.items()) == set(old_pairs) assert tuple(d.items()) != old_pairs assert tuple(d.inverse.keys()) == tuple(d.values()) assert tuple(d.inverse.values()) == tuple(d.keys()) - d['foo'] == 'bar' + d['foo'] = 'bar' assert len(d) == len(d.inverse) == len(old_pairs) + 1 assert d.index('foo') == len(d) - 1 assert d.inverse.index('bar') == len(d) - 1 From c157e39aac071ac1b506938ad8dc3f6f5fa3b9ca Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Mon, 15 Aug 2016 14:44:59 +0300 Subject: [PATCH 102/112] - --- .../test_cute_enum/test.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_cute_enum/test.py b/source_py3/test_python_toolbox/test_nifty_collections/test_cute_enum/test.py index e162452a4..89ff4ee2a 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_cute_enum/test.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_cute_enum/test.py @@ -39,3 +39,27 @@ class Flavor(CuteEnum): assert Flavor[:2] == (Flavor.CHOCOLATE, Flavor.VANILLA) + +def test_comparable_enum_recipe(): + class ComparableEnum(CuteEnum): + def __eq__(self, other): + if type(other) == type(self): + return self is other + elif isinstance(other, str): + return self.value == other + else: + return NotImplemented + + __str__ = lambda self: self.value + + class Style(ComparableEnum): + ROCK = 'rock' + JAZZ = 'jazz' + BLUES = 'blues' + + + assert Style.ROCK == Style.ROCK == str(Style.ROCK) == 'rock' + assert 'jazz' != Style.ROCK != Style.JAZZ + assert Style.ROCK != Style.ROCK.number == 0 + + assert sorted(Style) == [Style.ROCK, Style.JAZZ, Style.BLUES] \ No newline at end of file From a0a3d11b75307340b67d19be601ea6ef5728bf05 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 16 Aug 2016 21:34:15 +0300 Subject: [PATCH 103/112] - --- source_py3/python_toolbox/cute_testing.py | 2 +- .../nifty_collections/nifty_dicts/abstract.py | 5 ++--- .../nifty_collections/nifty_dicts/nifty_dicts.py | 4 ---- .../test_nifty_dicts/abstract_one_base_test_cases.py | 6 +++--- .../test_nifty_dicts/abstract_two_base_test_cases.py | 8 ++++++++ 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/source_py3/python_toolbox/cute_testing.py b/source_py3/python_toolbox/cute_testing.py index 0e1ec1dc2..8b3d7aca7 100644 --- a/source_py3/python_toolbox/cute_testing.py +++ b/source_py3/python_toolbox/cute_testing.py @@ -32,7 +32,7 @@ class RaiseAssertor(context_management.ContextManager): ''' - def __init__(self, exception_type=Exception, text='', + def __init__(self, exception_type=Exception, text='', * assert_exact_type=False): ''' Construct the `RaiseAssertor`. diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index 2452379e4..97091429f 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -74,7 +74,7 @@ def __init__(self, *args, **kwargs): in self._dict.items()) if len(internal_inverse) != len(self._dict): raise ValueError("There's a repeating value given to the " - "double-sided dict, that is not allowed.") # blocktodo test + "double-sided dict, that is not allowed.") assert len(internal_inverse) == len(self._dict) self.inverse = type(self).__new__(type(self)) self.inverse._dict = internal_inverse @@ -122,7 +122,7 @@ def __setitem__(self, key, value): def __delitem__(self, key): - value = self[key] # Propagating possible KeyError # blocktodo test + value = self[key] # Propagating KeyError self._assert_valid() del self._dict[key] del self.inverse._dict[value] @@ -130,7 +130,6 @@ def __delitem__(self, key): def clear(self): - 'D.clear() -> None. Remove all items from D.' self._assert_valid() self._dict.clear() self.inverse._dict.clear() diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py index a1bbc5343..fa823b7bc 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/nifty_dicts.py @@ -122,7 +122,3 @@ class DoubleFrozenOrderedDict(abstract._OrderedDictDelegator, '''blocktododoc''' -# blocktodo: Do sophisticated tests that iterate over the dict classes, see -# their attributes (defined in the tests) and do the appropriate tests. For -# example if a dict is ordered and mutable, we should test .sort and -# .move_to_end, otherwise we should test these methods don't exist. diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index ea17982e8..a4c656ea7 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -42,11 +42,11 @@ def test_inverse_basics(self): def test_no_value_repeats(self): - with cute_testing.RaiseAssertor(ValueError): + with cute_testing.RaiseAssertor(ValueError, text='repeating value'): self.d_type((('a', 'b'), ('c', 'd',), ('e', 'b',))) - with cute_testing.RaiseAssertor(ValueError): + with cute_testing.RaiseAssertor(ValueError, text='repeating value'): self.d_type(foo=7, bar=7) - with cute_testing.RaiseAssertor(ValueError): + with cute_testing.RaiseAssertor(ValueError, text='repeating value'): self.d_type({1: ('meow',), 2: ('meow',),}) diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index 214549f3f..24d7074f4 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -47,6 +47,14 @@ def test_changing_affects_inverse(self): assert len(d) == len(inverse) == 0 assert '2' not in inverse + def test_del_key_error(self): + d = self.d_type(((1, 2), (3, 4), (5, 6))) + del d[1] + with cute_testing.RaiseAssertor(KeyError): + del d[1] + with cute_testing.RaiseAssertor(KeyError): + del d['woof'] + class AbstractNotDoubleFrozenDictTestCase(AbstractFrozenDictTestCase, From 0e1f01a817a6f8a75271fb59721c6435512affc3 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 16 Aug 2016 21:44:51 +0300 Subject: [PATCH 104/112] - --- source_py3/python_toolbox/abc_tools.py | 21 +++++-------------- .../nifty_collections/nifty_dicts/abstract.py | 6 +++--- .../bind_savvy_evt_handler/name_parser.py | 3 ++- ...ic_method.py => test_abstract_whatever.py} | 4 +--- .../abstract_one_base_test_cases.py | 3 +++ .../abstract_two_base_test_cases.py | 18 ++++++++++++++++ 6 files changed, 32 insertions(+), 23 deletions(-) rename source_py3/test_python_toolbox/test_abc_tools/{test_abstract_static_method.py => test_abstract_whatever.py} (85%) diff --git a/source_py3/python_toolbox/abc_tools.py b/source_py3/python_toolbox/abc_tools.py index 5affb74ae..964dbb79d 100644 --- a/source_py3/python_toolbox/abc_tools.py +++ b/source_py3/python_toolbox/abc_tools.py @@ -3,20 +3,9 @@ '''Defines tools related to abstract base classes from the `abc` module.''' +import abc -class AbstractStaticMethod(staticmethod): - ''' - A combination of `abc.abstractmethod` and `staticmethod`. - - A method which (a) doesn't take a `self` argument and (b) must be - overridden in any subclass if you want that subclass to be instanciable. - - This class is good only for documentation; it doesn't enforce overriding - methods to be static. - ''' - __slots__ = () - __isabstractmethod__ = True - - def __init__(self, function): - super().__init__(function) - function.__isabstractmethod__ = True + +def abstract_whatever(): + return abc.abstractmethod(lambda: None) + \ No newline at end of file diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index 97091429f..30aa7cd9b 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -95,17 +95,17 @@ def __setitem__(self, key, value): except KeyError: pass else: - raise Exception( + raise ValueError( "Can't add key %s with value %s because there is already a " "key %s with the same value." % (key, value, - self.inverse[value]) # blocktodo test + self.inverse[value]) ) try: hash(value) except TypeError as hashing_error: raise TypeError('%s is not hashable so it can\'t be used as a ' - 'value in a double-sided dict.' % value) + 'value in a double-sided dict.' % value) from hashing_error try: existing_value = self[key] diff --git a/source_py3/python_toolbox/wx_tools/widgets/cute_window/bind_savvy_evt_handler/name_parser.py b/source_py3/python_toolbox/wx_tools/widgets/cute_window/bind_savvy_evt_handler/name_parser.py index ddef605a0..2c92a702c 100644 --- a/source_py3/python_toolbox/wx_tools/widgets/cute_window/bind_savvy_evt_handler/name_parser.py +++ b/source_py3/python_toolbox/wx_tools/widgets/cute_window/bind_savvy_evt_handler/name_parser.py @@ -24,7 +24,8 @@ class CaseStyleType(abc.ABCMeta): class BaseCaseStyle(metaclass=CaseStyleType): '''Base class for case styles.''' - @abc_tools.AbstractStaticMethod + @staticmethod + @abc.abstractmethod def parse(name): ''' Parse a name with the given convention into a tuple of "words". diff --git a/source_py3/test_python_toolbox/test_abc_tools/test_abstract_static_method.py b/source_py3/test_python_toolbox/test_abc_tools/test_abstract_whatever.py similarity index 85% rename from source_py3/test_python_toolbox/test_abc_tools/test_abstract_static_method.py rename to source_py3/test_python_toolbox/test_abc_tools/test_abstract_whatever.py index 8fd4e5d85..19a5c52aa 100644 --- a/source_py3/test_python_toolbox/test_abc_tools/test_abstract_static_method.py +++ b/source_py3/test_python_toolbox/test_abc_tools/test_abstract_whatever.py @@ -1,14 +1,12 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. -'''Testing module for `python_toolbox.abc_tools.AbstractStaticMethod`.''' - import sys import abc import nose -from python_toolbox.abc_tools import AbstractStaticMethod +from python_toolbox.abc_tools import abstract_whatever def test_instantiate_without_subclassing(): diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index a4c656ea7..bd1256b39 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -178,6 +178,9 @@ def test_ordered_on_long(self): assert tuple(reversed(d)) == next(zip(*tuple(d.items())[::-1])) + def test_index(self): + d = self.dict_type + class AbstractNotOrderedDictTestCase(AbstractDictTestCase): def test_not_ordered_dict_base_class(self): diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py index 24d7074f4..02bdfaa98 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_two_base_test_cases.py @@ -55,6 +55,24 @@ def test_del_key_error(self): with cute_testing.RaiseAssertor(KeyError): del d['woof'] + def test_cant_use_existing_value(self): + d = self.d_type(((1, 2), (3, 4), (5, 6))) + with cute_testing.RaiseAssertor(ValueError, text='same value'): + d['foo'] = 4 + assert len(d) == 3 + + def test_cant_use_unhashable_value(self): + d = self.d_type(((1, 2), (3, 4), (5, 6))) + with cute_testing.RaiseAssertor(TypeError, text='not hashable'): + d['foo'] = [] + assert len(d) == 3 + + + def test_change_existing_key(self): + d = self.d_type(((1, 2), (3, 4), (5, 6))) + d[3] = 'foo' + assert dict(d) == {1: 2, 3: 'foo', 5: 6} + assert dict(d.inverse) == {2: 1, 'foo': 3, 6: 5} class AbstractNotDoubleFrozenDictTestCase(AbstractFrozenDictTestCase, From 76ba03b3caf2e7090968591f716102fecea8c1a5 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 16 Aug 2016 21:52:19 +0300 Subject: [PATCH 105/112] - --- source_py3/python_toolbox/abc_tools.py | 2 +- source_py3/python_toolbox/cute_testing.py | 2 +- .../nifty_collections/nifty_dicts/abstract.py | 13 ++-- .../test_abc_tools/test_abstract_whatever.py | 63 ++++++++++++------- .../abstract_dict_test_case.py | 6 +- 5 files changed, 50 insertions(+), 36 deletions(-) diff --git a/source_py3/python_toolbox/abc_tools.py b/source_py3/python_toolbox/abc_tools.py index 964dbb79d..805e8523e 100644 --- a/source_py3/python_toolbox/abc_tools.py +++ b/source_py3/python_toolbox/abc_tools.py @@ -6,6 +6,6 @@ import abc -def abstract_whatever(): +def abstract_whatever(_=None): return abc.abstractmethod(lambda: None) \ No newline at end of file diff --git a/source_py3/python_toolbox/cute_testing.py b/source_py3/python_toolbox/cute_testing.py index 8b3d7aca7..a27a71811 100644 --- a/source_py3/python_toolbox/cute_testing.py +++ b/source_py3/python_toolbox/cute_testing.py @@ -32,7 +32,7 @@ class RaiseAssertor(context_management.ContextManager): ''' - def __init__(self, exception_type=Exception, text='', * + def __init__(self, exception_type=Exception, text='', *, assert_exact_type=False): ''' Construct the `RaiseAssertor`. diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index 30aa7cd9b..898f6730f 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -92,8 +92,11 @@ def __setitem__(self, key, value): self._assert_valid() try: existing_key = self.inverse[value] - except KeyError: - pass + except TypeError as hashing_error: + raise TypeError( + "%s is not hashable so it can't be used as a value in a " + "double-sided dict." % value + ) from hashing_error else: raise ValueError( "Can't add key %s with value %s because there is already a " @@ -101,12 +104,6 @@ def __setitem__(self, key, value): self.inverse[value]) ) - try: - hash(value) - except TypeError as hashing_error: - raise TypeError('%s is not hashable so it can\'t be used as a ' - 'value in a double-sided dict.' % value) from hashing_error - try: existing_value = self[key] except KeyError: diff --git a/source_py3/test_python_toolbox/test_abc_tools/test_abstract_whatever.py b/source_py3/test_python_toolbox/test_abc_tools/test_abstract_whatever.py index 19a5c52aa..67e64e69a 100644 --- a/source_py3/test_python_toolbox/test_abc_tools/test_abstract_whatever.py +++ b/source_py3/test_python_toolbox/test_abc_tools/test_abstract_whatever.py @@ -6,37 +6,52 @@ import nose +from python_toolbox import cute_testing + from python_toolbox.abc_tools import abstract_whatever -def test_instantiate_without_subclassing(): - '''Test you can't instantiate a class with an `AbstractStaticMethod`.''' +def test(): class A(metaclass=abc.ABCMeta): - @AbstractStaticMethod - def f(): - pass - - nose.tools.assert_raises(TypeError, lambda: A()) - + foo = abstract_whatever() -def test_override(): - ''' - Can't instantiate subclass that doesn't override `AbstractStaticMethod`. - ''' - - class B(metaclass=abc.ABCMeta): - @AbstractStaticMethod - def f(): - pass - - class C(B): - @staticmethod - def f(): - return 7 + @abstract_whatever + def bar(self): pass - c = C() + with cute_testing.RaiseAssertor(TypeError): + A() + + + class B(A): + pass + + with cute_testing.RaiseAssertor(TypeError): + B() + + + class C(A): + foo = 7 + + with cute_testing.RaiseAssertor(TypeError): + C() + + + class D(A): + foo = 7 + bar = 9 + + D() + + + class E(A): + def foo(self): pass + def bar(self): pass + + E() - assert C.f() == c.f() == 7 + + + \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py index aaf519060..43763a84c 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_dict_test_case.py @@ -2,11 +2,13 @@ # This program is distributed under the MIT license. import collections.abc +import abc from python_toolbox.third_party import unittest2 import nose +from python_toolbox import abc_tools from python_toolbox import sequence_tools from python_toolbox import logic_tools from python_toolbox import cute_iter_tools @@ -20,9 +22,9 @@ ) -class AbstractDictTestCase(cute_testing.TestCase): +class AbstractDictTestCase(cute_testing.TestCase, metaclass=abc.ABCMeta): __test__ = False - d_type = None # Filled in by subclasses + d_type = abc_tools.abstract_whatever() def test_mapping_base_class(self): assert issubclass(self.d_type, collections.Mapping) From db8cf03e5452a08b777cff6599d9bf1b08fa7aee Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 16 Aug 2016 21:54:12 +0300 Subject: [PATCH 106/112] - --- .../python_toolbox/nifty_collections/nifty_dicts/abstract.py | 2 ++ .../test_nifty_dicts/abstract_one_base_test_cases.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py index 898f6730f..791921411 100644 --- a/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/nifty_dicts/abstract.py @@ -92,6 +92,8 @@ def __setitem__(self, key, value): self._assert_valid() try: existing_key = self.inverse[value] + except KeyError: + pass except TypeError as hashing_error: raise TypeError( "%s is not hashable so it can't be used as a value in a " diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py index bd1256b39..8715bc7bf 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_nifty_dicts/abstract_one_base_test_cases.py @@ -179,7 +179,10 @@ def test_ordered_on_long(self): assert tuple(reversed(d)) == next(zip(*tuple(d.items())[::-1])) def test_index(self): - d = self.dict_type + d = self.d_type(((1, 2), (3, 4))) + assert d.index(3) + with cute_testing.RaiseAssertor(ValueError): + d.index('foo') class AbstractNotOrderedDictTestCase(AbstractDictTestCase): From 43658bdc3931158be9fe2813b6ad635bfd4de2d1 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 16 Aug 2016 21:57:55 +0300 Subject: [PATCH 107/112] - --- source_py3/python_toolbox/nifty_collections/abstract.py | 3 +-- .../python_toolbox/nifty_collections/various_ordered_sets.py | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/abstract.py b/source_py3/python_toolbox/nifty_collections/abstract.py index 9a99b95d0..14692d785 100644 --- a/source_py3/python_toolbox/nifty_collections/abstract.py +++ b/source_py3/python_toolbox/nifty_collections/abstract.py @@ -48,8 +48,7 @@ class DefinitelyUnordered(metaclass=abc.ABCMeta): @classmethod def __subclasshook__(cls, type_): - if cls is DefinitelyUnordered and \ - issubclass(type_, collections.OrderedDict): + if cls is DefinitelyUnordered and issubclass(type_, Ordered): return False else: return NotImplemented diff --git a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py index f1f64fe5f..f7943c4bd 100644 --- a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py +++ b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py @@ -10,12 +10,15 @@ from python_toolbox import caching from python_toolbox import freezing +from . import abstract + KEY, PREV, NEXT = range(3) -class BaseOrderedSet(collections.abc.Set, collections.abc.Sequence): +class BaseOrderedSet(collections.abc.Set, collections.abc.Sequence, + abstract.Ordered): ''' Base class for `OrderedSet` and `FrozenOrderedSet`, i.e. set with an order. From 2e23df949a31413c1a671cbd3822c505a648aa20 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 16 Aug 2016 22:28:30 +0300 Subject: [PATCH 108/112] - --- .../nifty_collections/various_ordered_sets.py | 47 ++++++++++++++++--- .../test_various_ordered_sets.py | 27 +++++++++++ .../test_canonical_slice.py | 22 +++++---- 3 files changed, 80 insertions(+), 16 deletions(-) diff --git a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py index f7943c4bd..d8276cb09 100644 --- a/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py +++ b/source_py3/python_toolbox/nifty_collections/various_ordered_sets.py @@ -32,12 +32,36 @@ def __init__(self, iterable=()): self.__add(item) def __getitem__(self, index): - for i, item in enumerate(self): - if i == index: - return item + our_length = len(self) + if isinstance(index, int): + if index < 0: + index += our_length + if not 0 <= index < our_length: + raise IndexError + for i, item in enumerate(self): + if i == index: + return item + else: + raise RuntimeError + elif isinstance(index, slice): + from python_toolbox import sequence_tools + canonical_slice = sequence_tools.CanonicalSlice(index, self) + if canonical_slice.step > 0 or canonical_slice.step is None: + return type(self)(itertools.islice(self, *canonical_slice)) + else: + reversed_slice = ( + (None if canonical_slice.start is None else + our_length - 1 - canonical_slice.start), + (None if canonical_slice.stop is None else + our_length - 1 - canonical_slice.stop), + -canonical_slice.step + ) + return type(self)(itertools.islice(reversed(self), + *reversed_slice)) + else: - raise IndexError - + raise TypeError + def __len__(self): return len(self._map) @@ -239,4 +263,15 @@ def __eq__(self, other): def get_without_emitter(self): '''Get a version of this ordered set without an emitter attached.''' return OrderedSet(self) - \ No newline at end of file + + def __getitem__(self, index): + if isinstance(index, slice): + raise Exception( + "We won't let you slice an `EmittingOrderedSet` because we " + "can't be sure whether you'll want it to use the same " + "emitter." + ) + else: + return super().__getitem__(index) + + \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_nifty_collections/test_various_ordered_sets.py b/source_py3/test_python_toolbox/test_nifty_collections/test_various_ordered_sets.py index cb4ddb493..beb797bd4 100644 --- a/source_py3/test_python_toolbox/test_nifty_collections/test_various_ordered_sets.py +++ b/source_py3/test_python_toolbox/test_nifty_collections/test_various_ordered_sets.py @@ -24,6 +24,33 @@ def test_bool(self): assert bool(self.ordered_set_type({0})) is True assert bool(self.ordered_set_type(range(5))) is True + def test_getitem(self): + s = self.ordered_set_type('abc') + assert s[0] == 'a' + assert s[1] == 'b' + assert s[2] == 'c' + assert s[-1] == 'c' + assert s[-2] == 'b' + assert s[-3] == 'a' + with cute_testing.RaiseAssertor(IndexError): + s[3] + with cute_testing.RaiseAssertor(IndexError): + s[-4] + with cute_testing.RaiseAssertor(IndexError): + s[300] + with cute_testing.RaiseAssertor(TypeError): + s['foo'] + if not issubclass(self.ordered_set_type, EmittingOrderedSet): + assert self.ordered_set_type(range(10))[7:2:-2] == \ + self.ordered_set_type([7, 5, 3]) + assert s[:] == s + assert s[:] is not s + assert s[:-1] == self.ordered_set_type('ab') + assert s[1:-1] == self.ordered_set_type('b') + assert s[-1:1:-1] == self.ordered_set_type('c') + assert s[::2] == self.ordered_set_type('ac') + assert s[::-2] == self.ordered_set_type('ca') + class BaseMutableOrderedSetTestCase(BaseOrderedSetTestCase): __test__ = False diff --git a/source_py3/test_python_toolbox/test_sequence_tools/test_canonical_slice.py b/source_py3/test_python_toolbox/test_sequence_tools/test_canonical_slice.py index 7082c5ec4..ff2c07b5f 100644 --- a/source_py3/test_python_toolbox/test_sequence_tools/test_canonical_slice.py +++ b/source_py3/test_python_toolbox/test_sequence_tools/test_canonical_slice.py @@ -23,20 +23,22 @@ def test(): slice(None, None, -2)] for slice_ in slices: - canonical_slice = CanonicalSlice(slice_) + generic_canonical_slice = CanonicalSlice(slice_) # Replacing `infinity` with huge number cause Python's lists can't # handle `infinity`: - if abs(canonical_slice.start) == infinity: - start = 10**10 * math_tools.get_sign(canonical_slice.start) - if abs(canonical_slice.stop) == infinity: - stop = 10**10 * math_tools.get_sign(canonical_slice.stop) - if abs(canonical_slice.step) == infinity: - step = 10**10 * math_tools.get_sign(canonical_slice.step) + if abs(generic_canonical_slice.start) == infinity: + start = 10**10 * math_tools.get_sign(generic_canonical_slice.start) + if abs(generic_canonical_slice.stop) == infinity: + stop = 10**10 * math_tools.get_sign(generic_canonical_slice.stop) + if abs(generic_canonical_slice.step) == infinity: + step = 10**10 * math_tools.get_sign(generic_canonical_slice.step) ####################################################################### - assert [canonical_slice.start, canonical_slice.stop, - canonical_slice.step].count(None) == 0 + assert [generic_canonical_slice.start, generic_canonical_slice.stop, + generic_canonical_slice.step].count(None) == 0 for range_ in ranges: - assert range_[slice_] == range_[canonical_slice.slice_] \ No newline at end of file + canonical_slice = CanonicalSlice(slice_, range_) + assert (range_[slice_] == range_[generic_canonical_slice.slice_] == + range_[canonical_slice.slice_]) \ No newline at end of file From 76da330a4d81f5d4aa9c53c4e432b294e71af7c7 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 31 Aug 2016 23:59:52 +0300 Subject: [PATCH 109/112] - --- source_py3/python_toolbox/future_tools.py | 22 +++++++++++++++++++ .../test_future_tools/test_future_tools.py | 19 ++++++++++++++-- .../test_sequence_tools/test_partitions.py | 8 +++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/source_py3/python_toolbox/future_tools.py b/source_py3/python_toolbox/future_tools.py index bbca84c49..faabbae0b 100644 --- a/source_py3/python_toolbox/future_tools.py +++ b/source_py3/python_toolbox/future_tools.py @@ -126,3 +126,25 @@ class CuteProcessPoolExecutor(concurrent.futures.ProcessPoolExecutor, computed, and not the order in which they were submitted. ''' + +class CuteDummyExecutor(BaseCuteExecutor): + ''' + A dummy executor that executes commands synchronously. + + This is a degenerate executor that simply executes all commands that are + fed to it synchronously and immediately, in the same thread as the one it + was called from, as if you didn't use an executor at all. + ''' + + def submit(self, fn, *args, **kwargs): + future = concurrent.futures.Future() + + try: + result = fn(*args, **kwargs) + except BaseException as exception: + future.set_exception(exception) + else: + future.set_result(result) + + return future + \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_future_tools/test_future_tools.py b/source_py3/test_python_toolbox/test_future_tools/test_future_tools.py index 2e4a0da93..467a84392 100644 --- a/source_py3/test_python_toolbox/test_future_tools/test_future_tools.py +++ b/source_py3/test_python_toolbox/test_future_tools/test_future_tools.py @@ -1,6 +1,8 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. +import os +import threading import concurrent.futures import time @@ -36,5 +38,18 @@ def sleep_and_return(seconds): as_completed=True)) == tuple(range(10)) - - \ No newline at end of file +def test_cute_dummy_executor(): + + def foo(i): + n = foo.n + foo.n += 1 + return (i, n, os.getpid(), threading.get_ident()) + foo.n = 0 + + with future_tools.CuteDummyExecutor() as executor: + results = tuple(executor.map(foo, range(5))) + zipped_results = tuple(zip(*results)) + assert zipped_results[0] == zipped_results[1] == tuple(range(5)) + assert len(set(zipped_results[2])) == 1 + assert len(set(zipped_results[3])) == 1 + diff --git a/source_py3/test_python_toolbox/test_sequence_tools/test_partitions.py b/source_py3/test_python_toolbox/test_sequence_tools/test_partitions.py index cd6346602..666d6b254 100644 --- a/source_py3/test_python_toolbox/test_sequence_tools/test_partitions.py +++ b/source_py3/test_python_toolbox/test_sequence_tools/test_partitions.py @@ -102,3 +102,11 @@ def test_fill_value(): assert partitions(r, 3, fill_value=None) == [[0, 1, 2], [3, 4, None]] assert partitions([], 3, fill_value=None) == [] + +def test_whatever(): + assert partitions( + 'blablablabadbfbadfb', partition_size=5, fill_value=0 + ) == [['b', 'l', 'a', 'b', 'l'], ['a', 'b', 'l', 'a', 'b'], + ['a', 'd', 'b', 'f', 'b'], ['a', 'd', 'f', 'b', 0]] + + \ No newline at end of file From bd372f922df9b99f08279cb55c36a41e1dd67e5f Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Thu, 1 Sep 2016 00:00:25 +0300 Subject: [PATCH 110/112] Remove PipeQueue, I don't think it's useful enough --- .../multiprocessing_tools/__init__.py | 2 - .../multiprocessing_tools/pipe_queueing.py | 21 ------- .../test_pipe_queue.py | 62 ------------------- 3 files changed, 85 deletions(-) delete mode 100644 source_py3/python_toolbox/multiprocessing_tools/pipe_queueing.py delete mode 100644 source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py diff --git a/source_py3/python_toolbox/multiprocessing_tools/__init__.py b/source_py3/python_toolbox/multiprocessing_tools/__init__.py index 4641879a9..39c1bbf18 100644 --- a/source_py3/python_toolbox/multiprocessing_tools/__init__.py +++ b/source_py3/python_toolbox/multiprocessing_tools/__init__.py @@ -1,4 +1,2 @@ # Copyright 2009-2017 Ram Rachum. # This program is distributed under the MIT license. - -from .pipe_queueing import PipeQueue \ No newline at end of file diff --git a/source_py3/python_toolbox/multiprocessing_tools/pipe_queueing.py b/source_py3/python_toolbox/multiprocessing_tools/pipe_queueing.py deleted file mode 100644 index da347cf65..000000000 --- a/source_py3/python_toolbox/multiprocessing_tools/pipe_queueing.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2009-2017 Ram Rachum. -# This program is distributed under the MIT license. - -import multiprocessing - -class PipeQueue: - def __init__(self): - self.queue = multiprocessing.Queue() - - def create_pipe(self): - master_connection, slave_connection = multiprocessing.Pipe() - self.queue.put(slave_connection) - return master_connection - - def wait_for_connection(self): - return self.queue.get() - - - - - \ No newline at end of file diff --git a/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py b/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py deleted file mode 100644 index d94466809..000000000 --- a/source_py3/test_python_toolbox/test_multiprocessing_tools/test_pipe_queue.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2009-2017 Ram Rachum. -# This program is distributed under the MIT license. - -import time -import multiprocessing - -from python_toolbox import multiprocessing_tools - -class Worker(multiprocessing.Process): - def __init__(self, pipe_queue): - multiprocessing.Process.__init__(self) - self.pipe_queue = pipe_queue - - def run(self): - connection = self.pipe_queue.wait_for_connection() - message = connection.recv() - assert message == 'Hello worker!' - connection.send('Why hello there master!') - message = connection.recv() - assert message == 'Please exit mister worker!' - - -def test(): - pipe_queue = multiprocessing_tools.PipeQueue() - workers = [Worker(pipe_queue) for _ in range(3)] - def get_n_alive_workers(): - return len([worker.is_alive() for worker in workers]) - def assert_n_live_workers(n): - for i in range(5): - if get_n_alive_workers() == n: - return - else: - time.sleep(i) - else: - raise AssertionError - - connection_1 = pipe_queue.create_pipe() - connection_1.send('Hello worker!') - for worker in workers: - worker.start() - assert_n_live_workers(3) - connection_2 = pipe_queue.create_pipe() - connection_2.send('Hello worker!') - message = connection_1.recv() - assert message == 'Why hello there master!' - message = connection_2.recv() - assert message == 'Why hello there master!' - assert_n_live_workers(3) - connection_1.send('Please exit mister worker!') - assert_n_live_workers(2) - connection_2.send('Please exit mister worker!') - connection_3 = pipe_queue.create_pipe() - assert_n_live_workers(1) - connection_3.send('Hello worker!') - assert_n_live_workers(1) - message = connection_3.recv() - assert message == 'Why hello there master!' - connection_3.send('Please exit mister worker!') - assert_n_live_workers(1) - - - \ No newline at end of file From f1a4e8311406fc23f1e0231398cafee0a1b4e7c3 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Thu, 1 Sep 2016 00:14:50 +0300 Subject: [PATCH 111/112] - --- .../nifty_collections/condition_list.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/source_py3/python_toolbox/nifty_collections/condition_list.py b/source_py3/python_toolbox/nifty_collections/condition_list.py index 46d985abd..5a67c519a 100644 --- a/source_py3/python_toolbox/nifty_collections/condition_list.py +++ b/source_py3/python_toolbox/nifty_collections/condition_list.py @@ -10,6 +10,30 @@ class ConditionList(collections.abc.MutableSequence, context_management.DelegatingContextManager): + ''' + Thread synchronization tool. + + This is a complex tool for synchronizing threads, so let me explain how it + works. + + `ConditionList` is similar to a Python `list`. You can add items to it, + view the items in it, change their order, remove items, etc. + + What makes it different than a normal `list` is that it provides methods + `wait_for` and `wait_for_missing` that let you orchestrate threads based on + this list. + + `wait_for` receives a list of items (or just one) and makes the thread + block until the `ConditionList` object contains all of these items, and + then the thread results. `wait_for_missing` is the opposite, waiting for a + specified list of items to *not* be in the `ConditionList`, and only then + does the thread resume. (See the docstrings for these functions for more + details about the arguments.) + + When is `ConditionList` useful? Maybe you have a thread that's + + blocktododoc + ''' def __init__(self, iterable=None): self.__list = list(iterable) if iterable is not None else [] From 9a9e1f4a3803a35104a95aaece430cd3ab8bfa5b Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sat, 3 Sep 2016 16:28:36 +0300 Subject: [PATCH 112/112] - --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index ffc21c7ee..46d5e331c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [nosetests] where=source_py2/test_python_toolbox py3where=source_py3/test_python_toolbox - + verbosity=3 detailed-errors=1