# Py4J lets a Python interpreter drive Java objects it never imported

> Py4J is a bridge that lets Python programs dynamically access Java objects in a Java virtual machine, calling methods as if the objects lived in the interpreter and reading Java collections through the standard Python collection methods, with a callback path in the other direction for Java code calling Python. The repository builds a jar and a Python package from one source tree.

**py4j/py4j** — Py4J enables Python programs to dynamically access arbitrary Java objects

- Repository: https://github.com/py4j/py4j
- Website: https://www.py4j.org
- Stars: 1,289 · Forks: 236
- Language: Java
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/py4j-py4j

## Calling Java methods as if they were local

Py4J enables Python programs running in a Python interpreter to dynamically access Java objects in a Java Virtual Machine. Methods are called as if the Java objects resided in the Python interpreter, and Java collections can be accessed through standard Python collection methods.

That last clause is the one that matters most in practice. A bridge that only exposed methods would still force you to write Java-flavoured code on the Python side, and the collection interoperability is what makes the difference between a bridge and a wrapper. You can iterate a Java list as if it were a Python list, and that is what removes the impedance for a lot of code.

The word dynamic in the project description is about the access, not about the typing. There is no compile time binding to the Java class, and the objects are reached through a gateway rather than through generated stubs.

And the direction is not one way. Py4J also enables Java programs to call back Python objects, so a Java library that takes a callback can be given a Python callable, which is how you use a Java library whose extension points are all callbacks without rewriting the library.

The implementation is Java on the gateway side and Python on the client side, the licence is BSD 3-Clause, the homepage is py4j.org, and the last push was on 2026-06-11.

## Three projects in one source tree

The repository is described as the source repository of Py4J projects, and the top level listing shows what that means in practice.

There is py4j-java/, which is the Java side including the gateway and the jar, py4j-python/, which is the Python client package with its own documentation directory and its own test requirements, and py4j-web/, which holds the website and its documentation requirements.

So this is not a single library with two language bindings. It is a Java program that acts as a gateway, a Python library that talks to it, and a documentation site, kept in one repository because they have to agree on the wire protocol and the version number.

There is also a note about the compiled release. The latest compiled release is available in the current-release directory, which is a convenience for people who want the jar without building it, and a reminder that the source tree and the published artefact are not the same thing.

The rest of the tree is standard Python packaging: setup.py at the root, MANIFEST.in, requirements.txt, LICENSE.txt, AUTHORS.txt, CONTRIBUTING.rst and README.rst.

The topic tags say distributed-systems, java, programming-languages and python, and the distributed systems one is worth a note, because the Py4J architecture is a client and a server over a socket, which is what makes it usable across machines and also what makes it not a simple in-process library.

## The install builds the jar with Gradle

The setup script is unusual in a way that catches people out, and it is worth reading before installing.

The version is read out of the Python package source, from a version.py file inside py4j-python/src/py4j, and it is evaluated with a compile and exec rather than imported. The comment explains why: for Python 3 compatibility they cannot use execfile, and this is the 2to3 conversion of it.

Then the script changes directory into py4j-java and builds the jar.

```python
os.chdir("py4j-java")
if os.name == "nt":
    subprocess.call("./gradlew.bat buildPython", shell=True)
else:
    subprocess.call("./gradle buildPython", shell=True)
os.chdir("..")
```

So installing the Python package from source also runs a Gradle build, and on Windows it uses the gradlew.bat wrapper. That is a real requirement rather than an optimisation, because the jar is part of the distribution.

The jar is then shipped as a data file.

```python
JAR_FILE = "py4j" + VERSION + ".jar"
JAR_FILE_PATH = os.path.join("py4j-python", "py4j-java", JAR_FILE)
```

and the setup call installs it under share/py4j in the data files, with the Python packages themselves taken from py4j-python/src and the test package included alongside the main one.

The practical consequence is that the Python package and the jar version are the same string, which is what keeps the two ends of the protocol in step, and that an installation from source needs a JDK and a working Gradle build.

## One project, two package managers

The badges show the distribution across two ecosystems, and the names tell you the coordinates.

On the Python side there is the PyPI badge for the package version and another for the supported Python versions, pointing at pypi.python.org. On the Java side there is a Maven Central badge for the artefact net.sf.py4j, which is the group id and artefact id that a Java project would put in its build file.

That is a deliberate choice with a practical consequence. A Java developer who wants to call Python, or a Python developer who wants to call Java, can pull their half from the registry their own toolchain already uses, and the two halves arrive as the same version.

