cengal.code_flow_control.chained_flow.versions.v_1.chained_flow

  1#!/usr/bin/env python
  2# coding=utf-8
  3
  4# Copyright © 2012-2024 ButenkoMS. All rights reserved. Contacts: <gtalk@butenkoms.space>
  5# 
  6# Licensed under the Apache License, Version 2.0 (the "License");
  7# you may not use this file except in compliance with the License.
  8# You may obtain a copy of the License at
  9# 
 10#     http://www.apache.org/licenses/LICENSE-2.0
 11# 
 12# Unless required by applicable law or agreed to in writing, software
 13# distributed under the License is distributed on an "AS IS" BASIS,
 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15# See the License for the specific language governing permissions and
 16# limitations under the License.
 17
 18import sys
 19import os
 20import copy
 21import traceback
 22from enum import Enum
 23from contextlib import contextmanager
 24from cengal.data_generation.id_generator import IDGenerator, GeneratorType
 25from cengal.code_flow_control.smart_values.versions.v_1.smart_values import *
 26
 27"""
 28Module Docstring
 29Docstrings: http://www.python.org/dev/peps/pep-0257/
 30"""
 31
 32__author__ = "ButenkoMS <gtalk@butenkoms.space>"
 33__copyright__ = "Copyright © 2012-2024 ButenkoMS. All rights reserved. Contacts: <gtalk@butenkoms.space>"
 34__credits__ = ["ButenkoMS <gtalk@butenkoms.space>", ]
 35__license__ = "Apache License, Version 2.0"
 36__version__ = "4.4.1"
 37__maintainer__ = "ButenkoMS <gtalk@butenkoms.space>"
 38__email__ = "gtalk@butenkoms.space"
 39# __status__ = "Prototype"
 40__status__ = "Development"
 41# __status__ = "Production"
 42
 43
 44class ChainLinkFailed(Exception): pass
 45
 46
 47class ChainClosed(Exception):
 48    """
 49    link considers it ordinary external exception
 50    """
 51
 52    def __init__(self, chain, link_id, link_info):
 53        super(ChainClosed, self).__init__('Content Holder is closed. '
 54                                          'Content Holder ID: {}; '
 55                                          'Content Holder Info: {}; '
 56                                          'Link ID: {}; '
 57                                          'Link Info {}'.format(str(chain._chain_id),
 58                                                                 str(chain._chain_info),
 59                                                                 str(link_id),
 60                                                                 str(link_info)))
 61        self.chain = chain
 62        self.link_id = link_id
 63        self.link_info = link_info
 64
 65
 66class ChainFunctionParameterNeeded(Exception): pass
 67
 68
 69class CriteriaType(Enum):
 70    # class CriteriaType():  # much more efficient than Enum inheritance
 71    needed = 0  # only set of this links is needed (should be already successfully done)
 72    optional = 1  # all links are needed except of this set of links
 73    any = 2  # any result will fit criteria
 74    forbidden = 3  # set of this links should be already failed
 75    not_successful = 4  # set of this links should not be successfully done (also may not start) at the check time
 76
 77
 78class IgnoreLinkResultCriteriaType(Enum):
 79    do_not_ignore = 0
 80    ignore_if_failed = 1
 81    ignore_if_successful = 2
 82
 83
 84class ChainHistoryExport(Exception):
 85    def __init__(self, history, process_error_result=True):
 86        super(ChainHistoryExport, self).__init__('')
 87        self.history = history
 88        self.process_error_result = process_error_result
 89
 90
 91class ChainInternalResult:
 92    def __init__(self, type_id, str_data, data):
 93        self.type_id = type_id
 94        self.str_data = str_data
 95        self.data = data
 96
 97    def __str__(self):
 98        return self.str_data
 99
