Repository: frictionlessdata/goodtables-py

Issue:
Goodtables CLI should fail if received paths doesn't exist
For example:

```
$ goodtables foobar                                       
DATASET
=======
{'error-count': 1,
 'preset': 'nested',
 'table-count': 1,
 'time': 0.002,
 'valid': False}

TABLE [1]
=========
{'error-count': 1,
 'headers': [],
 'row-count': 0,
 'source': 'foobar',
 'time': 0.001,
 'valid': False}
---------
[-,-] [format-error] Format "None" is not supported
```

It should have failed with `[-,-] [io-error] [Errno 2] No such file or directory: 'foobar'`, as it already does if the file ends in `.csv`

Discussion:
tsungyong: I've updated it.  I wonder if it would be a bit cleaner if this was implemented in tabulator.Stream though, as the root issue is that `Stream.open('invalid.csv')` is giving a different error from `Stream.open('invalid')`, even though they are both the same error (the local file is not found).

Patch:
diff --git a/goodtables/inspector.py b/goodtables/inspector.py
index e17e464..8491ace 100644
--- a/goodtables/inspector.py
+++ b/goodtables/inspector.py
@@ -4,6 +4,12 @@ from __future__ import print_function
 from __future__ import absolute_import
 from __future__ import unicode_literals
 
+import os
+try:
+    from urllib.parse import urlparse
+except ImportError:
+    from urlparse import urlparse
+
 import six
 import datetime
 import operator
@@ -174,6 +180,9 @@ class Inspector(object):
             if schema is None:
                 checks = _filter_checks(checks, type='schema', inverse=True)
         except Exception as exception:
+            if _local_file_not_found(source):
+                message = "No such file or directory: '%s'" % source
+                exception = tabulator.exceptions.SourceError(message)
             fatal_error = True
             error = _compose_error_from_exception(exception)
             errors.append(error)
@@ -309,6 +318,11 @@ def _filter_checks(checks, type=None, context=None, inverse=False):
     return result
 
 
+def _local_file_not_found(source):
+    return urlparse(source).scheme == '' \
+           and not os.path.isfile(source)
+
+
 def _compose_error_from_exception(exception):
     code = 'source-error'
     message = str(exception)


Test patch:
diff --git a/tests/test_inspector.py b/tests/test_inspector.py
index e43c233..b180fd0 100644
--- a/tests/test_inspector.py
+++ b/tests/test_inspector.py
@@ -122,6 +122,13 @@ def test_inspector_catch_all_iter_exceptions(log):
     ]
 
 
+def test_inspector_missing_local_file_raises_source_error_issue_315(log):
+    inspector = Inspector()
+    report = inspector.inspect([{'source': 'invalid'}])
+    assert log(report) == [
+        (1, None, None, 'source-error'),
+    ]
+
 # Warnings
 
 def test_inspector_warnings_no():