The author is Barthelemy Dagenais, and the description in the setup script matches the readme, that the library enables Python programs to dynamically access arbitrary Java objects.

The topics on the repository also hint at where this sits in the ecosystem. The distributed-systems tag next to programming-languages suggests a tool that is used to hold two runtimes together, and the callback direction is what makes the pairing work in both directions rather than only from Python.

## Support, contributions and the CI badge

The support route is a mailing list, and the readme gives the address in the traditional obfuscated form, py4j at py4j dot org, and asks you to register and then post.

That is an old school arrangement for a project of this age, and it has an implication. A mailing list is where the accumulated knowledge of a project like this lives, and it is searchable in a way that a GitHub issue tracker often is not for questions rather than bugs. For a library whose difficulty is often in the details of how the two runtimes interact, that archive is genuinely useful.

Contributions go through the same site. The project says it is always happy to receive code contributions, bug reports and documentation fixes, and points at the contributing guide.

The continuous integration badge points at a GitHub Actions workflow named test.yml, and the badge is the image of the workflow status, so the test run is visible at the top of the readme without clicking anything.

There is a security consideration that the project itself does not spell out and that a reader should think about. Py4J is a bridge that lets one runtime call into another, and the mechanism involves a gateway that the Python side connects to. If you expose a Py4J gateway to anything other than your own code, you have exposed the JVM behind it, and the readme does not discuss that, so it is on the person deploying it.

## Licence, packaging and the version question

Py4J is distributed with the BSD 3-Clause license, with the full text in LICENSE.txt, and the readme points at it explicitly. GitHub cannot classify the licence as a standard one, which is the usual consequence of a licence file that a classifier does not recognise, and the LICENSE.txt file is the authoritative text.

The permissive terms matter for a bridge. A BSD 3-Clause library can be embedded in a commercial product without the copyleft obligations, and for a tool that sits between two languages that is usually what people need.

The packaging is the other thing to know. The Python side is a normal setuptools project with the package and its tests, the source under py4j-python/src, and the jar shipped as a data file. The requirements file is a two line aggregator that pulls in the test requirements of the Python side and the documentation requirements of the web side, which is a neat way to keep a monorepo's test tooling in one place.

There are no GitHub releases in this repository, which is consistent with how the project is distributed. The artefacts live on PyPI and Maven Central, and the current-release directory in the tree holds the compiled jar for people who want it directly, so the git history is the development record and the registries are the distribution.

## Conclusion

Adopt Py4J when you need to drive an existing Java library from Python and cannot or will not expose it over HTTP or JNI, and you accept that the bridge is a socket and a JVM on the other side of it. Do not adopt it as a general interoperability layer between Python and Java services, because it is designed for the single process case with a Java gateway. Verify first that the jar version you ship matches the Python package version, since the setup script couples them, and that the JVM you start is the one your Java code needs, because Py4J talks to a real virtual machine rather than to a stub.

## FAQ

### What does Py4J do?

It enables Python programs running in a Python interpreter to dynamically access Java objects in a Java Virtual Machine. Methods are called as if the Java objects resided in the Python interpreter, and Java collections can be accessed through standard Python collection methods.

### Can Java code call Python through Py4J?

Yes. Py4J also enables Java programs to call back Python objects, which is what lets a Java library with callback style extension points be driven from Python.

### What is in the Py4J repository?

Three projects in one source tree: py4j-java for the Java side and the jar, py4j-python for the Python client package and its documentation, and py4j-web for the site. There is also a current-release directory holding the latest compiled release.

### What does installing Py4J from source involve?

The setup script reads the version from the Python package source, changes into the py4j-java directory and runs a Gradle build of the jar, using gradlew.bat on Windows and gradle elsewhere, then ships the jar as a data file under share/py4j. So an install from source needs a JDK and a working Gradle build.

### Where is Py4J published?

On both registries, so each toolchain can pull its own half: the Python package is on PyPI and the Java artefact is on Maven Central as net.sf.py4j. The two carry the same version string, which keeps the client and the gateway in step.

### What licence is Py4J under?

BSD 3-Clause, with the full text in the LICENSE.txt file at the root of the repository, though GitHub cannot classify it as a standard licence.

## Sources

- [Issues](https://github.com/py4j/py4j/issues)
- [Project website](https://www.py4j.org)
- [py4j/py4j on GitHub](https://github.com/py4j/py4j)
- [README](https://github.com/py4j/py4j/blob/master/README.md)

---

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