Repository: Gurobi/gurobi-logtools

Issue:
Throw a more useful error message if no logs were parsed
If the glob pattern matches no files (which can easily happen due to a typo, or being in the wrong directory), we get this unhelpful error during post-processing:

```
>>> import grblogtools as glt
>>> glt.parse("asdjkhaskd")
<grblogtools.api.ParseResult object at 0x1023f75b0>
>>> glt.parse("asdjkhaskd").summary()
Traceback (most recent call last):
  File "/Users/bowly/.pyenv/versions/3.10.3/lib/python3.10/site-packages/pandas/core/indexes/base.py", line 3800, in get_loc
    return self._engine.get_loc(casted_key)
  File "pandas/_libs/index.pyx", line 138, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/index.pyx", line 165, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/hashtable_class_helper.pxi", line 5745, in pandas._libs.hashtable.PyObjectHashTable.get_item
  File "pandas/_libs/hashtable_class_helper.pxi", line 5753, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 'Version'
```

We should throw something more useful, like a FileNotFoundError at the parsing step if no files match.

Discussion:

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f53933..53e1b05 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 ## Unreleased
 ### Fixed
 ### Changed
+- If logfiles are not found when parsing then a FileNotFoundError is raised
 ### Removed
 
 ## 3.1.0 - 2024-07-25
diff --git a/src/gurobi_logtools/api.py b/src/gurobi_logtools/api.py
index 09269a2..437e971 100644
--- a/src/gurobi_logtools/api.py
+++ b/src/gurobi_logtools/api.py
@@ -169,8 +169,12 @@ def parse(patterns: Union[str, List[str]], write_to_dir=None) -> ParseResult:
     result = ParseResult(write_to_dir=write_to_dir)
     if type(patterns) is str:
         patterns = [patterns]
-    logfiles = itertools.chain(*(glob.glob(pattern) for pattern in patterns))
-    for logfile in sorted(set(logfiles)):
+    logfiles = sorted(
+        set(itertools.chain(*(glob.glob(pattern) for pattern in patterns)))
+    )
+    if not len(logfiles):
+        raise FileNotFoundError(f"No logfiles found in patterns: {patterns}")
+    for logfile in logfiles:
         result.parse(logfile)
     return result
 
@@ -190,8 +194,10 @@ def get_dataframe(logfiles: List[str], timelines=False, prettyparams=False):
     """
     result = parse(logfiles)
     summary = result.summary(prettyparams=prettyparams)
+
     if not timelines:
         return summary
+
     return summary, dict(
         norel=result.progress("norel"),
         rootlp=result.progress("rootlp"),


Test patch:
diff --git a/tests/test_api.py b/tests/test_api.py
index 00ce2d1..bd9ee7a 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -281,3 +281,8 @@ def test_rewrite_filenames():
             assert log_file.stem == expected_name
             assert len(start_lines) == 1
             assert len(end_lines) == 1
+
+
+def test_no_logfile_error():
+    with pytest.raises(FileNotFoundError):
+        glt.parse("/file/with/a/tyop")