Repository: iterative/dvc

Issue:
'NoneType' object is not subscriptable 
## Bug Report

I came up with this issue by other paths, but I tried to find an easy way to isolate it.

#### steps to reproduce

```sh
dvc remote add s3 s3://
dvc remote default s3
dvc run --no-exec -n train \
  --outs-persist model.data \
   ./train.sh

git add dvc.yaml
dvc commit
```
outputs 
```sh
ERROR: unexpected error - 'NoneType' object is not subscriptable 
Having any troubles? Hit us up at https://dvc.org/support, we are always happy to help!               
```

### Please provide information about your setup

**Output of `dvc version`:**

```console
WARNING: Unable to detect supported link types, as cache directory '.dvc/cache' doesn't exist. It is usually auto-created by commands such as `dvc add/fetch/pull/run/import`, but you could create it manually to enable this check.
DVC version: 1.1.11
Python version: 3.7.6
Platform: Darwin-19.0.0-x86_64-i386-64bit
Binary: False
Package: pip
Supported remotes: azure, gdrive, gs, hdfs, http, https, s3, ssh, oss
Repo: dvc, git
Filesystem type (workspace): ('apfs', '/dev/disk1s5')
```




Discussion:
Ran into this as well. Working on a fix...

Resolving patch:
diff --git a/dvc/dependency/repo.py b/dvc/dependency/repo.py
--- a/dvc/dependency/repo.py
+++ b/dvc/dependency/repo.py
@@ -67,7 +67,7 @@ def _get_checksum(self, locked=True):
                     return self.repo.cache.local.tree.get_hash(path, tree=tree)
                 return tree.get_file_hash(path)
 
-    def status(self):
+    def workspace_status(self):
         current_checksum = self._get_checksum(locked=True)
         updated_checksum = self._get_checksum(locked=False)
 
@@ -76,6 +76,9 @@ def status(self):
 
         return {}
 
+    def status(self):
+        return self.workspace_status()
+
     def save(self):
         pass
 
diff --git a/dvc/output/base.py b/dvc/output/base.py
--- a/dvc/output/base.py
+++ b/dvc/output/base.py
@@ -199,10 +199,7 @@ def changed_cache(self, filter_info=None):
 
         return self.cache.changed_cache(self.checksum, filter_info=filter_info)
 
-    def status(self):
-        if self.checksum and self.use_cache and self.changed_cache():
-            return {str(self): "not in cache"}
-
+    def workspace_status(self):
         if not self.exists:
             return {str(self): "deleted"}
 
@@ -214,6 +211,12 @@ def status(self):
 
         return {}
 
+    def status(self):
+        if self.checksum and self.use_cache and self.changed_cache():
+            return {str(self): "not in cache"}
+
+        return self.workspace_status()
+
     def changed(self):
         status = self.status()
         logger.debug(str(status))
@@ -280,6 +283,7 @@ def save(self):
         self.info = self.save_info()
 
     def commit(self):
+        assert self.info
         if self.use_cache:
             self.cache.save(self.path_info, self.cache.tree, self.info)
 
diff --git a/dvc/stage/__init__.py b/dvc/stage/__init__.py
--- a/dvc/stage/__init__.py
+++ b/dvc/stage/__init__.py
@@ -396,7 +396,7 @@ def ignore_outs(self):
 
     @staticmethod
     def _changed_entries(entries):
-        return [str(entry) for entry in entries if entry.changed_checksum()]
+        return [str(entry) for entry in entries if entry.workspace_status()]
 
     def _changed_stage_entry(self):
         return f"'md5' of {self} changed."


Test patch:
diff --git a/tests/func/test_commit.py b/tests/func/test_commit.py
--- a/tests/func/test_commit.py
+++ b/tests/func/test_commit.py
@@ -2,7 +2,9 @@
 
 import pytest
 
+from dvc.dependency.base import DependencyDoesNotExistError
 from dvc.dvcfile import PIPELINE_FILE
+from dvc.output.base import OutputDoesNotExistError
 from dvc.stage.exceptions import StageCommitError
 from dvc.utils.yaml import dump_yaml, load_yaml
 
@@ -86,6 +88,24 @@ def test_commit_no_exec(tmp_dir, dvc):
     assert dvc.status(stage.path) == {}
 
 
+def test_commit_no_exec_missing_dep(tmp_dir, dvc):
+    stage = dvc.run(
+        name="my", cmd="mycmd", deps=["dep"], outs=["out"], no_exec=True
+    )
+    assert dvc.status(stage.path)
+
+    with pytest.raises(DependencyDoesNotExistError):
+        dvc.commit(stage.path, force=True)
+
+
+def test_commit_no_exec_missing_out(tmp_dir, dvc):
+    stage = dvc.run(name="my", cmd="mycmd", outs=["out"], no_exec=True)
+    assert dvc.status(stage.path)
+
+    with pytest.raises(OutputDoesNotExistError):
+        dvc.commit(stage.path, force=True)
+
+
 def test_commit_pipeline_stage(tmp_dir, dvc, run_copy):
     tmp_dir.gen("foo", "foo")
     stage = run_copy("foo", "bar", no_commit=True, name="copy-foo-bar")