Repository: PrefectHQ/prefect

Issue:
Add `Aborted` state
- `Failed` subclass used to indicate that a flow run (or task) was canceled due to user input.
Update state colors + doc diagram
To match @itsngansense 's awesome new color palette:
<img width="650" alt="states" src="https://user-images.githubusercontent.com/13255838/56510837-daa14f00-64df-11e9-9295-08eda2d5ae5b.png">



Discussion:

Resolving patch:
diff --git a/src/prefect/engine/state.py b/src/prefect/engine/state.py
--- a/src/prefect/engine/state.py
+++ b/src/prefect/engine/state.py
@@ -41,7 +41,7 @@ class State:
         - result (Any, optional): Defaults to `None`. A data payload for the state.
     """
 
-    color = "#000000"
+    color = "#696969"
 
     def __init__(self, message: str = None, result: Any = NoResult):
         self.message = message
@@ -234,7 +234,7 @@ class Pending(State):
             keys to fully hydrated `Result`s.  Used / set if the Task requires Retries.
     """
 
-    color = "#d3d3d3"
+    color = "#7ebdff"
 
     def __init__(
         self,
@@ -258,7 +258,7 @@ class Paused(Pending):
             keys to fully hydrated `Result`s.  Used / set if the Task requires Retries.
     """
 
-    color = "#800000"
+    color = "#cfd8dc"
 
 
 class Scheduled(Pending):
@@ -278,7 +278,7 @@ class Scheduled(Pending):
             keys to fully hydrated `Result`s.  Used / set if the Task requires Retries.
     """
 
-    color = "#b0c4de"
+    color = "#ffab00"
 
     def __init__(
         self,
@@ -301,8 +301,6 @@ class _MetaState(State):
     easily identified.
     """
 
-    color = "#b0c4de"
-
     def __init__(
         self, message: str = None, result: Any = NoResult, state: State = None
     ):
@@ -327,6 +325,8 @@ class ClientFailed(_MetaState):
 
     """
 
+    color = "#eb0000"
+
 
 class Submitted(_MetaState):
     """
@@ -347,6 +347,8 @@ class Submitted(_MetaState):
 
     """
 
+    color = "#ffdf5d"
+
 
 class Queued(_MetaState):
     """
@@ -365,6 +367,8 @@ class Queued(_MetaState):
 
     """
 
+    color = "#ffea7f"
+
     def __init__(
         self,
         message: str = None,
@@ -389,7 +393,7 @@ class Resume(Scheduled):
             keys to fully hydrated `Result`s.  Used / set if the Task requires Retries.
     """
 
-    color = "#20B2AA"
+    color = "#fb8532"
 
 
 class Retrying(Scheduled):
@@ -408,7 +412,7 @@ class Retrying(Scheduled):
             if that value isn't found.
     """
 
-    color = "#FFFF00"
+    color = "#f66a0a"
 
     def __init__(
         self,
@@ -445,7 +449,7 @@ class Running(State):
         - result (Any, optional): Defaults to `None`. A data payload for the state.
     """
 
-    color = "#00FF00"
+    color = "#3d67ff"
 
 
 # -------------------------------------------------------------------
@@ -463,7 +467,7 @@ class Finished(State):
         - result (Any, optional): Defaults to `None`. A data payload for the state.
     """
 
-    color = "#BA55D3"
+    color = "#003ccb"
 
 
 class Success(Finished):
@@ -476,7 +480,7 @@ class Success(Finished):
         - result (Any, optional): Defaults to `None`. A data payload for the state.
     """
 
-    color = "#008000"
+    color = "#28a745"
 
 
 class Cached(Success):
@@ -495,7 +499,7 @@ class Cached(Success):
             expires and can no longer be used. Defaults to `None`
     """
 
-    color = "#ffa500"
+    color = "#34d058"
 
     def __init__(
         self,
@@ -533,7 +537,7 @@ class Mapped(Success):
             of itself to map its operation over its inputs. Those copies are the children.
     """
 
-    color = "#97FFFF"
+    color = "#003ccb"
 
     def __init__(
         self,
@@ -559,7 +563,20 @@ class Failed(Finished):
         - result (Any, optional): Defaults to `None`. A data payload for the state.
     """
 
-    color = "#FF0000"
+    color = "#eb0000"
+
+
+class Aborted(Failed):
+    """
+    Finished state indicating that a user aborted the flow run manually.
+
+    Args:
+        - message (str or Exception, optional): Defaults to `None`. A message about the
+            state, which could be an `Exception` (or [`Signal`](signals.html)) that caused it.
+        - result (Any, optional): Defaults to `None`. A data payload for the state.
+    """
+
+    color = "#c42800"
 
 
 class TimedOut(Failed):
@@ -574,7 +591,7 @@ class TimedOut(Failed):
             keys to fully hydrated `Result`s.  Used / set if the Task requires Retries.
     """
 
-    color = "#CDC9A5"
+    color = "#ff4e33"
 
     def __init__(
         self,
@@ -596,7 +613,7 @@ class TriggerFailed(Failed):
         - result (Any, optional): Defaults to `None`. A data payload for the state.
     """
 
-    color = "#F08080"
+    color = "#ff5131"
 
 
 class Skipped(Success):
@@ -609,7 +626,7 @@ class Skipped(Success):
         - result (Any, optional): Defaults to `None`. A data payload for the state.
     """
 
-    color = "#F0FFF0"
+    color = "#62757f"
 
     # note: this does not allow setting "cached" as Success states do
     def __init__(self, message: str = None, result: Any = NoResult):
diff --git a/src/prefect/serialization/state.py b/src/prefect/serialization/state.py
--- a/src/prefect/serialization/state.py
+++ b/src/prefect/serialization/state.py
@@ -146,6 +146,11 @@ class Meta:
         object_class = state.Failed
 
 
+class AbortedSchema(FailedSchema):
+    class Meta:
+        object_class = state.Aborted
+
+
 class TimedOutSchema(FinishedSchema):
     class Meta:
         object_class = state.TimedOut
@@ -182,6 +187,7 @@ class StateSchema(OneOfSchema):
 
     # map class name to schema
     type_schemas = {
+        "Aborted": AbortedSchema,
         "Cached": CachedSchema,
         "ClientFailed": ClientFailedSchema,
         "Failed": FailedSchema,


Test patch:
diff --git a/tests/core/test_flow.py b/tests/core/test_flow.py
--- a/tests/core/test_flow.py
+++ b/tests/core/test_flow.py
@@ -1008,9 +1008,20 @@ def test_viz_reflects_mapping_if_flow_state_provided(self):
             )
 
         # one colored node for each mapped result
-        assert 'label="a_nice_task <map>" color="#00800080"' in graph.source
-        assert 'label="a_nice_task <map>" color="#FF000080"' in graph.source
-        assert 'label=a_list_task color="#00800080"' in graph.source
+        assert (
+            'label="a_nice_task <map>" color="{success}80"'.format(
+                success=Success.color
+            )
+            in graph.source
+        )
+        assert (
+            'label="a_nice_task <map>" color="{failed}80"'.format(failed=Failed.color)
+            in graph.source
+        )
+        assert (
+            'label=a_list_task color="{success}80"'.format(success=Success.color)
+            in graph.source
+        )
         assert 'label=8 color="#00000080"' in graph.source
 
         # two edges for each input to add()
diff --git a/tests/engine/test_state.py b/tests/engine/test_state.py
--- a/tests/engine/test_state.py
+++ b/tests/engine/test_state.py
@@ -11,6 +11,7 @@
 from prefect.engine.result import NoResult, Result, SafeResult
 from prefect.engine.result_handlers import JSONResultHandler, LocalResultHandler
 from prefect.engine.state import (
+    Aborted,
     Cached,
     ClientFailed,
     Failed,
@@ -281,6 +282,9 @@ def test_success_is_finished(self):
     def test_failed_is_finished(self):
         assert issubclass(Failed, Finished)
 
+    def test_aborted_is_failed(self):
+        assert issubclass(Aborted, Failed)
+
     def test_trigger_failed_is_finished(self):
         assert issubclass(TriggerFailed, Finished)
 
@@ -303,6 +307,7 @@ def test_trigger_failed_is_failed(self):
 @pytest.mark.parametrize(
     "state_check",
     [
+        dict(state=Aborted(), assert_true={"is_finished", "is_failed"}),
         dict(state=Cached(), assert_true={"is_cached", "is_finished", "is_successful"}),
         dict(state=ClientFailed(), assert_true={"is_meta_state"}),
         dict(state=Failed(), assert_true={"is_finished", "is_failed"}),