I am following the SWIG tutorial and I'm currently on the section: "32.9.1 Converting Python list to a char **". The example in question returns a malloc error on my machine:
import example
example.print_args(["a","bc","dc"])
python(57911,0x10bd32e00) malloc: *** error for object 0x7f7ee0406b90: pointer being freed was not allocated
python(57911,0x10bd32e00) malloc: *** set a breakpoint in malloc_error_break to debug
1 57911 abort python
1 57911 abort python
The error is unexpected as this is exactly the code that the tutorial offers. Any help welcome! Thanks in advance
Specs:
MacOS Big Sur
Python 3.8
C++17
Here are my setup.py (the whole archive for reproducibility):
#!/usr/bin/env python
"""
setup.py file for SWIG example
"""
from distutils.core import setup, Extension
import os
import sys
import glob
# gather up all the source files
srcFiles = ['example.i']
includeDirs = []
srcDir = os.path.abspath('src')
for root, dirnames, filenames in os.walk(srcDir):
for dirname in dirnames:
absPath = os.path.join(root, dirname)
globStr = "%s/*.c*" % absPath
files = glob.glob(globStr)
includeDirs.append(absPath)
srcFiles += files
extra_args = ['-stdlib=libc++', '-mmacosx-version-min=10.7', '-std=c++17', '-fno-rtti']
os.environ["CC"] = 'clang++'
#
example_module = Extension('_example',
srcFiles, # + ['example.cpp'], # ['example_wrap.cxx', 'example.cpp'],
include_dirs=includeDirs,
swig_opts=['-c++'],
extra_compile_args=extra_args,
)
setup(name='example',
version='0.1',
author="SWIG Docs",
description="""Simple swig example from docs""",
ext_modules=[example_module],
py_modules=["example"],
)
The example code would work with Python 2, but has a bug as well as a syntax change for Python 3. char** must be passed byte strings, which are the default in Python 2 when using "string" syntax, but need a leading b, e.g. b"string" in Python 3.
This works:
import example
example.print_args([b"a",b"bc",b"dc"])
The crash is due to a bug calling free twice if an incorrect parameter type is found. Make the following change to the example:
if (PyString_Check(o)) {
$1[i] = PyString_AsString(PyList_GetItem($input, i));
} else {
//free($1); // REMOVE THIS FREE
PyErr_SetString(PyExc_TypeError, "list must contain strings");
SWIG_fail;
SWIG_fail; ends up called the freearg typemap, which calls free a second time. With this change, you should see the following if passing incorrect arguments, such as a non-list or Unicode strings instead of byte strings:
>>> import argv
>>> argv.print_args(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\argv.py", line 66, in print_args
return _argv.print_args(argv)
TypeError: not a list
>>> argv.print_args(['abc','def'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\argv.py", line 66, in print_args
return _argv.print_args(argv)
TypeError: list must contain strings
>>> argv.print_args([b'abc',b'def'])
argv[0] = abc
argv[1] = def
2
Changing the error message to "list must contain byte strings" would help as well 😊
Related
I need to use an existing library in my python application.
This is a library to read a particular data file.
If you are curious you can download it from here https://www.hbm.com/en/2082/somat-download-archive/ (somat libsie).
The first thing I need to do it open the file so my python scripts starts like:
import ctypes
hllDll = ctypes.WinDLL(r"libsie.dll")
context = hllDll.sie_context_new()
file = hllDll.sie_file_open(context, "test.sie".encode())
but I get the following error:
Traceback (most recent call last):
File "C:\Program Files\JetBrains\PyCharm 2019.3.5\plugins\python\helpers\pydev\_pydevd_bundle\pydevd_exec2.py", line 3, in Exec
exec(exp, global_vars, local_vars)
File "<input>", line 1, in <module>
OSError: exception: access violation reading 0x000000009F6C06B8
I verified that the .sie file is accessible.
I think the problem lies in the "context" object that gets passed as first argument. I think the type is the issue.
Here is part of the header file where context is defined:
typedef void sie_Context;
...
SIE_DECLARE(sie_Context *) sie_context_new(void);
/* > Returns a new library context. */
Am I calling these functions correctly?
Is there a problem with passing the context object?
Thanks in advance
On a 64-bit system, return values default to c_int (32-bit). At a minimum, set the .restype to at least a c_void_p to indicate a 64-bit pointer is returned.
Ideally, set .argtypes and .restype for each function called.
import ctypes
hllDll = ctypes.WinDLL(r"libsie.dll")
hllDll.sie_context_new.argtypes = () # optional but recommended
hllDll.sie_context_new.restype = ctypes.c_void_p # add this
hllDll.sie_context_new.argtypes = ctypes.c_void_p, ctypes.c_char_p # guess, need prototype
# hllDll.sie_context_new.restype = ???
context = hllDll.sie_context_new()
file = hllDll.sie_file_open(context, b"test.sie")
Consider the following MWE:
import hashlib
def tstfun(h: hashlib._hashlib.HASH):
print(h)
h = hashlib.md5()
tstfun(h)
# reveal_type(h)
Running this as-is yields - no surprise:
$ python mypytest.py
<md5 _hashlib.HASH object # 0x7fa645dedd90>
But checking this with mypy fails with:
$ mypy mypytest.py
mypytest.py:4: error: Name 'hashlib._hashlib.HASH' is not defined
Found 1 error in 1 file (checked 1 source file)
Now, revealing the type on h (commenting in that reveal_type line):
$ mypy mypytest.py
mypytest.py:4: error: Name 'hashlib._hashlib.HASH' is not defined
mypytest.py:10: note: Revealed type is 'hashlib._Hash'
Found 1 error in 1 file (checked 1 source file)
Well, ok, then changing the type hint from hashlib._hashlib.HASH to hashlib._Hash:
$ python mypytest.py
Traceback (most recent call last):
File "/radarugs/hintze/s4-cnc-tools/mypytest.py", line 4, in <module>
def tstfun(h: hashlib._HASH):
AttributeError: module 'hashlib' has no attribute '_HASH'
$ mypy mypytest.py
mypytest.py:4: error: Name 'hashlib._HASH' is not defined
Found 1 error in 1 file (checked 1 source file)
...which is the worst outcome.
How to check if the type stubs for the hashlib are correctly found and used by mypy? What else to check? What do I get wrong?
According to the traceback, you used hashlib._HASH.
With this code:
import hashlib
def tstfun(h: hashlib._Hash):
print(h)
h = hashlib.md5()
tstfun(h)
Mypy reports: Success: no issues found in 1 source file
Using hashlib._Hash is correct, but you also need to from __future__ import annotations if you don't want to use quotes. See https://github.com/python/typeshed/issues/2928
from __future__ import annotations
import hashlib
def tstfun(h: hashlib._Hash):
print(h)
h = hashlib.md5()
tstfun(h)
N.B.: __future__.annotations is available starting in python 3.7.0b1. See https://docs.python.org/3/library/__future__.html
I am learning python and I am trying to do a simple task of reading information from a config file.
So using the Python Doc and this similar problem as a reference I created two files.
This is my config file config.ini (also tried config.cfg)
[DEFAULT]
OutDir = path_to_file/
[AUTH]
TestInt = 100
TestStr = blue
TestParse = blua
and this is my python file test.py
import ConfigParser
from ConfigParser import *
config = ConfigParser()
config.read(config.cfg)
for name in config.options('AUTH'):
print name
out = config.get('DEFAULT', 'OutDir')
print 'Output directory is ' + out
however when running the command python test.py I am erroring out and receiving this error
Traceback (most recent call last):
File "test.py", line 7, in <module>
config.read(config.cfg)
AttributeError: ConfigParser instance has no attribute 'cfg'
Note: I thought that meant it the extension couldn't be read so I created the .ini file and changed it in the code and I received the same error but it instead read ...has no attribute 'ini'
I am not sure what I am doing wrong since I am doing the exact same as the python doc and the solution someone used to fix this similar issue.
config.read takes a string as its argument. You forgot to quote the file name, and config was coincidentally the name of a Python object (the module) that potentially could have a cfg attribute. You'd get an entirely different error if you had written config.read(foobarbaz.ini).
The correct line is
config.read('config.cfg') # or 'config.ini', if that's the file name
I have a method define in C/C++ DLL that takes 2 args
void SetLines(char** args,int argCount);
I need to call it from Python, what is the proper way to do so.
from ctypes import *
path="test.dll"
lib = cdll.LoadLibrary(path)
Lines=["line 2","line 2"]
lib.SetLines(Lines,len(lines))
print(code)
Excuting the Python code gives the following error:
Traceback (most recent call last):
File "<test.py>", line 6, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: Don't know how to convert parameter 1
After some code digging I figure it out:
any C/C++ parameter that accepts a pointer to a list of values should be wrapped in python with
MyType=ctypes.ARRAY(/*any ctype*/,len)
MyList=MyType()
and filled with
MyList[index]=/*that ctype*/
in mycase the solution was:
from ctypes import *
path="test.dll"
lib = cdll.LoadLibrary(path)
Lines=["line 1","line 2"]
string_pointer= ARRAY(c_char_p,len(Lines))
c_Lines=string_pointer()
for i in range(len(Lines)):
c_Lines[i]=c_char_p(Lines[i].encode("utf-8"))
lib.SetLines(c_Lines,len(lines))
import os
s = os.sys.stdin.buffer.read(1024*32)
failed with
D:\Projects\pytools>python t1.py
Traceback (most recent call last):
File "t1.py", line 2, in <module>
s = os.sys.stdin.buffer.read(1024*32)
OSError: [Errno 12] Not enough space
buf if given buflen = 1024*32-1 then it goes right
import os
s = os.sys.stdin.buffer.read(1024*32-1)
if you run python t1.py, then the process blocked and waiting for input as expect.
Why python3.3 have 1024*32-1 buffer length limitation? Is it system different, or just a the same for all systems? How can we remove this limitation?
BTW: i using windows 7 python 32 bit version 3.3
We start by looking at the source of os module here, where line 26 reads
import sys, errno
This tells us that os.sys is just a reference to the standard sys module.
Then we head over to the source of the sys module, where in line 1593 we find the following comment (thankfully someone put it there...):
/* stdin/stdout/stderr are now set by pythonrun.c */
Then we go to the pythonrun.c file, where we meet the following code in line 1086:
std = create_stdio(iomod, fd, 0, "<stdin>", encoding, errors);
and this on line 1091:
PySys_SetObject("stdin", std);
Then we look for definition of create_stdio() function which we find in line 910. We look for the return value of this function which is on line 999 and looks like this:
return stream;
Now we have to find out what the stream is. It's the return value of function _PyObject_CallMethodId() called in line 984.
I hope you see the flow - try to follow from here.