Repository: bentoml/BentoML
Pull request: feat: support parsing space for deletion #3418
## What does this PR address?

This PR adds support for parsing space in delete command for both `benotml delete` and `bentoml models delete`
Instead of returning value argument as a STRING , It is removed to take default one. Also added nrgs=-1 to have functionality of multiple targets.
Rest of the code is just parsing.

**TEST SNAPSHOTS:**
Issue reproduce (bentoml models delete):
```
(base) vrushali@Vrushalis-MacBook-Air simple_pickable_model % bentoml models list
 Tag                               Module                   Size      Creation Time       
 my_python_model:wurpysaau6io4js4  bentoml.picklable_model  1.01 KiB  2023-06-01 23:41:19 
 iris_clf:i7wa4yqau6dnajs4         bentoml.sklearn          5.71 KiB  2023-06-01 23:38:16 
 linear_reg:l7l4uqh6a6mvejs4       bentoml.sklearn          940.00 B  2023-05-29 15:28:34 
(base) vrushali@Vrushalis-MacBook-Air simple_pickable_model % bentoml models delete my_python_model:wurpysaau6io4js4 iris_clf:i7wa4yqau6dnajs4 linear_reg:l7l4uqh6a6mvejs4
Usage: bentoml models delete [OPTIONS] DELETE_TARGETS
Try 'bentoml models delete -h' for help.

Error: Got unexpected extra arguments (iris_clf:i7wa4yqau6dnajs4 linear_reg:l7l4uqh6a6mvejs4)
```
After Issue Fixes (bentoml models delete):
```
(base) vrushali@Vrushalis-MacBook-Air BentoML % bentoml models delete my_python_model:wurpysaau6io4js4 iris_clf:i7wa4yqau6dnajs4 linear_reg:l7l4uqh6a6mvejs4
delete model my_python_model:wurpysaau6io4js4? [y/N]: y
Model(tag="my_python_model:wurpysaau6io4js4") deleted.
delete model iris_clf:i7wa4yqau6dnajs4? [y/N]: y
Model(tag="iris_clf:i7wa4yqau6dnajs4") deleted.
delete model linear_reg:l7l4uqh6a6mvejs4? [y/N]: y
Model(tag="linear_reg:l7l4uqh6a6mvejs4") deleted.
(base) vrushali@Vrushalis-MacBook-Air BentoML % 

**USING COMMA separated :**

(base) vrushali@Vrushalis-MacBook-Air simple_pickable_model % bentoml models delete my_python_model:obhsbaabggj5qjs4,iris_clf:mqhgz2qbggi6ijs4,linear_reg:eugaadabgggnujs4
delete model my_python_model:obhsbaabggj5qjs4? [y/N]: y
Model(tag="my_python_model:obhsbaabggj5qjs4") deleted.
delete model iris_clf:mqhgz2qbggi6ijs4? [y/N]: y
Model(tag="iris_clf:mqhgz2qbggi6ijs4") deleted.
delete model linear_reg:eugaadabgggnujs4? [y/N]: y
Model(tag="linear_reg:eugaadabgggnujs4") deleted.
(base) vrushali@Vrushalis-MacBook-Air simple_pickable_model % 
```
Issue reproduce (bentoml delete):
```
(base) vrushali@Vrushalis-MacBook-Air BentoML % bentoml list                                                                                                         
 Tag                                 Size       Creation Time       
 linear_regression:pm6ixwqbgk57mjs4  14.92 KiB  2023-06-02 16:14:42 
 iris_classifier:ljzurzabgkx24js4    25.10 KiB  2023-06-02 16:14:02 
 simple_square_svc:enxfuwqbgkkrqjs4  15.10 KiB  2023-06-02 16:12:19 
(base) vrushali@Vrushalis-MacBook-Air BentoML % bentoml delete linear_regression:pm6ixwqbgk57mjs4 iris_classifier:ljzurzabgkx24js4 simple_square_svc:enxfuwqbgkkrqjs4
Usage: bentoml delete [OPTIONS] DELETE_TARGETS
Try 'bentoml delete -h' for help.

Error: Got unexpected extra arguments (iris_classifier:ljzurzabgkx24js4 simple_square_svc:enxfuwqbgkkrqjs4)
```
After Issue Fixes (bentoml delete):
```
(base) vrushali@Vrushalis-MacBook-Air BentoML % bentoml list                                                                                                         
 Tag                                 Size       Creation Time       
 linear_regression:pm6ixwqbgk57mjs4  14.92 KiB  2023-06-02 16:14:42 
 iris_classifier:ljzurzabgkx24js4    25.10 KiB  2023-06-02 16:14:02 
 simple_square_svc:enxfuwqbgkkrqjs4  15.10 KiB  2023-06-02 16:12:19 
(base) vrushali@Vrushalis-MacBook-Air BentoML % bentoml delete linear_regression:pm6ixwqbgk57mjs4 iris_classifier:ljzurzabgkx24js4 simple_square_svc:enxfuwqbgkkrqjs4
delete bento linear_regression:pm6ixwqbgk57mjs4? [y/N]: y
Bento(tag="linear_regression:pm6ixwqbgk57mjs4") deleted.
delete bento iris_classifier:ljzurzabgkx24js4? [y/N]: y
Bento(tag="iris_classifier:ljzurzabgkx24js4") deleted.
delete bento simple_square_svc:enxfuwqbgkkrqjs4? [y/N]: y
Bento(tag="simple_square_svc:enxfuwqbgkkrqjs4") deleted.

**COMMA separated:**

(base) vrushali@Vrushalis-MacBook-Air quickstart % bentoml list                                                                                                         
 Tag                                 Size       Creation Time       
 iris_classifier:pcb53labgozryjs4    25.10 KiB  2023-06-02 16:21:49 
 simple_square_svc:nrtoqsabgo3t4js4  15.10 KiB  2023-06-02 16:21:28 
 linear_regression:miggyeabgoh7mjs4  14.92 KiB  2023-06-02 16:21:09 
(base) vrushali@Vrushalis-MacBook-Air quickstart % bentoml delete iris_classifier:pcb53labgozryjs4,simple_square_svc:nrtoqsabgo3t4js4,linear_regression:miggyeabgoh7mjs4
delete bento iris_classifier:pcb53labgozryjs4? [y/N]: y
Bento(tag="iris_classifier:pcb53labgozryjs4") deleted.
delete bento simple_square_svc:nrtoqsabgo3t4js4? [y/N]: y
Bento(tag="simple_square_svc:nrtoqsabgo3t4js4") deleted.
delete bento linear_regression:miggyeabgoh7mjs4? [y/N]: y
Bento(tag="linear_regression:miggyeabgoh7mjs4") deleted.
(base) vrushali@Vrushalis-MacBook-Air quickstart % bentoml list                                                                                                         
 Tag  Size  Creation Time 
(base) vrushali@Vrushalis-MacBook-Air quickstart % 

```
<!--
Thanks for sending a pull request!

