Skip to content
This repository was archived by the owner on Jan 10, 2023. It is now read-only.

Commit 397b762

Browse files
committed
Switch use of google-flags (gflags) to more standard argparse.
It is more appropriate for open source projects to use argparse. Keep the 'clever' approach and uses the docstring on methods on AdbCommands/FastbootCommands to generate the arguments on the parser. Override the documentation only when it doesn't make sense, like when an argument can be "object-like". Update default fastboot packet size from 4kb to 1Mb. Fix tests (adb_test.py was broken). Make them executable.
1 parent 29e0c83 commit 397b762

File tree

8 files changed

+399
-219
lines changed

8 files changed

+399
-219
lines changed

adb/adb_commands.py

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,13 @@ def Devices(cls):
109109
def GetState(self):
110110
return self._device_state
111111

112-
def Install(self, apk_path, destination_dir=None, timeout_ms=None):
112+
def Install(self, apk_path, destination_dir='', timeout_ms=None):
113113
"""Install an apk to the device.
114114
115115
Doesn't support verifier file, instead allows destination directory to be
116116
overridden.
117117
118-
Arguments:
118+
Args:
119119
apk_path: Local path to apk to install.
120120
destination_dir: Optional destination directory. Use /system/app/ for
121121
persistent applications.
@@ -135,44 +135,42 @@ def Install(self, apk_path, destination_dir=None, timeout_ms=None):
135135
def Push(self, source_file, device_filename, mtime='0', timeout_ms=None):
136136
"""Push a file or directory to the device.
137137
138-
Arguments:
138+
Args:
139139
source_file: Either a filename, a directory or file-like object to push to
140140
the device.
141-
device_filename: The filename on the device to write to.
141+
device_filename: Destination on the device to write to.
142142
mtime: Optional, modification time to set on the file.
143143
timeout_ms: Expected timeout for any part of the push.
144144
"""
145-
146-
if os.path.isdir(source_file):
147-
self.Shell("mkdir " + device_filename)
148-
for dir_file in os.listdir(source_file):
149-
self.Push(os.path.join(source_file, dir_file), device_filename + "/" + dir_file)
150-
return
151-
152-
connection = self.protocol_handler.Open(
153-
self.handle, destination='sync:',
154-
timeout_ms=timeout_ms)
155145
if isinstance(source_file, basestring):
146+
if os.path.isdir(source_file):
147+
self.Shell("mkdir " + device_filename)
148+
for f in os.listdir(source_file):
149+
self.Push(os.path.join(source_file, f), device_filename + '/' + f)
150+
return
156151
source_file = open(source_file)
152+
153+
connection = self.protocol_handler.Open(
154+
self.handle, destination='sync:', timeout_ms=timeout_ms)
157155
self.filesync_handler.Push(connection, source_file, device_filename,
158156
mtime=int(mtime))
159157
connection.Close()
160158

161-
def Pull(self, device_filename, dest_file=None, timeout_ms=None):
159+
def Pull(self, device_filename, dest_file='', timeout_ms=None):
162160
"""Pull a file from the device.
163161
164-
Arguments:
165-
device_filename: The filename on the device to pull.
162+
Args:
163+
device_filename: Filename on the device to pull.
166164
dest_file: If set, a filename or writable file-like object.
167165
timeout_ms: Expected timeout for any part of the pull.
168166
169167
Returns:
170168
The file data if dest_file is not set.
171169
"""
172-
if isinstance(dest_file, basestring):
173-
dest_file = open(dest_file, 'w')
174-
elif not dest_file:
170+
if not dest_file:
175171
dest_file = cStringIO.StringIO()
172+
elif isinstance(dest_file, basestring):
173+
dest_file = open(dest_file, 'w')
176174
connection = self.protocol_handler.Open(
177175
self.handle, destination='sync:',
178176
timeout_ms=timeout_ms)
@@ -192,7 +190,11 @@ def Stat(self, device_filename):
192190
return mode, size, mtime
193191

194192
def List(self, device_path):
195-
"""Return a directory listing of the given path."""
193+
"""Return a directory listing of the given path.
194+
195+
Args:
196+
device_path: Directory to list.
197+
"""
196198
connection = self.protocol_handler.Open(self.handle, destination='sync:')
197199
listing = self.filesync_handler.List(connection, device_path)
198200
connection.Close()
@@ -201,7 +203,8 @@ def List(self, device_path):
201203
def Reboot(self, destination=''):
202204
"""Reboot the device.
203205
204-
Specify 'bootloader' for fastboot.
206+
Args:
207+
destination: Specify 'bootloader' for fastboot.
205208
"""
206209
self.protocol_handler.Open(self.handle, 'reboot:%s' % destination)
207210

@@ -227,7 +230,7 @@ def StreamingShell(self, command, timeout_ms=None):
227230
"""Run command on the device, yielding each line of output.
228231
229232
Args:
230-
command: the command to run on the target.
233+
command: Command to run on the target.
231234
timeout_ms: Maximum time to allow the command to run.
232235
233236
Yields:
@@ -238,7 +241,11 @@ def StreamingShell(self, command, timeout_ms=None):
238241
timeout_ms=timeout_ms)
239242

