Repository: ray-project/ray

Issue:
UI timeline doesn't work when remote function has an argument of type "bytes".
To reproduce the issue, run the following

```python
import ray
ray.init()

@ray.remote
def f(x):
    pass

f.remote(b'hello')
```

Then go to the UI and try to generate the timeline. I see the following

```
Collected profiles for 2 tasks.
Dumping task profile data to /var/folders/15/54jf68993rd7753c5fms424r0000gn/T/tmpm91862cq.json, this might take a while...
Creating JSON 6/2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/Workspace/ray/python/ray/experimental/ui.py in handle_submit(sender)
    390                                              breakdowns=breakdown,
    391                                              obj_dep=obj_dep.value,
--> 392                                              task_dep=task_dep.value)
    393         print("Opening html file in browser...")
    394 

~/Workspace/ray/python/ray/experimental/state.py in dump_catapult_trace(self, path, task_info, breakdowns, task_dep, obj_dep)
    823         print("Creating JSON {}/{}".format(len(full_trace), len(task_info)))
    824         with open(path, "w") as outfile:
--> 825             json.dump(full_trace, outfile)
    826 
    827     def _get_times(self, data):

~/anaconda3/lib/python3.6/json/__init__.py in dump(obj, fp, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    177     # could accelerate with writelines in some versions of Python, at
    178     # a debuggability cost
--> 179     for chunk in iterable:
    180         fp.write(chunk)
    181 

~/anaconda3/lib/python3.6/json/encoder.py in _iterencode(o, _current_indent_level)
    426             yield _floatstr(o)
    427         elif isinstance(o, (list, tuple)):
--> 428             yield from _iterencode_list(o, _current_indent_level)
    429         elif isinstance(o, dict):
    430             yield from _iterencode_dict(o, _current_indent_level)

~/anaconda3/lib/python3.6/json/encoder.py in _iterencode_list(lst, _current_indent_level)
    323                 else:
    324                     chunks = _iterencode(value, _current_indent_level)
--> 325                 yield from chunks
    326         if newline_indent is not None:
    327             _current_indent_level -= 1

~/anaconda3/lib/python3.6/json/encoder.py in _iterencode_dict(dct, _current_indent_level)
    402                 else:
    403                     chunks = _iterencode(value, _current_indent_level)
--> 404                 yield from chunks
    405         if newline_indent is not None:
    406             _current_indent_level -= 1

~/anaconda3/lib/python3.6/json/encoder.py in _iterencode_dict(dct, _current_indent_level)
    402                 else:
    403                     chunks = _iterencode(value, _current_indent_level)
--> 404                 yield from chunks
    405         if newline_indent is not None:
    406             _current_indent_level -= 1

~/anaconda3/lib/python3.6/json/encoder.py in _iterencode_list(lst, _current_indent_level)
    323                 else:
    324                     chunks = _iterencode(value, _current_indent_level)
--> 325                 yield from chunks
    326         if newline_indent is not None:
    327             _current_indent_level -= 1

~/anaconda3/lib/python3.6/json/encoder.py in _iterencode(o, _current_indent_level)
    435                     raise ValueError("Circular reference detected")
    436                 markers[markerid] = o
--> 437             o = _default(o)
    438             yield from _iterencode(o, _current_indent_level)
    439             if markers is not None:

~/anaconda3/lib/python3.6/json/encoder.py in default(self, o)
    178         """
    179         raise TypeError("Object of type '%s' is not JSON serializable" %
--> 180                         o.__class__.__name__)
    181 
    182     def encode(self, o):

TypeError: Object of type 'bytes' is not JSON serializable
```


Discussion:

Resolving patch:
diff --git a/python/ray/experimental/state.py b/python/ray/experimental/state.py
--- a/python/ray/experimental/state.py
+++ b/python/ray/experimental/state.py
@@ -594,6 +594,10 @@ def dump_catapult_trace(self,
                 # TODO (hme): do something to correct slider here,
                 # slider should be correct to begin with, though.
                 task_table[task_id] = self.task_table(task_id)
+                task_table[task_id]["TaskSpec"]["Args"] = [
+                    repr(arg)
+                    for arg in task_table[task_id]["TaskSpec"]["Args"]
+                ]
             except Exception as e:
                 print("Could not find task {}".format(task_id))
 


Test patch:
diff --git a/test/runtest.py b/test/runtest.py
--- a/test/runtest.py
+++ b/test/runtest.py
@@ -2057,7 +2057,7 @@ def testDumpTraceFile(self):
         ray.init(redirect_output=True)
 
         @ray.remote
-        def f():
+        def f(*xs):
             return 1
 
         @ray.remote
@@ -2068,7 +2068,16 @@ def __init__(self):
             def method(self):
                 pass
 
-        ray.get([f.remote() for _ in range(10)])
+        # We use a number of test objects because objects that are not JSON
+        # serializable caused problems in the past.
+        test_objects = [
+            0, 0.5, "hi", b"hi",
+            ray.put(0),
+            np.zeros(3), [0], (0, ), {
+                0: 0
+            }, True, False, None
+        ]
+        ray.get([f.remote(obj) for obj in test_objects])
         actors = [Foo.remote() for _ in range(5)]
         ray.get([actor.method.remote() for actor in actors])
         ray.get([actor.method.remote() for actor in actors])