We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add --print-location option to `bentoml get`  command
**Is your feature request related to a problem? Please describe.**
See related discussions here: https://github.com/bentoml/BentoML/pull/810#pullrequestreview-432731488

Currently, we've been using `jq` to do this in our documentation:
```bash
$ bentoml get IrisClassifier:latest -q | jq -r ".uri.uri"

/Users/chaoyu/bentoml/repository/IrisClassifier/20200616144911_7CE204
```
But this approach requires installing `jq`

**Describe the solution you'd like**

Would be great to allow the user to get just the saved location, without jq installed, from the CLI:
```bash
$ bentoml get IrisClassifier:latest --print-location

/Users/chaoyu/bentoml/repository/IrisClassifier/20200616144911_7CE204
```

**Describe alternatives you've considered**

A general json selector syntax that works for all JSON CLI output and allow the user to select other fields in the JSON output?

e.g.
```
$ bentoml get IrisClassifier:latest --json-output ".uri.uri"

/Users/chaoyu/bentoml/repository/IrisClassifier/20200616144911_7CE204
```

It can use the same jq syntax, with something like https://github.com/doloopwhile/pyjq

**Additional context**
Add any other context or screenshots about the feature request here.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bentoml/cli/bento.py`
Content:
```
1 # Copyright 2019 Atalaya Tech, Inc.
2 
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 
7 # http://www.apache.org/licenses/LICENSE-2.0
8 
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 import click
15 import os
16 from google.protobuf.json_format import MessageToJson
17 from tabulate import tabulate
18 
19 from bentoml.cli.click_utils import (
20     CLI_COLOR_ERROR,
21     _echo,
22     parse_bento_tag_list_callback,
23 )
24 from bentoml.cli.utils import humanfriendly_age_from_datetime
25 from bentoml.yatai.proto import status_pb2
26 from bentoml.utils import pb_to_yaml, status_pb_to_error_code_and_message
27 from bentoml.utils.usage_stats import track_cli
28 from bentoml.yatai.client import YataiClient
29 from bentoml.saved_bundle import safe_retrieve
30 
31 
32 def _print_bento_info(bento, output_type):
33     if output_type == 'yaml':
34         _echo(pb_to_yaml(bento))
35     else:
36         _echo(MessageToJson(bento))
37 
38 
39 def _print_bento_table(bentos, wide=False):
40     table = []
41     if wide:
42         headers = ['BENTO_SERVICE', 'CREATED_AT', 'APIS', 'ARTIFACTS', 'URI']
43     else:
44         headers = ['BENTO_SERVICE', 'AGE', 'APIS', 'ARTIFACTS']
45 
46     for bento in bentos:
47         artifacts = [
48             f'{artifact.name}<{artifact.artifact_type}>'
49             for artifact in bento.bento_service_metadata.artifacts
50         ]
51         apis = [
52             f'{api.name}<{api.input_type}:{api.output_type}>'
53             for api in bento.bento_service_metadata.apis
54         ]
55         if wide:
56             created_at = bento.bento_service_metadata.created_at.ToDatetime().strftime(
57                 "%Y-%m-%d %H:%M"
58             )
59         else:
60             created_at = humanfriendly_age_from_datetime(
61                 bento.bento_service_metadata.created_at.ToDatetime()
62             )
63         row = [
64             f'{bento.name}:{bento.version}',
65             created_at,
66             ', '.join(apis),
67             ', '.join(artifacts),
68         ]
69         if wide:
70             row.append(bento.uri.uri)
71         table.append(row)
72 
73     table_display = tabulate(table, headers, tablefmt='plain')
74     _echo(table_display)
75 
76 
77 def _print_bentos_info(bentos, output_type):
78     if output_type == 'table':
79         _print_bento_table(bentos)
80     elif output_type == 'wide':
81         _print_bento_table(bentos, wide=True)
82     else:
83         for bento in bentos:
84             _print_bento_info(bento, output_type)
85 
86 
87 def add_bento_sub_command(cli):
88     # pylint: disable=unused-variable
89     @cli.command(help='Get BentoService information')
90     @click.argument('bento', type=click.STRING)
91     @click.option(
92         '--limit', type=click.INT, help='Limit how many resources will be retrieved'
93     )
94     @click.option('--ascending-order', is_flag=True)
95     @click.option(
96         '-o', '--output', type=click.Choice(['json', 'yaml', 'table', 'wide'])
97     )
98     def get(bento, limit, ascending_order, output):
99         if ':' in bento:
100             name, version = bento.split(':')
101         else:
102             name = bento
103             version = None
104         yatai_client = YataiClient()
105 
106         if name and version:
107             track_cli('bento-get')
108             output = output or 'json'
109             get_bento_result = yatai_client.repository.get(name, version)
110             if get_bento_result.status.status_code != status_pb2.Status.OK:
111                 error_code, error_message = status_pb_to_error_code_and_message(
112                     get_bento_result.status
113                 )
114                 _echo(
115                     f'BentoService {name}:{version} not found - '
116                     f'{error_code}:{error_message}',
117                     CLI_COLOR_ERROR,
118                 )
119                 return
120             _print_bento_info(get_bento_result.bento, output)
121             return
122         elif name:
123             track_cli('bento-list')
124             output = output or 'table'
125             list_bento_versions_result = yatai_client.repository.list(
126                 bento_name=name, limit=limit, ascending_order=ascending_order
127             )
128             if list_bento_versions_result.status.status_code != status_pb2.Status.OK:
129                 error_code, error_message = status_pb_to_error_code_and_message(
130                     list_bento_versions_result.status
131                 )
132                 _echo(
133                     f'Failed to list versions for BentoService {name} '
134                     f'{error_code}:{error_message}',
135                     CLI_COLOR_ERROR,
136                 )
137                 return
138 
139             _print_bentos_info(list_bento_versions_result.bentos, output)
140 
141     @cli.command(name='list', help='List BentoServices information')
142     @click.option(
143         '--limit', type=click.INT, help='Limit how many BentoServices will be retrieved'
144     )
145     @click.option(
146         '--offset', type=click.INT, help='How many BentoServices will be skipped'
147     )
148     @click.option(
149         '--order-by', type=click.Choice(['created_at', 'name']), default='created_at',
150     )
151     @click.option('--ascending-order', is_flag=True)
152     @click.option(
153         '-o',
154         '--output',
155         type=click.Choice(['json', 'yaml', 'table', 'wide']),
156         default='table',
157     )
158     def list_bentos(limit, offset, order_by, ascending_order, output):
159         yatai_client = YataiClient()
160         track_cli('bento-list')
161         list_bentos_result = yatai_client.repository.list(
162             limit=limit,
163             offset=offset,
164             order_by=order_by,
165             ascending_order=ascending_order,
166         )
167         if list_bentos_result.status.status_code != status_pb2.Status.OK:
168             error_code, error_message = status_pb_to_error_code_and_message(
169                 list_bentos_result.status
170             )
171             _echo(
172                 f'Failed to list BentoServices ' f'{error_code}:{error_message}',
173                 CLI_COLOR_ERROR,
174             )
175             return
176 
177         _print_bentos_info(list_bentos_result.bentos, output)
178 
179     @cli.command()
180     @click.argument("bentos", type=click.STRING, callback=parse_bento_tag_list_callback)
181     @click.option(
182         '-y', '--yes', '--assume-yes', is_flag=True, help='Automatic yes to prompts'
183     )
184     def delete(bentos, yes):
185         """Delete saved BentoService.
186 
187         BENTO is the target BentoService to be deleted, referenced by its name and
188         version in format of name:version. For example: "iris_classifier:v1.2.0"
189 
190         `bentoml delete` command also supports deleting multiple saved BentoService at
191         once, by providing name version tag separated by ",", for example:
192 
193         `bentoml delete iris_classifier:v1.2.0,my_svc:v1,my_svc2:v3`
194         """
195         yatai_client = YataiClient()
196         for bento in bentos:
197             name, version = bento.split(':')
198             if not name and not version:
199                 _echo(
200                     'BentoService name or version is missing. Please provide in the '
201                     'format of name:version',
202                     CLI_COLOR_ERROR,
203                 )
204                 return
205             if not yes and not click.confirm(
206                 f'Are you sure about delete {bento}? This will delete the BentoService '
207                 f'saved bundle files permanently'
208             ):
209                 return
210             result = yatai_client.repository.dangerously_delete_bento(
211                 name=name, version=version
212             )
213             if result.status.status_code != status_pb2.Status.OK:
214                 error_code, error_message = status_pb_to_error_code_and_message(
215                     result.status
216                 )
217                 _echo(
218                     f'Failed to delete Bento {name}:{version} '
219                     f'{error_code}:{error_message}',
220                     CLI_COLOR_ERROR,
221                 )
222             _echo(f'BentoService {name}:{version} deleted')
223 
224     @cli.command(
225         help='Retrieves BentoService artifacts into a target directory',
226         short_help="Retrieves BentoService artifacts into a target directory",
227     )
228     @click.argument("bento", type=click.STRING)
229     @click.option(
230         '--target_dir',
231         help="Directory to put artifacts into. Defaults to pwd.",
232         default=os.getcwd(),
233     )
234     def retrieve(bento, target_dir):
235         if ':' not in bento:
236             _echo(f'BentoService {bento} invalid - specify name:version')
237             return
238         name, version = bento.split(':')
239 
240         yatai_client = YataiClient()
241 
242         track_cli('bento-retrieve')
243         get_bento_result = yatai_client.repository.get(name, version)
244         if get_bento_result.status.status_code != status_pb2.Status.OK:
245             error_code, error_message = status_pb_to_error_code_and_message(
246                 get_bento_result.status
247             )
248             _echo(
249                 f'BentoService {name}:{version} not found - '
250                 f'{error_code}:{error_message}',
251                 CLI_COLOR_ERROR,
252             )
253             return
254 
255         if get_bento_result.bento.uri.s3_presigned_url:
256             bento_service_bundle_path = get_bento_result.bento.uri.s3_presigned_url
257         else:
258             bento_service_bundle_path = get_bento_result.bento.uri.uri
259 
260         safe_retrieve(bento_service_bundle_path, target_dir)
261 
262         click.echo('Service %s artifact directory => %s' % (name, target_dir))
263
```
--- 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/bentoml/cli/bento.py b/bentoml/cli/bento.py
--- a/bentoml/cli/bento.py
+++ b/bentoml/cli/bento.py
@@ -92,10 +92,11 @@
         '--limit', type=click.INT, help='Limit how many resources will be retrieved'
     )
     @click.option('--ascending-order', is_flag=True)
+    @click.option('--print-location', is_flag=True)
     @click.option(
         '-o', '--output', type=click.Choice(['json', 'yaml', 'table', 'wide'])
     )
-    def get(bento, limit, ascending_order, output):
+    def get(bento, limit, ascending_order, print_location, output):
         if ':' in bento:
             name, version = bento.split(':')
         else:
@@ -117,6 +118,9 @@
                     CLI_COLOR_ERROR,
                 )
                 return
+            if print_location:
+                _echo(get_bento_result.bento.uri.uri)
+                return
             _print_bento_info(get_bento_result.bento, output)
             return
         elif name:

{"golden_diff": "diff --git a/bentoml/cli/bento.py b/bentoml/cli/bento.py\n--- a/bentoml/cli/bento.py\n+++ b/bentoml/cli/bento.py\n@@ -92,10 +92,11 @@\n         '--limit', type=click.INT, help='Limit how many resources will be retrieved'\n     )\n     @click.option('--ascending-order', is_flag=True)\n+    @click.option('--print-location', is_flag=True)\n     @click.option(\n         '-o', '--output', type=click.Choice(['json', 'yaml', 'table', 'wide'])\n     )\n-    def get(bento, limit, ascending_order, output):\n+    def get(bento, limit, ascending_order, print_location, output):\n         if ':' in bento:\n             name, version = bento.split(':')\n         else:\n@@ -117,6 +118,9 @@\n                     CLI_COLOR_ERROR,\n                 )\n                 return\n+            if print_location:\n+                _echo(get_bento_result.bento.uri.uri)\n+                return\n             _print_bento_info(get_bento_result.bento, output)\n             return\n         elif name:\n", "issue": "Add --print-location option to `bentoml get`  command\n**Is your feature request related to a problem? Please describe.**\r\nSee related discussions here: https://github.com/bentoml/BentoML/pull/810#pullrequestreview-432731488\r\n\r\nCurrently, we've been using `jq` to do this in our documentation:\r\n```bash\r\n$ bentoml get IrisClassifier:latest -q | jq -r \".uri.uri\"\r\n\r\n/Users/chaoyu/bentoml/repository/IrisClassifier/20200616144911_7CE204\r\n```\r\nBut this approach requires installing `jq`\r\n\r\n**Describe the solution you'd like**\r\n\r\nWould be great to allow the user to get just the saved location, without jq installed, from the CLI:\r\n```bash\r\n$ bentoml get IrisClassifier:latest --print-location\r\n\r\n/Users/chaoyu/bentoml/repository/IrisClassifier/20200616144911_7CE204\r\n```\r\n\r\n**Describe alternatives you've considered**\r\n\r\nA general json selector syntax that works for all JSON CLI output and allow the user to select other fields in the JSON output?\r\n\r\ne.g.\r\n```\r\n$ bentoml get IrisClassifier:latest --json-output \".uri.uri\"\r\n\r\n/Users/chaoyu/bentoml/repository/IrisClassifier/20200616144911_7CE204\r\n```\r\n\r\nIt can use the same jq syntax, with something like https://github.com/doloopwhile/pyjq\r\n\r\n**Additional context**\r\nAdd any other context or screenshots about the feature request here.\r\n\n", "before_files": [{"content": "# Copyright 2019 Atalaya Tech, Inc.\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n# http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport click\nimport os\nfrom google.protobuf.json_format import MessageToJson\nfrom tabulate import tabulate\n\nfrom bentoml.cli.click_utils import (\n    CLI_COLOR_ERROR,\n    _echo,\n    parse_bento_tag_list_callback,\n)\nfrom bentoml.cli.utils import humanfriendly_age_from_datetime\nfrom bentoml.yatai.proto import status_pb2\nfrom bentoml.utils import pb_to_yaml, status_pb_to_error_code_and_message\nfrom bentoml.utils.usage_stats import track_cli\nfrom bentoml.yatai.client import YataiClient\nfrom bentoml.saved_bundle import safe_retrieve\n\n\ndef _print_bento_info(bento, output_type):\n    if output_type == 'yaml':\n        _echo(pb_to_yaml(bento))\n    else:\n        _echo(MessageToJson(bento))\n\n\ndef _print_bento_table(bentos, wide=False):\n    table = []\n    if wide:\n        headers = ['BENTO_SERVICE', 'CREATED_AT', 'APIS', 'ARTIFACTS', 'URI']\n    else:\n        headers = ['BENTO_SERVICE', 'AGE', 'APIS', 'ARTIFACTS']\n\n    for bento in bentos:\n        artifacts = [\n            f'{artifact.name}<{artifact.artifact_type}>'\n            for artifact in bento.bento_service_metadata.artifacts\n        ]\n        apis = [\n            f'{api.name}<{api.input_type}:{api.output_type}>'\n            for api in bento.bento_service_metadata.apis\n        ]\n        if wide:\n            created_at = bento.bento_service_metadata.created_at.ToDatetime().strftime(\n                \"%Y-%m-%d %H:%M\"\n            )\n        else:\n            created_at = humanfriendly_age_from_datetime(\n                bento.bento_service_metadata.created_at.ToDatetime()\n            )\n        row = [\n            f'{bento.name}:{bento.version}',\n            created_at,\n            ', '.join(apis),\n            ', '.join(artifacts),\n        ]\n        if wide:\n            row.append(bento.uri.uri)\n        table.append(row)\n\n    table_display = tabulate(table, headers, tablefmt='plain')\n    _echo(table_display)\n\n\ndef _print_bentos_info(bentos, output_type):\n    if output_type == 'table':\n        _print_bento_table(bentos)\n    elif output_type == 'wide':\n        _print_bento_table(bentos, wide=True)\n    else:\n        for bento in bentos:\n            _print_bento_info(bento, output_type)\n\n\ndef add_bento_sub_command(cli):\n    # pylint: disable=unused-variable\n    @cli.command(help='Get BentoService information')\n    @click.argument('bento', type=click.STRING)\n    @click.option(\n        '--limit', type=click.INT, help='Limit how many resources will be retrieved'\n    )\n    @click.option('--ascending-order', is_flag=True)\n    @click.option(\n        '-o', '--output', type=click.Choice(['json', 'yaml', 'table', 'wide'])\n    )\n    def get(bento, limit, ascending_order, output):\n        if ':' in bento:\n            name, version = bento.split(':')\n        else:\n            name = bento\n            version = None\n        yatai_client = YataiClient()\n\n        if name and version:\n            track_cli('bento-get')\n            output = output or 'json'\n            get_bento_result = yatai_client.repository.get(name, version)\n            if get_bento_result.status.status_code != status_pb2.Status.OK:\n                error_code, error_message = status_pb_to_error_code_and_message(\n                    get_bento_result.status\n                )\n                _echo(\n                    f'BentoService {name}:{version} not found - '\n                    f'{error_code}:{error_message}',\n                    CLI_COLOR_ERROR,\n                )\n                return\n            _print_bento_info(get_bento_result.bento, output)\n            return\n        elif name:\n            track_cli('bento-list')\n            output = output or 'table'\n            list_bento_versions_result = yatai_client.repository.list(\n                bento_name=name, limit=limit, ascending_order=ascending_order\n            )\n            if list_bento_versions_result.status.status_code != status_pb2.Status.OK:\n                error_code, error_message = status_pb_to_error_code_and_message(\n                    list_bento_versions_result.status\n                )\n                _echo(\n                    f'Failed to list versions for BentoService {name} '\n                    f'{error_code}:{error_message}',\n                    CLI_COLOR_ERROR,\n                )\n                return\n\n            _print_bentos_info(list_bento_versions_result.bentos, output)\n\n    @cli.command(name='list', help='List BentoServices information')\n    @click.option(\n        '--limit', type=click.INT, help='Limit how many BentoServices will be retrieved'\n    )\n    @click.option(\n        '--offset', type=click.INT, help='How many BentoServices will be skipped'\n    )\n    @click.option(\n        '--order-by', type=click.Choice(['created_at', 'name']), default='created_at',\n    )\n    @click.option('--ascending-order', is_flag=True)\n    @click.option(\n        '-o',\n        '--output',\n        type=click.Choice(['json', 'yaml', 'table', 'wide']),\n        default='table',\n    )\n    def list_bentos(limit, offset, order_by, ascending_order, output):\n        yatai_client = YataiClient()\n        track_cli('bento-list')\n        list_bentos_result = yatai_client.repository.list(\n            limit=limit,\n            offset=offset,\n            order_by=order_by,\n            ascending_order=ascending_order,\n        )\n        if list_bentos_result.status.status_code != status_pb2.Status.OK:\n            error_code, error_message = status_pb_to_error_code_and_message(\n                list_bentos_result.status\n            )\n            _echo(\n                f'Failed to list BentoServices ' f'{error_code}:{error_message}',\n                CLI_COLOR_ERROR,\n            )\n            return\n\n        _print_bentos_info(list_bentos_result.bentos, output)\n\n    @cli.command()\n    @click.argument(\"bentos\", type=click.STRING, callback=parse_bento_tag_list_callback)\n    @click.option(\n        '-y', '--yes', '--assume-yes', is_flag=True, help='Automatic yes to prompts'\n    )\n    def delete(bentos, yes):\n        \"\"\"Delete saved BentoService.\n\n        BENTO is the target BentoService to be deleted, referenced by its name and\n        version in format of name:version. For example: \"iris_classifier:v1.2.0\"\n\n        `bentoml delete` command also supports deleting multiple saved BentoService at\n        once, by providing name version tag separated by \",\", for example:\n\n        `bentoml delete iris_classifier:v1.2.0,my_svc:v1,my_svc2:v3`\n        \"\"\"\n        yatai_client = YataiClient()\n        for bento in bentos:\n            name, version = bento.split(':')\n            if not name and not version:\n                _echo(\n                    'BentoService name or version is missing. Please provide in the '\n                    'format of name:version',\n                    CLI_COLOR_ERROR,\n                )\n                return\n            if not yes and not click.confirm(\n                f'Are you sure about delete {bento}? This will delete the BentoService '\n                f'saved bundle files permanently'\n            ):\n                return\n            result = yatai_client.repository.dangerously_delete_bento(\n                name=name, version=version\n            )\n            if result.status.status_code != status_pb2.Status.OK:\n                error_code, error_message = status_pb_to_error_code_and_message(\n                    result.status\n                )\n                _echo(\n                    f'Failed to delete Bento {name}:{version} '\n                    f'{error_code}:{error_message}',\n                    CLI_COLOR_ERROR,\n                )\n            _echo(f'BentoService {name}:{version} deleted')\n\n    @cli.command(\n        help='Retrieves BentoService artifacts into a target directory',\n        short_help=\"Retrieves BentoService artifacts into a target directory\",\n    )\n    @click.argument(\"bento\", type=click.STRING)\n    @click.option(\n        '--target_dir',\n        help=\"Directory to put artifacts into. Defaults to pwd.\",\n        default=os.getcwd(),\n    )\n    def retrieve(bento, target_dir):\n        if ':' not in bento:\n            _echo(f'BentoService {bento} invalid - specify name:version')\n            return\n        name, version = bento.split(':')\n\n        yatai_client = YataiClient()\n\n        track_cli('bento-retrieve')\n        get_bento_result = yatai_client.repository.get(name, version)\n        if get_bento_result.status.status_code != status_pb2.Status.OK:\n            error_code, error_message = status_pb_to_error_code_and_message(\n                get_bento_result.status\n            )\n            _echo(\n                f'BentoService {name}:{version} not found - '\n                f'{error_code}:{error_message}',\n                CLI_COLOR_ERROR,\n            )\n            return\n\n        if get_bento_result.bento.uri.s3_presigned_url:\n            bento_service_bundle_path = get_bento_result.bento.uri.s3_presigned_url\n        else:\n            bento_service_bundle_path = get_bento_result.bento.uri.uri\n\n        safe_retrieve(bento_service_bundle_path, target_dir)\n\n        click.echo('Service %s artifact directory => %s' % (name, target_dir))\n", "path": "bentoml/cli/bento.py"}], "after_files": [{"content": "# Copyright 2019 Atalaya Tech, Inc.\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n# http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport click\nimport os\nfrom google.protobuf.json_format import MessageToJson\nfrom tabulate import tabulate\n\nfrom bentoml.cli.click_utils import (\n    CLI_COLOR_ERROR,\n    _echo,\n    parse_bento_tag_list_callback,\n)\nfrom bentoml.cli.utils import humanfriendly_age_from_datetime\nfrom bentoml.yatai.proto import status_pb2\nfrom bentoml.utils import pb_to_yaml, status_pb_to_error_code_and_message\nfrom bentoml.utils.usage_stats import track_cli\nfrom bentoml.yatai.client import YataiClient\nfrom bentoml.saved_bundle import safe_retrieve\n\n\ndef _print_bento_info(bento, output_type):\n    if output_type == 'yaml':\n        _echo(pb_to_yaml(bento))\n    else:\n        _echo(MessageToJson(bento))\n\n\ndef _print_bento_table(bentos, wide=False):\n    table = []\n    if wide:\n        headers = ['BENTO_SERVICE', 'CREATED_AT', 'APIS', 'ARTIFACTS', 'URI']\n    else:\n        headers = ['BENTO_SERVICE', 'AGE', 'APIS', 'ARTIFACTS']\n\n    for bento in bentos:\n        artifacts = [\n            f'{artifact.name}<{artifact.artifact_type}>'\n            for artifact in bento.bento_service_metadata.artifacts\n        ]\n        apis = [\n            f'{api.name}<{api.input_type}:{api.output_type}>'\n            for api in bento.bento_service_metadata.apis\n        ]\n        if wide:\n            created_at = bento.bento_service_metadata.created_at.ToDatetime().strftime(\n                \"%Y-%m-%d %H:%M\"\n            )\n        else:\n            created_at = humanfriendly_age_from_datetime(\n                bento.bento_service_metadata.created_at.ToDatetime()\n            )\n        row = [\n            f'{bento.name}:{bento.version}',\n            created_at,\n            ', '.join(apis),\n            ', '.join(artifacts),\n        ]\n        if wide:\n            row.append(bento.uri.uri)\n        table.append(row)\n\n    table_display = tabulate(table, headers, tablefmt='plain')\n    _echo(table_display)\n\n\ndef _print_bentos_info(bentos, output_type):\n    if output_type == 'table':\n        _print_bento_table(bentos)\n    elif output_type == 'wide':\n        _print_bento_table(bentos, wide=True)\n    else:\n        for bento in bentos:\n            _print_bento_info(bento, output_type)\n\n\ndef add_bento_sub_command(cli):\n    # pylint: disable=unused-variable\n    @cli.command(help='Get BentoService information')\n    @click.argument('bento', type=click.STRING)\n    @click.option(\n        '--limit', type=click.INT, help='Limit how many resources will be retrieved'\n    )\n    @click.option('--ascending-order', is_flag=True)\n    @click.option('--print-location', is_flag=True)\n    @click.option(\n        '-o', '--output', type=click.Choice(['json', 'yaml', 'table', 'wide'])\n    )\n    def get(bento, limit, ascending_order, print_location, output):\n        if ':' in bento:\n            name, version = bento.split(':')\n        else:\n            name = bento\n            version = None\n        yatai_client = YataiClient()\n\n        if name and version:\n            track_cli('bento-get')\n            output = output or 'json'\n            get_bento_result = yatai_client.repository.get(name, version)\n            if get_bento_result.status.status_code != status_pb2.Status.OK:\n                error_code, error_message = status_pb_to_error_code_and_message(\n                    get_bento_result.status\n                )\n                _echo(\n                    f'BentoService {name}:{version} not found - '\n                    f'{error_code}:{error_message}',\n                    CLI_COLOR_ERROR,\n                )\n                return\n            if print_location:\n                _echo(get_bento_result.bento.uri.uri)\n                return\n            _print_bento_info(get_bento_result.bento, output)\n            return\n        elif name:\n            track_cli('bento-list')\n            output = output or 'table'\n            list_bento_versions_result = yatai_client.repository.list(\n                bento_name=name, limit=limit, ascending_order=ascending_order\n            )\n            if list_bento_versions_result.status.status_code != status_pb2.Status.OK:\n                error_code, error_message = status_pb_to_error_code_and_message(\n                    list_bento_versions_result.status\n                )\n                _echo(\n                    f'Failed to list versions for BentoService {name} '\n                    f'{error_code}:{error_message}',\n                    CLI_COLOR_ERROR,\n                )\n                return\n\n            _print_bentos_info(list_bento_versions_result.bentos, output)\n\n    @cli.command(name='list', help='List BentoServices information')\n    @click.option(\n        '--limit', type=click.INT, help='Limit how many BentoServices will be retrieved'\n    )\n    @click.option(\n        '--offset', type=click.INT, help='How many BentoServices will be skipped'\n    )\n    @click.option(\n        '--order-by', type=click.Choice(['created_at', 'name']), default='created_at',\n    )\n    @click.option('--ascending-order', is_flag=True)\n    @click.option(\n        '-o',\n        '--output',\n        type=click.Choice(['json', 'yaml', 'table', 'wide']),\n        default='table',\n    )\n    def list_bentos(limit, offset, order_by, ascending_order, output):\n        yatai_client = YataiClient()\n        track_cli('bento-list')\n        list_bentos_result = yatai_client.repository.list(\n            limit=limit,\n            offset=offset,\n            order_by=order_by,\n            ascending_order=ascending_order,\n        )\n        if list_bentos_result.status.status_code != status_pb2.Status.OK:\n            error_code, error_message = status_pb_to_error_code_and_message(\n                list_bentos_result.status\n            )\n            _echo(\n                f'Failed to list BentoServices ' f'{error_code}:{error_message}',\n                CLI_COLOR_ERROR,\n            )\n            return\n\n        _print_bentos_info(list_bentos_result.bentos, output)\n\n    @cli.command()\n    @click.argument(\"bentos\", type=click.STRING, callback=parse_bento_tag_list_callback)\n    @click.option(\n        '-y', '--yes', '--assume-yes', is_flag=True, help='Automatic yes to prompts'\n    )\n    def delete(bentos, yes):\n        \"\"\"Delete saved BentoService.\n\n        BENTO is the target BentoService to be deleted, referenced by its name and\n        version in format of name:version. For example: \"iris_classifier:v1.2.0\"\n\n        `bentoml delete` command also supports deleting multiple saved BentoService at\n        once, by providing name version tag separated by \",\", for example:\n\n        `bentoml delete iris_classifier:v1.2.0,my_svc:v1,my_svc2:v3`\n        \"\"\"\n        yatai_client = YataiClient()\n        for bento in bentos:\n            name, version = bento.split(':')\n            if not name and not version:\n                _echo(\n                    'BentoService name or version is missing. Please provide in the '\n                    'format of name:version',\n                    CLI_COLOR_ERROR,\n                )\n                return\n            if not yes and not click.confirm(\n                f'Are you sure about delete {bento}? This will delete the BentoService '\n                f'saved bundle files permanently'\n            ):\n                return\n            result = yatai_client.repository.dangerously_delete_bento(\n                name=name, version=version\n            )\n            if result.status.status_code != status_pb2.Status.OK:\n                error_code, error_message = status_pb_to_error_code_and_message(\n                    result.status\n                )\n                _echo(\n                    f'Failed to delete Bento {name}:{version} '\n                    f'{error_code}:{error_message}',\n                    CLI_COLOR_ERROR,\n                )\n            _echo(f'BentoService {name}:{version} deleted')\n\n    @cli.command(\n        help='Retrieves BentoService artifacts into a target directory',\n        short_help=\"Retrieves BentoService artifacts into a target directory\",\n    )\n    @click.argument(\"bento\", type=click.STRING)\n    @click.option(\n        '--target_dir',\n        help=\"Directory to put artifacts into. Defaults to pwd.\",\n        default=os.getcwd(),\n    )\n    def retrieve(bento, target_dir):\n        if ':' not in bento:\n            _echo(f'BentoService {bento} invalid - specify name:version')\n            return\n        name, version = bento.split(':')\n\n        yatai_client = YataiClient()\n\n        track_cli('bento-retrieve')\n        get_bento_result = yatai_client.repository.get(name, version)\n        if get_bento_result.status.status_code != status_pb2.Status.OK:\n            error_code, error_message = status_pb_to_error_code_and_message(\n                get_bento_result.status\n            )\n            _echo(\n                f'BentoService {name}:{version} not found - '\n                f'{error_code}:{error_message}',\n                CLI_COLOR_ERROR,\n            )\n            return\n\n        if get_bento_result.bento.uri.s3_presigned_url:\n            bento_service_bundle_path = get_bento_result.bento.uri.s3_presigned_url\n        else:\n            bento_service_bundle_path = get_bento_result.bento.uri.uri\n\n        safe_retrieve(bento_service_bundle_path, target_dir)\n\n        click.echo('Service %s artifact directory => %s' % (name, target_dir))\n", "path": "bentoml/cli/bento.py"}]}