Java-to-Python source translator with line-level structural correspondence for side-by-side review
Project description
j2py
j2py is a Java-to-Python source translator. It converts Java classes to Python that preserves line-level structural correspondence — same method order, same control flow, camelCase -> snake_case naming — so reviewers can audit output against the original Java side by side. The goal is reviewable equivalence, not a fully idiomatic rewrite. After deterministic conversion a file can be passed to an LLM for an additional conversion attempt.
How it works
Java source
→ parse (tree-sitter-java)
→ analyze (symbols, dependency graph)
→ translate (deterministic rule layer, then optional LLM completion)
→ validate (syntax, lint, types)
→ Python output
The rule layer handles common language constructs deterministically (~70% of typical code). Where rules stop, Claude (optional) fills gaps using disk-cached prompts. Every file gets a confidence score based on rule-layer coverage and structured diagnostics for anything left unhandled.
Status
Alpha. The library is usable for experimentation, fixture-driven development, and batch translation of real Java projects, but construct coverage is still incomplete and output on large enterprise codebases will contain TODOs and review warnings.
Deterministic support today includes:
- tree-sitter Java parsing and symbol extraction
- class, nested class, local/anonymous class helpers, interface, enum, and record skeletons
- interface abstract methods, default methods, and static methods
- fields, constructors, methods, and overloads: chained constructor delegation,
builder-style forwarding merged into default parameters, and type-dispatch overload
groups via a vendored
@overloadedruntime dispatcher (ADR 0009) - common expressions: literals, identifiers, field access, arrays, class literals,
assignments, updates, ternaries, null checks, collection calls, string concat, and
typed
get(...)lowering for lists, maps, and common API receivers - stream pipelines:
map,filter,flatMap,distinct,sorted, collectors such astoList,toSet,joining,groupingBy/mapping,toMap, and block lambdas - control flow:
if/else, enhanced and classicfor,while,do while, safeswitchforms,try/catch/finally,throw,break, andcontinue - configured import emission, naming policy, type maps, exception maps, and comment flags
- dependency-ordered directory translation
- structured diagnostics, confidence scoring, validation, post-LLM structural verification, and optional Anthropic completion
- side-by-side Java/Python review via
j2py compare
Known gaps include:
- overload groups whose erased Python signatures collide (e.g.
intvslong) and other ambiguous overload groups that still fall back to manual-dispatch TODOs - complex enum static initialization beyond translated enum constant class bodies
- annotation semantics beyond syntactic metadata shells
- runtime/framework behavior (dependency injection, persistence mappings, container lifecycle) — j2py translates source structure, not application frameworks
Quick start
Install the alpha from PyPI:
pip install --pre j2py-converter
j2py --help
The PyPI distribution is j2py-converter; the import package and CLI command are
j2py. (The bare j2py name on PyPI is owned by an unrelated project.)
Local development:
uv sync --locked
make check
Translate a file without LLM completion:
uv run j2py translate tests/fixtures/java/HelloWorld.java --no-llm --no-validate --dry-run
Translate a directory in dependency order:
uv run j2py translate path/to/java/root --output translated_py --no-llm
Skip unchanged files on repeated directory runs:
uv run j2py translate path/to/java/root --output translated_py --incremental
Generate review reports:
uv run j2py translate path/to/java/root --output translated_py --dashboard dashboard.html
uv run j2py translate SomeClass.java --report review.html
Watch a source tree and incrementally re-translate changed Java files:
uv run j2py watch path/to/java/root --output translated_py --no-llm
Side-by-side review in VS Code:
uv run j2py compare tests/fixtures/java/HelloWorld.java --no-llm
Print compare paths without opening an editor:
uv run j2py compare tests/fixtures/java/HelloWorld.java --no-open --no-llm
LLM completion (requires ANTHROPIC_API_KEY):
ANTHROPIC_API_KEY=... uv run j2py translate SomeClass.java
Configuration can live in j2py.yaml, j2py.toml, [tool.j2py] in
pyproject.toml, or j2py_config.py. See
docs/configuration.md for the schema.
Quality gates
make check # ruff + mypy strict + pytest (excludes behavior, live_llm)
make test-behavior # Java/Python stdout/stderr/exit-code equivalence (requires JDK)
make test-targets # future xfail roadmap targets (empty while all targets graduated)
make release-check # alpha release gate: release-test + dist-check (3.11+ in CI publish workflow)
Benchmark corpus
Translation quality is measured against a multi-library corpus: pinned checkouts of
Spring Framework, Guava, Apache Commons Lang, Jackson, and Caffeine, plus small curated
construct fixtures under tests/fixtures/corpus/. These libraries are open-source stress
tests for the deterministic rule layer — not product scope or target runtime.
make corpus-list-presets # show all pinned presets
make corpus-clone-all # one-time: clone all checkouts into .corpus/
make corpus-guava-dense-check # Guava collect/base vs baseline
make corpus-commons-lang-dense-check # Commons Lang utilities vs baseline
make corpus-jackson-dense-check # Jackson databind vs baseline
make corpus-caffeine-dense-check # Caffeine cache code vs baseline
make corpus-spring-dense-check # Spring dense preset + construct fixtures
make corpus-hotspots # rank gaps across all committed baselines
Presets and baselines live in scripts/corpus/corpus_presets.py and
tests/fixtures/corpus/. In git worktrees, set J2PY_CORPUS_ROOT to your main checkout
so scripts reuse $J2PY_CORPUS_ROOT/.corpus/. Regenerate a baseline with
make corpus-<name>-update-baseline only after comparison shows no regressions.
See docs/CORPUS_SCOREBOARD.md, docs/TRANSLATION_TARGETS.md, and the full documentation index.
On-demand live LLM evaluation (excluded from make check):
make test-llm-e2e
# or: ANTHROPIC_API_KEY=... uv run pytest -m live_llm tests/llm/test_e2e_llm.py -v -s
Adding translation rules
- Add or update a Java/Python fixture pair under
tests/fixtures/. - Implement the smallest deterministic rule in
j2py/translate/. - Graduate the behavior into normal tests once it passes.
- Run
make checkand relevant corpus checks, such asmake corpus-guava-dense-checkfor generics/collections ormake corpus-spring-dense-checkwhen construct-mix behavior may shift. - Update a corpus baseline only when comparison shows no regressions.
Material translation policy changes should get an ADR under docs/decisions/.
Alpha release notes
j2py-converter is published as an alpha package. Expect incomplete construct
coverage, diagnostics for unsupported regions, and manual review on production-scale
codebases. See docs/RELEASING.md for the release checklist.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file j2py_converter-0.4.0a1.tar.gz.
File metadata
- Download URL: j2py_converter-0.4.0a1.tar.gz
- Upload date:
- Size: 299.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
848a5680b2a3148980872f3ed4f7e508456220e7261f01ed6c1fdea3188c018e
|
|
| MD5 |
a1502dfb4fc6dbacf7d1d0e367465b19
|
|
| BLAKE2b-256 |
1c8a046383409dddbc51fdcc71b047fead011d0c576066182a731f66d0f97d01
|
Provenance
The following attestation bundles were made for j2py_converter-0.4.0a1.tar.gz:
Publisher:
publish.yml on tomanizer/j2py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
j2py_converter-0.4.0a1.tar.gz -
Subject digest:
848a5680b2a3148980872f3ed4f7e508456220e7261f01ed6c1fdea3188c018e - Sigstore transparency entry: 1826697246
- Sigstore integration time:
-
Permalink:
tomanizer/j2py@8831ddc0b8e8f6aece3dea1fe9015047a061d327 -
Branch / Tag:
refs/tags/v0.4.0a1 - Owner: https://github.com/tomanizer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8831ddc0b8e8f6aece3dea1fe9015047a061d327 -
Trigger Event:
release
-
Statement type:
File details
Details for the file j2py_converter-0.4.0a1-py3-none-any.whl.
File metadata
- Download URL: j2py_converter-0.4.0a1-py3-none-any.whl
- Upload date:
- Size: 127.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
73dacb7906fc577c78074d86db13683c0e9041a20dac9e5d716e13efefc43d27
|
|
| MD5 |
bebd58761759584e8c7f73f45d539c23
|
|
| BLAKE2b-256 |
0f385409b35451118be56b25cee7540a65f1e06227551fbcdb180d7d22e6b159
|
Provenance
The following attestation bundles were made for j2py_converter-0.4.0a1-py3-none-any.whl:
Publisher:
publish.yml on tomanizer/j2py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
j2py_converter-0.4.0a1-py3-none-any.whl -
Subject digest:
73dacb7906fc577c78074d86db13683c0e9041a20dac9e5d716e13efefc43d27 - Sigstore transparency entry: 1826697345
- Sigstore integration time:
-
Permalink:
tomanizer/j2py@8831ddc0b8e8f6aece3dea1fe9015047a061d327 -
Branch / Tag:
refs/tags/v0.4.0a1 - Owner: https://github.com/tomanizer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8831ddc0b8e8f6aece3dea1fe9015047a061d327 -
Trigger Event:
release
-
Statement type: