Self-hosted service
syssec-utd/pylingual avatar
syssec-utd/pylingual

PyLingual: Local Decompilation of CPython 3.6+ Bytecode

Python decompiler for modern Python versions.

1,422 stars191 forksPythonGPL-3.0

At a glance

What is it?
PyLingual converts `.pyc` bytecode into Python source for supported CPython releases. It is intended for reverse engineering and research, but its local code may produce less accurate control flow than the hosted service.
Who is it for?
PyLingual suits reverse engineers, security researchers, and Python tooling developers who need a local decompiler for CPython bytecode. It is a poor choice for PyPy bytecode, exact source recovery, or users requiring the hosted service's reported control-flow accuracy.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

PyLingual's CPython Bytecode Problem

PyLingual addresses a narrow reverse-engineering task: converting compiled Python bytecode back into readable source. Its input is `.pyc` content, which does not preserve the original formatting, comments, variable names, or complete source layout. The output should therefore be treated as reconstructed source rather than the file that originally produced the bytecode.

The project targets all released CPython versions since Python 3.6, with `pyproject.toml` describing support for versions 3.6 through 3.15. That makes it relevant to engineers inspecting packaged Python programs, recovering source from build artifacts, studying generated code, and producing datasets for decompiler research. The README also identifies correctness verification and model training data generation as project needs.

This is not a general executable packer, binary decompiler, or universal Python reverse-engineering suite. Its declared scope is CPython bytecode. The repository does not document PyPy bytecode support, native extension modules, or reconstruction of comments and original formatting. Those absences matter because a `.py` file cannot always be recovered exactly from `.pyc` data.

PyLingual can run through `https://pylingual.io` or locally from source. The distinction matters because the README warns that the repository is optimized for readability and extension, while control-flow accuracy may initially regress compared with the hosted web service. Local adoption therefore involves both installation cost and a possible output-quality gap.

PyLingual's Bytecode-to-Source Pipeline

The CLI presents PyLingual as an end-to-end pipeline from Python bytecode to source code. It accepts one or more files through the `FILES` argument and can automatically detect the Python version associated with each `.pyc`. If automatic detection is unsuitable, the `--version VERSION` option supplies the target version explicitly. Results can be written to an output directory with `-o` or `--out-dir`.

The architecture uses translation models rather than presenting PyLingual as a conventional rule-only decompiler. Its dependencies include `torch`, `transformers`, `tokenizers`, `huggingface-hub`, `datasets`, and `seqeval`, while `xdis>=6.1.4` handles bytecode-related processing. The README also refers to a segmentation model and translations back into source code. A model configuration file can be supplied through `-c` or `--config-file`.

Bytecode control flow must first be divided into segments before the model can translate them. By default, segmentation follows the segmentation model. Passing `--trust-lnotab` instead makes the CLI use `lnotab` for segmentation. The `-k` or `--top-k` option limits how many additional segmentations may be considered, exposing a direct trade-off between search scope and command configuration.

Target-version compilation is part of the verification process. For Python 3.8 through 3.15, PyLingual uses managed installations from `uv`; Python 3.6 and 3.7 require `pyenv`, or `pyenv-win` on Windows. This dependency shows that decompilation correctness is not treated solely as text generation. PyLingual can compile code in the target version to support correctness checks and training-set production, although the README does not describe the precise comparison or acceptance criteria.

Installing PyLingual with uv

Local installation starts with `uv`, which the README requires for project dependencies and managed Python versions. The project itself declares Python 3.12 or newer and exposes a `pylingual` command through its `pyproject.toml`. Clone the repository and install the local project as a `uv` tool with these documented commands:

sh
git clone https://github.com/syssec-utd/pylingual
uv tool install ./pylingual

The first command downloads the repository, while the second installs PyLingual and places its CLI where `uv` manages tool commands. The repository also contains `uv.lock`, so the source tree includes dependency locking information. The documented installation method does not provide a separate download package or installation for the web service.

There is a version inconsistency worth checking during setup. The release list names `v0.0.1`, while `pyproject.toml` currently declares `version = "0.1.0"`. Neither the README nor the repository files explain that difference. A user installing the current `main` branch should record the checked-out commit and inspect `pylingual --version` before comparing it with a published release.

For Python 3.6 and 3.7 targets, installation alone is insufficient. The README says those versions use `pyenv` or `pyenv-win`, whereas current versions from 3.8 through 3.15 use `uv` managed installations. The CLI's `--init-pyenv` option installs pyenv before decompilation, but the README does not describe automatic cleanup or conflict handling for an existing pyenv configuration.

Running the First PyLingual Decompilation

The optional Redis cache is the only complete input-file invocation shown in the README. It is useful when translating multiple files or repeating similar work, but Redis is not required for basic use. With Docker and Docker Compose installed, start the bundled service from the cloned repository:

sh
cd pylingual
docker compose up -d redis

The Compose file maps Redis only to `127.0.0.1:6379` on the host. Once that service is available, the documented invocation is:

sh
pylingual --redis-host 127.0.0.1 example.pyc

Without a Redis host, caching remains disabled. The environment variable `PYLINGUAL_REDIS_HOST` can configure the same setting, while `PYLINGUAL_REDIS_PORT` changes the port from its default of `6379`. Command-line settings override environment variables. `--redis-port` is the corresponding command-line option.

