1from _pydevd_bundle.pydevd_constants import DebugInfoHolder, IS_PY2, \ 2 get_global_debugger, GetGlobalDebugger, set_global_debugger # Keep for backward compatibility @UnusedImport 3from _pydevd_bundle.pydevd_utils import quote_smart as quote, to_string 4from _pydevd_bundle.pydevd_comm_constants import ID_TO_MEANING, CMD_EXIT 5from _pydevd_bundle.pydevd_constants import HTTP_PROTOCOL, HTTP_JSON_PROTOCOL, \ 6 get_protocol, IS_JYTHON, ForkSafeLock 7import json 8from _pydev_bundle import pydev_log 9 10 11class _BaseNetCommand(object): 12 13 # Command id. Should be set in instance. 14 id = -1 15 16 # Dict representation of the command to be set in instance. Only set for json commands. 17 as_dict = None 18 19 def send(self, *args, **kwargs): 20 pass 21 22 23class _NullNetCommand(_BaseNetCommand): 24 pass 25 26 27class _NullExitCommand(_NullNetCommand): 28 29 id = CMD_EXIT 30 31 32# Constant meant to be passed to the writer when the command is meant to be ignored. 33NULL_NET_COMMAND = _NullNetCommand() 34 35# Exit command -- only internal (we don't want/need to send this to the IDE). 36NULL_EXIT_COMMAND = _NullExitCommand() 37 38 39class NetCommand(_BaseNetCommand): 40 """ 41 Commands received/sent over the network. 42 43 Command can represent command received from the debugger, 44 or one to be sent by daemon. 45 """ 46 next_seq = 0 # sequence numbers 47 48 _showing_debug_info = 0 49 _show_debug_info_lock = ForkSafeLock(rlock=True) 50 51 def __init__(self, cmd_id, seq, text, is_json=False): 52 """ 53 If sequence is 0, new sequence will be generated (otherwise, this was the response 54 to a command from the client). 55 """ 56 protocol = get_protocol() 57 self.id = cmd_id 58 if seq == 0: 59 NetCommand.next_seq += 2 60 seq = NetCommand.next_seq 61 62 self.seq = seq 63 64 if is_json: 65 if hasattr(text, 'to_dict'): 66 as_dict = text.to_dict(update_ids_to_dap=True) 67 else: 68 assert isinstance(text, dict) 69 as_dict = text 70 as_dict['pydevd_cmd_id'] = cmd_id 71 as_dict['seq'] = seq 72 self.as_dict = as_dict 73 text = json.dumps(as_dict) 74 75 if IS_PY2: 76 if isinstance(text, unicode): 77 text = text.encode('utf-8') 78 else: 79 assert isinstance(text, str) 80 else: 81 assert isinstance(text, str) 82 83 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1: 84 self._show_debug_info(cmd_id, seq, text) 85 86 if is_json: 87 msg = text 88 else: 89 if protocol not in (HTTP_PROTOCOL, HTTP_JSON_PROTOCOL): 90 encoded = quote(to_string(text), '/<>_=" \t') 91 msg = '%s\t%s\t%s\n' % (cmd_id, seq, encoded) 92 93 else: 94 msg = '%s\t%s\t%s' % (cmd_id, seq, text) 95 96 if IS_PY2: 97 assert isinstance(msg, str) # i.e.: bytes 98 as_bytes = msg 99 else: 100 if isinstance(msg, str): 101 msg = msg.encode('utf-8') 102 103 assert isinstance(msg, bytes) 104 as_bytes = msg 105 self._as_bytes = as_bytes 106 107 def send(self, sock): 108 as_bytes = self._as_bytes 109 try: 110 if get_protocol() in (HTTP_PROTOCOL, HTTP_JSON_PROTOCOL): 111 sock.sendall(('Content-Length: %s\r\n\r\n' % len(as_bytes)).encode('ascii')) 112 sock.sendall(as_bytes) 113 except: 114 if IS_JYTHON: 115 # Ignore errors in sock.sendall in Jython (seems to be common for Jython to 116 # give spurious exceptions at interpreter shutdown here). 117 pass 118 else: 119 raise 120 121 @classmethod 122 def _show_debug_info(cls, cmd_id, seq, text): 123 with cls._show_debug_info_lock: 124 # Only one thread each time (rlock). 125 if cls._showing_debug_info: 126 # avoid recursing in the same thread (just printing could create 127 # a new command when redirecting output). 128 return 129 130 cls._showing_debug_info += 1 131 try: 132 out_message = 'sending cmd (%s) --> ' % (get_protocol(),) 133 out_message += "%20s" % ID_TO_MEANING.get(str(cmd_id), 'UNKNOWN') 134 out_message += ' ' 135 out_message += text.replace('\n', ' ') 136 try: 137 pydev_log.critical('%s\n', out_message) 138 except: 139 pass 140 finally: 141 cls._showing_debug_info -= 1 142 143