Showing with 528 additions and 74 deletions.
  1. +16 −23 twisted/internet/_pollingfile.py
  2. +16 −20 twisted/python/lockfile.py
  3. +95 −18 twisted/python/test/test_win32.py
  4. +315 −13 twisted/python/win32.py
  5. +86 −0 twisted/python/win32_kernel.h
39 changes: 16 additions & 23 deletions twisted/internet/_pollingfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,8 @@ def _pollEvent(self):
# If we ever (let's hope not) need the above functionality on UNIX, this could
# be factored into a different module.

import win32pipe
import win32file
import win32api
import pywintypes
from twisted.python import win32


class _PollableReadPipe(_PollableResource):

Expand All @@ -119,14 +117,14 @@ def checkWork(self):

while 1:
try:
buffer, bytesToRead, result = win32pipe.PeekNamedPipe(self.pipe, 1)
finished, bytesToRead = win32.PeekNamedPipe(self.pipe, 1)
# finished = (result == -1)
if not bytesToRead:
break
hr, data = win32file.ReadFile(self.pipe, bytesToRead, None)
hr, data = win32.ReadFile(self.pipe, bytesToRead)
fullDataRead.append(data)
except win32api.error:
finished = 1

except win32.WindowsAPIError:
break

dataBuf = ''.join(fullDataRead)
Expand All @@ -142,8 +140,8 @@ def cleanup(self):

def close(self):
try:
win32api.CloseHandle(self.pipe)
except pywintypes.error:
win32.CloseHandle(self.pipe)
except win32.WindowsAPIError:
# You can't close std handles...?
pass

Expand Down Expand Up @@ -172,11 +170,8 @@ def __init__(self, writePipe, lostCallback):
self.writePipe = writePipe
self.lostCallback = lostCallback
try:
win32pipe.SetNamedPipeHandleState(writePipe,
win32pipe.PIPE_NOWAIT,
None,
None)
except pywintypes.error:
win32.SetNamedPipeHandleState(writePipe, win32.kernel32.PIPE_NOWAIT)
except win32.WindowsAPIError:
# Maybe it's an invalid handle. Who knows.
pass

Expand Down Expand Up @@ -228,8 +223,8 @@ def unregisterProducer(self):
def writeConnectionLost(self):
self.deactivate()
try:
win32api.CloseHandle(self.writePipe)
except pywintypes.error:
win32.CloseHandle(self.writePipe)
except win32.WindowsAPIError:
# OMG what
pass
self.lostCallback()
Expand Down Expand Up @@ -274,17 +269,15 @@ def checkWork(self):
self.writeConnectionLost()
return 0
try:
win32file.WriteFile(self.writePipe, '', None)
except pywintypes.error:
win32.WriteFile(self.writePipe, "")
except win32.WindowsAPIError:
self.writeConnectionLost()
return numBytesWritten
while self.outQueue:
data = self.outQueue.pop(0)
errCode = 0
try:
errCode, nBytesWritten = win32file.WriteFile(self.writePipe,
data, None)
except win32api.error:
nBytesWritten = win32.WriteFile(self.writePipe, data)
except win32.WindowsAPIError:
self.writeConnectionLost()
break
else:
Expand Down
36 changes: 16 additions & 20 deletions twisted/python/lockfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from time import time as _uniquefloat

from twisted.python.runtime import platform
from twisted.python.win32 import WindowsAPIError, OpenProcess, kernel32

def unique():
return str(int(_uniquefloat() * 1000))
Expand All @@ -28,26 +29,21 @@ def unique():
else:
_windows = True

try:
from win32api import OpenProcess
import pywintypes
except ImportError:
kill = None
else:
ERROR_ACCESS_DENIED = 5
ERROR_INVALID_PARAMETER = 87

def kill(pid, signal):
try:
OpenProcess(0, 0, pid)
except pywintypes.error as e:
if e.args[0] == ERROR_ACCESS_DENIED:
return
elif e.args[0] == ERROR_INVALID_PARAMETER:
raise OSError(errno.ESRCH, None)
raise
else:
raise RuntimeError("OpenProcess is required to fail.")
# TODO: deprecate module level attributes?
ERROR_ACCESS_DENIED = kernel32.ERROR_ACCESS_DENIED
ERROR_INVALID_PARAMETER = kernel32.ERROR_INVALID_PARAMETER

def kill(pid, signal):
try:
OpenProcess(dwProcessId=pid)
except WindowsAPIError as e:
if e.args[0] == kernel32.ERROR_ACCESS_DENIED:
return
elif e.args[0] == kernel32.ERROR_INVALID_PARAMETER:
raise OSError(errno.ESRCH, None)
raise
else:
raise RuntimeError("OpenProcess is required to fail.")

