Ticket #2157: t-internet-t-test.2.patch

File t-internet-t-test.2.patch, 19.5 KB (added by John Popplewell, 6 years ago)

Ignore the attached _win32stdio, this fixed patch includes those changes

  • twisted/test/test_stdio.py

     
    189189
    190190        # p.onConnection.addCallback(lambda ign: __import__('time').sleep(5))
    191191
     192        usePTY = True
     193        if platform.isWindows():
     194            usePTY = False
    192195        try:
    193196            self._spawnProcess(
    194197                p, 'stdio_test_lastwrite.py', UNIQUE_LAST_WRITE_STRING,
    195                 usePTY=True)
     198                usePTY=usePTY)
    196199        except ValueError, e:
    197200            # Some platforms don't work with usePTY=True
    198201            raise unittest.SkipTest(str(e))
  • twisted/internet/_pollingfile.py

     
    44
    55"""
    66Implements a simple polling interface for file descriptors that don't work with
    7 select() - this is pretty much only useful on Windows.
     7C{select()} - this is pretty much only useful on Windows.
    88"""
    99
     10import sys
    1011from zope.interface import implements
    1112
     13import win32pipe
     14import win32file
     15import win32api
     16import pywintypes
     17
    1218from twisted.internet.interfaces import IConsumer, IPushProducer
    1319
    1420
     
    1622MAX_TIMEOUT = 0.1
    1723
    1824
     25class _PollableResource(object):
    1926
    20 class _PollableResource:
    2127    active = True
    2228
    2329    def activate(self):
    2430        self.active = True
    2531
    26 
    2732    def deactivate(self):
    2833        self.active = False
    2934
    3035
    31 
    32 class _PollingTimer:
     36class _PollingTimer(object):
    3337    # Everything is private here because it is really an implementation detail.
    3438
    3539    def __init__(self, reactor):
     
    98102# If we ever (let's hope not) need the above functionality on UNIX, this could
    99103# be factored into a different module.
    100104
    101 import win32pipe
    102 import win32file
    103 import win32api
    104 import pywintypes
     105class Channel(object):
     106    def closeRead(self):
     107        raise NotImplementedError()
    105108
    106 class _PollableReadPipe(_PollableResource):
     109    def closeWrite(self):
     110        raise NotImplementedError()
    107111
     112    def read(self, size):
     113        raise NotImplementedError()
     114
     115    def write(self, data):
     116        raise NotImplementedError()
     117
     118    def isWriteClosed(self):
     119        raise NotImplementedError()
     120
     121    def setEcho(self, enabled):
     122        raise NotImplementedError()
     123
     124
     125class ChannelReadPipe(Channel):
     126    def __init__(self, pipe):
     127        self.handle = pipe
     128
     129    def read(self):
     130        _, bytesToRead, _ = win32pipe.PeekNamedPipe(self.handle, 1)
     131        if not bytesToRead:
     132            return ''
     133        _, data = win32file.ReadFile(self.handle, bytesToRead, None)
     134        return data
     135
     136    def closeRead(self):
     137        try:
     138            win32api.CloseHandle(self.handle)
     139        except pywintypes.error:
     140            pass
     141
     142
     143class ChannelWritePipe(Channel):
     144    def __init__(self, pipe):
     145        self.handle = pipe
     146        try:
     147            win32pipe.SetNamedPipeHandleState(self.handle, win32pipe.PIPE_NOWAIT, None, None)
     148        except pywintypes.error:
     149            # Maybe it's an invalid handle.  Who knows.
     150            pass
     151
     152    def write(self, data):
     153        try:
     154            _, bytesWritten = win32file.WriteFile(self.handle, data, None)
     155        except win32api.error:
     156            return None
     157        return bytesWritten
     158
     159    def closeWrite(self):
     160        try:
     161            win32api.CloseHandle(self.handle)
     162        except pywintypes.error:
     163            pass
     164
     165    def isWriteClosed(self):
     166        try:
     167            win32file.WriteFile(self.handle, '', None)
     168            return False
     169        except pywintypes.error:
     170            return True
     171
     172
     173class ChannelConsole(Channel):
     174    def __init__(self):
     175        import win32conio
     176        self.console = win32conio.Channel()
     177
     178    def read(self):
     179        return self.console.read()
     180
     181    def write(self, data):
     182        try:
     183            bytesWritten = self.console.write(data)
     184        except (pywintypes.error,), err:
     185            if err.winerror == 8:
     186                # 'Not enough storage is available to process this command.'
     187                raise ValueError(err.strerror)
     188            return None
     189        return bytesWritten
     190
     191    def closeRead(self):
     192        self.console.closeRead()
     193
     194    def closeWrite(self):
     195        self.console.closeWrite()
     196
     197    def isWriteClosed(self):
     198        return self.console.isWriteClosed()
     199
     200    def setEcho(self, enabled):
     201        self.console.setEcho(enabled)
     202
     203
     204class _PollableReader(_PollableResource):
     205
    108206    implements(IPushProducer)
    109207
    110     def __init__(self, pipe, receivedCallback, lostCallback):
    111         # security attributes for pipes
    112         self.pipe = pipe
     208    def __init__(self, channel, receivedCallback, lostCallback):
     209        self.channel = channel
    113210        self.receivedCallback = receivedCallback
    114211        self.lostCallback = lostCallback
    115212
    116213    def checkWork(self):
    117214        finished = 0
    118215        fullDataRead = []
    119 
    120216        while 1:
    121217            try:
    122                 buffer, bytesToRead, result = win32pipe.PeekNamedPipe(self.pipe, 1)
    123                 # finished = (result == -1)
    124                 if not bytesToRead:
     218                data = self.channel.read()
     219                if not data:
    125220                    break
    126                 hr, data = win32file.ReadFile(self.pipe, bytesToRead, None)
    127221                fullDataRead.append(data)
    128             except win32api.error:
     222            except pywintypes.error:
    129223                finished = 1
    130224                break
    131 
    132225        dataBuf = ''.join(fullDataRead)
    133226        if dataBuf:
    134227            self.receivedCallback(dataBuf)
     
    141234        self.lostCallback()
    142235
    143236    def close(self):
    144         try:
    145             win32api.CloseHandle(self.pipe)
    146         except pywintypes.error:
    147             # You can't close std handles...?
    148             pass
     237        self.channel.closeRead()
    149238
    150239    def stopProducing(self):
    151240        self.close()
     
    157246        self.activate()
    158247
    159248
    160 FULL_BUFFER_SIZE = 64 * 1024
    161249
    162 class _PollableWritePipe(_PollableResource):
     250class _PollableWriter(_PollableResource):
     251    FULL_BUFFER_SIZE = 64 * 1024
    163252
    164253    implements(IConsumer)
    165254
    166     def __init__(self, writePipe, lostCallback):
     255    def __init__(self, channel, lostCallback):
    167256        self.disconnecting = False
    168257        self.producer = None
    169258        self.producerPaused = 0
    170259        self.streamingProducer = 0
    171260        self.outQueue = []
    172         self.writePipe = writePipe
     261        self.channel = channel
    173262        self.lostCallback = lostCallback
    174         try:
    175             win32pipe.SetNamedPipeHandleState(writePipe,
    176                                               win32pipe.PIPE_NOWAIT,
    177                                               None,
    178                                               None)
    179         except pywintypes.error:
    180             # Maybe it's an invalid handle.  Who knows.
    181             pass
    182263
    183264    def close(self):
    184265        self.disconnecting = True
     266        self.checkWork()
    185267
    186268    def bufferFull(self):
    187269        if self.producer is not None:
     
    227309
    228310    def writeConnectionLost(self):
    229311        self.deactivate()
    230         try:
    231             win32api.CloseHandle(self.writePipe)
    232         except pywintypes.error:
    233             # OMG what
    234             pass
     312        self.channel.closeWrite()
    235313        self.lostCallback()
    236314
    237 
    238315    def writeSequence(self, seq):
    239         """
    240         Append a C{list} or C{tuple} of bytes to the output buffer.
    241 
    242         @param seq: C{list} or C{tuple} of C{str} instances to be appended to
    243             the output buffer.
    244 
    245         @raise TypeError: If C{seq} contains C{unicode}.
    246         """
    247         if unicode in map(type, seq):
    248             raise TypeError("Unicode not allowed in output buffer.")
     316        if self.disconnecting:
     317            return
    249318        self.outQueue.extend(seq)
     319        if sum(map(len, self.outQueue)) > self.FULL_BUFFER_SIZE:
     320            self.bufferFull()
     321        self.checkWork()
    250322
    251 
    252323    def write(self, data):
    253         """
    254         Append some bytes to the output buffer.
    255 
    256         @param data: C{str} to be appended to the output buffer.
    257         @type data: C{str}.
    258 
    259         @raise TypeError: If C{data} is C{unicode} instead of C{str}.
    260         """
    261         if isinstance(data, unicode):
    262             raise TypeError("Unicode not allowed in output buffer.")
    263324        if self.disconnecting:
    264325            return
    265326        self.outQueue.append(data)
    266         if sum(map(len, self.outQueue)) > FULL_BUFFER_SIZE:
     327        if sum(map(len, self.outQueue)) > self.FULL_BUFFER_SIZE:
    267328            self.bufferFull()
     329        self.checkWork()
    268330
    269 
    270331    def checkWork(self):
    271         numBytesWritten = 0
    272332        if not self.outQueue:
    273             if self.disconnecting:
     333            if self.disconnecting or self.channel.isWriteClosed():
    274334                self.writeConnectionLost()
    275335                return 0
    276             try:
    277                 win32file.WriteFile(self.writePipe, '', None)
    278             except pywintypes.error:
    279                 self.writeConnectionLost()
    280                 return numBytesWritten
     336        totalBytesWritten = 0
    281337        while self.outQueue:
    282338            data = self.outQueue.pop(0)
    283             errCode = 0
     339            if isinstance(data, unicode):
     340                raise TypeError("unicode not allowed")
     341
    284342            try:
    285                 errCode, nBytesWritten = win32file.WriteFile(self.writePipe,
    286                                                              data, None)
    287             except win32api.error:
     343                bytesWritten = self.channel.write(data)
     344            except ValueError:
     345                # WriteConsole() has variable buffer length limitations.
     346                # Split data into two (roughly), put back into queue and
     347                # try again.
     348                len2 = len(data)/2
     349                d1, d2 = data[:len2], data[len2:]
     350                self.outQueue.insert(0, d2)
     351                self.outQueue.insert(0, d1)
     352                continue
     353            if bytesWritten is None:        # error occurred
    288354                self.writeConnectionLost()
    289355                break
    290             else:
    291                 # assert not errCode, "wtf an error code???"
    292                 numBytesWritten += nBytesWritten
    293                 if len(data) > nBytesWritten:
    294                     self.outQueue.insert(0, data[nBytesWritten:])
    295                     break
     356            totalBytesWritten += bytesWritten
     357            if len(data) > bytesWritten:
     358                self.outQueue.insert(0, data[bytesWritten:])
     359                break
    296360        else:
    297361            resumed = self.bufferEmpty()
    298362            if not resumed and self.disconnecting:
    299363                self.writeConnectionLost()
    300         return numBytesWritten
     364        return totalBytesWritten
     365
     366
     367# _pollingfile support
     368class _PollableReadConsole(_PollableReader):
     369    def __init__(self, channelConsole, receivedCallback, lostCallback):
     370        _PollableReader.__init__(self, channelConsole, receivedCallback, lostCallback)
     371
     372
     373class _PollableReadPipe(_PollableReader):
     374    def __init__(self, handle, receivedCallback, lostCallback):
     375        _PollableReader.__init__(self, ChannelReadPipe(handle), receivedCallback, lostCallback)
     376
     377
     378class _PollableWriteConsole(_PollableWriter):
     379    def __init__(self, channelConsole, lostCallback):
     380        _PollableWriter.__init__(self, channelConsole, lostCallback)
     381
     382
     383class _PollableWritePipe(_PollableWriter):
     384    def __init__(self, handle, lostCallback):
     385        _PollableWriter.__init__(self, ChannelWritePipe(handle), lostCallback)
     386
  • twisted/internet/test/test_pollingfile.py

     
    1414    _pollingfile = None
    1515
    1616
    17 
    1817class TestPollableWritePipe(TestCase):
    1918    """
    2019    Tests for L{_pollingfile._PollableWritePipe}.
    2120    """
    2221
    23     def test_writeUnicode(self):
     22    def test_checkWorkUnicode(self):
    2423        """
    25         L{_pollingfile._PollableWritePipe.write} raises a C{TypeError} if an
    26         attempt is made to append unicode data to the output buffer.
     24        When one tries to pass unicode to L{_pollingfile._PollableWritePipe}, a
     25        C{TypeError} is raised instead of passing the data to C{WriteFile}
     26        call which is going to mangle it.
    2727        """
    2828        p = _pollingfile._PollableWritePipe(1, lambda: None)
     29        p.write("test")
     30        p.checkWork()
    2931        self.assertRaises(TypeError, p.write, u"test")
    3032
    31 
    3233    def test_writeSequenceUnicode(self):
    3334        """
    3435        L{_pollingfile._PollableWritePipe.writeSequence} raises a C{TypeError}
     
    3637        output buffer.
    3738        """
    3839        p = _pollingfile._PollableWritePipe(1, lambda: None)
     40       
    3941        self.assertRaises(TypeError, p.writeSequence, [u"test"])
    40         self.assertRaises(TypeError, p.writeSequence, (u"test", ))
     42       
     43        self.assertRaises(TypeError, p.writeSequence, (u"test",))
    4144
    4245
    43 
    44 
    4546if _pollingfile is None:
    4647    TestPollableWritePipe.skip = "Test will run only on Windows."
  • twisted/internet/_dumbwin32proc.py

     
    144144        StartupInfo.hStdInput  = hStdinR
    145145        StartupInfo.dwFlags = win32process.STARTF_USESTDHANDLES
    146146
    147         # Create new handles whose inheritance property is false
    148         currentPid = win32api.GetCurrentProcess()
     147        win32api.SetHandleInformation(self.hStdoutR, win32con.HANDLE_FLAG_INHERIT, 0)
     148        win32api.SetHandleInformation(self.hStderrR, win32con.HANDLE_FLAG_INHERIT, 0)
     149        win32api.SetHandleInformation(self.hStdinW,  win32con.HANDLE_FLAG_INHERIT, 0)
    149150
    150         tmp = win32api.DuplicateHandle(currentPid, self.hStdoutR, currentPid, 0, 0,
    151                                        win32con.DUPLICATE_SAME_ACCESS)
    152         win32file.CloseHandle(self.hStdoutR)
    153         self.hStdoutR = tmp
    154 
    155         tmp = win32api.DuplicateHandle(currentPid, self.hStderrR, currentPid, 0, 0,
    156                                        win32con.DUPLICATE_SAME_ACCESS)
    157         win32file.CloseHandle(self.hStderrR)
    158         self.hStderrR = tmp
    159 
    160         tmp = win32api.DuplicateHandle(currentPid, self.hStdinW, currentPid, 0, 0,
    161                                        win32con.DUPLICATE_SAME_ACCESS)
    162         win32file.CloseHandle(self.hStdinW)
    163         self.hStdinW = tmp
    164 
    165151        # Add the specified environment to the current environment - this is
    166152        # necessary because certain operations are only supported on Windows
    167153        # if certain environment variables are present.
  • twisted/internet/_win32stdio.py

     
    1 # -*- test-case-name: twisted.test.test_stdio -*-
     1# -*- test-case-name: twisted.test.test_process.ProcessTestCase.testStdio,twisted.test.test_conio.StdIOTestCase -*-
    22
     3# Copyright (c) 2009 Twisted Matrix Laboratories.
     4# See LICENSE for details.
     5
    36"""
    47Windows-specific implementation of the L{twisted.internet.stdio} interface.
    58"""
     9import os
     10import sys
     11import errno
     12import msvcrt
     13import time
    614
     15import pywintypes
    716import win32api
    8 import os, msvcrt
    917
    1018from zope.interface import implements
    1119
    1220from twisted.internet.interfaces import IHalfCloseableProtocol, ITransport, IAddress
    1321from twisted.internet.interfaces import IConsumer, IPushProducer
     22from twisted.internet import main, abstract
    1423
    15 from twisted.internet import _pollingfile, main
     24import _pollingfile
    1625from twisted.python.failure import Failure
    1726
    1827
     
    2029    implements(IAddress)
    2130
    2231
     32class Win32ConsoleAddress(object):
     33    implements(IAddress)
    2334
     35
    2436class StandardIO(_pollingfile._PollingTimer):
    2537
    2638    implements(ITransport,
     
    3042    disconnecting = False
    3143    disconnected = False
    3244
    33     def __init__(self, proto):
     45    def __init__(self, proto, forceConsole=False):
    3446        """
    3547        Start talking to standard IO with the given protocol.
    3648
    37         Also, put it stdin/stdout/stderr into binary mode.
     49        Also, put stdin/stdout/stderr into binary mode.
    3850        """
    3951        from twisted.internet import reactor
    40 
    41         for stdfd in range(0, 1, 2):
     52       
     53        for stdfd in (0, 1, 2):
    4254            msvcrt.setmode(stdfd, os.O_BINARY)
    4355
    4456        _pollingfile._PollingTimer.__init__(self, reactor)
    4557        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)
    4688
    47         hstdin = win32api.GetStdHandle(win32api.STD_INPUT_HANDLE)
    48         hstdout = win32api.GetStdHandle(win32api.STD_OUTPUT_HANDLE)
    49 
    50         self.stdin = _pollingfile._PollableReadPipe(
    51             hstdin, self.dataReceived, self.readConnectionLost)
    52 
    53         self.stdout = _pollingfile._PollableWritePipe(
    54             hstdout, self.writeConnectionLost)
    55 
    5689        self._addPollableResource(self.stdin)
    5790        self._addPollableResource(self.stdout)
    5891
     
    75108
    76109    def checkConnLost(self):
    77110        self.connsLost += 1
    78         if self.connsLost >= 2:
     111        if self.connsLost >= self._disconnectCount:
    79112            self.disconnecting = True
    80113            self.disconnected = True
    81114            self.proto.connectionLost(Failure(main.CONNECTION_DONE))
    82115
    83116    # ITransport
    84117
     118    # XXX Actually, see #3597.
     119    def loseWriteConnection(self):
     120        self.stdout.close()
     121
    85122    def write(self, data):
    86123        self.stdout.write(data)
    87124
     
    90127
    91128    def loseConnection(self):
    92129        self.disconnecting = True
     130        self.disconnected = True
    93131        self.stdin.close()
    94132        self.stdout.close()
     133        if self.stderr:
     134            self.stderr.close()
    95135
     136    def setEcho(self, enabled):
     137        self.stdin.channel.setEcho(enabled)
     138
    96139    def getPeer(self):
    97         return Win32PipeAddress()
     140        if os.isatty(0) and os.isatty(1):
     141            return Win32ConsoleAddress()
     142        else:
     143            return Win32PipeAddress()
    98144
    99145    def getHost(self):
    100         return Win32PipeAddress()
     146        if os.isatty(0) and os.isatty(1):
     147            return Win32ConsoleAddress()
     148        else:
     149            return Win32PipeAddress()
    101150
     151
    102152    # IConsumer
    103153
    104154    def registerProducer(self, producer, streaming):