Repository: iterative/dvc

Issue:
import: support --no-exec
What's the point of `import-url --no-exec`?

Our [docs](https://dvc.org/doc/command-reference/import-url#options) read "E.g. if the file or directory already exist it can be used to skip download. dvc commit <out>.dvc should be used to calculate the URL and data hash, update the .dvc files, and save existing data to the cache."

Why would you want to import a file that you already have, and that would the be immediately overwritten as a regular output with `commit`? (if I get that last part right)

And why doesn't it exist in `import`?


Discussion:
Please transfer to dvc.org if this is mainly a matter of re-explaining the option's motivation. But it seems inconsistent that `import` doesn't also have it so we could keep this one as a request to add it there.
@jorgeorpinel, a contributor asked for it, here's the motivation: #3513.
I see. Shouldn't it be in `import` as well though? For consistency at least, and because of the same motivation.

> Also commented in https://github.com/iterative/dvc/issues/3513#issuecomment-692192135
@jorgeorpinel We'll add one if someone will ask for it. The consistency there is not as obvious to add a feature that is uncalled for.
I'm mentioning all this because someone asked for it implicitly in https://discuss.dvc.org/t/direct-copy-between-shared-cache-and-external-dependencies-outputs/487/3
@jorgeorpinel Ah, ok, makes sense. Repurposing this issue then.
Thanks!
Hello everyone! I'm picking this up. :) 

Patch:
diff --git a/dvc/command/imp.py b/dvc/command/imp.py
--- a/dvc/command/imp.py
+++ b/dvc/command/imp.py
@@ -17,6 +17,7 @@ def run(self):
                 out=self.args.out,
                 fname=self.args.file,
                 rev=self.args.rev,
+                no_exec=self.args.no_exec,
             )
         except DvcException:
             logger.exception(
@@ -65,4 +66,10 @@ def add_parser(subparsers, parent_parser):
         help="Specify name of the DVC-file this command will generate.",
         metavar="<filename>",
     )
+    import_parser.add_argument(
+        "--no-exec",
+        action="store_true",
+        default=False,
+        help="Only create DVC-file without actually downloading it.",
+    )
     import_parser.set_defaults(func=CmdImport)
diff --git a/dvc/repo/imp.py b/dvc/repo/imp.py
--- a/dvc/repo/imp.py
+++ b/dvc/repo/imp.py
@@ -1,6 +1,8 @@
-def imp(self, url, path, out=None, fname=None, rev=None):
+def imp(self, url, path, out=None, fname=None, rev=None, no_exec=False):
     erepo = {"url": url}
     if rev is not None:
         erepo["rev"] = rev
 
-    return self.imp_url(path, out=out, fname=fname, erepo=erepo, frozen=True)
+    return self.imp_url(
+        path, out=out, fname=fname, erepo=erepo, frozen=True, no_exec=no_exec
+    )


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
@@ -465,3 +465,13 @@ def test_try_import_complete_repo(tmp_dir, dvc, erepo_dir):
     with pytest.raises(IsADVCRepoError) as exc_info:
         dvc.imp(os.fspath(erepo_dir), os.curdir, out="out")
     assert expected_message == str(exc_info.value)
+
+
+def test_import_with_no_exec(tmp_dir, dvc, erepo_dir):
+    with erepo_dir.chdir():
+        erepo_dir.dvc_gen("foo", "foo content", commit="create foo")
+
+    dvc.imp(os.fspath(erepo_dir), "foo", out="foo_imported", no_exec=True)
+
+    dst = tmp_dir / "foo_imported"
+    assert not dst.exists()
diff --git a/tests/unit/command/test_imp.py b/tests/unit/command/test_imp.py
--- a/tests/unit/command/test_imp.py
+++ b/tests/unit/command/test_imp.py
@@ -24,5 +24,41 @@ def test_import(mocker):
     assert cmd.run() == 0
 
     m.assert_called_once_with(
-        "repo_url", path="src", out="out", fname="file", rev="version"
+        "repo_url",
+        path="src",
+        out="out",
+        fname="file",
+        rev="version",
+        no_exec=False,
+    )
+
+
+def test_import_no_exec(mocker):
+    cli_args = parse_args(
+        [
+            "import",
+            "repo_url",
+            "src",
+            "--out",
+            "out",
+            "--file",
+            "file",
+            "--rev",
+            "version",
+            "--no-exec",
+        ]
+    )
+
+    cmd = cli_args.func(cli_args)
+    m = mocker.patch.object(cmd.repo, "imp", autospec=True)
+
+    assert cmd.run() == 0
+
+    m.assert_called_once_with(
+        "repo_url",
+        path="src",
+        out="out",
+        fname="file",
+        rev="version",
+        no_exec=True,
     )