Skip to content

Commit 48396ce

Browse files
committed
Add type hints
1 parent 2c87157 commit 48396ce

File tree

5 files changed

+54
-41
lines changed

5 files changed

+54
-41
lines changed

dopple/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from .dopple import (
2-
main
3-
)
1+
from .dopple import ( # noqa: F401
2+
main,
3+
)

dopple/dopple.py

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,24 @@
1818
"""
1919

2020
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
21-
from http.server import HTTPServer, BaseHTTPRequestHandler
22-
from os import path
23-
from urllib.parse import urlparse
24-
2521
import errno
2622
import pkg_resources
23+
from http.server import HTTPServer, BaseHTTPRequestHandler
24+
from os import path
2725
import socket
2826
import sys
2927
import time
3028
import threading
29+
from typing import Any, Optional # noqa: F401
30+
from urllib.parse import urlparse
31+
3132

3233
if sys.platform == 'win32':
3334
import win32file
3435
import pywintypes
36+
else:
37+
win32file: Any = None
38+
pywintypes: Any = None
3539

3640
try:
3741
VERSION = pkg_resources.get_distribution("dopple").version
@@ -40,14 +44,16 @@
4044

4145
BUFSIZE = 32
4246
DELIMITER = ord('\n')
43-
BACKEND_CONNECTION_TIMEOUT=30.0
47+
BACKEND_CONNECTION_TIMEOUT = 30.0
4448
INFO = """Dopple JSON-RPC Proxy
4549
4650
Version: {version}
4751
Proxy: {proxy_url}
4852
Backend: {backend_url} (connected: {connected})
4953
"""
5054

55+
SocketAlias = socket.socket
56+
5157

5258
class BackendError(Exception):
5359
pass
@@ -56,19 +62,19 @@ class BackendError(Exception):
5662
class UnixSocketConnector(object):
5763
"""Unix Domain Socket connector. Connects to socket lazily."""
5864

59-
def __init__(self, socket_path):
65+
def __init__(self, socket_path: str) -> None:
6066
self._socket_path = socket_path
61-
self._socket = None
67+
self._socket: Optional[SocketAlias] = None
6268

6369
@staticmethod
64-
def _get_error_message(os_error_number):
70+
def _get_error_message(os_error_number: int) -> str:
6571
if os_error_number == errno.ENOENT:
6672
return "Unix Domain Socket '{}' does not exist"
6773
if os_error_number == errno.ECONNREFUSED:
6874
return "Connection to '{}' refused"
6975
return "Unknown error when connecting to '{}'"
7076

71-
def socket(self):
77+
def socket(self) -> SocketAlias:
7278
"""Returns connected socket."""
7379
if self._socket is None:
7480
try:
@@ -83,16 +89,16 @@ def socket(self):
8389
raise err from ex
8490
return self._socket
8591

86-
def close(self):
92+
def close(self) -> None:
8793
if self._socket is not None:
8894
self._socket.shutdown(socket.SHUT_RDWR)
8995
self._socket.close()
9096
self._socket = None
9197

92-
def is_connected(self):
98+
def is_connected(self) -> bool:
9399
return self._socket is not None
94100

95-
def check_connection(self, timeout):
101+
def check_connection(self, timeout: float) -> None:
96102
SLEEPTIME = 0.1
97103
wait_time = 0.0
98104
last_exception = None
@@ -106,12 +112,15 @@ def check_connection(self, timeout):
106112
time.sleep(SLEEPTIME)
107113
wait_time += SLEEPTIME
108114
if wait_time > timeout:
109-
raise last_exception if last_exception else TimeoutError
115+
if last_exception is not None:
116+
raise last_exception
117+
else:
118+
raise TimeoutError()
110119

111-
def recv(self, max_length):
120+
def recv(self, max_length: int) -> bytes:
112121
return self.socket().recv(max_length)
113122

114-
def sendall(self, data):
123+
def sendall(self, data: bytes) -> None:
115124
try:
116125
return self.socket().sendall(data)
117126
except OSError as ex:
@@ -126,42 +135,44 @@ def sendall(self, data):
126135
class NamedPipeConnector(object):
127136
"""Windows named pipe simulating socket."""
128137

129-
def __init__(self, ipc_path):
138+
def __init__(self, ipc_path: str) -> None:
130139
try:
131140
self.handle = win32file.CreateFile(
132141
ipc_path, win32file.GENERIC_READ | win32file.GENERIC_WRITE,
133142
0, None, win32file.OPEN_EXISTING, 0, None)
134143
except pywintypes.error as err:
135144
raise IOError(err)
136145

137-
def is_connected(self):
146+
def is_connected(self) -> bool:
138147
return True
139148

140-
def check_connection(self, timeout):
149+
def check_connection(self, timeout: float) -> None:
141150
pass
142151

143-
def recv(self, max_length):
152+
def recv(self, max_length: int) -> Any:
144153
(err, data) = win32file.ReadFile(self.handle, max_length)
145154
if err:
146155
raise IOError(err)
147156
return data
148157

149-
def sendall(self, data):
158+
def sendall(self, data: bytes) -> 'win32file.WriteFile':
150159
return win32file.WriteFile(self.handle, data)
151160

152-
def close(self):
161+
def close(self) -> None:
153162
self.handle.close()
154163

155164

156-
def get_ipc_connector(ipc_path):
165+
def get_ipc_connector(ipc_path: str) -> Any:
157166
if sys.platform == 'win32':
158167
return NamedPipeConnector(ipc_path)
159168
return UnixSocketConnector(ipc_path)
160169

161170

162171
class HTTPRequestHandler(BaseHTTPRequestHandler):
163172

164-
def do_GET(self):
173+
server: 'Proxy'
174+
175+
def do_GET(self) -> None:
165176
if self.path != '/':
166177
self.send_response(404)
167178
self.end_headers()
@@ -179,14 +190,14 @@ def do_GET(self):
179190
connected=self.server.conn.is_connected())
180191
self.wfile.write(info.encode('utf-8'))
181192

182-
def do_OPTIONS(self):
193+
def do_OPTIONS(self) -> None:
183194
self.send_response(200)
184195
self.send_header("Content-type", "text/plain")
185196
self.addCORS()
186197
self.end_headers()
187198

188-
def do_POST(self):
189-
request_length = int(self.headers['Content-Length'])
199+
def do_POST(self) -> None:
200+
request_length = int(self.headers['Content-Length']) # type: ignore
190201
request_content = self.rfile.read(request_length)
191202
# self.log_message("Headers: {}".format(self.headers))
192203
# self.log_message("Request: {}".format(request_content))
@@ -197,7 +208,7 @@ def do_POST(self):
197208

198209
self.send_response(200)
199210
self.send_header("Content-type", "application/json")
200-
self.send_header("Content-length", len(response_content))
211+
self.send_header("Content-length", str(len(response_content)))
201212
self.addCORS()
202213
self.end_headers()
203214
self.wfile.write(response_content)
@@ -206,22 +217,22 @@ def do_POST(self):
206217
error_msg = str(err).encode('utf-8')
207218
# TODO: Send as JSON-RPC response
208219
self.send_header("Content-type", "text/plain")
209-
self.send_header("Content-length", len(error_msg))
220+
self.send_header("Content-length", str(len(error_msg)))
210221
self.end_headers()
211222
self.wfile.write(error_msg)
212223
self.log_message("Backend Error: {}".format(err))
213224

214225
# TODO: Handle other exceptions as error 500.
215226

216-
def addCORS(self):
227+
def addCORS(self) -> None:
217228
self.send_header("Access-Control-Allow-Origin", "*")
218229
self.send_header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
219230
self.send_header("Access-Control-Allow-Headers", "content-type")
220231

221232

222233
class Proxy(HTTPServer):
223234

224-
def __init__(self, proxy_url, backend_path):
235+
def __init__(self, proxy_url: str, backend_path: str) -> None:
225236
self.proxy_url = proxy_url
226237
url = urlparse(proxy_url)
227238
assert url.scheme == 'http'
@@ -231,7 +242,7 @@ def __init__(self, proxy_url, backend_path):
231242

232243
self.backend_address = path.expanduser(backend_path)
233244

234-
def process(self, request):
245+
def process(self, request: Any) -> bytes:
235246
self.conn.sendall(request)
236247

237248
response = b''
@@ -246,7 +257,7 @@ def process(self, request):
246257

247258
return response
248259

249-
def run(self):
260+
def run(self) -> None:
250261
self.conn = get_ipc_connector(self.backend_address)
251262
self.conn.check_connection(timeout=BACKEND_CONNECTION_TIMEOUT)
252263

@@ -266,7 +277,7 @@ def run(self):
266277
PROXY_URL_HELP = "URL for this proxy server"
267278

268279

269-
def parse_args():
280+
def parse_args() -> Any:
270281
parser = ArgumentParser(
271282
description='Dopple HTTP Proxy for JSON-RPC servers',
272283
formatter_class=ArgumentDefaultsHelpFormatter
@@ -281,25 +292,26 @@ def parse_args():
281292
return parser.parse_args()
282293

283294

284-
def run(proxy_url=DEFAULT_PROXY_URL, backend_path=DEFAULT_BACKEND_PATH):
295+
def run(proxy_url: str=DEFAULT_PROXY_URL, backend_path: str=DEFAULT_BACKEND_PATH) -> None:
285296
proxy = Proxy(proxy_url, backend_path)
286297
try:
287298
proxy.run()
288299
except KeyboardInterrupt:
289300
proxy.shutdown()
290301

291302

292-
def run_daemon(proxy_url=DEFAULT_PROXY_URL, backend_path=DEFAULT_BACKEND_PATH):
303+
def run_daemon(proxy_url: str=DEFAULT_PROXY_URL, backend_path: str=DEFAULT_BACKEND_PATH) -> Proxy:
293304
proxy = Proxy(proxy_url, backend_path)
294305
th = threading.Thread(name='dopple', target=proxy.run)
295306
th.daemon = True
296307
th.start()
297308
return proxy
298309

299-
def main():
310+
311+
def main() -> None:
300312
args = parse_args()
301313
run(args.proxy_url, args.backend_path)
302314

315+
303316
if __name__ == '__main__':
304317
main()
305-

dopple/py.typed

Whitespace-only changes.

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
zip_safe=False,
6363
keywords='ethereum',
6464
packages=find_packages(exclude=["tests", "tests.*"]),
65+
package_data={'dopple': ['py.typed']},
6566
classifiers=[
6667
'Development Status :: 3 - Alpha',
6768
'Intended Audience :: Developers',

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ whitelist_externals=make
3838
basepython=python
3939
extras=lint
4040
commands=
41-
mypy -p {toxinidir}/dopple --config-file {toxinidir}/mypy.ini
41+
mypy -p dopple --config-file {toxinidir}/mypy.ini
4242
flake8 {toxinidir}/dopple {toxinidir}/tests
4343
isort --recursive --check-only --diff {toxinidir}/dopple {toxinidir}/tests
4444
pydocstyle {toxinidir}/dopple {toxinidir}/tests

0 commit comments

Comments
 (0)