# decompyle3: a Python 3.7 and 3.8 decompiler carrying its predecessor's unsolved problems

> decompyle3 is a GPL-3.0 Python decompiler for 3.7 and 3.8 bytecode, forked from uncompyle6 so the grammar could be refactored and the control flow detection rewritten. The judgement is that the author has diagnosed the real problem correctly, and that the rewrite of that problem is happening in a separate non-public project.

**rocky/python-decompile3** — Python decompiler for 3.7-3.8 Stripped down from uncompyle6 so we can refactor and start to fix up some long-standing problems

- Repository: https://github.com/rocky/python-decompile3
- Stars: 1,280 · Forks: 165
- Language: Python
- License: GPL-3.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/rocky-python-decompile3

## The fork exists because control flow detection was never solved

decompyle3 is a native Python cross-version decompiler and fragment decompiler, and it is a reworking of uncompyle6. The package name on PyPI is `decompyle3`.

The README explains the fork's reason in a way that is more useful than a changelog. Uncompyle6 is described as having a fundamental problem in the way it handles control flow. In the early days of Python, when there was little optimization and code was generated in a very template-oriented way, figuring out control flow structures could be done by simply looking at code patterns.

Then optimisation caught up. More code optimization, specifically around handling jumps, made it harder to support detecting control flow strictly from code patterns. The README notes this was noticed as far back as Python 2.4 in 2004, and that because it is a difficult problem it has not been tackled in a satisfactory way.

The attempts to fix it are described honestly. The initial attempt was to add markers in the instruction stream, initially a `COME_FROM` instruction, and then to use that in pattern detection. Over the years that was extended to `COME_FROM_LOOP` and `COME_FROM_WITH`, with checks at grammar-reduce time to try to make sure jumps match the supposed `COME_FROM` targets. The author's own summary of the outcome is that all of this is complicated, not dependable, has greatly slowed down deparsing, and is not really tenable.

So the fork began rewriting and refactoring the grammar. But the README is direct that even that is not enough, because control flow needs to be addressed by using dominators and reverse-dominators, which the python-control-flow project can provide. That work is described as something the author is finally slowly doing in yet another non-public project, with the caveat that it is a lot of work, that funding is appreciated but is not commensurate with the effort, and that the author currently has a full-time job. So it may take time before it is available publicly, if at all.

That sentence is the single most important line in the README for an adopter, and it sits above the installation instructions.

## Installing and running it takes one command

The installation is standard Python packaging.

```shell
pip install decompyle3
```

From source the project uses setup.py, so it follows the standard routine, either `pip install -e .` to set up to run from the source tree, or `python setup.py install` which may need sudo. A GNU Makefile is also provided, so `make install`, possibly as root or sudo, will do the steps above.

Running it is one command with a glob:

```shell
$ decompyle3 *compiled-python-file-pyc-or-pyo*
```

And `decompyle3 -h` gives the usage help. The `pyproject.toml` registers three console scripts: `decompyle3` for the main tool, `decompyle3-code` for the code-type decompiler, and `decompyle3-tokenize` for the tokenize variant.

The dependencies are three: `click` for the command line, `spark-parser >= 1.8.9, < 1.9.2` for the parsing grammar, and `xdis > 6.2` for the bytecode handling. The `xdis` dependency is the significant one, because it is the library that knows how to read the bytecode of many Python versions, and that is what makes the cross-version claim possible.

The package requires Python 3.7 or newer to run, and the classifiers list 3.7 through 3.13 plus PyPy. The code itself can be run on Python 3.7 or 3.8, and the bytecode files it can read have been tested on Python bytecodes from versions 3.7 and 3.8. Those are two different ranges and the distinction matters: the tool runs on a modern interpreter but only reads old bytecode.

The `decompyle3` package directory sits at the root alongside `admin-tools/`, `appveyor/`, `bin/`, `compile_tests`, `pytest/`, `test/` and `test-resources`. There is a `HOW-TO-REPORT-A-BUG.md` file and a `NEWS.md` changelog.

## Verification options that go beyond a syntax check

The README describes several ways to check whether the decompiled output is right, and this is where the project's testing approach shows.

The basic one is the `--syntax-verify` option, which checks Python syntax of the decompiled result. The documentation adds a caveat that matters in practice: since Python syntax changes, you should use this option if the bytecode is the right bytecode for the Python interpreter that will be checking the syntax. A 3.7 bytecode decompiled by a 3.11 interpreter can fail a syntax check for reasons that have nothing to do with the decompilation.

