Windows / ctypes issue with custom build
Eric Frederich
eric.frederich at gmail.com
Mon Mar 27 21:59:53 EDT 2017
I built my own Python 2.7.13 for Windows because I'm using bindings to a
3rd party application which were built with Visual Studio 2012.
I started to code up some stuff using the "click" module and found an error
when using click.echo with any kind of unicode input.
Python 2.7.13 (default, Mar 27 2017, 11:11:01) [MSC v.1700 64 bit
(AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import click
>>> click.echo('Hello World')
Hello World
>>> click.echo(u'Hello World')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\eric\my_env\lib\site-packages\click\utils.py", line
259, in echo
file.write(message)
File "C:\Users\eric\my_env\lib\site-packages\click\_winconsole.py",
line 180, in write
return self._text_stream.write(x)
File "C:\Users\eric\my_env\lib\site-packages\click\_compat.py", line
63, in write
return io.TextIOWrapper.write(self, x)
File "C:\Users\eric\my_env\lib\site-packages\click\_winconsole.py",
line 164, in write
raise OSError(self._get_error_message(GetLastError()))
OSError: Windows error 6
If I download and install the Python 2.7.13 64-bit installer I don't get
this issue. It echo's just fine.
I have looked into this a lot and am at a loss right now.
I'm not too familiar with Windows, Visual Studio, or ctypes.
I spent some time looking at the code path to produce the smallest file
(without click) which demonstrates this problem (see below)
It produces the same "Windows error 6"... again, this works fine with the
python installed from the 2.7.13 64 bit MSI installer.
Can someone share the process used to create the Windows installers? Is
this a manual process or is it automated?
Maybe I'm missing some important switch to msbuild or something. Any help
or ideas are appreciated.
I cannot use a downloaded copy of Python... it needs to be built with a
specific version, update, patch, etc of Visual Studio.
All I did was
1) clone cpython from github and checkout 2.7.13
2) edit some xp stuff out of tk stuff to get it to compile on Windows
Server 2003
In `externals\tk-8.5.15.0\win\Makefile.in` remove
`ttkWinXPTheme.$(OBJEXT)` line
In `externals\tk-8.5.15.0\win\makefile.vc` remove
`$(TMP_DIR)\ttkWinXPTheme.obj` line
In `externals\tk-8.5.15.0\win\ttkWinMonitor.c` remove 2
`TtkXPTheme_Init` lines
In `PCbuild\tcltk.props` change VC9 to VC11 at the bottom
3) PCbuild\build.bat -e -p x64 "/p:PlatformToolset=v110"
After that I created an "install" by copying .exe, .pyd, .dll files, ran
get-pip.py, then python -m pip install virtualenv, then virtualenv my_env,
then activated it, then did a pip install click.
But with this stripped down version you don't need pip, virtualenv or
click... just ctypes.
You could probably even build it without the -e switch to build.bat.
from ctypes import byref, POINTER, py_object, pythonapi, Structure,
windll
from ctypes import c_char, c_char_p, c_int, c_ssize_t, c_ulong, c_void_p
c_ssize_p = POINTER(c_ssize_t)
kernel32 = windll.kernel32
STDOUT_HANDLE = kernel32.GetStdHandle(-11)
PyBUF_SIMPLE = 0
MAX_BYTES_WRITTEN = 32767
class Py_buffer(Structure):
_fields_ = [
('buf', c_void_p),
('obj', py_object),
('len', c_ssize_t),
('itemsize', c_ssize_t),
('readonly', c_int),
('ndim', c_int),
('format', c_char_p),
('shape', c_ssize_p),
('strides', c_ssize_p),
('suboffsets', c_ssize_p),
('internal', c_void_p)
]
_fields_.insert(-1, ('smalltable', c_ssize_t * 2))
bites = u"Hello World".encode('utf-16-le')
bytes_to_be_written = len(bites)
buf = Py_buffer()
pythonapi.PyObject_GetBuffer(py_object(bites), byref(buf), PyBUF_SIMPLE)
buffer_type = c_char * buf.len
buf = buffer_type.from_address(buf.buf)
code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN)
// 2
code_units_written = c_ulong()
kernel32.WriteConsoleW(STDOUT_HANDLE, buf, code_units_to_be_written,
byref(code_units_written), None)
bytes_written = 2 * code_units_written.value
if bytes_written == 0 and bytes_to_be_written > 0:
raise OSError('Windows error %s' % kernel32.GetLastError())
More information about the Python-list
mailing list