web3.py/web3/providers/ipc.py
2017-01-10 10:08:41 -07:00

114 lines
2.9 KiB
Python

from __future__ import absolute_import
import sys
import os
import contextlib
import json
try:
from json import JSONDecodeError
except ImportError:
JSONDecodeError = ValueError
from web3.utils.compat import (
Timeout,
threading,
socket,
)
from web3.utils.string import (
force_text,
)
from .base import JSONBaseProvider
@contextlib.contextmanager
def get_ipc_socket(ipc_path, timeout=0.1):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(ipc_path)
sock.settimeout(timeout)
yield sock
sock.close()
def get_default_ipc_path(testnet=False):
if testnet:
testnet = "testnet"
else:
testnet = ""
if sys.platform == 'darwin':
return os.path.expanduser(os.path.join(
"~",
"Library",
"Ethereum",
testnet,
"geth.ipc",
))
elif sys.platform.startswith('linux'):
return os.path.expanduser(os.path.join(
"~",
".ethereum",
testnet,
"geth.ipc",
))
elif sys.platform == 'win32':
return os.path.expanduser(os.path.join(
"~",
"AppData",
"Roaming",
"Ethereum",
))
else:
raise ValueError(
"Unsupported platform '{0}'. Only darwin/linux2/win32 are "
"supported. You must specify the ipc_path".format(sys.platform)
)
class IPCProvider(JSONBaseProvider):
def __init__(self, ipc_path=None, testnet=False, *args, **kwargs):
if ipc_path is None:
self.ipc_path = get_default_ipc_path(testnet)
else:
self.ipc_path = ipc_path
self._lock = threading.Lock()
super(IPCProvider, self).__init__(*args, **kwargs)
def make_request(self, method, params):
request = self.encode_rpc_request(method, params)
self._lock.acquire()
try:
with get_ipc_socket(self.ipc_path) as sock:
sock.sendall(request)
# TODO: use a BytesIO object here
response_raw = b""
with Timeout(10) as timeout:
while True:
try:
response_raw += sock.recv(4096)
except socket.timeout:
timeout.sleep(0)
continue
if response_raw == b"":
timeout.sleep(0)
else:
try:
json.loads(force_text(response_raw))
except JSONDecodeError:
timeout.sleep(0)
continue
else:
break
finally:
self._lock.release()
return response_raw