Chromium Code Reviews| Index: build/android/pylib/device/device_utils.py |
| diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py |
| index 75b2ae06ced358d9863cdda2c5a383cdf6e5298b..5d04a22135221d0eac8ad38f734774e595402520 100644 |
| --- a/build/android/pylib/device/device_utils.py |
| +++ b/build/android/pylib/device/device_utils.py |
| @@ -11,6 +11,7 @@ Eventually, this will be based on adb_wrapper. |
| import multiprocessing |
| import os |
| import pipes |
| +import re |
| import sys |
| import tempfile |
| import time |
| @@ -86,6 +87,7 @@ class DeviceUtils(object): |
| self._commands_installed = False |
| self._default_timeout = default_timeout |
| self._default_retries = default_retries |
| + self._cache = {} |
| assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)) |
| assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR)) |
| @@ -144,8 +146,8 @@ class DeviceUtils(object): |
| raise device_errors.CommandFailedError( |
| 'Could not enable root.', device=str(self)) |
| - @decorators.WithTimeoutAndRetriesFromInstance() |
| - def GetExternalStoragePath(self, timeout=None, retries=None): |
| + def GetExternalStoragePath(self, timeout=_DEFAULT_TIMEOUT, |
|
jbudorick
2014/10/14 16:55:19
I'm not crazy about moving the timeout/retry bound
|
| + retries=_DEFAULT_RETRIES): |
| """Get the device's path to its SD card. |
| Args: |
| @@ -160,14 +162,16 @@ class DeviceUtils(object): |
| CommandTimeoutError on timeout. |
| DeviceUnreachableError on missing device. |
| """ |
| - return self._GetExternalStoragePathImpl() |
| + if 'external_storage' in self._cache: |
| + return self._cache['external_storage'] |
| - def _GetExternalStoragePathImpl(self): |
| - try: |
| - return self.old_interface.GetExternalStorage() |
| - except AssertionError as e: |
| - raise device_errors.CommandFailedError( |
| - str(e), device=str(self)), None, sys.exc_info()[2] |
| + value = self._NewRunShellImpl('echo $EXTERNAL_STORAGE', timeout=timeout, |
| + retries=retries).rstrip() |
| + if not value: |
|
jbudorick
2014/10/14 16:55:19
I'm not sure that this _has_ to be an error (altho
perezju
2014/10/15 09:22:54
This is what the current implementation does, so n
|
| + raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', |
| + str(self)) |
| + self._cache['external_storage'] = value |
|
jbudorick
2014/10/14 16:55:19
I like the idea of caching things like this and ce
perezju
2014/10/15 09:22:54
Acknowledged.
|
| + return value |
| @decorators.WithTimeoutAndRetriesFromInstance() |
| def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): |
| @@ -326,6 +330,51 @@ class DeviceUtils(object): |
| output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout) |
| return output |
| + def _NewRunShellImpl(self, cmd, cwd=None, env=None, as_root=False, |
|
jbudorick
2014/10/14 16:55:19
I'm not sure about eliminating check_return here.
perezju
2014/10/15 09:22:54
I would argue that we shouldn't make it easy for c
|
| + timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): |
| + """Proposed new interface to run ADB shell commands. |
| + |
| + When the command to run |cmd| is given as a string, it will be interpreted |
| + and run by the shell on the device. |
| + |
| + Alternatively, one can supply |cmd| as a sequence containing the name of |
| + the command to run followed by its arguments. In this case, arguments are |
| + passed to the command exactly as given, without any further processing by |
| + the shell. This allows to easily pass arguments containing spaces or |
| + special characters without having to worry about getting quoting right. |
| + |
| + Args: |
| + cmd: A string with the full command to run on the device, or a sequence |
| + containing the command and its arguments. |
| + cwd: The device directory in which the command should be run. |
| + env: The environment variables with which the command should be run. |
| + as_root: A boolean indicating whether the shell command should be run |
| + with root privileges. |
| + timeout: timeout in seconds |
| + retries: number of retries |
| + |
| + Returns: |
| + The output of the command as a single string. |
| + |
| + Raises: |
| + CommandFailedError if the exit status of the command run is non-zero. |
| + CommandTimeoutError on timeout. |
| + """ |
| + def env_quote(key, value): |
| + if not re.match('^[a-zA-Z_][a-zA-Z0-9_]*$', key): |
|
jbudorick
2014/10/14 16:55:19
Compile the regex as a constant and then use that
perezju
2014/10/15 09:22:54
Acknowledged.
|
| + raise KeyError('Invalid shell variable name %r' % key) |
| + return '%s=%s' % (key, pipes.quote(value)) |
| + |
| + if not isinstance(cmd, basestring): |
| + cmd = ' '.join(pipes.quote(s) for s in cmd) |
| + if as_root and not self._HasRootImpl(): |
| + cmd = 'su -c %s' % cmd |
| + if env is not None: |
| + cmd = '%s %s' % (' '.join(env_quote(*kv) for kv in env.iteritems()), cmd) |
|
jbudorick
2014/10/14 16:55:19
This seems a little more readable to me:
env_qu
perezju
2014/10/15 09:22:54
Acknowledged.
|
| + if cwd is not None: |
| + cmd = 'cd %s && %s' % (pipes.quote(cwd), cmd) |
| + return self.adb.Shell(cmd, expect_rc=0, timeout=timeout, retries=retries) |
| + |
| @decorators.WithTimeoutAndRetriesFromInstance() |
| def KillAll(self, process_name, signum=9, as_root=False, blocking=False, |
| timeout=None, retries=None): |
| @@ -622,7 +671,7 @@ class DeviceUtils(object): |
| zip_proc.start() |
| zip_proc.join() |
| - zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl() |
| + zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath() |
| try: |
| self.adb.Push(zip_file.name, zip_on_device) |
| self._RunShellCommandImpl( |