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,12 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Twisted Spread: Spreadable (Distributed) Computing.
Future Plans: PB, Jelly and Banana need to be optimized.
@author: Glyph Lefkowitz
"""

View file

@ -0,0 +1,358 @@
# -*- test-case-name: twisted.test.test_banana -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Banana -- s-exp based protocol.
Future Plans: This module is almost entirely stable. The same caveat applies
to it as applies to L{twisted.spread.jelly}, however. Read its future plans
for more details.
@author: Glyph Lefkowitz
"""
import copy, cStringIO, struct
from twisted.internet import protocol
from twisted.persisted import styles
from twisted.python import log
class BananaError(Exception):
pass
def int2b128(integer, stream):
if integer == 0:
stream(chr(0))
return
assert integer > 0, "can only encode positive integers"
while integer:
stream(chr(integer & 0x7f))
integer = integer >> 7
def b1282int(st):
"""
Convert an integer represented as a base 128 string into an C{int} or
C{long}.
@param st: The integer encoded in a string.
@type st: C{str}
@return: The integer value extracted from the string.
@rtype: C{int} or C{long}
"""
e = 1
i = 0
for char in st:
n = ord(char)
i += (n * e)
e <<= 7
return i
# delimiter characters.
LIST = chr(0x80)
INT = chr(0x81)
STRING = chr(0x82)
NEG = chr(0x83)
FLOAT = chr(0x84)
# "optional" -- these might be refused by a low-level implementation.
LONGINT = chr(0x85)
LONGNEG = chr(0x86)
# really optional; this is is part of the 'pb' vocabulary
VOCAB = chr(0x87)
HIGH_BIT_SET = chr(0x80)
def setPrefixLimit(limit):
"""
Set the limit on the prefix length for all Banana connections
established after this call.
The prefix length limit determines how many bytes of prefix a banana
decoder will allow before rejecting a potential object as too large.
@type limit: C{int}
@param limit: The number of bytes of prefix for banana to allow when
decoding.
"""
global _PREFIX_LIMIT
_PREFIX_LIMIT = limit
setPrefixLimit(64)
SIZE_LIMIT = 640 * 1024 # 640k is all you'll ever need :-)
class Banana(protocol.Protocol, styles.Ephemeral):
knownDialects = ["pb", "none"]
prefixLimit = None
sizeLimit = SIZE_LIMIT
def setPrefixLimit(self, limit):
"""
Set the prefix limit for decoding done by this protocol instance.
@see: L{setPrefixLimit}
"""
self.prefixLimit = limit
self._smallestLongInt = -2 ** (limit * 7) + 1
self._smallestInt = -2 ** 31
self._largestInt = 2 ** 31 - 1
self._largestLongInt = 2 ** (limit * 7) - 1
def connectionReady(self):
"""Surrogate for connectionMade
Called after protocol negotiation.
"""
def _selectDialect(self, dialect):
self.currentDialect = dialect
self.connectionReady()
def callExpressionReceived(self, obj):
if self.currentDialect:
self.expressionReceived(obj)
else:
# this is the first message we've received
if self.isClient:
# if I'm a client I have to respond
for serverVer in obj:
if serverVer in self.knownDialects:
self.sendEncoded(serverVer)
self._selectDialect(serverVer)
break
else:
# I can't speak any of those dialects.
log.msg("The client doesn't speak any of the protocols "
"offered by the server: disconnecting.")
self.transport.loseConnection()
else:
if obj in self.knownDialects:
self._selectDialect(obj)
else:
# the client just selected a protocol that I did not suggest.
log.msg("The client selected a protocol the server didn't "
"suggest and doesn't know: disconnecting.")
self.transport.loseConnection()
def connectionMade(self):
self.setPrefixLimit(_PREFIX_LIMIT)
self.currentDialect = None
if not self.isClient:
self.sendEncoded(self.knownDialects)
def gotItem(self, item):
l = self.listStack
if l:
l[-1][1].append(item)
else:
self.callExpressionReceived(item)
buffer = ''
def dataReceived(self, chunk):
buffer = self.buffer + chunk
listStack = self.listStack
gotItem = self.gotItem
while buffer:
assert self.buffer != buffer, "This ain't right: %s %s" % (repr(self.buffer), repr(buffer))
self.buffer = buffer
pos = 0
for ch in buffer:
if ch >= HIGH_BIT_SET:
break
pos = pos + 1
else:
if pos > self.prefixLimit:
raise BananaError("Security precaution: more than %d bytes of prefix" % (self.prefixLimit,))
return
num = buffer[:pos]
typebyte = buffer[pos]
rest = buffer[pos+1:]
if len(num) > self.prefixLimit:
raise BananaError("Security precaution: longer than %d bytes worth of prefix" % (self.prefixLimit,))
if typebyte == LIST:
num = b1282int(num)
if num > SIZE_LIMIT:
raise BananaError("Security precaution: List too long.")
listStack.append((num, []))
buffer = rest
elif typebyte == STRING:
num = b1282int(num)
if num > SIZE_LIMIT:
raise BananaError("Security precaution: String too long.")
if len(rest) >= num:
buffer = rest[num:]
gotItem(rest[:num])
else:
return
elif typebyte == INT:
buffer = rest
num = b1282int(num)
gotItem(num)
elif typebyte == LONGINT:
buffer = rest
num = b1282int(num)
gotItem(num)
elif typebyte == LONGNEG:
buffer = rest
num = b1282int(num)
gotItem(-num)
elif typebyte == NEG:
buffer = rest
num = -b1282int(num)
gotItem(num)
elif typebyte == VOCAB:
buffer = rest
num = b1282int(num)
gotItem(self.incomingVocabulary[num])
elif typebyte == FLOAT:
if len(rest) >= 8:
buffer = rest[8:]
gotItem(struct.unpack("!d", rest[:8])[0])
else:
return
else:
raise NotImplementedError(("Invalid Type Byte %r" % (typebyte,)))
while listStack and (len(listStack[-1][1]) == listStack[-1][0]):
item = listStack.pop()[1]
gotItem(item)
self.buffer = ''
def expressionReceived(self, lst):
"""Called when an expression (list, string, or int) is received.
"""
raise NotImplementedError()
outgoingVocabulary = {
# Jelly Data Types
'None' : 1,
'class' : 2,
'dereference' : 3,
'reference' : 4,
'dictionary' : 5,
'function' : 6,
'instance' : 7,
'list' : 8,
'module' : 9,
'persistent' : 10,
'tuple' : 11,
'unpersistable' : 12,
# PB Data Types
'copy' : 13,
'cache' : 14,
'cached' : 15,
'remote' : 16,
'local' : 17,
'lcache' : 18,
# PB Protocol Messages
'version' : 19,
'login' : 20,
'password' : 21,
'challenge' : 22,
'logged_in' : 23,
'not_logged_in' : 24,
'cachemessage' : 25,
'message' : 26,
'answer' : 27,
'error' : 28,
'decref' : 29,
'decache' : 30,
'uncache' : 31,
}
incomingVocabulary = {}
for k, v in outgoingVocabulary.items():
incomingVocabulary[v] = k
def __init__(self, isClient=1):
self.listStack = []
self.outgoingSymbols = copy.copy(self.outgoingVocabulary)
self.outgoingSymbolCount = 0
self.isClient = isClient
def sendEncoded(self, obj):
io = cStringIO.StringIO()
self._encode(obj, io.write)
value = io.getvalue()
self.transport.write(value)
def _encode(self, obj, write):
if isinstance(obj, (list, tuple)):
if len(obj) > SIZE_LIMIT:
raise BananaError(
"list/tuple is too long to send (%d)" % (len(obj),))
int2b128(len(obj), write)
write(LIST)
for elem in obj:
self._encode(elem, write)
elif isinstance(obj, (int, long)):
if obj < self._smallestLongInt or obj > self._largestLongInt:
raise BananaError(
"int/long is too large to send (%d)" % (obj,))
if obj < self._smallestInt:
int2b128(-obj, write)
write(LONGNEG)
elif obj < 0:
int2b128(-obj, write)
write(NEG)
elif obj <= self._largestInt:
int2b128(obj, write)
write(INT)
else:
int2b128(obj, write)
write(LONGINT)
elif isinstance(obj, float):
write(FLOAT)
write(struct.pack("!d", obj))
elif isinstance(obj, str):
# TODO: an API for extending banana...
if self.currentDialect == "pb" and obj in self.outgoingSymbols:
symbolID = self.outgoingSymbols[obj]
int2b128(symbolID, write)
write(VOCAB)
else:
if len(obj) > SIZE_LIMIT:
raise BananaError(
"string is too long to send (%d)" % (len(obj),))
int2b128(len(obj), write)
write(STRING)
write(obj)
else:
raise BananaError("could not send object: %r" % (obj,))
# For use from the interactive interpreter
_i = Banana()
_i.connectionMade()
_i._selectDialect("none")
def encode(lst):
"""Encode a list s-expression."""
io = cStringIO.StringIO()
_i.transport = io
_i.sendEncoded(lst)
return io.getvalue()
def decode(st):
"""
Decode a banana-encoded string.
"""
l = []
_i.expressionReceived = l.append
try:
_i.dataReceived(st)
finally:
_i.buffer = ''
del _i.expressionReceived
return l[0]

