We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[tune] Add `--output` to the Tune docs
We should add --output to the docs.

_Originally posted by @richardliaw in https://github.com/ray-project/ray/pull/4322#issuecomment-477903993_

cc @andrewztan

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `python/ray/tune/commands.py`
Content:
```
1 from __future__ import absolute_import
2 from __future__ import division
3 from __future__ import print_function
4 
5 import glob
6 import json
7 import logging
8 import os
9 import sys
10 import subprocess
11 import operator
12 from datetime import datetime
13 
14 import pandas as pd
15 from pandas.api.types import is_string_dtype, is_numeric_dtype
16 from ray.tune.util import flatten_dict
17 from ray.tune.result import TRAINING_ITERATION, MEAN_ACCURACY, MEAN_LOSS
18 from ray.tune.trial import Trial
19 try:
20     from tabulate import tabulate
21 except ImportError:
22     tabulate = None
23 
24 logger = logging.getLogger(__name__)
25 
26 EDITOR = os.getenv("EDITOR", "vim")
27 
28 TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S (%A)"
29 
30 DEFAULT_EXPERIMENT_INFO_KEYS = (
31     "trainable_name",
32     "experiment_tag",
33     "trial_id",
34     "status",
35     "last_update_time",
36 )
37 
38 DEFAULT_RESULT_KEYS = (TRAINING_ITERATION, MEAN_ACCURACY, MEAN_LOSS)
39 
40 DEFAULT_PROJECT_INFO_KEYS = (
41     "name",
42     "total_trials",
43     "running_trials",
44     "terminated_trials",
45     "error_trials",
46     "last_updated",
47 )
48 
49 try:
50     TERM_HEIGHT, TERM_WIDTH = subprocess.check_output(["stty", "size"]).split()
51     TERM_HEIGHT, TERM_WIDTH = int(TERM_HEIGHT), int(TERM_WIDTH)
52 except subprocess.CalledProcessError:
53     TERM_HEIGHT, TERM_WIDTH = 100, 100
54 
55 OPERATORS = {
56     '<': operator.lt,
57     '<=': operator.le,
58     '==': operator.eq,
59     '!=': operator.ne,
60     '>=': operator.ge,
61     '>': operator.gt,
62 }
63 
64 
65 def _check_tabulate():
66     """Checks whether tabulate is installed."""
67     if tabulate is None:
68         raise ImportError(
69             "Tabulate not installed. Please run `pip install tabulate`.")
70 
71 
72 def print_format_output(dataframe):
73     """Prints output of given dataframe to fit into terminal.
74 
75     Returns:
76         table (pd.DataFrame): Final outputted dataframe.
77         dropped_cols (list): Columns dropped due to terminal size.
78         empty_cols (list): Empty columns (dropped on default).
79     """
80     print_df = pd.DataFrame()
81     dropped_cols = []
82     empty_cols = []
83     # column display priority is based on the info_keys passed in
84     for i, col in enumerate(dataframe):
85         if dataframe[col].isnull().all():
86             # Don't add col to print_df if is fully empty
87             empty_cols += [col]
88             continue
89 
90         print_df[col] = dataframe[col]
91         test_table = tabulate(print_df, headers="keys", tablefmt="psql")
92         if str(test_table).index('\n') > TERM_WIDTH:
93             # Drop all columns beyond terminal width
94             print_df.drop(col, axis=1, inplace=True)
95             dropped_cols += list(dataframe.columns)[i:]
96             break
97 
98     table = tabulate(
99         print_df, headers="keys", tablefmt="psql", showindex="never")
100 
101     print(table)
102     if dropped_cols:
103         print("Dropped columns:", dropped_cols)
104         print("Please increase your terminal size to view remaining columns.")
105     if empty_cols:
106         print("Empty columns:", empty_cols)
107 
108     return table, dropped_cols, empty_cols
109 
110 
111 def _get_experiment_state(experiment_path, exit_on_fail=False):
112     experiment_path = os.path.expanduser(experiment_path)
113     experiment_state_paths = glob.glob(
114         os.path.join(experiment_path, "experiment_state*.json"))
115     if not experiment_state_paths:
116         if exit_on_fail:
117             print("No experiment state found!")
118             sys.exit(0)
119         else:
120             return
121     experiment_filename = max(list(experiment_state_paths))
122 
123     with open(experiment_filename) as f:
124         experiment_state = json.load(f)
125     return experiment_state
126 
127 
128 def list_trials(experiment_path,
129                 sort=None,
130                 output=None,
131                 filter_op=None,
132                 info_keys=DEFAULT_EXPERIMENT_INFO_KEYS,
133                 result_keys=DEFAULT_RESULT_KEYS):
134     """Lists trials in the directory subtree starting at the given path.
135 
136     Args:
137         experiment_path (str): Directory where trials are located.
138             Corresponds to Experiment.local_dir/Experiment.name.
139         sort (str): Key to sort by.
140         output (str): Name of file where output is saved.
141         filter_op (str): Filter operation in the format
142             "<column> <operator> <value>".
143         info_keys (list): Keys that are displayed.
144         result_keys (list): Keys of last result that are displayed.
145     """
146     _check_tabulate()
147     experiment_state = _get_experiment_state(
148         experiment_path, exit_on_fail=True)
149 
150     checkpoint_dicts = experiment_state["checkpoints"]
151     checkpoint_dicts = [flatten_dict(g) for g in checkpoint_dicts]
152     checkpoints_df = pd.DataFrame(checkpoint_dicts)
153 
154     result_keys = ["last_result:{}".format(k) for k in result_keys]
155     col_keys = [
156         k for k in list(info_keys) + result_keys if k in checkpoints_df
157     ]
158     checkpoints_df = checkpoints_df[col_keys]
159 
160     if "last_update_time" in checkpoints_df:
161         with pd.option_context("mode.use_inf_as_null", True):
162             datetime_series = checkpoints_df["last_update_time"].dropna()
163 
164         datetime_series = datetime_series.apply(
165             lambda t: datetime.fromtimestamp(t).strftime(TIMESTAMP_FORMAT))
166         checkpoints_df["last_update_time"] = datetime_series
167 
168     if "logdir" in checkpoints_df:
169         # logdir often too verbose to view in table, so drop experiment_path
170         checkpoints_df["logdir"] = checkpoints_df["logdir"].str.replace(
171             experiment_path, '')
172 
173     if filter_op:
174         col, op, val = filter_op.split(' ')
175         col_type = checkpoints_df[col].dtype
176         if is_numeric_dtype(col_type):
177             val = float(val)
178         elif is_string_dtype(col_type):
179             val = str(val)
180         # TODO(Andrew): add support for datetime and boolean
181         else:
182             raise ValueError("Unsupported dtype for '{}': {}".format(
183                 val, col_type))
184         op = OPERATORS[op]
185         filtered_index = op(checkpoints_df[col], val)
186         checkpoints_df = checkpoints_df[filtered_index]
187 
188     if sort:
189         if sort not in checkpoints_df:
190             raise KeyError("Sort Index '{}' not in: {}".format(
191                 sort, list(checkpoints_df)))
192         checkpoints_df = checkpoints_df.sort_values(by=sort)
193 
194     print_format_output(checkpoints_df)
195 
196     if output:
197         experiment_path = os.path.expanduser(experiment_path)
198         output_path = os.path.join(experiment_path, output)
199         file_extension = os.path.splitext(output)[1].lower()
200         if file_extension in (".p", ".pkl", ".pickle"):
201             checkpoints_df.to_pickle(output_path)
202         elif file_extension == ".csv":
203             checkpoints_df.to_csv(output_path, index=False)
204         else:
205             raise ValueError("Unsupported filetype: {}".format(output))
206         print("Output saved at:", output_path)
207 
208 
209 def list_experiments(project_path,
210                      sort=None,
211                      output=None,
212                      filter_op=None,
213                      info_keys=DEFAULT_PROJECT_INFO_KEYS):
214     """Lists experiments in the directory subtree.
215 
216     Args:
217         project_path (str): Directory where experiments are located.
218             Corresponds to Experiment.local_dir.
219         sort (str): Key to sort by.
220         output (str): Name of file where output is saved.
221         filter_op (str): Filter operation in the format
222             "<column> <operator> <value>".
223         info_keys (list): Keys that are displayed.
224     """
225     _check_tabulate()
226     base, experiment_folders, _ = next(os.walk(project_path))
227 
228     experiment_data_collection = []
229 
230     for experiment_dir in experiment_folders:
231         experiment_state = _get_experiment_state(
232             os.path.join(base, experiment_dir))
233         if not experiment_state:
234             logger.debug("No experiment state found in %s", experiment_dir)
235             continue
236 
237         checkpoints = pd.DataFrame(experiment_state["checkpoints"])
238         runner_data = experiment_state["runner_data"]
239 
240         # Format time-based values.
241         time_values = {
242             "start_time": runner_data.get("_start_time"),
243             "last_updated": experiment_state.get("timestamp"),
244         }
245 
246         formatted_time_values = {
247             key: datetime.fromtimestamp(val).strftime(TIMESTAMP_FORMAT)
248             if val else None
249             for key, val in time_values.items()
250         }
251 
252         experiment_data = {
253             "name": experiment_dir,
254             "total_trials": checkpoints.shape[0],
255             "running_trials": (checkpoints["status"] == Trial.RUNNING).sum(),
256             "terminated_trials": (
257                 checkpoints["status"] == Trial.TERMINATED).sum(),
258             "error_trials": (checkpoints["status"] == Trial.ERROR).sum(),
259         }
260         experiment_data.update(formatted_time_values)
261         experiment_data_collection.append(experiment_data)
262 
263     if not experiment_data_collection:
264         print("No experiments found!")
265         sys.exit(0)
266 
267     info_df = pd.DataFrame(experiment_data_collection)
268     col_keys = [k for k in list(info_keys) if k in info_df]
269     if not col_keys:
270         print("None of keys {} in experiment data!".format(info_keys))
271         sys.exit(0)
272     info_df = info_df[col_keys]
273 
274     if filter_op:
275         col, op, val = filter_op.split(' ')
276         col_type = info_df[col].dtype
277         if is_numeric_dtype(col_type):
278             val = float(val)
279         elif is_string_dtype(col_type):
280             val = str(val)
281         # TODO(Andrew): add support for datetime and boolean
282         else:
283             raise ValueError("Unsupported dtype for '{}': {}".format(
284                 val, col_type))
285         op = OPERATORS[op]
286         filtered_index = op(info_df[col], val)
287         info_df = info_df[filtered_index]
288 
289     if sort:
290         if sort not in info_df:
291             raise KeyError("Sort Index '{}' not in: {}".format(
292                 sort, list(info_df)))
293         info_df = info_df.sort_values(by=sort)
294 
295     print_format_output(info_df)
296 
297     if output:
298         output_path = os.path.join(base, output)
299         file_extension = os.path.splitext(output)[1].lower()
300         if file_extension in (".p", ".pkl", ".pickle"):
301             info_df.to_pickle(output_path)
302         elif file_extension == ".csv":
303             info_df.to_csv(output_path, index=False)
304         else:
305             raise ValueError("Unsupported filetype: {}".format(output))
306         print("Output saved at:", output_path)
307 
308 
309 def add_note(path, filename="note.txt"):
310     """Opens a txt file at the given path where user can add and save notes.
311 
312     Args:
313         path (str): Directory where note will be saved.
314         filename (str): Name of note. Defaults to "note.txt"
315     """
316     path = os.path.expanduser(path)
317     assert os.path.isdir(path), "{} is not a valid directory.".format(path)
318 
319     filepath = os.path.join(path, filename)
320     exists = os.path.isfile(filepath)
321 
322     try:
323         subprocess.call([EDITOR, filepath])
324     except Exception as exc:
325         logger.error("Editing note failed!")
326         raise exc
327     if exists:
328         print("Note updated at:", filepath)
329     else:
330         print("Note created at:", filepath)
331
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/python/ray/tune/commands.py b/python/ray/tune/commands.py
--- a/python/ray/tune/commands.py
+++ b/python/ray/tune/commands.py
@@ -194,16 +194,14 @@
     print_format_output(checkpoints_df)
 
     if output:
-        experiment_path = os.path.expanduser(experiment_path)
-        output_path = os.path.join(experiment_path, output)
         file_extension = os.path.splitext(output)[1].lower()
         if file_extension in (".p", ".pkl", ".pickle"):
-            checkpoints_df.to_pickle(output_path)
+            checkpoints_df.to_pickle(output)
         elif file_extension == ".csv":
-            checkpoints_df.to_csv(output_path, index=False)
+            checkpoints_df.to_csv(output, index=False)
         else:
             raise ValueError("Unsupported filetype: {}".format(output))
-        print("Output saved at:", output_path)
+        print("Output saved at:", output)
 
 
 def list_experiments(project_path,
@@ -295,15 +293,14 @@
     print_format_output(info_df)
 
     if output:
-        output_path = os.path.join(base, output)
         file_extension = os.path.splitext(output)[1].lower()
         if file_extension in (".p", ".pkl", ".pickle"):
-            info_df.to_pickle(output_path)
+            info_df.to_pickle(output)
         elif file_extension == ".csv":
-            info_df.to_csv(output_path, index=False)
+            info_df.to_csv(output, index=False)
         else:
             raise ValueError("Unsupported filetype: {}".format(output))
-        print("Output saved at:", output_path)
+        print("Output saved at:", output)
 
 
 def add_note(path, filename="note.txt"):

{"golden_diff": "diff --git a/python/ray/tune/commands.py b/python/ray/tune/commands.py\n--- a/python/ray/tune/commands.py\n+++ b/python/ray/tune/commands.py\n@@ -194,16 +194,14 @@\n     print_format_output(checkpoints_df)\n \n     if output:\n-        experiment_path = os.path.expanduser(experiment_path)\n-        output_path = os.path.join(experiment_path, output)\n         file_extension = os.path.splitext(output)[1].lower()\n         if file_extension in (\".p\", \".pkl\", \".pickle\"):\n-            checkpoints_df.to_pickle(output_path)\n+            checkpoints_df.to_pickle(output)\n         elif file_extension == \".csv\":\n-            checkpoints_df.to_csv(output_path, index=False)\n+            checkpoints_df.to_csv(output, index=False)\n         else:\n             raise ValueError(\"Unsupported filetype: {}\".format(output))\n-        print(\"Output saved at:\", output_path)\n+        print(\"Output saved at:\", output)\n \n \n def list_experiments(project_path,\n@@ -295,15 +293,14 @@\n     print_format_output(info_df)\n \n     if output:\n-        output_path = os.path.join(base, output)\n         file_extension = os.path.splitext(output)[1].lower()\n         if file_extension in (\".p\", \".pkl\", \".pickle\"):\n-            info_df.to_pickle(output_path)\n+            info_df.to_pickle(output)\n         elif file_extension == \".csv\":\n-            info_df.to_csv(output_path, index=False)\n+            info_df.to_csv(output, index=False)\n         else:\n             raise ValueError(\"Unsupported filetype: {}\".format(output))\n-        print(\"Output saved at:\", output_path)\n+        print(\"Output saved at:\", output)\n \n \n def add_note(path, filename=\"note.txt\"):\n", "issue": "[tune] Add `--output` to the Tune docs\nWe should add --output to the docs.\r\n\r\n_Originally posted by @richardliaw in https://github.com/ray-project/ray/pull/4322#issuecomment-477903993_\r\n\r\ncc @andrewztan\n", "before_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport glob\nimport json\nimport logging\nimport os\nimport sys\nimport subprocess\nimport operator\nfrom datetime import datetime\n\nimport pandas as pd\nfrom pandas.api.types import is_string_dtype, is_numeric_dtype\nfrom ray.tune.util import flatten_dict\nfrom ray.tune.result import TRAINING_ITERATION, MEAN_ACCURACY, MEAN_LOSS\nfrom ray.tune.trial import Trial\ntry:\n    from tabulate import tabulate\nexcept ImportError:\n    tabulate = None\n\nlogger = logging.getLogger(__name__)\n\nEDITOR = os.getenv(\"EDITOR\", \"vim\")\n\nTIMESTAMP_FORMAT = \"%Y-%m-%d %H:%M:%S (%A)\"\n\nDEFAULT_EXPERIMENT_INFO_KEYS = (\n    \"trainable_name\",\n    \"experiment_tag\",\n    \"trial_id\",\n    \"status\",\n    \"last_update_time\",\n)\n\nDEFAULT_RESULT_KEYS = (TRAINING_ITERATION, MEAN_ACCURACY, MEAN_LOSS)\n\nDEFAULT_PROJECT_INFO_KEYS = (\n    \"name\",\n    \"total_trials\",\n    \"running_trials\",\n    \"terminated_trials\",\n    \"error_trials\",\n    \"last_updated\",\n)\n\ntry:\n    TERM_HEIGHT, TERM_WIDTH = subprocess.check_output([\"stty\", \"size\"]).split()\n    TERM_HEIGHT, TERM_WIDTH = int(TERM_HEIGHT), int(TERM_WIDTH)\nexcept subprocess.CalledProcessError:\n    TERM_HEIGHT, TERM_WIDTH = 100, 100\n\nOPERATORS = {\n    '<': operator.lt,\n    '<=': operator.le,\n    '==': operator.eq,\n    '!=': operator.ne,\n    '>=': operator.ge,\n    '>': operator.gt,\n}\n\n\ndef _check_tabulate():\n    \"\"\"Checks whether tabulate is installed.\"\"\"\n    if tabulate is None:\n        raise ImportError(\n            \"Tabulate not installed. Please run `pip install tabulate`.\")\n\n\ndef print_format_output(dataframe):\n    \"\"\"Prints output of given dataframe to fit into terminal.\n\n    Returns:\n        table (pd.DataFrame): Final outputted dataframe.\n        dropped_cols (list): Columns dropped due to terminal size.\n        empty_cols (list): Empty columns (dropped on default).\n    \"\"\"\n    print_df = pd.DataFrame()\n    dropped_cols = []\n    empty_cols = []\n    # column display priority is based on the info_keys passed in\n    for i, col in enumerate(dataframe):\n        if dataframe[col].isnull().all():\n            # Don't add col to print_df if is fully empty\n            empty_cols += [col]\n            continue\n\n        print_df[col] = dataframe[col]\n        test_table = tabulate(print_df, headers=\"keys\", tablefmt=\"psql\")\n        if str(test_table).index('\\n') > TERM_WIDTH:\n            # Drop all columns beyond terminal width\n            print_df.drop(col, axis=1, inplace=True)\n            dropped_cols += list(dataframe.columns)[i:]\n            break\n\n    table = tabulate(\n        print_df, headers=\"keys\", tablefmt=\"psql\", showindex=\"never\")\n\n    print(table)\n    if dropped_cols:\n        print(\"Dropped columns:\", dropped_cols)\n        print(\"Please increase your terminal size to view remaining columns.\")\n    if empty_cols:\n        print(\"Empty columns:\", empty_cols)\n\n    return table, dropped_cols, empty_cols\n\n\ndef _get_experiment_state(experiment_path, exit_on_fail=False):\n    experiment_path = os.path.expanduser(experiment_path)\n    experiment_state_paths = glob.glob(\n        os.path.join(experiment_path, \"experiment_state*.json\"))\n    if not experiment_state_paths:\n        if exit_on_fail:\n            print(\"No experiment state found!\")\n            sys.exit(0)\n        else:\n            return\n    experiment_filename = max(list(experiment_state_paths))\n\n    with open(experiment_filename) as f:\n        experiment_state = json.load(f)\n    return experiment_state\n\n\ndef list_trials(experiment_path,\n                sort=None,\n                output=None,\n                filter_op=None,\n                info_keys=DEFAULT_EXPERIMENT_INFO_KEYS,\n                result_keys=DEFAULT_RESULT_KEYS):\n    \"\"\"Lists trials in the directory subtree starting at the given path.\n\n    Args:\n        experiment_path (str): Directory where trials are located.\n            Corresponds to Experiment.local_dir/Experiment.name.\n        sort (str): Key to sort by.\n        output (str): Name of file where output is saved.\n        filter_op (str): Filter operation in the format\n            \"<column> <operator> <value>\".\n        info_keys (list): Keys that are displayed.\n        result_keys (list): Keys of last result that are displayed.\n    \"\"\"\n    _check_tabulate()\n    experiment_state = _get_experiment_state(\n        experiment_path, exit_on_fail=True)\n\n    checkpoint_dicts = experiment_state[\"checkpoints\"]\n    checkpoint_dicts = [flatten_dict(g) for g in checkpoint_dicts]\n    checkpoints_df = pd.DataFrame(checkpoint_dicts)\n\n    result_keys = [\"last_result:{}\".format(k) for k in result_keys]\n    col_keys = [\n        k for k in list(info_keys) + result_keys if k in checkpoints_df\n    ]\n    checkpoints_df = checkpoints_df[col_keys]\n\n    if \"last_update_time\" in checkpoints_df:\n        with pd.option_context(\"mode.use_inf_as_null\", True):\n            datetime_series = checkpoints_df[\"last_update_time\"].dropna()\n\n        datetime_series = datetime_series.apply(\n            lambda t: datetime.fromtimestamp(t).strftime(TIMESTAMP_FORMAT))\n        checkpoints_df[\"last_update_time\"] = datetime_series\n\n    if \"logdir\" in checkpoints_df:\n        # logdir often too verbose to view in table, so drop experiment_path\n        checkpoints_df[\"logdir\"] = checkpoints_df[\"logdir\"].str.replace(\n            experiment_path, '')\n\n    if filter_op:\n        col, op, val = filter_op.split(' ')\n        col_type = checkpoints_df[col].dtype\n        if is_numeric_dtype(col_type):\n            val = float(val)\n        elif is_string_dtype(col_type):\n            val = str(val)\n        # TODO(Andrew): add support for datetime and boolean\n        else:\n            raise ValueError(\"Unsupported dtype for '{}': {}\".format(\n                val, col_type))\n        op = OPERATORS[op]\n        filtered_index = op(checkpoints_df[col], val)\n        checkpoints_df = checkpoints_df[filtered_index]\n\n    if sort:\n        if sort not in checkpoints_df:\n            raise KeyError(\"Sort Index '{}' not in: {}\".format(\n                sort, list(checkpoints_df)))\n        checkpoints_df = checkpoints_df.sort_values(by=sort)\n\n    print_format_output(checkpoints_df)\n\n    if output:\n        experiment_path = os.path.expanduser(experiment_path)\n        output_path = os.path.join(experiment_path, output)\n        file_extension = os.path.splitext(output)[1].lower()\n        if file_extension in (\".p\", \".pkl\", \".pickle\"):\n            checkpoints_df.to_pickle(output_path)\n        elif file_extension == \".csv\":\n            checkpoints_df.to_csv(output_path, index=False)\n        else:\n            raise ValueError(\"Unsupported filetype: {}\".format(output))\n        print(\"Output saved at:\", output_path)\n\n\ndef list_experiments(project_path,\n                     sort=None,\n                     output=None,\n                     filter_op=None,\n                     info_keys=DEFAULT_PROJECT_INFO_KEYS):\n    \"\"\"Lists experiments in the directory subtree.\n\n    Args:\n        project_path (str): Directory where experiments are located.\n            Corresponds to Experiment.local_dir.\n        sort (str): Key to sort by.\n        output (str): Name of file where output is saved.\n        filter_op (str): Filter operation in the format\n            \"<column> <operator> <value>\".\n        info_keys (list): Keys that are displayed.\n    \"\"\"\n    _check_tabulate()\n    base, experiment_folders, _ = next(os.walk(project_path))\n\n    experiment_data_collection = []\n\n    for experiment_dir in experiment_folders:\n        experiment_state = _get_experiment_state(\n            os.path.join(base, experiment_dir))\n        if not experiment_state:\n            logger.debug(\"No experiment state found in %s\", experiment_dir)\n            continue\n\n        checkpoints = pd.DataFrame(experiment_state[\"checkpoints\"])\n        runner_data = experiment_state[\"runner_data\"]\n\n        # Format time-based values.\n        time_values = {\n            \"start_time\": runner_data.get(\"_start_time\"),\n            \"last_updated\": experiment_state.get(\"timestamp\"),\n        }\n\n        formatted_time_values = {\n            key: datetime.fromtimestamp(val).strftime(TIMESTAMP_FORMAT)\n            if val else None\n            for key, val in time_values.items()\n        }\n\n        experiment_data = {\n            \"name\": experiment_dir,\n            \"total_trials\": checkpoints.shape[0],\n            \"running_trials\": (checkpoints[\"status\"] == Trial.RUNNING).sum(),\n            \"terminated_trials\": (\n                checkpoints[\"status\"] == Trial.TERMINATED).sum(),\n            \"error_trials\": (checkpoints[\"status\"] == Trial.ERROR).sum(),\n        }\n        experiment_data.update(formatted_time_values)\n        experiment_data_collection.append(experiment_data)\n\n    if not experiment_data_collection:\n        print(\"No experiments found!\")\n        sys.exit(0)\n\n    info_df = pd.DataFrame(experiment_data_collection)\n    col_keys = [k for k in list(info_keys) if k in info_df]\n    if not col_keys:\n        print(\"None of keys {} in experiment data!\".format(info_keys))\n        sys.exit(0)\n    info_df = info_df[col_keys]\n\n    if filter_op:\n        col, op, val = filter_op.split(' ')\n        col_type = info_df[col].dtype\n        if is_numeric_dtype(col_type):\n            val = float(val)\n        elif is_string_dtype(col_type):\n            val = str(val)\n        # TODO(Andrew): add support for datetime and boolean\n        else:\n            raise ValueError(\"Unsupported dtype for '{}': {}\".format(\n                val, col_type))\n        op = OPERATORS[op]\n        filtered_index = op(info_df[col], val)\n        info_df = info_df[filtered_index]\n\n    if sort:\n        if sort not in info_df:\n            raise KeyError(\"Sort Index '{}' not in: {}\".format(\n                sort, list(info_df)))\n        info_df = info_df.sort_values(by=sort)\n\n    print_format_output(info_df)\n\n    if output:\n        output_path = os.path.join(base, output)\n        file_extension = os.path.splitext(output)[1].lower()\n        if file_extension in (\".p\", \".pkl\", \".pickle\"):\n            info_df.to_pickle(output_path)\n        elif file_extension == \".csv\":\n            info_df.to_csv(output_path, index=False)\n        else:\n            raise ValueError(\"Unsupported filetype: {}\".format(output))\n        print(\"Output saved at:\", output_path)\n\n\ndef add_note(path, filename=\"note.txt\"):\n    \"\"\"Opens a txt file at the given path where user can add and save notes.\n\n    Args:\n        path (str): Directory where note will be saved.\n        filename (str): Name of note. Defaults to \"note.txt\"\n    \"\"\"\n    path = os.path.expanduser(path)\n    assert os.path.isdir(path), \"{} is not a valid directory.\".format(path)\n\n    filepath = os.path.join(path, filename)\n    exists = os.path.isfile(filepath)\n\n    try:\n        subprocess.call([EDITOR, filepath])\n    except Exception as exc:\n        logger.error(\"Editing note failed!\")\n        raise exc\n    if exists:\n        print(\"Note updated at:\", filepath)\n    else:\n        print(\"Note created at:\", filepath)\n", "path": "python/ray/tune/commands.py"}], "after_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport glob\nimport json\nimport logging\nimport os\nimport sys\nimport subprocess\nimport operator\nfrom datetime import datetime\n\nimport pandas as pd\nfrom pandas.api.types import is_string_dtype, is_numeric_dtype\nfrom ray.tune.util import flatten_dict\nfrom ray.tune.result import TRAINING_ITERATION, MEAN_ACCURACY, MEAN_LOSS\nfrom ray.tune.trial import Trial\ntry:\n    from tabulate import tabulate\nexcept ImportError:\n    tabulate = None\n\nlogger = logging.getLogger(__name__)\n\nEDITOR = os.getenv(\"EDITOR\", \"vim\")\n\nTIMESTAMP_FORMAT = \"%Y-%m-%d %H:%M:%S (%A)\"\n\nDEFAULT_EXPERIMENT_INFO_KEYS = (\n    \"trainable_name\",\n    \"experiment_tag\",\n    \"trial_id\",\n    \"status\",\n    \"last_update_time\",\n)\n\nDEFAULT_RESULT_KEYS = (TRAINING_ITERATION, MEAN_ACCURACY, MEAN_LOSS)\n\nDEFAULT_PROJECT_INFO_KEYS = (\n    \"name\",\n    \"total_trials\",\n    \"running_trials\",\n    \"terminated_trials\",\n    \"error_trials\",\n    \"last_updated\",\n)\n\ntry:\n    TERM_HEIGHT, TERM_WIDTH = subprocess.check_output([\"stty\", \"size\"]).split()\n    TERM_HEIGHT, TERM_WIDTH = int(TERM_HEIGHT), int(TERM_WIDTH)\nexcept subprocess.CalledProcessError:\n    TERM_HEIGHT, TERM_WIDTH = 100, 100\n\nOPERATORS = {\n    '<': operator.lt,\n    '<=': operator.le,\n    '==': operator.eq,\n    '!=': operator.ne,\n    '>=': operator.ge,\n    '>': operator.gt,\n}\n\n\ndef _check_tabulate():\n    \"\"\"Checks whether tabulate is installed.\"\"\"\n    if tabulate is None:\n        raise ImportError(\n            \"Tabulate not installed. Please run `pip install tabulate`.\")\n\n\ndef print_format_output(dataframe):\n    \"\"\"Prints output of given dataframe to fit into terminal.\n\n    Returns:\n        table (pd.DataFrame): Final outputted dataframe.\n        dropped_cols (list): Columns dropped due to terminal size.\n        empty_cols (list): Empty columns (dropped on default).\n    \"\"\"\n    print_df = pd.DataFrame()\n    dropped_cols = []\n    empty_cols = []\n    # column display priority is based on the info_keys passed in\n    for i, col in enumerate(dataframe):\n        if dataframe[col].isnull().all():\n            # Don't add col to print_df if is fully empty\n            empty_cols += [col]\n            continue\n\n        print_df[col] = dataframe[col]\n        test_table = tabulate(print_df, headers=\"keys\", tablefmt=\"psql\")\n        if str(test_table).index('\\n') > TERM_WIDTH:\n            # Drop all columns beyond terminal width\n            print_df.drop(col, axis=1, inplace=True)\n            dropped_cols += list(dataframe.columns)[i:]\n            break\n\n    table = tabulate(\n        print_df, headers=\"keys\", tablefmt=\"psql\", showindex=\"never\")\n\n    print(table)\n    if dropped_cols:\n        print(\"Dropped columns:\", dropped_cols)\n        print(\"Please increase your terminal size to view remaining columns.\")\n    if empty_cols:\n        print(\"Empty columns:\", empty_cols)\n\n    return table, dropped_cols, empty_cols\n\n\ndef _get_experiment_state(experiment_path, exit_on_fail=False):\n    experiment_path = os.path.expanduser(experiment_path)\n    experiment_state_paths = glob.glob(\n        os.path.join(experiment_path, \"experiment_state*.json\"))\n    if not experiment_state_paths:\n        if exit_on_fail:\n            print(\"No experiment state found!\")\n            sys.exit(0)\n        else:\n            return\n    experiment_filename = max(list(experiment_state_paths))\n\n    with open(experiment_filename) as f:\n        experiment_state = json.load(f)\n    return experiment_state\n\n\ndef list_trials(experiment_path,\n                sort=None,\n                output=None,\n                filter_op=None,\n                info_keys=DEFAULT_EXPERIMENT_INFO_KEYS,\n                result_keys=DEFAULT_RESULT_KEYS):\n    \"\"\"Lists trials in the directory subtree starting at the given path.\n\n    Args:\n        experiment_path (str): Directory where trials are located.\n            Corresponds to Experiment.local_dir/Experiment.name.\n        sort (str): Key to sort by.\n        output (str): Name of file where output is saved.\n        filter_op (str): Filter operation in the format\n            \"<column> <operator> <value>\".\n        info_keys (list): Keys that are displayed.\n        result_keys (list): Keys of last result that are displayed.\n    \"\"\"\n    _check_tabulate()\n    experiment_state = _get_experiment_state(\n        experiment_path, exit_on_fail=True)\n\n    checkpoint_dicts = experiment_state[\"checkpoints\"]\n    checkpoint_dicts = [flatten_dict(g) for g in checkpoint_dicts]\n    checkpoints_df = pd.DataFrame(checkpoint_dicts)\n\n    result_keys = [\"last_result:{}\".format(k) for k in result_keys]\n    col_keys = [\n        k for k in list(info_keys) + result_keys if k in checkpoints_df\n    ]\n    checkpoints_df = checkpoints_df[col_keys]\n\n    if \"last_update_time\" in checkpoints_df:\n        with pd.option_context(\"mode.use_inf_as_null\", True):\n            datetime_series = checkpoints_df[\"last_update_time\"].dropna()\n\n        datetime_series = datetime_series.apply(\n            lambda t: datetime.fromtimestamp(t).strftime(TIMESTAMP_FORMAT))\n        checkpoints_df[\"last_update_time\"] = datetime_series\n\n    if \"logdir\" in checkpoints_df:\n        # logdir often too verbose to view in table, so drop experiment_path\n        checkpoints_df[\"logdir\"] = checkpoints_df[\"logdir\"].str.replace(\n            experiment_path, '')\n\n    if filter_op:\n        col, op, val = filter_op.split(' ')\n        col_type = checkpoints_df[col].dtype\n        if is_numeric_dtype(col_type):\n            val = float(val)\n        elif is_string_dtype(col_type):\n            val = str(val)\n        # TODO(Andrew): add support for datetime and boolean\n        else:\n            raise ValueError(\"Unsupported dtype for '{}': {}\".format(\n                val, col_type))\n        op = OPERATORS[op]\n        filtered_index = op(checkpoints_df[col], val)\n        checkpoints_df = checkpoints_df[filtered_index]\n\n    if sort:\n        if sort not in checkpoints_df:\n            raise KeyError(\"Sort Index '{}' not in: {}\".format(\n                sort, list(checkpoints_df)))\n        checkpoints_df = checkpoints_df.sort_values(by=sort)\n\n    print_format_output(checkpoints_df)\n\n    if output:\n        file_extension = os.path.splitext(output)[1].lower()\n        if file_extension in (\".p\", \".pkl\", \".pickle\"):\n            checkpoints_df.to_pickle(output)\n        elif file_extension == \".csv\":\n            checkpoints_df.to_csv(output, index=False)\n        else:\n            raise ValueError(\"Unsupported filetype: {}\".format(output))\n        print(\"Output saved at:\", output)\n\n\ndef list_experiments(project_path,\n                     sort=None,\n                     output=None,\n                     filter_op=None,\n                     info_keys=DEFAULT_PROJECT_INFO_KEYS):\n    \"\"\"Lists experiments in the directory subtree.\n\n    Args:\n        project_path (str): Directory where experiments are located.\n            Corresponds to Experiment.local_dir.\n        sort (str): Key to sort by.\n        output (str): Name of file where output is saved.\n        filter_op (str): Filter operation in the format\n            \"<column> <operator> <value>\".\n        info_keys (list): Keys that are displayed.\n    \"\"\"\n    _check_tabulate()\n    base, experiment_folders, _ = next(os.walk(project_path))\n\n    experiment_data_collection = []\n\n    for experiment_dir in experiment_folders:\n        experiment_state = _get_experiment_state(\n            os.path.join(base, experiment_dir))\n        if not experiment_state:\n            logger.debug(\"No experiment state found in %s\", experiment_dir)\n            continue\n\n        checkpoints = pd.DataFrame(experiment_state[\"checkpoints\"])\n        runner_data = experiment_state[\"runner_data\"]\n\n        # Format time-based values.\n        time_values = {\n            \"start_time\": runner_data.get(\"_start_time\"),\n            \"last_updated\": experiment_state.get(\"timestamp\"),\n        }\n\n        formatted_time_values = {\n            key: datetime.fromtimestamp(val).strftime(TIMESTAMP_FORMAT)\n            if val else None\n            for key, val in time_values.items()\n        }\n\n        experiment_data = {\n            \"name\": experiment_dir,\n            \"total_trials\": checkpoints.shape[0],\n            \"running_trials\": (checkpoints[\"status\"] == Trial.RUNNING).sum(),\n            \"terminated_trials\": (\n                checkpoints[\"status\"] == Trial.TERMINATED).sum(),\n            \"error_trials\": (checkpoints[\"status\"] == Trial.ERROR).sum(),\n        }\n        experiment_data.update(formatted_time_values)\n        experiment_data_collection.append(experiment_data)\n\n    if not experiment_data_collection:\n        print(\"No experiments found!\")\n        sys.exit(0)\n\n    info_df = pd.DataFrame(experiment_data_collection)\n    col_keys = [k for k in list(info_keys) if k in info_df]\n    if not col_keys:\n        print(\"None of keys {} in experiment data!\".format(info_keys))\n        sys.exit(0)\n    info_df = info_df[col_keys]\n\n    if filter_op:\n        col, op, val = filter_op.split(' ')\n        col_type = info_df[col].dtype\n        if is_numeric_dtype(col_type):\n            val = float(val)\n        elif is_string_dtype(col_type):\n            val = str(val)\n        # TODO(Andrew): add support for datetime and boolean\n        else:\n            raise ValueError(\"Unsupported dtype for '{}': {}\".format(\n                val, col_type))\n        op = OPERATORS[op]\n        filtered_index = op(info_df[col], val)\n        info_df = info_df[filtered_index]\n\n    if sort:\n        if sort not in info_df:\n            raise KeyError(\"Sort Index '{}' not in: {}\".format(\n                sort, list(info_df)))\n        info_df = info_df.sort_values(by=sort)\n\n    print_format_output(info_df)\n\n    if output:\n        file_extension = os.path.splitext(output)[1].lower()\n        if file_extension in (\".p\", \".pkl\", \".pickle\"):\n            info_df.to_pickle(output)\n        elif file_extension == \".csv\":\n            info_df.to_csv(output, index=False)\n        else:\n            raise ValueError(\"Unsupported filetype: {}\".format(output))\n        print(\"Output saved at:\", output)\n\n\ndef add_note(path, filename=\"note.txt\"):\n    \"\"\"Opens a txt file at the given path where user can add and save notes.\n\n    Args:\n        path (str): Directory where note will be saved.\n        filename (str): Name of note. Defaults to \"note.txt\"\n    \"\"\"\n    path = os.path.expanduser(path)\n    assert os.path.isdir(path), \"{} is not a valid directory.\".format(path)\n\n    filepath = os.path.join(path, filename)\n    exists = os.path.isfile(filepath)\n\n    try:\n        subprocess.call([EDITOR, filepath])\n    except Exception as exc:\n        logger.error(\"Editing note failed!\")\n        raise exc\n    if exists:\n        print(\"Note updated at:\", filepath)\n    else:\n        print(\"Note created at:\", filepath)\n", "path": "python/ray/tune/commands.py"}]}