_open = file

Expand Down
113 changes: 95 additions & 18 deletions twisted/python/test/test_win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"""

from twisted.trial import unittest
from twisted.python.runtime import platform
from twisted.python import win32
from twisted.python.compat import _PY3


class CommandLineQuotingTests(unittest.TestCase):
Expand Down Expand Up @@ -39,32 +39,109 @@ def test_emptyStringArg(self):
self.assertEqual(win32.cmdLineQuote(''), '""')


class TestWindowsLibrariesTest(unittest.TestCase):
"""
Tests for L{twisted.python.win32._getWindowsLibraries}.
"""
def test_setsUnicode(self):
"""
Tests to ensure that the resulting instance of cffi.FFI is
unicode. This is required for some of the types we're using
in the Windows api.
"""
ffi = win32._getWindowsLibraries()[0]
self.assertTrue(ffi._windows_unicode)


class ProgramPathsTests(unittest.TestCase):
class RaiseErrorIfZeroTests(unittest.TestCase):
"""
Tests for L{getProgramsMenuPath} and L{getProgramFilesPath}.
Tests for L{twisted.python.win32._raiseErrorIfZero}.
"""
def test_raisesTypeError(self):
"""
TypeError should be raised if the first argument
to _raiseErrorIfZero is not an integer.
"""
with self.assertRaises(TypeError):
win32._raiseErrorIfZero(1.0, "")

def test_raisesWindowsAPIError(self):
"""
Test that win32._raiseErrorIfZero(0, "") raises WindowsAPIError
"""
with self.assertRaises(win32.WindowsAPIError):
win32._raiseErrorIfZero(0, "")

def test_getProgramsMenuPath(self):
def test_noErrorForPositiveInt(self):
"""
L{getProgramsMenuPath} guesses the programs menu path on non-win32
platforms. On non-win32 it will try to figure out the path by
examining the registry.
Test that win32._raiseErrorIfZero(1, "") does nothing.
"""
if not platform.isWindows():
self.assertEqual(win32.getProgramsMenuPath(),
"C:\\Windows\\Start Menu\\Programs")
else:
self.assertIsInstance(win32.getProgramsMenuPath(), str)
win32._raiseErrorIfZero(1, "")

def test_noErrorForNegativeInt(self):
"""
Test that win32._raiseErrorIfZero(-1, "") does nothing.
This test exists to guard against a change that modifies the logic
of _raiseErrorIfZero from ``if ok == 0:`` to ``if ok >= 0`` or similar
statement. The type of errors _raiseErrorIfZero handles are
documented by Microsoft such that any non-zero value is considered
success. If this test breaks either _raiseErrorIfZero was updated on
purpose to allow for a new value or the value being passed into
_raiseErrorIfZero is incorrect and someone thought they found a bug.
"""
win32._raiseErrorIfZero(-1, "")

def test_getProgramFilesPath(self):
def test_allowsLongForOk(self):
"""
L{getProgramFilesPath} returns the "program files" path on win32.
In Python 2 int and long are two different things however in Python
3 there's only int. This test ensures we accept a long when it's
available because the Windows API can sometimes return a long even
though a number can fit within an int.
"""
self.assertIsInstance(win32.getProgramFilesPath(), str)
if not _PY3:
win32._raiseErrorIfZero(long(1), "")

if not platform.isWindows():
test_getProgramFilesPath.skip = (
"Cannot figure out the program files path on non-win32 platform")

class OpenProcessTests(unittest.TestCase):
"""
Tests for L{twisted.python.win32.OpenProcess}.
"""
def test_openFailure(self):
"""
The default arguments to OpenProcess should
cause an exception to be raised because we don't
assume what level of access someone will need.
"""
with self.assertRaises(win32.WindowsAPIError):
win32.OpenProcess()

def test_openFailureMessage(self):
"""
Tests the content of the error message. Normally this is not
something we'd test but in this case the exception arguments
are used elsewhere in the code base.
"""
try:
win32.OpenProcess()
except win32.WindowsAPIError as error:
self.assertEqual(
error.args, (
win32.kernel32.ERROR_ACCESS_DENIED,
"OpenProcess",
"Access is denied"
)
)


class CloseHandleTests(unittest.TestCase):
"""
Tests for L{twisted.python.win32.CloseHandle}.
"""
def test_closesReader(self):
"""
Creates two pipes, closes the reader and then attempts to
read from it (which we should not be able to do).
"""
reader, writer = win32.CreatePipe()
win32.CloseHandle(reader)
Loading