Congrats for making it this far! Here's a 🍱 for you. There are still a few steps ahead.

Please make sure to read the contribution guidelines, then fill out the blanks below before requesting a code review.



Describe your changes in detail. Attach screenshots here if appropriate.

Once you're done with this, someone from BentoML team or community member will help review your PR (see "Who can help review?" section for potential reviewers.). If no one has reviewed your PR after a week have passed, don't hesitate to post a new comment and ping @-the same person. Notifications sometimes get lost 🥲.
-->

<!-- Remove if not applicable -->

Fixes #3418

## Before submitting:

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
<!--- If you plan to update documentation or tests in follow-up, please note -->

- [X] Does the Pull Request follow [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/#summary) naming? Here are [GitHub's
      guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) on how to create a pull request.
- [X] Does the code follow BentoML's code style, both `make format` and `make lint` script have passed ([instructions](https://github.com/bentoml/BentoML/blob/main/DEVELOPMENT.md#style-check-auto-formatting-type-checking))?
- [X] Did you read through [contribution guidelines](https://github.com/bentoml/BentoML/blob/main/CONTRIBUTING.md#ways-to-contribute) and follow [development guidelines](https://github.com/bentoml/BentoML/blob/main/DEVELOPMENT.md#start-developing)?
- [X] Did your changes require updates to the documentation? Have you updated
      those accordingly? Here are [documentation guidelines](https://github.com/bentoml/BentoML/tree/main/docs) and [tips on writting docs](https://github.com/bentoml/BentoML/tree/main/docs#writing-documentation).
- [X] Did you write tests to cover your changes?
- IF applicable I will do that in follow up issue. 


Problem statement:
feature(cli): support parsing space for deletion
### Feature request

Right now we don't support parsing space

```python
bentoml models delete model1 model2 ...
```
will fail.

Note that we can already do

```python
bentoml models delete model1,model2
```

### Motivation

_No response_

### Other

_No response_

Hints from issue discussion:
Could I try this feature?
yes please go ahead. Feel free to ping me if you run into any issues.

Some place to start with:
- https://github.com/bentoml/BentoML/blob/main/src/bentoml_cli/models.py and https://github.com/bentoml/BentoML/blob/main/src/bentoml_cli/bentos.py
I have some questions to clarify this issue.

1. When I read the document, https://docs.bentoml.org/en/latest/reference/cli.html#bentoml-models-delete
there is already multiple model deletion feature.
`e.g.: benotml models delete iris_clf:v1,iris_clf:v2`
What is the goal of this issue?
I guess bentoml wants another way of deletion adding existing one. (seperate mutiple models with ',' or ' ')
If not, let me know and please clarify more about this issue.

2. Also, `b models delete model1 model2 ...` here, b means bentoml? 

3. You wrote two reference files `models.py` and `bentos.py`. Both has a similar feature of deletion. Do you intend to make feature about not only model deletion but also bentos.py?






1. I don't think we parse space correctly
	```bash
	b models delete iris_clf:v1 iris_clf:v2
	```
	So ideally we should be able to do this with different model as well
	```bash
	b models delete model1:asdf model2:v2
	```
2. ah yes force of habit I have `b` as an alias to `bentoml`.
3. Yes. they are similar. You can make two PR, one for parsing multiple models, then the second one is for multiple bentos

ps: I will clarify in the description.
(the above deleted comment was made in error; please disregard)

Commit message: add documentation #3418

Patch to review:
diff --git a/src/bentoml_cli/bentos.py b/src/bentoml_cli/bentos.py
index eba435ce40b..c504595b4fb 100644
--- a/src/bentoml_cli/bentos.py
+++ b/src/bentoml_cli/bentos.py
@@ -35,7 +35,11 @@ def parse_delete_targets_argument_callback(
 ) -> list[str]:
     if value is None:
         return value
-    delete_targets = value.split(",")
+    value = " ".join(value)
+    if "," in value:
+        delete_targets = value.split(",")
+    else:
+        delete_targets = value.split()
     delete_targets = list(map(str.strip, delete_targets))
     for delete_target in delete_targets:
         if not (
@@ -145,7 +149,7 @@ def list_bentos(bento_name: str, output: str) -> None:  # type: ignore (not acce
     @cli.command()
     @click.argument(
         "delete_targets",
-        type=click.STRING,
+        nargs=-1,
         callback=parse_delete_targets_argument_callback,
         required=True,
     )
@@ -164,6 +168,7 @@ def delete(delete_targets: list[str], yes: bool) -> None:  # type: ignore (not a
             * Delete single bento bundle by "name:version", e.g: `bentoml delete IrisClassifier:v1`
             * Bulk delete all bento bundles with a specific name, e.g.: `bentoml delete IrisClassifier`
             * Bulk delete multiple bento bundles by name and version, separated by ",", e.g.: `benotml delete Irisclassifier:v1,MyPredictService:v2`
+            * Bulk delete multiple bento bundles by name and version, separated by " ", e.g.: `benotml delete Irisclassifier:v1 MyPredictService:v2`
             * Bulk delete without confirmation, e.g.: `bentoml delete IrisClassifier --yes`
         """
 
diff --git a/src/bentoml_cli/models.py b/src/bentoml_cli/models.py
index e2bfcd53f39..cb72bc3edd4 100644
--- a/src/bentoml_cli/models.py
+++ b/src/bentoml_cli/models.py
@@ -24,7 +24,11 @@ def parse_delete_targets_argument_callback(
 ) -> t.Any:
     if value is None:
         return value
-    delete_targets = value.split(",")
+    value = " ".join(value)
+    if "," in value:
+        delete_targets = value.split(",")
+    else:
+        delete_targets = value.split()
     delete_targets = list(map(str.strip, delete_targets))
     for delete_target in delete_targets:
         if not (
@@ -136,7 +140,7 @@ def list_models(model_name: str, output: str) -> None:  # type: ignore (not acce
     @model_cli.command()
     @click.argument(
         "delete_targets",
-        type=click.STRING,
+        nargs=-1,
         callback=parse_delete_targets_argument_callback,
         required=True,
     )
@@ -155,6 +159,7 @@ def delete(delete_targets: str, yes: bool) -> None:  # type: ignore (not accesse
             * Delete single model by "name:version", e.g: `bentoml models delete iris_clf:v1`
             * Bulk delete all models with a specific name, e.g.: `bentoml models delete iris_clf`
             * Bulk delete multiple models by name and version, separated by ",", e.g.: `benotml models delete iris_clf:v1,iris_clf:v2`
+            * Bulk delete multiple models by name and version, separated by " ", e.g.: `benotml models delete iris_clf:v1 iris_clf:v2`
             * Bulk delete without confirmation, e.g.: `bentoml models delete IrisClassifier --yes`
         """  # noqa
 


Code review comments:
diff_hunk: @@ -164,6 +168,7 @@ def delete(delete_targets: list[str], yes: bool) -> None:  # type: ignore (not a
             * Delete single bento bundle by "name:version", e.g: `bentoml delete IrisClassifier:v1`
             * Bulk delete all bento bundles with a specific name, e.g.: `bentoml delete IrisClassifier`
             * Bulk delete multiple bento bundles by name and version, separated by ",", e.g.: `benotml delete Irisclassifier:v1,MyPredictService:v2`
+            * Bulk delete multiple bento bundles by name and version, separated by " ", e.g.: `benotml delete Irisclassifier:v1 MyPredictService:v2`
path: src/bentoml_cli/bentos.py
text: @user1:
```suggestion
            * Bulk delete multiple bento bundles by name and version, separated by ",", e.g.: `bentoml delete Irisclassifier:v1,MyPredictService:v2`
            * Bulk delete multiple bento bundles by name and version, separated by " ", e.g.: `bentoml delete Irisclassifier:v1 MyPredictService:v2`
```

diff_hunk: @@ -155,6 +159,7 @@ def delete(delete_targets: str, yes: bool) -> None:  # type: ignore (not accesse
             * Delete single model by "name:version", e.g: `bentoml models delete iris_clf:v1`
             * Bulk delete all models with a specific name, e.g.: `bentoml models delete iris_clf`
             * Bulk delete multiple models by name and version, separated by ",", e.g.: `benotml models delete iris_clf:v1,iris_clf:v2`
+            * Bulk delete multiple models by name and version, separated by " ", e.g.: `benotml models delete iris_clf:v1 iris_clf:v2`
path: src/bentoml_cli/models.py
text: @user1:
Small typo :)

diff_hunk: @@ -155,6 +159,7 @@ def delete(delete_targets: str, yes: bool) -> None:  # type: ignore (not accesse
             * Delete single model by "name:version", e.g: `bentoml models delete iris_clf:v1`
             * Bulk delete all models with a specific name, e.g.: `bentoml models delete iris_clf`
             * Bulk delete multiple models by name and version, separated by ",", e.g.: `benotml models delete iris_clf:v1,iris_clf:v2`
+            * Bulk delete multiple models by name and version, separated by " ", e.g.: `benotml models delete iris_clf:v1 iris_clf:v2`
path: src/bentoml_cli/models.py
text: @user1:
```suggestion
            * Bulk delete multiple models by name and version, separated by ",", e.g.: `bentoml models delete iris_clf:v1,iris_clf:v2`
            * Bulk delete multiple models by name and version, separated by " ", e.g.: `bentoml models delete iris_clf:v1 iris_clf:v2`
```