Repository: ReviewNB/treon

Issue:
Allow multiple PATHs to be provided
According to `treon --help`, treon is meant to be invoked as

```
treon [PATH] [--threads=<number>] [-v] [--exclude=<string>]...
```

However, it is currently not possible to pass multiple PATHs, unlike many command-line tools (e.g., `cat`).  If multiple PATHs are provided, the help text is displayed and nothing else happens.

It is possible to _exclude_ multiple paths (#1), but currently, the only way to include multiple paths (e.g., to test multiple notebooks by individual filename or to test multiple directories) is to invoke treon multiple times sequentially, once for each.  But this means that the full benefits of multithreading are not available.

Discussion:

Patch:
diff --git a/treon/treon.py b/treon/treon.py
index 60c75f0..f34f9fe 100644
--- a/treon/treon.py
+++ b/treon/treon.py
@@ -2,7 +2,7 @@
 """
 Usage:
   treon
-  treon [PATH] [--threads=<number>] [-v] [--exclude=<string>]...
+  treon [PATH]... [--threads=<number>] [-v] [--exclude=<string>]...
 
 Arguments:
   PATH                File or directory path to find notebooks to test. Searches recursively for directory paths. [default: current working directory]
@@ -120,24 +120,29 @@ def filter_results(results, args):
 
 
 def get_notebooks_to_test(args):
-    path = args['PATH'] or os.getcwd()
+    paths = args['PATH'] or [os.getcwd()]
     result = []
 
-    if os.path.isdir(path):
-        LOG.info('Recursively scanning %s for notebooks...', path)
-        path = os.path.join(path, '')  # adds trailing slash (/) if it's missing
-        glob_path = path + '**/*.ipynb'
-        result = glob.glob(glob_path, recursive=True)
-    elif os.path.isfile(path):
-        if path.lower().endswith('.ipynb'):
-            LOG.debug('Testing notebook %s', path)
-            result = [path]
+    for path in paths:
+        current_result = []
+
+        if os.path.isdir(path):
+            LOG.info('Recursively scanning %s for notebooks...', path)
+            path = os.path.join(path, '')  # adds trailing slash (/) if it's missing
+            glob_path = path + '**/*.ipynb'
+            current_result = glob.glob(glob_path, recursive=True)
+        elif os.path.isfile(path):
+            if path.lower().endswith('.ipynb'):
+                LOG.debug('Testing notebook %s', path)
+                current_result = [path]
+            else:
+                sys.exit('{path} is not a Notebook'.format(path=path))
         else:
-            sys.exit('{path} is not a Notebook'.format(path=path))
-    else:
-        sys.exit('{path} is not a valid path'.format(path=path))
+            sys.exit('{path} is not a valid path'.format(path=path))
 
-    if not result:
-        sys.exit('No notebooks to test in {path}'.format(path=path))
+        if not current_result:
+            sys.exit('No notebooks to test in {path}'.format(path=path))
+
+        result.extend(current_result)
 
     return filter_results(result, args)


Test patch:
diff --git a/tests/test_treon.py b/tests/test_treon.py
index a07dd7a..39d1fd6 100644
--- a/tests/test_treon.py
+++ b/tests/test_treon.py
@@ -68,3 +68,17 @@ def test_filter_results_exclude_is_not_dir(mock_isdir):
     filtered = treon.filter_results(results=results, args=args)
     expected = []
     assert filtered == expected
+
+
+def test_get_notebooks_to_test_with_multiple_paths():
+    notebook_files = [
+        os.path.join(os.path.dirname(__file__), "resources/basic.ipynb"),
+        os.path.join(os.path.dirname(__file__), "resources/unittest_failed.ipynb"),
+    ]
+    args = {
+        "PATH": list(notebook_files),
+        "--exclude": [],
+    }
+    notebooks = treon.get_notebooks_to_test(args=args)
+    expected = notebook_files
+    assert notebooks == expected