Skip to content

Commit

Permalink
Merge ce90927 into f42d4a3
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffLIrion committed May 7, 2020
2 parents f42d4a3 + ce90927 commit c09ae8b
Show file tree
Hide file tree
Showing 16 changed files with 452 additions and 336 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
@@ -1,11 +1,11 @@
language: python
python:
- "2.7"
- "3.6"
- "3.7"
- "3.8"
install:
- pip install .
- pip install flake8 pylint coveralls cryptography
- python --version 2>&1 | grep -q "Python 2" && pip install mock || true
script:
- flake8 adb_shell/ && pylint adb_shell/ && coverage run --source adb_shell setup.py test && coverage report -m
after_success:
Expand Down
8 changes: 4 additions & 4 deletions README.rst
Expand Up @@ -32,15 +32,15 @@ Example Usage
# Connect (no authentication necessary)
device1 = AdbDeviceTcp('192.168.0.111', 5555, default_timeout_s=9.)
device1.connect(auth_timeout_s=0.1)
await device1.connect(auth_timeout_s=0.1)
# Connect (authentication required)
with open('path/to/adbkey') as f:
priv = f.read()
signer = PythonRSASigner('', priv)
device2 = AdbDeviceTcp('192.168.0.222', 5555, default_timeout_s=9.)
device2.connect(rsa_keys=[signer], auth_timeout_s=0.1)
await device2.connect(rsa_keys=[signer], auth_timeout_s=0.1)
# Send a shell command
response1 = device1.shell('echo TEST1')
response2 = device2.shell('echo TEST2')
response1 = await device1.shell('echo TEST1')
response2 = await device2.shell('echo TEST2')
181 changes: 91 additions & 90 deletions adb_shell/adb_device.py

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions adb_shell/handle/base_handle.py
Expand Up @@ -32,13 +32,13 @@ class BaseHandle(ABC):
"""

@abstractmethod
def close(self):
async def close(self):
"""Close the connection.
"""

@abstractmethod
def connect(self, timeout_s=None):
async def connect(self, timeout_s=None):
"""Create a connection to the device.
Parameters
Expand All @@ -49,7 +49,7 @@ def connect(self, timeout_s=None):
"""

@abstractmethod
def bulk_read(self, numbytes, timeout_s=None):
async def bulk_read(self, numbytes, timeout_s=None):
"""Read data from the device.
Parameters
Expand All @@ -67,7 +67,7 @@ def bulk_read(self, numbytes, timeout_s=None):
"""

@abstractmethod
def bulk_write(self, data, timeout_s=None):
async def bulk_write(self, data, timeout_s=None):
"""Send data to the device.
Parameters
Expand Down
62 changes: 34 additions & 28 deletions adb_shell/handle/tcp_handle.py
Expand Up @@ -30,8 +30,7 @@
"""


import select
import socket
import asyncio

from .base_handle import BaseHandle
from ..exceptions import TcpTimeoutException
Expand All @@ -51,37 +50,41 @@ class TcpHandle(BaseHandle):
Attributes
----------
_connection : socket.socket, None
A socket connection to the device
_default_timeout_s : float, None
Default timeout in seconds for TCP packets, or ``None``
_host : str
The address of the device; may be an IP address or a host name
_port : int
The device port to which we are connecting (default is 5555)
_reader : StreamReader, None
TODO
_writer : StreamWriter, None
TODO
"""
def __init__(self, host, port=5555, default_timeout_s=None):
self._host = host
self._port = port
self._default_timeout_s = default_timeout_s

self._connection = None
self._reader = None
self._writer = None

def close(self):
async def close(self):
"""Close the socket connection.
"""
if self._connection:
if self._writer:
try:
self._connection.shutdown(socket.SHUT_RDWR)
self._writer.close()
await self._writer.wait_closed()
except OSError:
pass

self._connection.close()
self._connection = None
self._reader = None
self._writer = None

def connect(self, timeout_s=None):
async def connect(self, timeout_s=None):
"""Create a socket connection to the device.
Parameters
Expand All @@ -91,13 +94,14 @@ def connect(self, timeout_s=None):
"""
timeout = self._default_timeout_s if timeout_s is None else timeout_s
self._connection = socket.create_connection((self._host, self._port), timeout=timeout)
if timeout:
# Put the socket in non-blocking mode
# https://docs.python.org/3/library/socket.html#socket.socket.settimeout
self._connection.setblocking(0)

