1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
import sys
import threading
import traceback
import warnings
from _pydev_bundle._pydev_filesystem_encoding import getfilesystemencoding
from _pydev_bundle.pydev_imports import _queue, xmlrpclib
from _pydevd_bundle.pydevd_constants import Null
Queue = _queue.Queue
# This may happen in IronPython (in Python it shouldn't happen as there are
# 'fast' replacements that are used in xmlrpclib.py)
warnings.filterwarnings("ignore", "The xmllib module is obsolete.*", DeprecationWarning)
file_system_encoding = getfilesystemencoding()
# =======================================================================================================================
# _ServerHolder
# =======================================================================================================================
class _ServerHolder:
"""
Helper so that we don't have to use a global here.
"""
SERVER = None
# =======================================================================================================================
# set_server
# =======================================================================================================================
def set_server(server):
_ServerHolder.SERVER = server
# =======================================================================================================================
# ParallelNotification
# =======================================================================================================================
class ParallelNotification(object):
def __init__(self, method, args):
self.method = method
self.args = args
def to_tuple(self):
return self.method, self.args
# =======================================================================================================================
# KillServer
# =======================================================================================================================
class KillServer(object):
pass
# =======================================================================================================================
# ServerFacade
# =======================================================================================================================
class ServerFacade(object):
def __init__(self, notifications_queue):
self.notifications_queue = notifications_queue
def notifyTestsCollected(self, *args):
self.notifications_queue.put_nowait(ParallelNotification("notifyTestsCollected", args))
def notifyConnected(self, *args):
self.notifications_queue.put_nowait(ParallelNotification("notifyConnected", args))
def notifyTestRunFinished(self, *args):
self.notifications_queue.put_nowait(ParallelNotification("notifyTestRunFinished", args))
def notifyStartTest(self, *args):
self.notifications_queue.put_nowait(ParallelNotification("notifyStartTest", args))
def notifyTest(self, *args):
new_args = []
for arg in args:
new_args.append(_encode_if_needed(arg))
args = tuple(new_args)
self.notifications_queue.put_nowait(ParallelNotification("notifyTest", args))
# =======================================================================================================================
# ServerComm
# =======================================================================================================================
class ServerComm(threading.Thread):
def __init__(self, notifications_queue, port, daemon=False):
# If daemon is False, wait for all the notifications to be passed before exiting!
threading.Thread.__init__(self, daemon=daemon)
self.finished = False
self.notifications_queue = notifications_queue
from _pydev_bundle import pydev_localhost
# It is necessary to specify an encoding, that matches
# the encoding of all bytes-strings passed into an
# XMLRPC call: "All 8-bit strings in the data structure are assumed to use the
# packet encoding. Unicode strings are automatically converted,
# where necessary."
# Byte strings most likely come from file names.
encoding = file_system_encoding
if encoding == "mbcs":
# Windos symbolic name for the system encoding CP_ACP.
# We need to convert it into a encoding that is recognized by Java.
# Unfortunately this is not always possible. You could use
# GetCPInfoEx and get a name similar to "windows-1251". Then
# you need a table to translate on a best effort basis. Much to complicated.
# ISO-8859-1 is good enough.
encoding = "ISO-8859-1"
self.server = xmlrpclib.Server("http://%s:%s" % (pydev_localhost.get_localhost(), port), encoding=encoding)
def run(self):
while True:
kill_found = False
commands = []
command = self.notifications_queue.get(block=True)
if isinstance(command, KillServer):
kill_found = True
else:
assert isinstance(command, ParallelNotification)
commands.append(command.to_tuple())
try:
while True:
command = self.notifications_queue.get(block=False) # No block to create a batch.
if isinstance(command, KillServer):
kill_found = True
else:
assert isinstance(command, ParallelNotification)
commands.append(command.to_tuple())
except:
pass # That's OK, we're getting it until it becomes empty so that we notify multiple at once.
if commands:
try:
self.server.notifyCommands(commands)
except:
traceback.print_exc()
if kill_found:
self.finished = True
return
# =======================================================================================================================
# initialize_server
# =======================================================================================================================
def initialize_server(port, daemon=False):
if _ServerHolder.SERVER is None:
if port is not None:
notifications_queue = Queue()
_ServerHolder.SERVER = ServerFacade(notifications_queue)
_ServerHolder.SERVER_COMM = ServerComm(notifications_queue, port, daemon)
_ServerHolder.SERVER_COMM.start()
else:
# Create a null server, so that we keep the interface even without any connection.
_ServerHolder.SERVER = Null()
_ServerHolder.SERVER_COMM = Null()
try:
if _ServerHolder.SERVER is not None:
_ServerHolder.SERVER.notifyConnected()
except:
traceback.print_exc()
# =======================================================================================================================
# notifyTest
# =======================================================================================================================
def notifyTestsCollected(tests_count):
assert tests_count is not None
try:
if _ServerHolder.SERVER is not None:
_ServerHolder.SERVER.notifyTestsCollected(tests_count)
except:
traceback.print_exc()
# =======================================================================================================================
# notifyStartTest
# =======================================================================================================================
def notifyStartTest(file, test):
"""
@param file: the tests file (c:/temp/test.py)
@param test: the test ran (i.e.: TestCase.test1)
"""
assert file is not None
if test is None:
test = "" # Could happen if we have an import error importing module.
try:
if _ServerHolder.SERVER is not None:
_ServerHolder.SERVER.notifyStartTest(file, test)
except:
traceback.print_exc()
def _encode_if_needed(obj):
# In the java side we expect strings to be ISO-8859-1 (org.python.pydev.debug.pyunit.PyUnitServer.initializeDispatches().new Dispatch() {...}.getAsStr(Object))
if isinstance(obj, str): # Unicode in py3
return xmlrpclib.Binary(obj.encode("ISO-8859-1", "xmlcharrefreplace"))
elif isinstance(obj, bytes):
try:
return xmlrpclib.Binary(obj.decode(sys.stdin.encoding, "replace").encode("ISO-8859-1", "xmlcharrefreplace"))
except:
return xmlrpclib.Binary(obj) # bytes already
return obj
# =======================================================================================================================
# notifyTest
# =======================================================================================================================
def notifyTest(cond, captured_output, error_contents, file, test, time):
"""
@param cond: ok, fail, error
@param captured_output: output captured from stdout
@param captured_output: output captured from stderr
@param file: the tests file (c:/temp/test.py)
@param test: the test ran (i.e.: TestCase.test1)
@param time: float with the number of seconds elapsed
"""
if _ServerHolder.SERVER is None:
return
assert cond is not None
assert captured_output is not None
assert error_contents is not None
assert file is not None
if test is None:
test = "" # Could happen if we have an import error importing module.
assert time is not None
try:
captured_output = _encode_if_needed(captured_output)
error_contents = _encode_if_needed(error_contents)
_ServerHolder.SERVER.notifyTest(cond, captured_output, error_contents, file, test, time)
except:
traceback.print_exc()
# =======================================================================================================================
# notifyTestRunFinished
# =======================================================================================================================
def notifyTestRunFinished(total_time):
assert total_time is not None
try:
if _ServerHolder.SERVER is not None:
_ServerHolder.SERVER.notifyTestRunFinished(total_time)
except:
traceback.print_exc()
# =======================================================================================================================
# force_server_kill
# =======================================================================================================================
def force_server_kill():
_ServerHolder.SERVER_COMM.notifications_queue.put_nowait(KillServer())
|