)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"c5afbfa91576f0ff98581a2914328786029c0cef","unresolved":false,"context_lines":[{"line_number":15,"context_line":""},{"line_number":16,"context_line":"- Example has been added."},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"Implements: blueprint subgraph-execution"},{"line_number":19,"context_line":"Implements: blueprint reversion-strategies"},{"line_number":20,"context_line":"Implements: blueprint smart-revert"},{"line_number":21,"context_line":""}],"source_content_type":"text/x-gerrit-commit-message","patch_set":14,"id":"AAAAUX%2F%2B6P4%3D","line":18,"updated":"2014-02-18 09:09:59.000000000","message":"Does it?","commit_id":"a3c31831210f03ac0221652070cd91a354c90fcd"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"83d0ca75a0de8838e76a36820d78dfb9148eef18","unresolved":false,"context_lines":[{"line_number":15,"context_line":""},{"line_number":16,"context_line":"- Example has been added."},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"Implements: blueprint subgraph-execution"},{"line_number":19,"context_line":"Implements: blueprint reversion-strategies"},{"line_number":20,"context_line":"Implements: blueprint smart-revert"},{"line_number":21,"context_line":""}],"source_content_type":"text/x-gerrit-commit-message","patch_set":14,"id":"AAAAUX%2F%2B5hE%3D","line":18,"in_reply_to":"AAAAUX%2F%2B6P4%3D","updated":"2014-02-18 10:34:19.000000000","message":"Yes, it does :)","commit_id":"a3c31831210f03ac0221652070cd91a354c90fcd"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"eadd1cde9ad89acf7b0d4133590c0df4b7d4cad1","unresolved":false,"context_lines":[{"line_number":9,"context_line":"- Remove flow REVERTING state. Now flow can be running and reverting simultaneously."},{"line_number":10,"context_line":"  Until flow isn\u0027t finished it is in RUNNING state."},{"line_number":11,"context_line":""},{"line_number":12,"context_line":"- Add RETRYING state for the retry contorller."},{"line_number":13,"context_line":""},{"line_number":14,"context_line":"- Implement smart revertion and flow retries and retries resumption."},{"line_number":15,"context_line":""}],"source_content_type":"text/x-gerrit-commit-message","patch_set":17,"id":"AAAAUX%2F%2BUf0%3D","line":12,"updated":"2014-02-21 22:04:19.000000000","message":"contorller -\u003e controller","commit_id":"7edad2077977b39efa9ab4e065a0d3299c987c66"}],"taskflow/engines/action_engine/engine.py":[{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"c5afbfa91576f0ff98581a2914328786029c0cef","unresolved":false,"context_lines":[{"line_number":85,"context_line":"    def run(self):"},{"line_number":86,"context_line":"        \"\"\"Runs the flow in the engine to completion.\"\"\""},{"line_number":87,"context_line":"        if self.storage.get_flow_state() \u003d\u003d states.REVERTED:"},{"line_number":88,"context_line":"            self._reset()"},{"line_number":89,"context_line":"        self.compile()"},{"line_number":90,"context_line":"        external_provides \u003d set(self.storage.fetch_all().keys())"},{"line_number":91,"context_line":"        missing \u003d self._flow.requires - external_provides"}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAAUX%2F%2B6Oc%3D","line":88,"updated":"2014-02-18 09:09:59.000000000","message":"Does this still work correctly? As far as I can see, Storage.reset_tasks does not work with retries, and does not set intentions.","commit_id":"a3c31831210f03ac0221652070cd91a354c90fcd"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"83d0ca75a0de8838e76a36820d78dfb9148eef18","unresolved":false,"context_lines":[{"line_number":85,"context_line":"    def run(self):"},{"line_number":86,"context_line":"        \"\"\"Runs the flow in the engine to completion.\"\"\""},{"line_number":87,"context_line":"        if self.storage.get_flow_state() \u003d\u003d states.REVERTED:"},{"line_number":88,"context_line":"            self._reset()"},{"line_number":89,"context_line":"        self.compile()"},{"line_number":90,"context_line":"        external_provides \u003d set(self.storage.fetch_all().keys())"},{"line_number":91,"context_line":"        missing \u003d self._flow.requires - external_provides"}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAAUX%2F%2B5gI%3D","line":88,"in_reply_to":"AAAAUX%2F%2B6Oc%3D","updated":"2014-02-18 10:34:19.000000000","message":"After Atom had been reverted intention sets to EXECUTE by graph_action, this means that _reset should not set intentions","commit_id":"a3c31831210f03ac0221652070cd91a354c90fcd"}],"taskflow/engines/action_engine/graph_action.py":[{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"ba09f8e541f3201a52293b3d0a90edce537eccc0","unresolved":false,"context_lines":[{"line_number":50,"context_line":"        if self._analyzer.is_success():"},{"line_number":51,"context_line":"            return st.SUCCESS"},{"line_number":52,"context_line":"        else:"},{"line_number":53,"context_line":"            return st.REVERTED"},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"    def _run(self):"},{"line_number":56,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9dbY%3D","line":53,"updated":"2014-02-06 20:03:47.000000000","message":"I guess we came to the point where we should just throw away our logic that defines final flow state and derive it from the state of nodes.","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"ba09f8e541f3201a52293b3d0a90edce537eccc0","unresolved":false,"context_lines":[{"line_number":92,"context_line":"                    if event \u003d\u003d ex.EXECUTED:"},{"line_number":93,"context_line":"                        self._process_atom_failure(node, result)"},{"line_number":94,"context_line":"                        next_nodes.extend("},{"line_number":95,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":96,"context_line":"                    else:"},{"line_number":97,"context_line":"                        result.reraise()"},{"line_number":98,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9dEI%3D","line":95,"updated":"2014-02-06 20:03:47.000000000","message":"I think there is no need in special-casing failures here, common call to get_next_nodes will do.","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b015671c28d95b9f235742c79be5b4ec64196d8c","unresolved":false,"context_lines":[{"line_number":92,"context_line":"                    if event \u003d\u003d ex.EXECUTED:"},{"line_number":93,"context_line":"                        self._process_atom_failure(node, result)"},{"line_number":94,"context_line":"                        next_nodes.extend("},{"line_number":95,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":96,"context_line":"                    else:"},{"line_number":97,"context_line":"                        result.reraise()"},{"line_number":98,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9WH8%3D","line":95,"in_reply_to":"AAAAUH%2F9dEI%3D","updated":"2014-02-07 17:10:59.000000000","message":"I call get_next_nodes for the current completed node, but browse_nodes_for_revert should search across a whole graph, because in case of two or more independent nodes like unordered flow, other nodes of subflow should be scheduled for revert too.\n                  -------- B( success)\nA( success)\n                  -------- C (failure)\n\nWhen C fails, B should be scheduled, but B doesn\u0027t depend on C.","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"ba09f8e541f3201a52293b3d0a90edce537eccc0","unresolved":false,"context_lines":[{"line_number":155,"context_line":"        retry \u003d self._analyzer.find_atom_retry(atom)"},{"line_number":156,"context_line":"        if retry:"},{"line_number":157,"context_line":"            action \u003d self._retry_action.on_failure(retry, failure)"},{"line_number":158,"context_line":"            self._analyzer.on_failure(retry, action)"},{"line_number":159,"context_line":"            if action \u003d\u003d r.RETRY:"},{"line_number":160,"context_line":"                self._storage.set_atom_intention(retry.name, st.RETRY)"},{"line_number":161,"context_line":"                for node in self._analyzer.iterate_subgraph(retry):"}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9dF8%3D","line":158,"updated":"2014-02-06 20:03:47.000000000","message":"what?","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b015671c28d95b9f235742c79be5b4ec64196d8c","unresolved":false,"context_lines":[{"line_number":155,"context_line":"        retry \u003d self._analyzer.find_atom_retry(atom)"},{"line_number":156,"context_line":"        if retry:"},{"line_number":157,"context_line":"            action \u003d self._retry_action.on_failure(retry, failure)"},{"line_number":158,"context_line":"            self._analyzer.on_failure(retry, action)"},{"line_number":159,"context_line":"            if action \u003d\u003d r.RETRY:"},{"line_number":160,"context_line":"                self._storage.set_atom_intention(retry.name, st.RETRY)"},{"line_number":161,"context_line":"                for node in self._analyzer.iterate_subgraph(retry):"}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9YOM%3D","line":158,"in_reply_to":"AAAAUH%2F9dF8%3D","updated":"2014-02-07 17:10:59.000000000","message":"Done","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"249daa9021036959212d61dba367e3c3b904b201","unresolved":false,"context_lines":[{"line_number":60,"context_line":"                # executes scheduling"},{"line_number":61,"context_line":"                if isinstance(node, task.BaseTask):"},{"line_number":62,"context_line":"                    future \u003d self._schedule_task(node)"},{"line_number":63,"context_line":"                else:"},{"line_number":64,"context_line":"                    future \u003d self._schedule_retry(node)"},{"line_number":65,"context_line":"                if future is not None:"},{"line_number":66,"context_line":"                    not_done.append(future)"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2Fxa4%3D","line":63,"updated":"2014-02-10 22:14:31.000000000","message":"how about the following instead.\n\n  if isinstance(node, task.BaseTask):\n     future \u003d self._schedule_task(node)\n  elif isinstance(node, Retry):\n    ...\n  else:\n    raise TypeError(\"Unknown how to schedule node %s\")","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b10d5c58802087d5d15a7497e4d2ae8c355bdb71","unresolved":false,"context_lines":[{"line_number":60,"context_line":"                # executes scheduling"},{"line_number":61,"context_line":"                if isinstance(node, task.BaseTask):"},{"line_number":62,"context_line":"                    future \u003d self._schedule_task(node)"},{"line_number":63,"context_line":"                else:"},{"line_number":64,"context_line":"                    future \u003d self._schedule_retry(node)"},{"line_number":65,"context_line":"                if future is not None:"},{"line_number":66,"context_line":"                    not_done.append(future)"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2FkhQ%3D","line":63,"in_reply_to":"AAAAUX%2F%2Fxa4%3D","updated":"2014-02-12 09:42:19.000000000","message":"Done","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"249daa9021036959212d61dba367e3c3b904b201","unresolved":false,"context_lines":[{"line_number":80,"context_line":"            not_done \u003d list(not_done)"},{"line_number":81,"context_line":"            next_nodes \u003d []"},{"line_number":82,"context_line":"            for future in done:"},{"line_number":83,"context_line":"                # NOTE(harlowja): event will be used in the future for smart"},{"line_number":84,"context_line":"                # reversion (ignoring it for now)."},{"line_number":85,"context_line":"                node, event, result \u003d future.result()"},{"line_number":86,"context_line":"                if isinstance(node, task.BaseTask):"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2FxZ8%3D","line":83,"updated":"2014-02-10 22:14:31.000000000","message":"Seems like we can remove this comment now.","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b10d5c58802087d5d15a7497e4d2ae8c355bdb71","unresolved":false,"context_lines":[{"line_number":80,"context_line":"            not_done \u003d list(not_done)"},{"line_number":81,"context_line":"            next_nodes \u003d []"},{"line_number":82,"context_line":"            for future in done:"},{"line_number":83,"context_line":"                # NOTE(harlowja): event will be used in the future for smart"},{"line_number":84,"context_line":"                # reversion (ignoring it for now)."},{"line_number":85,"context_line":"                node, event, result \u003d future.result()"},{"line_number":86,"context_line":"                if isinstance(node, task.BaseTask):"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2Fkf4%3D","line":83,"in_reply_to":"AAAAUX%2F%2FxZ8%3D","updated":"2014-02-12 09:42:19.000000000","message":"Done","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"af6c85923aaf9dd96dad01a49a5fe72312afecdb","unresolved":false,"context_lines":[{"line_number":86,"context_line":"                if event \u003d\u003d ex.EXECUTED and intention \u003d\u003d st.REVERT:"},{"line_number":87,"context_line":"                    next_nodes.add(node)"},{"line_number":88,"context_line":"                elif event \u003d\u003d ex.REVERTED:"},{"line_number":89,"context_line":"                    self._storage.set_atom_intention(node.name, st.EXECUTE)"},{"line_number":90,"context_line":"                if isinstance(result, misc.Failure):"},{"line_number":91,"context_line":"                    if event \u003d\u003d ex.EXECUTED:"},{"line_number":92,"context_line":"                        self._process_atom_failure(node, result)"}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2BlCs%3D","line":89,"updated":"2014-02-20 12:04:59.000000000","message":"Why it\u0027s here? I thought this should be done in when retrying subflow (around line 144 below).","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"c052b87bbfc8cc6fae12214f95a0b27293d45aab","unresolved":false,"context_lines":[{"line_number":86,"context_line":"                if event \u003d\u003d ex.EXECUTED and intention \u003d\u003d st.REVERT:"},{"line_number":87,"context_line":"                    next_nodes.add(node)"},{"line_number":88,"context_line":"                elif event \u003d\u003d ex.REVERTED:"},{"line_number":89,"context_line":"                    self._storage.set_atom_intention(node.name, st.EXECUTE)"},{"line_number":90,"context_line":"                if isinstance(result, misc.Failure):"},{"line_number":91,"context_line":"                    if event \u003d\u003d ex.EXECUTED:"},{"line_number":92,"context_line":"                        self._process_atom_failure(node, result)"}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2BjZs%3D","line":89,"in_reply_to":"AAAAUX%2F%2BkUA%3D","updated":"2014-02-20 15:08:50.000000000","message":"Done","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"264f3f4cdf59912abf0959eb80fe5e3640a88cff","unresolved":false,"context_lines":[{"line_number":86,"context_line":"                if event \u003d\u003d ex.EXECUTED and intention \u003d\u003d st.REVERT:"},{"line_number":87,"context_line":"                    next_nodes.add(node)"},{"line_number":88,"context_line":"                elif event \u003d\u003d ex.REVERTED:"},{"line_number":89,"context_line":"                    self._storage.set_atom_intention(node.name, st.EXECUTE)"},{"line_number":90,"context_line":"                if isinstance(result, misc.Failure):"},{"line_number":91,"context_line":"                    if event \u003d\u003d ex.EXECUTED:"},{"line_number":92,"context_line":"                        self._process_atom_failure(node, result)"}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2BkUA%3D","line":89,"in_reply_to":"AAAAUX%2F%2BlCs%3D","updated":"2014-02-20 13:37:34.000000000","message":"If my analysis is correct, this is a bug.\n\nConsider flow:\n\n    linear_flow.Flow(\u0027name\u0027, retry\u003dretry.Times(5)).add(A(), B())\n\nIf A fails on first retry, intention for A and B are set to REVERT. A gets reverted (B does not, as it is still PENGDING), so we have A in (REVERTED, EXCEUTE) and B in (PENDING, REVERT). On next retry B will not be executed.","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"b6466f709b8dce8c02efbf3ef3aa2fb904dae5de","unresolved":false,"context_lines":[{"line_number":91,"context_line":"                    if event \u003d\u003d ex.EXECUTED:"},{"line_number":92,"context_line":"                        self._process_atom_failure(node, result)"},{"line_number":93,"context_line":"                        next_nodes.update("},{"line_number":94,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":95,"context_line":"                    else:"},{"line_number":96,"context_line":"                        result.reraise()"},{"line_number":97,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2BZMM%3D","line":94,"updated":"2014-02-21 12:57:43.000000000","message":"This will return all currently REVERTING tasks. So, unless I\u0027m missing something, if two different nodes in two different parts of graph fail on different iterations of while cycle (one that starts on line 73) we may end up with nodes scheduled for reversion two times. Looks like a bug.","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"7b3d3f8bebaeb4f59419b6390928345b71bee74c","unresolved":false,"context_lines":[{"line_number":91,"context_line":"                    if event \u003d\u003d ex.EXECUTED:"},{"line_number":92,"context_line":"                        self._process_atom_failure(node, result)"},{"line_number":93,"context_line":"                        next_nodes.update("},{"line_number":94,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":95,"context_line":"                    else:"},{"line_number":96,"context_line":"                        result.reraise()"},{"line_number":97,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2BYW4%3D","line":94,"in_reply_to":"AAAAUX%2F%2BYgM%3D","updated":"2014-02-21 14:24:40.000000000","message":"I thought we do, and I can\u0027t see where it is handled. REVERTING-\u003eREVERTING transition is allowed for tasks, because of resumption.","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"487f662040b09eaf3394e36d0371019e4889f954","unresolved":false,"context_lines":[{"line_number":91,"context_line":"                    if event \u003d\u003d ex.EXECUTED:"},{"line_number":92,"context_line":"                        self._process_atom_failure(node, result)"},{"line_number":93,"context_line":"                        next_nodes.update("},{"line_number":94,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":95,"context_line":"                    else:"},{"line_number":96,"context_line":"                        result.reraise()"},{"line_number":97,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2BYgM%3D","line":94,"in_reply_to":"AAAAUX%2F%2BZMM%3D","updated":"2014-02-21 14:18:38.000000000","message":"Node can\u0027t be scheduled for reversion twice, because we don\u0027t schedule nodes with status \u0027REVERTING\u0027","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"b0789c5880d6f5bef4cb93cdfbfe0ec1b3fab425","unresolved":false,"context_lines":[{"line_number":142,"context_line":"            self._retry_subflow(retry)"},{"line_number":143,"context_line":"            return self._retry_action.execute(retry)"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":"    def _process_atom_failure(self, atom, failure):"},{"line_number":146,"context_line":"        \"\"\"On atom failure find its retry controller, ask for the action to"},{"line_number":147,"context_line":"        perform with failed subflow and set proper intention for subflow nodes."},{"line_number":148,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":18,"id":"AAAAVX%2F%2Fwmw%3D","line":145,"updated":"2014-02-28 13:55:22.000000000","message":"This function is not atomic (and can\u0027t be), so if process fails there is some work to be done to bring flow to consistent state. Here is simple failing unit test:\n\n     def test_retry_after_failure_before_processig_failure(self):\n         flow \u003d uf.Flow(\u0027flow-1\u0027, retry.Times(3, provides\u003d\u0027x\u0027)).add(\n             utils.SaveOrderTask(\u0027task1\u0027))\n         engine \u003d self._make_engine(flow)\n         engine.compile()\n         # imagine we run engine\n         engine.storage.set_flow_state(st.RUNNING)\n         engine.storage.set_atom_intention(\u0027flow-1_retry\u0027, st.EXECUTE)\n         engine.storage.set_atom_intention(\u0027task1\u0027, st.EXECUTE)\n         # we execute retry\n         engine.storage.save(\u0027flow-1_retry\u0027, 1)\n         # task fails (if we comment it out, it works)\n         engine.storage.save(\u0027task1\u0027,\n                             misc.Failure.from_exception(RuntimeError(\u0027foo\u0027)),\n                             state\u003dst.FAILURE)\n         # then process die and we resume engine\n         engine.run()\n         # i would expect task1 would be retried and engine will run\n         # successfully. but what actually happens, we fail here, because flow\n         # gets reverted without considering retry and then my\n         # RuntimeError(\u0027foo\u0027) gets re-raised\n\nThe most interesting problem imo is how to determine if current failure was already added to failure_history of the retry, especially in presence of several simultaneous task failures.","commit_id":"351bb02c411f46c2964329f52e228af975068708"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"1d159fc922bb8b7b88684e27fcaec33454c82b07","unresolved":false,"context_lines":[{"line_number":142,"context_line":"            self._retry_subflow(retry)"},{"line_number":143,"context_line":"            return self._retry_action.execute(retry)"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":"    def _process_atom_failure(self, atom, failure):"},{"line_number":146,"context_line":"        \"\"\"On atom failure find its retry controller, ask for the action to"},{"line_number":147,"context_line":"        perform with failed subflow and set proper intention for subflow nodes."},{"line_number":148,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":18,"id":"AAAAVn%2F%2F8Dc%3D","line":145,"in_reply_to":"AAAAVX%2F%2Fwmw%3D","updated":"2014-03-03 09:51:24.000000000","message":"It worth noting that there are more possible failures out there. In general, when task fails and subflow should be retried, the following happens (correct me if I\u0027m wrong):\n\n    (a) we save task failure\n    (b) we save task failure to retry\u0027s failure_history\n    (c) we set retry\u0027s intention to RETRY\n    (d) we go setting intentions to REVERT for subgraph nodes\n\nEverything that happens after (c) I hope is now covered by _prepare_flow_for_resume(), but failures between (a) and (b) (test in my comment above) and between (b) and (c) apparently are not.\n\nThere are also couple of similar cases when retry for current atom says REVERT and we go to it\u0027s parent retry, and probably farther to parent of that parent, and so on.","commit_id":"351bb02c411f46c2964329f52e228af975068708"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"5050739f0d3c6c12f11462b07cc09e8cdc80aaf6","unresolved":false,"context_lines":[{"line_number":142,"context_line":"            self._retry_subflow(retry)"},{"line_number":143,"context_line":"            return self._retry_action.execute(retry)"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":"    def _process_atom_failure(self, atom, failure):"},{"line_number":146,"context_line":"        \"\"\"On atom failure find its retry controller, ask for the action to"},{"line_number":147,"context_line":"        perform with failed subflow and set proper intention for subflow nodes."},{"line_number":148,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":18,"id":"AAAAVn%2F%2Fu28%3D","line":145,"in_reply_to":"AAAAVn%2F%2F8Dc%3D","updated":"2014-03-04 12:52:14.000000000","message":"Done","commit_id":"351bb02c411f46c2964329f52e228af975068708"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"304bc2d8db9b6532e7a7660629b3264b173ada40","unresolved":false,"context_lines":[{"line_number":94,"context_line":"                        next_nodes.update("},{"line_number":95,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":96,"context_line":"                    else:"},{"line_number":97,"context_line":"                        result.reraise()"},{"line_number":98,"context_line":"                else:"},{"line_number":99,"context_line":"                    next_nodes.update(self._analyzer.get_next_nodes(node))"},{"line_number":100,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2F0JQ%3D","line":97,"updated":"2014-03-04 01:02:10.000000000","message":"Is this intended? Doesn\u0027t this cause the current futures, that are still being worked on to never be finished? Instead of previously where we just ran until either they all failed (no more next_nodes). Why reraise here? Seems bad to just leave futures running but never to complete them... Perhaps do something like before and capture these results as nodes that have failed REVERT as a special case that basically causes the execution to continue (only on currently executing nodes and marks the whole flow as ERRORed?)","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"3c51d0f8aad0ee3f93e59a54386f7b82de9e11f2","unresolved":false,"context_lines":[{"line_number":94,"context_line":"                        next_nodes.update("},{"line_number":95,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":96,"context_line":"                    else:"},{"line_number":97,"context_line":"                        result.reraise()"},{"line_number":98,"context_line":"                else:"},{"line_number":99,"context_line":"                    next_nodes.update(self._analyzer.get_next_nodes(node))"},{"line_number":100,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2FuuI%3D","line":97,"in_reply_to":"AAAAVn%2F%2F0JQ%3D","updated":"2014-03-04 12:51:45.000000000","message":"Done","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"5cf523d74fc968add63ea023039a687972a72547","unresolved":false,"context_lines":[{"line_number":95,"context_line":"                        next_nodes.update("},{"line_number":96,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":97,"context_line":"                    else:"},{"line_number":98,"context_line":"                        failure \u003d result"},{"line_number":99,"context_line":"                else:"},{"line_number":100,"context_line":"                    next_nodes.update(self._analyzer.get_next_nodes(node))"},{"line_number":101,"context_line":""}],"source_content_type":"text/x-python","patch_set":20,"id":"AAAAVn%2F%2FolU%3D","line":98,"updated":"2014-03-05 00:07:58.000000000","message":"Can\u0027t there be more than one of these? Maybe I\u0027m wrong but I thought that multiple tasks could run (and fail) and instead of just re-raising one u would need to re-raise them all (and also need to revert if that\u0027s the desired intention). If multiple tasks are executing (and many fail) then this would seem to only reraise the last one?","commit_id":"ffe357c69a264fde992d99d7038a8faa62bdf9d5"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"1b719663cdc8de32c8d589cdc564eb4709a491aa","unresolved":false,"context_lines":[{"line_number":95,"context_line":"                        next_nodes.update("},{"line_number":96,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":97,"context_line":"                    else:"},{"line_number":98,"context_line":"                        failure \u003d result"},{"line_number":99,"context_line":"                else:"},{"line_number":100,"context_line":"                    next_nodes.update(self._analyzer.get_next_nodes(node))"},{"line_number":101,"context_line":""}],"source_content_type":"text/x-python","patch_set":20,"id":"AAAAVn%2F%2FkGQ%3D","line":98,"in_reply_to":"AAAAVn%2F%2FlCQ%3D","updated":"2014-03-05 11:14:01.000000000","message":"Now it is difficult to implement resuming when revert fails, because we don\u0027t have a possibility to store errors in DB, except as task results. I propose to implement it later.","commit_id":"ffe357c69a264fde992d99d7038a8faa62bdf9d5"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"eb9259098f23029c53ac21b1e05679c45d7971d7","unresolved":false,"context_lines":[{"line_number":95,"context_line":"                        next_nodes.update("},{"line_number":96,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":97,"context_line":"                    else:"},{"line_number":98,"context_line":"                        failure \u003d result"},{"line_number":99,"context_line":"                else:"},{"line_number":100,"context_line":"                    next_nodes.update(self._analyzer.get_next_nodes(node))"},{"line_number":101,"context_line":""}],"source_content_type":"text/x-python","patch_set":20,"id":"AAAAVn%2F%2FlCQ%3D","line":98,"in_reply_to":"AAAAVn%2F%2FolU%3D","updated":"2014-03-05 08:57:26.000000000","message":"+1, and it becomes even more complicated when flow is resuming while there already are failures. I think we should not raise any exceptions from this method (except when it\u0027s taskflow\u0027s own failure) and rely on engine._run() to reraise failures if needed.","commit_id":"ffe357c69a264fde992d99d7038a8faa62bdf9d5"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"1b719663cdc8de32c8d589cdc564eb4709a491aa","unresolved":false,"context_lines":[{"line_number":95,"context_line":"                        next_nodes.update("},{"line_number":96,"context_line":"                            self._analyzer.browse_nodes_for_revert())"},{"line_number":97,"context_line":"                    else:"},{"line_number":98,"context_line":"                        failure \u003d result"},{"line_number":99,"context_line":"                else:"},{"line_number":100,"context_line":"                    next_nodes.update(self._analyzer.get_next_nodes(node))"},{"line_number":101,"context_line":""}],"source_content_type":"text/x-python","patch_set":20,"id":"AAAAVn%2F%2FkGw%3D","line":98,"in_reply_to":"AAAAVn%2F%2FolU%3D","updated":"2014-03-05 11:14:01.000000000","message":"Done","commit_id":"ffe357c69a264fde992d99d7038a8faa62bdf9d5"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"990053a69ff551a4a2b5e481cecaab3427d04c62","unresolved":false,"context_lines":[{"line_number":61,"context_line":"                elif isinstance(node, r.Retry):"},{"line_number":62,"context_line":"                    future \u003d self._schedule_retry(node)"},{"line_number":63,"context_line":"                else:"},{"line_number":64,"context_line":"                    raise TypeError(\"Unknown how to schedule node %s\")"},{"line_number":65,"context_line":"                if future is not None:"},{"line_number":66,"context_line":"                    not_done.append(future)"},{"line_number":67,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":22,"id":"AAAAVn%2F%2FVzQ%3D","line":64,"updated":"2014-03-06 18:39:23.000000000","message":"I think this is missing a format value ;)\n\n\"Unknown how to schedule node %s\" % (node) ?","commit_id":"6bc3061bd64289c1c59632699469ac3c95044f24"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"7cc2ff8d0aeb7cbb61615da64eef9db05d423452","unresolved":false,"context_lines":[{"line_number":61,"context_line":"                elif isinstance(node, r.Retry):"},{"line_number":62,"context_line":"                    future \u003d self._schedule_retry(node)"},{"line_number":63,"context_line":"                else:"},{"line_number":64,"context_line":"                    raise TypeError(\"Unknown how to schedule node %s\")"},{"line_number":65,"context_line":"                if future is not None:"},{"line_number":66,"context_line":"                    not_done.append(future)"},{"line_number":67,"context_line":"                else:"}],"source_content_type":"text/x-python","patch_set":22,"id":"AAAAVn%2F%2FPpU%3D","line":64,"in_reply_to":"AAAAVn%2F%2FVzQ%3D","updated":"2014-03-07 10:03:59.000000000","message":"Done","commit_id":"6bc3061bd64289c1c59632699469ac3c95044f24"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"878c628eb58d3e20c9c431057d30483889821e32","unresolved":false,"context_lines":[{"line_number":121,"context_line":"            return self._task_action.schedule_execution(task)"},{"line_number":122,"context_line":"        elif intention \u003d\u003d st.REVERT:"},{"line_number":123,"context_line":"            return self._task_action.schedule_reversion(task)"},{"line_number":124,"context_line":"        else:"},{"line_number":125,"context_line":"            return None"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":"    def _complete_task(self, task, event, result):"}],"source_content_type":"text/x-python","patch_set":25,"id":"AAAAWH%2F%2FyrI%3D","line":124,"updated":"2014-03-13 19:38:12.000000000","message":"What does this else do, does any code handle None being returned, whats a None future? If a None future won\u0027t work with any of the other code why even return it? Not thinking that much of the other code will work with None anyway right? Maybe throw an error instead?","commit_id":"031dcc293bfc8b58326658e958938ce78ed64ace"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"45460fff39cae5b6380060fe13a3116e414bfd36","unresolved":false,"context_lines":[{"line_number":121,"context_line":"            return self._task_action.schedule_execution(task)"},{"line_number":122,"context_line":"        elif intention \u003d\u003d st.REVERT:"},{"line_number":123,"context_line":"            return self._task_action.schedule_reversion(task)"},{"line_number":124,"context_line":"        else:"},{"line_number":125,"context_line":"            return None"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":"    def _complete_task(self, task, event, result):"}],"source_content_type":"text/x-python","patch_set":25,"id":"AAAAWH%2F%2Ftmg%3D","line":124,"in_reply_to":"AAAAWH%2F%2FyrI%3D","updated":"2014-03-14 09:47:13.000000000","message":"Done","commit_id":"031dcc293bfc8b58326658e958938ce78ed64ace"}],"taskflow/engines/action_engine/graph_analyzer.py":[{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"ba09f8e541f3201a52293b3d0a90edce537eccc0","unresolved":false,"context_lines":[{"line_number":111,"context_line":"            next_nodes \u003d []"},{"line_number":112,"context_line":"            for node in nodes:"},{"line_number":113,"context_line":"                if self._graph.nodes[node].get(\u0027retry\u0027) \u003d\u003d retry:"},{"line_number":114,"context_line":"                    yield node"},{"line_number":115,"context_line":"                    if isinstance(node, r.Retry):"},{"line_number":116,"context_line":"                        for nested_node in self.iterate_subgraph(node):"},{"line_number":117,"context_line":"                            yield nested_node"}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9dSU%3D","line":114,"updated":"2014-02-06 20:03:47.000000000","message":"Looks like for some grpahs it yields same nodes several times.","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b015671c28d95b9f235742c79be5b4ec64196d8c","unresolved":false,"context_lines":[{"line_number":111,"context_line":"            next_nodes \u003d []"},{"line_number":112,"context_line":"            for node in nodes:"},{"line_number":113,"context_line":"                if self._graph.nodes[node].get(\u0027retry\u0027) \u003d\u003d retry:"},{"line_number":114,"context_line":"                    yield node"},{"line_number":115,"context_line":"                    if isinstance(node, r.Retry):"},{"line_number":116,"context_line":"                        for nested_node in self.iterate_subgraph(node):"},{"line_number":117,"context_line":"                            yield nested_node"}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9VSk%3D","line":114,"in_reply_to":"AAAAUH%2F9dSU%3D","updated":"2014-02-07 17:10:59.000000000","message":"Done","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"ba09f8e541f3201a52293b3d0a90edce537eccc0","unresolved":false,"context_lines":[{"line_number":128,"context_line":"    def is_success(self):"},{"line_number":129,"context_line":"        for node in self._graph.nodes_iter():"},{"line_number":130,"context_line":"            state \u003d self._storage.get_task_state(node.name)"},{"line_number":131,"context_line":"            if state in (st.REVERTED, st.FAILURE):"},{"line_number":132,"context_line":"                return False"},{"line_number":133,"context_line":"        return True"}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9dRw%3D","line":131,"updated":"2014-02-06 20:03:47.000000000","message":"Why not just state !\u003d st.SUCCESS?","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b015671c28d95b9f235742c79be5b4ec64196d8c","unresolved":false,"context_lines":[{"line_number":128,"context_line":"    def is_success(self):"},{"line_number":129,"context_line":"        for node in self._graph.nodes_iter():"},{"line_number":130,"context_line":"            state \u003d self._storage.get_task_state(node.name)"},{"line_number":131,"context_line":"            if state in (st.REVERTED, st.FAILURE):"},{"line_number":132,"context_line":"                return False"},{"line_number":133,"context_line":"        return True"}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9WHs%3D","line":131,"in_reply_to":"AAAAUH%2F9dRw%3D","updated":"2014-02-07 17:10:59.000000000","message":"Done","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"249daa9021036959212d61dba367e3c3b904b201","unresolved":false,"context_lines":[{"line_number":105,"context_line":"        return all(state in (st.PENDING, st.REVERTED)"},{"line_number":106,"context_line":"                   for state, intention in six.itervalues(task_states))"},{"line_number":107,"context_line":""},{"line_number":108,"context_line":"    def iterate_subgraph(self, retry):"},{"line_number":109,"context_line":"        visited_nodes \u003d set()"},{"line_number":110,"context_line":""},{"line_number":111,"context_line":"        def iterate_internal(retry):"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2FxWY%3D","line":108,"updated":"2014-02-10 22:14:31.000000000","message":"Would using https://github.com/networkx/networkx/blob/master/networkx/algorithms/traversal/depth_first_search.py#L19 work, seems to be doing the same thing (in a little more generic way).","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"3b20f2d344c4245a2c67ef595963a3893eb7f649","unresolved":false,"context_lines":[{"line_number":105,"context_line":"        return all(state in (st.PENDING, st.REVERTED)"},{"line_number":106,"context_line":"                   for state, intention in six.itervalues(task_states))"},{"line_number":107,"context_line":""},{"line_number":108,"context_line":"    def iterate_subgraph(self, retry):"},{"line_number":109,"context_line":"        visited_nodes \u003d set()"},{"line_number":110,"context_line":""},{"line_number":111,"context_line":"        def iterate_internal(retry):"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2FiOI%3D","line":108,"in_reply_to":"AAAAUX%2F%2Fkbw%3D","updated":"2014-02-12 14:52:00.000000000","message":"Also it provides a natural iteration order:\n\nA1 -\u003e A2 -\u003e ( B1, B2, B3 ) -\u003e A3\nIt iterates current subflow (A) using BFS, but when it meets nested subflow (B), it iterates B nodes first, then continues to iterate A nodes.","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b10d5c58802087d5d15a7497e4d2ae8c355bdb71","unresolved":false,"context_lines":[{"line_number":105,"context_line":"        return all(state in (st.PENDING, st.REVERTED)"},{"line_number":106,"context_line":"                   for state, intention in six.itervalues(task_states))"},{"line_number":107,"context_line":""},{"line_number":108,"context_line":"    def iterate_subgraph(self, retry):"},{"line_number":109,"context_line":"        visited_nodes \u003d set()"},{"line_number":110,"context_line":""},{"line_number":111,"context_line":"        def iterate_internal(retry):"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2Fkbw%3D","line":108,"in_reply_to":"AAAAUX%2F%2FxWY%3D","updated":"2014-02-12 09:42:19.000000000","message":"I use bfs here and this code iterates only nodes from the current subflow and stops on nodes from the parent flow, but bfs_tree algorithm returns all graph nodes started from the source node.","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"e4102af74a28a73cc4236ddbf7a203f3fd191af3","unresolved":false,"context_lines":[{"line_number":121,"context_line":"                        next_nodes +\u003d self._graph.successors(node)"},{"line_number":122,"context_line":"            nodes \u003d next_nodes"},{"line_number":123,"context_line":""},{"line_number":124,"context_line":"    def revert_all(self):"},{"line_number":125,"context_line":"        for node in self._graph.nodes_iter():"},{"line_number":126,"context_line":"            self._storage.set_atom_intention(node.name, st.REVERT)"},{"line_number":127,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2Bkgw%3D","line":124,"updated":"2014-02-20 13:11:50.000000000","message":"Why this method is analyser? I would expect graph action do something like this.","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"487f662040b09eaf3394e36d0371019e4889f954","unresolved":false,"context_lines":[{"line_number":121,"context_line":"                        next_nodes +\u003d self._graph.successors(node)"},{"line_number":122,"context_line":"            nodes \u003d next_nodes"},{"line_number":123,"context_line":""},{"line_number":124,"context_line":"    def revert_all(self):"},{"line_number":125,"context_line":"        for node in self._graph.nodes_iter():"},{"line_number":126,"context_line":"            self._storage.set_atom_intention(node.name, st.REVERT)"},{"line_number":127,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2BYeI%3D","line":124,"in_reply_to":"AAAAUX%2F%2Bkgw%3D","updated":"2014-02-21 14:18:38.000000000","message":"Done","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"304bc2d8db9b6532e7a7660629b3264b173ada40","unresolved":false,"context_lines":[{"line_number":103,"context_line":"        return all(state in (st.PENDING, st.REVERTED)"},{"line_number":104,"context_line":"                   for state, intention in six.itervalues(task_states))"},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"    def iterate_subgraph(self, retry):"},{"line_number":107,"context_line":"        visited_nodes \u003d set()"},{"line_number":108,"context_line":"        retries_scope \u003d set()"},{"line_number":109,"context_line":"        retries_scope.add(retry)"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2F0Mg%3D","line":106,"updated":"2014-03-04 01:02:10.000000000","message":"Can u add a comment as to what this method does.\n\nIterates all nodes/successors that are connected to this retry (and any child retries...)?","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"304bc2d8db9b6532e7a7660629b3264b173ada40","unresolved":false,"context_lines":[{"line_number":103,"context_line":"        return all(state in (st.PENDING, st.REVERTED)"},{"line_number":104,"context_line":"                   for state, intention in six.itervalues(task_states))"},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"    def iterate_subgraph(self, retry):"},{"line_number":107,"context_line":"        visited_nodes \u003d set()"},{"line_number":108,"context_line":"        retries_scope \u003d set()"},{"line_number":109,"context_line":"        retries_scope.add(retry)"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2F0Mk%3D","line":106,"updated":"2014-03-04 01:02:10.000000000","message":"Can u add a comment as to what this method does.\n\nIterates all nodes/successors that are connected to this retry (and any child retries...)?","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"3c51d0f8aad0ee3f93e59a54386f7b82de9e11f2","unresolved":false,"context_lines":[{"line_number":103,"context_line":"        return all(state in (st.PENDING, st.REVERTED)"},{"line_number":104,"context_line":"                   for state, intention in six.itervalues(task_states))"},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"    def iterate_subgraph(self, retry):"},{"line_number":107,"context_line":"        visited_nodes \u003d set()"},{"line_number":108,"context_line":"        retries_scope \u003d set()"},{"line_number":109,"context_line":"        retries_scope.add(retry)"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2FutY%3D","line":106,"in_reply_to":"AAAAVn%2F%2F0Mk%3D","updated":"2014-03-04 12:51:45.000000000","message":"Done","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"304bc2d8db9b6532e7a7660629b3264b173ada40","unresolved":false,"context_lines":[{"line_number":121,"context_line":"                        next_nodes +\u003d self._graph.successors(node)"},{"line_number":122,"context_line":"            nodes \u003d next_nodes"},{"line_number":123,"context_line":""},{"line_number":124,"context_line":"    def iterate_retries(self, state\u003dNone):"},{"line_number":125,"context_line":"        for node in self._graph.nodes_iter():"},{"line_number":126,"context_line":"            if isinstance(node, r.Retry):"},{"line_number":127,"context_line":"                if not state or self.get_state(node) \u003d\u003d state:"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2F0LU%3D","line":124,"updated":"2014-03-04 01:02:10.000000000","message":"Same as above.","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"3c51d0f8aad0ee3f93e59a54386f7b82de9e11f2","unresolved":false,"context_lines":[{"line_number":121,"context_line":"                        next_nodes +\u003d self._graph.successors(node)"},{"line_number":122,"context_line":"            nodes \u003d next_nodes"},{"line_number":123,"context_line":""},{"line_number":124,"context_line":"    def iterate_retries(self, state\u003dNone):"},{"line_number":125,"context_line":"        for node in self._graph.nodes_iter():"},{"line_number":126,"context_line":"            if isinstance(node, r.Retry):"},{"line_number":127,"context_line":"                if not state or self.get_state(node) \u003d\u003d state:"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2Fuss%3D","line":124,"in_reply_to":"AAAAVn%2F%2F0LU%3D","updated":"2014-03-04 12:51:45.000000000","message":"Done","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"}],"taskflow/engines/action_engine/retry_action.py":[{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"b6466f709b8dce8c02efbf3ef3aa2fb904dae5de","unresolved":false,"context_lines":[{"line_number":28,"context_line":""},{"line_number":29,"context_line":"# Execution and reversion events."},{"line_number":30,"context_line":"EXECUTED \u003d \u0027executed\u0027"},{"line_number":31,"context_line":"REVERTED \u003d \u0027reverted\u0027"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"class RetryAction(object):"}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2BaVs%3D","line":31,"updated":"2014-02-21 12:57:43.000000000","message":"Why do you redefine them here, instead of re-using ones from executor?","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"487f662040b09eaf3394e36d0371019e4889f954","unresolved":false,"context_lines":[{"line_number":28,"context_line":""},{"line_number":29,"context_line":"# Execution and reversion events."},{"line_number":30,"context_line":"EXECUTED \u003d \u0027executed\u0027"},{"line_number":31,"context_line":"REVERTED \u003d \u0027reverted\u0027"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"class RetryAction(object):"}],"source_content_type":"text/x-python","patch_set":16,"id":"AAAAUX%2F%2BYac%3D","line":31,"in_reply_to":"AAAAUX%2F%2BaVs%3D","updated":"2014-02-21 14:18:38.000000000","message":"Done","commit_id":"44047d5b326af9a0e58b405c64609fb5df544dec"}],"taskflow/examples/retry_flow.py":[{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"c5afbfa91576f0ff98581a2914328786029c0cef","unresolved":false,"context_lines":[{"line_number":43,"context_line":"# parameters."},{"line_number":44,"context_line":""},{"line_number":45,"context_line":""},{"line_number":46,"context_line":"class SelectPhoneNumber(retry.Retry):"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    default_provides \u003d \u0027jim_number\u0027"},{"line_number":49,"context_line":""}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAAUX%2F%2B6QM%3D","line":46,"updated":"2014-02-18 09:09:59.000000000","message":"This is really cool example, but...\n\nDidn\u0027t we want to make Retry a bit more internal-ish feature? And, while preserving all the great power of Retries we designed, hide it from users behind few simple default Retry implementations, which would cover most of usecases:\n- one that always reverts (already present);\n- one that retries N times (like, Flow(retry\u003dretry.NTimes(n\u003d5)));\n- one that retries iterating over list of items given in constructor;\n- one that retires iterating over list of items that came from resources.","commit_id":"a3c31831210f03ac0221652070cd91a354c90fcd"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"83d0ca75a0de8838e76a36820d78dfb9148eef18","unresolved":false,"context_lines":[{"line_number":43,"context_line":"# parameters."},{"line_number":44,"context_line":""},{"line_number":45,"context_line":""},{"line_number":46,"context_line":"class SelectPhoneNumber(retry.Retry):"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    default_provides \u003d \u0027jim_number\u0027"},{"line_number":49,"context_line":""}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAAUX%2F%2B5fM%3D","line":46,"in_reply_to":"AAAAUX%2F%2B6QM%3D","updated":"2014-02-18 10:34:19.000000000","message":"Will do a separate change when default retries will be added","commit_id":"a3c31831210f03ac0221652070cd91a354c90fcd"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"c5afbfa91576f0ff98581a2914328786029c0cef","unresolved":false,"context_lines":[{"line_number":48,"context_line":"    default_provides \u003d \u0027jim_number\u0027"},{"line_number":49,"context_line":""},{"line_number":50,"context_line":"    def __init__(self):"},{"line_number":51,"context_line":"        super(SelectPhoneNumber, self).__init__()"},{"line_number":52,"context_line":""},{"line_number":53,"context_line":"    def execute(self, phone_directory, results_history, failures_history):"},{"line_number":54,"context_line":"        \"\"\"Select a phone number from the directory.\"\"\""}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAAUX%2F%2B6UE%3D","line":51,"updated":"2014-02-18 09:09:59.000000000","message":"Empty init is not needed in python afaik.","commit_id":"a3c31831210f03ac0221652070cd91a354c90fcd"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"83d0ca75a0de8838e76a36820d78dfb9148eef18","unresolved":false,"context_lines":[{"line_number":48,"context_line":"    default_provides \u003d \u0027jim_number\u0027"},{"line_number":49,"context_line":""},{"line_number":50,"context_line":"    def __init__(self):"},{"line_number":51,"context_line":"        super(SelectPhoneNumber, self).__init__()"},{"line_number":52,"context_line":""},{"line_number":53,"context_line":"    def execute(self, phone_directory, results_history, failures_history):"},{"line_number":54,"context_line":"        \"\"\"Select a phone number from the directory.\"\"\""}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAAUX%2F%2B5gE%3D","line":51,"in_reply_to":"AAAAUX%2F%2B6UE%3D","updated":"2014-02-18 10:34:19.000000000","message":"Done","commit_id":"a3c31831210f03ac0221652070cd91a354c90fcd"}],"taskflow/exceptions.py":[{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"249daa9021036959212d61dba367e3c3b904b201","unresolved":false,"context_lines":[{"line_number":52,"context_line":"    pass"},{"line_number":53,"context_line":""},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"class InvalidObjectType(TaskFlowException):"},{"line_number":56,"context_line":"    \"\"\"Raised when an object has different type then expected.\"\"\""},{"line_number":57,"context_line":"    pass"},{"line_number":58,"context_line":""}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2FxVE%3D","line":55,"updated":"2014-02-10 22:14:31.000000000","message":"Whats the difference between this and TypeError? Can they be the same ??","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b10d5c58802087d5d15a7497e4d2ae8c355bdb71","unresolved":false,"context_lines":[{"line_number":52,"context_line":"    pass"},{"line_number":53,"context_line":""},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"class InvalidObjectType(TaskFlowException):"},{"line_number":56,"context_line":"    \"\"\"Raised when an object has different type then expected.\"\"\""},{"line_number":57,"context_line":"    pass"},{"line_number":58,"context_line":""}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2FkbQ%3D","line":55,"in_reply_to":"AAAAUX%2F%2FxVE%3D","updated":"2014-02-12 09:42:19.000000000","message":"Removed","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"}],"taskflow/persistence/logbook.py":[{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"304bc2d8db9b6532e7a7660629b3264b173ada40","unresolved":false,"context_lines":[{"line_number":197,"context_line":""},{"line_number":198,"context_line":"    @abc.abstractmethod"},{"line_number":199,"context_line":"    def reset(self, state):"},{"line_number":200,"context_line":"        \"\"\"Resets detail results ans failures, sets a given state.\"\"\""},{"line_number":201,"context_line":""},{"line_number":202,"context_line":""},{"line_number":203,"context_line":"class TaskDetail(AtomDetail):"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2F0RI%3D","line":200,"updated":"2014-03-04 01:02:10.000000000","message":"\u0027Resets detail results and failures,\u0027","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"3c51d0f8aad0ee3f93e59a54386f7b82de9e11f2","unresolved":false,"context_lines":[{"line_number":197,"context_line":""},{"line_number":198,"context_line":"    @abc.abstractmethod"},{"line_number":199,"context_line":"    def reset(self, state):"},{"line_number":200,"context_line":"        \"\"\"Resets detail results ans failures, sets a given state.\"\"\""},{"line_number":201,"context_line":""},{"line_number":202,"context_line":""},{"line_number":203,"context_line":"class TaskDetail(AtomDetail):"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2Fusk%3D","line":200,"in_reply_to":"AAAAVn%2F%2F0RI%3D","updated":"2014-03-04 12:51:45.000000000","message":"Done","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"}],"taskflow/retry.py":[{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"b0789c5880d6f5bef4cb93cdfbfe0ec1b3fab425","unresolved":false,"context_lines":[{"line_number":179,"context_line":"    \"\"\""},{"line_number":180,"context_line":""},{"line_number":181,"context_line":"    def __init__(self, name\u003dNone, provides\u003dNone, requires\u003dNone,"},{"line_number":182,"context_line":"                 auto_extract\u003dTrue, rebind\u003dNone):"},{"line_number":183,"context_line":"        super(ParameterizedForEach, self).__init__(name, provides, requires,"},{"line_number":184,"context_line":"                                                   auto_extract, rebind)"},{"line_number":185,"context_line":""}],"source_content_type":"text/x-python","patch_set":18,"id":"AAAAU3%2F%2F4%2BE%3D","line":182,"updated":"2014-02-28 13:55:22.000000000","message":"Still a useless forwarding constructor here.","commit_id":"351bb02c411f46c2964329f52e228af975068708"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"7a3e7bf5e649a171546f564587253ba6c26328b7","unresolved":false,"context_lines":[{"line_number":179,"context_line":"    \"\"\""},{"line_number":180,"context_line":""},{"line_number":181,"context_line":"    def __init__(self, name\u003dNone, provides\u003dNone, requires\u003dNone,"},{"line_number":182,"context_line":"                 auto_extract\u003dTrue, rebind\u003dNone):"},{"line_number":183,"context_line":"        super(ParameterizedForEach, self).__init__(name, provides, requires,"},{"line_number":184,"context_line":"                                                   auto_extract, rebind)"},{"line_number":185,"context_line":""}],"source_content_type":"text/x-python","patch_set":18,"id":"AAAAVn%2F%2F5RA%3D","line":182,"in_reply_to":"AAAAU3%2F%2F4%2BE%3D","updated":"2014-03-03 17:20:03.000000000","message":"Done","commit_id":"351bb02c411f46c2964329f52e228af975068708"}],"taskflow/states.py":[{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"ba09f8e541f3201a52293b3d0a90edce537eccc0","unresolved":false,"context_lines":[{"line_number":161,"context_line":"    # NOTE(akarpinska): If flow had failed when some tasks were in RUNNING or"},{"line_number":162,"context_line":"    # REVERTING states, then those tasks should be marked as FAILURE"},{"line_number":163,"context_line":"    (REVERTING, RUNNING),"},{"line_number":164,"context_line":"    (RUNNING, REVERTING),"},{"line_number":165,"context_line":"]"},{"line_number":166,"context_line":""},{"line_number":167,"context_line":"# NOTE(harlowja): ignore transitions to the same state (in these cases)."}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9dXI%3D","line":164,"updated":"2014-02-06 20:03:47.000000000","message":"Why all those transitions are ignored? Aren\u0027t they errors?","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b015671c28d95b9f235742c79be5b4ec64196d8c","unresolved":false,"context_lines":[{"line_number":161,"context_line":"    # NOTE(akarpinska): If flow had failed when some tasks were in RUNNING or"},{"line_number":162,"context_line":"    # REVERTING states, then those tasks should be marked as FAILURE"},{"line_number":163,"context_line":"    (REVERTING, RUNNING),"},{"line_number":164,"context_line":"    (RUNNING, REVERTING),"},{"line_number":165,"context_line":"]"},{"line_number":166,"context_line":""},{"line_number":167,"context_line":"# NOTE(harlowja): ignore transitions to the same state (in these cases)."}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAUH%2F9VRQ%3D","line":164,"in_reply_to":"AAAAUH%2F9dXI%3D","updated":"2014-02-07 17:10:59.000000000","message":"I don\u0027t understand why some transitions are ignored, but other are errors, but when I use check_task_transition function I want to know if I can switch task to another state and I wonder when I get an exception for some disallowed transition.","commit_id":"ae23e1d55893abe48ef915e0c64b2625aee3d1a9"}],"taskflow/storage.py":[{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"249daa9021036959212d61dba367e3c3b904b201","unresolved":false,"context_lines":[{"line_number":322,"context_line":"        with self._lock.write_lock():"},{"line_number":323,"context_line":"            td \u003d self._taskdetail_by_name(retry_name)"},{"line_number":324,"context_line":"            if td.atom_type !\u003d logbook.RETRY_DETAIL:"},{"line_number":325,"context_line":"                raise exceptions.InvalidObjectType("},{"line_number":326,"context_line":"                    \"Atom %s is not a retry controller.\" % retry_name)"},{"line_number":327,"context_line":"            td.results[\u0027failures_history\u0027].append(failure)"},{"line_number":328,"context_line":"            self._with_connection(self._save_task_detail, task_detail\u003dtd)"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2FxUs%3D","line":325,"updated":"2014-02-10 22:14:31.000000000","message":"TypeError?","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"b10d5c58802087d5d15a7497e4d2ae8c355bdb71","unresolved":false,"context_lines":[{"line_number":322,"context_line":"        with self._lock.write_lock():"},{"line_number":323,"context_line":"            td \u003d self._taskdetail_by_name(retry_name)"},{"line_number":324,"context_line":"            if td.atom_type !\u003d logbook.RETRY_DETAIL:"},{"line_number":325,"context_line":"                raise exceptions.InvalidObjectType("},{"line_number":326,"context_line":"                    \"Atom %s is not a retry controller.\" % retry_name)"},{"line_number":327,"context_line":"            td.results[\u0027failures_history\u0027].append(failure)"},{"line_number":328,"context_line":"            self._with_connection(self._save_task_detail, task_detail\u003dtd)"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAUX%2F%2FkbA%3D","line":325,"in_reply_to":"AAAAUX%2F%2FxUs%3D","updated":"2014-02-12 09:42:19.000000000","message":"Done","commit_id":"9f0cdf149b922314aa951a89a7805c962249e826"},{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"6eb773a1df999931310531265f7dbc9d8fd76b71","unresolved":false,"context_lines":[{"line_number":514,"context_line":"            td \u003d self._taskdetail_by_name(name)"},{"line_number":515,"context_line":"            if td.failure is not None:"},{"line_number":516,"context_line":"                cached \u003d self._failures.get(name)"},{"line_number":517,"context_line":"                if td.failure.matches(cached):"},{"line_number":518,"context_line":"                    td.results.append((cached, {}))"},{"line_number":519,"context_line":"                else:"},{"line_number":520,"context_line":"                    return td.results.append((td.failure, {}))"}],"source_content_type":"text/x-python","patch_set":27,"id":"AAAAWH%2F%2FpJ8%3D","line":517,"updated":"2014-03-14 19:31:13.000000000","message":"How is this a read_lock if its appending to td.results? It appears this caching is writing/appending to td.results, which means this method should have a write lock. Is this intended to have a get_*() function acquire the write_lock, that seems a little wrong. Why not just adjust the cache in the save method and then only read from it here?","commit_id":"5a64099f6371b8b91c0ed0d458355e9e144a8993"},{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"e48b9e882d44136143bbf57c044eba037f1a0dee","unresolved":false,"context_lines":[{"line_number":515,"context_line":"            if td.failure is not None:"},{"line_number":516,"context_line":"                cached \u003d self._failures.get(name)"},{"line_number":517,"context_line":"                if td.failure.matches(cached):"},{"line_number":518,"context_line":"                    td.results.append((cached, {}))"},{"line_number":519,"context_line":"                else:"},{"line_number":520,"context_line":"                    return td.results.append((td.failure, {}))"},{"line_number":521,"context_line":"            return td.results"}],"source_content_type":"text/x-python","patch_set":27,"id":"AAAAWH%2F%2Fk54%3D","line":518,"updated":"2014-03-16 12:03:49.000000000","message":"And what if get_retry_history is called twice? Or ten times?\n\nI suggest to copy td.retults here and append an item to the copy.","commit_id":"5a64099f6371b8b91c0ed0d458355e9e144a8993"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"98870caaa52cba4522f48e880a3fbe062225a1d3","unresolved":false,"context_lines":[{"line_number":515,"context_line":"            if td.failure is not None:"},{"line_number":516,"context_line":"                cached \u003d self._failures.get(name)"},{"line_number":517,"context_line":"                if td.failure.matches(cached):"},{"line_number":518,"context_line":"                    td.results.append((cached, {}))"},{"line_number":519,"context_line":"                else:"},{"line_number":520,"context_line":"                    return td.results.append((td.failure, {}))"},{"line_number":521,"context_line":"            return td.results"}],"source_content_type":"text/x-python","patch_set":27,"id":"AAAAWH%2F%2Fhzw%3D","line":518,"in_reply_to":"AAAAWH%2F%2Fk54%3D","updated":"2014-03-17 10:02:49.000000000","message":"Done","commit_id":"5a64099f6371b8b91c0ed0d458355e9e144a8993"}],"taskflow/tests/unit/test_retries.py":[{"author":{"_account_id":7366,"name":"Ivan Melnikov","email":"imelnikov@griddynamics.com","username":"imelnikov"},"change_message_id":"edfb17f794920628a8cf57b587d75698e78a534a","unresolved":false,"context_lines":[{"line_number":548,"context_line":"        engine.storage.inject({\u0027values\u0027: values, \u0027y\u0027: 1})"},{"line_number":549,"context_line":"        self.assertRaisesRegexp(exc.NotFound, \u0027^No elements left\u0027, engine.run)"},{"line_number":550,"context_line":""},{"line_number":551,"context_line":"    def test_retry_after_failure_before_processig_failure(self):"},{"line_number":552,"context_line":"        flow \u003d uf.Flow(\u0027flow-1\u0027, retry.Times(3, provides\u003d\u0027x\u0027)).add("},{"line_number":553,"context_line":"            utils.SaveOrderTask(\u0027task1\u0027))"},{"line_number":554,"context_line":"        engine \u003d self._make_engine(flow)"}],"source_content_type":"text/x-python","patch_set":26,"id":"AAAAWH%2F%2FsdE%3D","line":551,"updated":"2014-03-14 13:01:47.000000000","message":"processing?","commit_id":"b58fe213279349db6fd0aeb43276c9ecec34858c"}],"taskflow/tests/utils.py":[{"author":{"_account_id":1297,"name":"Joshua Harlow","email":"jxharlow@godaddy.com","username":"harlowja"},"change_message_id":"304bc2d8db9b6532e7a7660629b3264b173ada40","unresolved":false,"context_lines":[{"line_number":99,"context_line":"            return dict((k, k) for k in self.provides)"},{"line_number":100,"context_line":""},{"line_number":101,"context_line":""},{"line_number":102,"context_line":"def callback(state, values, details):"},{"line_number":103,"context_line":"    name \u003d details.get(\u0027task_name\u0027, None)"},{"line_number":104,"context_line":"    if not name:"},{"line_number":105,"context_line":"        name \u003d details.get(\u0027retry_name\u0027, \u0027\u003cunknown\u003e\u0027)"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2F0ZU%3D","line":102,"updated":"2014-03-04 01:02:10.000000000","message":"How about call this \u0027task_callback\u0027 since u called the other one \u0027flow_callback\u0027? :)","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"},{"author":{"_account_id":7349,"name":"Anastasia Karpinska","email":"akarpinska@griddynamics.com","username":"anastasia-karpinska"},"change_message_id":"3c51d0f8aad0ee3f93e59a54386f7b82de9e11f2","unresolved":false,"context_lines":[{"line_number":99,"context_line":"            return dict((k, k) for k in self.provides)"},{"line_number":100,"context_line":""},{"line_number":101,"context_line":""},{"line_number":102,"context_line":"def callback(state, values, details):"},{"line_number":103,"context_line":"    name \u003d details.get(\u0027task_name\u0027, None)"},{"line_number":104,"context_line":"    if not name:"},{"line_number":105,"context_line":"        name \u003d details.get(\u0027retry_name\u0027, \u0027\u003cunknown\u003e\u0027)"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAVn%2F%2FusA%3D","line":102,"in_reply_to":"AAAAVn%2F%2F0ZU%3D","updated":"2014-03-04 12:51:45.000000000","message":"Done","commit_id":"ad137e3ac937af6fc85f82c2f203bb6511891661"}]}
