Skip to content

Python 3.15 support#186

Merged
rocky merged 20 commits into
rocky:masterfrom
Quinntyx:python-3.15-support
Jun 2, 2026
Merged

Python 3.15 support#186
rocky merged 20 commits into
rocky:masterfrom
Quinntyx:python-3.15-support

Conversation

@Quinntyx

Copy link
Copy Markdown
Contributor

Implements Python 3.15 support.

This pull request aims to accomplish the following:

  • Implement unmarshal.py support for the new frozendict builtin type (and add tests for it)
    • Create a polyfill type in cross_types.py to facilitate disassembling 3.15.0 code on pre-3.15 versions
  • Implement support for lazy/eager imports
  • Renumber opcodes to match 3.15's new opcode numbers
  • Update stack effect numbers to match new changes (affected: END_SEND, POP_ITER, GET_ITER, CALL_LIST_APPEND, SEND_GEN, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN)
  • Implement support for new opcodes/instructions (BINARY_OP_SUBSCR_USTR_INT, CALL_EX_NON_PY_GENERAL, CALL_EX_PY, FOR_ITER_VIRTUAL, GET_ITER_SELF, GET_ITER_VIRTUAL, RESUME_CHECK_JIT, SEND_ASYNC_GEN, SEND_VIRTUAL)
  • Add the 3.15.0b1 magic number (3666), which should hopefully be the 3.15.0 magic number as well

There may be things I've missed. In particular, I haven't really touched any of the formatting code. I'll also keep testing this in the coming weeks to make sure it's fully correct.

Quinntyx added 20 commits May 27, 2026 16:15
…t of None out of co_consts and into LOAD_COMMON_CONSTS
Frozendict and big_dict disassembly reference files for 3.15.
import struct
import time

# 1. Compile a simple dummy script

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

It would be nice to remove the (Gemini?) AI numbers. I think the motivation for these is so that when you are in conversation with Gemini or AI, it is easy for you to refer to various steps. However, in the long term, the numbers are going to get in the way and become out of order.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I will do this after merge.

Comment thread test/test_pythonlib.py
# ----- configure this for your needs

lib_prefix = "/usr/lib"
lib_prefix = os.environ.get("XDIS_LIB_PREFIX", "/usr/lib")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Probably a good idea. Thanks!

Comment thread test_unit/test_marsh.py
source_size) = load_module(mod_file)
self.assertEqual(version, 2.5,
source_size, *_) = load_module(mod_file)
self.assertEqual(version, (2, 5),

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the fix!

Comment thread xdis/magics.py
Comment on lines 870 to +871
add_canonic_versions("3.14-dev", "3.14b3")
add_canonic_versions("3.14 3.14.0 3.14.1, 3.14.2 3.14.3 3.14.4", "3.14rc3")
add_canonic_versions("3.14 3.14.0 3.14.1, 3.14.2 3.14.3 3.14.4 3.14.5", "3.14rc3")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the update.

@@ -0,0 +1,414 @@
# (C) Copyright 2026 by Rocky Bernstein

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

You can substitute your name here. You did the work!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh, sure, I wasn't sure about that lol
I don't know if you want me to make another commit on my branch (maybe awkward since this PR is already merged) or...
I have not that much experience working on PRs for projects like this so I'm not entirely sure what the process is usually like

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Oh, sure, I wasn't sure about that lol I don't know if you want me to make another commit on my branch (maybe awkward since this PR is already merged) or...

I'd be grateful if you just add a new PR with the change .

I have not that much experience working on PRs for projects like this so I'm not entirely sure what the process is usually like

I have much experience and am stil not entire sure what the process is usually like. :-) It tends to change from project to project.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, I opened it as PR #187 with a single commit

@rocky

rocky commented May 31, 2026

Copy link
Copy Markdown
Owner

There may be things I've missed. In particular, I haven't really touched any of the formatting code.

Custom formatting can wait.

(In the long run it should be generalized more, but this kind of idea will always be around.)

I'll also keep testing this in the coming weeks to make sure it's fully correct.

Thanks.

