Created
May 11, 2025 08:49
-
-
Save ShalokShalom/fce61a7bd17e4e0cc80c9292cfa43477 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# python_julia_connector.py | |
import os | |
from juliacall import Main as jl | |
# from juliacall import JuliaError # To specifically catch Julia errors if needed | |
# --- Environment Management & Initialization --- | |
# You might need to configure JuliaCall if Julia is not in your PATH | |
# or if you want to use a specific Julia project environment. | |
# For example: | |
# from juliacall import JuliaCall # For more control | |
# jl = JuliaCall(julia="path/to/julia_executable") # Specify Julia executable | |
# jl.Pkg.activate("path/to/your/JuliaProject") # Activate a Julia project | |
print("Python: Initializing Julia connection via juliacall...") | |
try: | |
# Eagerly evaluate something simple to ensure Julia is working | |
# This also triggers Julia compilation/setup if it's the first time. | |
jl_version_info = jl.seval("VERSION") | |
# jl.VERSION is a juliacall object, convert to string for cleaner printing | |
jl_version_str = f"{jl_version_info.major}.{jl_version_info.minor}.{jl_version_info.patch}" | |
print(f"Python: Successfully connected to Julia {jl_version_str}") | |
except Exception as e: | |
print(f"Python: CRITICAL - Error initializing Julia: {e}") | |
print("Python: Please ensure Julia is installed and juliacall is configured correctly.") | |
# Re-raise to make it clear to Racket/pyffi that initialization failed. | |
raise | |
# --- Function Exposure & Julia Interaction --- | |
# Define some Julia functions directly for this example. | |
jl.seval(""" | |
module PyConnectorJuliaModule | |
export greet_from_julia, add_in_julia, process_list_julia, complex_julia_computation, julia_sqrt | |
function greet_from_julia(name::String) | |
return "Hello, " * name * " from Julia! 👋 (via PyConnectorJuliaModule)" | |
end | |
function add_in_julia(a::Number, b::Number) | |
return a + b | |
end | |
function process_list_julia(arr::Vector) | |
return isempty(arr) ? 0.0 : sum(x^2 for x in arr) | |
end | |
function complex_julia_computation(data::Dict) | |
if haskey(data, "value") && haskey(data, "multiplier") | |
val = data["value"] | |
mult = data["multiplier"] | |
if !(val isa Number && mult isa Number) | |
return "Error: 'value' and 'multiplier' must be numbers." | |
end | |
return val * mult | |
else | |
return "Error: Missing 'value' or 'multiplier' in Dict." | |
end | |
end | |
julia_sqrt(x::Float64) = sqrt(x) # Simple function for eval_julia_string later | |
end | |
""") | |
# Make the module's functions accessible in Python via jl.ModuleName | |
jl.seval("using .PyConnectorJuliaModule") | |
print("Python: PyConnectorJuliaModule loaded and its functions are available.") | |
# --- Python functions callable from Racket (Abstraction Layer) --- | |
def call_julia_greet(name_str): | |
"""Calls the greet_from_julia function in Julia.""" | |
print(f"Python: call_julia_greet received '{name_str}'") | |
try: | |
result = jl.PyConnectorJuliaModule.greet_from_julia(name_str) | |
print(f"Python: Julia returned: '{result}'") | |
return result | |
except Exception as e: | |
print(f"Python: Error in call_julia_greet: {e}") | |
return f"Python Error: {e}" | |
def call_julia_add(a, b): | |
"""Calls the add_in_julia function in Julia.""" | |
print(f"Python: call_julia_add received {a}, {b}") | |
try: | |
result = jl.PyConnectorJuliaModule.add_in_julia(a, b) | |
print(f"Python: Julia returned: {result}") | |
return result | |
except Exception as e: | |
print(f"Python: Error in call_julia_add: {e}") | |
raise | |
def call_julia_process_list(py_list): | |
"""Calls process_list_julia with a list.""" | |
print(f"Python: call_julia_process_list received {py_list}") | |
try: | |
result = jl.PyConnectorJuliaModule.process_list_julia(py_list) | |
print(f"Python: Julia returned: {result}") | |
return result | |
except Exception as e: | |
print(f"Python: Error in call_julia_process_list: {e}") | |
raise | |
def call_julia_complex_op(py_dict): | |
"""Calls complex_julia_computation with a dictionary.""" | |
print(f"Python: call_julia_complex_op received {py_dict}") | |
try: | |
result = jl.PyConnectorJuliaModule.complex_julia_computation(py_dict) | |
print(f"Python: Julia returned: {result}") | |
return result | |
except Exception as e: | |
print(f"Python: Error in call_julia_complex_op: {e}") | |
raise | |
def eval_julia_string(julia_code_string): | |
"""Evaluates an arbitrary string of Julia code in the Main Julia module.""" | |
print(f"Python: eval_julia_string received: '{julia_code_string}'") | |
try: | |
# jl.seval evaluates in Julia's Main scope. | |
# If functions from PyConnectorJuliaModule are needed, they are already `using`ed. | |
result = jl.seval(julia_code_string) | |
# juliacall might return specific Julia types (e.g. JuliaObject). | |
# For simple results, it often converts them to Python types. | |
# If result is a complex Julia object, you might need jl.pyconvert(result) | |
print(f"Python: Julia eval returned: {result} (type: {type(result)})") | |
return result | |
except Exception as e: | |
print(f"Python: Error evaluating Julia string: {e}") | |
# Return a string indicating error, or re-raise | |
return f"Python Error during Julia eval: {e}" | |
if __name__ == '__main__': | |
# For testing the Python script directly | |
print("\n--- Testing Python script directly ---") | |
print(f"Greet: {call_julia_greet('Direct Python Test')}") | |
print(f"Add: {call_julia_add(10, 25)}") | |
print(f"Process List: {call_julia_process_list([1, 2, 3, 4])}") | |
print(f"Complex Op: {call_julia_complex_op({'value': 5, 'multiplier': 3, 'extra_key': 'test'})}") | |
print(f"Eval String (sqrt): {eval_julia_string('PyConnectorJuliaModule.julia_sqrt(16.0)')}") | |
print(f"Eval String (mean): {eval_julia_string('using Statistics; mean([10.0, 20.0, 30.0])')}") | |
print("--- End direct Python test ---") | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#lang racket/base | |
(require pyffi | |
racket/file | |
racket/pretty | |
racket/match | |
racket/format) | |
(printf "Racket: Starting Julia bridge script...\n") | |
;; --- Configuration (Optional: Customize Python Executable) --- | |
;; If your Python executable (especially from a virtual environment) | |
;; is not in the default PATH, or you want to be explicit: | |
;; (pyffi-option-set 'python-executable "/path/to/your/venv/bin/python") | |
;; Example for a venv in the current project directory: | |
;; (define venv-python-path (build-path (current-directory) ".venv" "bin" "python")) | |
;; (when (file-exists? venv-python-path) | |
;; (printf "Racket: Using Python from ~s\n" venv-python-path) | |
;; (pyffi-option-set 'python-executable venv-python-path)) | |
;; --- Path to the Python Connector Script --- | |
(define python-connector-filename "python_julia_connector.py") | |
(define python-connector-path (build-path (current-directory) python-connector-filename)) | |
(unless (file-exists? python-connector-path) | |
(eprintf "Racket: CRITICAL - Python connector script not found: ~a\n" python-connector-path) | |
(eprintf "Racket: Please ensure '~a' is in the same directory as this Racket script.\n" python-connector-filename) | |
(exit 1)) | |
(printf "Racket: Found Python connector at ~s\n" python-connector-path) | |
;; --- Importing the Python module --- | |
;; This will execute python_julia_connector.py. | |
;; Output from Python's print statements during its initialization will appear here. | |
(printf "Racket: Attempting to import Python module via pyffi...\n") | |
(define pyjuliaconnector | |
(try | |
(python-import python-connector-path) ; pyffi imports the file as a module | |
(handle [exn:fail:pyffi? exn] | |
(eprintf "Racket: CRITICAL - pyffi failed to import or initialize the Python module.\n") | |
(eprintf "Racket: Python error: ~a\n" (exn-message exn)) | |
(eprintf "Racket: Check Python console output above for details from 'python_julia_connector.py'.\n") | |
(eprintf "Racket: Ensure Python, Julia, and juliacall are correctly installed and configured.\n") | |
(exit 1)))) | |
(printf "Racket: Python module imported successfully as 'pyjuliaconnector'.\n\n") | |
;; --- Racket Functions to Interact with Julia via Python --- | |
(define (julia-greet name) | |
(printf "Racket: Calling julia-greet with \"~a\"\n" name) | |
(handle-python-call | |
(lambda () (pyjuliaconnector call_julia_greet name)) | |
'julia-greet)) | |
(define (julia-add a b) | |
(printf "Racket: Calling julia-add with ~a and ~a\n" a b) | |
(handle-python-call | |
(lambda () (pyjuliaconnector call_julia_add a b)) | |
'julia-add)) | |
(define (julia-process-list rkt-list) | |
(printf "Racket: Calling julia-process-list with ~s\n" rkt-list) | |
(handle-python-call | |
(lambda () (pyjuliaconnector call_julia_process_list rkt-list)) | |
'julia-process-list)) | |
(define (julia-complex-op rkt-hash) | |
(printf "Racket: Calling julia-complex-op with ~s\n" rkt-hash) | |
(handle-python-call | |
(lambda () (pyjuliaconnector call_julia_complex_op rkt-hash)) | |
'julia-complex-op)) | |
(define (julia-eval-string julia-code) | |
(printf "Racket: Calling julia-eval-string with \"~a\"\n" julia-code) | |
(handle-python-call | |
(lambda () (pyjuliaconnector eval_julia_string julia-code)) | |
'julia-eval-string)) | |
;; Helper for handling pyffi exceptions | |
(define (handle-python-call thunk call-name) | |
(with-handlers ([exn:fail:pyffi? | |
(lambda (exn) | |
(eprintf "Racket: Error during '~a' call to Python/Julia.\n" call-name) | |
(eprintf " Python/Julia error: ~a\n" (exn-message exn)) | |
(values #f (exn-message exn)))]) ; Return #f and error message | |
(let ([result (thunk)]) | |
(printf "Racket: '~a' successful. Received: ~s\n" call-name result) | |
(values result #f)))) ; Return result and no error | |
;; --- Demonstrating the Bridge --- | |
(printf "\n--- Racket: Demonstrating Julia Calls via Python Bridge ---\n") | |
(let-values ([(result err) (julia-greet "Racket User")]) | |
(if err | |
(printf "Racket Greet Error: ~a\n" err) | |
(printf "Racket Greet Result: ~s\n\n" result))) | |
(let-values ([(result err) ( |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment