Skip to content

gh-143715: Deprecate incomplete initialization of struct.Struct()#145580

Open
serhiy-storchaka wants to merge 31 commits intopython:mainfrom
serhiy-storchaka:deprecate-struct-init
Open

gh-143715: Deprecate incomplete initialization of struct.Struct()#145580
serhiy-storchaka wants to merge 31 commits intopython:mainfrom
serhiy-storchaka:deprecate-struct-init

Conversation

@serhiy-storchaka
Copy link
Member

@serhiy-storchaka serhiy-storchaka commented Mar 6, 2026

  • Struct.__new__() will require a mandatory argument (format)
  • Calls of __init__() method on initialized Struct are deprecated

This is an evolution of #143659, but since virtually all code and test were rewritten I created a new PR.


📚 Documentation preview 📚: https://cpython-previews--145580.org.readthedocs.build/

skirpichev and others added 29 commits January 10, 2026 16:37
* ``Struct.__new__()`` will require a mandatory argument (format)
* Calls of ``__init__()`` method on initialized Struct are deprecated
This make format argument in the __init__() - optional.  If it's
missing, the object must be already initialized in __new__().
Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This catch current pattern for Struct's subclassing like

class MyStruct(Struct):
    def __init__(self):
        super().__init__('>h')
:meth:`~object.__init__` method on initialized :class:`~struct.Struct`
objects is deprecated and will be removed in Python 3.20.

(Contributed by Sergey B Kirpichev in :gh:`143715`.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(Contributed by Sergey B Kirpichev in :gh:`143715`.)
(Contributed by Serhiy Storchaka in :gh:`143715`.)

:meth:`~object.__init__` method on initialized :class:`~struct.Struct`
objects is deprecated and will be removed in Python 3.20.

(Contributed by Sergey B Kirpichev in :gh:`143715`.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(Contributed by Sergey B Kirpichev in :gh:`143715`.)
(Contributed by Serhiy Storchaka in :gh:`143715`.)

def check_sizeof(self, format_str, number_of_codes):
# The size of 'PyStructObject'
totalsize = support.calcobjsize('2n3P')
totalsize = support.calcobjsize('2n3P1?')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
totalsize = support.calcobjsize('2n3P1?')
totalsize = support.calcobjsize('2n3P?')

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be '2n3P?0P', with padding.

super().__init__('>h')

my_struct = MyStruct('>h')
self.assertEqual(my_struct.pack(12345), b'\x30\x39')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will be more clear, per Victor's suggestion:

Suggested change
self.assertEqual(my_struct.pack(12345), b'\x30\x39')
self.assertEqual(my_struct.format, '>h')

(and in all cases below too)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if self->s_format and and self->s_codes are de-synchronized? The original test used Struct.pack().

def __init__(self, *args, **kwargs):
super().__init__('>h')

my_struct = MyStruct('>h')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this not raises a warning? I think we should warn in all cases, where Struct.__init__() was explicitly called. // #143659 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this works with old and with new code. Both __new__ and __init__ take the same argument.

The code

class MyStruct(struct.Struct):
    def __init__(self, format):
        super().__init__(format)
        # some other initialization

works now and will work in future.


my_struct = MyStruct('>h')
self.assertEqual(my_struct.pack(12345), b'\x30\x39')
my_struct = MyStruct(format='>h')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I entirely forgot that format is a positional or keyword argument in the master. I'll fix my patch.

But again, this should emit a warning.

Comment on lines +880 to +888
# New way, no warnings:
class MyStruct(struct.Struct):
def __new__(cls, newargs, initargs):
return super().__new__(cls, *newargs)
def __init__(self, newargs, initargs):
if initargs is not None:
super().__init__(*initargs)

my_struct = MyStruct(('>h',), ('>h',))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, why no warnings here?

BTW, I doubt this usage pattern come from reality ;-)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because __new__() and __init__() are called with the same argument, as expected. You are supposed to do this if you write a code that works in all versions.

As for other cases, having both custom __new__() and __init__() which call corresponding parent's methods with different arguments is unusual, but this need to be tested. I found bugs in my code when added these tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants