Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
274 views
in Technique[技术] by (71.8m points)

python - ctypes.Structure.from_buffer_copy doesn't check for c_char-type

I want to create ctypes.Structure-like object from bytes with Object(bytes_buffer)-like pattern.

According to https://stackoverflow.com/a/11486058 I use __new__ factory

from ctypes import Structure, c_char, c_uint8

ARRAY_LENGTH = 5

class Person(Structure):
     _fields_ = [("name", c_char * ARRAY_LENGTH),
                 ("age", c_uint8)]
     def __new__(cls, buf):
        return cls.from_buffer_copy(buf)

When calling Structure.from_buffer_copy it looks like the method doesn't check the character (and it looks like Python-style)

>>> p = Person.from_buffer_copy(b"Piterx10")
>>> print(p.name, p.age)
... b'Piter' 16

But when calling from_buffer_copy from Structure.__new__ the method checks character when interpreting c_char * 5 (and it looks like C-style)

>>> p = Person(b"Piterx10")
Traceback (most recent call last):
  File "/home/denis/person.py", line 69, in <module>
    p = Person(b"Piterx10")
ValueError: bytes too long (6, maximum length 5)

But using C-style string works well in both cases:

ARRAY_LENGTH = 6

class Person(Structure):
     _fields_ = [("name", c_char * ARRAY_LENGTH),
                 ("age", c_uint8)]
     def __new__(cls, buf):
        return cls.from_buffer_copy(buf)
>>> p = Person.from_buffer_copy(b"Piterx10")
>>> print(p.name, p.age)
... b'Piter' 16
>>> p = Person(b"Piterx10")
>>> print(p.name, p.age)
... b'Piter' 16

So

  1. Person.from_buffer_copy(b"Piterx10") interprets full array length when is not presented.
  2. Person(b"Piterx10") requires the .

I have no idea why it's so. Is it Python feature?

I run code with both Python 2 and Python 3

Python 3.8.5 (default, Sep  4 2020, 07:30:14) 
[GCC 7.3.0] on linux

and

Python 2.7.18 (default, Aug  4 2020, 11:16:42) 
[GCC 9.3.0] on linux2

(used "Piterx10" instead of b"Piterx10")


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

you've missed the empty __init__

from ctypes import Structure, c_char, c_uint8

ARRAY_LENGTH = 5

class Person(Structure):
     _fields_ = [("name", c_char * ARRAY_LENGTH),
                 ("age", c_uint8)]
     def __new__(cls, buf):
        return cls.from_buffer_copy(buf)
     def __init__(*args):
        pass
p = Person(b"Piterx10")
print(p.name)

works just fine

you can read more about __new__ here

Briefly explained, __new__ gets called before __init__ and does what you want it to do, but then __init__ gets called anyway and it doesnt know how to use your input


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...