cros_build_lib.Open: pass down additional kwargs

With Python 3, we can pass down |encoding| when calling open().
Add support for it here too, and add missing unittest coverage.

BUG=None
TEST=CQ passes

Change-Id: Ib1b4328fbfd7ed4c603178431bcf822ef6b0e439
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2404488
Tested-by: Mike Frysinger <vapier@chromium.org>
Auto-Submit: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Alex Klein <saklein@chromium.org>
Commit-Queue: Mike Frysinger <vapier@chromium.org>
diff --git a/lib/cros_build_lib.py b/lib/cros_build_lib.py
index ccd738c..40e70aa 100644
--- a/lib/cros_build_lib.py
+++ b/lib/cros_build_lib.py
@@ -1577,10 +1577,10 @@
 
 
 @contextlib.contextmanager
-def Open(obj, mode='r'):
+def Open(obj, mode='r', **kwargs):
   """Convenience ctx that accepts a file path or an already open file object."""
   if isinstance(obj, six.string_types):
-    with open(obj, mode=mode) as f:
+    with open(obj, mode=mode, **kwargs) as f:
       yield f
   else:
     yield obj
diff --git a/lib/cros_build_lib_unittest.py b/lib/cros_build_lib_unittest.py
index 972a3c9..2e580eb 100644
--- a/lib/cros_build_lib_unittest.py
+++ b/lib/cros_build_lib_unittest.py
@@ -17,6 +17,7 @@
 import socket
 import subprocess
 import sys
+import unittest
 
 import mock
 from six.moves import builtins
@@ -1267,3 +1268,33 @@
 
     self.assertEqual(self.mockRun.call_count, 3)
     self.assertEqual(cm.exception.args[1].returncode, 1)
+
+
+class OpenTests(cros_test_lib.TempDirTestCase):
+  """Tests for cros_build_lib.Open."""
+
+  def testFile(self):
+    """Read/write a file by path."""
+    path = os.path.join(self.tempdir, 'test.txt')
+    with cros_build_lib.Open(path, mode='w') as fp:
+      fp.write('foo')
+    with cros_build_lib.Open(path, mode='r') as fp:
+      self.assertEqual('foo', fp.read())
+
+  def testHandle(self):
+    """Read/write a file by an open handle."""
+    path = os.path.join(self.tempdir, 'test.txt')
+    with open(path, mode='w') as fp:
+      with cros_build_lib.Open(fp) as fp2:
+        fp2.write('foo')
+    with open(path, mode='r') as fp:
+      with cros_build_lib.Open(fp) as fp2:
+        self.assertEqual('foo', fp2.read())
+
+  @unittest.skipIf(sys.version_info.major < 3, 'Requires py3')
+  def testEncoding(self):
+    """Verify we pass kwargs down."""
+    path = os.path.join(self.tempdir, 'test.txt')
+    with cros_build_lib.Open(path, mode='w', encoding='utf-8') as fp:
+      fp.write(u'ßomß')
+    self.assertEqual(u'ßomß', osutils.ReadFile(path))