The second option is cross-comparison with another Python decompiler such as unpyc37. Because the two work differently, bugs in one are often not in the other and vice versa, so disagreement between them is a signal.

The third is the most interesting, and the README describes it as an interesting class of programs that is readily available to give stronger verification: those programs that, when run, test themselves. The test suite includes these.

The fourth is running the tests against the standard test suite that Python uses to check itself. The repository has code in `test/stdlib` to facilitate this kind of checking. The author's own summary of that opportunity is that at any given time there are dozens of known problems that are pretty well isolated and could be solved if somebody put in the time, with the observation that there aren't that many people who have been working on bug fixing.

That framing is worth holding on to. The tool's accuracy is bounded less by the algorithm than by how much of CPython's own test suite has been run against it, and that is a volunteer-time problem rather than a design problem.

The Makefile encodes the test matrix by Python version. `check-3.7` and `check-3.8` run the tests with `PYTHONLIB_OPTS="--run --verify-run"` against the `test` directory, while `check-3.9` through `check-3.13` run `check-bytecode-3.7`, so a newer interpreter is used to check the 3.7 bytecode. There is a separate `pytest` target that runs `make -C pytest check`, a `check-short` target for the quick tests, and a `type-check` target running mypy over the `decompyle3` package. The `test` and `pytest` directories are separate, and there is a `compile_tests` directory for the compile-time test material.

## The documented restrictions are extensive and honest

The Known Bugs and Restrictions section is the most valuable part of the README for anyone deciding whether to use the tool, and it is worth reading in full rather than skimming.

On version support: only released versions are supported, not candidate versions, with the note that the magic of a released version is usually the same as the last candidate version prior to release.

On obfuscation: the tool does not handle PJOrion or otherwise obfuscated code. The advice is to try the PJOrion Deobfuscator to unscramble the bytecode to get valid bytecode before trying this tool, and that pydecipher might help.

On bundled executables: the program cannot decompile Microsoft Windows EXE files created by Py2EXE, although the author says it can probably decompile the code after the bytecode is properly extracted, and that pydeinstaller may help with unpacking Pyinstaller bundlers.

On performance and scope: handling pathologically long lists of expressions or statements is slow, and Cython and MicroPython are not handled because they do not use bytecode.

And then the general statement: there are numerous bugs in decompilation, and that is true for every other CPython decompiler the author has encountered, even the ones that claimed to be perfect on some particular version like 2.4.

The forward-looking part is the pessimistic one. The author suspects that as Python progresses, decompilation gets harder because the compilation is more sophisticated and the language itself is more sophisticated, and that there will be fewer ad-hoc attempts like unpyc37 simply because it is harder to do. He says the good news is that he thinks he understands what is needed to address the problems in a more dependable way, but that until the project is better funded he does not intend to make any serious effort to support Python versions 3.8 or 3.9, including bugs that might come in, though he imagines at some point he may be interested in it.

That sentence is the practical constraint. Despite the classifiers listing 3.9 through 3.13 and PyPy, the supported bytecode range is 3.7 and 3.8, and the direction of travel is away from newer versions rather than towards them.

## Reporting bugs has a price, and the project says so

The README's section on bug reports is unusually candid about how a project of this kind is actually sustained.

You may run across a bug and want to report it, and the request is to please do so, with the awareness that it might not get attention for a while. If you sponsor or support the project in some way, the author will prioritise your issues above the queue of other things he might be doing instead. And in rare situations he can do a hand decompilation of bytecode for a fee, which the README says is expensive and usually beyond what most people are willing to spend.

That is a direct description of the economics. The correctness of a decompiler is a function of how much of CPython's behaviour has been verified against it, and the verification work is done by a maintainer whose time is the limiting resource. Sponsorship buys priority on the issue queue. The alternative, which the README also points out, is the standard library test suite as a source of bug reports, and the note that there aren't that many people who have been working on bug fixing.

The author also gave a talk on the project at BlackHat Asia 2024, and the release tags carry conference names, with 3.9.1 tagged at BlackHat Asia 2024 Ides of March and 3.9.3 as BSides NYC 2025. Those tags are a reasonable proxy for how the project's visibility is being built.

For an adopter, the practical reading is that this is a research-grade tool with a single maintainer, and that the reasonable way to use it is as one input among several. Cross-comparing with a second decompiler, as the README recommends, is cheap and catches a useful class of problems.

## Where it sits against the other decompilers

The README has a See Also section that maps the field, and it is more informative than a list of names.