100
101class ChainInternalResultType(Enum):
102    # class CriteriaType():  # much more efficient than Enum inheritance
103    built_in_exception__chain_link_failed = 0
104    built_in_exception__bad_history_import = 1
105    external_exception = 2
106    link_did_not_returned_an_answer = 3
107
108
109class Chain:
110    def __init__(self, chain_id=None, chain_info=None, global_link_results_criteria=None,
111                 raise_exceptions=False, save_debug_trace=False, closeable=True):
112        """
113
114        :param chain_id:
115        :param chain_info:
116        :param global_link_results_criteria: will be set to ValueType(CriteriaType.optional, set()) if None;
117            in this case all links are required.
118        :param raise_exceptions:
119        :param save_debug_trace:
120        :param closeable:
121        :return:
122        """
123        # Use only ValueType(CriteriaType.optional, ...) or ValueType(CriteriaType.needed, set()).
124        # Other will be ignored here.
125        # You may use global_link_results_criteria=ValueType(CriteriaType.optional, set()) to create criteria
126        # "no fails in any link"
127        self._chain_id = chain_id
128        self._chain_info = chain_info
129        self._internal_links_index = IDGenerator()
130        self._reserve_link_id_generator = IDGenerator(GeneratorType.guid_string)
131        self._criteria_list = list()
132        global_link_results_criteria = global_link_results_criteria or ValueType(CriteriaType.optional, set())
133        if global_link_results_criteria is not None:
134            self._criteria_list.append(global_link_results_criteria)
135        self._raise_exceptions = raise_exceptions
136        self._save_debug_trace = save_debug_trace
137        self._closeable = closeable
138        self._full_history = list()
139        self._links_library = dict()
140        self._all_made_links = set()
141        self._good_links = set()
142        self._bad_links = set()
143
144        self._current_link_id = None
145        self._current_link_info = None
146        self._current_link_result = None
147        self._closed = False
148
149        self._bool_result = ValueCache()
150
151    # def _push_criteria(self, set_of_needed_links=None, set_of_optional_links=None):
152    def _push_criteria(self, link_results_criteria):
153        # Do not use!
154        self._bool_result()
155        self._criteria_list.append(link_results_criteria)
156
157    def _pop_criteria(self):
158        # Do not use!
159        # May raise exception if len(self.criteria_list)==0, but this is OK.
160        self._bool_result()
161        return self._criteria_list.pop()
162
163    def read_criteria(self):
164        criteria = None
165        if self._criteria_list:
166            criteria = self._criteria_list[-1]
167        return criteria
168
169    def _push_link_info(self, link_id, link_info=None):
170        self._current_link_id = link_id
171        self._current_link_info = link_info
172        self._current_link_result = None
173
174    def _pop_link_info(self):
175        self._current_link_id = None
176        self._current_link_info = None
177        self._current_link_result = None
178
179    def push_result(self, bool_result, info_or_data=None):
180        self._current_link_result = (bool_result, info_or_data)
181
182    def push_result_c(self, result):
183        # "class" version: to use when result = ValueExistence()
184        self._current_link_result = (result.existence, result.result)
185
186    def read_link_result_link(self, link_id):
187        # result is NOT protected from changing!
188        original_result_data = self._links_library[link_id][3]
189        result = ValueExistence(original_result_data[0], original_result_data[1])
190        # result = self._links_library[link_id][3]
191        return result
192
193    def read_link_result_copy(self, link_id):
194        original_result_data = self._links_library[link_id][3]
195        result = ValueExistence(original_result_data[0], copy.copy(original_result_data[1]))
196        return result
197
198    def read_link_result_deepcopy(self, link_id):
199        original_result_data = self._links_library[link_id][3]
200        result = ValueExistence(original_result_data[0], copy.deepcopy(original_result_data[1]))
201        return result
202
203    def _save_link_result(self, ignore_link_result_criteria=None):
204        # ignore_link_result_criteria = ignore_link_result_criteria or IgnoreLinkResultCriteriaType.do_not_ignore
205        if ((IgnoreLinkResultCriteriaType.ignore_if_failed == ignore_link_result_criteria) and
206            (not self._current_link_result[0])) \
207                or ((IgnoreLinkResultCriteriaType.ignore_if_successful == ignore_link_result_criteria) and
208                    self._current_link_result[0]):
209            return
210
211        self._bool_result()
212        import_depth = 0
213        full_link_info = (self._internal_links_index(), self._current_link_id, self._current_link_info,
214                           self._current_link_result, import_depth)
215        self._full_history.append(full_link_info)
216        self._links_library[self._current_link_id] = full_link_info
217        self._all_made_links.add(self._current_link_id)
218        if self._current_link_result[0]:
219            self._good_links.add(self._current_link_id)
220        else:
221            self._bad_links.add(self._current_link_id)
222
223    def __bool__(self):
224        if self._bool_result:
225            return self._bool_result.get()
226        else:
227            current_criteria = self.read_criteria()
228            result = True
229            if CriteriaType.needed == current_criteria:
230                if len(current_criteria.result) != len(current_criteria.result & self._good_links):
231                    result = False
232            elif CriteriaType.optional == current_criteria:
233                if len(self._bad_links - current_criteria.result) != 0:
234                    result = False
235            elif CriteriaType.any == current_criteria:
236                result = True
237            elif CriteriaType.forbidden == current_criteria:
238                if len(current_criteria.result) != len(current_criteria.result & self._bad_links):
239                    result = False
240            elif CriteriaType.not_successful == current_criteria:
241                if len(current_criteria.result & self._good_links) != 0:
242                    result = False
243            self._bool_result.set(result)
244            return result
245
246    def __nonzero__(self):
247        return self.__bool__()
248
249    @staticmethod
250    def _link_list_to_str(link_list):
251        links_str = ',\n'.join('(index({}), depth({}), ID({}), INFO({}), RESULT({}))'.format(str(another_link[0]),
252                                                                                              str(another_link[4]),
253                                                                                              str(another_link[1]),
254                                                                                              str(another_link[2]),
255                                                                                              '({}, {})'.format(
256                                                                                                  str(another_link[3][0]),
257                                                                                                  str(another_link[3][1])))
258                                for another_link in link_list)
259        return links_str
260
261    def _link_str_to_chain_str(self, links_str):
262        full_string = '{{{{CONTEXT_HOLDER_ID({}): CONTEXT_HOLDER_INFO({})}}:[\n{}\n]}}'.format(
263                self._chain_id, self._chain_info, links_str)
264        return full_string
265
266    def get_bad_links(self):
267        result = list()
268        for another_link in self._full_history:
269            if not another_link[3][0]:
270                result.append(another_link)
271        return result
272
273    def get_bad_links_str(self):
274        bad_links = self.get_bad_links()
275        full_history_str = self._link_list_to_str(bad_links)
276        full_string = self._link_str_to_chain_str(full_history_str)
277        return full_string
278
279    def raise_bad_links(self):
280        raise ChainHistoryExport(self.get_bad_links())
281
282    def raise_full_history(self):
283        raise ChainHistoryExport(self._full_history)
284
285    def process_history_import(self, his_ex):
286        history = his_ex.history
287        for another_link in history:
288            full_link_info = (self._internal_links_index(), another_link[1], another_link[2],
289                               another_link[3], another_link[4] + 1)
290            self._full_history.append(full_link_info)
291
292    def close(self):
293        self._closed = True
294
295    def _reopen(self):
296        self._closed = False
297
298    def __str__(self):
299        full_history_str = self._link_list_to_str(self._full_history)
300        full_string = self._link_str_to_chain_str(full_history_str)
301        return full_string
302
303    def __call__(self, *args, **kwargs):
304        return link(self, *args, **kwargs)
305
306    def chain(self, *args, **kwargs):
307        return self.__call__(*args, **kwargs)
308
309
310@contextmanager
311def link(chain, link_id, link_info=None, link_results_criteria=None, ignore_link_result_criteria=None):
312    if link_id is None:
313        new_id = chain._reserve_link_id_generator()
314        link_id = (new_id, new_id)
315
316    if chain._closeable and chain._closed:
317        raise ChainClosed(chain, link_id, link_info)
318
319    chain._push_link_info(link_id, link_info)
320    if link_results_criteria is not None:
321        chain._push_criteria(link_results_criteria)
322    need_to_save_link_result = True
323    try:
324        yield chain
325    except ChainLinkFailed as exc:
326        result_info = None
327        if exc.args:
328            result_info = exc.args[0]
329        chain.push_result(False, ChainInternalResult(
330                ChainInternalResultType.built_in_exception__chain_link_failed,
331                'CHAIN INTERNAL RESULT. BUILT-IN EXCEPTION: ChainLinkFailed ({})'.format(result_info), result_info))
332    except ChainHistoryExport as export:
333        chain.process_history_import(export)
334        if export.process_error_result:
335            chain.push_result(False, ChainInternalResult(
336                    ChainInternalResultType.built_in_exception__bad_history_import,
337                    'CHAIN INTERNAL RESULT. BUILT-IN EXCEPTION: BAD HISTORY IMPORT EXCEPTION', None))
338    except:
339        exc = sys.exc_info()
340        exc_type, exc_obj, exc_tb = exc
341        tb_full_file_name = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
342        tb_line_number = exc_tb.tb_lineno
343        tb_function_name = str()
344        tb_text = str()
345
346        tb_list = traceback.extract_tb(exc_tb, 2)
347        if len(tb_list) >= 2:
348            actual_tb = tb_list[1]
349            tb_full_file_name, tb_line_number, tb_function_name, tb_text = actual_tb
350
351        exception = exc
352        error_str = '{} {}'.format(str(exception[0]), str(exception[1].args[0]))
353        # print('+++', error_str)
354        formatted_traceback = traceback.format_exception(exception[0], exception[1], exception[2])
355        exception = exception[:2] + (formatted_traceback,)
356        trace_str = ''.join(exception[2])
357        if chain._save_debug_trace:
358            result_string = 'CHAIN INTERNAL RESULT. CODE EXCEPTION "{}" AT "{}":{} in {} WITH TRACE: \n' \
359                            '{}\n' \
360                            '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' \
361                            ''.format(error_str, tb_full_file_name, tb_line_number, tb_function_name, trace_str)
362        else:
363            result_string = 'CHAIN INTERNAL RESULT. CODE EXCEPTION "{}" AT "{}":{} in {}'.format(
364                    error_str, tb_full_file_name, tb_line_number, tb_function_name)
365        chain.push_result(False, ChainInternalResult(
366                ChainInternalResultType.external_exception, result_string, exc))
367        # print(result_string)
368        # _chain_reader_runner__chain.push_result(False, sys.exc_info()[1])
369        if chain._raise_exceptions:
370            need_to_save_link_result = False
371            chain._save_link_result()
372            chain.raise_bad_links()
373            # raise
374    else:
375        if chain._current_link_result is None:
376            # _chain_reader_runner__chain.push_result(True)
377            chain.push_result(False, ChainInternalResult(
378                    ChainInternalResultType.link_did_not_returned_an_answer,
379                    'CHAIN INTERNAL RESULT. Link DID NOT RETURN RESULT', None))
380    finally:
381        if need_to_save_link_result:
382            chain._save_link_result(ignore_link_result_criteria)
383        if link_results_criteria is not None:
384            chain._pop_criteria()
385        chain._pop_link_info()
386
387
388def link__function(target_function):
389    """
390    Parameters: chain__chain= (required)
391        , chain__link_id= (required)
392        , chain__link_info= (optional)
393        , chain__link_results_criteria= (optional)
394        , chain__ignore_link_result_criteria= (optional).
395    Parameters passed to the target_function: chain__chain (after local link configuration).
396    :param target_function: function
397    :return:
398    """
399
400    def new_target_function(*args, **kwargs):
401        chain = None
402        if 'chain__chain' in kwargs:
403            chain = kwargs['chain__chain']
404            del kwargs['chain__chain']
405        else:
406            raise ChainFunctionParameterNeeded('chain__chain')
407
408        link_id = None
409        if 'chain__link_id' in kwargs:
410            link_id = kwargs['chain__link_id']
411            del kwargs['chain__link_id']
412        else:
413            raise ChainFunctionParameterNeeded('chain__link_id')
414
415        link_info = None
416        if 'chain__link_info' in kwargs:
417            link_info = kwargs['chain__link_info']
418            del kwargs['chain__link_info']
419
420        link_results_criteria = None
421        if 'chain__link_results_criteria' in kwargs:
422            link_results_criteria = kwargs['chain__link_results_criteria']
423            del kwargs['chain__link_results_criteria']
424
425        ignore_link_result_criteria = None
426        if 'chain__ignore_link_result_criteria' in kwargs:
427            ignore_link_result_criteria = kwargs['chain__ignore_link_result_criteria']
428            del kwargs['chain__ignore_link_result_criteria']
429
430        target_function_result = None
431        with link(chain, link_id, link_info, link_results_criteria, ignore_link_result_criteria) as \
432                context:
433            kwargs['chain__chain'] = context
434            target_function_result = target_function(*args, **kwargs)
435
436        return target_function_result
437
438    return new_target_function
439
440
441class _ChainRunner:
442    def __init__(self, current_globals, chain, link_id=None, link_info=None, link_results_criteria=None,
443                 ignore_link_result_criteria=None, function_result_criteria=None):
444        self._chain_runner__current_globals = current_globals
445        self._chain_runner__chain = chain
446        self._chain_runner__link_id = link_id
447        self._chain_runner__link_info = link_info
448        self._chain_runner__link_results_criteria = link_results_criteria
449        self._chain_runner__ignore_link_result_criteria = ignore_link_result_criteria
450
451    def __getattr__(self, name):
452        target_functor = None
453        if name in self._chain_runner__current_globals:
454            target_functor = self._chain_runner__current_globals[name]
455        else:
456            raise AttributeError(name)
457
458        def new_target_function(*args, **kwargs):
459            target_function_result = None
460            link_id = self._chain_runner__link_id or str(target_functor)
461            with link(self._chain_runner__chain, link_id, self._chain_runner__link_info,
462                      self._chain_runner__link_results_criteria,
463                      self._chain_runner__ignore_link_result_criteria) as context:
464                kwargs['chain__chain'] = context
465                target_function_result = target_functor(*args, **kwargs)
466
467            return target_function_result
468
469        return new_target_function
470
471
472class ChainRunner:
473    def __init__(self, current_globals, chain):
474        self.current_globals = current_globals
475        self.chain = chain
476
477    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
478                 ignore_link_result_criteria=None):
479        result = _ChainRunner(self.current_globals, self.chain, link_id, link_info, link_results_criteria,
480                              ignore_link_result_criteria)
481        return result
482
483
484class _ChainCallRunner:
485    def __init__(self, chain, link_id=None, link_info=None, link_results_criteria=None,
486                 ignore_link_result_criteria=None, function_result_criteria=None):
487        self._chain_runner__chain = chain
488        self._chain_runner__link_id = link_id
489        self._chain_runner__link_info = link_info
490        self._chain_runner__link_results_criteria = link_results_criteria
491        self._chain_runner__ignore_link_result_criteria = ignore_link_result_criteria
492
493    def __call__(self, target_functor, *args, **kwargs):
494        target_function_result = None
495        link_id = self._chain_runner__link_id or str(target_functor)
496        with link(self._chain_runner__chain, link_id, self._chain_runner__link_info,
497                  self._chain_runner__link_results_criteria,
498                  self._chain_runner__ignore_link_result_criteria) as context:
499            kwargs['chain__chain'] = context
500            target_function_result = target_functor(*args, **kwargs)
501
502        return target_function_result
503
504
505class ChainCallRunner:
506    def __init__(self, chain):
507        self.chain = chain
508
509    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
510                 ignore_link_result_criteria=None):
511        result = _ChainCallRunner(self.chain, link_id, link_info, link_results_criteria,
512                                  ignore_link_result_criteria)
513        return result
514
515
516def link__function__simple(target_function):
517    """
518    Parameters: chain__chain= (required)
519        , chain__link_id= (optional) (default value == str(target_function))
520        , chain__link_results_criteria= (optional)
521        , chain__ignore_link_result_criteria= (optional).
522    Parameters passed to the target_function: .
523    :param target_function: function
524    :return:
525    """
526
527    def new_target_function(*args, **kwargs):
528        chain = None
529        if 'chain__chain' in kwargs:
530            chain = kwargs['chain__chain']
531            del kwargs['chain__chain']
532        else:
533            raise ChainFunctionParameterNeeded('chain__chain')
534
535        # link_id = '__UNNAMED_FUNCTION_SIMPLE_LINK__'
536        link_id = str(target_function)
537        if 'chain__link_id' in kwargs:
538            link_id = kwargs['chain__link_id']
539            del kwargs['chain__link_id']
540
541        link_results_criteria = None
542        if 'chain__link_results_criteria' in kwargs:
543            link_results_criteria = kwargs['chain__link_results_criteria']
544            del kwargs['chain__link_results_criteria']
545
546        ignore_link_result_criteria = None
547        if 'chain__ignore_link_result_criteria' in kwargs:
548            ignore_link_result_criteria = kwargs['chain__ignore_link_result_criteria']
549            del kwargs['chain__ignore_link_result_criteria']
550
551        target_function_result = None
552        with link(chain, link_id, None, link_results_criteria, ignore_link_result_criteria) as context:
553            if context:
554                target_function_result = target_function(*args, **kwargs)
555                context.push_result(True, target_function_result)
556
557        return target_function_result
558
559    return new_target_function
560
561
562class _ChainRunnerSimple:
563    def __init__(self, current_globals, chain, link_id=None, link_info=None, link_results_criteria=None,
564                 ignore_link_result_criteria=None, function_result_criteria=None):
565        self._chain_runner_simple__current_globals = current_globals
566        self._chain_runner_simple__chain = chain
567        self._chain_runner_simple__link_id = link_id
568        self._chain_runner_simple__link_info = link_info
569        self._chain_runner_simple__link_results_criteria = link_results_criteria
570        self._chain_runner_simple__ignore_link_result_criteria = ignore_link_result_criteria
571        self._chain_runner_simple__function_result_criteria = function_result_criteria or (lambda result: True)
572
573    def __getattr__(self, name):
574        target_functor = None
575        if name in self._chain_runner_simple__current_globals:
576            target_functor = self._chain_runner_simple__current_globals[name]
577        else:
578            raise AttributeError(name)
579
580        def new_target_function(*args, **kwargs):
581            target_function_result = None
582            link_id = self._chain_runner_simple__link_id or str(target_functor)
583            with link(self._chain_runner_simple__chain, link_id, self._chain_runner_simple__link_info,
584                      self._chain_runner_simple__link_results_criteria,
585                      self._chain_runner_simple__ignore_link_result_criteria) as context:
586                if context:
587                    target_function_result = target_functor(*args, **kwargs)
588                    is_good_result = self._chain_runner_simple__function_result_criteria(target_function_result)
589                    context.push_result(is_good_result, target_function_result)
590
591            return target_function_result
592
593        return new_target_function
594
595
596class ChainUniRunner:
597    def __init__(self, current_globals, chain, simple_mode=False, function_result_criteria=None):
598        self.current_globals = current_globals
599        self.chain = chain
600        self.simple_mode = simple_mode
601        self.default_function_result_criteria = function_result_criteria or (lambda result: True)
602        self.function_result_criteria = self.default_function_result_criteria
603
604        self.runner_class = _ChainRunner
605        if self.simple_mode:
606            self.runner_class = _ChainRunnerSimple
607
608    def set_function_result_criteria(self, result_criteria_computer):
609        self.function_result_criteria = result_criteria_computer
610
611    def reset_function_result_criteria(self):
612        self.function_result_criteria = self.default_function_result_criteria
613
614    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
615                 ignore_link_result_criteria=None):
616        result = self.runner_class(self.current_globals, self.chain, link_id, link_info,
617                                   link_results_criteria, ignore_link_result_criteria, self.function_result_criteria)
618        return result
619
620
621class _ChainCallRunnerSimple:
622    def __init__(self, chain, link_id=None, link_info=None, link_results_criteria=None,
623                 ignore_link_result_criteria=None, function_result_criteria=None):
624        self._chain_runner_simple__chain = chain
625        self._chain_runner_simple__link_id = link_id
626        self._chain_runner_simple__link_info = link_info
627        self._chain_runner_simple__link_results_criteria = link_results_criteria
628        self._chain_runner_simple__ignore_link_result_criteria = ignore_link_result_criteria
629        self._chain_runner_simple__function_result_criteria = function_result_criteria or (lambda result: True)
630
631    def __call__(self, target_functor, *args, **kwargs):
632        target_function_result = None
633        link_id = self._chain_runner_simple__link_id or str(target_functor)
634        with link(self._chain_runner_simple__chain, link_id, self._chain_runner_simple__link_info,
635                  self._chain_runner_simple__link_results_criteria,
636                  self._chain_runner_simple__ignore_link_result_criteria) as context:
637            if context:
638                target_function_result = target_functor(*args, **kwargs)
639                is_good_result = self._chain_runner_simple__function_result_criteria(target_function_result)
640                context.push_result(is_good_result, target_function_result)
641
642        return target_function_result
643
644
645class ChainUniCallRunner:
646    def __init__(self, chain, simple_mode=False, function_result_criteria=None):
647        self.chain = chain
648        self.simple_mode = simple_mode
649        self.default_function_result_criteria = function_result_criteria or (lambda result: True)
650        self.function_result_criteria = self.default_function_result_criteria
651
652        self.runner_class = _ChainCallRunner
653        if self.simple_mode:
654            self.runner_class = _ChainCallRunnerSimple
655
656    def set_function_result_criteria(self, result_criteria_computer):
657        self.function_result_criteria = result_criteria_computer
658
659    def reset_function_result_criteria(self):
660        self.function_result_criteria = self.default_function_result_criteria
661
662    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
663                 ignore_link_result_criteria=None):
664        result = self.runner_class(self.chain, link_id, link_info,
665                                   link_results_criteria, ignore_link_result_criteria, self.function_result_criteria)
666        return result
667
668
669class _ChainValRunner:
670    def __init__(self, chain, link_id=None, link_info=None, link_results_criteria=None,
671                 ignore_link_result_criteria=None, function_result_criteria=None, reaction_to_the_result=None):
672        self._chain_runner_simple__chain = chain
673        self._chain_runner_simple__link_id = link_id
674        self._chain_runner_simple__link_info = link_info
675        self._chain_runner_simple__link_results_criteria = link_results_criteria
676        self._chain_runner_simple__ignore_link_result_criteria = ignore_link_result_criteria
677        self._chain_runner_simple__function_result_criteria = function_result_criteria or (lambda result: True)
678        self._chain_runner_simple__reaction_to_the_result = reaction_to_the_result
679
680    def __call__(self, functor_result):
681        target_function_result = functor_result
682        link_id = self._chain_runner_simple__link_id
683        with link(self._chain_runner_simple__chain, link_id, self._chain_runner_simple__link_info,
684                  self._chain_runner_simple__link_results_criteria,
685                  self._chain_runner_simple__ignore_link_result_criteria) as context:
686            if context:
687                is_good_result = self._chain_runner_simple__function_result_criteria(target_function_result)
688                if self._chain_runner_simple__reaction_to_the_result is not None:
689                    verdict = self._chain_runner_simple__reaction_to_the_result(is_good_result, target_function_result)
690                    target_function_result = (target_function_result, verdict)
691                context.push_result(is_good_result, target_function_result)
692
693        return functor_result
694
695
696class ChainValRunner:
697    def __init__(self, chain, function_result_criteria=None, reaction_to_the_result=None):
698        self.chain = chain
699        self.default_function_result_criteria = function_result_criteria or (lambda result: True)
700        self.function_result_criteria = self.default_function_result_criteria
701        self.reaction_to_the_result = reaction_to_the_result
702
703    def set_function_result_criteria(self, result_criteria_computer):
704        self.function_result_criteria = result_criteria_computer
705
706    def reset_function_result_criteria(self):
707        self.function_result_criteria = self.default_function_result_criteria
708
709    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
710                 ignore_link_result_criteria=None):
711        result = _ChainValRunner(self.chain, link_id, link_info,
712                                 link_results_criteria, ignore_link_result_criteria,
713                                 self.function_result_criteria, self.reaction_to_the_result)
714        return result
715
716
717@contextmanager
718def chain_reader(chain, link_results_criteria=None, close=False):
719    if link_results_criteria is not None:
720        chain._push_criteria(link_results_criteria)
721    try:
722        yield chain
723    except:
724        raise
725    finally:
726        if link_results_criteria is not None:
727            chain._pop_criteria()
728        if close:
729            chain.close()
730
731
732def chain_reader__function(target_function):
733    """
734    Parameters: chain__chain= (required), chain__link_results_criteria= (optional), chain__close= (optional).
735    Parameters passed to the target_function: chain__chain (after local link configuration).
736    :param target_function: function
737    :return:
738    """
739
740    def new_target_function(*args, **kwargs):
741        chain = None
742        if 'chain__chain' in kwargs:
743            chain = kwargs['chain__chain']
744            del kwargs['chain__chain']
745        else:
746            raise ChainFunctionParameterNeeded('chain__chain')
747
748        link_results_criteria = None
749        if 'chain__link_results_criteria' in kwargs:
750            link_results_criteria = kwargs['chain__link_results_criteria']
751            del kwargs['chain__link_results_criteria']
752
753        close = None
754        if 'chain__close' in kwargs:
755            close = kwargs['chain__close']
756            del kwargs['chain__close']
757
758        target_function_result = None
759        with chain_reader(chain, link_results_criteria, close) as context:
760            kwargs['chain__chain'] = context
761            target_function_result = target_function(*args, **kwargs)
762
763        return target_function_result
764
765    return new_target_function
766
767
768class _ChainReaderRunner:
769    def __init__(self, current_globals, chain, link_results_criteria=None, close=False):
770        self._chain_reader_runner__current_globals = current_globals
771        self._chain_reader_runner__chain = chain
772        self._chain_reader_runner__link_results_criteria = link_results_criteria
773        self._chain_reader_runner__close = close
774
775    def __getattr__(self, name):
776        target_functor = None
777        if name in self._chain_reader_runner__current_globals:
778            target_functor = self._chain_reader_runner__current_globals[name]
779        else:
780            raise AttributeError(name)
781
782        def new_target_function(*args, **kwargs):
783            target_function_result = None
784            with chain_reader(self._chain_reader_runner__chain,
785                              self._chain_reader_runner__link_results_criteria,
786                              self._chain_reader_runner__close) as context:
787                kwargs['chain__chain'] = context
788                target_function_result = target_functor(*args, **kwargs)
789
790            return target_function_result
791
792        return new_target_function
793
794
795class ChainReaderRunner:
796    def __init__(self, current_globals, chain):
797        self.current_globals = current_globals
798        self.chain = chain
799
800    def __call__(self, link_results_criteria=None, close=False):
801        result = _ChainReaderRunner(self.current_globals, self.chain, link_results_criteria, close)
802        return result
803
804
805class _ChainReaderCallRunner:
806    def __init__(self, chain, link_results_criteria=None, close=False):
807        self._chain_reader_runner__chain = chain
808        self._chain_reader_runner__link_results_criteria = link_results_criteria
809        self._chain_reader_runner__close = close
810
811    def __call__(self, target_functor, *args, **kwargs):
812        target_function_result = None
813        with chain_reader(self._chain_reader_runner__chain,
814                          self._chain_reader_runner__link_results_criteria,
815                          self._chain_reader_runner__close) as context:
816            kwargs['chain__chain'] = context
817            target_function_result = target_functor(*args, **kwargs)
818
819        return target_function_result
820
821
822class ChainReaderCallRunner:
823    def __init__(self, chain):
824        self.chain = chain
825
826    def __call__(self, link_results_criteria=None, close=False):
827        result = _ChainReaderCallRunner(self.chain, link_results_criteria, close)
828        return result
class ChainLinkFailed(builtins.Exception):
45class ChainLinkFailed(Exception): pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class ChainClosed(builtins.Exception):
48class ChainClosed(Exception):
49    """
50    link considers it ordinary external exception
51    """
52
53    def __init__(self, chain, link_id, link_info):
54        super(ChainClosed, self).__init__('Content Holder is closed. '
55                                          'Content Holder ID: {}; '
56                                          'Content Holder Info: {}; '
57                                          'Link ID: {}; '
58                                          'Link Info {}'.format(str(chain._chain_id),
59                                                                 str(chain._chain_info),
60                                                                 str(link_id),
61                                                                 str(link_info)))
62        self.chain = chain
63        self.link_id = link_id
64        self.link_info = link_info