View file

@ -0,0 +1,590 @@
# -*- test-case-name: twisted.test.test_pb -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
This module represents flavors of remotely acessible objects.
Currently this is only objects accessible through Perspective Broker, but will
hopefully encompass all forms of remote access which can emulate subsets of PB
(such as XMLRPC or SOAP).
Future Plans: Optimization. Exploitation of new-style object model.
Optimizations to this module should not affect external-use semantics at all,
but may have a small impact on users who subclass and override methods.
@author: Glyph Lefkowitz
"""
# NOTE: this module should NOT import pb; it is supposed to be a module which
# abstractly defines remotely accessible types. Many of these types expect to
# be serialized by Jelly, but they ought to be accessible through other
# mechanisms (like XMLRPC)
# system imports
import sys
from zope.interface import implements, Interface
# twisted imports
from twisted.python import log, reflect
# sibling imports
from jelly import setUnjellyableForClass, setUnjellyableForClassTree, setUnjellyableFactoryForClass, unjellyableRegistry
from jelly import Jellyable, Unjellyable, _newDummyLike
from jelly import setInstanceState, getInstanceState
# compatibility
setCopierForClass = setUnjellyableForClass
setCopierForClassTree = setUnjellyableForClassTree
setFactoryForClass = setUnjellyableFactoryForClass
copyTags = unjellyableRegistry
copy_atom = "copy"
cache_atom = "cache"
cached_atom = "cached"
remote_atom = "remote"
class NoSuchMethod(AttributeError):
"""Raised if there is no such remote method"""
class IPBRoot(Interface):
"""Factory for root Referenceable objects for PB servers."""
def rootObject(broker):
"""Return root Referenceable for broker."""
class Serializable(Jellyable):
"""An object that can be passed remotely.
I am a style of object which can be serialized by Perspective
Broker. Objects which wish to be referenceable or copied remotely
have to subclass Serializable. However, clients of Perspective
Broker will probably not want to directly subclass Serializable; the
Flavors of transferable objects are listed below.
What it means to be \"Serializable\" is that an object can be
passed to or returned from a remote method. Certain basic types
(dictionaries, lists, tuples, numbers, strings) are serializable by
default; however, classes need to choose a specific serialization
style: L{Referenceable}, L{Viewable}, L{Copyable} or L{Cacheable}.
You may also pass C{[lists, dictionaries, tuples]} of L{Serializable}
instances to or return them from remote methods, as many levels deep
as you like.
"""
def processUniqueID(self):
"""Return an ID which uniquely represents this object for this process.
By default, this uses the 'id' builtin, but can be overridden to
indicate that two values are identity-equivalent (such as proxies
for the same object).
"""
return id(self)
class Referenceable(Serializable):
perspective = None
"""I am an object sent remotely as a direct reference.
When one of my subclasses is sent as an argument to or returned
from a remote method call, I will be serialized by default as a
direct reference.
This means that the peer will be able to call methods on me;
a method call xxx() from my peer will be resolved to methods
of the name remote_xxx.
"""
def remoteMessageReceived(self, broker, message, args, kw):
"""A remote message has been received. Dispatch it appropriately.
The default implementation is to dispatch to a method called
'remote_messagename' and call it with the same arguments.
"""
args = broker.unserialize(args)
kw = broker.unserialize(kw)
method = getattr(self, "remote_%s" % message, None)
if method is None:
raise NoSuchMethod("No such method: remote_%s" % (message,))
try:
state = method(*args, **kw)
except TypeError:
log.msg("%s didn't accept %s and %s" % (method, args, kw))
raise
return broker.serialize(state, self.perspective)
def jellyFor(self, jellier):
"""(internal)
Return a tuple which will be used as the s-expression to
serialize this to a peer.
"""
return ["remote", jellier.invoker.registerReference(self)]
class Root(Referenceable):
"""I provide a root object to L{pb.Broker}s for a L{pb.BrokerFactory}.
When a L{pb.BrokerFactory} produces a L{pb.Broker}, it supplies that
L{pb.Broker} with an object named \"root\". That object is obtained
by calling my rootObject method.
"""
implements(IPBRoot)
def rootObject(self, broker):
"""A L{pb.BrokerFactory} is requesting to publish me as a root object.
When a L{pb.BrokerFactory} is sending me as the root object, this
method will be invoked to allow per-broker versions of an
object. By default I return myself.
"""
return self
class ViewPoint(Referenceable):
"""
I act as an indirect reference to an object accessed through a
L{pb.Perspective}.
Simply put, I combine an object with a perspective so that when a
peer calls methods on the object I refer to, the method will be
invoked with that perspective as a first argument, so that it can
know who is calling it.
While L{Viewable} objects will be converted to ViewPoints by default
when they are returned from or sent as arguments to a remote
method, any object may be manually proxied as well. (XXX: Now that
this class is no longer named C{Proxy}, this is the only occourance
of the term 'proxied' in this docstring, and may be unclear.)
This can be useful when dealing with L{pb.Perspective}s, L{Copyable}s,
and L{Cacheable}s. It is legal to implement a method as such on
a perspective::
| def perspective_getViewPointForOther(self, name):
| defr = self.service.getPerspectiveRequest(name)
| defr.addCallbacks(lambda x, self=self: ViewPoint(self, x), log.msg)
| return defr
This will allow you to have references to Perspective objects in two
different ways. One is through the initial 'attach' call -- each
peer will have a L{pb.RemoteReference} to their perspective directly. The
other is through this method; each peer can get a L{pb.RemoteReference} to
all other perspectives in the service; but that L{pb.RemoteReference} will
be to a L{ViewPoint}, not directly to the object.
The practical offshoot of this is that you can implement 2 varieties
of remotely callable methods on this Perspective; view_xxx and
C{perspective_xxx}. C{view_xxx} methods will follow the rules for
ViewPoint methods (see ViewPoint.L{remoteMessageReceived}), and
C{perspective_xxx} methods will follow the rules for Perspective
methods.
"""
def __init__(self, perspective, object):
"""Initialize me with a Perspective and an Object.
"""
self.perspective = perspective
self.object = object
def processUniqueID(self):
"""Return an ID unique to a proxy for this perspective+object combination.
"""
return (id(self.perspective), id(self.object))
def remoteMessageReceived(self, broker, message, args, kw):
"""A remote message has been received. Dispatch it appropriately.
The default implementation is to dispatch to a method called
'C{view_messagename}' to my Object and call it on my object with
the same arguments, modified by inserting my Perspective as
the first argument.
"""
args = broker.unserialize(args, self.perspective)
kw = broker.unserialize(kw, self.perspective)
method = getattr(self.object, "view_%s" % message)
try:
state = method(*(self.perspective,)+args, **kw)
except TypeError:
log.msg("%s didn't accept %s and %s" % (method, args, kw))
raise
rv = broker.serialize(state, self.perspective, method, args, kw)
return rv
class Viewable(Serializable):
"""I will be converted to a L{ViewPoint} when passed to or returned from a remote method.
The beginning of a peer's interaction with a PB Service is always
through a perspective. However, if a C{perspective_xxx} method returns
a Viewable, it will be serialized to the peer as a response to that
method.
"""
def jellyFor(self, jellier):
"""Serialize a L{ViewPoint} for me and the perspective of the given broker.
"""
return ViewPoint(jellier.invoker.serializingPerspective, self).jellyFor(jellier)
class Copyable(Serializable):
"""Subclass me to get copied each time you are returned from or passed to a remote method.
When I am returned from or passed to a remote method call, I will be
converted into data via a set of callbacks (see my methods for more
info). That data will then be serialized using Jelly, and sent to
the peer.
The peer will then look up the type to represent this with; see
L{RemoteCopy} for details.
"""
def getStateToCopy(self):
"""Gather state to send when I am serialized for a peer.
I will default to returning self.__dict__. Override this to
customize this behavior.
"""
return self.__dict__
def getStateToCopyFor(self, perspective):
"""
Gather state to send when I am serialized for a particular
perspective.
I will default to calling L{getStateToCopy}. Override this to
customize this behavior.
"""
return self.getStateToCopy()
def getTypeToCopy(self):
"""Determine what type tag to send for me.
By default, send the string representation of my class
(package.module.Class); normally this is adequate, but
you may override this to change it.
"""
return reflect.qual(self.__class__)
def getTypeToCopyFor(self, perspective):
"""Determine what type tag to send for me.
By default, defer to self.L{getTypeToCopy}() normally this is
adequate, but you may override this to change it.
"""
return self.getTypeToCopy()
def jellyFor(self, jellier):
"""Assemble type tag and state to copy for this broker.
This will call L{getTypeToCopyFor} and L{getStateToCopy}, and
return an appropriate s-expression to represent me.
"""
if jellier.invoker is None:
return getInstanceState(self, jellier)
p = jellier.invoker.serializingPerspective
t = self.getTypeToCopyFor(p)
state = self.getStateToCopyFor(p)
sxp = jellier.prepare(self)
sxp.extend([t, jellier.jelly(state)])
return jellier.preserve(self, sxp)
class Cacheable(Copyable):
"""A cached instance.
This means that it's copied; but there is some logic to make sure
that it's only copied once. Additionally, when state is retrieved,
it is passed a "proto-reference" to the state as it will exist on
the client.
XXX: The documentation for this class needs work, but it's the most
complex part of PB and it is inherently difficult to explain.
"""
def getStateToCacheAndObserveFor(self, perspective, observer):
"""
Get state to cache on the client and client-cache reference
to observe locally.
This is similiar to getStateToCopyFor, but it additionally
passes in a reference to the client-side RemoteCache instance
that will be created when it is unserialized. This allows
Cacheable instances to keep their RemoteCaches up to date when
they change, such that no changes can occur between the point
at which the state is initially copied and the client receives
it that are not propogated.
"""
return self.getStateToCopyFor(perspective)
def jellyFor(self, jellier):
"""Return an appropriate tuple to serialize me.
Depending on whether this broker has cached me or not, this may
return either a full state or a reference to an existing cache.
"""
if jellier.invoker is None:
return getInstanceState(self, jellier)
luid = jellier.invoker.cachedRemotelyAs(self, 1)
if luid is None:
luid = jellier.invoker.cacheRemotely(self)
p = jellier.invoker.serializingPerspective
type_ = self.getTypeToCopyFor(p)
observer = RemoteCacheObserver(jellier.invoker, self, p)
state = self.getStateToCacheAndObserveFor(p, observer)
l = jellier.prepare(self)
jstate = jellier.jelly(state)
l.extend([type_, luid, jstate])
return jellier.preserve(self, l)
else:
return cached_atom, luid
def stoppedObserving(self, perspective, observer):
"""This method is called when a client has stopped observing me.
The 'observer' argument is the same as that passed in to
getStateToCacheAndObserveFor.
"""
class RemoteCopy(Unjellyable):
"""I am a remote copy of a Copyable object.
When the state from a L{Copyable} object is received, an instance will
be created based on the copy tags table (see setUnjellyableForClass) and
sent the L{setCopyableState} message. I provide a reasonable default
implementation of that message; subclass me if you wish to serve as
a copier for remote data.
NOTE: copiers are invoked with no arguments. Do not implement a
constructor which requires args in a subclass of L{RemoteCopy}!
"""
def setCopyableState(self, state):
"""I will be invoked with the state to copy locally.
'state' is the data returned from the remote object's
'getStateToCopyFor' method, which will often be the remote
object's dictionary (or a filtered approximation of it depending
on my peer's perspective).
"""
self.__dict__ = state
def unjellyFor(self, unjellier, jellyList):
if unjellier.invoker is None:
return setInstanceState(self, unjellier, jellyList)
self.setCopyableState(unjellier.unjelly(jellyList[1]))
return self
class RemoteCache(RemoteCopy, Serializable):
"""A cache is a local representation of a remote L{Cacheable} object.
This represents the last known state of this object. It may
also have methods invoked on it -- in order to update caches,
the cached class generates a L{pb.RemoteReference} to this object as
it is originally sent.
Much like copy, I will be invoked with no arguments. Do not
implement a constructor that requires arguments in one of my
subclasses.
"""
def remoteMessageReceived(self, broker, message, args, kw):
"""A remote message has been received. Dispatch it appropriately.
The default implementation is to dispatch to a method called
'C{observe_messagename}' and call it on my with the same arguments.
"""
args = broker.unserialize(args)
kw = broker.unserialize(kw)
method = getattr(self, "observe_%s" % message)
try:
state = method(*args, **kw)
except TypeError:
log.msg("%s didn't accept %s and %s" % (method, args, kw))
raise
return broker.serialize(state, None, method, args, kw)
def jellyFor(self, jellier):
"""serialize me (only for the broker I'm for) as the original cached reference
"""
if jellier.invoker is None:
return getInstanceState(self, jellier)
assert jellier.invoker is self.broker, "You cannot exchange cached proxies between brokers."
return 'lcache', self.luid
def unjellyFor(self, unjellier, jellyList):
if unjellier.invoker is None:
return setInstanceState(self, unjellier, jellyList)
self.broker = unjellier.invoker
self.luid = jellyList[1]
cProxy = _newDummyLike(self)
# XXX questionable whether this was a good design idea...
init = getattr(cProxy, "__init__", None)
if init:
init()
unjellier.invoker.cacheLocally(jellyList[1], self)
cProxy.setCopyableState(unjellier.unjelly(jellyList[2]))
# Might have changed due to setCopyableState method; we'll assume that
# it's bad form to do so afterwards.
self.__dict__ = cProxy.__dict__
# chomp, chomp -- some existing code uses "self.__dict__ =", some uses
# "__dict__.update". This is here in order to handle both cases.
self.broker = unjellier.invoker
self.luid = jellyList[1]
return cProxy
## def __really_del__(self):
## """Final finalization call, made after all remote references have been lost.
## """
def __cmp__(self, other):
"""Compare me [to another RemoteCache.
"""
if isinstance(other, self.__class__):
return cmp(id(self.__dict__), id(other.__dict__))
else:
return cmp(id(self.__dict__), other)
def __hash__(self):
"""Hash me.
"""
return int(id(self.__dict__) % sys.maxint)
broker = None
luid = None
def __del__(self):
"""Do distributed reference counting on finalize.
"""
try:
# log.msg( ' --- decache: %s %s' % (self, self.luid) )
if self.broker:
self.broker.decCacheRef(self.luid)
except:
log.deferr()
def unjellyCached(unjellier, unjellyList):
luid = unjellyList[1]
cNotProxy = unjellier.invoker.cachedLocallyAs(luid)
cProxy = _newDummyLike(cNotProxy)
return cProxy
setUnjellyableForClass("cached", unjellyCached)
def unjellyLCache(unjellier, unjellyList):
luid = unjellyList[1]
obj = unjellier.invoker.remotelyCachedForLUID(luid)
return obj
setUnjellyableForClass("lcache", unjellyLCache)
def unjellyLocal(unjellier, unjellyList):
obj = unjellier.invoker.localObjectForID(unjellyList[1])
return obj
setUnjellyableForClass("local", unjellyLocal)
class RemoteCacheMethod:
"""A method on a reference to a L{RemoteCache}.
"""
def __init__(self, name, broker, cached, perspective):
"""(internal) initialize.
"""
self.name = name
self.broker = broker
self.perspective = perspective
self.cached = cached
def __cmp__(self, other):
return cmp((self.name, self.broker, self.perspective, self.cached), other)
def __hash__(self):
return hash((self.name, self.broker, self.perspective, self.cached))
def __call__(self, *args, **kw):
"""(internal) action method.
"""
cacheID = self.broker.cachedRemotelyAs(self.cached)
if cacheID is None:
from pb import ProtocolError
raise ProtocolError("You can't call a cached method when the object hasn't been given to the peer yet.")
return self.broker._sendMessage('cache', self.perspective, cacheID, self.name, args, kw)
class RemoteCacheObserver:
"""I am a reverse-reference to the peer's L{RemoteCache}.
I am generated automatically when a cache is serialized. I
represent a reference to the client's L{RemoteCache} object that
will represent a particular L{Cacheable}; I am the additional
object passed to getStateToCacheAndObserveFor.
"""
def __init__(self, broker, cached, perspective):
"""(internal) Initialize me.
@param broker: a L{pb.Broker} instance.
@param cached: a L{Cacheable} instance that this L{RemoteCacheObserver}
corresponds to.
@param perspective: a reference to the perspective who is observing this.
"""
self.broker = broker
self.cached = cached
self.perspective = perspective
def __repr__(self):
return "<RemoteCacheObserver(%s, %s, %s) at %s>" % (
self.broker, self.cached, self.perspective, id(self))
def __hash__(self):
"""Generate a hash unique to all L{RemoteCacheObserver}s for this broker/perspective/cached triplet
"""
return ( (hash(self.broker) % 2**10)
+ (hash(self.perspective) % 2**10)
+ (hash(self.cached) % 2**10))
def __cmp__(self, other):
"""Compare me to another L{RemoteCacheObserver}.
"""
return cmp((self.broker, self.perspective, self.cached), other)
def callRemote(self, _name, *args, **kw):
"""(internal) action method.
"""
cacheID = self.broker.cachedRemotelyAs(self.cached)
if cacheID is None:
from pb import ProtocolError
raise ProtocolError("You can't call a cached method when the "
"object hasn't been given to the peer yet.")
return self.broker._sendMessage('cache', self.perspective, cacheID,
_name, args, kw)
def remoteMethod(self, key):
"""Get a L{pb.RemoteMethod} for this key.
"""
return RemoteCacheMethod(key, self.broker, self.cached, self.perspective)

View file

@ -0,0 +1,31 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Twisted Spread Interfaces.
"""
from zope.interface import Interface
class IJellyable(Interface):
def jellyFor(jellier):
"""
Jelly myself for jellier.
"""
class IUnjellyable(Interface):
def unjellyFor(jellier, jellyList):
"""
Unjelly myself for the jellier.
@param jellier: A stateful object which exists for the lifetime of a
single call to L{unjelly}.
@param jellyList: The C{list} which represents the jellied state of the
object to be unjellied.
@return: The object which results from unjellying.
"""

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,142 @@
# -*- test-case-name: twisted.test.test_pb -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Persistently cached objects for PB.
Maintainer: Glyph Lefkowitz
Future Plans: None known.
"""
import time
from twisted.internet import defer
from twisted.spread import banana, jelly, flavors
class Publishable(flavors.Cacheable):
"""An object whose cached state persists across sessions.
"""
def __init__(self, publishedID):
self.republish()
self.publishedID = publishedID
def republish(self):
"""Set the timestamp to current and (TODO) update all observers.
"""
self.timestamp = time.time()
def view_getStateToPublish(self, perspective):
'(internal)'
return self.getStateToPublishFor(perspective)
def getStateToPublishFor(self, perspective):
"""Implement me to special-case your state for a perspective.
"""
return self.getStateToPublish()
def getStateToPublish(self):
"""Implement me to return state to copy as part of the publish phase.
"""
raise NotImplementedError("%s.getStateToPublishFor" % self.__class__)
def getStateToCacheAndObserveFor(self, perspective, observer):
"""Get all necessary metadata to keep a clientside cache.
"""
if perspective:
pname = perspective.perspectiveName
sname = perspective.getService().serviceName
else:
pname = "None"
sname = "None"
return {"remote": flavors.ViewPoint(perspective, self),
"publishedID": self.publishedID,
"perspective": pname,
"service": sname,
"timestamp": self.timestamp}
class RemotePublished(flavors.RemoteCache):
"""The local representation of remote Publishable object.
"""
isActivated = 0
_wasCleanWhenLoaded = 0
def getFileName(self, ext='pub'):
return ("%s-%s-%s.%s" %
(self.service, self.perspective, str(self.publishedID), ext))
def setCopyableState(self, state):
self.__dict__.update(state)
self._activationListeners = []
try:
dataFile = file(self.getFileName(), "rb")
data = dataFile.read()
dataFile.close()
except IOError:
recent = 0
else:
newself = jelly.unjelly(banana.decode(data))
recent = (newself.timestamp == self.timestamp)
if recent:
self._cbGotUpdate(newself.__dict__)
self._wasCleanWhenLoaded = 1
else:
self.remote.callRemote('getStateToPublish').addCallbacks(self._cbGotUpdate)
def __getstate__(self):
other = self.__dict__.copy()
# Remove PB-specific attributes
del other['broker']
del other['remote']
del other['luid']
# remove my own runtime-tracking stuff
del other['_activationListeners']
del other['isActivated']
return other
def _cbGotUpdate(self, newState):
self.__dict__.update(newState)
self.isActivated = 1
# send out notifications
for listener in self._activationListeners:
listener(self)
self._activationListeners = []
self.activated()
dataFile = file(self.getFileName(), "wb")
dataFile.write(banana.encode(jelly.jelly(self)))
dataFile.close()
def activated(self):
"""Implement this method if you want to be notified when your
publishable subclass is activated.
"""
def callWhenActivated(self, callback):
"""Externally register for notification when this publishable has received all relevant data.
"""
if self.isActivated:
callback(self)
else:
self._activationListeners.append(callback)
def whenReady(d):
"""
Wrap a deferred returned from a pb method in another deferred that
expects a RemotePublished as a result. This will allow you to wait until
the result is really available.
Idiomatic usage would look like::
publish.whenReady(serverObject.getMeAPublishable()).addCallback(lookAtThePublishable)
"""
d2 = defer.Deferred()
d.addCallbacks(_pubReady, d2.errback,
callbackArgs=(d2,))
return d2
def _pubReady(result, d2):
'(internal)'
result.callWhenActivated(d2.callback)

View file

@ -0,0 +1,12 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Twisted Spread UI: UI utilities for various toolkits connecting to PB.
"""
# Undeprecating this until someone figures out a real plan for alternatives to spread.ui.
##import warnings
##warnings.warn("twisted.spread.ui is deprecated. Please do not use.", DeprecationWarning)

View file

@ -0,0 +1,218 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
import gtk
from twisted import copyright
from twisted.internet import defer
from twisted.python import failure, log, util
from twisted.spread import pb
from twisted.cred.credentials import UsernamePassword
from twisted.internet import error as netError
def login(client=None, **defaults):
"""
@param host:
@param port:
@param identityName:
@param password:
@param serviceName:
@param perspectiveName:
@returntype: Deferred RemoteReference of Perspective
"""
d = defer.Deferred()
LoginDialog(client, d, defaults)
return d
class GladeKeeper:
"""
@cvar gladefile: The file in which the glade GUI definition is kept.
@type gladefile: str
@cvar _widgets: Widgets that should be attached to me as attributes.
@type _widgets: list of strings
"""
gladefile = None
_widgets = ()
def __init__(self):
from gtk import glade
self.glade = glade.XML(self.gladefile)
# mold can go away when we get a newer pygtk (post 1.99.14)
mold = {}
for k in dir(self):
mold[k] = getattr(self, k)
self.glade.signal_autoconnect(mold)
self._setWidgets()
def _setWidgets(self):
get_widget = self.glade.get_widget
for widgetName in self._widgets:
setattr(self, "_" + widgetName, get_widget(widgetName))
class LoginDialog(GladeKeeper):
# IdentityConnector host port identityName password
# requestLogin -> identityWrapper or login failure
# requestService serviceName perspectiveName client
# window killed
# cancel button pressed
# login button activated
fields = ['host','port','identityName','password',
'perspectiveName']
_widgets = ("hostEntry", "portEntry", "identityNameEntry", "passwordEntry",
"perspectiveNameEntry", "statusBar",
"loginDialog")
_advancedControls = ['perspectiveLabel', 'perspectiveNameEntry',
'protocolLabel', 'versionLabel']
gladefile = util.sibpath(__file__, "login2.glade")
_timeoutID = None
def __init__(self, client, deferred, defaults):
self.client = client
self.deferredResult = deferred
GladeKeeper.__init__(self)
self.setDefaults(defaults)
self._loginDialog.show()
def setDefaults(self, defaults):
if not defaults.has_key('port'):
defaults['port'] = str(pb.portno)
elif isinstance(defaults['port'], (int, long)):
defaults['port'] = str(defaults['port'])
for k, v in defaults.iteritems():
if k in self.fields:
widget = getattr(self, "_%sEntry" % (k,))
widget.set_text(v)
def _setWidgets(self):
GladeKeeper._setWidgets(self)
self._statusContext = self._statusBar.get_context_id("Login dialog.")
get_widget = self.glade.get_widget
get_widget("versionLabel").set_text(copyright.longversion)
get_widget("protocolLabel").set_text("Protocol PB-%s" %
(pb.Broker.version,))
def _on_loginDialog_response(self, widget, response):
handlers = {gtk.RESPONSE_NONE: self._windowClosed,
gtk.RESPONSE_DELETE_EVENT: self._windowClosed,
gtk.RESPONSE_OK: self._doLogin,
gtk.RESPONSE_CANCEL: self._cancelled}
handler = handlers.get(response)
if handler is not None:
handler()
else:
log.msg("Unexpected dialog response %r from %s" % (response,
widget))
def _on_loginDialog_close(self, widget, userdata=None):
self._windowClosed()
def _on_loginDialog_destroy_event(self, widget, userdata=None):
self._windowClosed()
def _cancelled(self):
if not self.deferredResult.called:
self.deferredResult.errback(netError.UserError("User hit Cancel."))
self._loginDialog.destroy()
def _windowClosed(self, reason=None):
if not self.deferredResult.called:
self.deferredResult.errback(netError.UserError("Window closed."))
def _doLogin(self):
idParams = {}
idParams['host'] = self._hostEntry.get_text()
idParams['port'] = self._portEntry.get_text()
idParams['identityName'] = self._identityNameEntry.get_text()
idParams['password'] = self._passwordEntry.get_text()
try:
idParams['port'] = int(idParams['port'])
except ValueError:
pass
f = pb.PBClientFactory()
from twisted.internet import reactor
reactor.connectTCP(idParams['host'], idParams['port'], f)
creds = UsernamePassword(idParams['identityName'], idParams['password'])
d = f.login(creds, self.client)
def _timeoutLogin():
self._timeoutID = None
d.errback(failure.Failure(defer.TimeoutError("Login timed out.")))
self._timeoutID = reactor.callLater(30, _timeoutLogin)
d.addCallbacks(self._cbGotPerspective, self._ebFailedLogin)
self.statusMsg("Contacting server...")
# serviceName = self._serviceNameEntry.get_text()
# perspectiveName = self._perspectiveNameEntry.get_text()
# if not perspectiveName:
# perspectiveName = idParams['identityName']
# d = _identityConnector.requestService(serviceName, perspectiveName,
# self.client)
# d.addCallbacks(self._cbGotPerspective, self._ebFailedLogin)
# setCursor to waiting
def _cbGotPerspective(self, perspective):
self.statusMsg("Connected to server.")
if self._timeoutID is not None:
self._timeoutID.cancel()
self._timeoutID = None
self.deferredResult.callback(perspective)
# clear waiting cursor
self._loginDialog.destroy()
def _ebFailedLogin(self, reason):
if isinstance(reason, failure.Failure):
reason = reason.value
self.statusMsg(reason)
if isinstance(reason, (unicode, str)):
text = reason
else:
text = unicode(reason)
msg = gtk.MessageDialog(self._loginDialog,
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE,
text)
msg.show_all()
msg.connect("response", lambda *a: msg.destroy())
# hostname not found
# host unreachable
# connection refused
# authentication failed
# no such service
# no such perspective
# internal server error
def _on_advancedButton_toggled(self, widget, userdata=None):
active = widget.get_active()
if active:
op = "show"
else:
op = "hide"
for widgetName in self._advancedControls:
widget = self.glade.get_widget(widgetName)
getattr(widget, op)()
def statusMsg(self, text):
if not isinstance(text, (unicode, str)):
text = unicode(text)
return self._statusBar.push(self._statusContext, text)

View file

@ -0,0 +1,461 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkDialog" id="loginDialog">
<property name="title" translatable="yes">Login</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">True</property>
<property name="has_separator">True</property>
<signal name="response" handler="_on_loginDialog_response" last_modification_time="Sat, 25 Jan 2003 13:52:57 GMT"/>
<signal name="close" handler="_on_loginDialog_close" last_modification_time="Sat, 25 Jan 2003 13:53:04 GMT"/>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancelbutton1">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="loginButton">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="response_id">-5</property>
<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-ok</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="label9">
<property name="visible">True</property>
<property name="label" translatable="yes">_Login</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>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkStatusbar" id="statusBar">
<property name="visible">True</property>
<property name="has_resize_grip">False</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="n_rows">6</property>
<property name="n_columns">2</property>
<property name="homogeneous">False</property>
<property name="row_spacing">2</property>
<property name="column_spacing">0</property>
<child>
<widget class="GtkLabel" id="hostLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">_Host:</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.9</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="mnemonic_widget">hostEntry</property>
<accessibility>
<atkrelation target="hostEntry" type="label-for"/>
<atkrelation target="portEntry" type="label-for"/>
</accessibility>
</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_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkEntry" id="hostEntry">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">The name of a host to connect to.</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes">localhost</property>
<property name="has_frame">True</property>
<property name="invisible_char" translatable="yes">*</property>
<property name="activates_default">True</property>
<accessibility>
<atkrelation target="hostLabel" type="labelled-by"/>
</accessibility>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="portEntry">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">The number of a port to connect on.</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes">8787</property>
<property name="has_frame">True</property>
<property name="invisible_char" translatable="yes">*</property>
<property name="activates_default">True</property>
<property name="width_chars">5</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</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">fill</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="nameLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">_Name:</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.9</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="mnemonic_widget">identityNameEntry</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_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="identityNameEntry">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">An identity to log in as.</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char" translatable="yes">*</property>
<property name="activates_default">True</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="GtkEntry" id="passwordEntry">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">The Identity's log-in password.</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">False</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char" translatable="yes">*</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="passwordLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">_Password:</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.9</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="mnemonic_widget">passwordEntry</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="perspectiveLabel">
<property name="label" translatable="yes">Perspective:</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.9</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">5</property>
<property name="bottom_attach">6</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="perspectiveNameEntry">
<property name="tooltip" translatable="yes">The name of a Perspective to request.</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char" translatable="yes">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkLabel" id="protocolLabel">
<property name="label" translatable="yes">Insert Protocol Version Here</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.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="versionLabel">
<property name="label" translatable="yes">Insert Twisted Version Here</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.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>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">fill</property>
<property name="y_options">fill</property>
</packing>
</child>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="xalign">1</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">1</property>
<child>
<widget class="GtkToggleButton" id="advancedButton">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Advanced options.</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Advanced &gt;&gt;</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="active">False</property>
<property name="inconsistent">False</property>
<signal name="toggled" handler="_on_advancedButton_toggled" object="Login" last_modification_time="Sat, 25 Jan 2003 13:47:17 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
</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,204 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
What I want it to look like:
+- One
| \- Two
| |- Three
| |- Four
| +- Five
| | \- Six
| |- Seven
+- Eight
| \- Nine
"""
import os
from Tkinter import *
class Node:
def __init__(self):
"""
Do whatever you want here.
"""
self.item=None
def getName(self):
"""
Return the name of this node in the tree.
"""
pass
def isExpandable(self):
"""
Return true if this node is expandable.
"""
return len(self.getSubNodes())>0
def getSubNodes(self):
"""
Return the sub nodes of this node.
"""
return []
def gotDoubleClick(self):
"""
Called when we are double clicked.
"""
pass
def updateMe(self):
"""
Call me when something about me changes, so that my representation
changes.
"""
if self.item:
self.item.update()
class FileNode(Node):
def __init__(self,name):
Node.__init__(self)
self.name=name
def getName(self):
return os.path.basename(self.name)
def isExpandable(self):
return os.path.isdir(self.name)
def getSubNodes(self):
names=map(lambda x,n=self.name:os.path.join(n,x),os.listdir(self.name))
return map(FileNode,names)
class TreeItem:
def __init__(self,widget,parent,node):
self.widget=widget
self.node=node
node.item=self
if self.node.isExpandable():
self.expand=0
else:
self.expand=None
self.parent=parent
if parent:
self.level=self.parent.level+1
else:
self.level=0
self.first=0 # gets set in Tree.expand()
self.subitems=[]
def __del__(self):
del self.node
del self.widget
def __repr__(self):
return "<Item for Node %s at level %s>"%(self.node.getName(),self.level)
def render(self):
"""
Override in a subclass.
"""
raise NotImplementedError
def update(self):
self.widget.update(self)
class ListboxTreeItem(TreeItem):
def render(self):
start=self.level*"| "
if self.expand==None and not self.first:
start=start+"|"
elif self.expand==0:
start=start+"L"
elif self.expand==1:
start=start+"+"
else:
start=start+"\\"
r=[start+"- "+self.node.getName()]
if self.expand:
for i in self.subitems:
r.extend(i.render())
return r
class ListboxTree:
def __init__(self,parent=None,**options):
self.box=apply(Listbox,[parent],options)
self.box.bind("<Double-1>",self.flip)
self.roots=[]
self.items=[]
def pack(self,*args,**kw):
"""
for packing.
"""
apply(self.box.pack,args,kw)
def grid(self,*args,**kw):
"""
for gridding.
"""
apply(self.box.grid,args,kw)
def yview(self,*args,**kw):
"""
for scrolling.
"""
apply(self.box.yview,args,kw)
def addRoot(self,node):
r=ListboxTreeItem(self,None,node)
self.roots.append(r)
self.items.append(r)
self.box.insert(END,r.render()[0])
return r
def curselection(self):
c=self.box.curselection()
if not c: return
return self.items[int(c[0])]
def flip(self,*foo):
if not self.box.curselection(): return
item=self.items[int(self.box.curselection()[0])]
if item.expand==None: return
if not item.expand:
self.expand(item)
else:
self.close(item)
item.node.gotDoubleClick()
def expand(self,item):
if item.expand or item.expand==None: return
item.expand=1
item.subitems=map(lambda x,i=item,s=self:ListboxTreeItem(s,i,x),item.node.getSubNodes())
if item.subitems:
item.subitems[0].first=1
i=self.items.index(item)
self.items,after=self.items[:i+1],self.items[i+1:]
self.items=self.items+item.subitems+after
c=self.items.index(item)
self.box.delete(c)
r=item.render()
for i in r:
self.box.insert(c,i)
c=c+1
def close(self,item):
if not item.expand: return
item.expand=0
length=len(item.subitems)
for i in item.subitems:
self.close(i)
c=self.items.index(item)
del self.items[c+1:c+1+length]
for i in range(length+1):
self.box.delete(c)
self.box.insert(c,item.render()[0])
def remove(self,item):
if item.expand:
self.close(item)
c=self.items.index(item)
del self.items[c]
if item.parent:
item.parent.subitems.remove(item)
self.box.delete(c)
def update(self,item):
if item.expand==None:
c=self.items.index(item)
self.box.delete(c)
self.box.insert(c,item.render()[0])
elif item.expand:
self.close(item)
self.expand(item)
if __name__=="__main__":
tk=Tk()
s=Scrollbar()
t=ListboxTree(tk,yscrollcommand=s.set)
t.pack(side=LEFT,fill=BOTH)
s.config(command=t.yview)
s.pack(side=RIGHT,fill=Y)
t.addRoot(FileNode("C:/"))
#mainloop()

View file

@ -0,0 +1,397 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""Utilities for building L{PB<twisted.spread.pb>} clients with L{Tkinter}.
"""
from Tkinter import *
from tkSimpleDialog import _QueryString
from tkFileDialog import _Dialog
from twisted.spread import pb
from twisted.internet import reactor
from twisted import copyright
import string
#normalFont = Font("-adobe-courier-medium-r-normal-*-*-120-*-*-m-*-iso8859-1")
#boldFont = Font("-adobe-courier-bold-r-normal-*-*-120-*-*-m-*-iso8859-1")
#errorFont = Font("-adobe-courier-medium-o-normal-*-*-120-*-*-m-*-iso8859-1")
class _QueryPassword(_QueryString):
def body(self, master):
w = Label(master, text=self.prompt, justify=LEFT)
w.grid(row=0, padx=5, sticky=W)
self.entry = Entry(master, name="entry",show="*")
self.entry.grid(row=1, padx=5, sticky=W+E)
if self.initialvalue:
self.entry.insert(0, self.initialvalue)
self.entry.select_range(0, END)
return self.entry
def askpassword(title, prompt, **kw):
'''get a password from the user
@param title: the dialog title
@param prompt: the label text
@param **kw: see L{SimpleDialog} class
@returns: a string
'''
d = apply(_QueryPassword, (title, prompt), kw)
return d.result
def grid_setexpand(widget):
cols,rows=widget.grid_size()
for i in range(cols):
widget.columnconfigure(i,weight=1)
for i in range(rows):
widget.rowconfigure(i,weight=1)
class CList(Frame):
def __init__(self,parent,labels,disablesorting=0,**kw):
Frame.__init__(self,parent)
self.labels=labels
self.lists=[]
self.disablesorting=disablesorting
kw["exportselection"]=0
for i in range(len(labels)):
b=Button(self,text=labels[i],anchor=W,height=1,pady=0)
b.config(command=lambda s=self,i=i:s.setSort(i))
b.grid(column=i,row=0,sticky=N+E+W)
box=apply(Listbox,(self,),kw)
box.grid(column=i,row=1,sticky=N+E+S+W)
self.lists.append(box)
grid_setexpand(self)
self.rowconfigure(0,weight=0)
self._callall("bind",'<Button-1>',self.Button1)
self._callall("bind",'<B1-Motion>',self.Button1)
self.bind('<Up>',self.UpKey)
self.bind('<Down>',self.DownKey)
self.sort=None
def _callall(self,funcname,*args,**kw):
rets=[]
for l in self.lists:
func=getattr(l,funcname)
ret=apply(func,args,kw)
if ret!=None: rets.append(ret)
if rets: return rets
def Button1(self,e):
index=self.nearest(e.y)
self.select_clear(0,END)
self.select_set(index)
self.activate(index)
return "break"
def UpKey(self,e):
index=self.index(ACTIVE)
if index:
self.select_clear(0,END)
self.select_set(index-1)
return "break"
def DownKey(self,e):
index=self.index(ACTIVE)
if index!=self.size()-1:
self.select_clear(0,END)
self.select_set(index+1)
return "break"
def setSort(self,index):
if self.sort==None:
self.sort=[index,1]
elif self.sort[0]==index:
self.sort[1]=-self.sort[1]
else:
self.sort=[index,1]
self._sort()
def _sort(self):
if self.disablesorting:
return
if self.sort==None:
return
ind,direc=self.sort
li=list(self.get(0,END))
li.sort(lambda x,y,i=ind,d=direc:d*cmp(x[i],y[i]))
self.delete(0,END)
for l in li:
self._insert(END,l)
def activate(self,index):
self._callall("activate",index)
# def bbox(self,index):
# return self._callall("bbox",index)
def curselection(self):
return self.lists[0].curselection()
def delete(self,*args):
apply(self._callall,("delete",)+args)
def get(self,*args):
bad=apply(self._callall,("get",)+args)
if len(args)==1:
return bad
ret=[]
for i in range(len(bad[0])):
r=[]
for j in range(len(bad)):
r.append(bad[j][i])
ret.append(r)
return ret
def index(self,index):
return self.lists[0].index(index)
def insert(self,index,items):
self._insert(index,items)
self._sort()
def _insert(self,index,items):
for i in range(len(items)):
self.lists[i].insert(index,items[i])
def nearest(self,y):
return self.lists[0].nearest(y)
def see(self,index):
self._callall("see",index)
def size(self):
return self.lists[0].size()
def selection_anchor(self,index):
self._callall("selection_anchor",index)
select_anchor=selection_anchor
def selection_clear(self,*args):
apply(self._callall,("selection_clear",)+args)
select_clear=selection_clear
def selection_includes(self,index):
return self.lists[0].select_includes(index)
select_includes=selection_includes
def selection_set(self,*args):
apply(self._callall,("selection_set",)+args)
select_set=selection_set
def xview(self,*args):
if not args: return self.lists[0].xview()
apply(self._callall,("xview",)+args)
def yview(self,*args):
if not args: return self.lists[0].yview()
apply(self._callall,("yview",)+args)
class ProgressBar:
def __init__(self, master=None, orientation="horizontal",
min=0, max=100, width=100, height=18,
doLabel=1, appearance="sunken",
fillColor="blue", background="gray",
labelColor="yellow", labelFont="Verdana",
labelText="", labelFormat="%d%%",
value=0, bd=2):
# preserve various values
self.master=master
self.orientation=orientation
self.min=min
self.max=max
self.width=width
self.height=height
self.doLabel=doLabel
self.fillColor=fillColor
self.labelFont= labelFont
self.labelColor=labelColor
self.background=background
self.labelText=labelText
self.labelFormat=labelFormat
self.value=value
self.frame=Frame(master, relief=appearance, bd=bd)
self.canvas=Canvas(self.frame, height=height, width=width, bd=0,
highlightthickness=0, background=background)
self.scale=self.canvas.create_rectangle(0, 0, width, height,
fill=fillColor)
self.label=self.canvas.create_text(self.canvas.winfo_reqwidth() / 2,
height / 2, text=labelText,
anchor="c", fill=labelColor,
font=self.labelFont)
self.update()
self.canvas.pack(side='top', fill='x', expand='no')
def updateProgress(self, newValue, newMax=None):
if newMax:
self.max = newMax
self.value = newValue
self.update()
def update(self):
# Trim the values to be between min and max
value=self.value
if value > self.max:
value = self.max
if value < self.min:
value = self.min
# Adjust the rectangle
if self.orientation == "horizontal":
self.canvas.coords(self.scale, 0, 0,
float(value) / self.max * self.width, self.height)
else:
self.canvas.coords(self.scale, 0,
self.height - (float(value) /
self.max*self.height),
self.width, self.height)
# Now update the colors
self.canvas.itemconfig(self.scale, fill=self.fillColor)
self.canvas.itemconfig(self.label, fill=self.labelColor)
# And update the label
if self.doLabel:
if value:
if value >= 0:
pvalue = int((float(value) / float(self.max)) *
100.0)
else:
pvalue = 0
self.canvas.itemconfig(self.label, text=self.labelFormat
% pvalue)
else:
self.canvas.itemconfig(self.label, text='')
else:
self.canvas.itemconfig(self.label, text=self.labelFormat %
self.labelText)
self.canvas.update_idletasks()
class DirectoryBrowser(_Dialog):
command = "tk_chooseDirectory"
def askdirectory(**options):
"Ask for a directory to save to."
return apply(DirectoryBrowser, (), options).show()
class GenericLogin(Toplevel):
def __init__(self,callback,buttons):
Toplevel.__init__(self)
self.callback=callback
Label(self,text="Twisted v%s"%copyright.version).grid(column=0,row=0,columnspan=2)
self.entries={}
row=1
for stuff in buttons:
label,value=stuff[:2]
if len(stuff)==3:
dict=stuff[2]
else: dict={}
Label(self,text=label+": ").grid(column=0,row=row)
e=apply(Entry,(self,),dict)
e.grid(column=1,row=row)
e.insert(0,value)
self.entries[label]=e
row=row+1
Button(self,text="Login",command=self.doLogin).grid(column=0,row=row)
Button(self,text="Cancel",command=self.close).grid(column=1,row=row)
self.protocol('WM_DELETE_WINDOW',self.close)
def close(self):
self.tk.quit()
self.destroy()
def doLogin(self):
values={}
for k in self.entries.keys():
values[string.lower(k)]=self.entries[k].get()
self.callback(values)
self.destroy()
class Login(Toplevel):
def __init__(self,
callback,
referenced = None,
initialUser = "guest",
initialPassword = "guest",
initialHostname = "localhost",
initialService = "",
initialPortno = pb.portno):
Toplevel.__init__(self)
version_label = Label(self,text="Twisted v%s" % copyright.version)
self.pbReferenceable = referenced
self.pbCallback = callback
# version_label.show()
self.username = Entry(self)
self.password = Entry(self,show='*')
self.hostname = Entry(self)
self.service = Entry(self)
self.port = Entry(self)
self.username.insert(0,initialUser)
self.password.insert(0,initialPassword)
self.service.insert(0,initialService)
self.hostname.insert(0,initialHostname)
self.port.insert(0,str(initialPortno))
userlbl=Label(self,text="Username:")
passlbl=Label(self,text="Password:")
servicelbl=Label(self,text="Service:")
hostlbl=Label(self,text="Hostname:")
portlbl=Label(self,text="Port #:")
self.logvar=StringVar()
self.logvar.set("Protocol PB-%s"%pb.Broker.version)
self.logstat = Label(self,textvariable=self.logvar)
self.okbutton = Button(self,text="Log In", command=self.login)
version_label.grid(column=0,row=0,columnspan=2)
z=0
for i in [[userlbl,self.username],
[passlbl,self.password],
[hostlbl,self.hostname],
[servicelbl,self.service],
[portlbl,self.port]]:
i[0].grid(column=0,row=z+1)
i[1].grid(column=1,row=z+1)
z = z+1
self.logstat.grid(column=0,row=6,columnspan=2)
self.okbutton.grid(column=0,row=7,columnspan=2)
self.protocol('WM_DELETE_WINDOW',self.tk.quit)
def loginReset(self):
self.logvar.set("Idle.")
def loginReport(self, txt):
self.logvar.set(txt)
self.after(30000, self.loginReset)
def login(self):
host = self.hostname.get()
port = self.port.get()
service = self.service.get()
try:
port = int(port)
except:
pass
user = self.username.get()
pswd = self.password.get()
pb.connect(host, port, user, pswd, service,
client=self.pbReferenceable).addCallback(self.pbCallback).addErrback(
self.couldNotConnect)
def couldNotConnect(self,f):
self.loginReport("could not connect:"+f.getErrorMessage())
if __name__=="__main__":
root=Tk()
o=CList(root,["Username","Online","Auto-Logon","Gateway"])
o.pack()
for i in range(0,16,4):
o.insert(END,[i,i+1,i+2,i+3])
mainloop()

View file

@ -0,0 +1,215 @@
# -*- test-case-name: twisted.test.test_pb -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Utility classes for spread.
"""
from twisted.internet import defer
from twisted.python.failure import Failure
from twisted.spread import pb
from twisted.protocols import basic
from twisted.internet import interfaces
from zope.interface import implements
class LocalMethod:
def __init__(self, local, name):
self.local = local
self.name = name
def __call__(self, *args, **kw):
return self.local.callRemote(self.name, *args, **kw)
class LocalAsRemote:
"""
A class useful for emulating the effects of remote behavior locally.
"""
reportAllTracebacks = 1
def callRemote(self, name, *args, **kw):
"""
Call a specially-designated local method.
self.callRemote('x') will first try to invoke a method named
sync_x and return its result (which should probably be a
Deferred). Second, it will look for a method called async_x,
which will be called and then have its result (or Failure)
automatically wrapped in a Deferred.
"""
if hasattr(self, 'sync_'+name):
return getattr(self, 'sync_'+name)(*args, **kw)
try:
method = getattr(self, "async_" + name)
return defer.succeed(method(*args, **kw))
except:
f = Failure()
if self.reportAllTracebacks:
f.printTraceback()
return defer.fail(f)
def remoteMethod(self, name):
return LocalMethod(self, name)
class LocalAsyncForwarder:
"""
A class useful for forwarding a locally-defined interface.
"""
def __init__(self, forwarded, interfaceClass, failWhenNotImplemented=0):
assert interfaceClass.providedBy(forwarded)
self.forwarded = forwarded
self.interfaceClass = interfaceClass
self.failWhenNotImplemented = failWhenNotImplemented
def _callMethod(self, method, *args, **kw):
return getattr(self.forwarded, method)(*args, **kw)
def callRemote(self, method, *args, **kw):
if self.interfaceClass.queryDescriptionFor(method):
result = defer.maybeDeferred(self._callMethod, method, *args, **kw)
return result
elif self.failWhenNotImplemented:
return defer.fail(
Failure(NotImplementedError,
"No Such Method in Interface: %s" % method))
else:
return defer.succeed(None)
class Pager:
"""
I am an object which pages out information.
"""
def __init__(self, collector, callback=None, *args, **kw):
"""
Create a pager with a Reference to a remote collector and
an optional callable to invoke upon completion.
"""
if callable(callback):
self.callback = callback
self.callbackArgs = args
self.callbackKeyword = kw
else:
self.callback = None
self._stillPaging = 1
self.collector = collector
collector.broker.registerPageProducer(self)
def stillPaging(self):
"""
(internal) Method called by Broker.
"""
if not self._stillPaging:
self.collector.callRemote("endedPaging")
if self.callback is not None:
self.callback(*self.callbackArgs, **self.callbackKeyword)
return self._stillPaging
def sendNextPage(self):
"""
(internal) Method called by Broker.
"""
self.collector.callRemote("gotPage", self.nextPage())
def nextPage(self):
"""
Override this to return an object to be sent to my collector.
"""
raise NotImplementedError()
def stopPaging(self):
"""
Call this when you're done paging.
"""
self._stillPaging = 0
class StringPager(Pager):
"""
A simple pager that splits a string into chunks.
"""
def __init__(self, collector, st, chunkSize=8192, callback=None, *args, **kw):
self.string = st
self.pointer = 0
self.chunkSize = chunkSize
Pager.__init__(self, collector, callback, *args, **kw)
def nextPage(self):
val = self.string[self.pointer:self.pointer+self.chunkSize]
self.pointer += self.chunkSize
if self.pointer >= len(self.string):
self.stopPaging()
return val
class FilePager(Pager):
"""
Reads a file in chunks and sends the chunks as they come.
"""
implements(interfaces.IConsumer)
def __init__(self, collector, fd, callback=None, *args, **kw):
self.chunks = []
Pager.__init__(self, collector, callback, *args, **kw)
self.startProducing(fd)
def startProducing(self, fd):
self.deferred = basic.FileSender().beginFileTransfer(fd, self)
self.deferred.addBoth(lambda x : self.stopPaging())
def registerProducer(self, producer, streaming):
self.producer = producer
if not streaming:
self.producer.resumeProducing()
def unregisterProducer(self):
self.producer = None
def write(self, chunk):
self.chunks.append(chunk)
def sendNextPage(self):
"""
Get the first chunk read and send it to collector.
"""
if not self.chunks:
return
val = self.chunks.pop(0)
self.producer.resumeProducing()
self.collector.callRemote("gotPage", val)
# Utility paging stuff.
class CallbackPageCollector(pb.Referenceable):
"""
I receive pages from the peer. You may instantiate a Pager with a
remote reference to me. I will call the callback with a list of pages
once they are all received.
"""
def __init__(self, callback):
self.pages = []
self.callback = callback
def remote_gotPage(self, page):
self.pages.append(page)
def remote_endedPaging(self):
self.callback(self.pages)
def getAllPages(referenceable, methodName, *args, **kw):
"""
A utility method that will call a remote method which expects a
PageCollector as the first argument.
"""
d = defer.Deferred()
referenceable.callRemote(methodName, CallbackPageCollector(d.callback), *args, **kw)
return d