The primary comparison is uncompyle6, the project this one was forked from, and for older bytecode the README directs you there. Uncompyle6 remains the right tool for anything before 3.7.

The second is unpyc37, described as an indirect fork of the uncompyle3 project, and the note about it is technical. The above projects use a different decompiling technique than what is used here. Instructions are walked. Some instructions use the stack to generate strings, while others do not. Because control flow is not dealt with directly, it too suffers the same problems as the various uncompyle and decompyle projects.

That is the sharpest contrast available. Both approaches struggle with the same class of problem, control flow reconstruction, but they get there differently. decompyle3 uses a pattern and marker based approach with grammar rules, which is why the grammar rewrite matters. The unpyc37 line walks the instruction stack instead, which avoids needing the template patterns but inherits the same difficulty.

There is also a mention of a reference to the remaining projects in the family, with the note that the list is incomplete.

The dependency set reflects the same choice. `spark-parser` provides the grammar parsing that the pattern approach needs, and `xdis` handles the cross-version bytecode reading. A tool built on stack walking would need neither in quite the same way, which is a fair structural comparison of the two approaches.

The tooling around the project is conventional and worth a note for contributors. The `pyproject.toml` uses setuptools as the build backend with the version read from `decompyle3.version.__version__`, and the optional `dev` extra adds `pre-commit` and `pytest`. The `requirements.txt` pins `hypothesis==6.24.2` and `pytest>=9.0.3` and includes the project itself as an editable install. There is a `tox.ini`, a `setup.cfg`, a `pre-commit-config.yaml`, a `.isort.cfg`, and a `Makefile` that includes remake-style target comments so that `remake --tasks` lists the available targets.

## Conclusion

decompyle3 fits a reverse engineer or a developer who needs readable source from a Python 3.7 or 3.8 `.pyc` file, and who is prepared to read the output critically rather than trust it. It does not fit a user who needs to decompile 3.9 and later, since the author states plainly that he does not intend to make serious effort to support 3.8 or 3.9, and it does not fit anyone expecting round-trip source that recompiles identically. Verify first that your bytecode is 3.7 or 3.8, since that is the tested range, that the output passes `--syntax-verify` against a matching interpreter, and that you read the Known Bugs section before relying on the result, because the author states there are numerous bugs in decompilation and that this is true of every other CPython decompiler he has encountered.

## FAQ

### How do I install and run decompyle3?

Install it from PyPI with `pip install decompyle3`, or from source with `pip install -e .`. A GNU Makefile is provided, so `make install` does the same steps. Then run `decompyle3` against a compiled file, for example `decompyle3 *compiled-python-file-pyc-or-pyo*`, and use `decompyle3 -h` for usage help.

### Which Python versions can decompyle3 decompile?

Bytecode from Python 3.7 and 3.8, which is the tested range. The tool itself runs on Python 3.7 or 3.8, and the author states that until the project is better funded he does not intend to make serious effort to support versions 3.8 or 3.9, including bugs that might come in. For older bytecode, the documentation points to uncompyle6.

### How do I check whether a decompyle3 result is correct?

Use the `--syntax-verify` option, but only with an interpreter whose version matches the bytecode, since Python syntax changes. You can also cross-compare with another decompiler such as unpyc37, and the test suite includes programs that test themselves, with code in `test/stdlib` to check results against CPython's own standard library tests.

### Can decompyle3 handle obfuscated or packaged Python code?

No. The documentation states that it does not handle PJOrion or otherwise obfuscated code, and suggests unscrambling the bytecode to valid bytecode first with a PJOrion Deobfuscator. It also cannot decompile Windows EXE files created by Py2EXE, though the code can probably be decompiled once the bytecode is properly extracted.

### Why was decompyle3 forked from uncompyle6?

Because uncompyle6 has a fundamental problem handling control flow, which became hard as Python's optimisation around jumps improved. The fork began rewriting the grammar, but the documentation is explicit that this is still not enough, since control flow needs dominators and reverse-dominators from the python-control-flow project, work the author is doing in a separate non-public project.

## Sources

- [Issues](https://github.com/rocky/python-decompile3/issues)
- [License: GPL-3.0](https://github.com/rocky/python-decompile3/blob/master/LICENSE)
- [README](https://github.com/rocky/python-decompile3/blob/master/README.md)
- [Releases](https://github.com/rocky/python-decompile3/releases)
- [rocky/python-decompile3 on GitHub](https://github.com/rocky/python-decompile3)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rocky-python-decompile3
