FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-143715: Deprecate incomplete initialization of struct.Struct() by serhiy-storchaka · Pull Request #145580 · python/cpython · GitHub

Repository navigation

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

Merged
serhiy-storchaka merged 38 commits into
python:mainfrom
serhiy-storchaka:deprecate-struct-init
Mar 12, 2026
Merged

serhiy-storchaka merged 38 commits into
python:mainfrom
serhiy-storchaka:deprecate-struct-init

Conversation

serhiy-storchaka commented Mar 6, 2026 •
edited by github-actions Bot
Loading

Copy link
Copy Markdown
Member
  • 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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality
Suggested change
(Contributed by Sergey B Kirpichev in :gh:`143715`.)
(Contributed by Serhiy Storchaka in :gh:`143715`.)

Comment thread Doc/whatsnew/3.15.rst Outdated
: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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality
Suggested change
(Contributed by Sergey B Kirpichev in :gh:`143715`.)
(Contributed by Serhiy Storchaka in :gh:`143715`.)

Comment thread Lib/test/test_struct.py Outdated
def check_sizeof(self, format_str, number_of_codes):
# The size of 'PyStructObject'
totalsize = support.calcobjsize('2n3P')
totalsize = support.calcobjsize('2n3P1?')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality
Suggested change
totalsize = support.calcobjsize('2n3P1?')
totalsize = support.calcobjsize('2n3P?')

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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

Comment thread Lib/test/test_struct.py
super().__init__('>h')

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

The original test used Struct.pack().

That's true. But I think that with format field tests will be more clear.

What if self->s_format and and self->s_codes are de-synchronized?

I see, you test some such cases with bad characters.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I added assertions for format, but we need also functional tests, because internals can be initialized twice, and if something goes wrong, we will and with inconsistent format and pack(). See new cases in test_Struct_reinitialization. We should fix this in other issue.

Comment thread Lib/test/test_struct.py
def __init__(self, *args, **kwargs):
super().__init__('>h')

my_struct = MyStruct('>h')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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.

Comment thread Lib/test/test_struct.py

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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 thread Lib/test/test_struct.py
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Again, why no warnings here?

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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.

skirpichev self-requested a review March 8, 2026 00:10

skirpichev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This looks ok for me and have much better test coverage than my pr #143659.

Though, I worry that it's too complex: #143659 (comment)

Comment thread Lib/test/test_struct.py
super().__init__('>h')

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

The original test used Struct.pack().

That's true. But I think that with format field tests will be more clear.

What if self->s_format and and self->s_codes are de-synchronized?

I see, you test some such cases with bad characters.

serhiy-storchaka left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

The code is complex because the problem is complex.

We should minimize user inconvenience. If the code works in past and future versions, it should not emit warnings. There should be a way to write a warning-free code compatible with old versions (because you usually support more than one Python versions), and it should be valid in future versions.

If ignore this, we could just make a breaking change without deprecation period. And we do this if there is no other way (for example, the format attribute is now a string, not a bytes object like in older versions -- it was impossible to make this change not abrupt).

Comment thread Lib/test/test_struct.py
super().__init__('>h')

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I added assertions for format, but we need also functional tests, because internals can be initialized twice, and if something goes wrong, we will and with inconsistent format and pack(). See new cases in test_Struct_reinitialization. We should fix this in other issue.

Copy link
Copy Markdown
Member

There should be a way to write a warning-free code compatible with old versions (because you usually support more than one Python versions), and it should be valid in future versions.

As I said in #143659 (comment), this requirement looks too strong for me. To support several versions, people could also use if sys.version_info blocks.

CC @encukou, would you mind to review this pr?

Comment thread Modules/_struct.c Outdated
return -1;
}
Py_SETREF(self->s_format, format);
if (prepare_s(self)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

What do you think of restoring s_format to its previous value if prepare_s() fails?

Comment thread Lib/test/test_struct.py Outdated
def __new__(cls, *args, **kwargs):
return super().__new__(cls, '>h')

my_struct = MyStruct('>h')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

You can factorize some tests using ony 1 positional argument:

        for format in ('>h', '<h', 42, '$', '\u20ac'):
            with self.subTest(format=format):
                my_struct = MyStruct(format)
                self.assertEqual(my_struct.format, '>h')
                self.assertEqual(my_struct.pack(12345), b'\x30\x39')

Comment thread Lib/test/test_struct.py Outdated
with self.assertWarns(DeprecationWarning):
with self.assertRaises(struct.error):
s.__init__('$')
self.assertEqual(s.format, '$')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I would prefer to restore the old format if __init__() tries to set an invalid format string.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This is a different issue. See #145744.

encukou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I like this approach! Thank you for caring about backwards compatibility!

Copy link
Copy Markdown
Member Author

About deprecation warning in the repeated __init__(). I suggested this as a simple intermediate step, but if in future it will be identical to object.__init__(), which is no-op, there should not be warning for calling it with the same format (which is also no-op currently), but calling it with different format should emit a FutureWarning instead of a DeprecationWarning.

Accidentally, this also makes the code a little bit simpler.

serhiy-storchaka merged commit 7245630 into python:main Mar 12, 2026
91 of 92 checks passed
serhiy-storchaka deleted the deprecate-struct-init branch March 12, 2026 07:44
ljfp pushed a commit to ljfp/cpython that referenced this pull request Apr 25, 2026
…() (pythonGH-145580)

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

Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants


Back | FazBrowse Home | New Git URL