links: Port local link from cros.factory.device.links.
BUG=none
TEST=none
Change-Id: I7cdb8669257bb713e6e488f8d25d95005598be3d
Reviewed-on: https://chromium-review.googlesource.com/418540
Commit-Ready: Chih-Yu Huang <akahuang@chromium.org>
Tested-by: Chih-Yu Huang <akahuang@chromium.org>
Reviewed-by: Wei-Han Chen <stimim@chromium.org>
diff --git a/graphyte/links/local.py b/graphyte/links/local.py
new file mode 100644
index 0000000..36d1e5c
--- /dev/null
+++ b/graphyte/links/local.py
@@ -0,0 +1,62 @@
+# Copyright 2016 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Implementation of graphyte.link.DeviceLink on local system."""
+
+import pipes
+import shutil
+import subprocess
+
+from .. import link
+
+
+class LocalLink(link.DeviceLink):
+ """Runs locally on a device."""
+
+ def __init__(self, shell_path=None):
+ """Link constructor.
+
+ Args:
+ shell_path: A string for the path of default shell.
+ """
+ self._shell_path = shell_path
+
+ def Push(self, local, remote):
+ shutil.copy(local, remote)
+
+ def PushDirectory(self, local, remote):
+ shutil.copytree(local, remote)
+
+ def Pull(self, remote, local=None):
+ if local is None:
+ with open(remote) as f:
+ return f.read()
+ shutil.copy(remote, local)
+
+ def Shell(self, command, stdin=None, stdout=None, stderr=None):
+ # On most remote links, we always need to execute the commands via shell. To
+ # unify the behavior we should always run the command using shell even on
+ # local links. Ideally python should find the right shell intepreter for us,
+ # however at least in Python 2.x, it was unfortunately hard-coded as
+ # (['/bin/sh', '-c'] + args) when shell=True. In other words, if your
+ # default shell is not sh or if it is in other location (for instance,
+ # Android only has /system/bin/sh) then calling Popen may give you 'No such
+ # file or directory' error.
+
+ if not isinstance(command, basestring):
+ command = ' '.join(pipes.quote(param) for param in command)
+
+ if self._shell_path:
+ # Shell path is specified and we have to quote explicitly.
+ command = [self._shell_path, '-c', command]
+ shell = False
+ else:
+ # Trust default path specified by Python runtime. Useful for non-POSIX
+ # systems like Windows.
+ shell = True
+ return subprocess.Popen(command, shell=shell, close_fds=True, stdin=stdin,
+ stdout=stdout, stderr=stderr)
+
+ def IsReady(self):
+ return True