link considers it ordinary external exception

ChainClosed(chain, link_id, link_info)
53    def __init__(self, chain, link_id, link_info):
54        super(ChainClosed, self).__init__('Content Holder is closed. '
55                                          'Content Holder ID: {}; '
56                                          'Content Holder Info: {}; '
57                                          'Link ID: {}; '
58                                          'Link Info {}'.format(str(chain._chain_id),
59                                                                 str(chain._chain_info),
60                                                                 str(link_id),
61                                                                 str(link_info)))
62        self.chain = chain
63        self.link_id = link_id
64        self.link_info = link_info
chain
Inherited Members
builtins.BaseException
with_traceback
args
class ChainFunctionParameterNeeded(builtins.Exception):
67class ChainFunctionParameterNeeded(Exception): pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class CriteriaType(enum.Enum):
70class CriteriaType(Enum):
71    # class CriteriaType():  # much more efficient than Enum inheritance
72    needed = 0  # only set of this links is needed (should be already successfully done)
73    optional = 1  # all links are needed except of this set of links
74    any = 2  # any result will fit criteria
75    forbidden = 3  # set of this links should be already failed
76    not_successful = 4  # set of this links should not be successfully done (also may not start) at the check time

An enumeration.

needed = <CriteriaType.needed: 0>
optional = <CriteriaType.optional: 1>
any = <CriteriaType.any: 2>
forbidden = <CriteriaType.forbidden: 3>
not_successful = <CriteriaType.not_successful: 4>
Inherited Members
enum.Enum
name
value
class IgnoreLinkResultCriteriaType(enum.Enum):
79class IgnoreLinkResultCriteriaType(Enum):
80    do_not_ignore = 0
81    ignore_if_failed = 1
82    ignore_if_successful = 2

