-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
String seems incorrect #77
Comments
|
And, I really am confused here, since it looks like these definitions have been around quite a while... I want to blame me - but can't see anything I changed on my end... I'll try and create a smaller test case... |
|
OK. I ran the tests. They all pass on 2.7.17 and 3.6.8, but 6 of them fail on Python 3.7.4. The failed tests are: Curiously, the test I added while attempting to reproduce the problem works so far (but it's overly simplistic). There is a 3.7.6 out there, and of course, there's 3.8 to test against. I'll see if I can try those. |
|
OK. It fails on 3.8.1 just like on 3.7.6. |
|
OK. It looks like the other tests fail for similar reasons as my complaint: All 6 fail for the same reason. I'll try and figure out why the code passed our CI processes. My guess is that it wasn't running 3.7 or 3.8. |
|
OK. It appears that this worked with 3.7.1, but fails with 3.7.4 and beyond... |
|
OK... In Travis, it seems to work with Python 3.7.5. I appear to have been testing with 3.7.6. |
|
There appear to be a number ctypes fixes recently. Not sure where the problem was introduced... |
|
It passes on 3.6.9 also. |
|
The particular piece of code that is causing the exception is here: This is in the function And here's where that behavior/bug was introduced: |
|
OK. This appears to have been introduced with this change: A very deliberate change. I'm guessing that ctypesgen is generating technically incorrect object descriptions. |
|
Python 3.6 seems to work fine too. So, it is broken for all versions of Python >= 3.7.6. |
This is because ctypesgen generates Python code which Python 3.7.x (for x greater than some value), and Python 3.8 doesn't think works correctly. You can read about this here: ctypesgen/ctypesgen#77 Hopefully this restriction will eventually go away, but as of now that's the best choice. The good news is that the CMA demo starts up apparently without problems, and also the unit tests run without complaint. YAY!!
|
Yes, this was a deliberate change to fix long-standing Python issue bpo-16575. If you think there is a bug in the logic of the fix, feel free to open a new Python issue with a minimal script demonstrating the bug. |
|
I understood that. I'm not sure why the ctypesgen code generates String references the way it does, but it definitely broke every ctypesgen-generated string reference. |
|
@vsajip I'll try and create a small example that reproduces the problem. Then you can tell me if the example is wrong, or the patch could be improved. But I have a workaround for the Assimilation project - now that I know what the issue is, and I have an Assimilation release to get out in the next few days. |
|
I have actually wondered about this bit of string handling done by ctypesgen. I am not entirely sure if this special handling is currently necessary or if the original purpose it served is warranted. Taking a closer look at this, it seems that, as an argument, the "String" class at best helps to automatically convert various things into a byte array. I personally think that the bytes type should be required to be used at the c-interface when a "const char *" parameter is required -- let ctypes handle the automatic conversion to the c_char_p; there is no real sense in trying to reimplement the automatic type conversion already happening in ctypes. Other auto-conversion I think are misplaced, especially in light of the very specific differences between str and bytes. As a return value, it appears that the intent was to allow non-const char* types to be returned as mutable strings. Playing around with this---it does not seem that this actually works correctly. Replacing this with a simpler ctypes.Union appears to work. Since I don't currently have >py36, I'd appreciate someone else trying this for me for newer python versions to see what happens: import ctypes, ctypes.util
class Ustr(ctypes.Union):
_fields_ = [("raw", ctypes.POINTER(ctypes.c_char)), ("data", ctypes.c_char_p)]
def __bytes__(self):
return self.data
def __str__(self):
return self.data.decode()
def __repr__(self):
return repr(self.data)
def __getitem__(self, index):
if isinstance(index, int):
return chr(self.data[index]).encode()
return self.data[index]
def __setitem__(self, index, sub):
if isinstance(index, int):
self.raw[index] = sub
return
assert (index.stop - index.start) == len(sub), 'bytes array size mismatch'
for i,c in zip(range(index.start, index.stop, index.step if index.step else 1), sub):
self.raw[i] = c
clib = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
clib.getenv.argtypes = [ctypes.c_char_p]
clib.getenv.restype = Ustr
# now try changing the mutable environmental string:
orig_home = clib.getenv(b'HOME')
print('HOME was originally: ', orig_home)
orig_home[2] = b'*'
print('Modified HOME variable to: ', orig_home)
print('Fresh from environment: ', clib.getenv(b'HOME'))
if bytes(orig_home) == bytes(clib.getenv(b'HOME')):
print('SUCCESS: was able to change mutable string')
else:
print('FAIL!!!: could not change supposedly mutable string') |
|
@olsonse: If you're running Linux, it's easy to get a variety of Python versions available. If you send me a separate email, I can help you with that. It seems like it would be handy in order to be able to run more test environments both inside and outside of Tox. |
|
|
The GRASS GIS project is using ctypesgen for a long time (thanks!) but now facing problems as it fails with Python 3.7.6+. While we could work-around it would be nice to see this fixed here. Please see the related bug report: |
The capture_output paramter was introduced in Python 3.7. There is a PPA for Ubuntu 18 that allows 3.7.9 to be installed, however this has other issues due to ctypesgen/ctypesgen#77
|
Strange. Just tried this with Py 3.7.5 and it seems to have had no problems, including the return value being mutable as expected. On the other hand: Any thoughts on replacing this interface with something less wonky? I propose the following changes:
These changes should make the generated code:
Update: |
|
I think this was caused by a regression in Python. It took a long time to get fixed everywhere. |
|
Revisiting the situation now that I'm a bit less new to ctypes, I think I strongly approve of @olsonse's suggestions. IMO, explicit string encoding/decoding would be much better anyway, because implicit conversion is not guaranteed to work in the general case and can well produce inappropriate/confusing results. It always depends on the encoding the C function expects, whether NUL-termination is needed, etc. Concerning backwards (in)compatibility, I see two options:
|
I ran into some problems, and need help with them (version 1.0.1-1.0.2 on python 3.7.4):
The C source in question looks like this:
The corresponding header file looks like this:
WINEXPORT gpointer proj_class_new(gsize objsize, const char * static_classname);The second argument is clearly a pointer, not a call by value - of anything.
The generated Python looks like this:
String is a generated class, which looks like this:
As an experiment, I removed the Union from the String definition, and then it seemed to work a bit better...
A few milliseconds later, I got this error:
File "/home/alanr/monitor/src/cma/AssimCtypes.py", line 1028, in
('priv', POINTER(GSourcePrivate)),
TypeError: second item in fields tuple (index 11) must be a C type
The code in question looks like this:
It appears that String is not a legal field type. So, this is a problem...
-- Alan
The text was updated successfully, but these errors were encountered: