Repository: iterative/dvc

Issue:
dvc diff ERROR: unexpected error - 'NoneType' object has no attribute 'endswith' 
If given rev is not tracked fails. I.E 2f05868ebe3693a7e7afbdd8923c15ce327b179d does not have metrics:
```
dvc diff  --show-json 2f05868ebe3693a7e7afbdd8923c15ce327b179d 19b33235917f6b3732de8486b35235ba200e74a2
ERROR: unexpected error - 'NoneType' object has no attribute 'endswith' 
```

I would expect at least the difference of files added 

**Please provide information about your setup**
dvc 0.87.0
Python 3.6.9
ubuntu 10.04


dvc diff ERROR: unexpected error - 'NoneType' object has no attribute 'endswith' 
If given rev is not tracked fails. I.E 2f05868ebe3693a7e7afbdd8923c15ce327b179d does not have metrics:
```
dvc diff  --show-json 2f05868ebe3693a7e7afbdd8923c15ce327b179d 19b33235917f6b3732de8486b35235ba200e74a2
ERROR: unexpected error - 'NoneType' object has no attribute 'endswith' 
```

I would expect at least the difference of files added 

**Please provide information about your setup**
dvc 0.87.0
Python 3.6.9
ubuntu 10.04




Discussion:
Not able to reproduce. Not with `dvc diff` nor with `dvc metrics diff`, nor with current version nor with 0.87.0. Closing for now. @DavidGOrtega Please let us know if you are still experiencing this issue. And please provide verbose logs (by adding -v to the command) next time, so it is easier to see the issue in details. Thank you!
Hello, 

I have the same error on 1.0.0a2 when I do either `dvc diff`, `dvc metrics diff` or `dvc metrics show`. Here is the verbos log:

```
2020-05-20 15:16:59,145 ERROR: unexpected error - 'NoneType' object has no attribute 'endswith'
------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/main.py", line 48, in main
    ret = cmd.run()
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/command/metrics.py", line 45, in run
    recursive=self.args.recursive,
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/metrics/__init__.py", line 13, in show
    return show(self.repo, *args, **kwargs)
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/__init__.py", line 25, in wrapper
    ret = f(repo, *args, **kwargs)
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/metrics/show.py", line 99, in show
    vals = _read_metrics(repo, metrics, rev)
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/metrics/show.py", line 63, in _read_metrics
    with tree.open(metric, "r") as fobj:
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/tree.py", line 192, in open
    path, mode=mode, encoding=encoding, **kwargs
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/tree.py", line 49, in open
    if len(outs) != 1 or outs[0].is_dir_checksum:
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/output/base.py", line 170, in is_dir_checksum
    return self.remote.is_dir_checksum(self.checksum)
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/remote/base.py", line 322, in is_dir_checksum
    return checksum.endswith(cls.CHECKSUM_DIR_SUFFIX)
AttributeError: 'NoneType' object has no attribute 'endswith'
------------------------------------------------------------
```

Please let me know how can I help on that.

Here is my `dvc version`:
```
DVC version: 1.0.0a2
Python version: 3.6.8
Platform: Darwin-19.3.0-x86_64-i386-64bit
Binary: False
Package: pip
Supported remotes: http, https, s3
Cache: reflink - supported, hardlink - supported, symlink - supported
Filesystem type (cache directory): ('apfs', '/dev/disk1s2')
Repo: dvc, git
Filesystem type (workspace): ('apfs', '/dev/disk1s2')
```
Not able to reproduce. Not with `dvc diff` nor with `dvc metrics diff`, nor with current version nor with 0.87.0. Closing for now. @DavidGOrtega Please let us know if you are still experiencing this issue. And please provide verbose logs (by adding -v to the command) next time, so it is easier to see the issue in details. Thank you!
Hello, 

I have the same error on 1.0.0a2 when I do either `dvc diff`, `dvc metrics diff` or `dvc metrics show`. Here is the verbos log:

```
2020-05-20 15:16:59,145 ERROR: unexpected error - 'NoneType' object has no attribute 'endswith'
------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/main.py", line 48, in main
    ret = cmd.run()
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/command/metrics.py", line 45, in run
    recursive=self.args.recursive,
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/metrics/__init__.py", line 13, in show
    return show(self.repo, *args, **kwargs)
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/__init__.py", line 25, in wrapper
    ret = f(repo, *args, **kwargs)
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/metrics/show.py", line 99, in show
    vals = _read_metrics(repo, metrics, rev)
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/metrics/show.py", line 63, in _read_metrics
    with tree.open(metric, "r") as fobj:
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/tree.py", line 192, in open
    path, mode=mode, encoding=encoding, **kwargs
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/repo/tree.py", line 49, in open
    if len(outs) != 1 or outs[0].is_dir_checksum:
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/output/base.py", line 170, in is_dir_checksum
    return self.remote.is_dir_checksum(self.checksum)
  File "/Users/corentinhembise/.local/share/virtualenvs/speech-XlnoNrSS/lib/python3.6/site-packages/dvc/remote/base.py", line 322, in is_dir_checksum
    return checksum.endswith(cls.CHECKSUM_DIR_SUFFIX)
AttributeError: 'NoneType' object has no attribute 'endswith'
------------------------------------------------------------
```

Please let me know how can I help on that.

Here is my `dvc version`:
```
DVC version: 1.0.0a2
Python version: 3.6.8
Platform: Darwin-19.3.0-x86_64-i386-64bit
Binary: False
Package: pip
Supported remotes: http, https, s3
Cache: reflink - supported, hardlink - supported, symlink - supported
Filesystem type (cache directory): ('apfs', '/dev/disk1s2')
Repo: dvc, git
Filesystem type (workspace): ('apfs', '/dev/disk1s2')
```

Resolving patch:
diff --git a/dvc/remote/base.py b/dvc/remote/base.py
--- a/dvc/remote/base.py
+++ b/dvc/remote/base.py
@@ -319,6 +319,8 @@ def load_dir_cache(self, checksum):
 
     @classmethod
     def is_dir_checksum(cls, checksum):
+        if not checksum:
+            return False
         return checksum.endswith(cls.CHECKSUM_DIR_SUFFIX)
 
     def get_checksum(self, path_info):


Test patch:
diff --git a/tests/unit/remote/test_base.py b/tests/unit/remote/test_base.py
--- a/tests/unit/remote/test_base.py
+++ b/tests/unit/remote/test_base.py
@@ -136,3 +136,11 @@ def test_cache_checksums(dvc):
     ):
         checksums = list(remote.cache_checksums())
         assert checksums == ["123456"]
+
+
+@pytest.mark.parametrize(
+    "checksum, result",
+    [(None, False), ("", False), ("3456.dir", True), ("3456", False)],
+)
+def test_is_dir_checksum(checksum, result):
+    assert BaseRemote.is_dir_checksum(checksum) == result