Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoraleherbau2011-05-24 05:08:27 -0400
committeraleherbau2011-05-24 05:08:27 -0400
commitbb40c1e5e91bbb68530efd57552fe731a314a1b2 (patch)
treeace1d17d2956731ff89da0ced1c67178109660d8
parentc27b35ce63b8e661e5669b76e1390aeb30a99fe7 (diff)
downloadorg.eclipse.tcf-bb40c1e5e91bbb68530efd57552fe731a314a1b2.tar.gz
org.eclipse.tcf-bb40c1e5e91bbb68530efd57552fe731a314a1b2.tar.xz
org.eclipse.tcf-bb40c1e5e91bbb68530efd57552fe731a314a1b2.zip
TCF Python: Implemented service proxies Streams and Terminals
-rw-r--r--python/src/tcf/services/remote/MemoryMapProxy.py6
-rw-r--r--python/src/tcf/services/remote/StreamsProxy.py145
-rw-r--r--python/src/tcf/services/remote/TerminalsProxy.py123
-rw-r--r--python/src/tcf/services/streams.py224
-rw-r--r--python/src/tcf/services/terminals.py222
-rw-r--r--python/src/tcf/transport.py5
-rw-r--r--python/todo.twiki4
7 files changed, 722 insertions, 7 deletions
diff --git a/python/src/tcf/services/remote/MemoryMapProxy.py b/python/src/tcf/services/remote/MemoryMapProxy.py
index 1897c3cba..8bf153bac 100644
--- a/python/src/tcf/services/remote/MemoryMapProxy.py
+++ b/python/src/tcf/services/remote/MemoryMapProxy.py
@@ -54,10 +54,8 @@ class MemoryMapProxy(memorymap.MemoryMapService):
self.listeners[listener] = l
def removeListener(self, listener):
- l = self.listeners.get(listener)
- if l:
- del self.listeners[listener]
- self.channel.removeEventListener(self, l)
+ l = self.listeners.pop(listener, None)
+ if l: self.channel.removeEventListener(self, l)
class ChannelEventListener(channel.EventListener):
def __init__(self, service, listener):
diff --git a/python/src/tcf/services/remote/StreamsProxy.py b/python/src/tcf/services/remote/StreamsProxy.py
new file mode 100644
index 000000000..3d700c488
--- /dev/null
+++ b/python/src/tcf/services/remote/StreamsProxy.py
@@ -0,0 +1,145 @@
+# *******************************************************************************
+# * Copyright (c) 2011 Wind River Systems, Inc. and others.
+# * All rights reserved. This program and the accompanying materials
+# * are made available under the terms of the Eclipse Public License v1.0
+# * which accompanies this distribution, and is available at
+# * http://www.eclipse.org/legal/epl-v10.html
+# *
+# * Contributors:
+# * Wind River Systems - initial API and implementation
+# *******************************************************************************
+
+from tcf.services import streams
+from tcf import channel
+from tcf.channel.Command import Command
+
+class StreamsProxy(streams.StreamsService):
+ def __init__(self, channel):
+ self.channel = channel
+
+ def connect(self, stream_id, done):
+ done = self._makeCallback(done)
+ service = self
+ class ConnectCommand(Command):
+ def __init__(self):
+ super(ConnectCommand, self).__init__(service.channel, service, "connect", (stream_id,))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneConnect(self.token, error)
+ return ConnectCommand().token
+
+ def disconnect(self, stream_id, done):
+ done = self._makeCallback(done)
+ service = self
+ class DisconnectCommand(Command):
+ def __init__(self):
+ super(DisconnectCommand, self).__init__(service.channel, service, "disconnect", (stream_id,))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneDisconnect(self.token, error)
+ return DisconnectCommand().token
+
+ def eos(self, stream_id, done):
+ done = self._makeCallback(done)
+ service = self
+ class EOSCommand(Command):
+ def __init__(self):
+ super(EOSCommand, self).__init__(service.channel, service, "eos", (stream_id,))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneEOS(self.token, error)
+ return EOSCommand().token
+
+ def read(self, stream_id, size, done):
+ done = self._makeCallback(done)
+ service = self
+ class ReadCommand(Command):
+ def __init__(self):
+ super(ReadCommand, self).__init__(service.channel, service, "read", (stream_id, size))
+ def done(self, error, args):
+ lost_size = 0
+ data = None
+ eos = False
+ if not error:
+ assert len(args) == 4
+ data = channel.toByteArray(args[0])
+ error = self.toError(args[1])
+ lost_size = args[2]
+ eos = args[3]
+ done.doneRead(self.token, error, lost_size, data, eos)
+ return ReadCommand().token
+
+ def subscribe(self, stream_type, listener, done):
+ done = self._makeCallback(done)
+ service = self
+ class SubscribeCommand(Command):
+ def __init__(self):
+ super(SubscribeCommand, self).__init__(service.channel, service, "subscribe", (stream_type,))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ if not error:
+ l = ChannelEventListener(service, listener)
+ service.listeners[listener] = l
+ service.channel.addEventListener(service, l)
+ done.doneSubscribe(self.token, error)
+ return SubscribeCommand().token
+
+ def unsubscribe(self, stream_type, listener, done):
+ done = self._makeCallback(done)
+ service = self
+ class UnsubscribeCommand(Command):
+ def __init__(self):
+ super(UnsubscribeCommand, self).__init__(service.channel, service, "unsubscribe", (stream_type,))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ if not error:
+ l = service.listeners.pop(listener, None)
+ if l: service.channel.removeEventListener(service, l)
+ done.doneUnsubscribe(self.token, error)
+ return UnsubscribeCommand().token
+
+ def write(self, stream_id, buf, offset, size, done):
+ done = self._makeCallback(done)
+ service = self
+ binary = buf[offset:offset+size]
+ class WriteCommand(Command):
+ def __init__(self):
+ super(WriteCommand, self).__init__(service.channel, service, "write", (stream_id, binary))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneWrite(self.token, error)
+ return WriteCommand().token
+
+
+class ChannelEventListener(channel.EventListener):
+ def __init__(self, service, listener):
+ self.service = service
+ self.listener = listener
+ def event(self, name, data):
+ try:
+ args = channel.fromJSONSequence(data)
+ if name == "created":
+ if len(args) == 3:
+ self.listener.created(args[0], args[1], args[2])
+ else:
+ assert len(args) == 2
+ self.listener.created(args[0], args[1], None)
+ elif name == "disposed":
+ assert len(args) == 2
+ self.listener.disposed(args[0], args[1])
+ else:
+ raise IOError("Streams service: unknown event: " + name);
+ except Exception as x:
+ self.service.channel.terminate(x)
diff --git a/python/src/tcf/services/remote/TerminalsProxy.py b/python/src/tcf/services/remote/TerminalsProxy.py
new file mode 100644
index 000000000..5e68c3406
--- /dev/null
+++ b/python/src/tcf/services/remote/TerminalsProxy.py
@@ -0,0 +1,123 @@
+# *******************************************************************************
+# * Copyright (c) 2011 Wind River Systems, Inc. and others.
+# * All rights reserved. This program and the accompanying materials
+# * are made available under the terms of the Eclipse Public License v1.0
+# * which accompanies this distribution, and is available at
+# * http://www.eclipse.org/legal/epl-v10.html
+# *
+# * Contributors:
+# * Wind River Systems - initial API and implementation
+# *******************************************************************************
+
+from tcf.services import terminals
+from tcf import channel
+from tcf.channel.Command import Command
+
+class TerminalContext(terminals.TerminalContext):
+ def __init__(self, service, props):
+ super(TerminalContext, self).__init__(props)
+ self.service = service
+
+ def exit(self, done):
+ service = self.service
+ done = service._makeCallback(done)
+ id = self.getID()
+ class ExitCommand(Command):
+ def __init__(self, cmd, args):
+ super(ExitCommand, self).__init__(service.channel, service, "exit", id)
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneCommand(self.token, error)
+ return ExitCommand().token
+
+
+class TerminalsProxy(terminals.TerminalsService):
+ def __init__(self, channel):
+ self.channel = channel
+ self.listeners = {}
+
+ def getContext(self, id, done):
+ done = self._makeCallback(done)
+ service = self
+ class GetContextCommand(Command):
+ def __init__(self):
+ super(GetContextCommand, self).__init__(service.channel, service, "getContext", (id,))
+ def done(self, error, args):
+ ctx = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ if args[1]: ctx = TerminalContext(service, args[1])
+ done.doneGetContext(self.token, error, ctx)
+ return GetContextCommand().token
+
+ def launch(self, type, encoding, environment, done):
+ done = self._makeCallback(done)
+ service = self
+ class LaunchCommand(Command):
+ def __init__(self):
+ super(LaunchCommand, self).__init__(service.channel, service, "launch", (type, encoding, environment))
+ def done(self, error, args):
+ ctx = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ if args[1]: ctx = TerminalContext(service, args[1])
+ done.doneLaunch(self.token, error, ctx)
+ return LaunchCommand().token
+
+ def setWinSize(self, context_id, newWidth, newHeight, done):
+ done = self._makeCallback(done)
+ service = self
+ class SetWinSizeCommand(Command):
+ def __init__(self):
+ super(SetWinSizeCommand, self).__init__(service.channel, service, "setWinSize", (context_id, newWidth, newHeight))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneCommand(self.token, error)
+ return SetWinSizeCommand().token
+
+ def exit(self, context_id, done):
+ done = self._makeCallback(done)
+ service = self
+ class ExitCommand(Command):
+ def __init__(self):
+ super(ExitCommand, self).__init__(service.channel, service, "exit", (context_id,))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneCommand(self.token, error)
+ return ExitCommand().token
+
+ def addListener(self, listener):
+ l = ChannelEventListener()
+ self.channel.addEventListener(self, l)
+ self.listeners[listener] = l
+
+ def removeListener(self, listener):
+ l = self.listeners.pop(listener, None)
+ if l: self.channel.removeEventListener(self, l)
+
+
+class ChannelEventListener(channel.EventListener):
+ def __init__(self, service, listener):
+ self.service = service
+ self.listener = listener
+ def event(self, name, data):
+ try:
+ args = channel.fromJSONSequence(data)
+ if name == "exited":
+ assert len(args) == 2
+ self.listener.exited(args[0], args[1])
+ elif name == "winSizeChanged":
+ assert len(args) == 3
+ self.listener.winSizeChanged(args[0], args[1], args[2])
+ else:
+ raise IOError("Terminals service: unknown event: " + name);
+ except Exception as x:
+ self.service.channel.terminate(x)
diff --git a/python/src/tcf/services/streams.py b/python/src/tcf/services/streams.py
new file mode 100644
index 000000000..77a210b86
--- /dev/null
+++ b/python/src/tcf/services/streams.py
@@ -0,0 +1,224 @@
+# *******************************************************************************
+# * Copyright (c) 2011 Wind River Systems, Inc. and others.
+# * All rights reserved. This program and the accompanying materials
+# * are made available under the terms of the Eclipse Public License v1.0
+# * which accompanies this distribution, and is available at
+# * http://www.eclipse.org/legal/epl-v10.html
+# *
+# * Contributors:
+# * Wind River Systems - initial API and implementation
+# *******************************************************************************
+
+"""
+Streams service is a generic interface to support streaming of data between host and remote agents.
+
+The service supports:
+ 1. Asynchronous overlapped data streaming: multiple 'read' or 'write' command can be issued at same time, both peers
+ can continue data processing concurrently with data transmission.
+ 2. Multicast: multiple clients can receive data from same stream.
+ 3. Subscription model: clients are required to expressed interest in particular streams by subscribing for the service.
+ 4. Flow control: peers can throttle data flow of individual streams by delaying 'read' and 'write' commands.
+"""
+
+from tcf import services
+
+NAME = "Streams"
+
+class StreamsService(services.Service):
+ def getName(self):
+ return NAME
+
+ def subscribe(self, stream_type, listener, done):
+ """
+ Clients must subscribe for one or more stream types to be able to send or receive stream data.
+ Subscribers receive notifications when a stream of given type is created or disposed.
+ Subscribers are required to respond with 'read' or 'disconnect' commands as necessary.
+ @param stream_type - the stream source type.
+ @param listener - client implementation of StreamsListener interface.
+ @param done - command result call back object.
+ @return - pending command handle.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def unsubscribe(self, stream_type, listener, done):
+ """
+ Unsubscribe the client from given stream source type.
+ @param stream_type - the stream source type.
+ @param listener - client implementation of StreamsListener interface.
+ @param done - command result call back object.
+ @return - pending command handle.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def read(self, stream_id, size, done):
+ """
+ Read data from a stream. If stream buffer is empty, the command will wait until data is available.
+ Remote peer will continue to process other commands while 'read' command is pending.
+ Client can send more 'read' commands without waiting for the first command to complete.
+ Doing that improves communication channel bandwidth utilization.
+ Pending 'read' commands will be executed in same order as issued.
+ Client can delay sending of 'read' command if it is not ready to receive more data,
+ however, delaying for too long can cause stream buffer overflow and lost of data.
+ @param stream_id - ID of the stream.
+ @param size - max number of bytes to read.
+ @param done - command result call back object.
+ @return - pending command handle.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def write(self, stream_id, buf, offset, size, done):
+ """
+ Write data to a stream. If stream buffer is full, the command will wait until space is available.
+ Remote peer will continue to process other commands while 'write' command is pending.
+ Client can send more 'write' commands without waiting for the first command to complete.
+ Doing that improves communication channel bandwidth utilization.
+ Pending 'write' commands will be executed in same order as issued.
+ @param stream_id - ID of the stream.
+ @param buf - buffer that contains stream data.
+ @param offset - byte offset in the buffer.
+ @param size - number of bytes to write.
+ @param done - command result call back object.
+ @return - pending command handle.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def eos(self, stream_id, done):
+ """
+ Send End Of Stream marker to a stream. No more writing to the stream is allowed after that.
+ @param stream_id - ID of the stream.
+ @param done - command result call back object.
+ @return - pending command handle.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def connect(self, stream_id, done):
+ """
+ Connect client to a stream.
+ Some data might be dropped from the stream by the time "connect" command is executed.
+ Client should be able to re-sync with stream data if it wants to read from such stream.
+ If a client wants to read a stream from the beginning it should use "subscribe" command
+ instead of "connect".
+ @param stream_id - ID of the stream.
+ @param done - command result call back object.
+ @return - pending command handle.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def disconnect(self, stream_id, done):
+ """
+ Disconnect client from a stream.
+ @param stream_id - ID of the stream.
+ @param done - command result call back object.
+ @return - pending command handle.
+ """
+ raise NotImplementedError("Abstract method")
+
+
+class StreamsListener(object):
+ """
+ Clients can implement StreamsListener interface to be notified
+ when a stream is created or disposed. The interface is registered with 'subscribe' command.
+
+ When new stream is created, client must decide if it is interested in that particular stream instance.
+ If not interested, client should send 'disconnect' command to allow remote peer to free resources and bandwidth.
+ If not disconnected, client is required to send 'read' commands as necessary to prevent stream buffer overflow.
+ """
+
+ def created(self, stream_type, stream_id, context_id):
+ """
+ Called when a new stream is created.
+ @param stream_type - source type of the stream.
+ @param stream_id - ID of the stream.
+ @param context_id - a context ID that is associated with the stream, or null.
+ Exact meaning of the context ID depends on stream type.
+ Stream types and context IDs are defined by services that use Streams service to transmit data.
+ """
+ pass
+
+ def disposed(self, stream_type, stream_id):
+ """
+ Called when a stream is disposed.
+ @param stream_type - source type of the stream.
+ @param stream_id - ID of the stream.
+ """
+ pass
+
+class DoneSubscribe(object):
+ """
+ Call back interface for 'subscribe' command.
+ """
+ def doneSubscribe(self, token, error):
+ pass
+
+class DoneUnsubscribe(object):
+ """
+ Call back interface for 'unsubscribe' command.
+ """
+ def doneUnsubscribe(self, token, error):
+ pass
+
+class DoneRead(object):
+ """
+ Call back interface for 'read' command.
+ """
+ def doneRead(self, token, error, lost_size, data, eos):
+ """
+ Called when 'read' command is done.
+ @param token - command handle.
+ @param error - error object or null.
+ @param lost_size - number of bytes that were lost because of buffer overflow.
+ 'lost_size' -1 means unknown number of bytes were lost.
+ if both 'lost_size' and 'data.length' are non-zero then lost bytes are considered
+ located right before read bytes.
+ @param data - bytes read from the stream.
+ @param eos - true if end of stream was reached.
+ """
+ pass
+
+class DoneWrite(object):
+ """
+ Call back interface for 'write' command.
+ """
+ def doneWrite(self, token, error):
+ """
+ Called when 'write' command is done.
+ @param token - command handle.
+ @param error - error object or null.
+ """
+ pass
+
+class DoneEOS(object):
+ """
+ Call back interface for 'eos' command.
+ """
+ def doneEOS(self, token, error):
+ """
+ Called when 'eos' command is done.
+ @param token - command handle.
+ @param error - error object or null.
+ """
+ pass
+
+class DoneConnect(object):
+ """
+ Call back interface for 'connect' command.
+ """
+ def doneConnect(self, token, error):
+ """
+ Called when 'connect' command is done.
+ @param token - command handle.
+ @param error - error object or null.
+ """
+ pass
+
+class DoneDisconnect(object):
+ """
+ Call back interface for 'disconnect' command.
+ """
+ def doneDisconnect(self, token, error):
+ """
+ Called when 'disconnect' command is done.
+ @param token - command handle.
+ @param error - error object or null.
+ """
+ pass
diff --git a/python/src/tcf/services/terminals.py b/python/src/tcf/services/terminals.py
new file mode 100644
index 000000000..f63f6decb
--- /dev/null
+++ b/python/src/tcf/services/terminals.py
@@ -0,0 +1,222 @@
+# *******************************************************************************
+# * Copyright (c) 2011 Wind River Systems, Inc. and others.
+# * All rights reserved. This program and the accompanying materials
+# * are made available under the terms of the Eclipse Public License v1.0
+# * which accompanies this distribution, and is available at
+# * http://www.eclipse.org/legal/epl-v10.html
+# *
+# * Contributors:
+# * Wind River Systems - initial API and implementation
+# *******************************************************************************
+
+"""
+ITerminalsService allows to launch a new terminal on the remote target system.
+"""
+
+from tcf import services
+
+# This service name, as it appears on the wire - a TCF name of the service.
+NAME = "Terminals"
+
+# Context property names.
+# The TCF context ID
+PROP_ID = "ID",
+
+# The process ID of the login process of the terminal
+PROP_PROCESS_ID = "ProcessID",
+
+# The PTY type
+PROP_PTY_TYPE = "PtyType",
+
+# terminal encoding
+PROP_ENCODING = "Encoding",
+
+# window width size
+PROP_WIDTH = "Width",
+
+# window height size
+PROP_HEIGHT = "Height",
+
+# Process standard input stream ID
+PROP_STDIN_ID = "StdInID",
+
+# Process standard output stream ID
+PROP_STDOUT_ID = "StdOutID",
+
+# Process standard error stream ID
+PROP_STDERR_ID = "StdErrID"
+
+class TerminalContext(object):
+ def __init__(self, props):
+ self._props = props or {}
+
+ def __str__(self):
+ return "[Terminals Context %s]" % str(self._props)
+
+ def getID(self):
+ """
+ Get context ID.
+ Same as getProperties().get(“ID”)
+ """
+ return self._props.get(PROP_ID)
+
+ def getProcessID(self):
+ """
+ Get process ID of the login process of the terminal.
+ Same as getProperties().get(“ProcessID”)
+ """
+ return self._props.get(PROP_PROCESS_ID)
+
+ def getPtyType(self):
+ """
+ Get terminal type.
+ Same as getProperties().get(“PtyType”)
+ """
+ return self._props.get(PROP_PTY_TYPE)
+
+ def getEncoding(self):
+ """
+ Get encoding.
+ Same as getProperties().get(“Encoding”)
+ """
+ return self._props.get(PROP_ENCODING)
+
+ def getWidth(self):
+ """
+ Get width.
+ Same as getProperties().get(“Width”)
+ """
+ return self._props.get(PROP_WIDTH)
+
+ def getHeight(self):
+ """
+ Get height.
+ Same as getProperties().get(“Height”)
+ """
+ return self._props.get(PROP_HEIGHT)
+
+ def getProperties(self):
+ """
+ Get all available context properties.
+ @return Map 'property name' -> 'property value'
+ """
+ return self._props
+
+ def exit(self, done):
+ """
+ Exit the terminal.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise NotImplementedError("Abstract method")
+
+class TerminalsService(services.Service):
+ def getName(self):
+ return NAME
+
+ def getContext(self, id, done):
+ """
+ Retrieve context info for given context ID.
+ A context corresponds to an terminal.
+ Context IDs are valid across TCF services, so it is allowed to issue
+ 'ITerminals.getContext' command with a context that was obtained,
+ for example, from Memory service.
+ However, 'ITerminals.getContext' is supposed to return only terminal specific data,
+ If the ID is not a terminal ID, 'ITerminals.getContext' may not return any
+ useful information
+
+ @param id – context ID.
+ @param done - call back interface called when operation is completed.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def launch(self, type, encoding, environment, done):
+ """
+ Launch a new terminal to remote machine.
+ @param type - requested terminal type for the new terminal.
+ @param encoding - requested encoding for the new terminal.
+ @param environment - Array of environment variable strings.
+ if null then default set of environment variables will be used.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def setWinSize(self, context_id, newWidth, newHeight, done):
+ """
+ Set the terminal widows size
+ @param context_id - context ID.
+ @param signal - signal code.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def exit(self, context_id, done):
+ """
+ Exit a terminal.
+ @param context_id - context ID.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def addListener(self, listener):
+ """
+ Add terminals service event listener.
+ @param listener - event listener implementation.
+ """
+ raise NotImplementedError("Abstract method")
+
+ def removeListener(self, listener):
+ """
+ Remove terminals service event listener.
+ @param listener - event listener implementation.
+ """
+ raise NotImplementedError("Abstract method")
+
+
+class DoneGetContext(object):
+ """
+ Client call back interface for getContext().
+ """
+ def doneGetContext(self, token, error, context):
+ """
+ Called when contexts data retrieval is done.
+ @param error – error description if operation failed, null if succeeded.
+ @param context – context data.
+ """
+ pass
+
+class DoneCommand(object):
+ def doneCommand(self, token, error):
+ pass
+
+class DoneLaunch(object):
+ """
+ Call-back interface to be called when "start" command is complete.
+ """
+ def doneLaunch(self, token, error, terminal):
+ pass
+
+class TerminalsListener(object):
+ """
+ Process event listener is notified when a terminal exits.
+ Event are reported only for terminals that were started by 'launch' command.
+ """
+ def exited(self, terminal_id, exit_code):
+ """
+ Called when a terminal exits.
+ @param terminal_id - terminal context ID
+ @param exit_code - terminal exit code
+ """
+ pass
+
+ def winSizeChanged (self, terminal_id, newWidth, newHeight):
+ """
+ Called when a terminal exits.
+ @param terminal_id - terminal context ID
+ @param newWidth – new terminal width
+ @param newHeight – new terminal height
+ """
+ pass
diff --git a/python/src/tcf/transport.py b/python/src/tcf/transport.py
index 1a046cee1..295f1c857 100644
--- a/python/src/tcf/transport.py
+++ b/python/src/tcf/transport.py
@@ -87,7 +87,10 @@ def addChannelOpenListener(listener):
_listeners.append(listener)
def removeChannelOpenListener(listener):
- _listeners.remove(listener)
+ try:
+ _listeners.remove(listener)
+ except ValueError:
+ pass # ignore
class TCPTransportProvider(TransportProvider):
def getName(self):
diff --git a/python/todo.twiki b/python/todo.twiki
index f3c814d6c..94f41f9e3 100644
--- a/python/todo.twiki
+++ b/python/todo.twiki
@@ -21,9 +21,9 @@
* [done] MemoryMap
* [done] Memory
* [done] PathMap
- * Diagnostics
+ * [done] Diagnostics
* Disassembly
- * Streams
+ * [done] Streams
* Terminals
* SysMonitor
* Local services:

Back to the top