/usr/share/pyshared/guppy/heapy/Remote.py is in python-guppy 0.1.9-2ubuntu4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | #._cv_part guppy.heapy.Remote
"""
Support remote access to a Python interpreter.
"""
from guppy.etc import cmd
from guppy import hpy
from guppy.heapy import heapyc, Target
from guppy.heapy.RemoteConstants import *
from guppy.heapy.Console import Console
from guppy.sets import mutbitset
import os, socket, sys, time, thread, threading, traceback, Queue
class SocketClosed(Exception):
pass
class IsolatedCaller:
# Isolates the target interpreter from us
# when the _hiding_tag_ is set to the _hiding_tag_ of our hp.
# A solution of a problem discussed in notes Nov 8-9 2005.
# Note feb 3 2006: The class in the Target instance must be used.
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwds):
return self.func(*args, **kwds)
class QueueWithReadline(Queue.Queue):
def readline(self, size=-1):
# Make sure we are interruptible
# in case we get a keyboard interrupt.
# Not a very elegant way but the 'only' there is?
while 1:
try:
return self.get(timeout=0.5)
except Queue.Empty:
continue
class NotiInput:
def __init__(self, input, output):
self.input = input
self.output = output
def read(self, size=-1):
# This may return less data than what was requested
return self.readline(size)
def readline(self, size=-1):
self.output.write(READLINE)
return self.input.readline(size)
class Annex(cmd.Cmd):
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
use_rawinput = 0
prompt = '<Annex> '
def __init__(self, target, port=None):
cmd.Cmd.__init__(self)
if port is None:
port = HEAPYPORT
self.server_address = (LOCALHOST, port)
self.target = target
#target.close = target.sys.modules['guppy.heapy.Remote'].IsolatedCaller(
target.close = IsolatedCaller(
self.asynch_close)
self.socket = None
self.isclosed = 0
self.closelock = thread.allocate_lock()
self.intlocals = {
}
self.do_reset('')
def asynch_close(self):
# This may be called asynchronously
# by some other thread than the current (annex) thread.
# So I need to protect for a possible race condition.
# It is NOT enough with just an atomic test-and-set here
# since we need to wait during the time a close initiated
# from another thread is in progress, before exiting.
self.closelock.acquire()
try:
if not self.isclosed:
self.isclosed = 1
self.disconnect()
finally:
self.closelock.release()
def connect(self):
self.socket = socket.socket(self.address_family,
self.socket_type)
while not self.isclosed:
try:
# print 'connecting'
self.socket.connect(self.server_address)
except SystemExit:
raise
except socket.error:
if self.isclosed:
raise
time.sleep(2)
else:
break
else:
return
#print 'CONNECTED'
self.stdout = self.socket.makefile('w', bufsize=0)
self.stdin = NotiInput(self.socket.makefile('r'), self.stdout)
self.stderr = sys.stderr
if sys.version_info < (2, 4):
self.interruptible = 0
else:
self.start_ki_thread()
self.interruptible = 1
cmd.Cmd.__init__(self, stdin=self.stdin, stdout=self.stdout)
def start_ki_thread(self):
# Start a thread that can generates keyboard interrupr
# Inserts a spy thread between old stdin and a new stdin
queue = QueueWithReadline()
ostdin = self.stdin
self.stdin = NotiInput(input=queue,
output=ostdin.output)
socket = self.socket
def run():
try:
_hiding_tag_ = self.intlocals['hp']._hiding_tag_
while socket is self.socket:
line = ostdin.input.readline()
if not line:
break
if line == KEYBOARDINTERRUPT:
if socket is self.socket:
heapyc.set_async_exc(self.target.annex_thread,
KeyboardInterrupt)
else:
queue.put(line)
finally:
if socket is self.socket:
heapyc.set_async_exc(self.target.annex_thread,
SocketClosed)
th = threading.Thread(target=run,
args=())
th.start()
def disconnect(self):
socket = self.socket
if socket is None:
return
self.socket = None
try:
socket.send(DONE)
except:
pass
try:
socket.close()
except:
pass
sys.last_traceback=None
sys.exc_clear()
def do_close(self, arg):
self.asynch_close()
return 1
def help_close(self):
print >>self.stdout, """close
-----
Close and disable this remote connection completely. It can then not
be reopened other than by some command from within the target process.
Normally you shouldn't need to use this command, because you can
return to the Monitor via other commands (<Ctrl-C> or .) keeping the
connection open.
But it might be useful when you want to get rid of the remote control
interpreter and thread, if it uses too much memory or disturbs the
target process in some other way."""
do_h = cmd.Cmd.do_help
def help_h(self):
print >>self.stdout, """h(elp)
-----
Without argument, print the list of available commands.
With a command name as argument, print help about that command."""
help_help = help_h
def do_int(self, arg):
# XXX We should really stop other tasks while we use changed stdio files
# but that seems to be hard to do
# so this is ok for some practical purposes.
# --- new note May 8 2005:
# --- and doesn't matter since we are in a different interpreter -
# --- so there is no XXX issue ?
ostdin = sys.stdin
ostdout = sys.stdout
ostderr = sys.stderr
try:
sys.stdin = self.stdin
sys.stdout = self.stdout
sys.stderr = self.stdout
con = Console(stdin=sys.stdin,stdout=sys.stdout,
locals=self.intlocals)
con.interact(
"Remote interactive console. To return to Annex, type %r."%
con.EOF_key_sequence)
finally:
sys.stdin = ostdin
sys.stdout = ostdout
sys.stderr = ostderr
def help_int(self):
print >>self.stdout, """int
-----
Interactive console.
Bring up a Python console in the Remote Control interpreter.
This console will initially have access to a heapy constructor, named
hpy, and a ready-made instance, named hp, and the target (see also the
reset command). Other things may be imported as needed.
After returning to the Annex (by q) or to the Monitor (by . or
<Ctrl-C>), the data in the interactive console will remain there - and
will be available till the next time the console is entered. But the
data may be cleared and reset to the initial state - a new heapy
instance will be created - by the 'reset' command of Annex.
It should be noted that the interpreter thread under investigation is
executing in parallell with the remote control interpreter. So there
may be some problems to do with that if both are executing at the same
time. This has to be dealt with for each case specifically."""
_bname = 'a1e55f5dc4c9f708311e9f97b8098cd3'
def do_isolatest(self, arg):
hp = self.intlocals['hp']
a = []
self._a = a
b = []
self.intlocals[self._bname] = b
eval('0', self.intlocals) # to make __builtins__ exist if it did not already
testobjects = [a,
b,
self.intlocals['__builtins__'],
self.intlocals,
hp]
h = hp.heap()
if hp.iso(*testobjects) & h:
print >>self.stdout, 'Isolation test failed.'
for i, v in enumerate(testobjects):
if hp.iso(v) & h:
print >>self.stdout, '-- Shortest Path(s) to testobjects[%d] --'%i
print >>self.stdout, hp.iso(v).shpaths
else:
print >>self.stdout, 'Isolation test succeeded.'
del self._a
del self.intlocals[self._bname]
def help_isolatest(self):
print >>self.stdout, """isolatest
----------
Isolation test.
Test that the target interpreter heap view is isolated from the data
in the remote control interpreter. Data introduced here, eg in the
interactive console, should not be seen in the heap as reported by
hp.heap() etc. This is achieved by setting hp to not follow the
calling interpreter root. However, this isolation may become broken.
This test is intended to diagnose this problem. The test checks that
none of a number of test objects is visible in the target heap
view. If the test failed, it will show the shortest path(s) to each of
the test objects that was visible."""
def do_q(self, arg):
print >>self.stdout, 'To return to Monitor, type <Ctrl-C> or .'
print >>self.stdout, "To close this connection ('permanently'), type close"
def help_q(self):
print >>self.stdout, """q
-----
Quit.
This doesn't currently do anything except printing a message. (I
thought it would be too confusing to have a q (quit) command from the
Annex, when there was a similarly named command in the Monitor.)"""
def do_reset(self, arg):
self.intlocals.clear()
self.intlocals.update(
{'hpy' : self.hpy,
'hp' : self.hpy(),
'target':self.target
})
# Set shorthand h, it is so commonly used
# and the instance name now used in README example etc
self.intlocals['h'] = self.intlocals['hp']
def help_reset(self):
print >>self.stdout, """reset
-----
Reset things to an initial state.
This resets the state of the interactive console data only, for now.
It is reinitialized to contain the following:
hpy --- from guppy import hpy
hp --- hp = hpy()
target --- a reference to some data in the target interpreter
h --- h = hp; h is a shorthand for hp
(The hpy function is modified here from the normal one so
it sets some options to make it be concerned with the target
interpreter heap under investigation rather than the current one.)
"""
def do_stat(self, arg):
print >>self.stdout, "Target overview"
print >>self.stdout, "------------------------------------"
print >>self.stdout, "target.sys.executable = %s"%self.target.sys.executable
print >>self.stdout, "target.sys.argv = %s"%self.target.sys.argv
print >>self.stdout, "target.wd = %s"%self.target.wd
print >>self.stdout, "target.pid = %d"%self.target.pid
print >>self.stdout, "------------------------------------"
if not self.interruptible:
print >>self.stdout, "noninterruptible interactive console"
def help_stat(self):
print >>self.stdout, """stat
-----
Print an overview status table, with data from the target interpreter.
In the table, sys.executable and sys.argv means the current values of
those attributes in the sys module of the target interpreter. The row
labeled target.wd is the working directory of the target interpreter,
at the time the Remote Control interpreter was started (the actual
working directory may have changed since that time). The row labeled
target.pid is the process id of the target interpreter.
"""
def hpy(self, *args, **kwds):
from guppy import hpy
hp = hpy(*args, **kwds)
hp.View.is_hiding_calling_interpreter = 1
hp.View.target = self.target
self.target.close._hiding_tag_ = hp._hiding_tag_
hp.reprefix = 'hp.'
return hp
def run(self):
try:
while not self.isclosed:
self.connect()
if not self.isclosed:
self.do_stat('')
while 1:
try:
self.cmdloop()
except SocketClosed:
break
except:
try:
traceback.print_exc(file=self.stdout)
except:
traceback.print_exc(file=sys.stdout)
break
continue
self.disconnect()
finally:
# Make sure the thread/interpreter can't terminate
# without the annex being closed,
# and that we WAIT if someone else is being closing us.
self.asynch_close()
#print 'Annex DONE'
def on():
# Start a remote monitoring enabling thread,
# unless I am that thread myself.
global annex_thread, target
if annex_thread is not None:
return
if getattr(sys, '_is_guppy_heapy_remote_interpreter_', 0):
return
start_annex = """\
# Set a flag to stop recursion when importing site
# in case sitecustomize tries to do Remote.on()
import sys
sys._is_guppy_heapy_remote_interpreter_ = 1
import site
from guppy.heapy import Remote
Remote.Annex(target).run()
"""
target = Target.Target()
annex_thread = heapyc.interpreter(start_annex, {'target':target})
target.annex_thread = annex_thread
def off():
global annex_thread, target
if annex_thread is None:
return
for i in range(10):
try:
close = target.close
except AttributeError:
# It may not have been initiated yet.
# wait and repeat
print 'Can not turn it off yet, waiting..'
time.sleep(1)
else:
close()
break
else:
raise
heapyc.set_async_exc(annex_thread, SystemExit)
annex_thread = target = None
annex_thread = None
target = None
|