File: C:/Users/fred/anaconda3/Lib/site-packages/spyder/utils/bsdsocket.py
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""BSD socket interface communication utilities"""
# Be extra careful here. The interface is used to communicate with subprocesses
# by redirecting output streams through a socket. Any exception in this module
# and failure to read out buffers will most likely lock up Spyder.
import os
import pickle
import socket
import struct
import threading
import errno
import traceback
# Local imports
from spyder.config.base import get_debug_level, STDERR
DEBUG_EDITOR = get_debug_level() >= 3
PICKLE_HIGHEST_PROTOCOL = 2
def temp_fail_retry(error, fun, *args):
"""Retry to execute function, ignoring EINTR error (interruptions)"""
while 1:
try:
return fun(*args)
except error as e:
eintr = errno.WSAEINTR if os.name == 'nt' else errno.EINTR
if e.args[0] == eintr:
continue
raise
SZ = struct.calcsize("l")
def write_packet(sock, data, already_pickled=False):
"""Write *data* to socket *sock*"""
if already_pickled:
sent_data = data
else:
sent_data = pickle.dumps(data, PICKLE_HIGHEST_PROTOCOL)
sent_data = struct.pack("l", len(sent_data)) + sent_data
nsend = len(sent_data)
while nsend > 0:
nsend -= temp_fail_retry(socket.error, sock.send, sent_data)
def read_packet(sock, timeout=None):
"""
Read data from socket *sock*
Returns None if something went wrong
"""
sock.settimeout(timeout)
dlen, data = None, None
try:
if os.name == 'nt':
# Windows implementation
datalen = sock.recv(SZ)
dlen, = struct.unpack("l", datalen)
data = b''
while len(data) < dlen:
data += sock.recv(dlen)
else:
# Linux/MacOSX implementation
# Thanks to eborisch:
# See spyder-ide/spyder#1106.
datalen = temp_fail_retry(socket.error, sock.recv,
SZ, socket.MSG_WAITALL)
if len(datalen) == SZ:
dlen, = struct.unpack("l", datalen)
data = temp_fail_retry(socket.error, sock.recv,
dlen, socket.MSG_WAITALL)
except socket.timeout:
raise
except socket.error:
data = None
finally:
sock.settimeout(None)
if data is not None:
try:
return pickle.loads(data)
except Exception:
# Catch all exceptions to avoid locking spyder
if DEBUG_EDITOR:
traceback.print_exc(file=STDERR)
return
# Using a lock object to avoid communication issues described in
# spyder-ide/spyder#857.
COMMUNICATE_LOCK = threading.Lock()
# * Old com implementation *
# See solution (1) in spyder-ide/spyder#434, comment 13:
def communicate(sock, command, settings=[]):
"""Communicate with monitor"""
try:
COMMUNICATE_LOCK.acquire()
write_packet(sock, command)
for option in settings:
write_packet(sock, option)
return read_packet(sock)
finally:
COMMUNICATE_LOCK.release()
# new com implementation:
# See solution (2) in spyder-ide/spyder#434, comment 13:
#def communicate(sock, command, settings=[], timeout=None):
# """Communicate with monitor"""
# write_packet(sock, command)
# for option in settings:
# write_packet(sock, option)
# if timeout == 0.:
# # non blocking socket is not really supported:
# # setting timeout to 0. here is equivalent (in current monitor's
# # implementation) to say 'I don't need to receive anything in return'
# return
# while True:
# output = read_packet(sock, timeout=timeout)
# if output is None:
# return
# output_command, output_data = output
# if command == output_command:
# return output_data
# elif DEBUG:
# logging.debug("###### communicate/warning /Begin ######")
# logging.debug("was expecting '%s', received '%s'" \
# % (command, output_command))
# logging.debug("###### communicate/warning /End ######")
class PacketNotReceived(object):
pass
PACKET_NOT_RECEIVED = PacketNotReceived()
if __name__ == '__main__':
if not os.name == 'nt':
# socket read/write testing - client and server in one thread
# (techtonik): the stuff below is placed into public domain
print("-- Testing standard Python socket interface --") # spyder: test-skip
address = ("127.0.0.1", 9999)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind( address )
server.listen(2)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect( address )
client.send("data to be catched".encode('utf-8'))
# accepted server socket is the one we can read from
# note that it is different from server socket
accsock, addr = server.accept()
print('..got "%s" from %s' % (accsock.recv(4096), addr)) # spyder: test-skip
# accsock.close()
# client.send("more data for recv")
#socket.error: [Errno 9] Bad file descriptor
# accsock, addr = server.accept()
#socket.error: [Errno 11] Resource temporarily unavailable
print("-- Testing BSD socket write_packet/read_packet --") # spyder: test-skip
write_packet(client, "a tiny piece of data")
print('..got "%s" from read_packet()' % (read_packet(accsock))) # spyder: test-skip
client.close()
server.close()
print("-- Done.") # spyder: test-skip