240243
def Logcat(self, options, timeout_ms=None):
241-
"""Run 'shell logcat' and stream the output to stdout."""
244+
"""Run 'shell logcat' and stream the output to stdout.
245+
246+
Args:
247+
options: Arguments to pass to 'logcat'.
248+
"""
242249
return self.protocol_handler.StreamingCommand(
243250
self.handle, service='shell', command='logcat %s' % options,
244251
timeout_ms=timeout_ms)

adb/adb_debug.py

Lines changed: 143 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@
1212
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
15-
"""ADB debugging binary.
1615

17-
Call it similar to how you call android's adb. Takes either --serial or
18-
--port_path to connect to a device.
19-
"""
16+
"""Daemon-less ADB client in python."""
17+
18+
import argparse
19+
import logging
2020
import os
21+
import pipes
22+
import stat
2123
import sys
22-
23-
import gflags
24+
import time
2425

2526
import adb_commands
2627
import common_cli
@@ -36,34 +37,147 @@
3637
rsa_signer = None
3738

3839

39-
gflags.ADOPT_module_key_flags(common_cli)
40+
def Devices(args):
41+
"""Lists the available devices.
42+
43+
Mimics 'adb devices' output:
44+
List of devices attached
45+
015DB7591102001A device 1,2
46+
"""
47+
for d in adb_commands.AdbCommands.Devices():
48+
if args.output_port_path:
49+
print('%s\tdevice\t%s' % (
50+
d.serial_number, ','.join(str(p) for p in d.port_path)))
51+
else:
52+
print('%s\tdevice' % d.serial_number)
53+
return 0
54+
55+
56+
def List(self, device_path):
57+
"""Prints a directory listing.
58+
59+
Args:
60+
device_path: Directory to list.
61+
"""
62+
files = adb_commands.AdbCommands.List(self, device_path)
63+
files.sort(key=lambda x: x.filename)
64+
maxname = max(len(f.filename) for f in files)
65+
maxsize = max(len(str(f.size)) for f in files)
66+
for f in files:
67+
mode = (
68+
('d' if stat.S_ISDIR(f.mode) else '-') +
69+
('r' if f.mode & stat.S_IRUSR else '-') +
70+
('w' if f.mode & stat.S_IWUSR else '-') +
71+
('x' if f.mode & stat.S_IXUSR else '-') +
72+
('r' if f.mode & stat.S_IRGRP else '-') +
73+
('w' if f.mode & stat.S_IWGRP else '-') +
74+
('x' if f.mode & stat.S_IXGRP else '-') +
75+
('r' if f.mode & stat.S_IROTH else '-') +
76+
('w' if f.mode & stat.S_IWOTH else '-') +
77+
('x' if f.mode & stat.S_IXOTH else '-'))
78+
t = time.gmtime(f.mtime)
79+
yield '%s %*d %04d-%02d-%02d %02d:%02d:%02d %-*s\n' % (
80+
mode, maxsize, f.size,
81+
t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
82+
maxname, f.filename)
83+
84+
85+
def Logcat(self, *options):
86+
return adb_commands.AdbCommands.StreamingShell(
87+
self, 'logcat ' + ' '.join(pipes.quote(o) for o in options))
88+
89+
90+
Logcat.__doc__ = adb_commands.AdbCommands.Logcat.__doc__
91+
92+
93+
def Shell(self, *command):
94+
"""Runs a command on the device and prints the stdout.
95+
96+
Args:
97+
command: Command to run on the target.
98+
"""
99+
return adb_commands.AdbCommands.StreamingShell(
100+
self, ' '.join(pipes.quote(o) for o in command))
101+
102+
103+
def main():
104+
common = common_cli.GetCommonArguments()
105+
common.add_argument(
106+
'--rsa_key_path', action='append', default=[],
107+
metavar='~/.android/adbkey',
108+
help='RSA key(s) to use, use multiple times to load mulitple keys')
109+
common.add_argument(
110+
'--auth_timeout_s', default=60., metavar='60', type=int,
111+
help='Seconds to wait for the dialog to be accepted when using '
112+
'authenticated ADB.')
113+
device = common_cli.GetDeviceArguments()
114+
parents = [common, device]
115+
116+
parser = argparse.ArgumentParser(
117+
description=sys.modules[__name__].__doc__, parents=[common])
118+
subparsers = parser.add_subparsers(title='Commands', dest='command_name')
40119