An enumeration.

Inherited Members
enum.Enum
name
value
class ChainHistoryExport(builtins.Exception):
85class ChainHistoryExport(Exception):
86    def __init__(self, history, process_error_result=True):
87        super(ChainHistoryExport, self).__init__('')
88        self.history = history
89        self.process_error_result = process_error_result

Common base class for all non-exit exceptions.

ChainHistoryExport(history, process_error_result=True)
86    def __init__(self, history, process_error_result=True):
87        super(ChainHistoryExport, self).__init__('')
88        self.history = history
89        self.process_error_result = process_error_result
history
process_error_result
Inherited Members
builtins.BaseException
with_traceback
args
class ChainInternalResult:
92class ChainInternalResult:
93    def __init__(self, type_id, str_data, data):
94        self.type_id = type_id
95        self.str_data = str_data
96        self.data = data
97
98    def __str__(self):
99        return self.str_data
ChainInternalResult(type_id, str_data, data)
93    def __init__(self, type_id, str_data, data):
94        self.type_id = type_id
95        self.str_data = str_data
96        self.data = data
type_id
str_data
data
class ChainInternalResultType(enum.Enum):
102class ChainInternalResultType(Enum):
103    # class CriteriaType():  # much more efficient than Enum inheritance
104    built_in_exception__chain_link_failed = 0
105    built_in_exception__bad_history_import = 1
106    external_exception = 2
107    link_did_not_returned_an_answer = 3

