{"id":13570142,"url":"https://github.com/rocky/x-python","last_synced_at":"2026-01-14T08:26:39.293Z","repository":{"id":39887003,"uuid":"259635277","full_name":"rocky/x-python","owner":"rocky","description":"A Python implementation of the C Python Interpreter","archived":false,"fork":true,"pushed_at":"2025-11-23T12:35:03.000Z","size":1878,"stargazers_count":96,"open_issues_count":1,"forks_count":16,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-12-01T00:13:22.570Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"nedbat/byterun","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rocky.png","metadata":{"files":{"readme":"README.rst","changelog":"ChangeLog","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null},"funding":{"github":["rocky"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2020-04-28T12:48:58.000Z","updated_at":"2025-11-22T16:40:12.000Z","dependencies_parsed_at":"2023-02-14T04:30:24.448Z","dependency_job_id":null,"html_url":"https://github.com/rocky/x-python","commit_stats":null,"previous_names":["rocky/xpython"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/rocky/x-python","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky%2Fx-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky%2Fx-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky%2Fx-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky%2Fx-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rocky","download_url":"https://codeload.github.com/rocky/x-python/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rocky%2Fx-python/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28413962,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T08:16:59.381Z","status":"ssl_error","status_checked_at":"2026-01-14T08:13:45.490Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T14:00:48.728Z","updated_at":"2026-01-14T08:26:39.283Z","avatar_url":"https://github.com/rocky.png","language":"Python","readme":"|TravisCI| |CircleCI| |Pypi Installs| |Latest Version| |Supported Python Versions|\n\nx-python\n--------\n\nThis is a CPython bytecode interpreter written Python.\n\nYou can use this to:\n\n* Learn about how the internals of CPython works since this models that\n* Experiment with additional opcodes, or ways to change the run-time environment\n* Use as a sandboxed environment for trying pieces of execution\n* Have one Python program that runs multiple versions of Python bytecode.\n* Use in a dynamic fuzzer or in coholic execution for analysis\n\nThe ability to run simple Python bytecode as far back as 2.4 from\nPython 3.10 or Python 3.10 from a Python 2.7 interpreter (assuming no\nfancy async features of newer runtimes) I find pretty neat.\n\nAlso, idea of the sandboxed environment in a debugger I find\ninteresting. (Note: currently environments are not sandboxed that\nwell, but I am working towards that, and it is a bit challenging.)\n\nSince there is a separate execution, and traceback stack,\ninside a debugger you can try things out in the middle of a debug\nsession without effecting the real execution. On the other hand if a\nsequence of executions works out, it is possible to copy this (under\ncertain circumstances) back into CPython's execution stack.\n\nI have hooked in `trepan3k \u003chttps://pypi.python.org/pypi/trepan3k\u003e`_\ninto this interpreter so you have a pdb/gdb like debugger also with\nthe ability to step bytecode instructions.\n\nTo experiment with faster ways to support trace callbacks such as\nthose used in a debugger, added is an instruction to\nsupport fast breakpoints and breakpointing on a particular instruction\nthat doesn't happen to be on a line boundary. I believe this could\ncould and should be ported back to CPython and there would be benefit.\n(Python 3.8 supports the ability to save additional frame information which\nis where the original opcode is stored. It just needs the `BRKPT` opcode)\n\nAlthough this is far in the future, suppose you to add a race\ndetector? It might be easier to prototype it in Python here. (This\nassumes the interpreter supports threading well, I suspect it doesn't)\n\nAnother unexplored avenue implied above is mixing interpretation and\ndirect CPython execution. In fact, there are bugs so this happens\nright now, but it will be turned into a feature. Some functions or\nclasses you may want to not run under a slow interpreter while others\nyou do want to run under the interpreter.\n\n\nInstallation\n------------\n\n*For recent Python releases (Python 3.11+)*, you can install from PyPI using the name ``x-python``::\n\n    pip install x-python\n\n*For Python releases before 3.11*, do not install using PyPI, but instead install using a file in the [GitHub Releases section](https://github.com/rocky/python-x-python/releases). Older Python used to use `easy_install \u003chttps://python101.pythonlibrary.org/chapter29_pip.html#using-easy-install\u003e`_. But this is no longer supported in PyPi or newer Python versions. And vice versa, *poetry* nor *pip*, (the newer ways) are not supported on older Pythons.\n\nIf the Python version you are running x-python is between Python 2.4 through 2.7, use a tarball called x-python_24-*x.y.z*.tar.gz.\n\nIf the Python version you are running x-python is between Python 3.0 through 3.2, use a tarball called x-python_30-*x.y.z*.tar.gz.\n\nIf the Python version you are running x-python is between Python 3.3 through 3.5, use a tarball called x-python_33-*x.y.z*.tar.gz.\n\nIf the Python version you are running x-python is between Python 3.6 through 3.11, use a tarball called x-python_36-*x.y.z*.tar.gz.\n\nIf the Python version you are running x-python is 3.11 or later, use a called x-python-*x.y.z*.tar.gz.\n\nYou can also try eggs or wheels that have the same version designation, e.g., x-python-*x.y.z*-py39-none-any.whl for a Python 3.9 installation. *However, note that *the version without the designation means Python 3.11 or greater*.\n\nYou can also try eggs or wheels that have the same version designation, e.g., x-python-*x.y.z*-py39-none-any.whl for a Python 3.9 installation. *However, note that *the version without the designation means Python 3.11 or greater*.\n\nSimilarly, a tarball with without an underscore  e.g., x-python-*x.y.z* works only from Python 3.11 or greater.\n\nRationale for using Git Branches\n++++++++++++++++++++++++++++++++\n\nIt is currently impossible (if not impractical) to have one Python source code of this complexity and with this many features that can run both Python 2.7 and Python 3.13+. The languages have drifted so much, and Packing is vastly different. In fact, the packaging practice for Python 3.11+ is incompatible with Python 2.7 (and before back to Python 2.4), which favored \"easy_install\".\n\n\nInstallation from source text\n++++++++++++++++++++++++++++++\n\nTo install from source code, make sure you have the right Git\nbranch. See the Requirements section for the Git branch names.\n\nAfter setting the right branch::\n\n   $ pip install -e .  # or pip install -e .[dev] to include testing package\n\nA GNU makefile is also provided so ``make install`` (possibly as root or sudo) will do the steps above.\n\n\nExamples:\n+++++++++\n\nWhat to know instructions get run when you write some simple code?\nTry this:\n\n::\n\n   $ xpython -vc \"x, y = 2, 3; x **= y\"\n   INFO:xpython.vm:L. 1   @  0: LOAD_CONST (2, 3)\n   INFO:xpython.vm:       @  2: UNPACK_SEQUENCE 2\n   INFO:xpython.vm:       @  4: STORE_NAME (2) x\n   INFO:xpython.vm:       @  6: STORE_NAME (3) y\n   INFO:xpython.vm:L. 1   @  8: LOAD_NAME x\n   INFO:xpython.vm:       @ 10: LOAD_NAME y\n   INFO:xpython.vm:       @ 12: INPLACE_POWER (2, 3)\n   INFO:xpython.vm:       @ 14: STORE_NAME (8) x\n   INFO:xpython.vm:       @ 16: LOAD_CONST None\n   INFO:xpython.vm:       @ 18: RETURN_VALUE (None)\n\nOption ``-c`` is the same as Python's flag (program passed in as string)\nand ``-v`` is also analogous Python's flag. Here, it shows the bytecode\ninstructions run.\n\nNote that the disassembly above in the dynamic trace above gives a\nlittle more than what you'd see from a static disassembler from\nPython's ``dis`` module. In particular, the ``STORE_NAME``\ninstructions show the *value* that is store, e.g. \"2\" at instruction\noffset 4 into name ``x``. Similarly ``INPLACE_POWER`` shows the operands, 2 and 3, which is how the value\n8 is derived as the operand of the next instruction, ``STORE_NAME``.\n\nWant more like the execution stack stack and block stack in addition? Add another `v`:\n\n::\n\n   $ xpython -vvc \"x, y = 2, 3; x **= y\"\n\n   DEBUG:xpython.vm:make_frame: code=\u003ccode object \u003cmodule\u003e at 0x7f8018507db0, file \"\u003cstring x, y = 2, 3; x **= y\u003e\", line 1\u003e, callargs={}, f_globals=(\u003cclass 'dict'\u003e, 140188140947488), f_locals=(\u003cclass 'NoneType'\u003e, 93856967704000)\n   DEBUG:xpython.vm:\u003cFrame at 0x7f80184c1e50: '\u003cstring x, y = 2, 3; x **= y\u003e':1 @-1\u003e\n   DEBUG:xpython.vm:  frame.stack: []\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:L. 1   @  0: LOAD_CONST (2, 3) \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n   DEBUG:xpython.vm:  frame.stack: [(2, 3)]\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:       @  2: UNPACK_SEQUENCE 2 \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n   DEBUG:xpython.vm:  frame.stack: [3, 2]\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:       @  4: STORE_NAME (2) x \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n   DEBUG:xpython.vm:  frame.stack: [3]\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:       @  6: STORE_NAME (3) y \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n   DEBUG:xpython.vm:  frame.stack: []\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:L. 1   @  8: LOAD_NAME x \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n   DEBUG:xpython.vm:  frame.stack: [2]\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:       @ 10: LOAD_NAME y \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n   DEBUG:xpython.vm:  frame.stack: [2, 3]\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:       @ 12: INPLACE_POWER (2, 3)  \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n   DEBUG:xpython.vm:  frame.stack: [8]\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:       @ 14: STORE_NAME (8) x \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n   DEBUG:xpython.vm:  frame.stack: []\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:       @ 16: LOAD_CONST None \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n   DEBUG:xpython.vm:  frame.stack: [None]\n   DEBUG:xpython.vm:  blocks     : []\n   INFO:xpython.vm:       @ 18: RETURN_VALUE (None)  \u003cmodule\u003e in \u003cstring x, y = 2, 3; x **= y\u003e:1\n\n\nWant to see this colorized in a terminal? Use this via ``trepan-xpy -x``:\n|trepan-xpy-example|\n\nSuppose you have Python 2.4 bytecode (or some other bytecode) for\nthis, but you are running Python 3.7?\n\n::\n\n   $ xpython -v test/examples/assign-2.4.pyc\n   INFO:xpython.vm:L. 1   @  0: LOAD_CONST (2, 3)\n   INFO:xpython.vm:       @  3: UNPACK_SEQUENCE 2\n   INFO:xpython.vm:       @  6: STORE_NAME (2) x\n   INFO:xpython.vm:       @  9: STORE_NAME (3) y\n   INFO:xpython.vm:L. 2   @ 12: LOAD_NAME x\n   INFO:xpython.vm:       @ 15: LOAD_NAME y\n   INFO:xpython.vm:       @ 18: INPLACE_POWER (2, 3)\n   INFO:xpython.vm:       @ 19: STORE_NAME (8) x\n   INFO:xpython.vm:       @ 22: LOAD_CONST None\n   INFO:xpython.vm:       @ 25: RETURN_VALUE (None)\n\nNot much has changed here, other then the fact that that in after 3.6 instructions are two bytes instead of 1- or 3-byte instructions.\n\nThe above examples show straight-line code, so you see all of the instructions. But don't confuse this with a disassembler like ``pydisasm`` from ``xdis``.\nThe below example, with conditional branching example makes this more clear:\n::\n\n    $ xpython -vc \"x = 6 if __name__ != '__main__' else 10\"\n    INFO:xpython.vm:L. 1   @  0: LOAD_NAME __name__\n    INFO:xpython.vm:       @  2: LOAD_CONST __main__\n    INFO:xpython.vm:       @  4: COMPARE_OP ('__main__', '__main__') !=\n    INFO:xpython.vm:       @  6: POP_JUMP_IF_FALSE 12\n                                                   ^^ Note jump below\n    INFO:xpython.vm:       @ 12: LOAD_CONST 10\n    INFO:xpython.vm:       @ 14: STORE_NAME (10) x\n    INFO:xpython.vm:       @ 16: LOAD_CONST None\n    INFO:xpython.vm:       @ 18: RETURN_VALUE (None)\n\nWant even more status and control? See `trepan-xpy \u003chttps://github.com/rocky/trepan-xpy\u003e`_.\n\nStatus:\n+++++++\n\nCurrently bytecode from Python versions 3.10 to 3.2, and 2.7 to 2.4 are\nsupported. The most recent versions of Python don't have all opcodes\nimplemented. This is only one of many interests I have, so support may\nbe shoddy. I use funding to help me direct where my attention goes in\nfixing problems, which are vast in this project.\n\n\n*Byterun*, from which this was based on, is awesome. But it cheats in\nsubtle ways.\n\nWant to write a very small interpreter using CPython?\n\n::\n\n   # get code somehow\n   exec(code)\n\nThis cheats in kind of a gross way, but this the kind of cheating goes\non in *Byterun* in a more subtle way. As in the example above which\nrelies on built-in function ``exec`` to do all of the work, *Byterun*\nrelies on various similar sorts of built-in functions to support\nopcode interpretation. In fact, if the code you were *interpreting*\nwas the above, *Byterun* would use its built-in function for running\ncode inside the `exec` function call, so all of the bytecode that gets\nrun inside code inside *code* would not seen for interpretation.\n\nAlso, built-in functions like `exec`, and other built-in modules have\nan effect in the interpreter namespace.  So the two namespaces then\nget intermingled.\n\nOne example of this that has been noted is for ``import``. See\nhttps://github.com/nedbat/byterun/issues/26.  But there are others\ncases as well.  While we haven't addressed the ``import`` issue\nmentioned in issue 26, we have addressed similar kinds of issues like\nthis.\n\nSome built-in functions and the ``inspect`` module require built-in\ntypes like cell, traceback, or frame objects, and they can't use the\ncorresponding interpreter classes. Here is an example of this in\n*Byterun*: class ``__init__`` functions don't get traced into, because\nthe built-in function ``__build_class__`` is relied on. And\n``__build_class__`` needs a native function, not an\ninterpreter-traceable function. See\nhttps://github.com/nedbat/byterun/pull/20.\n\nAlso *Byterun* is loose in accepting bytecode opcodes that is invalid\nfor particular Python but may be valid for another. I suppose this is\nokay since you don't expect invalid opcodes appearing in valid\nbytecode. It can however accidentally or erroneously appear code that\nhas been obtained via some sort of extraction process, when the\nextraction process isn't accurate.\n\nIn contrast to *Byterun*, *x-python* is more stringent what opcodes it\naccepts.\n\nByterun needs the kind of overhaul we have here to be able to scale to\nsupport bytecode for more Pythons, and to be able to run bytecode\nacross different versions of Python. Specifically, you can't rely on\nPython's `dis \u003chttps://docs.python.org/3/library/dis.html\u003e`_ module if\nyou expect to run a bytecode other than the bytecode that the\ninterpreter is running, or run newer \"wordcode\" bytecode on a\n\"byte\"-oriented bytecode, or vice versa.\n\nIn contrast, *x-python* there is a clear distinction between the\nversion being interpreted and the version of Python that is\nrunning. There is tighter control of opcodes and an opcode's\nimplementation is kept for each Python version. So we'll warn early\nwhen something is invalid. You can run bytecode back to Python 2.4\nusing Python 3.10 (largely), which is amazing given that 3.10's native\nbyte code is 2 bytes per instruction while 2.4's is 1 or 3 bytes per\ninstruction.\n\nThe \"largely\" part is, as mentioned above, because the interpreter has\nalways made use of Python builtins and libraries, and for the most\npart these haven't changed very much. Often, since many of the\nunderlying builtins are the same, the interpreter can (and does) make\nuse interpreter internals. For example, built-in functions like\n``range()`` are supported this way.\n\nSo interpreting bytecode from a newer Python release than the release\nthe Python interpreter is using, is often doable too. Even though\nPython 2.7 doesn't support keyword-only arguments or format strings,\nit can still interpret bytecode created from using these constructs.\n\nThat's possible here because these specific features are more\nsyntactic sugar rather than extensions to the runtime. For example,\nformat strings basically map down to using the ``format()`` function\nwhich is available on 2.7.\n\nBut new features like asynchronous I/O and concurrency primitives are not\nin the older versions. So those need to be simulated, and that too is a\npossibility if there is interest or support.\n\nYou can run many of the tests that Python uses to test itself, and I\ndo! And most of those work. Right now this program works best on Python up to\n3.4 when life in Python was much simpler. It runs over 300 in Python's\ntest suite for itself without problems. For Python 3.6 the number\ndrops down to about 237; Python 3.9 is worse still.\n\n\nHistory\n+++++++\n\nThis is a fork of *Byterun.* which is a pure-Python implementation of\na Python bytecode execution virtual machine.  Ned Batchelder started\nit (based on work from Paul Swartz) to get a better understanding of\nbytecodes so he could fix branch coverage bugs in coverage.py.\n\n.. |CircleCI| image:: https://circleci.com/gh/rocky/x-python.svg?style=svg\n    :target: https://circleci.com/gh/rocky/x-python\n.. |TravisCI| image:: https://travis-ci.org/rocky/x-python.svg?branch=master\n\t\t :target: https://travis-ci.org/rocky/x-python\n\n.. |trepan-xpy-example| image:: https://github.com/rocky/x-python/blob/master/screenshots/trepan-xpy-assign.gif\n.. |Latest Version| image:: https://badge.fury.io/py/x-python.svg\n\t\t :target: https://badge.fury.io/py/x-python\n.. |PyPI Installs| image:: https://pepy.tech/badge/x-python/month\n.. |Supported Python Versions| image:: https://img.shields.io/pypi/pyversions/x-python.svg\n","funding_links":["https://github.com/sponsors/rocky"],"categories":["Bytecode"],"sub_categories":["Manual analysis"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frocky%2Fx-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frocky%2Fx-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frocky%2Fx-python/lists"}