41-
gflags.DEFINE_multistring('rsa_key_path', '~/.android/adbkey',
42-
'RSA key(s) to use')
43-
gflags.DEFINE_integer('auth_timeout_s', 60,
44-
'Seconds to wait for the dialog to be accepted when using '
45-
'authenticated ADB.')
46-
FLAGS = gflags.FLAGS
120+
subparser = subparsers.add_parser(
121+
name='help', help='Prints the commands available')
122+
subparser = subparsers.add_parser(
123+
name='devices', help='Lists the available devices', parents=[common])
124+
subparser.add_argument(
125+
'--output_port_path', action='store_true',
126+
help='Outputs the port_path alongside the serial')
47127

128+
subparser = common_cli.MakeSubparser(
129+
subparsers, parents, adb_commands.AdbCommands.Install)
130+
subparser = common_cli.MakeSubparser(subparsers, parents, List)
131+
subparser = common_cli.MakeSubparser(subparsers, parents, Logcat)
132+
subparser = common_cli.MakeSubparser(
133+
subparsers, parents, adb_commands.AdbCommands.Push,
134+
{'source_file': 'Filename or directory to push to the device.'})
135+
subparser = common_cli.MakeSubparser(
136+
subparsers, parents, adb_commands.AdbCommands.Pull,
137+
{
138+
'dest_file': 'Filename to write to on the host, if not specified, '
139+
'prints the content to stdout.',
140+
})
141+
subparser = common_cli.MakeSubparser(
142+
subparsers, parents, adb_commands.AdbCommands.Reboot)
143+
subparser = common_cli.MakeSubparser(
144+
subparsers, parents, adb_commands.AdbCommands.RebootBootloader)
145+
subparser = common_cli.MakeSubparser(
146+
subparsers, parents, adb_commands.AdbCommands.Remount)
147+
subparser = common_cli.MakeSubparser(
148+
subparsers, parents, adb_commands.AdbCommands.Root)
149+
subparser = common_cli.MakeSubparser(subparsers, parents, Shell)
48150

49-
def GetRSAKwargs():
50-
if FLAGS.rsa_key_path:
51-
if rsa_signer is None:
52-
print >> sys.stderr, 'Please install either M2Crypto or python-rsa'
53-
sys.exit(1)
54-
return {
55-
'rsa_keys': [rsa_signer(os.path.expanduser(path))
56-
for path in FLAGS.rsa_key_path],
57-
'auth_timeout_ms': int(FLAGS.auth_timeout_s * 1000.0),
58-
}
59-
return {}
151+
if len(sys.argv) == 1:
152+
parser.print_help()
153+
return 2
60154

155+
args = parser.parse_args()
156+
if args.verbose:
157+
logging.basicConfig(level=logging.DEBUG)
158+
if not args.rsa_key_path:
159+
default = os.path.expanduser('~/.android/adbkey')
160+
if os.path.isfile(default):
161+
args.rsa_key_path = [default]
162+
if args.rsa_key_path and not rsa_signer:
163+
parser.error('Please install either M2Crypto or python-rsa')
164+
# Hacks so that the generated doc is nicer.
165+
if args.command_name == 'devices':
166+
return Devices(args)
167+
if args.command_name == 'help':
168+
parser.print_help()
169+
return 0
170+
if args.command_name == 'logcat':
171+
args.positional = args.options
172+
elif args.command_name == 'shell':
173+
args.positional = args.command
61174

62-
def main(argv):
63-
common_cli.StartCli(
64-
argv, adb_commands.AdbCommands.ConnectDevice,
65-
list_callback=adb_commands.AdbCommands.Devices, **GetRSAKwargs())
175+
return common_cli.StartCli(
176+
args,
177+
adb_commands.AdbCommands.ConnectDevice,
178+
auth_timeout_ms=args.auth_timeout_s * 1000,
179+
rsa_keys=[rsa_signer(path) for path in args.rsa_key_path])
66180

67181

68182
if __name__ == '__main__':
69-
main(FLAGS(sys.argv))
183+
sys.exit(main())

0 commit comments

Comments
 (0)