add Linux_i686
This commit is contained in:
parent
75f9a2fcbc
commit
95cd9b11f2
1644 changed files with 564260 additions and 0 deletions
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
|
||||
"""
|
||||
Twisted Manhole: interactive interpreter and direct manipulation support for Twisted.
|
||||
"""
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
|
||||
"""An input/output window for the glade reactor inspector.
|
||||
"""
|
||||
|
||||
import time
|
||||
import gtk
|
||||
import gobject
|
||||
import gtk.glade
|
||||
from twisted.python.util import sibpath
|
||||
from twisted.python import reflect
|
||||
|
||||
from twisted.manhole.ui import gtk2manhole
|
||||
from twisted.python.components import Adapter, registerAdapter
|
||||
from twisted.python import log
|
||||
from twisted.protocols import policies
|
||||
from zope.interface import implements, Interface
|
||||
|
||||
# the glade file uses stock icons, which requires gnome to be installed
|
||||
import gnome
|
||||
version = "$Revision: 1.1 $"[11:-2]
|
||||
gnome.init("gladereactor Inspector", version)
|
||||
|
||||
class ConsoleOutput(gtk2manhole.ConsoleOutput):
|
||||
def _captureLocalLog(self):
|
||||
self.fobs = log.FileLogObserver(gtk2manhole._Notafile(self, "log"))
|
||||
self.fobs.start()
|
||||
|
||||
def stop(self):
|
||||
self.fobs.stop()
|
||||
del self.fobs
|
||||
|
||||
class ConsoleInput(gtk2manhole.ConsoleInput):
|
||||
def sendMessage(self):
|
||||
buffer = self.textView.get_buffer()
|
||||
iter1, iter2 = buffer.get_bounds()
|
||||
text = buffer.get_text(iter1, iter2, False)
|
||||
self.do(text)
|
||||
|
||||
def do(self, text):
|
||||
self.toplevel.do(text)
|
||||
|
||||
class INode(Interface):
|
||||
"""A node in the inspector tree model.
|
||||
"""
|
||||
|
||||
def __adapt__(adaptable, default):
|
||||
if hasattr(adaptable, "__dict__"):
|
||||
return InstanceNode(adaptable)
|
||||
return AttributesNode(adaptable)
|
||||
|
||||
class InspectorNode(Adapter):
|
||||
implements(INode)
|
||||
|
||||
def postInit(self, offset, parent, slot):
|
||||
self.offset = offset
|
||||
self.parent = parent
|
||||
self.slot = slot
|
||||
|
||||
def getPath(self):
|
||||
L = []
|
||||
x = self
|
||||
while x.parent is not None:
|
||||
L.append(x.offset)
|
||||
x = x.parent
|
||||
L.reverse()
|
||||
return L
|
||||
|
||||
def __getitem__(self, index):
|
||||
slot, o = self.get(index)
|
||||
n = INode(o, persist=False)
|
||||
n.postInit(index, self, slot)
|
||||
return n
|
||||
|
||||
def origstr(self):
|
||||
return str(self.original)
|
||||
|
||||
def format(self):
|
||||
return (self.slot, self.origstr())
|
||||
|
||||
|
||||
class ConstantNode(InspectorNode):
|
||||
def __len__(self):
|
||||
return 0
|
||||
|
||||
class DictionaryNode(InspectorNode):
|
||||
def get(self, index):
|
||||
L = self.original.items()
|
||||
L.sort()
|
||||
return L[index]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.original)
|
||||
|
||||
def origstr(self):
|
||||
return "Dictionary"
|
||||
|
||||
class ListNode(InspectorNode):
|
||||
def get(self, index):
|
||||
return index, self.original[index]
|
||||
|
||||
def origstr(self):
|
||||
return "List"
|
||||
|
||||
def __len__(self):
|
||||
return len(self.original)
|
||||
|
||||
class AttributesNode(InspectorNode):
|
||||
def __len__(self):
|
||||
return len(dir(self.original))
|
||||
|
||||
def get(self, index):
|
||||
L = dir(self.original)
|
||||
L.sort()
|
||||
return L[index], getattr(self.original, L[index])
|
||||
|
||||
class InstanceNode(InspectorNode):
|
||||
def __len__(self):
|
||||
return len(self.original.__dict__) + 1
|
||||
|
||||
def get(self, index):
|
||||
if index == 0:
|
||||
if hasattr(self.original, "__class__"):
|
||||
v = self.original.__class__
|
||||
else:
|
||||
v = type(self.original)
|
||||
return "__class__", v
|
||||
else:
|
||||
index -= 1
|
||||
L = self.original.__dict__.items()
|
||||
L.sort()
|
||||
return L[index]
|
||||
|
||||
import types
|
||||
|
||||
for x in dict, types.DictProxyType:
|
||||
registerAdapter(DictionaryNode, x, INode)
|
||||
for x in list, tuple:
|
||||
registerAdapter(ListNode, x, INode)
|
||||
for x in int, str:
|
||||
registerAdapter(ConstantNode, x, INode)
|
||||
|
||||
|
||||
class InspectorTreeModel(gtk.GenericTreeModel):
|
||||
def __init__(self, root):
|
||||
gtk.GenericTreeModel.__init__(self)
|
||||
self.root = INode(root, persist=False)
|
||||
self.root.postInit(0, None, 'root')
|
||||
|
||||
def on_get_flags(self):
|
||||
return 0
|
||||
|
||||
def on_get_n_columns(self):
|
||||
return 1
|
||||
|
||||
def on_get_column_type(self, index):
|
||||
return gobject.TYPE_STRING
|
||||
|
||||
def on_get_path(self, node):
|
||||
return node.getPath()
|
||||
|
||||
def on_get_iter(self, path):
|
||||
x = self.root
|
||||
for elem in path:
|
||||
x = x[elem]
|
||||
return x
|
||||
|
||||
def on_get_value(self, node, column):
|
||||
return node.format()[column]
|
||||
|
||||
def on_iter_next(self, node):
|
||||
try:
|
||||
return node.parent[node.offset + 1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def on_iter_children(self, node):
|
||||
return node[0]
|
||||
|
||||
def on_iter_has_child(self, node):
|
||||
return len(node)
|
||||
|
||||
def on_iter_n_children(self, node):
|
||||
return len(node)
|
||||
|
||||
def on_iter_nth_child(self, node, n):
|
||||
if node is None:
|
||||
return None
|
||||
return node[n]
|
||||
|
||||
def on_iter_parent(self, node):
|
||||
return node.parent
|
||||
|
||||
|
||||
class Inspectro:
|
||||
selected = None
|
||||
def __init__(self, o=None):
|
||||
self.xml = x = gtk.glade.XML(sibpath(__file__, "inspectro.glade"))
|
||||
self.tree_view = x.get_widget("treeview")
|
||||
colnames = ["Name", "Value"]
|
||||
for i in range(len(colnames)):
|
||||
self.tree_view.append_column(
|
||||
gtk.TreeViewColumn(
|
||||
colnames[i], gtk.CellRendererText(), text=i))
|
||||
d = {}
|
||||
for m in reflect.prefixedMethods(self, "on_"):
|
||||
d[m.im_func.__name__] = m
|
||||
self.xml.signal_autoconnect(d)
|
||||
if o is not None:
|
||||
self.inspect(o)
|
||||
self.ns = {'inspect': self.inspect}
|
||||
iwidget = x.get_widget('input')
|
||||
self.input = ConsoleInput(iwidget)
|
||||
self.input.toplevel = self
|
||||
iwidget.connect("key_press_event", self.input._on_key_press_event)
|
||||
self.output = ConsoleOutput(x.get_widget('output'))
|
||||
|
||||
def select(self, o):
|
||||
self.selected = o
|
||||
self.ns['it'] = o
|
||||
self.xml.get_widget("itname").set_text(repr(o))
|
||||
self.xml.get_widget("itpath").set_text("???")
|
||||
|
||||
def inspect(self, o):
|
||||
self.model = InspectorTreeModel(o)
|
||||
self.tree_view.set_model(self.model)
|
||||
self.inspected = o
|
||||
|
||||
def do(self, command):
|
||||
filename = '<inspector>'
|
||||
try:
|
||||
print repr(command)
|
||||
try:
|
||||
code = compile(command, filename, 'eval')
|
||||
except:
|
||||
code = compile(command, filename, 'single')
|
||||
val = eval(code, self.ns, self.ns)
|
||||
if val is not None:
|
||||
print repr(val)
|
||||
self.ns['_'] = val
|
||||
except:
|
||||
log.err()
|
||||
|
||||
def on_inspect(self, *a):
|
||||
self.inspect(self.selected)
|
||||
|
||||
def on_inspect_new(self, *a):
|
||||
Inspectro(self.selected)
|
||||
|
||||
def on_row_activated(self, tv, path, column):
|
||||
self.select(self.model.on_get_iter(path).original)
|
||||
|
||||
|
||||
class LoggingProtocol(policies.ProtocolWrapper):
|
||||
"""Log network traffic."""
|
||||
|
||||
logging = True
|
||||
logViewer = None
|
||||
|
||||
def __init__(self, *args):
|
||||
policies.ProtocolWrapper.__init__(self, *args)
|
||||
self.inLog = []
|
||||
self.outLog = []
|
||||
|
||||
def write(self, data):
|
||||
if self.logging:
|
||||
self.outLog.append((time.time(), data))
|
||||
if self.logViewer:
|
||||
self.logViewer.updateOut(self.outLog[-1])
|
||||
policies.ProtocolWrapper.write(self, data)
|
||||
|
||||
def dataReceived(self, data):
|
||||
if self.logging:
|
||||
self.inLog.append((time.time(), data))
|
||||
if self.logViewer:
|
||||
self.logViewer.updateIn(self.inLog[-1])
|
||||
policies.ProtocolWrapper.dataReceived(self, data)
|
||||
|
||||
def __repr__(self):
|
||||
r = "wrapped " + repr(self.wrappedProtocol)
|
||||
if self.logging:
|
||||
r += " (logging)"
|
||||
return r
|
||||
|
||||
|
||||
class LoggingFactory(policies.WrappingFactory):
|
||||
"""Wrap protocols with logging wrappers."""
|
||||
|
||||
protocol = LoggingProtocol
|
||||
logging = True
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
p = self.protocol(self, self.wrappedFactory.buildProtocol(addr))
|
||||
p.logging = self.logging
|
||||
return p
|
||||
|
||||
def __repr__(self):
|
||||
r = "wrapped " + repr(self.wrappedFactory)
|
||||
if self.logging:
|
||||
r += " (logging)"
|
||||
return r
|
||||
|
||||
|
||||
class LogViewer:
|
||||
"""Display log of network traffic."""
|
||||
|
||||
def __init__(self, p):
|
||||
self.p = p
|
||||
vals = [time.time()]
|
||||
if p.inLog:
|
||||
vals.append(p.inLog[0][0])
|
||||
if p.outLog:
|
||||
vals.append(p.outLog[0][0])
|
||||
self.startTime = min(vals)
|
||||
p.logViewer = self
|
||||
self.xml = gtk.glade.XML(sibpath(__file__, "logview.glade"))
|
||||
self.xml.signal_autoconnect(self)
|
||||
self.loglist = self.xml.get_widget("loglist")
|
||||
# setup model, connect it to my treeview
|
||||
self.model = gtk.ListStore(str, str, str)
|
||||
self.loglist.set_model(self.model)
|
||||
self.loglist.set_reorderable(1)
|
||||
self.loglist.set_headers_clickable(1)
|
||||
# self.servers.set_headers_draggable(1)
|
||||
# add a column
|
||||
for col in [
|
||||
gtk.TreeViewColumn('Time',
|
||||
gtk.CellRendererText(),
|
||||
text=0),
|
||||
gtk.TreeViewColumn('D',
|
||||
gtk.CellRendererText(),
|
||||
text=1),
|
||||
gtk.TreeViewColumn('Data',
|
||||
gtk.CellRendererText(),
|
||||
text=2)]:
|
||||
self.loglist.append_column(col)
|
||||
col.set_resizable(1)
|
||||
r = []
|
||||
for t, data in p.inLog:
|
||||
r.append(((str(t - self.startTime), "R", repr(data)[1:-1])))
|
||||
for t, data in p.outLog:
|
||||
r.append(((str(t - self.startTime), "S", repr(data)[1:-1])))
|
||||
r.sort()
|
||||
for i in r:
|
||||
self.model.append(i)
|
||||
|
||||
def updateIn(self, (time, data)):
|
||||
self.model.append((str(time - self.startTime), "R", repr(data)[1:-1]))
|
||||
|
||||
def updateOut(self, (time, data)):
|
||||
self.model.append((str(time - self.startTime), "S", repr(data)[1:-1]))
|
||||
|
||||
def on_logview_destroy(self, w):
|
||||
self.p.logViewer = None
|
||||
del self.p
|
||||
|
||||
|
||||
def main():
|
||||
x = Inspectro()
|
||||
x.inspect(x)
|
||||
gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
log.startLogging(sys.stdout)
|
||||
main()
|
||||
|
||||
|
|
@ -0,0 +1,654 @@
|
|||
# -*- test-case-name: twisted.test.test_explorer -*-
|
||||
# $Id: explorer.py,v 1.6 2003/02/18 21:15:30 acapnotic Exp $
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
|
||||
"""Support for python object introspection and exploration.
|
||||
|
||||
Note that Explorers, what with their list of attributes, are much like
|
||||
manhole.coil.Configurables. Someone should investigate this further. (TODO)
|
||||
|
||||
Also TODO: Determine how much code in here (particularly the function
|
||||
signature stuff) can be replaced with functions available in the
|
||||
L{inspect} module available in Python 2.1.
|
||||
"""
|
||||
|
||||
# System Imports
|
||||
import inspect, string, sys, types
|
||||
import UserDict
|
||||
|
||||
# Twisted Imports
|
||||
from twisted.spread import pb
|
||||
from twisted.python import reflect
|
||||
|
||||
|
||||
True=(1==1)
|
||||
False=not True
|
||||
|
||||
class Pool(UserDict.UserDict):
|
||||
def getExplorer(self, object, identifier):
|
||||
oid = id(object)
|
||||
if oid in self.data:
|
||||
# XXX: This potentially returns something with
|
||||
# 'identifier' set to a different value.
|
||||
return self.data[oid]
|
||||
else:
|
||||
klass = typeTable.get(type(object), ExplorerGeneric)
|
||||
e = types.InstanceType(klass, {})
|
||||
self.data[oid] = e
|
||||
klass.__init__(e, object, identifier)
|
||||
return e
|
||||
|
||||
explorerPool = Pool()
|
||||
|
||||
class Explorer(pb.Cacheable):
|
||||
properties = ["id", "identifier"]
|
||||
attributeGroups = []
|
||||
accessors = ["get_refcount"]
|
||||
|
||||
id = None
|
||||
identifier = None
|
||||
|
||||
def __init__(self, object, identifier):
|
||||
self.object = object
|
||||
self.identifier = identifier
|
||||
self.id = id(object)
|
||||
|
||||
self.properties = []
|
||||
reflect.accumulateClassList(self.__class__, 'properties',
|
||||
self.properties)
|
||||
|
||||
self.attributeGroups = []
|
||||
reflect.accumulateClassList(self.__class__, 'attributeGroups',
|
||||
self.attributeGroups)
|
||||
|
||||
self.accessors = []
|
||||
reflect.accumulateClassList(self.__class__, 'accessors',
|
||||
self.accessors)
|
||||
|
||||
def getStateToCopyFor(self, perspective):
|
||||
all = ["properties", "attributeGroups", "accessors"]
|
||||
all.extend(self.properties)
|
||||
all.extend(self.attributeGroups)
|
||||
|
||||
state = {}
|
||||
for key in all:
|
||||
state[key] = getattr(self, key)
|
||||
|
||||
state['view'] = pb.ViewPoint(perspective, self)
|
||||
state['explorerClass'] = self.__class__.__name__
|
||||
return state
|
||||
|
||||
def view_get_refcount(self, perspective):
|
||||
return sys.getrefcount(self)
|
||||
|
||||
class ExplorerGeneric(Explorer):
|
||||
properties = ["str", "repr", "typename"]
|
||||
|
||||
def __init__(self, object, identifier):
|
||||
Explorer.__init__(self, object, identifier)
|
||||
self.str = str(object)
|
||||
self.repr = repr(object)
|
||||
self.typename = type(object).__name__
|
||||
|
||||
|
||||
class ExplorerImmutable(Explorer):
|
||||
properties = ["value"]
|
||||
|
||||
def __init__(self, object, identifier):
|
||||
Explorer.__init__(self, object, identifier)
|
||||
self.value = object
|
||||
|
||||
|
||||
class ExplorerSequence(Explorer):
|
||||
properties = ["len"]
|
||||
attributeGroups = ["elements"]
|
||||
accessors = ["get_elements"]
|
||||
|
||||
def __init__(self, seq, identifier):
|
||||
Explorer.__init__(self, seq, identifier)
|
||||
self.seq = seq
|
||||
self.len = len(seq)
|
||||
|
||||
# Use accessor method to fill me in.
|
||||
self.elements = []
|
||||
|
||||
def get_elements(self):
|
||||
self.len = len(self.seq)
|
||||
l = []
|
||||
for i in xrange(self.len):
|
||||
identifier = "%s[%s]" % (self.identifier, i)
|
||||
|
||||
# GLOBAL: using global explorerPool
|
||||
l.append(explorerPool.getExplorer(self.seq[i], identifier))
|
||||
|
||||
return l
|
||||
|
||||
def view_get_elements(self, perspective):
|
||||
# XXX: set the .elements member of all my remoteCaches
|
||||
return self.get_elements()
|
||||
|
||||
|
||||
class ExplorerMapping(Explorer):
|
||||
properties = ["len"]
|
||||
attributeGroups = ["keys"]
|
||||
accessors = ["get_keys", "get_item"]
|
||||
|
||||
def __init__(self, dct, identifier):
|
||||
Explorer.__init__(self, dct, identifier)
|
||||
|
||||
self.dct = dct
|
||||
self.len = len(dct)
|
||||
|
||||
# Use accessor method to fill me in.
|
||||
self.keys = []
|
||||
|
||||
def get_keys(self):
|
||||
keys = self.dct.keys()
|
||||
self.len = len(keys)
|
||||
l = []
|
||||
for i in xrange(self.len):
|
||||
identifier = "%s.keys()[%s]" % (self.identifier, i)
|
||||
|
||||
# GLOBAL: using global explorerPool
|
||||
l.append(explorerPool.getExplorer(keys[i], identifier))
|
||||
|
||||
return l
|
||||
|
||||
def view_get_keys(self, perspective):
|
||||
# XXX: set the .keys member of all my remoteCaches
|
||||
return self.get_keys()
|
||||
|
||||
def view_get_item(self, perspective, key):
|
||||
if type(key) is types.InstanceType:
|
||||
key = key.object
|
||||
|
||||
item = self.dct[key]
|
||||
|
||||
identifier = "%s[%s]" % (self.identifier, repr(key))
|
||||
# GLOBAL: using global explorerPool
|
||||
item = explorerPool.getExplorer(item, identifier)
|
||||
return item
|
||||
|
||||
|
||||
class ExplorerBuiltin(Explorer):
|
||||
"""
|
||||
@ivar name: the name the function was defined as
|
||||
@ivar doc: function's docstring, or C{None} if unavailable
|
||||
@ivar self: if not C{None}, the function is a method of this object.
|
||||
"""
|
||||
properties = ["doc", "name", "self"]
|
||||
def __init__(self, function, identifier):
|
||||
Explorer.__init__(self, function, identifier)
|
||||
self.doc = function.__doc__
|
||||
self.name = function.__name__
|
||||
self.self = function.__self__
|
||||
|
||||
|
||||
class ExplorerInstance(Explorer):
|
||||
"""
|
||||
Attribute groups:
|
||||
- B{methods} -- dictionary of methods
|
||||
- B{data} -- dictionary of data members
|
||||
|
||||
Note these are only the *instance* methods and members --
|
||||
if you want the class methods, you'll have to look up the class.
|
||||
|
||||
TODO: Detail levels (me, me & class, me & class ancestory)
|
||||
|
||||
@ivar klass: the class this is an instance of.
|
||||
"""
|
||||
properties = ["klass"]
|
||||
attributeGroups = ["methods", "data"]
|
||||
|
||||
def __init__(self, instance, identifier):
|
||||
Explorer.__init__(self, instance, identifier)
|
||||
members = {}
|
||||
methods = {}
|
||||
for i in dir(instance):
|
||||
# TODO: Make screening of private attributes configurable.
|
||||
if i[0] == '_':
|
||||
continue
|
||||
mIdentifier = string.join([identifier, i], ".")
|
||||
member = getattr(instance, i)
|
||||
mType = type(member)
|
||||
|
||||
if mType is types.MethodType:
|
||||
methods[i] = explorerPool.getExplorer(member, mIdentifier)
|
||||
else:
|
||||
members[i] = explorerPool.getExplorer(member, mIdentifier)
|
||||
|
||||
self.klass = explorerPool.getExplorer(instance.__class__,
|
||||
self.identifier +
|
||||
'.__class__')
|
||||
self.data = members
|
||||
self.methods = methods
|
||||
|
||||
|
||||
class ExplorerClass(Explorer):
|
||||
"""
|
||||
@ivar name: the name the class was defined with
|
||||
@ivar doc: the class's docstring
|
||||
@ivar bases: a list of this class's base classes.
|
||||
@ivar module: the module the class is defined in
|
||||
|
||||
Attribute groups:
|
||||
- B{methods} -- class methods
|
||||
- B{data} -- other members of the class
|
||||
"""
|
||||
properties = ["name", "doc", "bases", "module"]
|
||||
attributeGroups = ["methods", "data"]
|
||||
def __init__(self, theClass, identifier):
|
||||
Explorer.__init__(self, theClass, identifier)
|
||||
if not identifier:
|
||||
identifier = theClass.__name__
|
||||
members = {}
|
||||
methods = {}
|
||||
for i in dir(theClass):
|
||||
if (i[0] == '_') and (i != '__init__'):
|
||||
continue
|
||||
|
||||
mIdentifier = string.join([identifier, i], ".")
|
||||
member = getattr(theClass, i)
|
||||
mType = type(member)
|
||||
|
||||
if mType is types.MethodType:
|
||||
methods[i] = explorerPool.getExplorer(member, mIdentifier)
|
||||
else:
|
||||
members[i] = explorerPool.getExplorer(member, mIdentifier)
|
||||
|
||||
self.name = theClass.__name__
|
||||
self.doc = inspect.getdoc(theClass)
|
||||
self.data = members
|
||||
self.methods = methods
|
||||
self.bases = explorerPool.getExplorer(theClass.__bases__,
|
||||
identifier + ".__bases__")
|
||||
self.module = getattr(theClass, '__module__', None)
|
||||
|
||||
|
||||
class ExplorerFunction(Explorer):
|
||||
properties = ["name", "doc", "file", "line","signature"]
|
||||
"""
|
||||
name -- the name the function was defined as
|
||||
signature -- the function's calling signature (Signature instance)
|
||||
doc -- the function's docstring
|
||||
file -- the file the function is defined in
|
||||
line -- the line in the file the function begins on
|
||||
"""
|
||||
def __init__(self, function, identifier):
|
||||
Explorer.__init__(self, function, identifier)
|
||||
code = function.func_code
|
||||
argcount = code.co_argcount
|
||||
takesList = (code.co_flags & 0x04) and 1
|
||||
takesKeywords = (code.co_flags & 0x08) and 1
|
||||
|
||||
n = (argcount + takesList + takesKeywords)
|
||||
signature = Signature(code.co_varnames[:n])
|
||||
|
||||
if function.func_defaults:
|
||||
i_d = 0
|
||||
for i in xrange(argcount - len(function.func_defaults),
|
||||
argcount):
|
||||
default = function.func_defaults[i_d]
|
||||
default = explorerPool.getExplorer(
|
||||
default, '%s.func_defaults[%d]' % (identifier, i_d))
|
||||
signature.set_default(i, default)
|
||||
|
||||
i_d = i_d + 1
|
||||
|
||||
if takesKeywords:
|
||||
signature.set_keyword(n - 1)
|
||||
|
||||
if takesList:
|
||||
signature.set_varlist(n - 1 - takesKeywords)
|
||||
|
||||
# maybe also: function.func_globals,
|
||||
# or at least func_globals.__name__?
|
||||
# maybe the bytecode, for disassembly-view?
|
||||
|
||||
self.name = function.__name__
|
||||
self.signature = signature
|
||||
self.doc = inspect.getdoc(function)
|
||||
self.file = code.co_filename
|
||||
self.line = code.co_firstlineno
|
||||
|
||||
|
||||
class ExplorerMethod(ExplorerFunction):
|
||||
properties = ["self", "klass"]
|
||||
"""
|
||||
In addition to ExplorerFunction properties:
|
||||
self -- the object I am bound to, or None if unbound
|
||||
klass -- the class I am a method of
|
||||
"""
|
||||
def __init__(self, method, identifier):
|
||||
|
||||
function = method.im_func
|
||||
if type(function) is types.InstanceType:
|
||||
function = function.__call__.im_func
|
||||
|
||||
ExplorerFunction.__init__(self, function, identifier)
|
||||
self.id = id(method)
|
||||
self.klass = explorerPool.getExplorer(method.im_class,
|
||||
identifier + '.im_class')
|
||||
self.self = explorerPool.getExplorer(method.im_self,
|
||||
identifier + '.im_self')
|
||||
|
||||
if method.im_self:
|
||||
# I'm a bound method -- eat the 'self' arg.
|
||||
self.signature.discardSelf()
|
||||
|
||||
|
||||
class ExplorerModule(Explorer):
|
||||
"""
|
||||
@ivar name: the name the module was defined as
|
||||
@ivar doc: documentation string for the module
|
||||
@ivar file: the file the module is defined in
|
||||
|
||||
Attribute groups:
|
||||
- B{classes} -- the public classes provided by the module
|
||||
- B{functions} -- the public functions provided by the module
|
||||
- B{data} -- the public data members provided by the module
|
||||
|
||||
(\"Public\" is taken to be \"anything that doesn't start with _\")
|
||||
"""
|
||||
properties = ["name","doc","file"]
|
||||
attributeGroups = ["classes", "functions", "data"]
|
||||
|
||||
def __init__(self, module, identifier):
|
||||
Explorer.__init__(self, module, identifier)
|
||||
functions = {}
|
||||
classes = {}
|
||||
data = {}
|
||||
for key, value in module.__dict__.items():
|
||||
if key[0] == '_':
|
||||
continue
|
||||
|
||||
mIdentifier = "%s.%s" % (identifier, key)
|
||||
|
||||
if type(value) is types.ClassType:
|
||||
classes[key] = explorerPool.getExplorer(value,
|
||||
mIdentifier)
|
||||
elif type(value) is types.FunctionType:
|
||||
functions[key] = explorerPool.getExplorer(value,
|
||||
mIdentifier)
|
||||
elif type(value) is types.ModuleType:
|
||||
pass # pass on imported modules
|
||||
else:
|
||||
data[key] = explorerPool.getExplorer(value, mIdentifier)
|
||||
|
||||
self.name = module.__name__
|
||||
self.doc = inspect.getdoc(module)
|
||||
self.file = getattr(module, '__file__', None)
|
||||
self.classes = classes
|
||||
self.functions = functions
|
||||
self.data = data
|
||||
|
||||
typeTable = {types.InstanceType: ExplorerInstance,
|
||||
types.ClassType: ExplorerClass,
|
||||
types.MethodType: ExplorerMethod,
|
||||
types.FunctionType: ExplorerFunction,
|
||||
types.ModuleType: ExplorerModule,
|
||||
types.BuiltinFunctionType: ExplorerBuiltin,
|
||||
types.ListType: ExplorerSequence,
|
||||
types.TupleType: ExplorerSequence,
|
||||
types.DictType: ExplorerMapping,
|
||||
types.StringType: ExplorerImmutable,
|
||||
types.NoneType: ExplorerImmutable,
|
||||
types.IntType: ExplorerImmutable,
|
||||
types.FloatType: ExplorerImmutable,
|
||||
types.LongType: ExplorerImmutable,
|
||||
types.ComplexType: ExplorerImmutable,
|
||||
}
|
||||
|
||||
class Signature(pb.Copyable):
|
||||
"""I represent the signature of a callable.
|
||||
|
||||
Signatures are immutable, so don't expect my contents to change once
|
||||
they've been set.
|
||||
"""
|
||||
_FLAVOURLESS = None
|
||||
_HAS_DEFAULT = 2
|
||||
_VAR_LIST = 4
|
||||
_KEYWORD_DICT = 8
|
||||
|
||||
def __init__(self, argNames):
|
||||
self.name = argNames
|
||||
self.default = [None] * len(argNames)
|
||||
self.flavour = [None] * len(argNames)
|
||||
|
||||
def get_name(self, arg):
|
||||
return self.name[arg]
|
||||
|
||||
def get_default(self, arg):
|
||||
if arg is types.StringType:
|
||||
arg = self.name.index(arg)
|
||||
|
||||
# Wouldn't it be nice if we just returned "None" when there
|
||||
# wasn't a default? Well, yes, but often times "None" *is*
|
||||
# the default, so return a tuple instead.
|
||||
if self.flavour[arg] == self._HAS_DEFAULT:
|
||||
return (True, self.default[arg])
|
||||
else:
|
||||
return (False, None)
|
||||
|
||||
def set_default(self, arg, value):
|
||||
if arg is types.StringType:
|
||||
arg = self.name.index(arg)
|
||||
|
||||
self.flavour[arg] = self._HAS_DEFAULT
|
||||
self.default[arg] = value
|
||||
|
||||
def set_varlist(self, arg):
|
||||
if arg is types.StringType:
|
||||
arg = self.name.index(arg)
|
||||
|
||||
self.flavour[arg] = self._VAR_LIST
|
||||
|
||||
def set_keyword(self, arg):
|
||||
if arg is types.StringType:
|
||||
arg = self.name.index(arg)
|
||||
|
||||
self.flavour[arg] = self._KEYWORD_DICT
|
||||
|
||||
def is_varlist(self, arg):
|
||||
if arg is types.StringType:
|
||||
arg = self.name.index(arg)
|
||||
|
||||
return (self.flavour[arg] == self._VAR_LIST)
|
||||
|
||||
def is_keyword(self, arg):
|
||||
if arg is types.StringType:
|
||||
arg = self.name.index(arg)
|
||||
|
||||
return (self.flavour[arg] == self._KEYWORD_DICT)
|
||||
|
||||
def discardSelf(self):
|
||||
"""Invoke me to discard the first argument if this is a bound method.
|
||||
"""
|
||||
## if self.name[0] != 'self':
|
||||
## log.msg("Warning: Told to discard self, but name is %s" %
|
||||
## self.name[0])
|
||||
self.name = self.name[1:]
|
||||
self.default.pop(0)
|
||||
self.flavour.pop(0)
|
||||
|
||||
def getStateToCopy(self):
|
||||
return {'name': tuple(self.name),
|
||||
'flavour': tuple(self.flavour),
|
||||
'default': tuple(self.default)}
|
||||
|
||||
def __len__(self):
|
||||
return len(self.name)
|
||||
|
||||
def __str__(self):
|
||||
arglist = []
|
||||
for arg in xrange(len(self)):
|
||||
name = self.get_name(arg)
|
||||
hasDefault, default = self.get_default(arg)
|
||||
if hasDefault:
|
||||
a = "%s=%s" % (name, default)
|
||||
elif self.is_varlist(arg):
|
||||
a = "*%s" % (name,)
|
||||
elif self.is_keyword(arg):
|
||||
a = "**%s" % (name,)
|
||||
else:
|
||||
a = name
|
||||
arglist.append(a)
|
||||
|
||||
return string.join(arglist,", ")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CRUFT_WatchyThingie:
|
||||
# TODO:
|
||||
#
|
||||
# * an exclude mechanism for the watcher's browser, to avoid
|
||||
# sending back large and uninteresting data structures.
|
||||
#
|
||||
# * an exclude mechanism for the watcher's trigger, to avoid
|
||||
# triggering on some frequently-called-method-that-doesn't-
|
||||
# actually-change-anything.
|
||||
#
|
||||
# * XXX! need removeWatch()
|
||||
|
||||
def watchIdentifier(self, identifier, callback):
|
||||
"""Watch the object returned by evaluating the identifier.
|
||||
|
||||
Whenever I think the object might have changed, I'll send an
|
||||
ObjectLink of it to the callback.
|
||||
|
||||
WARNING: This calls eval() on its argument!
|
||||
"""
|
||||
object = eval(identifier,
|
||||
self.globalNamespace,
|
||||
self.localNamespace)
|
||||
return self.watchObject(object, identifier, callback)
|
||||
|
||||
def watchObject(self, object, identifier, callback):
|
||||
"""Watch the given object.
|
||||
|
||||
Whenever I think the object might have changed, I'll send an
|
||||
ObjectLink of it to the callback.
|
||||
|
||||
The identifier argument is used to generate identifiers for
|
||||
objects which are members of this one.
|
||||
"""
|
||||
if type(object) is not types.InstanceType:
|
||||
raise TypeError, "Sorry, can only place a watch on Instances."
|
||||
|
||||
# uninstallers = []
|
||||
|
||||
dct = {}
|
||||
reflect.addMethodNamesToDict(object.__class__, dct, '')
|
||||
for k in object.__dict__.keys():
|
||||
dct[k] = 1
|
||||
|
||||
members = dct.keys()
|
||||
|
||||
clazzNS = {}
|
||||
clazz = types.ClassType('Watching%s%X' %
|
||||
(object.__class__.__name__, id(object)),
|
||||
(_MonkeysSetattrMixin, object.__class__,),
|
||||
clazzNS)
|
||||
|
||||
clazzNS['_watchEmitChanged'] = types.MethodType(
|
||||
lambda slf, i=identifier, b=self, cb=callback:
|
||||
cb(b.browseObject(slf, i)),
|
||||
None, clazz)
|
||||
|
||||
# orig_class = object.__class__
|
||||
object.__class__ = clazz
|
||||
|
||||
for name in members:
|
||||
m = getattr(object, name)
|
||||
# Only hook bound methods.
|
||||
if ((type(m) is types.MethodType)
|
||||
and (m.im_self is not None)):
|
||||
# What's the use of putting watch monkeys on methods
|
||||
# in addition to __setattr__? Well, um, uh, if the
|
||||
# methods modify their attributes (i.e. add a key to
|
||||
# a dictionary) instead of [re]setting them, then
|
||||
# we wouldn't know about it unless we did this.
|
||||
# (Is that convincing?)
|
||||
|
||||
monkey = _WatchMonkey(object)
|
||||
monkey.install(name)
|
||||
# uninstallers.append(monkey.uninstall)
|
||||
|
||||
# XXX: This probably prevents these objects from ever having a
|
||||
# zero refcount. Leak, Leak!
|
||||
## self.watchUninstallers[object] = uninstallers
|
||||
|
||||
|
||||
class _WatchMonkey:
|
||||
"""I hang on a method and tell you what I see.
|
||||
|
||||
TODO: Aya! Now I just do browseObject all the time, but I could
|
||||
tell you what got called with what when and returning what.
|
||||
"""
|
||||
oldMethod = None
|
||||
|
||||
def __init__(self, instance):
|
||||
"""Make a monkey to hang on this instance object.
|
||||
"""
|
||||
self.instance = instance
|
||||
|
||||
def install(self, methodIdentifier):
|
||||
"""Install myself on my instance in place of this method.
|
||||
"""
|
||||
oldMethod = getattr(self.instance, methodIdentifier, None)
|
||||
|
||||
# XXX: this conditional probably isn't effective.
|
||||
if oldMethod is not self:
|
||||
# avoid triggering __setattr__
|
||||
self.instance.__dict__[methodIdentifier] = types.MethodType(
|
||||
self, self.instance, self.instance.__class__)
|
||||
self.oldMethod = (methodIdentifier, oldMethod)
|
||||
|
||||
def uninstall(self):
|
||||
"""Remove myself from this instance and restore the original method.
|
||||
|
||||
(I hope.)
|
||||
"""
|
||||
if self.oldMethod is None:
|
||||
return
|
||||
|
||||
# XXX: This probably doesn't work if multiple monkies are hanging
|
||||
# on a method and they're not removed in order.
|
||||
if self.oldMethod[1] is None:
|
||||
delattr(self.instance, self.oldMethod[0])
|
||||
else:
|
||||
setattr(self.instance, self.oldMethod[0], self.oldMethod[1])
|
||||
|
||||
def __call__(self, instance, *a, **kw):
|
||||
"""Pretend to be the method I replaced, and ring the bell.
|
||||
"""
|
||||
if self.oldMethod[1]:
|
||||
rval = apply(self.oldMethod[1], a, kw)
|
||||
else:
|
||||
rval = None
|
||||
|
||||
instance._watchEmitChanged()
|
||||
return rval
|
||||
|
||||
|
||||
class _MonkeysSetattrMixin:
|
||||
"""A mix-in class providing __setattr__ for objects being watched.
|
||||
"""
|
||||
def __setattr__(self, k, v):
|
||||
"""Set the attribute and ring the bell.
|
||||
"""
|
||||
if hasattr(self.__class__.__bases__[1], '__setattr__'):
|
||||
# Hack! Using __bases__[1] is Bad, but since we created
|
||||
# this class, we can be reasonably sure it'll work.
|
||||
self.__class__.__bases__[1].__setattr__(self, k, v)
|
||||
else:
|
||||
self.__dict__[k] = v
|
||||
|
||||
# XXX: Hey, waitasec, did someone just hang a new method on me?
|
||||
# Do I need to put a monkey on it?
|
||||
|
||||
self._watchEmitChanged()
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
|
||||
<widget class="GtkWindow" id="window1">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">Twisted Daemon</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="default_width">256</property>
|
||||
<property name="default_height">300</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="servertree">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">True</property>
|
||||
<property name="rules_hint">False</property>
|
||||
<property name="reorderable">True</property>
|
||||
<property name="enable_search">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHButtonBox" id="hbuttonbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="suspend">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<signal name="clicked" handler="on_suspend_clicked" last_modification_time="Sun, 22 Jun 2003 05:09:20 GMT"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xscale">0</property>
|
||||
<property name="yscale">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">2</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImage" id="image2">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-undo</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label11">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Suspend</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="disconnect">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<signal name="clicked" handler="on_disconnect_clicked" last_modification_time="Sun, 22 Jun 2003 05:09:27 GMT"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xscale">0</property>
|
||||
<property name="yscale">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">2</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImage" id="image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-dialog-warning</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Disconnect</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="inspect">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<signal name="clicked" handler="on_inspect_clicked" last_modification_time="Wed, 17 Dec 2003 06:14:18 GMT"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment3">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xscale">0</property>
|
||||
<property name="yscale">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox4">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">2</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImage" id="image3">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-open</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label12">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Inspect</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="viewlog">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<signal name="clicked" handler="on_viewlog_clicked" last_modification_time="Sun, 04 Jan 2004 22:28:19 GMT"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment4">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xscale">0</property>
|
||||
<property name="yscale">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox5">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">2</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImage" id="image4">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-dialog-info</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label13">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">View Log</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="quit">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label">gtk-quit</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<signal name="clicked" handler="on_quit_clicked" last_modification_time="Sun, 04 Jan 2004 22:26:43 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
|
||||
"""
|
||||
A modified gtk2 reactor with a Glade dialog in-process that allows you to stop,
|
||||
suspend, resume and inspect transports interactively.
|
||||
"""
|
||||
|
||||
__all__ = ['install']
|
||||
|
||||
# Twisted Imports
|
||||
from twisted.python import util, reflect
|
||||
from twisted.internet.gtk2reactor import Gtk2Reactor as sup
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import gtk.glade
|
||||
|
||||
COLUMN_DESCRIPTION = 0
|
||||
COLUMN_TRANSPORT = 1
|
||||
COLUMN_READING = 2
|
||||
COLUMN_WRITING = 3
|
||||
|
||||
|
||||
class GladeReactor(sup):
|
||||
"""GTK+-2 event loop reactor with GUI.
|
||||
"""
|
||||
|
||||
def listenTCP(self, port, factory, backlog=50, interface=''):
|
||||
from _inspectro import LoggingFactory
|
||||
factory = LoggingFactory(factory)
|
||||
return sup.listenTCP(self, port, factory, backlog, interface)
|
||||
|
||||
def connectTCP(self, host, port, factory, timeout=30, bindAddress=None):
|
||||
from _inspectro import LoggingFactory
|
||||
factory = LoggingFactory(factory)
|
||||
return sup.connectTCP(self, host, port, factory, timeout, bindAddress)
|
||||
|
||||
def listenSSL(self, port, factory, contextFactory, backlog=50, interface=''):
|
||||
from _inspectro import LoggingFactory
|
||||
factory = LoggingFactory(factory)
|
||||
return sup.listenSSL(self, port, factory, contextFactory, backlog, interface)
|
||||
|
||||
def connectSSL(self, host, port, factory, contextFactory, timeout=30, bindAddress=None):
|
||||
from _inspectro import LoggingFactory
|
||||
factory = LoggingFactory(factory)
|
||||
return sup.connectSSL(self, host, port, factory, contextFactory, timeout, bindAddress)
|
||||
|
||||
def connectUNIX(self, address, factory, timeout=30):
|
||||
from _inspectro import LoggingFactory
|
||||
factory = LoggingFactory(factory)
|
||||
return sup.connectUNIX(self, address, factory, timeout)
|
||||
|
||||
def listenUNIX(self, address, factory, backlog=50, mode=0666):
|
||||
from _inspectro import LoggingFactory
|
||||
factory = LoggingFactory(factory)
|
||||
return sup.listenUNIX(self, address, factory, backlog, mode)
|
||||
|
||||
def on_disconnect_clicked(self, w):
|
||||
store, iter = self.servers.get_selection().get_selected()
|
||||
store[iter][COLUMN_TRANSPORT].loseConnection()
|
||||
|
||||
def on_viewlog_clicked(self, w):
|
||||
store, iter = self.servers.get_selection().get_selected()
|
||||
data = store[iter][1]
|
||||
from _inspectro import LogViewer
|
||||
if hasattr(data, "protocol") and not data.protocol.logViewer:
|
||||
LogViewer(data.protocol)
|
||||
|
||||
def on_inspect_clicked(self, w):
|
||||
store, iter = self.servers.get_selection().get_selected()
|
||||
data = store[iter]
|
||||
from _inspectro import Inspectro
|
||||
Inspectro(data[1])
|
||||
|
||||
def on_suspend_clicked(self, w):
|
||||
store, iter = self.servers.get_selection().get_selected()
|
||||
data = store[iter]
|
||||
sup.removeReader(self, data[1])
|
||||
sup.removeWriter(self, data[1])
|
||||
if data[COLUMN_DESCRIPTION].endswith('(suspended)'):
|
||||
if data[COLUMN_READING]:
|
||||
sup.addReader(self, data[COLUMN_TRANSPORT])
|
||||
if data[COLUMN_WRITING]:
|
||||
sup.addWriter(self, data[COLUMN_TRANSPORT])
|
||||
data[COLUMN_DESCRIPTION] = str(data[COLUMN_TRANSPORT])
|
||||
self.toggle_suspend(1)
|
||||
else:
|
||||
data[0] += ' (suspended)'
|
||||
self.toggle_suspend(0)
|
||||
|
||||
def toggle_suspend(self, suspending=0):
|
||||
stock, nonstock = [('gtk-redo', 'Resume'),
|
||||
('gtk-undo', 'Suspend')][suspending]
|
||||
b = self.xml.get_widget("suspend")
|
||||
b.set_use_stock(1)
|
||||
b.set_label(stock)
|
||||
b.get_child().get_child().get_children()[1].set_label(nonstock)
|
||||
|
||||
def servers_selection_changed(self, w):
|
||||
store, iter = w.get_selected()
|
||||
if iter is None:
|
||||
self.xml.get_widget("suspend").set_sensitive(0)
|
||||
self.xml.get_widget('disconnect').set_sensitive(0)
|
||||
else:
|
||||
data = store[iter]
|
||||
self.toggle_suspend(not
|
||||
data[COLUMN_DESCRIPTION].endswith('(suspended)'))
|
||||
self.xml.get_widget("suspend").set_sensitive(1)
|
||||
self.xml.get_widget('disconnect').set_sensitive(1)
|
||||
|
||||
def on_quit_clicked(self, w):
|
||||
self.stop()
|
||||
|
||||
def __init__(self):
|
||||
self.xml = gtk.glade.XML(util.sibpath(__file__,"gladereactor.glade"))
|
||||
d = {}
|
||||
for m in reflect.prefixedMethods(self, "on_"):
|
||||
d[m.im_func.__name__] = m
|
||||
self.xml.signal_autoconnect(d)
|
||||
self.xml.get_widget('window1').connect('destroy',
|
||||
lambda w: self.stop())
|
||||
self.servers = self.xml.get_widget("servertree")
|
||||
sel = self.servers.get_selection()
|
||||
sel.set_mode(gtk.SELECTION_SINGLE)
|
||||
sel.connect("changed",
|
||||
self.servers_selection_changed)
|
||||
## argh coredump: self.servers_selection_changed(sel)
|
||||
self.xml.get_widget('suspend').set_sensitive(0)
|
||||
self.xml.get_widget('disconnect').set_sensitive(0)
|
||||
# setup model, connect it to my treeview
|
||||
self.model = gtk.ListStore(str, object, gobject.TYPE_BOOLEAN,
|
||||
gobject.TYPE_BOOLEAN)
|
||||
self.servers.set_model(self.model)
|
||||
self.servers.set_reorderable(1)
|
||||
self.servers.set_headers_clickable(1)
|
||||
# self.servers.set_headers_draggable(1)
|
||||
# add a column
|
||||
for col in [
|
||||
gtk.TreeViewColumn('Server',
|
||||
gtk.CellRendererText(),
|
||||
text=0),
|
||||
gtk.TreeViewColumn('Reading',
|
||||
gtk.CellRendererToggle(),
|
||||
active=2),
|
||||
gtk.TreeViewColumn('Writing',
|
||||
gtk.CellRendererToggle(),
|
||||
active=3)]:
|
||||
|
||||
self.servers.append_column(col)
|
||||
col.set_resizable(1)
|
||||
sup.__init__(self)
|
||||
|
||||
def addReader(self, reader):
|
||||
sup.addReader(self, reader)
|
||||
## gtk docs suggest this - but it's stupid
|
||||
## self.model.set(self.model.append(),
|
||||
## 0, str(reader),
|
||||
## 1, reader)
|
||||
self._maybeAddServer(reader, read=1)
|
||||
|
||||
def _goAway(self,reader):
|
||||
for p in range(len(self.model)):
|
||||
if self.model[p][1] == reader:
|
||||
self.model.remove(self.model.get_iter_from_string(str(p)))
|
||||
return
|
||||
|
||||
|
||||
def _maybeAddServer(self, reader, read=0, write=0):
|
||||
p = 0
|
||||
for x in self.model:
|
||||
if x[1] == reader:
|
||||
if reader == 0:
|
||||
reader += 1
|
||||
x[2] += read
|
||||
x[3] += write
|
||||
x[2] = max(x[2],0)
|
||||
x[3] = max(x[3],0)
|
||||
|
||||
if not (x[2] or x[3]):
|
||||
x[0] = x[0] + '(disconnected)'
|
||||
self.callLater(5, self._goAway, reader)
|
||||
return
|
||||
p += 1
|
||||
else:
|
||||
read = max(read,0)
|
||||
write = max(write, 0)
|
||||
if read or write:
|
||||
self.model.append((reader,reader,read,write))
|
||||
|
||||
def addWriter(self, writer):
|
||||
sup.addWriter(self, writer)
|
||||
self._maybeAddServer(writer, write=1)
|
||||
|
||||
def removeReader(self, reader):
|
||||
sup.removeReader(self, reader)
|
||||
self._maybeAddServer(reader, read=-1)
|
||||
|
||||
def removeWriter(self, writer):
|
||||
sup.removeWriter(self, writer)
|
||||
self._maybeAddServer(writer, write=-1)
|
||||
|
||||
def crash(self):
|
||||
gtk.main_quit()
|
||||
|
||||
def run(self, installSignalHandlers=1):
|
||||
self.startRunning(installSignalHandlers=installSignalHandlers)
|
||||
self.simulate()
|
||||
gtk.main()
|
||||
|
||||
|
||||
def install():
|
||||
"""Configure the twisted mainloop to be run inside the gtk mainloop.
|
||||
"""
|
||||
reactor = GladeReactor()
|
||||
from twisted.internet.main import installReactor
|
||||
installReactor(reactor)
|
||||
return reactor
|
||||
|
|
@ -0,0 +1,510 @@
|
|||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
<requires lib="gnome"/>
|
||||
<requires lib="bonobo"/>
|
||||
|
||||
<widget class="GnomeApp" id="app1">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">Inspectro</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="default_width">640</property>
|
||||
<property name="default_height">480</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<property name="enable_layout_config">True</property>
|
||||
|
||||
<child internal-child="dock">
|
||||
<widget class="BonoboDock" id="bonobodock1">
|
||||
<property name="visible">True</property>
|
||||
<property name="allow_floating">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="BonoboDockItem" id="bonobodockitem1">
|
||||
<property name="visible">True</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuBar" id="menubar1">
|
||||
<property name="visible">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="inspector1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Inspector</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="inspector1_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="select1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Select</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_select" last_modification_time="Wed, 17 Dec 2003 05:05:34 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="inspect1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Inspect</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_inspect" last_modification_time="Wed, 17 Dec 2003 05:05:34 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="inspect_new1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Inspect New</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_inspect_new" last_modification_time="Wed, 17 Dec 2003 05:05:34 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="help1">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock_item">GNOMEUIINFO_MENU_HELP_TREE</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="help1_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="about1">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock_item">GNOMEUIINFO_MENU_ABOUT_ITEM</property>
|
||||
<signal name="activate" handler="on_about1_activate" last_modification_time="Wed, 17 Dec 2003 04:48:59 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="placement">BONOBO_DOCK_TOP</property>
|
||||
<property name="band">0</property>
|
||||
<property name="position">0</property>
|
||||
<property name="offset">0</property>
|
||||
<property name="behavior">BONOBO_DOCK_ITEM_BEH_EXCLUSIVE|BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL|BONOBO_DOCK_ITEM_BEH_LOCKED</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="BonoboDockItem" id="bonobodockitem2">
|
||||
<property name="visible">True</property>
|
||||
<property name="shadow_type">GTK_SHADOW_OUT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkToolbar" id="toolbar2">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
|
||||
<property name="tooltips">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="button" id="button13">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Select</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_pixmap">gtk-convert</property>
|
||||
<signal name="clicked" handler="on_select" last_modification_time="Wed, 17 Dec 2003 05:05:14 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="button" id="button14">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Inspect</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_pixmap">gtk-jump-to</property>
|
||||
<signal name="clicked" handler="on_inspect" last_modification_time="Wed, 17 Dec 2003 05:05:02 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="button" id="button15">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Inspect New</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_pixmap">gtk-redo</property>
|
||||
<signal name="clicked" handler="on_inspect_new" last_modification_time="Wed, 17 Dec 2003 05:04:50 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="placement">BONOBO_DOCK_TOP</property>
|
||||
<property name="band">1</property>
|
||||
<property name="position">0</property>
|
||||
<property name="offset">0</property>
|
||||
<property name="behavior">BONOBO_DOCK_ITEM_BEH_EXCLUSIVE</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHPaned" id="hpaned1">
|
||||
<property name="width_request">350</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="position">250</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">True</property>
|
||||
<property name="rules_hint">False</property>
|
||||
<property name="reorderable">False</property>
|
||||
<property name="enable_search">True</property>
|
||||
<signal name="row_activated" handler="on_row_activated" last_modification_time="Wed, 17 Dec 2003 05:07:55 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTable" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">2</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="row_spacing">0</property>
|
||||
<property name="column_spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="itname">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">None</property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="bottom_attach">1</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="itpath">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">[]</property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">It: </property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_RIGHT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="bottom_attach">1</property>
|
||||
<property name="x_padding">3</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Path: </property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_RIGHT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_padding">3</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">3</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">True</property>
|
||||
<property name="resize">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVPaned" id="vpaned1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="position">303</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTextView" id="output">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap_mode">GTK_WRAP_NONE</property>
|
||||
<property name="cursor_visible">True</property>
|
||||
<property name="pixels_above_lines">0</property>
|
||||
<property name="pixels_below_lines">0</property>
|
||||
<property name="pixels_inside_wrap">0</property>
|
||||
<property name="left_margin">0</property>
|
||||
<property name="right_margin">0</property>
|
||||
<property name="indent">0</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">False</property>
|
||||
<property name="resize">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkViewport" id="viewport1">
|
||||
<property name="visible">True</property>
|
||||
<property name="shadow_type">GTK_SHADOW_IN</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="button16">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<signal name="clicked" handler="on_execute" last_modification_time="Wed, 17 Dec 2003 05:06:44 GMT"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xscale">0</property>
|
||||
<property name="yscale">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">2</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImage" id="image2">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-execute</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label6">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">>>></property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTextView" id="input">
|
||||
<property name="height_request">25</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_focus">True</property>
|
||||
<property name="editable">True</property>
|
||||
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap_mode">GTK_WRAP_NONE</property>
|
||||
<property name="cursor_visible">True</property>
|
||||
<property name="pixels_above_lines">0</property>
|
||||
<property name="pixels_below_lines">0</property>
|
||||
<property name="pixels_inside_wrap">0</property>
|
||||
<property name="left_margin">0</property>
|
||||
<property name="right_margin">0</property>
|
||||
<property name="indent">0</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">False</property>
|
||||
<property name="resize">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">True</property>
|
||||
<property name="resize">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child internal-child="appbar">
|
||||
<widget class="GnomeAppBar" id="appbar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="has_progress">False</property>
|
||||
<property name="has_status">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
|
||||
<widget class="GtkWindow" id="logview">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">Log</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<signal name="destroy" handler="on_logview_destroy" last_modification_time="Sun, 04 Jan 2004 22:16:59 GMT"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="loglist">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">True</property>
|
||||
<property name="rules_hint">False</property>
|
||||
<property name="reorderable">False</property>
|
||||
<property name="enable_search">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
|
|
@ -0,0 +1,395 @@
|
|||
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
|
||||
"""L{twisted.manhole} L{PB<twisted.spread.pb>} service implementation.
|
||||
"""
|
||||
|
||||
# twisted imports
|
||||
from twisted import copyright
|
||||
from twisted.spread import pb
|
||||
from twisted.python import log, failure
|
||||
from twisted.cred import portal
|
||||
from twisted.application import service
|
||||
from zope.interface import implements, Interface
|
||||
|
||||
# sibling imports
|
||||
import explorer
|
||||
|
||||
import string
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
||||
class FakeStdIO:
|
||||
def __init__(self, type_, list):
|
||||
self.type = type_
|
||||
self.list = list
|
||||
|
||||
def write(self, text):
|
||||
log.msg("%s: %s" % (self.type, string.strip(str(text))))
|
||||
self.list.append((self.type, text))
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def consolidate(self):
|
||||
"""Concatenate adjacent messages of same type into one.
|
||||
|
||||
Greatly cuts down on the number of elements, increasing
|
||||
network transport friendliness considerably.
|
||||
"""
|
||||
if not self.list:
|
||||
return
|
||||
|
||||
inlist = self.list
|
||||
outlist = []
|
||||
last_type = inlist[0]
|
||||
block_begin = 0
|
||||
for i in xrange(1, len(self.list)):
|
||||
(mtype, message) = inlist[i]
|
||||
if mtype == last_type:
|
||||
continue
|
||||
else:
|
||||
if (i - block_begin) == 1:
|
||||
outlist.append(inlist[block_begin])
|
||||
else:
|
||||
messages = map(lambda l: l[1],
|
||||
inlist[block_begin:i])
|
||||
message = string.join(messages, '')
|
||||
outlist.append((last_type, message))
|
||||
last_type = mtype
|
||||
block_begin = i
|
||||
|
||||
|
||||
class IManholeClient(Interface):
|
||||
def console(list_of_messages):
|
||||
"""Takes a list of (type, message) pairs to display.
|
||||
|
||||
Types include:
|
||||
- \"stdout\" -- string sent to sys.stdout
|
||||
|
||||
- \"stderr\" -- string sent to sys.stderr
|
||||
|
||||
- \"result\" -- string repr of the resulting value
|
||||
of the expression
|
||||
|
||||
- \"exception\" -- a L{failure.Failure}
|
||||
"""
|
||||
|
||||
def receiveExplorer(xplorer):
|
||||
"""Receives an explorer.Explorer
|
||||
"""
|
||||
|
||||
def listCapabilities():
|
||||
"""List what manholey things I am capable of doing.
|
||||
|
||||
i.e. C{\"Explorer\"}, C{\"Failure\"}
|
||||
"""
|
||||
|
||||
def runInConsole(command, console, globalNS=None, localNS=None,
|
||||
filename=None, args=None, kw=None, unsafeTracebacks=False):
|
||||
"""Run this, directing all output to the specified console.
|
||||
|
||||
If command is callable, it will be called with the args and keywords
|
||||
provided. Otherwise, command will be compiled and eval'd.
|
||||
(Wouldn't you like a macro?)
|
||||
|
||||
Returns the command's return value.
|
||||
|
||||
The console is called with a list of (type, message) pairs for
|
||||
display, see L{IManholeClient.console}.
|
||||
"""
|
||||
output = []
|
||||
fakeout = FakeStdIO("stdout", output)
|
||||
fakeerr = FakeStdIO("stderr", output)
|
||||
errfile = FakeStdIO("exception", output)
|
||||
code = None
|
||||
val = None
|
||||
if filename is None:
|
||||
filename = str(console)
|
||||
if args is None:
|
||||
args = ()
|
||||
if kw is None:
|
||||
kw = {}
|
||||
if localNS is None:
|
||||
localNS = globalNS
|
||||
if (globalNS is None) and (not callable(command)):
|
||||
raise ValueError("Need a namespace to evaluate the command in.")
|
||||
|
||||
try:
|
||||
out = sys.stdout
|
||||
err = sys.stderr
|
||||
sys.stdout = fakeout
|
||||
sys.stderr = fakeerr
|
||||
try:
|
||||
if callable(command):
|
||||
val = apply(command, args, kw)
|
||||
else:
|
||||
try:
|
||||
code = compile(command, filename, 'eval')
|
||||
except:
|
||||
code = compile(command, filename, 'single')
|
||||
|
||||
if code:
|
||||
val = eval(code, globalNS, localNS)
|
||||
finally:
|
||||
sys.stdout = out
|
||||
sys.stderr = err
|
||||
except:
|
||||
(eType, eVal, tb) = sys.exc_info()
|
||||
fail = failure.Failure(eVal, eType, tb)
|
||||
del tb
|
||||
# In CVS reversion 1.35, there was some code here to fill in the
|
||||
# source lines in the traceback for frames in the local command
|
||||
# buffer. But I can't figure out when that's triggered, so it's
|
||||
# going away in the conversion to Failure, until you bring it back.
|
||||
errfile.write(pb.failure2Copyable(fail, unsafeTracebacks))
|
||||
|
||||
if console:
|
||||
fakeout.consolidate()
|
||||
console(output)
|
||||
|
||||
return val
|
||||
|
||||
def _failureOldStyle(fail):
|
||||
"""Pre-Failure manhole representation of exceptions.
|
||||
|
||||
For compatibility with manhole clients without the \"Failure\"
|
||||
capability.
|
||||
|
||||
A dictionary with two members:
|
||||
- \'traceback\' -- traceback.extract_tb output; a list of tuples
|
||||
(filename, line number, function name, text) suitable for
|
||||
feeding to traceback.format_list.
|
||||
|
||||
- \'exception\' -- a list of one or more strings, each
|
||||
ending in a newline. (traceback.format_exception_only output)
|
||||
"""
|
||||
import linecache
|
||||
tb = []
|
||||
for f in fail.frames:
|
||||
# (filename, line number, function name, text)
|
||||
tb.append((f[1], f[2], f[0], linecache.getline(f[1], f[2])))
|
||||
|
||||
return {
|
||||
'traceback': tb,
|
||||
'exception': traceback.format_exception_only(fail.type, fail.value)
|
||||
}
|
||||
|
||||
# Capabilities clients are likely to have before they knew how to answer a
|
||||
# "listCapabilities" query.
|
||||
_defaultCapabilities = {
|
||||
"Explorer": 'Set'
|
||||
}
|
||||
|
||||
class Perspective(pb.Avatar):
|
||||
lastDeferred = 0
|
||||
def __init__(self, service):
|
||||
self.localNamespace = {
|
||||
"service": service,
|
||||
"avatar": self,
|
||||
"_": None,
|
||||
}
|
||||
self.clients = {}
|
||||
self.service = service
|
||||
|
||||
def __getstate__(self):
|
||||
state = self.__dict__.copy()
|
||||
state['clients'] = {}
|
||||
if state['localNamespace'].has_key("__builtins__"):
|
||||
del state['localNamespace']['__builtins__']
|
||||
return state
|
||||
|
||||
def attached(self, client, identity):
|
||||
"""A client has attached -- welcome them and add them to the list.
|
||||
"""
|
||||
self.clients[client] = identity
|
||||
|
||||
host = ':'.join(map(str, client.broker.transport.getHost()[1:]))
|
||||
|
||||
msg = self.service.welcomeMessage % {
|
||||
'you': getattr(identity, 'name', str(identity)),
|
||||
'host': host,
|
||||
'longversion': copyright.longversion,
|
||||
}
|
||||
|
||||
client.callRemote('console', [("stdout", msg)])
|
||||
|
||||
client.capabilities = _defaultCapabilities
|
||||
client.callRemote('listCapabilities').addCallbacks(
|
||||
self._cbClientCapable, self._ebClientCapable,
|
||||
callbackArgs=(client,),errbackArgs=(client,))
|
||||
|
||||
def detached(self, client, identity):
|
||||
try:
|
||||
del self.clients[client]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def runInConsole(self, command, *args, **kw):
|
||||
"""Convience method to \"runInConsole with my stuff\".
|
||||
"""
|
||||
return runInConsole(command,
|
||||
self.console,
|
||||
self.service.namespace,
|
||||
self.localNamespace,
|
||||
str(self.service),
|
||||
args=args,
|
||||
kw=kw,
|
||||
unsafeTracebacks=self.service.unsafeTracebacks)
|
||||
|
||||
|
||||
### Methods for communicating to my clients.
|
||||
|
||||
def console(self, message):
|
||||
"""Pass a message to my clients' console.
|
||||
"""
|
||||
clients = self.clients.keys()
|
||||
origMessage = message
|
||||
compatMessage = None
|
||||
for client in clients:
|
||||
try:
|
||||
if "Failure" not in client.capabilities:
|
||||
if compatMessage is None:
|
||||
compatMessage = origMessage[:]
|
||||
for i in xrange(len(message)):
|
||||
if ((message[i][0] == "exception") and
|
||||
isinstance(message[i][1], failure.Failure)):
|
||||
compatMessage[i] = (
|
||||
message[i][0],
|
||||
_failureOldStyle(message[i][1]))
|
||||
client.callRemote('console', compatMessage)
|
||||
else:
|
||||
client.callRemote('console', message)
|
||||
except pb.ProtocolError:
|
||||
# Stale broker.
|
||||
self.detached(client, None)
|
||||
|
||||
def receiveExplorer(self, objectLink):
|
||||
"""Pass an Explorer on to my clients.
|
||||
"""
|
||||
clients = self.clients.keys()
|
||||
for client in clients:
|
||||
try:
|
||||
client.callRemote('receiveExplorer', objectLink)
|
||||
except pb.ProtocolError:
|
||||
# Stale broker.
|
||||
self.detached(client, None)
|
||||
|
||||
|
||||
def _cbResult(self, val, dnum):
|
||||
self.console([('result', "Deferred #%s Result: %r\n" %(dnum, val))])
|
||||
return val
|
||||
|
||||
def _cbClientCapable(self, capabilities, client):
|
||||
log.msg("client %x has %s" % (id(client), capabilities))
|
||||
client.capabilities = capabilities
|
||||
|
||||
def _ebClientCapable(self, reason, client):
|
||||
reason.trap(AttributeError)
|
||||
log.msg("Couldn't get capabilities from %s, assuming defaults." %
|
||||
(client,))
|
||||
|
||||
### perspective_ methods, commands used by the client.
|
||||
|
||||
def perspective_do(self, expr):
|
||||
"""Evaluate the given expression, with output to the console.
|
||||
|
||||
The result is stored in the local variable '_', and its repr()
|
||||
string is sent to the console as a \"result\" message.
|
||||
"""
|
||||
log.msg(">>> %s" % expr)
|
||||
val = self.runInConsole(expr)
|
||||
if val is not None:
|
||||
self.localNamespace["_"] = val
|
||||
from twisted.internet.defer import Deferred
|
||||
# TODO: client support for Deferred.
|
||||
if isinstance(val, Deferred):
|
||||
self.lastDeferred += 1
|
||||
self.console([('result', "Waiting for Deferred #%s...\n" % self.lastDeferred)])
|
||||
val.addBoth(self._cbResult, self.lastDeferred)
|
||||
else:
|
||||
self.console([("result", repr(val) + '\n')])
|
||||
log.msg("<<<")
|
||||
|
||||
def perspective_explore(self, identifier):
|
||||
"""Browse the object obtained by evaluating the identifier.
|
||||
|
||||
The resulting ObjectLink is passed back through the client's
|
||||
receiveBrowserObject method.
|
||||
"""
|
||||
object = self.runInConsole(identifier)
|
||||
if object:
|
||||
expl = explorer.explorerPool.getExplorer(object, identifier)
|
||||
self.receiveExplorer(expl)
|
||||
|
||||
def perspective_watch(self, identifier):
|
||||
"""Watch the object obtained by evaluating the identifier.
|
||||
|
||||
Whenever I think this object might have changed, I will pass
|
||||
an ObjectLink of it back to the client's receiveBrowserObject
|
||||
method.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
object = self.runInConsole(identifier)
|
||||
if object:
|
||||
# Return an ObjectLink of this right away, before the watch.
|
||||
oLink = self.runInConsole(self.browser.browseObject,
|
||||
object, identifier)
|
||||
self.receiveExplorer(oLink)
|
||||
|
||||
self.runInConsole(self.browser.watchObject,
|
||||
object, identifier,
|
||||
self.receiveExplorer)
|
||||
|
||||
|
||||
class Realm:
|
||||
|
||||
implements(portal.IRealm)
|
||||
|
||||
def __init__(self, service):
|
||||
self.service = service
|
||||
self._cache = {}
|
||||
|
||||
def requestAvatar(self, avatarId, mind, *interfaces):
|
||||
if pb.IPerspective not in interfaces:
|
||||
raise NotImplementedError("no interface")
|
||||
if avatarId in self._cache:
|
||||
p = self._cache[avatarId]
|
||||
else:
|
||||
p = Perspective(self.service)
|
||||
p.attached(mind, avatarId)
|
||||
def detached():
|
||||
p.detached(mind, avatarId)
|
||||
return (pb.IPerspective, p, detached)
|
||||
|
||||
|
||||
class Service(service.Service):
|
||||
|
||||
welcomeMessage = (
|
||||
"\nHello %(you)s, welcome to Manhole "
|
||||
"on %(host)s.\n"
|
||||
"%(longversion)s.\n\n")
|
||||
|
||||
def __init__(self, unsafeTracebacks=False, namespace=None):
|
||||
self.unsafeTracebacks = unsafeTracebacks
|
||||
self.namespace = {
|
||||
'__name__': '__manhole%x__' % (id(self),),
|
||||
'sys': sys
|
||||
}
|
||||
if namespace:
|
||||
self.namespace.update(namespace)
|
||||
|
||||
def __getstate__(self):
|
||||
"""This returns the persistent state of this shell factory.
|
||||
"""
|
||||
# TODO -- refactor this and twisted.reality.author.Author to
|
||||
# use common functionality (perhaps the 'code' module?)
|
||||
dict = self.__dict__.copy()
|
||||
ns = dict['namespace'].copy()
|
||||
dict['namespace'] = ns
|
||||
if ns.has_key('__builtins__'):
|
||||
del ns['__builtins__']
|
||||
return dict
|
||||
115
Linux_i686/lib/python2.7/site-packages/twisted/manhole/telnet.py
Normal file
115
Linux_i686/lib/python2.7/site-packages/twisted/manhole/telnet.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
|
||||
"""Telnet-based shell."""
|
||||
|
||||
# twisted imports
|
||||
from twisted.protocols import telnet
|
||||
from twisted.internet import protocol
|
||||
from twisted.python import log, failure
|
||||
|
||||
# system imports
|
||||
import string, copy, sys
|
||||
|
||||
|
||||
class Shell(telnet.Telnet):
|
||||
"""A Python command-line shell."""
|
||||
|
||||
def connectionMade(self):
|
||||
telnet.Telnet.connectionMade(self)
|
||||
self.lineBuffer = []
|
||||
|
||||
def loggedIn(self):
|
||||
self.transport.write(">>> ")
|
||||
|
||||
def checkUserAndPass(self, username, password):
|
||||
return ((self.factory.username == username) and (password == self.factory.password))
|
||||
|
||||
def write(self, data):
|
||||
"""Write some data to the transport.
|
||||
"""
|
||||
self.transport.write(data)
|
||||
|
||||
def telnet_Command(self, cmd):
|
||||
if self.lineBuffer:
|
||||
if not cmd:
|
||||
cmd = string.join(self.lineBuffer, '\n') + '\n\n\n'
|
||||
self.doCommand(cmd)
|
||||
self.lineBuffer = []
|
||||
return "Command"
|
||||
else:
|
||||
self.lineBuffer.append(cmd)
|
||||
self.transport.write("... ")
|
||||
return "Command"
|
||||
else:
|
||||
self.doCommand(cmd)
|
||||
return "Command"
|
||||
|
||||
def doCommand(self, cmd):
|
||||
|
||||
# TODO -- refactor this, Reality.author.Author, and the manhole shell
|
||||
#to use common functionality (perhaps a twisted.python.code module?)
|
||||
fn = '$telnet$'
|
||||
result = None
|
||||
try:
|
||||
out = sys.stdout
|
||||
sys.stdout = self
|
||||
try:
|
||||
code = compile(cmd,fn,'eval')
|
||||
result = eval(code, self.factory.namespace)
|
||||
except:
|
||||
try:
|
||||
code = compile(cmd, fn, 'exec')
|
||||
exec code in self.factory.namespace
|
||||
except SyntaxError, e:
|
||||
if not self.lineBuffer and str(e)[:14] == "unexpected EOF":
|
||||
self.lineBuffer.append(cmd)
|
||||
self.transport.write("... ")
|
||||
return
|
||||
else:
|
||||
failure.Failure().printTraceback(file=self)
|
||||
log.deferr()
|
||||
self.write('\r\n>>> ')
|
||||
return
|
||||
except:
|
||||
failure.Failure().printTraceback(file=self)
|
||||
log.deferr()
|
||||
self.write('\r\n>>> ')
|
||||
return
|
||||
finally:
|
||||
sys.stdout = out
|
||||
|
||||
self.factory.namespace['_'] = result
|
||||
if result is not None:
|
||||
self.transport.write(repr(result))
|
||||
self.transport.write('\r\n')
|
||||
self.transport.write(">>> ")
|
||||
|
||||
|
||||
|
||||
class ShellFactory(protocol.Factory):
|
||||
username = "admin"
|
||||
password = "admin"
|
||||
protocol = Shell
|
||||
service = None
|
||||
|
||||
def __init__(self):
|
||||
self.namespace = {
|
||||
'factory': self,
|
||||
'service': None,
|
||||
'_': None
|
||||
}
|
||||
|
||||
def setService(self, service):
|
||||
self.namespace['service'] = self.service = service
|
||||
|
||||
def __getstate__(self):
|
||||
"""This returns the persistent state of this shell factory.
|
||||
"""
|
||||
dict = self.__dict__
|
||||
ns = copy.copy(dict['namespace'])
|
||||
dict['namespace'] = ns
|
||||
if ns.has_key('__builtins__'):
|
||||
del ns['__builtins__']
|
||||
return dict
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.manhole}.
|
||||
"""
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.manhole.explorer}.
|
||||
"""
|
||||
|
||||
from twisted.trial import unittest
|
||||
from twisted.manhole.explorer import (
|
||||
CRUFT_WatchyThingie,
|
||||
ExplorerImmutable,
|
||||
Pool,
|
||||
_WatchMonkey,
|
||||
)
|
||||
|
||||
|
||||
class Foo:
|
||||
"""
|
||||
Test helper.
|
||||
"""
|
||||
|
||||
|
||||
class PoolTestCase(unittest.TestCase):
|
||||
"""
|
||||
Tests for the Pool class.
|
||||
"""
|
||||
|
||||
def test_instanceBuilding(self):
|
||||
"""
|
||||
If the object is not in the pool a new instance is created and
|
||||
returned.
|
||||
"""
|
||||
p = Pool()
|
||||
e = p.getExplorer(123, 'id')
|
||||
self.assertIsInstance(e, ExplorerImmutable)
|
||||
self.assertEqual(e.value, 123)
|
||||
self.assertEqual(e.identifier, 'id')
|
||||
|
||||
|
||||
|
||||
class CRUFTWatchyThingieTestCase(unittest.TestCase):
|
||||
"""
|
||||
Tests for the CRUFT_WatchyThingie class.
|
||||
"""
|
||||
def test_watchObjectConstructedClass(self):
|
||||
"""
|
||||
L{CRUFT_WatchyThingie.watchObject} changes the class of its
|
||||
first argument to a custom watching class.
|
||||
"""
|
||||
foo = Foo()
|
||||
cwt = CRUFT_WatchyThingie()
|
||||
cwt.watchObject(foo, 'id', 'cback')
|
||||
|
||||
# check new constructed class
|
||||
newClassName = foo.__class__.__name__
|
||||
self.assertEqual(newClassName, "WatchingFoo%X" % (id(foo),))
|
||||
|
||||
|
||||
def test_watchObjectConstructedInstanceMethod(self):
|
||||
"""
|
||||
L{CRUFT_WatchyThingie.watchingfoo} adds a C{_watchEmitChanged}
|
||||
attribute which refers to a bound method on the instance
|
||||
passed to it.
|
||||
"""
|
||||
foo = Foo()
|
||||
cwt = CRUFT_WatchyThingie()
|
||||
cwt.watchObject(foo, 'id', 'cback')
|
||||
|
||||
# check new constructed instance method
|
||||
self.assertIdentical(foo._watchEmitChanged.im_self, foo)
|
||||
|
||||
|
||||
|
||||
class WatchMonkeyTestCase(unittest.TestCase):
|
||||
"""
|
||||
Tests for the _WatchMonkey class.
|
||||
"""
|
||||
def test_install(self):
|
||||
"""
|
||||
When _WatchMonkey is installed on a method, calling that
|
||||
method calls the _WatchMonkey.
|
||||
"""
|
||||
class Foo:
|
||||
"""
|
||||
Helper.
|
||||
"""
|
||||
def someMethod(self):
|
||||
"""
|
||||
Just a method.
|
||||
"""
|
||||
|
||||
foo = Foo()
|
||||
wm = _WatchMonkey(foo)
|
||||
wm.install('someMethod')
|
||||
|
||||
# patch wm's method to check that the method was exchanged
|
||||
called = []
|
||||
wm.__call__ = lambda s: called.append(True)
|
||||
|
||||
# call and check
|
||||
foo.someMethod()
|
||||
self.assertTrue(called)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
|
||||
"""Twisted Manhole UI: User interface for direct manipulation in Twisted.
|
||||
"""
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
|
||||
<widget class="GtkWindow" id="manholeWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">Manhole</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="default_width">620</property>
|
||||
<property name="default_height">320</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<property name="decorated">True</property>
|
||||
<property name="skip_taskbar_hint">False</property>
|
||||
<property name="skip_pager_hint">False</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||
<signal name="delete_event" handler="_on_manholeWindow_delete_event" last_modification_time="Mon, 27 Jan 2003 05:14:26 GMT"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuBar" id="menubar1">
|
||||
<property name="visible">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem4">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_File</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem4_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="openMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-open</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="_on_openMenuItem_activate" last_modification_time="Sun, 02 Feb 2003 18:44:51 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="reload_self">
|
||||
<property name="visible">True</property>
|
||||
<property name="tooltip" translatable="yes">Reload the manhole client code. (Only useful for client development.)</property>
|
||||
<property name="label" translatable="yes">_Reload self</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_reload_self_activate" last_modification_time="Mon, 24 Feb 2003 00:15:10 GMT"/>
|
||||
|
||||
<child internal-child="image">
|
||||
<widget class="GtkImage" id="image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-revert-to-saved</property>
|
||||
<property name="icon_size">1</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="separatormenuitem1">
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="quitMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-quit</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="_on_quitMenuItem_activate" last_modification_time="Sun, 02 Feb 2003 18:48:12 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem5">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Edit</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem5_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="cut1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-cut</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_cut1_activate" last_modification_time="Mon, 27 Jan 2003 04:50:50 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="copy1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-copy</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_copy1_activate" last_modification_time="Mon, 27 Jan 2003 04:50:50 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="paste1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-paste</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_paste1_activate" last_modification_time="Mon, 27 Jan 2003 04:50:50 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="delete1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-delete</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_delete1_activate" last_modification_time="Mon, 27 Jan 2003 04:50:50 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem7">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Help</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem7_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="aboutMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_About</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="_on_aboutMenuItem_activate" last_modification_time="Thu, 06 Feb 2003 19:49:53 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVPaned" id="vpaned1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTextView" id="output">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="overwrite">False</property>
|
||||
<property name="accepts_tab">True</property>
|
||||
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap_mode">GTK_WRAP_WORD</property>
|
||||
<property name="cursor_visible">True</property>
|
||||
<property name="pixels_above_lines">0</property>
|
||||
<property name="pixels_below_lines">0</property>
|
||||
<property name="pixels_inside_wrap">0</property>
|
||||
<property name="left_margin">0</property>
|
||||
<property name="right_margin">0</property>
|
||||
<property name="indent">0</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">True</property>
|
||||
<property name="resize">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTextView" id="input">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_focus">True</property>
|
||||
<property name="editable">True</property>
|
||||
<property name="overwrite">False</property>
|
||||
<property name="accepts_tab">True</property>
|
||||
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap_mode">GTK_WRAP_NONE</property>
|
||||
<property name="cursor_visible">True</property>
|
||||
<property name="pixels_above_lines">0</property>
|
||||
<property name="pixels_below_lines">0</property>
|
||||
<property name="pixels_inside_wrap">0</property>
|
||||
<property name="left_margin">0</property>
|
||||
<property name="right_margin">0</property>
|
||||
<property name="indent">0</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">True</property>
|
||||
<property name="resize">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkStatusbar" id="statusbar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="has_resize_grip">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
# -*- test-case-name: twisted.manhole.ui.test.test_gtk2manhole -*-
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Manhole client with a GTK v2.x front-end.
|
||||
"""
|
||||
|
||||
__version__ = '$Revision: 1.9 $'[11:-2]
|
||||
|
||||
from twisted import copyright
|
||||
from twisted.internet import reactor
|
||||
from twisted.python import components, failure, log, util
|
||||
from twisted.python.reflect import prefixedMethodNames
|
||||
from twisted.spread import pb
|
||||
from twisted.spread.ui import gtk2util
|
||||
|
||||
from twisted.manhole.service import IManholeClient
|
||||
from zope.interface import implements
|
||||
|
||||
# The pygtk.require for version 2.0 has already been done by the reactor.
|
||||
import gtk
|
||||
|
||||
import code, types, inspect
|
||||
|
||||
# TODO:
|
||||
# Make wrap-mode a run-time option.
|
||||
# Explorer.
|
||||
# Code doesn't cleanly handle opening a second connection. Fix that.
|
||||
# Make some acknowledgement of when a command has completed, even if
|
||||
# it has no return value so it doesn't print anything to the console.
|
||||
|
||||
class OfflineError(Exception):
|
||||
pass
|
||||
|
||||
class ManholeWindow(components.Componentized, gtk2util.GladeKeeper):
|
||||
gladefile = util.sibpath(__file__, "gtk2manhole.glade")
|
||||
|
||||
_widgets = ('input','output','manholeWindow')
|
||||
|
||||
def __init__(self):
|
||||
self.defaults = {}
|
||||
gtk2util.GladeKeeper.__init__(self)
|
||||
components.Componentized.__init__(self)
|
||||
|
||||
self.input = ConsoleInput(self._input)
|
||||
self.input.toplevel = self
|
||||
self.output = ConsoleOutput(self._output)
|
||||
|
||||
# Ugh. GladeKeeper actually isn't so good for composite objects.
|
||||
# I want this connected to the ConsoleInput's handler, not something
|
||||
# on this class.
|
||||
self._input.connect("key_press_event", self.input._on_key_press_event)
|
||||
|
||||
def setDefaults(self, defaults):
|
||||
self.defaults = defaults
|
||||
|
||||
def login(self):
|
||||
client = self.getComponent(IManholeClient)
|
||||
d = gtk2util.login(client, **self.defaults)
|
||||
d.addCallback(self._cbLogin)
|
||||
d.addCallback(client._cbLogin)
|
||||
d.addErrback(self._ebLogin)
|
||||
|
||||
def _cbDisconnected(self, perspective):
|
||||
self.output.append("%s went away. :(\n" % (perspective,), "local")
|
||||
self._manholeWindow.set_title("Manhole")
|
||||
|
||||
def _cbLogin(self, perspective):
|
||||
peer = perspective.broker.transport.getPeer()
|
||||
self.output.append("Connected to %s\n" % (peer,), "local")
|
||||
perspective.notifyOnDisconnect(self._cbDisconnected)
|
||||
self._manholeWindow.set_title("Manhole - %s" % (peer))
|
||||
return perspective
|
||||
|
||||
def _ebLogin(self, reason):
|
||||
self.output.append("Login FAILED %s\n" % (reason.value,), "exception")
|
||||
|
||||
def _on_aboutMenuItem_activate(self, widget, *unused):
|
||||
import sys
|
||||
from os import path
|
||||
self.output.append("""\
|
||||
a Twisted Manhole client
|
||||
Versions:
|
||||
%(twistedVer)s
|
||||
Python %(pythonVer)s on %(platform)s
|
||||
GTK %(gtkVer)s / PyGTK %(pygtkVer)s
|
||||
%(module)s %(modVer)s
|
||||
http://twistedmatrix.com/
|
||||
""" % {'twistedVer': copyright.longversion,
|
||||
'pythonVer': sys.version.replace('\n', '\n '),
|
||||
'platform': sys.platform,
|
||||
'gtkVer': ".".join(map(str, gtk.gtk_version)),
|
||||
'pygtkVer': ".".join(map(str, gtk.pygtk_version)),
|
||||
'module': path.basename(__file__),
|
||||
'modVer': __version__,
|
||||
}, "local")
|
||||
|
||||
def _on_openMenuItem_activate(self, widget, userdata=None):
|
||||
self.login()
|
||||
|
||||
def _on_manholeWindow_delete_event(self, widget, *unused):
|
||||
reactor.stop()
|
||||
|
||||
def _on_quitMenuItem_activate(self, widget, *unused):
|
||||
reactor.stop()
|
||||
|
||||
def on_reload_self_activate(self, *unused):
|
||||
from twisted.python import rebuild
|
||||
rebuild.rebuild(inspect.getmodule(self.__class__))
|
||||
|
||||
|
||||
tagdefs = {
|
||||
'default': {"family": "monospace"},
|
||||
# These are message types we get from the server.
|
||||
'stdout': {"foreground": "black"},
|
||||
'stderr': {"foreground": "#AA8000"},
|
||||
'result': {"foreground": "blue"},
|
||||
'exception': {"foreground": "red"},
|
||||
# Messages generate locally.
|
||||
'local': {"foreground": "#008000"},
|
||||
'log': {"foreground": "#000080"},
|
||||
'command': {"foreground": "#666666"},
|
||||
}
|
||||
|
||||
# TODO: Factor Python console stuff back out to pywidgets.
|
||||
|
||||
class ConsoleOutput:
|
||||
_willScroll = None
|
||||
def __init__(self, textView):
|
||||
self.textView = textView
|
||||
self.buffer = textView.get_buffer()
|
||||
|
||||
# TODO: Make this a singleton tag table.
|
||||
for name, props in tagdefs.iteritems():
|
||||
tag = self.buffer.create_tag(name)
|
||||
# This can be done in the constructor in newer pygtk (post 1.99.14)
|
||||
for k, v in props.iteritems():
|
||||
tag.set_property(k, v)
|
||||
|
||||
self.buffer.tag_table.lookup("default").set_priority(0)
|
||||
|
||||
self._captureLocalLog()
|
||||
|
||||
def _captureLocalLog(self):
|
||||
return log.startLogging(_Notafile(self, "log"), setStdout=False)
|
||||
|
||||
def append(self, text, kind=None):
|
||||
# XXX: It seems weird to have to do this thing with always applying
|
||||
# a 'default' tag. Can't we change the fundamental look instead?
|
||||
tags = ["default"]
|
||||
if kind is not None:
|
||||
tags.append(kind)
|
||||
|
||||
self.buffer.insert_with_tags_by_name(self.buffer.get_end_iter(),
|
||||
text, *tags)
|
||||
# Silly things, the TextView needs to update itself before it knows
|
||||
# where the bottom is.
|
||||
if self._willScroll is None:
|
||||
self._willScroll = gtk.idle_add(self._scrollDown)
|
||||
|
||||
def _scrollDown(self, *unused):
|
||||
self.textView.scroll_to_iter(self.buffer.get_end_iter(), 0,
|
||||
True, 1.0, 1.0)
|
||||
self._willScroll = None
|
||||
return False
|
||||
|
||||
class History:
|
||||
def __init__(self, maxhist=10000):
|
||||
self.ringbuffer = ['']
|
||||
self.maxhist = maxhist
|
||||
self.histCursor = 0
|
||||
|
||||
def append(self, htext):
|
||||
self.ringbuffer.insert(-1, htext)
|
||||
if len(self.ringbuffer) > self.maxhist:
|
||||
self.ringbuffer.pop(0)
|
||||
self.histCursor = len(self.ringbuffer) - 1
|
||||
self.ringbuffer[-1] = ''
|
||||
|
||||
def move(self, prevnext=1):
|
||||
'''
|
||||
Return next/previous item in the history, stopping at top/bottom.
|
||||
'''
|
||||
hcpn = self.histCursor + prevnext
|
||||
if hcpn >= 0 and hcpn < len(self.ringbuffer):
|
||||
self.histCursor = hcpn
|
||||
return self.ringbuffer[hcpn]
|
||||
else:
|
||||
return None
|
||||
|
||||
def histup(self, textbuffer):
|
||||
if self.histCursor == len(self.ringbuffer) - 1:
|
||||
si, ei = textbuffer.get_start_iter(), textbuffer.get_end_iter()
|
||||
self.ringbuffer[-1] = textbuffer.get_text(si,ei)
|
||||
newtext = self.move(-1)
|
||||
if newtext is None:
|
||||
return
|
||||
textbuffer.set_text(newtext)
|
||||
|
||||
def histdown(self, textbuffer):
|
||||
newtext = self.move(1)
|
||||
if newtext is None:
|
||||
return
|
||||
textbuffer.set_text(newtext)
|
||||
|
||||
|
||||
class ConsoleInput:
|
||||
toplevel, rkeymap = None, None
|
||||
__debug = False
|
||||
|
||||
def __init__(self, textView):
|
||||
self.textView=textView
|
||||
self.rkeymap = {}
|
||||
self.history = History()
|
||||
for name in prefixedMethodNames(self.__class__, "key_"):
|
||||
keysymName = name.split("_")[-1]
|
||||
self.rkeymap[getattr(gtk.keysyms, keysymName)] = keysymName
|
||||
|
||||
def _on_key_press_event(self, entry, event):
|
||||
ksym = self.rkeymap.get(event.keyval, None)
|
||||
|
||||
mods = []
|
||||
for prefix, mask in [('ctrl', gtk.gdk.CONTROL_MASK), ('shift', gtk.gdk.SHIFT_MASK)]:
|
||||
if event.state & mask:
|
||||
mods.append(prefix)
|
||||
|
||||
if mods:
|
||||
ksym = '_'.join(mods + [ksym])
|
||||
|
||||
if ksym:
|
||||
rvalue = getattr(
|
||||
self, 'key_%s' % ksym, lambda *a, **kw: None)(entry, event)
|
||||
|
||||
if self.__debug:
|
||||
print ksym
|
||||
return rvalue
|
||||
|
||||
def getText(self):
|
||||
buffer = self.textView.get_buffer()
|
||||
iter1, iter2 = buffer.get_bounds()
|
||||
text = buffer.get_text(iter1, iter2, False)
|
||||
return text
|
||||
|
||||
def setText(self, text):
|
||||
self.textView.get_buffer().set_text(text)
|
||||
|
||||
def key_Return(self, entry, event):
|
||||
text = self.getText()
|
||||
# Figure out if that Return meant "next line" or "execute."
|
||||
try:
|
||||
c = code.compile_command(text)
|
||||
except SyntaxError, e:
|
||||
# This could conceivably piss you off if the client's python
|
||||
# doesn't accept keywords that are known to the manhole's
|
||||
# python.
|
||||
point = buffer.get_iter_at_line_offset(e.lineno, e.offset)
|
||||
buffer.place(point)
|
||||
# TODO: Componentize!
|
||||
self.toplevel.output.append(str(e), "exception")
|
||||
except (OverflowError, ValueError), e:
|
||||
self.toplevel.output.append(str(e), "exception")
|
||||
else:
|
||||
if c is not None:
|
||||
self.sendMessage()
|
||||
# Don't insert Return as a newline in the buffer.
|
||||
self.history.append(text)
|
||||
self.clear()
|
||||
# entry.emit_stop_by_name("key_press_event")
|
||||
return True
|
||||
else:
|
||||
# not a complete code block
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def key_Up(self, entry, event):
|
||||
# if I'm at the top, previous history item.
|
||||
textbuffer = self.textView.get_buffer()
|
||||
if textbuffer.get_iter_at_mark(textbuffer.get_insert()).get_line() == 0:
|
||||
self.history.histup(textbuffer)
|
||||
return True
|
||||
return False
|
||||
|
||||
def key_Down(self, entry, event):
|
||||
textbuffer = self.textView.get_buffer()
|
||||
if textbuffer.get_iter_at_mark(textbuffer.get_insert()).get_line() == (
|
||||
textbuffer.get_line_count() - 1):
|
||||
self.history.histdown(textbuffer)
|
||||
return True
|
||||
return False
|
||||
|
||||
key_ctrl_p = key_Up
|
||||
key_ctrl_n = key_Down
|
||||
|
||||
def key_ctrl_shift_F9(self, entry, event):
|
||||
if self.__debug:
|
||||
import pdb; pdb.set_trace()
|
||||
|
||||
def clear(self):
|
||||
buffer = self.textView.get_buffer()
|
||||
buffer.delete(*buffer.get_bounds())
|
||||
|
||||
def sendMessage(self):
|
||||
buffer = self.textView.get_buffer()
|
||||
iter1, iter2 = buffer.get_bounds()
|
||||
text = buffer.get_text(iter1, iter2, False)
|
||||
self.toplevel.output.append(pythonify(text), 'command')
|
||||
# TODO: Componentize better!
|
||||
try:
|
||||
return self.toplevel.getComponent(IManholeClient).do(text)
|
||||
except OfflineError:
|
||||
self.toplevel.output.append("Not connected, command not sent.\n",
|
||||
"exception")
|
||||
|
||||
|
||||
def pythonify(text):
|
||||
'''
|
||||
Make some text appear as though it was typed in at a Python prompt.
|
||||
'''
|
||||
lines = text.split('\n')
|
||||
lines[0] = '>>> ' + lines[0]
|
||||
return '\n... '.join(lines) + '\n'
|
||||
|
||||
class _Notafile:
|
||||
"""Curry to make failure.printTraceback work with the output widget."""
|
||||
def __init__(self, output, kind):
|
||||
self.output = output
|
||||
self.kind = kind
|
||||
|
||||
def write(self, txt):
|
||||
self.output.append(txt, self.kind)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
class ManholeClient(components.Adapter, pb.Referenceable):
|
||||
implements(IManholeClient)
|
||||
|
||||
capabilities = {
|
||||
# "Explorer": 'Set',
|
||||
"Failure": 'Set'
|
||||
}
|
||||
|
||||
def _cbLogin(self, perspective):
|
||||
self.perspective = perspective
|
||||
perspective.notifyOnDisconnect(self._cbDisconnected)
|
||||
return perspective
|
||||
|
||||
def remote_console(self, messages):
|
||||
for kind, content in messages:
|
||||
if isinstance(content, types.StringTypes):
|
||||
self.original.output.append(content, kind)
|
||||
elif (kind == "exception") and isinstance(content, failure.Failure):
|
||||
content.printTraceback(_Notafile(self.original.output,
|
||||
"exception"))
|
||||
else:
|
||||
self.original.output.append(str(content), kind)
|
||||
|
||||
def remote_receiveExplorer(self, xplorer):
|
||||
pass
|
||||
|
||||
def remote_listCapabilities(self):
|
||||
return self.capabilities
|
||||
|
||||
def _cbDisconnected(self, perspective):
|
||||
self.perspective = None
|
||||
|
||||
def do(self, text):
|
||||
if self.perspective is None:
|
||||
raise OfflineError
|
||||
return self.perspective.callRemote("do", text)
|
||||
|
||||
components.registerAdapter(ManholeClient, ManholeWindow, IManholeClient)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) 2009 Twisted Matrix Laboratories.
|
||||
"""
|
||||
Tests for the L{twisted.manhole.ui} package.
|
||||
"""
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# Copyright (c) 2009 Twisted Matrix Laboratories.
|
||||
"""
|
||||
Tests for GTK2 GUI manhole.
|
||||
"""
|
||||
|
||||
skip = False
|
||||
|
||||
try:
|
||||
import pygtk
|
||||
pygtk.require("2.0")
|
||||
except:
|
||||
skip = "GTK 2.0 not available"
|
||||
else:
|
||||
try:
|
||||
import gtk
|
||||
except ImportError:
|
||||
skip = "GTK 2.0 not available"
|
||||
except RuntimeError:
|
||||
skip = "Old version of GTK 2.0 requires DISPLAY, and we don't have one."
|
||||
else:
|
||||
if gtk.gtk_version[0] == 1:
|
||||
skip = "Requested GTK 2.0, but 1.0 was already imported."
|
||||
else:
|
||||
from twisted.manhole.ui.gtk2manhole import ConsoleInput
|
||||
|
||||
from twisted.trial.unittest import TestCase
|
||||
|
||||
from twisted.python.reflect import prefixedMethodNames
|
||||
|
||||
class ConsoleInputTests(TestCase):
|
||||
"""
|
||||
Tests for L{ConsoleInput}.
|
||||
"""
|
||||
|
||||
def test_reverseKeymap(self):
|
||||
"""
|
||||
Verify that a L{ConsoleInput} has a reverse mapping of the keysym names
|
||||
it needs for event handling to their corresponding keysym.
|
||||
"""
|
||||
ci = ConsoleInput(None)
|
||||
for eventName in prefixedMethodNames(ConsoleInput, 'key_'):
|
||||
keysymName = eventName.split("_")[-1]
|
||||
keysymValue = getattr(gtk.keysyms, keysymName)
|
||||
self.assertEqual(ci.rkeymap[keysymValue], keysymName)
|
||||
|
||||
|
||||
skip = skip
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue