1
0
mirror of https://github.com/google/adb-sync.git synced 2026-01-03 01:48:02 +00:00

pyformat.

This commit is contained in:
Rudolf Polzer
2018-11-27 08:45:13 -08:00
parent bbe25f9c68
commit c75950bbfe

251
adb-sync
View File

@@ -13,7 +13,6 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Sync files from/to an Android device."""
from __future__ import unicode_literals
@@ -52,7 +51,8 @@ class AdbFileSystem(object):
# - st_mode (but only about S_ISDIR and S_ISREG properties)
# Therefore, we only capture parts of 'ls -l' output that we actually use.
# The other fields will be filled with dummy values.
LS_TO_STAT_RE = re.compile(br'''^
LS_TO_STAT_RE = re.compile(
br"""^
(?:
(?P<S_IFREG> -) |
(?P<S_IFBLK> b) |
@@ -86,7 +86,8 @@ class AdbFileSystem(object):
[ ]
# Don't capture filename for symlinks (ambiguous).
(?(S_IFLNK) .* | (?P<filename> .*))
$''', re.DOTALL | re.VERBOSE)
$""", re.DOTALL | re.VERBOSE)
def LsToStat(self, line):
"""Convert a line from 'ls -l' output to a stat result.
@@ -109,19 +110,28 @@ class AdbFileSystem(object):
# Get the values we're interested in.
st_mode = ( # 0755
stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
if groups['S_IFREG']: st_mode |= stat.S_IFREG
if groups['S_IFBLK']: st_mode |= stat.S_IFBLK
if groups['S_IFCHR']: st_mode |= stat.S_IFCHR
if groups['S_IFDIR']: st_mode |= stat.S_IFDIR
if groups['S_IFIFO']: st_mode |= stat.S_IFIFO
if groups['S_IFLNK']: st_mode |= stat.S_IFLNK
if groups['S_IFSOCK']: st_mode |= stat.S_IFSOCK
stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH
| stat.S_IXOTH)
if groups['S_IFREG']:
st_mode |= stat.S_IFREG
if groups['S_IFBLK']:
st_mode |= stat.S_IFBLK
if groups['S_IFCHR']:
st_mode |= stat.S_IFCHR
if groups['S_IFDIR']:
st_mode |= stat.S_IFDIR
if groups['S_IFIFO']:
st_mode |= stat.S_IFIFO
if groups['S_IFLNK']:
st_mode |= stat.S_IFLNK
if groups['S_IFSOCK']:
st_mode |= stat.S_IFSOCK
st_size = groups['st_size']
if st_size is not None:
st_size = int(st_size)
st_mtime = time.mktime(time.strptime(match.group('st_mtime').decode('utf-8'),
'%Y-%m-%d %H:%M'))
st_mtime = time.mktime(
time.strptime(
match.group('st_mtime').decode('utf-8'), '%Y-%m-%d %H:%M'))
# Fill the rest with dummy values.
st_ino = 1
@@ -152,6 +162,7 @@ class AdbFileSystem(object):
"""
class Stdout(object):
def __init__(self, popen):
self.popen = popen
@@ -184,13 +195,14 @@ class AdbFileSystem(object):
# while echo does its own backslash escape handling additionally to the
# shell's. Too bad printf "%s\n" is not available.
test_strings = [
b'(',
b'(; #`ls`$PATH\'"(\\\\\\\\){};!\xc0\xaf\xff\xc2\xbf'
b'(', b'(; #`ls`$PATH\'"(\\\\\\\\){};!\xc0\xaf\xff\xc2\xbf'
]
for test_string in test_strings:
good = False
with self.Stdout(self.adb + [b'shell', _sprintf(b'date +%s',
self.QuoteArgument(test_string))]) as stdout:
with self.Stdout(
self.adb +
[b'shell',
_sprintf(b'date +%s', self.QuoteArgument(test_string))]) as stdout:
for line in stdout:
line = line.rstrip(b'\r\n')
if line == test_string:
@@ -201,8 +213,10 @@ class AdbFileSystem(object):
def listdir(self, path): # os's name, so pylint: disable=g-bad-name
"""List the contents of a directory, caching them for later lstat calls."""
with self.Stdout(self.adb + [b'shell', _sprintf(b'ls -al %s',
self.QuoteArgument(path + b'/'))]) as stdout:
with self.Stdout(
self.adb +
[b'shell',
_sprintf(b'ls -al %s', self.QuoteArgument(path + b'/'))]) as stdout:
for line in stdout:
if line.startswith(b'total '):
continue
@@ -221,8 +235,10 @@ class AdbFileSystem(object):
"""Stat a file."""
if path in self.stat_cache:
return self.stat_cache[path]
with self.Stdout(self.adb + [b'shell', _sprintf(b'ls -ald %s',
self.QuoteArgument(path))]) as stdout:
with self.Stdout(
self.adb +
[b'shell', _sprintf(b'ls -ald %s', self.QuoteArgument(path))]
) as stdout:
for line in stdout:
if line.startswith(b'total '):
continue
@@ -234,20 +250,23 @@ class AdbFileSystem(object):
def unlink(self, path): # os's name, so pylint: disable=g-bad-name
"""Delete a file."""
if subprocess.call(self.adb + [b'shell', _sprintf(b'rm %s',
self.QuoteArgument(path))]) != 0:
if subprocess.call(
self.adb +
[b'shell', _sprintf(b'rm %s', self.QuoteArgument(path))]) != 0:
raise OSError('unlink failed')
def rmdir(self, path): # os's name, so pylint: disable=g-bad-name
"""Delete a directory."""
if subprocess.call(self.adb + [b'shell', _sprintf(b'rmdir %s',
self.QuoteArgument(path))]) != 0:
if subprocess.call(
self.adb +
[b'shell', _sprintf(b'rmdir %s', self.QuoteArgument(path))]) != 0:
raise OSError('rmdir failed')
def makedirs(self, path): # os's name, so pylint: disable=g-bad-name
"""Create a directory."""
if subprocess.call(self.adb + [b'shell', _sprintf(b'mkdir -p %s',
self.QuoteArgument(path))]) != 0:
if subprocess.call(
self.adb +
[b'shell', _sprintf(b'mkdir -p %s', self.QuoteArgument(path))]) != 0:
raise OSError('mkdir failed')
def utime(self, path, times):
@@ -255,18 +274,23 @@ class AdbFileSystem(object):
"""Set the time of a file to a specified unix time."""
atime, mtime = times
timestr = time.strftime(b'%Y%m%d.%H%M%S', time.localtime(mtime))
if subprocess.call(self.adb + [b'shell', _sprintf(b'touch -mt %s %s',
timestr, self.QuoteArgument(path))]) != 0:
if subprocess.call(self.adb + [
b'shell',
_sprintf(b'touch -mt %s %s', timestr, self.QuoteArgument(path))
]) != 0:
raise OSError('touch failed')
timestr = time.strftime(b'%Y%m%d.%H%M%S', time.localtime(atime))
if subprocess.call(self.adb + [b'shell',_sprintf( b'touch -at %s %s',
timestr, self.QuoteArgument(path))]) != 0:
if subprocess.call(self.adb + [
b'shell',
_sprintf(b'touch -at %s %s', timestr, self.QuoteArgument(path))
]) != 0:
raise OSError('touch failed')
def glob(self, path):
with self.Stdout(self.adb + [b'shell',
_sprintf(b'for p in %s; do echo "$p"; done',
path)]) as stdout:
with self.Stdout(
self.adb +
[b'shell', _sprintf(b'for p in %s; do echo "$p"; done', path)]
) as stdout:
for line in stdout:
yield line.rstrip(b'\r\n')
@@ -413,8 +437,8 @@ class FileSyncer(object):
logging.info('Scanning and diffing...')
locallist = BuildFileList(os, self.local)
remotelist = BuildFileList(self.adb, self.remote)
self.local_only, self.both, self.remote_only = DiffLists(locallist,
remotelist)
self.local_only, self.both, self.remote_only = DiffLists(
locallist, remotelist)
if not self.local_only and not self.both and not self.remote_only:
logging.warning('No files seen. User error?')
self.src_to_dst = (self.local_to_remote, self.remote_to_local)
@@ -448,6 +472,7 @@ class FileSyncer(object):
dry_run = self.dry_run
class DeleteInterruptedFile(object):
def __enter__(self):
pass
@@ -529,10 +554,12 @@ class FileSyncer(object):
'which --no-clobber forbids.')
continue
if stat.S_ISDIR(dst_stat.st_mode):
kill_files = [x for x in self.dst_only[i]
if x[0][:len(name) + 1] == name + b'/']
self.dst_only[i][:] = [x for x in self.dst_only[i]
if x[0][:len(name) + 1] != name + b'/']
kill_files = [
x for x in self.dst_only[i] if x[0][:len(name) + 1] == name + b'/'
]
self.dst_only[i][:] = [
x for x in self.dst_only[i] if x[0][:len(name) + 1] != name + b'/'
]
for l, s in reversed(kill_files):
if stat.S_ISDIR(s.st_mode):
if not self.dry_run:
@@ -571,8 +598,7 @@ class FileSyncer(object):
self.num_bytes += s.st_size
if not self.dry_run:
if self.preserve_times:
logging.info('%s-Times: accessed %s, modified %s',
self.push[i],
logging.info('%s-Times: accessed %s, modified %s', self.push[i],
time.asctime(time.localtime(s.st_atime)),
time.asctime(time.localtime(s.st_mtime)))
self.dst_fs[i].utime(dst_name, (s.st_atime, s.st_mtime))
@@ -585,7 +611,8 @@ class FileSyncer(object):
end_time = time.time()
dt = end_time - self.start_time
rate = self.num_bytes / 1024.0 / dt
logging.info('Total: %d KB/s (%d bytes in %.3fs)', rate, self.num_bytes, dt)
logging.info('Total: %d KB/s (%d bytes in %.3fs)', rate, self.num_bytes,
dt)
def ExpandWildcards(globber, path):
@@ -617,64 +644,106 @@ def FixPath(src, dst):
def main(*args):
parser = argparse.ArgumentParser(
description='Synchronize a directory between an Android device and the '+
description='Synchronize a directory between an Android device and the ' +
'local file system')
parser.add_argument('source', metavar='SRC', type=str, nargs='+',
help='The directory to read files/directories from. '+
'This must be a local path if -R is not specified, '+
'and an Android path if -R is specified. If SRC does '+
'not end with a final slash, its last path component '+
parser.add_argument(
'source',
metavar='SRC',
type=str,
nargs='+',
help='The directory to read files/directories from. ' +
'This must be a local path if -R is not specified, ' +
'and an Android path if -R is specified. If SRC does ' +
'not end with a final slash, its last path component ' +
'is appended to DST (like rsync does).')
parser.add_argument('destination', metavar='DST', type=str,
help='The directory to write files/directories to. '+
'This must be an Android path if -R is not specified, '+
parser.add_argument(
'destination',
metavar='DST',
type=str,
help='The directory to write files/directories to. ' +
'This must be an Android path if -R is not specified, ' +
'and a local path if -R is specified.')
parser.add_argument('-e', '--adb', metavar='COMMAND', default='adb', type=str,
parser.add_argument(
'-e',
'--adb',
metavar='COMMAND',
default='adb',
type=str,
help='Use the given adb binary and arguments.')
parser.add_argument('--device', action='store_true',
help='Directs command to the only connected USB device; '+
'returns an error if more than one USB device is '+
'present. '+
parser.add_argument(
'--device',
action='store_true',
help='Directs command to the only connected USB device; ' +
'returns an error if more than one USB device is ' + 'present. ' +
'Corresponds to the "-d" option of adb.')
parser.add_argument('--emulator', action='store_true',
help='Directs command to the only running emulator; '+
'returns an error if more than one emulator is running. '+
parser.add_argument(
'--emulator',
action='store_true',
help='Directs command to the only running emulator; ' +
'returns an error if more than one emulator is running. ' +
'Corresponds to the "-e" option of adb.')
parser.add_argument('-s', '--serial', metavar='DEVICE', type=str,
help='Directs command to the device or emulator with '+
'the given serial number or qualifier. Overrides '+
'ANDROID_SERIAL environment variable. Use "adb devices" '+
'to list all connected devices with their respective '+
'serial number. '+
'Corresponds to the "-s" option of adb.')
parser.add_argument('-H', '--host', metavar='HOST', type=str,
help='Name of adb server host (default: localhost). '+
parser.add_argument(
'-s',
'--serial',
metavar='DEVICE',
type=str,
help='Directs command to the device or emulator with ' +
'the given serial number or qualifier. Overrides ' +
'ANDROID_SERIAL environment variable. Use "adb devices" ' +
'to list all connected devices with their respective ' + 'serial number. '
+ 'Corresponds to the "-s" option of adb.')
parser.add_argument(
'-H',
'--host',
metavar='HOST',
type=str,
help='Name of adb server host (default: localhost). ' +
'Corresponds to the "-H" option of adb.')
parser.add_argument('-P', '--port', metavar='PORT', type=str,
help='Port of adb server (default: 5037). '+
parser.add_argument(
'-P',
'--port',
metavar='PORT',
type=str,
help='Port of adb server (default: 5037). ' +
'Corresponds to the "-P" option of adb.')
parser.add_argument('-R', '--reverse', action='store_true',
parser.add_argument(
'-R',
'--reverse',
action='store_true',
help='Reverse sync (pull, not push).')
parser.add_argument('-2', '--two-way', action='store_true',
help='Two-way sync (compare modification time; after '+
'the sync, both sides will have all files in the '+
'respective newest version. This relies on the clocks '+
parser.add_argument(
'-2',
'--two-way',
action='store_true',
help='Two-way sync (compare modification time; after ' +
'the sync, both sides will have all files in the ' +
'respective newest version. This relies on the clocks ' +
'of your system and the device to match.')
#parser.add_argument('-t', '--times', action='store_true',
# help='Preserve modification times when copying.')
parser.add_argument('-d', '--delete', action='store_true',
help='Delete files from DST that are not present on '+
parser.add_argument(
'-d',
'--delete',
action='store_true',
help='Delete files from DST that are not present on ' +
'SRC. Mutually exclusive with -2.')
parser.add_argument('-f', '--force', action='store_true',
help='Allow deleting files/directories when having to '+
'replace a file by a directory or vice versa. This is '+
parser.add_argument(
'-f',
'--force',
action='store_true',
help='Allow deleting files/directories when having to ' +
'replace a file by a directory or vice versa. This is ' +
'disabled by default to prevent large scale accidents.')
parser.add_argument('-n', '--no-clobber', action='store_true',
help='Do not ever overwrite any '+
parser.add_argument(
'-n',
'--no-clobber',
action='store_true',
help='Do not ever overwrite any ' +
'existing files. Mutually exclusive with -f.')
parser.add_argument('--dry-run',action='store_true',
help='Do not do anything - just show what would '+
'be done.')
parser.add_argument(
'--dry-run',
action='store_true',
help='Do not do anything - just show what would ' + 'be done.')
args = parser.parse_args()
args_encoding = locale.getdefaultlocale()[1]
@@ -734,7 +803,8 @@ def main(*args):
if (remote_to_local and local_to_remote) or delete_missing:
if ((remote_to_local and len(localpaths) != len(set(localpaths))) or
(local_to_remote and len(remotepaths) != len(set(remotepaths)))):
logging.error('--two-way and --delete are only supported for disjoint sets of '
logging.error(
'--two-way and --delete are only supported for disjoint sets of '
'source and destination paths (in other words, all SRC must '
'differ in basename).')
parser.print_help()
@@ -742,9 +812,9 @@ def main(*args):
for i in range(len(localpaths)):
logging.info('Sync: local %r, remote %r', localpaths[i], remotepaths[i])
syncer = FileSyncer(adb, localpaths[i], remotepaths[i],
local_to_remote, remote_to_local, preserve_times,
delete_missing, allow_overwrite, allow_replace, dry_run)
syncer = FileSyncer(adb, localpaths[i], remotepaths[i], local_to_remote,
remote_to_local, preserve_times, delete_missing,
allow_overwrite, allow_replace, dry_run)
if not syncer.IsWorking():
logging.error('Device not connected or not working.')
return
@@ -756,5 +826,6 @@ def main(*args):
finally:
syncer.TimeReport()
if __name__ == '__main__':
main(*sys.argv)