Repository: iterative/dvc

Issue:
import: error message when imported file does not exist
## Description

When you import a file from a exernal Git repository but you specify a wrong path you receive a confusing error message.

### Reproduce
```
dvc import git@github.com:iterative/example-get-started data/fake.xml
```

### Expected

A concise error that informs that data/fake.xml does not exist.

### Environment information

```
DVC version: 2.3.0 (pip)
---------------------------------
Platform: Python 3.7.9 on Linux-4.15.0-96-generic-x86_64-with-debian-buster-sid
Supports: http, https
Cache types: symlink
Cache directory: nfs on 10.10.30.245:/NAS/VA
Caches: local
Remotes: None
Workspace directory: ext4 on /dev/sdb1
Repo: dvc, git
```

I would like to help fix this problem, I thought about adding some regression test for https://github.com/iterative/dvc/blob/master/dvc/repo/imp_url.py similar to https://github.com/iterative/dvc/blob/master/tests/func/test_import_url.py#L107 and catch the exception later to return a proper message


Discussion:
@mmeendez8 , Thank you for your reporting. 

```
ERROR: unexpected error - [Errno 2] No such file or directory: PosixPathInfo: '../../../../../private/var/folders/pv/474xpngj6w71zbfcsxhdxwc40000gn/T/tmp1y1uhfkwdvc-clone/data/fake.xml'
```

On my computer, I agreed that it should provide the original path instead of a temporary cloned one.

> I would like to help fix this problem, I thought about adding some regression test for

Very welcome if you'd like to fix it.
I have been trying to identify where this bug was happening and how to solve it but might no be as simple as I thought so I will try to write here my doubts.

It seems the unexpected error is happening in [repo.stage.collect_granullar](https://github.com/iterative/dvc/blob/master/dvc/repo/stage.py#L407) function. That `OutputNotFoundError` exception is not being raised since it is replaced by a more "general" exception called `NoOutputOrStageError` [a few lines below](https://github.com/iterative/dvc/blob/master/dvc/repo/stage.py#L429) and I do not really get why is happening.

I thought about raising that exception and capturing it later for showing a better error message that points to the original path but not sure if `OutputNotFoundError` exception adequates to this case, maybe `NoOutputInExternalRepoError` fits better here?


@mmeendez8 `NoOutputOrStageError` is the correct error for `stage.collect_granular`. That function can be called with a target that can be any one of

- output filename
- path to `.dvc` or `dvc.yaml` file
- name of a specific stage within a `dvc.yaml` file

`collect_granular` has to try all of those possibilities.

You will likely need to catch that exception higher in the call stack that is specific to importing, and re-raise a more appropriate error (with the corrected paths - since the original exception will contain local paths to our temporary clone of the repo we are trying to import).

Resolving patch:
diff --git a/dvc/dependency/repo.py b/dvc/dependency/repo.py
--- a/dvc/dependency/repo.py
+++ b/dvc/dependency/repo.py
@@ -97,7 +97,7 @@ def get_used_objs(
         self, **kwargs
     ) -> Dict[Optional["ObjectDB"], Set["HashFile"]]:
         from dvc.config import NoRemoteError
-        from dvc.exceptions import NoOutputOrStageError
+        from dvc.exceptions import NoOutputOrStageError, PathMissingError
         from dvc.objects.db.git import GitObjectDB
         from dvc.objects.stage import stage
 
@@ -126,12 +126,18 @@ def get_used_objs(
             except (NoRemoteError, NoOutputOrStageError):
                 pass
 
-            staged_obj = stage(
-                local_odb,
-                path_info,
-                repo.repo_fs,
-                local_odb.fs.PARAM_CHECKSUM,
-            )
+            try:
+                staged_obj = stage(
+                    local_odb,
+                    path_info,
+                    repo.repo_fs,
+                    local_odb.fs.PARAM_CHECKSUM,
+                )
+            except FileNotFoundError as exc:
+                raise PathMissingError(
+                    self.def_path, self.def_repo[self.PARAM_URL]
+                ) from exc
+
             self._staged_objs[rev] = staged_obj
             git_odb = GitObjectDB(repo.repo_fs, repo.root_dir)
             used_objs[git_odb].add(staged_obj)


Test patch:
diff --git a/tests/func/test_import.py b/tests/func/test_import.py
--- a/tests/func/test_import.py
+++ b/tests/func/test_import.py
@@ -8,7 +8,7 @@
 import dvc.data_cloud as cloud
 from dvc.config import NoRemoteError
 from dvc.dvcfile import Dvcfile
-from dvc.exceptions import DownloadError
+from dvc.exceptions import DownloadError, PathMissingError
 from dvc.objects.db import ODBManager
 from dvc.stage.exceptions import StagePathNotFoundError
 from dvc.system import System
@@ -289,7 +289,7 @@ def test_push_wildcard_from_bare_git_repo(
     dvc_repo = make_tmp_dir("dvc-repo", scm=True, dvc=True)
     with dvc_repo.chdir():
         dvc_repo.dvc.imp(os.fspath(tmp_dir), "dirextra")
-        with pytest.raises(FileNotFoundError):
+        with pytest.raises(PathMissingError):
             dvc_repo.dvc.imp(os.fspath(tmp_dir), "dir123")
 
 
@@ -347,11 +347,11 @@ def test_pull_non_workspace(tmp_dir, scm, dvc, erepo_dir):
 
 
 def test_import_non_existing(erepo_dir, tmp_dir, dvc):
-    with pytest.raises(FileNotFoundError):
+    with pytest.raises(PathMissingError):
         tmp_dir.dvc.imp(os.fspath(erepo_dir), "invalid_output")
 
     # https://github.com/iterative/dvc/pull/2837#discussion_r352123053
-    with pytest.raises(FileNotFoundError):
+    with pytest.raises(PathMissingError):
         tmp_dir.dvc.imp(os.fspath(erepo_dir), "/root/", "root")