def bulk_read(self, numbytes, timeout_s=None):
try:
self._reader, self._writer = await asyncio.wait_for(asyncio.open_connection(self._host, self._port), timeout)
except asyncio.TimeoutError:
msg = 'Connecting to {}:{} timed out ({} seconds)'.format(self._host, self._port, timeout)
raise TcpTimeoutException(msg)

async def bulk_read(self, numbytes, timeout_s=None):
"""Receive data from the socket.
Parameters
Expand All @@ -119,14 +123,14 @@ def bulk_read(self, numbytes, timeout_s=None):
"""
timeout = self._default_timeout_s if timeout_s is None else timeout_s
readable, _, _ = select.select([self._connection], [], [], timeout)
if readable:
return self._connection.recv(numbytes)

msg = 'Reading from {}:{} timed out ({} seconds)'.format(self._host, self._port, timeout)
raise TcpTimeoutException(msg)
try:
return await asyncio.wait_for(self._reader.read(numbytes), timeout)
except asyncio.TimeoutError:
msg = 'Reading from {}:{} timed out ({} seconds)'.format(self._host, self._port, timeout)
raise TcpTimeoutException(msg)

def bulk_write(self, data, timeout_s=None):
async def bulk_write(self, data, timeout_s=None):
"""Send data to the socket.
Parameters
Expand All @@ -148,9 +152,11 @@ def bulk_write(self, data, timeout_s=None):
"""
timeout = self._default_timeout_s if timeout_s is None else timeout_s
_, writeable, _ = select.select([], [self._connection], [], timeout)
if writeable:
return self._connection.send(data)

msg = 'Sending data to {}:{} timed out after {} seconds. No data was sent.'.format(self._host, self._port, timeout)
raise TcpTimeoutException(msg)
try:
self._writer.write(data)
await asyncio.wait_for(self._writer.drain(), timeout)
return len(data)
except asyncio.TimeoutError:
msg = 'Sending data to {}:{} timed out after {} seconds. No data was sent.'.format(self._host, self._port, timeout)
raise TcpTimeoutException(msg)
4 changes: 2 additions & 2 deletions setup.py
Expand Up @@ -15,9 +15,9 @@
packages=['adb_shell', 'adb_shell.auth', 'adb_shell.handle'],
install_requires=['cryptography', 'pyasn1', 'rsa'],
tests_require=['pycryptodome'],
python_requires='>=3.6',
classifiers=['Operating System :: OS Independent',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 2'],
'Programming Language :: Python :: 3'],
test_suite='tests'
)
12 changes: 12 additions & 0 deletions tests/async_wrapper.py
@@ -0,0 +1,12 @@
import asyncio


def _await(coro):
return asyncio.get_event_loop().run_until_complete(coro)


def awaiter(func):
def sync_func(*args, **kwargs):
return _await(func(*args, **kwargs))

return sync_func
2 changes: 1 addition & 1 deletion tests/keygen_stub.py
@@ -1,5 +1,5 @@
from contextlib import contextmanager
from mock import patch
from unittest.mock import patch


class FileReadWrite(object):
Expand Down
16 changes: 9 additions & 7 deletions tests/patchers.py
@@ -1,4 +1,4 @@
from mock import patch
from unittest.mock import patch

from adb_shell import constants
from adb_shell.adb_message import AdbMessage, unpack
Expand Down Expand Up @@ -46,19 +46,21 @@ def __init__(self, *args, **kwargs):
self._bulk_read = b''
self._bulk_write = b''

def close(self):
self._connection = None
async def close(self):
self._reader = None
self._writer = None

def connect(self, auth_timeout_s=None):
self._connection = True
async def connect(self, auth_timeout_s=None):
self._reader = True
self._writer = True

def bulk_read(self, numbytes, timeout_s=None):
async def bulk_read(self, numbytes, timeout_s=None):
num = min(numbytes, constants.MAX_ADB_DATA)
ret = self._bulk_read[:num]
self._bulk_read = self._bulk_read[num:]
return ret

def bulk_write(self, data, timeout_s=None):
async def bulk_write(self, data, timeout_s=None):
self._bulk_write += data
return len(data)

Expand Down

0 comments on commit c09ae8b

Please sign in to comment.