Repository: iterative/dvc

Issue:
`dvc.api.params_show`: More helpful error handling
# Bug Report

## Description

Doesn't show useful errors.

### Reproduce

1. `git clone git@github.com:iterative/pipeline-conifguration.git`
2. `cd pipeline-conifguration`
3. Start Python shell:

```python
>>> import dvc.api
>>> dvc.api.params_show()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/dave/Code/dvc/dvc/api/params.py", line 267, in params_show
    return _postprocess(params)
  File "/Users/dave/Code/dvc/dvc/api/params.py", line 256, in _postprocess
    return processed[first(processed)]
KeyError: None
```

### Expected

I'm guessing it fails because there's no `dvc.yaml` or `params.yaml` in the project root where the shell is started, but the error message doesn't help me understand this or how to fix it.



Discussion:
The error, in this case, can be avoided. Do you think it would be more useful to return an empty dict or rather raise an exception like `No params found`?
I think an error in this case? I doubt there's much reason to call `params_show` if you expect an empty dict.

What would happen if there was a `params.yaml` but no `dvc.yaml` found? What about the inverse?
> I think an error in this case? I doubt there's much reason to call params_show if you expect an empty dict.

I was thinking about returning an empty dict instead of raising an exception because `dvc {X} show` commands don't fail when there is nothing to return.

> What would happen if there was a params.yaml but no dvc.yaml found? 

The contents of `params.yaml` will be returned.

> What about the inverse?

The `KeyError` point will be reached.



Resolving patch:
diff --git a/dvc/api/params.py b/dvc/api/params.py
--- a/dvc/api/params.py
+++ b/dvc/api/params.py
@@ -3,6 +3,7 @@
 
 from funcy import first
 
+from dvc.exceptions import DvcException
 from dvc.repo import Repo
 
 
@@ -50,6 +51,9 @@ def params_show(
     Returns:
         Dict: See Examples below.
 
+    Raises:
+        DvcException: If no params are found in `repo`.
+
     Examples:
 
         - No arguments.
@@ -253,6 +257,9 @@ def _postprocess(params):
         if "workspace" in processed:
             del processed["workspace"]
 
+        if not processed:
+            raise DvcException("No params found")
+
         return processed[first(processed)]
 
     with Repo.open(repo) as _repo:


Test patch:
diff --git a/tests/func/api/test_params.py b/tests/func/api/test_params.py
--- a/tests/func/api/test_params.py
+++ b/tests/func/api/test_params.py
@@ -3,6 +3,7 @@
 import pytest
 
 from dvc import api
+from dvc.exceptions import DvcException
 
 
 @pytest.fixture
@@ -128,3 +129,19 @@ def test_params_show_repo(tmp_dir, erepo_dir):
             params=["foo"],
         )
     assert api.params_show(repo=erepo_dir) == {"foo": 1}
+
+
+def test_params_show_no_params_found(tmp_dir, dvc):
+    # Empty repo
+    with pytest.raises(DvcException, match="No params found"):
+        api.params_show()
+
+    # params.yaml but no dvc.yaml
+    (tmp_dir / "params.yaml").dump({"foo": 1})
+    assert api.params_show() == {"foo": 1}
+
+    # dvc.yaml but no params.yaml
+    (tmp_dir / "params.yaml").unlink()
+    dvc.stage.add(name="echo", cmd="echo foo")
+    with pytest.raises(DvcException, match="No params found"):
+        api.params_show()