Redis stores translation-model predictions keyed by Python version and normalized bytecode. Repeated statements can skip translation-model inference after a cache hit, although segmentation and model loading are not cached. This can matter when a reverse engineer processes several `.pyc` files from the same target version.

Cache correctness requires care. Keys do not include the translation model revision. The README therefore advises using a separate Redis instance when switching models so predictions from one model are not reused by another. The bundled `redis:latest` image also uses a floating tag, while PyLingual itself does not cache segmentation. These details make Redis an optimization with operational constraints, not simply a background service.

PyLingual Version, Output, and Cache Boundaries

Several boundaries can produce disappointing results if they are overlooked. First, the project is a decompiler for CPython bytecode, not a source debugger. Although Python bytecode and generated source can help analysts understand behavior, they do not by themselves restore comments, formatting, or every original identifier choice. The README does not claim byte-for-byte source recovery, so exact reconstruction should not be the adoption criterion.

Second, the local and hosted paths are not described as identical in output quality. The repository explicitly warns of control-flow accuracy regression relative to the web service because the codebase prioritizes readability and extension. This is the clearest documented reason to use the service rather than the local package when accurate branch reconstruction is the main requirement. The repository does not quantify that difference.

Third, automatic Python-version detection can be bypassed with `--version VERSION`. The repository does not explain how ambiguous headers are handled or what diagnostic output appears when detection fails. `-q` or `--quiet` suppresses console output, which may reduce useful evidence during troubleshooting.

Redis can also hide a version mismatch if operators switch models without separating cache instances. Because the translation model revision is absent from cache keys, predictions made by different model revisions may be reused under the same Python-version and bytecode identity. This is a design constraint explicitly documented by the project, not an inferred deployment issue. Users working with changing model configurations should avoid shared cache history or clear it through their own operational process.

PyLingual Maintenance and GPL-3.0 Costs

The repository is not archived, and its last push was on 2026-10-15. Those facts indicate recent development activity, but they do not establish API stability for adopters. The recent release list contains `v0.0.1`, dated 2026-09-21, while `pyproject.toml` reports `0.1.0`. With only one recent release listed, upgrade planning should account for possible differences between a release and the default branch.

PyLingual is licensed under `GPL-3.0-only` in `pyproject.toml`. Developers distributing a modified or combined work should review how GPL-3.0 obligations apply to their specific use. This is not legal advice. The practical point is narrower: the local tool is copyleft licensed, and source availability obligations can matter more for organizations embedding or redistributing it than for isolated command-line use.

The dependency set is substantial. `torch` and `transformers` imply a machine-learning runtime, while `datasets`, `tensorboardx`, `evaluate`, visualization libraries, and bytecode tooling broaden the installation surface. Redis is optional but adds container and cache-coordination work. Older target versions add `pyenv` or `pyenv-win` handling. These are concrete maintenance costs: hardware and storage requirements for model dependencies, attention to dependency upgrades, and separate cache management when model revisions change.

PyLingual Compared with xdis and the Hosted Service

The closest alternative visible in the project files is `xdis`, which PyLingual declares as a dependency at `xdis>=6.1.4`. Their approaches differ. PyLingual adds segmentation, learned translation, correctness-oriented target-version compilation, and source reconstruction. Selecting PyLingual means accepting that model-driven pipeline rather than working directly with bytecode-inspection primitives.

The other real alternative is `https://pylingual.io`. PyLingual offers the same named project as a hosted web service and as local source. The repository warns that the local codebase may have control-flow accuracy regression compared with the web service. Users who prioritize documented service output quality may prefer the hosted route, while users who need local execution, direct source access, or control over model configuration may prefer the package.

Neither alternative changes PyLingual's CPython scope. The README does not establish an external decompiler comparison, quantify accuracy, or describe a case where another product handles obfuscation more effectively. The defensible choice is therefore based on deployment and workflow: use the hosted service when its reported control-flow advantage dominates, use `xdis` directly when low-level bytecode inspection is the real task, and use local PyLingual when its translation pipeline and extension-oriented codebase are needed.

Editorial conclusion

PyLingual suits reverse engineers, security researchers, and Python tooling developers who need a local decompiler for CPython bytecode. It is a poor choice for PyPy bytecode, exact source recovery, or users requiring the hosted service's reported control-flow accuracy. Verify supported `.pyc` format and target-version handling first, then inspect output before relying on it in another tool.

Frequently asked questions

Which Python versions can PyLingual decompile?

PyLingual supports all released CPython versions since Python 3.6, and `pyproject.toml` describes a 3.6 to 3.15 range. Current targets from 3.8 through 3.15 use `uv` managed installations, while 3.6 and 3.7 use `pyenv` or `pyenv-win`.

Does local PyLingual produce exactly the same output as the web service?

The README warns that the local codebase may have control-flow accuracy regression compared with the version hosted at `pylingual.io`. It does not quantify the difference.

Is Redis required to use PyLingual?

No. Redis caching remains disabled unless a host is configured. The optional cache can reuse translation-model predictions across files and runs, but it does not cache segmentation or model loading.

Official sources

  1. License: GPL-3.0
  2. Project website
  3. README
  4. Releases
  5. syssec-utd/pylingual on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/syssec-utd-pylingual.svg)](https://hysenlabs.com/projects/syssec-utd-pylingual)

Was this useful?