add Linux_i686

This commit is contained in:
j 2014-05-17 18:11:40 +00:00 committed by Ubuntu
commit 95cd9b11f2
1644 changed files with 564260 additions and 0 deletions

View file

@ -0,0 +1,8 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Twisted Manhole: interactive interpreter and direct manipulation support for Twisted.
"""

View file

@ -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()

View file

@ -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()

View file

@ -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>

View file

@ -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

View file

@ -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">&gt;&gt;&gt;</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>

View file

@ -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>

View file

@ -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

View 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

View file

@ -0,0 +1,6 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.manhole}.
"""

View file

@ -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)

View file

@ -0,0 +1,7 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""Twisted Manhole UI: User interface for direct manipulation in Twisted.
"""

View file

@ -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>

View file

@ -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)

View file

@ -0,0 +1,4 @@
# Copyright (c) 2009 Twisted Matrix Laboratories.
"""
Tests for the L{twisted.manhole.ui} package.
"""

View file

@ -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