Revision | 0602b98481f568aadce84b8ff7c3ea581f202378 (tree) |
---|---|
Time | 2022-11-03 03:56:15 |
Author | Antoon Pardon <aej@pard...> |
Commiter | Antoon Pardon |
Threaders module, comm2 iterator and copy_defaults correction merged in.
@@ -0,0 +1,4 @@ | ||
1 | +#!/bin/bash | |
2 | + | |
3 | +pushd tests > /dev/null; make clean; popd > /dev/null | |
4 | +pushd source > /dev/null; make clean; popd > /dev/null |
@@ -1,47 +1,58 @@ | ||
1 | 1 | |
2 | 2 | from inspect import signature as signature_of, Parameter |
3 | 3 | from functools import wraps |
4 | -from copy import copy | |
4 | +from copy import deepcopy | |
5 | + | |
5 | 6 | default = object() |
6 | -'\nThis value kan be passed as an argument, where the functions provides a default value.\nIf you used the pass_defaults decorator, the function will use its default for that\nparameter\n' | |
7 | +''' | |
8 | +This value kan be passed as an argument, where the functions provides a default value. | |
9 | +If you used the pass_defaults decorator, the function will use its default for that | |
10 | +parameter | |
11 | +''' | |
7 | 12 | |
8 | 13 | def copy_defaults(func): |
9 | - '\n\tThis decorator makes that defaults values are copied on a call.\n\t' | |
10 | - signature = signature_of(func) | |
11 | - parameter_items = list(signature.parameters.items()) | |
14 | + ''' | |
15 | + This decorator makes that defaults values are copied on a call. | |
16 | + ''' | |
17 | + signature = signature_of(func) | |
18 | + parameter_items = list(signature.parameters.items()) | |
12 | 19 | |
13 | - @wraps(func) | |
14 | - def wrapper(*args, **kwds): | |
15 | - newargs = list(args) | |
16 | - tail_parameters = parameter_items[len(args):] | |
17 | - for (name, parameter) in tail_parameters: | |
18 | - try: | |
19 | - newargs.append(kwds[name]) | |
20 | - del kwds[name] | |
21 | - except KeyError: | |
22 | - if (parameter.default is not Parameter.empty): | |
23 | - newargs.append(copy(parameter.default)) | |
24 | - else: | |
25 | - break | |
26 | - return func(*newargs, **kwds) | |
27 | - return wrapper | |
20 | + @wraps(func) | |
21 | + def wrapper(*args, **kwds): | |
22 | + newargs = list(args) | |
23 | + tail_parameters = parameter_items[len(args):] | |
24 | + for name, parameter in tail_parameters: | |
25 | + try: | |
26 | + newargs.append(kwds[name]) | |
27 | + del kwds[name] | |
28 | + except KeyError: | |
29 | + if parameter.default is not Parameter.empty: | |
30 | + newargs.append(deepcopy(parameter.default)) | |
31 | + else: | |
32 | + #return func(*newargs, **kwds) | |
33 | + break | |
34 | + return func(*newargs, **kwds) | |
35 | + | |
36 | + return wrapper | |
28 | 37 | |
29 | 38 | def pass_defaults(func): |
30 | - '\n\tThis decorator allows to pass a specific "default" value, to\n\thave the function use the default value\n\t' | |
31 | - signature = signature_of(func) | |
32 | - parameter_items = list(signature.parameters.items()) | |
39 | + ''' | |
40 | + This decorator allows to pass a specific "default" value, to | |
41 | + have the function use the default value | |
42 | + ''' | |
43 | + signature = signature_of(func) | |
44 | + parameter_items = list(signature.parameters.items()) | |
33 | 45 | |
34 | - @wraps(func) | |
35 | - def wrapper(*args, **kwds): | |
36 | - newargs = [] | |
37 | - for (arg, item) in zip(args, parameter_items): | |
38 | - parameter = item[1] | |
39 | - if (arg is not default): | |
40 | - newargs.append(arg) | |
41 | - elif (parameter.default is not Parameter.empty): | |
42 | - newargs.append(parameter.default) | |
43 | - else: | |
44 | - raise ValueError('"default" used as argument when no default is available') | |
45 | - return func(*newargs, **kwds) | |
46 | - return wrapper | |
47 | - | |
46 | + @wraps(func) | |
47 | + def wrapper(*args, **kwds): | |
48 | + newargs = [] | |
49 | + for (arg, item) in zip(args, parameter_items): | |
50 | + parameter = item[1] | |
51 | + if (arg is not default): | |
52 | + newargs.append(arg) | |
53 | + elif (parameter.default is not Parameter.empty): | |
54 | + newargs.append(parameter.default) | |
55 | + else: | |
56 | + raise ValueError('"default" used as argument when no default is available') | |
57 | + return func(*newargs, **kwds) | |
58 | + return wrapper |
@@ -1,69 +1,148 @@ | ||
1 | +# Copyright © 2017-2022 Antoon Pardon | |
2 | +# | |
3 | +# Permission is hereby granted, free of charge, to any person obtaining a | |
4 | +# copy of this software and associated documentation files (the "Software"), | |
5 | +# to deal in the Software without restriction, including without limitation | |
6 | +# the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
7 | +# and/or sell copies of the Software, and to permit persons to whom the | |
8 | +# Software is furnished to do so, subject to the following conditions: | |
9 | +# | |
10 | +# The above copyright notice and this permission notice shall be included | |
11 | +# in all copies or substantial portions of the Software. | |
12 | +# | |
13 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
14 | +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
15 | +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
16 | +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
17 | +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
18 | +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
19 | +# | |
20 | +'''\ | |
21 | +This module contains a number of useful iterators. | |
1 | 22 | |
2 | -"This module contains a number of useful iterators.\n\nThe first family of\niterators are the classes of pipe iterators. These can be strung together\nin a pipe line, just as with the unix '|' operator.\n" | |
23 | +The first family of | |
24 | +iterators are the classes of pipe iterators. These can be strung together | |
25 | +in a pipe line, just as with the unix '|' operator. | |
26 | +''' | |
3 | 27 | __pdoc__ = {} |
4 | 28 | from functools import partial |
5 | 29 | from itertools import chain, filterfalse |
6 | 30 | _INVALID = object() |
7 | 31 | |
8 | 32 | class PipeIterator(): |
9 | - "\n\tBase class for constructing iterators that work like a pipe in the\n\tunix shell and work with the '|' operator. The '>>' operator in\n\tcorrespondece with C++ can also be used to get the same effect.\n\n\tThe use is mainly in the ease with which these iterators can be combined\n\n\tThis class can also be used as a decorator to turn a function/generator\n\tinto a pipe iterator.\n\t" | |
33 | + """ | |
34 | + Base class for constructing iterators that work like a pipe in the | |
35 | + unix shell and work with the '|' operator. The '>>' operator in | |
36 | + correspondece with C++ can also be used to get the same effect. | |
10 | 37 | |
11 | - def __init__(self, function): | |
12 | - self.apply = function | |
38 | + The use is mainly in the ease with which these iterators can be combined | |
13 | 39 | |
14 | - def _pipe(self, iterator): | |
15 | - return self.apply(iterator) | |
16 | - __ror__ = _pipe | |
17 | - __rrshift__ = _pipe | |
40 | + This class can also be used as a decorator to turn a function/generator | |
41 | + into a pipe iterator. | |
42 | + """ | |
43 | + | |
44 | + def __init__(self, function): | |
45 | + self.apply = function | |
46 | + | |
47 | + def _pipe(self, iterator): | |
48 | + return self.apply(iterator) | |
49 | + __ror__ = _pipe | |
50 | + __rrshift__ = _pipe | |
51 | + | |
52 | + | |
18 | 53 | Apply = PipeIterator |
19 | 54 | __pdoc__['Apply'] = False |
20 | 55 | |
21 | 56 | class Map(PipeIterator): |
22 | - '\n\tThis class produces a pipe iterator that will turn an iterator into a new\n\titerator where the new items are the result of applying the function.\n\n\tdouble = partial(operator.__mul__, 2) \n\t[3, 5, 8] | Map(double) --> 6, 10, 16\n\t' | |
57 | + ''' | |
58 | + This class produces a pipe iterator that will turn an iterator into a new | |
59 | + iterator where the new items are the result of applying the function. | |
23 | 60 | |
24 | - def __init__(self, function): | |
25 | - super().__init__(partial(map, function)) | |
61 | + double = partial(operator.__mul__, 2) | |
62 | + [3, 5, 8] | Map(double) --> 6, 10, 16 | |
63 | + ''' | |
64 | + | |
65 | + def __init__(self, function): | |
66 | + super().__init__(partial(map, function)) | |
26 | 67 | |
27 | 68 | class SubMap(Map): |
28 | - '\n\tThis class produces a pipe iterator that will turn an iterable of iterables\n\tinto a new iterables of iterables where the new items are the result of applying the function.\n\n\tdouble = partial(operator.__mul__, 2) \n\t[[3, 5, 8], [7, 4], [2]] | SubMap(double) --> [6, 10, 16], [14, 8], [4]\n\t' | |
69 | + ''' | |
70 | + This class produces a pipe iterator that will turn an iterable of iterables | |
71 | + into a new iterables of iterables where the new items are the result of applying the function.\n | |
72 | + double = partial(operator.__mul__, 2) | |
73 | + [[3, 5, 8], [7, 4], [2]] | SubMap(double) --> [6, 10, 16], [14, 8], [4] | |
74 | + ''' | |
29 | 75 | |
30 | - def __init__(self, function): | |
31 | - super().__init__(partial(map, function)) | |
76 | + def __init__(self, function): | |
77 | + super().__init__(partial(map, function)) | |
32 | 78 | concat = Apply(chain.from_iterable) |
33 | 79 | |
34 | 80 | class Select(PipeIterator): |
35 | - '\n\tThis class produces a pipe iterator that only lets items through that are\n\tselected through the condition.\n\n\t[21, 13, 8, 5, 3, 2] >> Select(is_odd) --> 21 13 5 3\n\t' | |
81 | + ''' | |
82 | + This class produces a pipe iterator that only lets items through that are | |
83 | + selected through the condition.\n | |
84 | + [21, 13, 8, 5, 3, 2] >> Select(is_odd) --> 21 13 5 3 | |
85 | + ''' | |
36 | 86 | |
37 | - def __init__(self, condition): | |
38 | - super().__init__(partial(filter, condition)) | |
87 | + def __init__(self, condition): | |
88 | + super().__init__(partial(filter, condition)) | |
39 | 89 | |
40 | 90 | class Discard(PipeIterator): |
41 | - '\n\tThis class produces a pipe iterator that removes items that are\n\tfiltered out through the condition.\n\n\t[21, 13, 8, 5, 3, 2] >> Discard(is_odd) --> 8 2\n\t' | |
91 | + ''' | |
92 | + This class produces a pipe iterator that removes items that are | |
93 | + filtered out through the condition.\n | |
94 | + [21, 13, 8, 5, 3, 2] >> Discard(is_odd) --> 8 2 | |
95 | + ''' | |
42 | 96 | |
43 | - def __init__(self, condition): | |
44 | - super().__init__(partial(filterfalse, condition)) | |
97 | + def __init__(self, condition): | |
98 | + super().__init__(partial(filterfalse, condition)) | |
45 | 99 | |
46 | 100 | class Peeker(): |
47 | - '\n\tThis class allows to peek ahead in an iterator\n\t' | |
48 | - | |
49 | - def __init__(self, iterable): | |
50 | - self.iterator = iter(iterable) | |
51 | - self.next = _INVALID | |
52 | - | |
53 | - def __iter__(self): | |
54 | - return self | |
101 | + ''' | |
102 | + This class allows to peek ahead in an iterator | |
103 | + ''' | |
55 | 104 | |
56 | - def __next__(self): | |
57 | - if (self.next is _INVALID): | |
58 | - next_item = next(self.iterator) | |
59 | - else: | |
60 | - next_item = self.next | |
61 | - self.next = _INVALID | |
62 | - return next_item | |
105 | + def __init__(self, iterable): | |
106 | + self.iterator = iter(iterable) | |
107 | + self.next = _INVALID | |
63 | 108 | |
64 | - def peek(self): | |
65 | - '\n\t\tThis method looks one item ahead in the iterator\n\t\t' | |
66 | - if (self.next is _INVALID): | |
67 | - self.next = next(self.iterator) | |
68 | - return self.next | |
109 | + def __iter__(self): | |
110 | + return self | |
69 | 111 | |
112 | + def __next__(self): | |
113 | + if (self.next is _INVALID): | |
114 | + next_item = next(self.iterator) | |
115 | + else: | |
116 | + next_item = self.next | |
117 | + self.next = _INVALID | |
118 | + return next_item | |
119 | + | |
120 | + def peek(self): | |
121 | + """ | |
122 | + This method looks one item ahead in the iterator | |
123 | + """ | |
124 | + if self.next is _INVALID: | |
125 | + self.next = next(self.iterator) | |
126 | + return self.next | |
127 | + | |
128 | +def comm2(it1, it2): | |
129 | + """ | |
130 | + This function behave a bit like the unix command comm. | |
131 | + It takes two sorted iterators and produces an iterator of tuples. | |
132 | + """ | |
133 | + top = 1,None | |
134 | + it1 = ((0, item) for item in it1) | |
135 | + it2 = ((0, item) for item in it2) | |
136 | + item1 = next(it1, top) | |
137 | + item2 = next(it2, top) | |
138 | + while (item1, item2) != (top, top): | |
139 | + if item1 < item2: | |
140 | + yield item1[1], None | |
141 | + item1 = next(it1, top) | |
142 | + elif item1 > item2: | |
143 | + yield None, item2[1] | |
144 | + item2 = next(it2, top) | |
145 | + else: | |
146 | + yield item1[1], item2[1] | |
147 | + item1 = next(it1, top) | |
148 | + item2 = next(it2, top) |
@@ -1,3 +1,3 @@ | ||
1 | -version = '0.01.12a-untyped' | |
2 | -modified = '2022-07-23 @ 17:16:36' | |
1 | +version = '0.01.14a-untyped' | |
2 | +modified = '2022-10-25 @ 19:07:31' | |
3 | 3 | installed = '*********************' |
@@ -0,0 +1,71 @@ | ||
1 | +""" | |
2 | + This module provides an alternative for the threading module in | |
3 | + the standard library | |
4 | +""" | |
5 | +from _thread import start_new_thread, get_ident, allocate_lock | |
6 | +from typing import Callable, Set, Any | |
7 | +from itertools import count | |
8 | + | |
9 | +from enum import Enum | |
10 | + | |
11 | +class TaskState (Enum): # pylint: disable=missing-class-docstring | |
12 | + Ready = 1 | |
13 | + Active = 2 | |
14 | + Done = 3 | |
15 | + | |
16 | +_TaskCounter = count() | |
17 | + | |
18 | +class Task: | |
19 | + """ | |
20 | + This class initiates a Task that can be started later for | |
21 | + assynchronized work | |
22 | + """ | |
23 | + # pylint: disable=too-many-instance-attributes | |
24 | + def __init__(self, func: Callable, *args, **kwds): | |
25 | + self.func = func | |
26 | + self.args = args | |
27 | + self.kwds = kwds | |
28 | + self.state = TaskState.Ready | |
29 | + self.tid = None | |
30 | + self.iid = next(_TaskCounter) | |
31 | + self.held_resources : Set[Any] = set() | |
32 | + self.joiner = None | |
33 | + | |
34 | + def start(self): # pylint: disable=missing-function-docstring | |
35 | + self.state = TaskState.Active | |
36 | + self.joiner = allocate_lock() | |
37 | + self.joiner.acquire() | |
38 | + start_new_thread(self._startup, ()) | |
39 | + | |
40 | + def throw(self, exception, *args): # pylint: disable=missing-function-docstring | |
41 | + pass | |
42 | + | |
43 | + def join(self): | |
44 | + """ | |
45 | + This function allows one task to synchronise with an other. | |
46 | + The task calling this method, will be blocked until the | |
47 | + task whose join method was called is terminated | |
48 | + """ | |
49 | + if self.state is TaskState.Ready: | |
50 | + raise RuntimeError("Can't join a Task that hasn't started yet") | |
51 | + if self.state is TaskState.Done: | |
52 | + return | |
53 | + joiner = self.joiner | |
54 | + joiner.acquire() | |
55 | + joiner.release() | |
56 | + | |
57 | + def _startup(self): | |
58 | + # pylint: disable=broad-except | |
59 | + try: | |
60 | + self.tid = get_ident() | |
61 | + self.func(*self.args, **self.kwds) | |
62 | + except Exception: | |
63 | + print("Something went wrong") | |
64 | + finally: | |
65 | + self.state = TaskState.Done | |
66 | + self.tid = None | |
67 | + self.joiner.release() | |
68 | + for resource in self.held_resources: | |
69 | + for attrname in ['close', 'release']: | |
70 | + if (release := getattr(resource, attrname, None)) is not None: | |
71 | + release() |
@@ -1,137 +1,192 @@ | ||
1 | 1 | |
2 | -from unittest import TestCase, main as unitmain | |
3 | -from random import shuffle | |
2 | +# pylint: disable=missing-function-docstring,invalid-name,missing-module-docstring | |
3 | + | |
4 | +from unittest import TestCase, main as unitmain #, skip | |
5 | +from random import shuffle, randint | |
4 | 6 | from itertools import product |
7 | +from operator import itemgetter | |
8 | + | |
9 | +#from typing import List | |
10 | + | |
11 | +# pylint: disable=unused-import | |
5 | 12 | from AGPlib.iterators import PipeIterator, Apply, Map, concat, SubMap, Select, Discard, Peeker |
13 | +from AGPlib.iterators import comm2 | |
14 | +# pylint: enable=unused-import | |
15 | + | |
6 | 16 | SIZE_LIMIT = 101 |
7 | 17 | |
8 | 18 | def shuffled_range_list(*args): |
9 | - step = 1 | |
10 | - start = 0 | |
11 | - if (len(args) == 1): | |
12 | - stop = args[0] | |
13 | - elif (len(args) == 2): | |
14 | - (start, stop) = args | |
15 | - else: | |
16 | - (start, stop, step) = args | |
17 | - lst = list(range(start, stop, step)) | |
18 | - shuffle(lst) | |
19 | - return lst | |
19 | + step = 1 | |
20 | + start = 0 | |
21 | + if (len(args) == 1): | |
22 | + stop = args[0] | |
23 | + elif (len(args) == 2): | |
24 | + (start, stop) = args | |
25 | + else: | |
26 | + (start, stop, step) = args | |
27 | + lst = list(range(start, stop, step)) | |
28 | + shuffle(lst) | |
29 | + return lst | |
20 | 30 | |
21 | 31 | def cube(nr): |
22 | - return ((nr * nr) * nr) | |
32 | + return ((nr * nr) * nr) | |
23 | 33 | |
24 | 34 | def square(nr): |
25 | - return (nr * nr) | |
35 | + return (nr * nr) | |
26 | 36 | |
27 | 37 | def is_odd(nr): |
28 | - return bool((nr % 2)) | |
38 | + return bool((nr % 2)) | |
29 | 39 | |
30 | 40 | def is_larger_than_50(nr): |
31 | - return (nr > 50) | |
41 | + return (nr > 50) | |
32 | 42 | |
33 | 43 | def collatz(nr): |
34 | - return (((3 * nr) + 1) if is_odd(nr) else (nr // 2)) | |
44 | + return (((3 * nr) + 1) if is_odd(nr) else (nr // 2)) | |
35 | 45 | |
36 | 46 | def concater(iterable): |
37 | - for sub_iterable in iterable: | |
38 | - (yield from sub_iterable) | |
47 | + for sub_iterable in iterable: | |
48 | + yield from sub_iterable | |
49 | + | |
50 | +def rndrange(*args): | |
51 | + for i in range(*args): | |
52 | + if randint(0, 1) == 1: | |
53 | + yield i | |
54 | + | |
55 | +# pylint: disable=no-self-argument | |
56 | + | |
39 | 57 | List = Apply(list) |
40 | 58 | |
41 | 59 | class PipeCase(TestCase): |
42 | - '\n\t\tclass for testing the PipeIterators\n\t' | |
43 | - | |
44 | - def test_apply(s): | |
45 | - for size in range(SIZE_LIMIT): | |
46 | - source = shuffled_range_list(size) | |
47 | - for function in (list, sum): | |
48 | - Function = Apply(function) | |
49 | - result1 = function(source) | |
50 | - result2 = (source | Function) | |
51 | - result3 = (source >> Function) | |
52 | - s.assertEqual(result1, result2) | |
53 | - s.assertEqual(result1, result3) | |
54 | - | |
55 | - def test_map(s): | |
56 | - for size in range(SIZE_LIMIT): | |
57 | - source = shuffled_range_list(size) | |
58 | - for function in (square, cube, is_odd, collatz): | |
59 | - result0 = list(map(function, source)) | |
60 | - result1 = ((source | Map(function)) | Apply(list)) | |
61 | - s.assertEqual(result0, result1) | |
62 | - result2 = ((source >> Map(function)) >> Apply(list)) | |
63 | - s.assertEqual(result0, result2) | |
60 | + ''' | |
61 | + class for testing the PipeIterators | |
62 | + ''' | |
64 | 63 | |
65 | - def test_concat(s): | |
66 | - for (list_size, sub_size) in product(range(0, SIZE_LIMIT, 5), range(0, SIZE_LIMIT, 4)): | |
67 | - source_list = [shuffled_range_list(sub_size) for _ in range(list_size)] | |
68 | - result0 = list(concater(source_list)) | |
69 | - result1 = ((source_list | concat) | List) | |
70 | - s.assertEqual(result0, result1) | |
71 | - result2 = ((source_list >> concat) >> List) | |
72 | - s.assertEqual(result0, result2) | |
73 | - | |
74 | - def test_submap(s): | |
75 | - for (list_size, sub_size) in product(range(0, SIZE_LIMIT, 5), range(0, SIZE_LIMIT, 4)): | |
76 | - source_list = [shuffled_range_list(sub_size) for _ in range(list_size)] | |
77 | - for function in (square, cube, is_odd, collatz): | |
78 | - result0 = [list(map(function, lst)) for lst in source_list] | |
79 | - result1 = (((source_list | SubMap(function)) | Map(list)) | List) | |
80 | - result2 = (((source_list >> SubMap(function)) >> Map(list)) >> List) | |
81 | - s.assertEqual(result0, result1) | |
82 | - s.assertEqual(result0, result2) | |
64 | + def test_apply(s): | |
65 | + for size in range(SIZE_LIMIT): | |
66 | + source = shuffled_range_list(size) | |
67 | + for function in (list, sum): | |
68 | + Function = Apply(function) | |
69 | + result1 = function(source) | |
70 | + result2 = (source | Function) | |
71 | + result3 = (source >> Function) | |
72 | + s.assertEqual(result1, result2) | |
73 | + s.assertEqual(result1, result3) | |
83 | 74 | |
84 | - def test_select(s): | |
85 | - for size in range(SIZE_LIMIT): | |
86 | - source = shuffled_range_list(size) | |
87 | - for condition in [is_odd, is_larger_than_50]: | |
88 | - result0 = [nr for nr in source if condition(nr)] | |
89 | - result1 = ((source | Select(condition)) | List) | |
90 | - result2 = ((source >> Select(condition)) >> List) | |
91 | - s.assertEqual(result0, result1) | |
92 | - s.assertEqual(result0, result2) | |
75 | + def test_map(s): | |
76 | + for size in range(SIZE_LIMIT): | |
77 | + source = shuffled_range_list(size) | |
78 | + for function in (square, cube, is_odd, collatz): | |
79 | + result0 = list(map(function, source)) | |
80 | + result1 = ((source | Map(function)) | Apply(list)) | |
81 | + s.assertEqual(result0, result1) | |
82 | + result2 = ((source >> Map(function)) >> Apply(list)) | |
83 | + s.assertEqual(result0, result2) | |
93 | 84 | |
94 | - def test_discard(s): | |
95 | - for size in range(SIZE_LIMIT): | |
96 | - source = shuffled_range_list(size) | |
97 | - for condition in [is_odd, is_larger_than_50]: | |
98 | - result0 = [nr for nr in source if (not condition(nr))] | |
99 | - result1 = ((source | Discard(condition)) | List) | |
100 | - result2 = ((source >> Discard(condition)) >> List) | |
101 | - s.assertEqual(result0, result1) | |
102 | - s.assertEqual(result0, result2) | |
85 | + def test_concat(s): | |
86 | + for (list_size, sub_size) in product(range(0, SIZE_LIMIT, 5), range(0, SIZE_LIMIT, 4)): | |
87 | + source_list = [shuffled_range_list(sub_size) for _ in range(list_size)] | |
88 | + result0 = list(concater(source_list)) | |
89 | + result1 = ((source_list | concat) | List) | |
90 | + s.assertEqual(result0, result1) | |
91 | + result2 = ((source_list >> concat) >> List) | |
92 | + s.assertEqual(result0, result2) | |
93 | + | |
94 | + def test_submap(s): | |
95 | + for (list_size, sub_size) in product(range(0, SIZE_LIMIT, 5), range(0, SIZE_LIMIT, 4)): | |
96 | + source_list = [shuffled_range_list(sub_size) for _ in range(list_size)] | |
97 | + for function in (square, cube, is_odd, collatz): | |
98 | + result0 = [list(map(function, lst)) for lst in source_list] | |
99 | + result1 = (((source_list | SubMap(function)) | Map(list)) | List) | |
100 | + result2 = (((source_list >> SubMap(function)) >> Map(list)) >> List) | |
101 | + s.assertEqual(result0, result1) | |
102 | + s.assertEqual(result0, result2) | |
103 | + | |
104 | + def test_select(s): | |
105 | + for size in range(SIZE_LIMIT): | |
106 | + source = shuffled_range_list(size) | |
107 | + for condition in [is_odd, is_larger_than_50]: | |
108 | + result0 = [nr for nr in source if condition(nr)] | |
109 | + result1 = ((source | Select(condition)) | List) | |
110 | + result2 = ((source >> Select(condition)) >> List) | |
111 | + s.assertEqual(result0, result1) | |
112 | + s.assertEqual(result0, result2) | |
113 | + | |
114 | + def test_discard(s): | |
115 | + for size in range(SIZE_LIMIT): | |
116 | + source = shuffled_range_list(size) | |
117 | + for condition in [is_odd, is_larger_than_50]: | |
118 | + result0 = [nr for nr in source if (not condition(nr))] | |
119 | + result1 = ((source | Discard(condition)) | List) | |
120 | + result2 = ((source >> Discard(condition)) >> List) | |
121 | + s.assertEqual(result0, result1) | |
122 | + s.assertEqual(result0, result2) | |
103 | 123 | |
104 | 124 | class IterCase(TestCase): |
105 | - '\n\tTestCase for the other iterators\n\t' | |
106 | - | |
107 | - def test_empty_peeker(s): | |
108 | - with s.assertRaises(StopIteration): | |
109 | - Peeker([]).peek() | |
110 | - | |
111 | - def test_peeker_is_iterator(s): | |
112 | - for size in range(SIZE_LIMIT): | |
113 | - source = shuffled_range_list(size) | |
114 | - peeker = Peeker(source) | |
115 | - s.assertIs(peeker, iter(peeker)) | |
125 | + ''' | |
126 | + TestCase for the other iterators | |
127 | + ''' | |
116 | 128 | |
117 | - def test_peeker_iterates_over_its_source(s): | |
118 | - for size in range(SIZE_LIMIT): | |
119 | - source = shuffled_range_list(size) | |
120 | - peeker = Peeker(source) | |
121 | - for (item, peeked) in zip(source, peeker): | |
122 | - s.assertEqual(item, peeked) | |
129 | + def test_empty_peeker(s): | |
130 | + with s.assertRaises(StopIteration): | |
131 | + Peeker([]).peek() | |
123 | 132 | |
124 | - def test_peek_ahead(s): | |
125 | - for size in range(1, SIZE_LIMIT): | |
126 | - source = shuffled_range_list(size) | |
127 | - peeker = Peeker(source) | |
128 | - peeked = peeker.peek() | |
129 | - with s.assertRaises(StopIteration): | |
130 | - Peeker([]).peek() | |
131 | - index = 0 | |
132 | - for (index, item) in enumerate(peeker): | |
133 | - s.assertEqual(item, peeked) | |
134 | - peeked = peeker.peek() | |
135 | - s.assertEqual((index + 1), size) | |
133 | + def test_peeker_is_iterator(s): | |
134 | + for size in range(SIZE_LIMIT): | |
135 | + source = shuffled_range_list(size) | |
136 | + peeker = Peeker(source) | |
137 | + s.assertIs(peeker, iter(peeker)) | |
138 | + | |
139 | + def test_peeker_iterates_over_its_source(s): | |
140 | + for size in range(SIZE_LIMIT): | |
141 | + source = shuffled_range_list(size) | |
142 | + peeker = Peeker(source) | |
143 | + for (item, peeked) in zip(source, peeker): | |
144 | + s.assertEqual(item, peeked) | |
145 | + | |
146 | + def test_peek_ahead(s): | |
147 | + for size in range(1, SIZE_LIMIT): | |
148 | + source = shuffled_range_list(size) | |
149 | + peeker = Peeker(source) | |
150 | + peeked = peeker.peek() | |
151 | + with s.assertRaises(StopIteration): | |
152 | + Peeker([]).peek() | |
153 | + index = 0 | |
154 | + for (index, item) in enumerate(peeker): | |
155 | + s.assertEqual(item, peeked) | |
156 | + peeked = peeker.peek() | |
157 | + s.assertEqual((index + 1), size) | |
158 | + | |
159 | + def test_comm2_double_empty(s): | |
160 | + comm_lst = list(comm2([], [])) | |
161 | + s.assertEqual(comm_lst, []) | |
162 | + | |
163 | + def test_comm2_one_empty(s): | |
164 | + lst = list(rndrange(200)) | |
165 | + length = len(lst) | |
166 | + Nones = length * [None] | |
167 | + comm_lst1 = list(comm2(lst, [])) | |
168 | + projected0 = list(map(itemgetter(0), comm_lst1)) | |
169 | + projected1 = list(map(itemgetter(1), comm_lst1)) | |
170 | + s.assertEqual(lst, projected0) | |
171 | + s.assertEqual(Nones, projected1) | |
172 | + comm_lst2 = list(comm2([], lst)) | |
173 | + projected0 = list(map(itemgetter(0), comm_lst2)) | |
174 | + projected1 = list(map(itemgetter(1), comm_lst2)) | |
175 | + s.assertEqual(Nones, projected0) | |
176 | + s.assertEqual(lst, projected1) | |
177 | + | |
178 | + def test_comm2_double_filled(s): | |
179 | + itr1 = rndrange(1, 4096) | |
180 | + itr2 = rndrange(1, 4096) | |
181 | + prv = 0 | |
182 | + for item in comm2(itr1, itr2): | |
183 | + if item[0] is None: | |
184 | + nxt = item[1] | |
185 | + elif item[1] is None: | |
186 | + nxt = item[0] | |
187 | + else: | |
188 | + s.assertEqual(*item) | |
189 | + nxt = item[1] | |
190 | + s.assertLess(prv, nxt) | |
191 | + | |
136 | 192 | unitmain() |
137 | - |
@@ -0,0 +1,63 @@ | ||
1 | + | |
2 | +# pylint: disable=missing-function-docstring,invalid-name,missing-module-docstring | |
3 | + | |
4 | +from unittest import TestCase, main as unitmain #, skip | |
5 | +from time import sleep | |
6 | + | |
7 | +# pylint: disable=unused-import | |
8 | +from AGPlib.threaders import Task, TaskState | |
9 | +# pylint: enable=unused-import | |
10 | + | |
11 | +class TaskTest(TestCase): | |
12 | + '''Testing tasks''' | |
13 | + def test_starttask(self): | |
14 | + test_task = Task(None) | |
15 | + self.assertIs(test_task.state, TaskState.Ready) | |
16 | + | |
17 | + def test_premature_join(self): | |
18 | + with self.assertRaises(RuntimeError): | |
19 | + Task(None).join() | |
20 | + | |
21 | + def test_asynchrone(self): | |
22 | + def appender(lst, appendant): | |
23 | + for item in appendant: | |
24 | + sleep(0.001) | |
25 | + lst.append(item) | |
26 | + #end appender | |
27 | + | |
28 | + lst = [] | |
29 | + task1 = Task(appender, lst, range(1000)) | |
30 | + task2 = Task(appender, lst, range(2000, 3000)) | |
31 | + task1.start() | |
32 | + task2.start() | |
33 | + task1.join() | |
34 | + task2.join() | |
35 | + sorted_lst = sorted(lst) | |
36 | + test_lst = [] | |
37 | + test_lst.extend(range(1000)) | |
38 | + test_lst.extend(range(2000, 3000)) | |
39 | + self.assertNotEqual(lst, sorted_lst) | |
40 | + self.assertEqual(test_lst, sorted_lst) | |
41 | + | |
42 | + def test_synchrone(self): | |
43 | + def appender(lst, appendant): | |
44 | + for item in appendant: | |
45 | + sleep(0.001) | |
46 | + lst.append(item) | |
47 | + #end appender | |
48 | + | |
49 | + lst = [] | |
50 | + task1 = Task(appender, lst, range(1000)) | |
51 | + task2 = Task(appender, lst, range(2000, 3000)) | |
52 | + task1.start() | |
53 | + task1.join() | |
54 | + task2.start() | |
55 | + task2.join() | |
56 | + sorted_lst = sorted(lst) | |
57 | + self.assertEqual(lst, sorted_lst) | |
58 | + test_lst = [] | |
59 | + test_lst.extend(range(1000)) | |
60 | + test_lst.extend(range(2000, 3000)) | |
61 | + self.assertEqual(test_lst, sorted_lst) | |
62 | + | |
63 | +unitmain() |