An enumeration.

built_in_exception__bad_history_import = <ChainInternalResultType.built_in_exception__bad_history_import: 1>
external_exception = <ChainInternalResultType.external_exception: 2>
Inherited Members
enum.Enum
name
value
class Chain:
110class Chain:
111    def __init__(self, chain_id=None, chain_info=None, global_link_results_criteria=None,
112                 raise_exceptions=False, save_debug_trace=False, closeable=True):
113        """
114
115        :param chain_id:
116        :param chain_info:
117        :param global_link_results_criteria: will be set to ValueType(CriteriaType.optional, set()) if None;
118            in this case all links are required.
119        :param raise_exceptions:
120        :param save_debug_trace:
121        :param closeable:
122        :return:
123        """
124        # Use only ValueType(CriteriaType.optional, ...) or ValueType(CriteriaType.needed, set()).
125        # Other will be ignored here.
126        # You may use global_link_results_criteria=ValueType(CriteriaType.optional, set()) to create criteria
127        # "no fails in any link"
128        self._chain_id = chain_id
129        self._chain_info = chain_info
130        self._internal_links_index = IDGenerator()
131        self._reserve_link_id_generator = IDGenerator(GeneratorType.guid_string)
132        self._criteria_list = list()
133        global_link_results_criteria = global_link_results_criteria or ValueType(CriteriaType.optional, set())
134        if global_link_results_criteria is not None:
135            self._criteria_list.append(global_link_results_criteria)
136        self._raise_exceptions = raise_exceptions
137        self._save_debug_trace = save_debug_trace
138        self._closeable = closeable
139        self._full_history = list()
140        self._links_library = dict()
141        self._all_made_links = set()
142        self._good_links = set()
143        self._bad_links = set()
144
145        self._current_link_id = None
146        self._current_link_info = None
147        self._current_link_result = None
148        self._closed = False
149
150        self._bool_result = ValueCache()
151
152    # def _push_criteria(self, set_of_needed_links=None, set_of_optional_links=None):
153    def _push_criteria(self, link_results_criteria):
154        # Do not use!
155        self._bool_result()
156        self._criteria_list.append(link_results_criteria)
157
158    def _pop_criteria(self):
159        # Do not use!
160        # May raise exception if len(self.criteria_list)==0, but this is OK.
161        self._bool_result()
162        return self._criteria_list.pop()
163
164    def read_criteria(self):
165        criteria = None
166        if self._criteria_list:
167            criteria = self._criteria_list[-1]
168        return criteria
169
170    def _push_link_info(self, link_id, link_info=None):
171        self._current_link_id = link_id
172        self._current_link_info = link_info
173        self._current_link_result = None
174
175    def _pop_link_info(self):
176        self._current_link_id = None
177        self._current_link_info = None
178        self._current_link_result = None
179
180    def push_result(self, bool_result, info_or_data=None):
181        self._current_link_result = (bool_result, info_or_data)
182
183    def push_result_c(self, result):
184        # "class" version: to use when result = ValueExistence()
185        self._current_link_result = (result.existence, result.result)
186
187    def read_link_result_link(self, link_id):
188        # result is NOT protected from changing!
189        original_result_data = self._links_library[link_id][3]
190        result = ValueExistence(original_result_data[0], original_result_data[1])
191        # result = self._links_library[link_id][3]
192        return result
193
194    def read_link_result_copy(self, link_id):
195        original_result_data = self._links_library[link_id][3]
196        result = ValueExistence(original_result_data[0], copy.copy(original_result_data[1]))
197        return result
198
199    def read_link_result_deepcopy(self, link_id):
200        original_result_data = self._links_library[link_id][3]
201        result = ValueExistence(original_result_data[0], copy.deepcopy(original_result_data[1]))
202        return result
203
204    def _save_link_result(self, ignore_link_result_criteria=None):
205        # ignore_link_result_criteria = ignore_link_result_criteria or IgnoreLinkResultCriteriaType.do_not_ignore
206        if ((IgnoreLinkResultCriteriaType.ignore_if_failed == ignore_link_result_criteria) and
207            (not self._current_link_result[0])) \
208                or ((IgnoreLinkResultCriteriaType.ignore_if_successful == ignore_link_result_criteria) and
209                    self._current_link_result[0]):
210            return
211
212        self._bool_result()
213        import_depth = 0
214        full_link_info = (self._internal_links_index(), self._current_link_id, self._current_link_info,
215                           self._current_link_result, import_depth)
216        self._full_history.append(full_link_info)
217        self._links_library[self._current_link_id] = full_link_info
218        self._all_made_links.add(self._current_link_id)
219        if self._current_link_result[0]:
220            self._good_links.add(self._current_link_id)
221        else:
222            self._bad_links.add(self._current_link_id)
223
224    def __bool__(self):
225        if self._bool_result:
226            return self._bool_result.get()
227        else:
228            current_criteria = self.read_criteria()
229            result = True
230            if CriteriaType.needed == current_criteria:
231                if len(current_criteria.result) != len(current_criteria.result & self._good_links):
232                    result = False
233            elif CriteriaType.optional == current_criteria:
234                if len(self._bad_links - current_criteria.result) != 0:
235                    result = False
236            elif CriteriaType.any == current_criteria:
237                result = True
238            elif CriteriaType.forbidden == current_criteria:
239                if len(current_criteria.result) != len(current_criteria.result & self._bad_links):
240                    result = False
241            elif CriteriaType.not_successful == current_criteria:
242                if len(current_criteria.result & self._good_links) != 0:
243                    result = False
244            self._bool_result.set(result)
245            return result
246
247    def __nonzero__(self):
248        return self.__bool__()
249
250    @staticmethod
251    def _link_list_to_str(link_list):
252        links_str = ',\n'.join('(index({}), depth({}), ID({}), INFO({}), RESULT({}))'.format(str(another_link[0]),
253                                                                                              str(another_link[4]),
254                                                                                              str(another_link[1]),
255                                                                                              str(another_link[2]),
256                                                                                              '({}, {})'.format(
257                                                                                                  str(another_link[3][0]),
258                                                                                                  str(another_link[3][1])))
259                                for another_link in link_list)
260        return links_str
261
262    def _link_str_to_chain_str(self, links_str):
263        full_string = '{{{{CONTEXT_HOLDER_ID({}): CONTEXT_HOLDER_INFO({})}}:[\n{}\n]}}'.format(
264                self._chain_id, self._chain_info, links_str)
265        return full_string
266
267    def get_bad_links(self):
268        result = list()
269        for another_link in self._full_history:
270            if not another_link[3][0]:
271                result.append(another_link)
272        return result
273
274    def get_bad_links_str(self):
275        bad_links = self.get_bad_links()
276        full_history_str = self._link_list_to_str(bad_links)
277        full_string = self._link_str_to_chain_str(full_history_str)
278        return full_string
279
280    def raise_bad_links(self):
281        raise ChainHistoryExport(self.get_bad_links())
282
283    def raise_full_history(self):
284        raise ChainHistoryExport(self._full_history)
285
286    def process_history_import(self, his_ex):
287        history = his_ex.history
288        for another_link in history:
289            full_link_info = (self._internal_links_index(), another_link[1], another_link[2],
290                               another_link[3], another_link[4] + 1)
291            self._full_history.append(full_link_info)
292
293    def close(self):
294        self._closed = True
295
296    def _reopen(self):
297        self._closed = False
298
299    def __str__(self):
300        full_history_str = self._link_list_to_str(self._full_history)
301        full_string = self._link_str_to_chain_str(full_history_str)
302        return full_string
303
304    def __call__(self, *args, **kwargs):
305        return link(self, *args, **kwargs)
306
307    def chain(self, *args, **kwargs):
308        return self.__call__(*args, **kwargs)
Chain( chain_id=None, chain_info=None, global_link_results_criteria=None, raise_exceptions=False, save_debug_trace=False, closeable=True)
111    def __init__(self, chain_id=None, chain_info=None, global_link_results_criteria=None,
112                 raise_exceptions=False, save_debug_trace=False, closeable=True):
113        """
114
115        :param chain_id:
116        :param chain_info:
117        :param global_link_results_criteria: will be set to ValueType(CriteriaType.optional, set()) if None;
118            in this case all links are required.
119        :param raise_exceptions:
120        :param save_debug_trace:
121        :param closeable:
122        :return:
123        """
124        # Use only ValueType(CriteriaType.optional, ...) or ValueType(CriteriaType.needed, set()).
125        # Other will be ignored here.
126        # You may use global_link_results_criteria=ValueType(CriteriaType.optional, set()) to create criteria
127        # "no fails in any link"
128        self._chain_id = chain_id
129        self._chain_info = chain_info
130        self._internal_links_index = IDGenerator()
131        self._reserve_link_id_generator = IDGenerator(GeneratorType.guid_string)
132        self._criteria_list = list()
133        global_link_results_criteria = global_link_results_criteria or ValueType(CriteriaType.optional, set())
134        if global_link_results_criteria is not None:
135            self._criteria_list.append(global_link_results_criteria)
136        self._raise_exceptions = raise_exceptions
137        self._save_debug_trace = save_debug_trace
138        self._closeable = closeable
139        self._full_history = list()
140        self._links_library = dict()
141        self._all_made_links = set()
142        self._good_links = set()
143        self._bad_links = set()
144
145        self._current_link_id = None
146        self._current_link_info = None
147        self._current_link_result = None
148        self._closed = False
149
150        self._bool_result = ValueCache()

