Repository: radiasoft/pykern

Issue:
add file compare using filecmp

Discussion:

Patch:
diff --git a/pykern/pkio.py b/pykern/pkio.py
index 898d378..9fcd508 100644
--- a/pykern/pkio.py
+++ b/pykern/pkio.py
@@ -10,6 +10,7 @@ from __future__ import absolute_import, division, print_function
 from pykern import pkconst
 import contextlib
 import errno
+import filecmp
 import glob
 import io
 import os
@@ -47,6 +48,36 @@ def atomic_write(path, contents, **kwargs):
                 pass
 
 
+def compare_files(path1, path2, force=False):
+    """Compares two files using `filecmp.cmp`
+
+    Note that `filecmp` uses `os.stat` to see if a file is the
+    same. If the size, mtime, and type are not identical, it does
+    a comparison of the contents.
+
+    `filecmp` caches prior resuls of the content comparisons.
+    `force` "ensures" no caching, but since the cache is global,
+    this can't be guaranteed in multithreaded environments.
+
+    Args:
+        path1 (str or py.path): first file
+        path2 (str or py.path): second file
+        force (bool): if True, call `filecmp.clear_cache` before comparison and ignore stats.
+
+    Returns:
+        bool: True if the files exist and have the same stats
+
+    """
+    if force:
+        filecmp.clear_cache()
+    try:
+        return filecmp.cmp(str(path1), str(path2), shallow=not force)
+    except Exception as e:
+        if exception_is_not_found(e):
+            return False
+        raise
+
+
 def exception_is_not_found(exc):
     """True if exception is IOError and ENOENT
 


Test patch:
diff --git a/tests/pkio_test.py b/tests/pkio_test.py
index 95d8a31..a971fa8 100644
--- a/tests/pkio_test.py
+++ b/tests/pkio_test.py
@@ -20,7 +20,7 @@ def test_atomic_write():
         pkunit.pkeq("abc", pkio.read_text("x.ABC"))
 
 
-def test__exception_reason():
+def test_exception_reason():
     from pykern import pkio
     from pykern.pkcollections import PKDict
     from pykern.pkunit import pkeq
@@ -40,6 +40,36 @@ def test__exception_reason():
         pkeq(e, a)
 
 
+def test_compare_files():
+    from pykern import pkio
+    from pykern import pkunit
+    from pykern.pkunit import pkok
+
+    with pkunit.save_chdir_work():
+        text = "abc"
+        b = pkio.py_path("base")
+        b.write(text)
+        c = pkio.py_path("copy")
+        b.copy(c)
+        c.setmtime(b.mtime())
+        c = pkio.py_path("same-content")
+        c.write(text)
+        c.setmtime(b.mtime() + 10)
+        c = pkio.py_path("same-stat")
+        c.write(len(text) * "X")
+        c.setmtime(b.mtime())
+        c = pkio.py_path("diff-size")
+        c.write(text + text)
+        c.setmtime(b.mtime())
+        pkok(pkio.compare_files("base", "copy"), "copy")
+        pkok(pkio.compare_files("base", "same-content"), "same-content")
+        pkok(pkio.compare_files("base", "same-stat"), "same-stat")
+        pkok(not pkio.compare_files("base", "same-stat", force=True), "same-stat force")
+        pkok(not pkio.compare_files("base", "diff-size"), "diff-size")
+        pkok(not pkio.compare_files("base", "not-found"), "not-found")
+        pkok(not pkio.compare_files("both-not-found", "not-found"), "both-not-found")
+
+
 def test_has_file_extension():
     from pykern.pkunit import pkeq
     from pykern import pkio