Ticket #2157: _win32stdio.py

File _win32stdio.py, 4.8 KB (added by John Popplewell, 6 years ago)

Fluffed it. Fixed version.

Line 
1# -*- test-case-name: twisted.test.test_process.ProcessTestCase.testStdio,twisted.test.test_conio.StdIOTestCase -*-
2
3# Copyright (c) 2009 Twisted Matrix Laboratories.
4# See LICENSE for details.
5
6"""
7Windows-specific implementation of the L{twisted.internet.stdio} interface.
8"""
9import os
10import sys
11import errno
12import msvcrt
13import time
14
15import pywintypes
16import win32api
17
18from zope.interface import implements
19
20from twisted.internet.interfaces import IHalfCloseableProtocol, ITransport, IAddress
21from twisted.internet.interfaces import IConsumer, IPushProducer
22from twisted.internet import main, abstract
23
24import _pollingfile
25from twisted.python.failure import Failure
26
27
28class Win32PipeAddress(object):
29    implements(IAddress)
30
31
32class Win32ConsoleAddress(object):
33    implements(IAddress)
34
35
36class StandardIO(_pollingfile._PollingTimer):
37
38    implements(ITransport,
39               IConsumer,
40               IPushProducer)
41
42    disconnecting = False
43    disconnected = False
44
45    def __init__(self, proto, forceConsole=False):
46        """
47        Start talking to standard IO with the given protocol.
48
49        Also, put stdin/stdout/stderr into binary mode.
50        """
51        from twisted.internet import reactor
52       
53        for stdfd in (0, 1, 2):
54            msvcrt.setmode(stdfd, os.O_BINARY)
55
56        _pollingfile._PollingTimer.__init__(self, reactor)
57        self.proto = proto
58       
59        # Check if we are connected to a console.
60        # If this is the case, connect to the console, else connect to
61        # anonymous pipes.
62        if forceConsole or os.isatty(0) or os.isatty(1) or os.isatty(2):
63            import win32conio
64            console = win32conio.Console()
65            self.stdin = _pollingfile._PollableReadConsole(
66                console, self.dataReceived, self.readConnectionLost
67                )
68            self.stdout = _pollingfile._PollableWriteConsole(
69                console, self.writeConnectionLost
70                )
71            self.stderr = None
72            self._disconnectCount = 2
73        else:
74            hstdin = win32api.GetStdHandle(win32api.STD_INPUT_HANDLE)
75            self.stdin = _pollingfile._PollableReadPipe(
76                hstdin, self.dataReceived, self.readConnectionLost
77                )
78            hstdout = win32api.GetStdHandle(win32api.STD_OUTPUT_HANDLE)
79            self.stdout = _pollingfile._PollableWritePipe(
80                hstdout, self.writeConnectionLost
81                )
82            hstderr = win32api.GetStdHandle(win32api.STD_ERROR_HANDLE)
83            self.stderr = _pollingfile._PollableWritePipe(
84                hstderr, self.writeConnectionLost
85                )
86            self._disconnectCount = 3
87            self._addPollableResource(self.stderr)
88
89        self._addPollableResource(self.stdin)
90        self._addPollableResource(self.stdout)
91
92        self.proto.makeConnection(self)
93
94    def dataReceived(self, data):
95        self.proto.dataReceived(data)
96
97    def readConnectionLost(self):
98        if IHalfCloseableProtocol.providedBy(self.proto):
99            self.proto.readConnectionLost()
100        self.checkConnLost()
101
102    def writeConnectionLost(self):
103        if IHalfCloseableProtocol.providedBy(self.proto):
104            self.proto.writeConnectionLost()
105        self.checkConnLost()
106
107    connsLost = 0
108
109    def checkConnLost(self):
110        self.connsLost += 1
111        if self.connsLost >= self._disconnectCount:
112            self.disconnecting = True
113            self.disconnected = True
114            self.proto.connectionLost(Failure(main.CONNECTION_DONE))
115
116    # ITransport
117
118    # XXX Actually, see #3597.
119    def loseWriteConnection(self):
120        self.stdout.close()
121
122    def write(self, data):
123        self.stdout.write(data)
124
125    def writeSequence(self, seq):
126        self.stdout.write(''.join(seq))
127
128    def loseConnection(self):
129        self.disconnecting = True
130        self.disconnected = True
131        self.stdin.close()
132        self.stdout.close()
133        if self.stderr:
134            self.stderr.close()
135
136    def setEcho(self, enabled):
137        self.stdin.channel.setEcho(enabled)
138
139    def getPeer(self):
140        if os.isatty(0) and os.isatty(1):
141            return Win32ConsoleAddress()
142        else:
143            return Win32PipeAddress()
144
145    def getHost(self):
146        if os.isatty(0) and os.isatty(1):
147            return Win32ConsoleAddress()
148        else:
149            return Win32PipeAddress()
150
151
152    # IConsumer
153
154    def registerProducer(self, producer, streaming):
155        return self.stdout.registerProducer(producer, streaming)
156
157    def unregisterProducer(self):
158        return self.stdout.unregisterProducer()
159
160    # def write() above
161
162    # IProducer
163
164    def stopProducing(self):
165        self.stdin.stopProducing()
166
167    # IPushProducer
168
169    def pauseProducing(self):
170        self.stdin.pauseProducing()
171
172    def resumeProducing(self):
173        self.stdin.resumeProducing()
174