Repository: E3SM-Project/zstash

Issue:
Improve Zstash extract warnings
Print a short explanation on the screen if the match doesn't return any files.

Discussion:

Patch:
diff --git a/zstash/extract.py b/zstash/extract.py
index 56704d7..bc44ac7 100644
--- a/zstash/extract.py
+++ b/zstash/extract.py
@@ -232,7 +232,11 @@ def extract_database(
                 u"select * from files where name GLOB ? or tar GLOB ?",
                 (args_file, args_file),
             )
-            matches_ = matches_ + cur.fetchall()
+            match: List[TupleFilesRow] = cur.fetchall()
+            if match:
+                matches_ = matches_ + match
+            else:
+                logger.info("No matches for {}".format(args_file))
 
     matches: List[FilesRow] = list(map(lambda match: FilesRow(match), matches_))
 


Test patch:
diff --git a/tests/test_extract.py b/tests/test_extract.py
index ff129c0..cdc9c62 100644
--- a/tests/test_extract.py
+++ b/tests/test_extract.py
@@ -24,13 +24,13 @@ class TestExtract(TestZstash):
 
     # `zstash extract` is tested in TestExtract and TestExtractParallel.
     # x = on, no mark = off, b = both on and off tested
-    # option | ExtractVerbose | Extract | ExtractCache | ExtractTars | ExtractParallel | ExtractParallelTars |
-    # --hpss    |x|x|x|x|x|x|
-    # --workers | | | | |x|x|
-    # --cache   | | |x| | | |
-    # --keep    | |x| | | | |
-    # --tars    | | | |x| |x|
-    # -v        |x| | | |b| |
+    # option | ExtractVerbose | Extract | ExtractCache | ExtractTars | ExtractFile | ExtractParallel | ExtractParallelTars |
+    # --hpss    |x|x|x|x|x|x|x|
+    # --workers | | | | | |x|x|
+    # --cache   | | |x| | | | |
+    # --keep    | |x| | | | | |
+    # --tars    | | | |x| | |x|
+    # -v        |x| | | | |b| |
 
     def helperExtractVerbose(self, test_name, hpss_path, zstash_path=ZSTASH_PATH):
         """
@@ -208,6 +208,42 @@ class TestExtract(TestZstash):
             )
             self.stop(error_message)
 
+    def helperExtractFile(self, test_name, hpss_path, zstash_path=ZSTASH_PATH):
+        """
+        Test `zstash extract` with a specific file.
+        """
+        self.hpss_path = hpss_path
+        use_hpss = self.setupDirs(test_name)
+        self.create(use_hpss, zstash_path)
+        self.assertWorkspace()
+        os.rename(self.test_dir, self.backup_dir)
+        os.mkdir(self.test_dir)
+        os.chdir(self.test_dir)
+        if not use_hpss:
+            shutil.copytree(
+                "{}/{}/{}".format(TOP_LEVEL, self.backup_dir, self.cache), self.copy_dir
+            )
+        cmd = "{}zstash extract --hpss={} file1.txt".format(zstash_path, self.hpss_path)
+        output, err = run_cmd(cmd)
+        os.chdir(TOP_LEVEL)
+        expected_present = [
+            "INFO: No matches for file1.txt",
+        ]
+        expected_absent = ["ERROR", "Not extracting"]
+        self.check_strings(cmd, output + err, expected_present, expected_absent)
+
+        os.chdir(self.test_dir)
+        cmd = "{}zstash extract --hpss={} file0.txt".format(zstash_path, self.hpss_path)
+        output, err = run_cmd(cmd)
+        os.chdir(TOP_LEVEL)
+        expected_present = [
+            "Extracting file0.txt",
+        ]
+        if use_hpss:
+            expected_present.append("Transferring file from HPSS")
+        expected_absent = ["ERROR", "Not extracting"]
+        self.check_strings(cmd, output + err, expected_present, expected_absent)
+
     def testExtractVerbose(self):
         self.helperExtractVerbose("testExtractVerbose", "none")
 
@@ -236,6 +272,13 @@ class TestExtract(TestZstash):
         self.conditional_hpss_skip()
         helperExtractTars(self, "testExtractTarsHPSS", HPSS_ARCHIVE)
 
+    def testExtractFile(self):
+        self.helperExtractFile("testExtractFile", "none")
+
+    def testExtractFileHPSS(self):
+        self.conditional_hpss_skip()
+        self.helperExtractFile("testExtractFileHPSS", HPSS_ARCHIVE)
+
 
 def helperExtractTars(
     tester, test_name, hpss_path, worker_str="", zstash_path=ZSTASH_PATH
diff --git a/tests/test_extract_parallel.py b/tests/test_extract_parallel.py
index bbbd360..c9dfd69 100644
--- a/tests/test_extract_parallel.py
+++ b/tests/test_extract_parallel.py
@@ -24,13 +24,13 @@ class TestExtractParallel(TestZstash):
 
     # `zstash extract` is tested in TestExtract and TestExtractParallel.
     # x = on, no mark = off, b = both on and off tested
-    # option | ExtractVerbose | Extract | ExtractCache | ExtractTars | ExtractParallel | ExtractParallelTars |
-    # --hpss    |x|x|x|x|x|x|
-    # --workers | | | | |x|x|
-    # --cache   | | |x| | | |
-    # --keep    | |x| | | | |
-    # --tars    | | | |x| |x|
-    # -v        |x| | | |b| |
+    # option | ExtractVerbose | Extract | ExtractCache | ExtractTars | ExtractFile | ExtractParallel | ExtractParallelTars |
+    # --hpss    |x|x|x|x|x|x|x|
+    # --workers | | | | | |x|x|
+    # --cache   | | |x| | | | |
+    # --keep    | |x| | | | | |
+    # --tars    | | | |x| | |x|
+    # -v        |x| | | | |b| |
 
     def helperExtractParallel(self, test_name, hpss_path, zstash_path=ZSTASH_PATH):
         """