Be aware that Python 3.15-dev may change in incompatible ways, and it typically does. I tried running tests on the 3.15-dev, and 3.15a7, and I got failures:

stream_fixture = <_io.StringIO object at 0x7bcba94477f0>

    def test_show_code(stream_fixture):
        dis.show_code(TEST_SOURCE_CODE, file=stream_fixture)
        actual = stream_fixture.getvalue()
>       assert actual == EXPECTED_CODE_INFO + "\n"
E       AssertionError: assert '# Method Nam...\n#    0: a\n' == '# Method Nam...\n#    0: a\n'
E         
E         Skipping 273 identical leading characters in diff, use -v to show
E             0: 10
E         + #    1: None
E           # Names:
E           #    0: a

pytest/test_std.py:138: AssertionError
======================================================================================== short test summary info =========================================================================================
FAILED pytest/test_stack_effect.py::test_stack_effect_vs_dis - AssertionError: 10 (END_SEND)  not okay; effect -2 vs -1
FAILED pytest/test_std.py::test_bytecode_info - AssertionError: assert '# Method Nam...s:\n#    0: a' == '# Method Nam...s:\n#    0: a'
FAILED pytest/test_std.py::test_code_info - AssertionError: assert '# Method Nam...s:\n#    0: a' == '# Method Nam...s:\n#    0: a'
FAILED pytest/test_std.py::test_show_code - AssertionError: assert '# Method Nam...\n#    0: a\n' == '# Method Nam...\n#    0: a\n'
================================================================================ 4 failed, 28 passed, 4 skipped in 0.15s =================================================================================

The other thing to note is that we support disassembling from an old Python to a current one! And this is done in branches python-3.6-to-3.10 for example. But I've usually been handling the merges from master into these branches.

@rocky rocky merged commit a79d186 into rocky:master Jun 2, 2026
12 checks passed
@Quinntyx

Quinntyx commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

I'm aware that Python 3.15-dev may change in incompatible ways, but I do believe that now that the beta is released it shouldn't happen? At least, the bytecode is supposed to be frozen now.

@rocky

rocky commented Jun 2, 2026

Copy link
Copy Markdown
Owner

I'm aware that Python 3.15-dev may change in incompatible ways, but I do believe that now that the beta is released it shouldn't happen? At least, the bytecode is supposed to be frozen now.

Weirder things have happened in Python bytecode.

@Quinntyx

Quinntyx commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Well, I guess I just hope that it doesn't change and if it does I'll have to fix it then

@rocky

rocky commented Jun 3, 2026

Copy link
Copy Markdown
Owner

@Quinntyx A couple of things I noticed after merge.

The main thing is that new code should not use any Python 3.15isms just yet; it needs to work on Python 3.11. This may change depending on how different 3.15 is from 3.14.

In particular, instead of list[type] and dict[type] use List[type] and Dict[type].

@Quinntyx

Quinntyx commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

I'm relatively sure that I haven't used any of the Python 3.15isms as I was testing my branch of xdis using Python 3.14 to run (decompiling 3.15-0b1 bytecode).

However, I wasn't aware that list[type] was not available in some older Python versions, thanks for pointing it out! I'll probably also test decompiling 3.15 bytecode from 3.11 and maybe some earlier versions to make sure that I didn't accidentally use any newer features that break compatibility with old versions.

@Quinntyx

Quinntyx commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

After some research, I see that list[type] and dict[type] etc were added in Python 3.9, and we are targeting 3.11, so they should be available, no? In my testing, my branch of xdis seems to run just fine in a 3.11 environment. Is there something I'm missing?

@rocky

rocky commented Jun 4, 2026

Copy link
Copy Markdown
Owner

After some research, I see that list[type] and dict[type] etc were added in Python 3.9, and we are targeting 3.11, so they should be available, no? In my testing, my branch of xdis seems to run just fine in a 3.11 environment. Is there something I'm missing?

Ok. My mistake then. Thanks for checking. xdis supports Python going back before 3.9, and probably I got confused in working with one of those other branches.. In the future, then list[type] is okay for the master branch.

@Quinntyx

Quinntyx commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Okay, thanks for the clarification!

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.

2 participants