:param chain_id: :param chain_info: :param global_link_results_criteria: will be set to ValueType(CriteriaType.optional, set()) if None; in this case all links are required. :param raise_exceptions: :param save_debug_trace: :param closeable: :return:

def read_criteria(self):
164    def read_criteria(self):
165        criteria = None
166        if self._criteria_list:
167            criteria = self._criteria_list[-1]
168        return criteria
def push_result(self, bool_result, info_or_data=None):
180    def push_result(self, bool_result, info_or_data=None):
181        self._current_link_result = (bool_result, info_or_data)
def push_result_c(self, result):
183    def push_result_c(self, result):
184        # "class" version: to use when result = ValueExistence()
185        self._current_link_result = (result.existence, result.result)
def raise_full_history(self):
283    def raise_full_history(self):
284        raise ChainHistoryExport(self._full_history)
def process_history_import(self, his_ex):
286    def process_history_import(self, his_ex):
287        history = his_ex.history
288        for another_link in history:
289            full_link_info = (self._internal_links_index(), another_link[1], another_link[2],
290                               another_link[3], another_link[4] + 1)
291            self._full_history.append(full_link_info)
def close(self):
293    def close(self):
294        self._closed = True
def chain(self, *args, **kwargs):
307    def chain(self, *args, **kwargs):
308        return self.__call__(*args, **kwargs)
class ChainRunner:
473class ChainRunner:
474    def __init__(self, current_globals, chain):
475        self.current_globals = current_globals
476        self.chain = chain
477
478    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
479                 ignore_link_result_criteria=None):
480        result = _ChainRunner(self.current_globals, self.chain, link_id, link_info, link_results_criteria,
481                              ignore_link_result_criteria)
482        return result
ChainRunner(current_globals, chain)
474    def __init__(self, current_globals, chain):
475        self.current_globals = current_globals
476        self.chain = chain
current_globals
chain
class ChainCallRunner:
506class ChainCallRunner:
507    def __init__(self, chain):
508        self.chain = chain
509
510    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
511                 ignore_link_result_criteria=None):
512        result = _ChainCallRunner(self.chain, link_id, link_info, link_results_criteria,
513                                  ignore_link_result_criteria)
514        return result
ChainCallRunner(chain)
507    def __init__(self, chain):
508        self.chain = chain
chain
class ChainUniRunner:
597class ChainUniRunner:
598    def __init__(self, current_globals, chain, simple_mode=False, function_result_criteria=None):
599        self.current_globals = current_globals
600        self.chain = chain
601        self.simple_mode = simple_mode
602        self.default_function_result_criteria = function_result_criteria or (lambda result: True)
603        self.function_result_criteria = self.default_function_result_criteria
604
605        self.runner_class = _ChainRunner
606        if self.simple_mode:
607            self.runner_class = _ChainRunnerSimple
608
609    def set_function_result_criteria(self, result_criteria_computer):
610        self.function_result_criteria = result_criteria_computer
611
612    def reset_function_result_criteria(self):
613        self.function_result_criteria = self.default_function_result_criteria
614
615    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
616                 ignore_link_result_criteria=None):
617        result = self.runner_class(self.current_globals, self.chain, link_id, link_info,
618                                   link_results_criteria, ignore_link_result_criteria, self.function_result_criteria)
619        return result
ChainUniRunner( current_globals, chain, simple_mode=False, function_result_criteria=None)
598    def __init__(self, current_globals, chain, simple_mode=False, function_result_criteria=None):
599        self.current_globals = current_globals
600        self.chain = chain
601        self.simple_mode = simple_mode
602        self.default_function_result_criteria = function_result_criteria or (lambda result: True)
603        self.function_result_criteria = self.default_function_result_criteria
604
605        self.runner_class = _ChainRunner
606        if self.simple_mode:
607            self.runner_class = _ChainRunnerSimple
current_globals
chain
simple_mode
default_function_result_criteria
function_result_criteria
runner_class
def set_function_result_criteria(self, result_criteria_computer):
609    def set_function_result_criteria(self, result_criteria_computer):
610        self.function_result_criteria = result_criteria_computer
def reset_function_result_criteria(self):
612    def reset_function_result_criteria(self):
613        self.function_result_criteria = self.default_function_result_criteria
class ChainUniCallRunner:
646class ChainUniCallRunner:
647    def __init__(self, chain, simple_mode=False, function_result_criteria=None):
648        self.chain = chain
649        self.simple_mode = simple_mode
650        self.default_function_result_criteria = function_result_criteria or (lambda result: True)
651        self.function_result_criteria = self.default_function_result_criteria
652
653        self.runner_class = _ChainCallRunner
654        if self.simple_mode:
655            self.runner_class = _ChainCallRunnerSimple
656
657    def set_function_result_criteria(self, result_criteria_computer):
658        self.function_result_criteria = result_criteria_computer
659
660    def reset_function_result_criteria(self):
661        self.function_result_criteria = self.default_function_result_criteria
662
663    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
664                 ignore_link_result_criteria=None):
665        result = self.runner_class(self.chain, link_id, link_info,
666                                   link_results_criteria, ignore_link_result_criteria, self.function_result_criteria)
667        return result
ChainUniCallRunner(chain, simple_mode=False, function_result_criteria=None)
647    def __init__(self, chain, simple_mode=False, function_result_criteria=None):
648        self.chain = chain
649        self.simple_mode = simple_mode
650        self.default_function_result_criteria = function_result_criteria or (lambda result: True)
651        self.function_result_criteria = self.default_function_result_criteria
652
653        self.runner_class = _ChainCallRunner
654        if self.simple_mode:
655            self.runner_class = _ChainCallRunnerSimple
chain
simple_mode
default_function_result_criteria
function_result_criteria
runner_class
def set_function_result_criteria(self, result_criteria_computer):
657    def set_function_result_criteria(self, result_criteria_computer):
658        self.function_result_criteria = result_criteria_computer
def reset_function_result_criteria(self):
660    def reset_function_result_criteria(self):
661        self.function_result_criteria = self.default_function_result_criteria
class ChainValRunner:
697class ChainValRunner:
698    def __init__(self, chain, function_result_criteria=None, reaction_to_the_result=None):
699        self.chain = chain
700        self.default_function_result_criteria = function_result_criteria or (lambda result: True)
701        self.function_result_criteria = self.default_function_result_criteria
702        self.reaction_to_the_result = reaction_to_the_result
703
704    def set_function_result_criteria(self, result_criteria_computer):
705        self.function_result_criteria = result_criteria_computer
706
707    def reset_function_result_criteria(self):
708        self.function_result_criteria = self.default_function_result_criteria
709
710    def __call__(self, link_id=None, link_info=None, link_results_criteria=None,
711                 ignore_link_result_criteria=None):
712        result = _ChainValRunner(self.chain, link_id, link_info,
713                                 link_results_criteria, ignore_link_result_criteria,
714                                 self.function_result_criteria, self.reaction_to_the_result)
715        return result
ChainValRunner(chain, function_result_criteria=None, reaction_to_the_result=None)
698    def __init__(self, chain, function_result_criteria=None, reaction_to_the_result=None):
699        self.chain = chain
700        self.default_function_result_criteria = function_result_criteria or (lambda result: True)
701        self.function_result_criteria = self.default_function_result_criteria
702        self.reaction_to_the_result = reaction_to_the_result
chain
default_function_result_criteria
function_result_criteria
reaction_to_the_result
def set_function_result_criteria(self, result_criteria_computer):
704    def set_function_result_criteria(self, result_criteria_computer):
705        self.function_result_criteria = result_criteria_computer
def reset_function_result_criteria(self):
707    def reset_function_result_criteria(self):
708        self.function_result_criteria = self.default_function_result_criteria
@contextmanager
def chain_reader(chain, link_results_criteria=None, close=False):
718@contextmanager
719def chain_reader(chain, link_results_criteria=None, close=False):
720    if link_results_criteria is not None:
721        chain._push_criteria(link_results_criteria)
722    try:
723        yield chain
724    except:
725        raise
726    finally:
727        if link_results_criteria is not None:
728            chain._pop_criteria()
729        if close:
730            chain.close()
def chain_reader__function(target_function):
733def chain_reader__function(target_function):
734    """
735    Parameters: chain__chain= (required), chain__link_results_criteria= (optional), chain__close= (optional).
736    Parameters passed to the target_function: chain__chain (after local link configuration).
737    :param target_function: function
738    :return:
739    """
740
741    def new_target_function(*args, **kwargs):
742        chain = None
743        if 'chain__chain' in kwargs:
744            chain = kwargs['chain__chain']
745            del kwargs['chain__chain']
746        else:
747            raise ChainFunctionParameterNeeded('chain__chain')
748
749        link_results_criteria = None
750        if 'chain__link_results_criteria' in kwargs:
751            link_results_criteria = kwargs['chain__link_results_criteria']
752            del kwargs['chain__link_results_criteria']
753
754        close = None
755        if 'chain__close' in kwargs:
756            close = kwargs['chain__close']
757            del kwargs['chain__close']
758
759        target_function_result = None
760        with chain_reader(chain, link_results_criteria, close) as context:
761            kwargs['chain__chain'] = context
762            target_function_result = target_function(*args, **kwargs)
763
764        return target_function_result
765
766    return new_target_function

Parameters: chain__chain= (required), chain__link_results_criteria= (optional), chain__close= (optional). Parameters passed to the target_function: chain__chain (after local link configuration). :param target_function: function :return:

class ChainReaderRunner:
796class ChainReaderRunner:
797    def __init__(self, current_globals, chain):
798        self.current_globals = current_globals
799        self.chain = chain
800
801    def __call__(self, link_results_criteria=None, close=False):
802        result = _ChainReaderRunner(self.current_globals, self.chain, link_results_criteria, close)
803        return result
ChainReaderRunner(current_globals, chain)
797    def __init__(self, current_globals, chain):
798        self.current_globals = current_globals
799        self.chain = chain
current_globals
chain
class ChainReaderCallRunner:
823class ChainReaderCallRunner:
824    def __init__(self, chain):
825        self.chain = chain
826
827    def __call__(self, link_results_criteria=None, close=False):
828        result = _ChainReaderCallRunner(self.chain, link_results_criteria, close)
829        return result
ChainReaderCallRunner(chain)
824    def __init__(self, chain):
825        self.chain = chain
chain