1
# -*- test-case-name: twisted.test.test_stdio -*-
3
"""Standard input/out/err support.
7
support for stderr, perhaps
8
Rewrite to use the reactor instead of an ad-hoc mechanism for connecting
9
protocols to transport.
11
Maintainer: James Y Knight
15
from zope.interface import implements
17
from twisted.internet import process, error, interfaces
18
from twisted.python import log, failure
21
class PipeAddress(object):
22
implements(interfaces.IAddress)
25
class StandardIO(object):
26
implements(interfaces.ITransport, interfaces.IProducer, interfaces.IConsumer, interfaces.IHalfCloseableDescriptor)
32
def __init__(self, proto, stdin=0, stdout=1):
33
from twisted.internet import reactor
36
self._reader=process.ProcessReader(reactor, self, 'read', stdin)
37
self._reader.startReading()
38
self._writer=process.ProcessWriter(reactor, self, 'write', stdout)
39
self._writer.startReading()
40
self.protocol.makeConnection(self)
44
# XXX Actually, see #3597.
45
def loseWriteConnection(self):
46
if self._writer is not None:
47
self._writer.loseConnection()
49
def write(self, data):
50
if self._writer is not None:
51
self._writer.write(data)
53
def writeSequence(self, data):
54
if self._writer is not None:
55
self._writer.writeSequence(data)
57
def loseConnection(self):
58
self.disconnecting = True
60
if self._writer is not None:
61
self._writer.loseConnection()
62
if self._reader is not None:
63
# Don't loseConnection, because we don't want to SIGPIPE it.
64
self._reader.stopReading()
73
# Callbacks from process.ProcessReader/ProcessWriter
74
def childDataReceived(self, fd, data):
75
self.protocol.dataReceived(data)
77
def childConnectionLost(self, fd, reason):
81
if reason.value.__class__ == error.ConnectionDone:
84
self._readConnectionLost(reason)
86
self._writeConnectionLost(reason)
88
self.connectionLost(reason)
90
def connectionLost(self, reason):
91
self.disconnected = True
93
# Make sure to cleanup the other half
94
_reader = self._reader
95
_writer = self._writer
96
protocol = self.protocol
97
self._reader = self._writer = None
100
if _writer is not None and not _writer.disconnected:
101
_writer.connectionLost(reason)
103
if _reader is not None and not _reader.disconnected:
104
_reader.connectionLost(reason)
107
protocol.connectionLost(reason)
111
def _writeConnectionLost(self, reason):
113
if self.disconnecting:
114
self.connectionLost(reason)
117
p = interfaces.IHalfCloseableProtocol(self.protocol, None)
120
p.writeConnectionLost()
123
self.connectionLost(failure.Failure())
125
def _readConnectionLost(self, reason):
127
p = interfaces.IHalfCloseableProtocol(self.protocol, None)
130
p.readConnectionLost()
133
self.connectionLost(failure.Failure())
135
self.connectionLost(reason)
138
def registerProducer(self, producer, streaming):
139
if self._writer is None:
140
producer.stopProducing()
142
self._writer.registerProducer(producer, streaming)
144
def unregisterProducer(self):
145
if self._writer is not None:
146
self._writer.unregisterProducer()
149
def stopProducing(self):
150
self.loseConnection()
152
def pauseProducing(self):
153
if self._reader is not None:
154
self._reader.pauseProducing()
156
def resumeProducing(self):
157
if self._reader is not None:
158
self._reader.resumeProducing()
160
# Stupid compatibility:
161
def closeStdin(self):
162
"""Compatibility only, don't use. Same as loseWriteConnection."""
163
warnings.warn("This function is deprecated, use loseWriteConnection instead.",
164
category=DeprecationWarning, stacklevel=2)
165
self.loseWriteConnection()
167
def stopReading(self):
168
"""Compatibility only, don't use. Call pauseProducing."""
169
self.pauseProducing()
171
def startReading(self):
172
"""Compatibility only, don't use. Call resumeProducing."""
173
self.resumeProducing()