A comprehensive Python-to-Java bridge enabling seamless integration between Python and Java virtual machines
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Handle Java exceptions within Python and bridge between Python and Java exception systems. This module provides seamless exception interoperability, allowing Python code to catch Java exceptions and Java code to handle Python exceptions.
Base classes for Java exception handling in Python.
class JException(Exception):
"""Base class for all Java exceptions.
Bridges Java exceptions to Python exception system.
All Java exceptions are subclasses of JException.
"""
def __init__(self, *args):
"""Initialize Java exception.
Args:
*args: Exception arguments
"""
def getMessage(self) -> str:
"""Get the Java exception message.
Returns:
str: Exception message from Java
"""
def getStackTrace(self):
"""Get Java stack trace.
Returns:
Java array of StackTraceElement objects
"""
def printStackTrace(self):
"""Print Java stack trace to standard error."""
def getCause(self):
"""Get the cause of this exception.
Returns:
JException or None: The underlying cause
"""import jpype
jpype.startJVM()
try:
# This will cause a Java exception
string_obj = jpype.java.lang.String("test")
char = string_obj.charAt(10) # StringIndexOutOfBoundsException
except jpype.JException as e:
print(f"Caught Java exception: {e}")
print(f"Exception type: {type(e)}")
print(f"Message: {e.getMessage()}")
jpype.shutdownJVM()import jpype
jpype.startJVM()
# Access Java exception classes
NumberFormatException = jpype.java.lang.NumberFormatException
IllegalArgumentException = jpype.java.lang.IllegalArgumentException
try:
# Parse invalid number
result = jpype.java.lang.Integer.parseInt("not_a_number")
except NumberFormatException as e:
print(f"Number format error: {e.getMessage()}")
except IllegalArgumentException as e:
print(f"Illegal argument: {e.getMessage()}")
except jpype.JException as e:
print(f"Other Java exception: {e}")
jpype.shutdownJVM()import jpype
jpype.startJVM()
# Java exception hierarchy maps to Python
RuntimeException = jpype.java.lang.RuntimeException
NullPointerException = jpype.java.lang.NullPointerException
try:
# Create null pointer situation
null_string = None
# This would cause NullPointerException in Java context
raise jpype.java.lang.NullPointerException("Test null pointer")
except NullPointerException as e:
print("Caught NullPointerException")
except RuntimeException as e:
print("Caught RuntimeException (parent class)")
except jpype.JException as e:
print("Caught general Java exception")
jpype.shutdownJVM()import jpype
jpype.startJVM()
try:
# Create a method that throws an exception
Collections = jpype.java.util.Collections
empty_list = Collections.emptyList()
element = empty_list.get(0) # IndexOutOfBoundsException
except jpype.JException as e:
print(f"Exception message: {e.getMessage()}")
print(f"Exception class: {e.getClass().getName()}")
# Print Java stack trace
print("\nJava stack trace:")
e.printStackTrace()
# Get stack trace elements
stack_trace = e.getStackTrace()
print(f"\nStack trace has {len(stack_trace)} elements")
if len(stack_trace) > 0:
top_element = stack_trace[0]
print(f"Top frame: {top_element.getClassName()}.{top_element.getMethodName()}")
print(f"Line number: {top_element.getLineNumber()}")
jpype.shutdownJVM()import jpype
jpype.startJVM()
try:
try:
# Cause initial exception
jpype.java.lang.Integer.parseInt("invalid")
except jpype.JException as e:
# Wrap in another exception
RuntimeException = jpype.java.lang.RuntimeException
wrapped = RuntimeException("Wrapped exception", e)
raise wrapped
except jpype.JException as e:
print(f"Outer exception: {e.getMessage()}")
# Get the original cause
cause = e.getCause()
if cause:
print(f"Original cause: {cause.getMessage()}")
print(f"Cause type: {cause.getClass().getName()}")
jpype.shutdownJVM()import jpype
from jpype import JImplements, JOverride
jpype.startJVM()
# Throw Java exceptions from Python code
def risky_operation(value):
if value < 0:
raise jpype.java.lang.IllegalArgumentException("Value cannot be negative")
if value == 0:
raise jpype.java.lang.ArithmeticException("Division by zero")
return 100 / value
try:
result1 = risky_operation(-5)
except jpype.java.lang.IllegalArgumentException as e:
print(f"Illegal argument: {e.getMessage()}")
try:
result2 = risky_operation(0)
except jpype.java.lang.ArithmeticException as e:
print(f"Arithmetic error: {e.getMessage()}")
# Normal case
result3 = risky_operation(10)
print(f"Normal result: {result3}")
jpype.shutdownJVM()import jpype
from jpype import JImplements, JOverride
jpype.startJVM()
Callable = jpype.java.util.concurrent.Callable
@JImplements(Callable)
class ExceptionThrowingTask:
def __init__(self, should_fail, error_type="runtime"):
self.should_fail = should_fail
self.error_type = error_type
@JOverride
def call(self):
if not self.should_fail:
return "Success"
# Throw different types of Java exceptions
if self.error_type == "runtime":
raise jpype.java.lang.RuntimeException("Task failed with runtime error")
elif self.error_type == "illegal_state":
raise jpype.java.lang.IllegalStateException("Invalid task state")
elif self.error_type == "io":
raise jpype.java.io.IOException("I/O operation failed")
else:
raise jpype.java.lang.Exception("Generic task failure")
# Use with ExecutorService
executor = jpype.java.util.concurrent.Executors.newSingleThreadExecutor()
# Test different exception types
test_cases = [
("runtime", jpype.java.lang.RuntimeException),
("illegal_state", jpype.java.lang.IllegalStateException),
("io", jpype.java.io.IOException),
("generic", jpype.java.lang.Exception)
]
for error_type, expected_exception in test_cases:
task = ExceptionThrowingTask(True, error_type)
future = executor.submit(task)
try:
result = future.get()
except jpype.java.util.concurrent.ExecutionException as e:
cause = e.getCause()
print(f"Task with {error_type} error failed: {cause.getMessage()}")
print(f"Exception type: {cause.getClass().getName()}")
executor.shutdown()
jpype.shutdownJVM()import jpype
jpype.startJVM()
def python_to_java_exception(python_func):
"""Decorator to translate Python exceptions to Java exceptions."""
def wrapper(*args, **kwargs):
try:
return python_func(*args, **kwargs)
except ValueError as e:
raise jpype.java.lang.IllegalArgumentException(str(e))
except FileNotFoundError as e:
raise jpype.java.io.FileNotFoundException(str(e))
except Exception as e:
raise jpype.java.lang.RuntimeException(f"Python error: {str(e)}")
return wrapper
@python_to_java_exception
def parse_positive_int(value_str):
"""Parse string to positive integer, raising appropriate exceptions."""
if not isinstance(value_str, str):
raise ValueError("Input must be a string")
try:
value = int(value_str)
except ValueError:
raise ValueError(f"'{value_str}' is not a valid integer")
if value <= 0:
raise ValueError("Value must be positive")
return value
# Test exception translation
test_values = ["123", "abc", "-5", 42]
for test_val in test_values:
try:
result = parse_positive_int(test_val)
print(f"Parsed {test_val} -> {result}")
except jpype.JException as e:
print(f"Java exception for {test_val}: {e.getClass().getSimpleName()} - {e.getMessage()}")
jpype.shutdownJVM()import jpype
from jpype import JImplements
jpype.startJVM()
# Create custom Java exception by extending existing ones
@JImplements("java.lang.RuntimeException", deferred=True)
class CustomBusinessException:
def __init__(self, message, error_code=None):
super().__init__(message)
self.error_code = error_code
def getErrorCode(self):
return self.error_code
# Use custom exception
def business_operation(account_id):
if account_id < 0:
raise CustomBusinessException("Invalid account ID", "INVALID_ACCOUNT")
if account_id == 999:
raise CustomBusinessException("Account suspended", "ACCOUNT_SUSPENDED")
return f"Operation successful for account {account_id}"
# Test custom exception
try:
result = business_operation(-1)
except CustomBusinessException as e:
print(f"Business error: {e.getMessage()}")
print(f"Error code: {e.getErrorCode()}")
jpype.shutdownJVM()Install with Tessl CLI
npx tessl i tessl/pypi-jpype1