Skip to content

Inline python#

Code Example

Runnable Example in Jac and JacLib

# Inline Python - Embedding Python code blocks within Jac

# ===== Part 1: Basic Python Block =====
::py::
def python_hello():
    return "Hello from Python!"

def python_add(a, b):
    return a + b
::py::

with entry {
    print("=== 1. Basic Python Block ===");
    print(python_hello());
    print(f"Python add: {python_add(10, 20)}");
}

# ===== Part 2: Python Block in Object =====
obj DataProcessor {
    has data: list = [];

    ::py::
    def process(self):
        """Process data using Python libraries."""
        if not self.data:
            return []
        # Use Python built-ins
        return [x * 2 for x in self.data if x > 0]

    def analyze(self):
        """Statistical analysis with Python."""
        if not self.data:
            return {"mean": 0, "sum": 0}
        return {
            "mean": sum(self.data) / len(self.data),
            "sum": sum(self.data),
            "max": max(self.data),
            "min": min(self.data)
        }
    ::py::
}

with entry {
    print("\n=== 2. Python Methods in Object ===");
    processor = DataProcessor(data=[1, -2, 3, 4, -5, 6]);
    processed = processor.process();
    print(f"Processed data: {processed}");

    stats = processor.analyze();
    mean_val = round(stats['mean'], 2);
    print(f"Statistics: mean={mean_val}, sum={stats['sum']}");
}

# ===== Part 3: Python Block with Jac Data Structures =====
::py::
def python_process_list(jac_list):
    """Python can work with Jac data structures."""
    # Lists, dicts, and other collections are compatible
    return [x ** 2 for x in jac_list]

def python_process_dict(jac_dict):
    """Process Jac dictionaries in Python."""
    return {k: v * 2 for k, v in jac_dict.items()}
::py::

with entry {
    print("\n=== 3. Python Processing Jac Data ===");

    jac_list = [1, 2, 3, 4, 5];
    squared = python_process_list(jac_list);
    print(f"Squared: {squared}");

    jac_dict = {"a": 10, "b": 20, "c": 30};
    doubled = python_process_dict(jac_dict);
    print(f"Doubled dict: {doubled}");
}

# ===== Part 4: Python Libraries Integration =====
::py::
import json
import math

def format_json(data):
    """Use Python's json library."""
    return json.dumps(data, indent=2)

def math_operations(x):
    """Use Python's math library."""
    return {
        "sqrt": math.sqrt(x),
        "log": math.log(x),
        "sin": math.sin(x)
    }
::py::

with entry {
    print("\n=== 4. Python Libraries ===");

    data = {"name": "Jac", "version": 1.0, "features": ["OSP", "Python interop"]};
    json_str = format_json(data);
    print(f"JSON output:\n{json_str}");

    math_result = math_operations(16);
    log_val = round(math_result['log'], 2);
    print(f"Math: sqrt={math_result['sqrt']}, log={log_val}");
}

# ===== Part 5: Python in Node with State =====
node MathNode {
    has value: float = 0.0;
    has computed: dict = {};

    ::py::
    def compute_all(self):
        """Compute various mathematical properties."""
        import math
        v = self.value
        self.computed = {
            "square": v ** 2,
            "cube": v ** 3,
            "sqrt": math.sqrt(abs(v)),
            "is_even": v % 2 == 0
        }
        return self.computed
    ::py::
}

with entry {
    print("\n=== 5. Python in Nodes ===");

    math_node = MathNode(value=9.0);
    results = math_node.compute_all();
    print(f"Node value: {math_node.value}");
    print(f"Computed: square={results['square']}, cube={results['cube']}");
}

# ===== Part 6: Python Block Accessing Jac Archetypes =====
obj Counter {
    has count: int = 0;

    ::py::
    def increment(self, by=1):
        """Increment counter (Python method)."""
        self.count += by
        return self.count

    def reset(self):
        """Reset counter."""
        self.count = 0
    ::py::
}

with entry {
    print("\n=== 6. Python Methods Accessing Jac State ===");

    counter = Counter();
    counter.increment(5);
    counter.increment(3);
    print(f"Counter after increments: {counter.count}");

    counter.reset();
    print(f"Counter after reset: {counter.count}");
}

# ===== Part 7: Mixed Jac and Python Methods =====
obj Calculator {
    has history: list = [];

    # Jac method
    def add_jac(value: int) {
        self.history.append(value);
        return sum(self.history);
    }

    ::py::
    # Python method
    def add_python(self, value):
        """Python version of add."""
        self.history.append(value)
        return sum(self.history)

    def get_stats(self):
        """Python statistics on history."""
        if not self.history:
            return {"avg": 0, "total": 0}
        return {
            "avg": sum(self.history) / len(self.history),
            "total": sum(self.history),
            "count": len(self.history)
        }
    ::py::
}

with entry {
    print("\n=== 7. Mixed Jac/Python Methods ===");

    calc = Calculator();
    calc.add_jac(10);
    calc.add_python(20);
    calc.add_jac(30);

    stats = calc.get_stats();
    avg_val = round(stats['avg'], 1);
    print(f"Stats: avg={avg_val}, total={stats['total']}, count={stats['count']}");
}

with entry {
    print("\n✓ Inline Python complete!");
}
# Inline Python - Embedding Python code blocks within Jac

# ===== Part 1: Basic Python Block =====
::py::
def python_hello():
    return "Hello from Python!"

def python_add(a, b):
    return a + b
::py::

with entry {
    print("=== 1. Basic Python Block ===");
    print(python_hello());
    print(f"Python add: {python_add(10, 20)}");
}

# ===== Part 2: Python Block in Object =====
obj DataProcessor {
    has data: list = [];

    ::py::
    def process(self):
        """Process data using Python libraries."""
        if not self.data:
            return []
        # Use Python built-ins
        return [x * 2 for x in self.data if x > 0]

    def analyze(self):
        """Statistical analysis with Python."""
        if not self.data:
            return {"mean": 0, "sum": 0}
        return {
            "mean": sum(self.data) / len(self.data),
            "sum": sum(self.data),
            "max": max(self.data),
            "min": min(self.data)
        }
    ::py::
}

with entry {
    print("\n=== 2. Python Methods in Object ===");
    processor = DataProcessor(data=[1, -2, 3, 4, -5, 6]);
    processed = processor.process();
    print(f"Processed data: {processed}");

    stats = processor.analyze();
    mean_val = round(stats['mean'], 2);
    print(f"Statistics: mean={mean_val}, sum={stats['sum']}");
}

# ===== Part 3: Python Block with Jac Data Structures =====
::py::
def python_process_list(jac_list):
    """Python can work with Jac data structures."""
    # Lists, dicts, and other collections are compatible
    return [x ** 2 for x in jac_list]

def python_process_dict(jac_dict):
    """Process Jac dictionaries in Python."""
    return {k: v * 2 for k, v in jac_dict.items()}
::py::

with entry {
    print("\n=== 3. Python Processing Jac Data ===");

    jac_list = [1, 2, 3, 4, 5];
    squared = python_process_list(jac_list);
    print(f"Squared: {squared}");

    jac_dict = {"a": 10, "b": 20, "c": 30};
    doubled = python_process_dict(jac_dict);
    print(f"Doubled dict: {doubled}");
}

# ===== Part 4: Python Libraries Integration =====
::py::
import json
import math

def format_json(data):
    """Use Python's json library."""
    return json.dumps(data, indent=2)

def math_operations(x):
    """Use Python's math library."""
    return {
        "sqrt": math.sqrt(x),
        "log": math.log(x),
        "sin": math.sin(x)
    }
::py::

with entry {
    print("\n=== 4. Python Libraries ===");

    data = {"name": "Jac", "version": 1.0, "features": ["OSP", "Python interop"]};
    json_str = format_json(data);
    print(f"JSON output:\n{json_str}");

    math_result = math_operations(16);
    log_val = round(math_result['log'], 2);
    print(f"Math: sqrt={math_result['sqrt']}, log={log_val}");
}

# ===== Part 5: Python in Node with State =====
node MathNode {
    has value: float = 0.0;
    has computed: dict = {};

    ::py::
    def compute_all(self):
        """Compute various mathematical properties."""
        import math
        v = self.value
        self.computed = {
            "square": v ** 2,
            "cube": v ** 3,
            "sqrt": math.sqrt(abs(v)),
            "is_even": v % 2 == 0
        }
        return self.computed
    ::py::
}

with entry {
    print("\n=== 5. Python in Nodes ===");

    math_node = MathNode(value=9.0);
    results = math_node.compute_all();
    print(f"Node value: {math_node.value}");
    print(f"Computed: square={results['square']}, cube={results['cube']}");
}

# ===== Part 6: Python Block Accessing Jac Archetypes =====
obj Counter {
    has count: int = 0;

    ::py::
    def increment(self, by=1):
        """Increment counter (Python method)."""
        self.count += by
        return self.count

    def reset(self):
        """Reset counter."""
        self.count = 0
    ::py::
}

with entry {
    print("\n=== 6. Python Methods Accessing Jac State ===");

    counter = Counter();
    counter.increment(5);
    counter.increment(3);
    print(f"Counter after increments: {counter.count}");

    counter.reset();
    print(f"Counter after reset: {counter.count}");
}

# ===== Part 7: Mixed Jac and Python Methods =====
obj Calculator {
    has history: list = [];

    # Jac method
    def add_jac(value: int) {
        self.history.append(value);
        return sum(self.history);
    }

    ::py::
    # Python method
    def add_python(self, value):
        """Python version of add."""
        self.history.append(value)
        return sum(self.history)

    def get_stats(self):
        """Python statistics on history."""
        if not self.history:
            return {"avg": 0, "total": 0}
        return {
            "avg": sum(self.history) / len(self.history),
            "total": sum(self.history),
            "count": len(self.history)
        }
    ::py::
}

with entry {
    print("\n=== 7. Mixed Jac/Python Methods ===");

    calc = Calculator();
    calc.add_jac(10);
    calc.add_python(20);
    calc.add_jac(30);

    stats = calc.get_stats();
    avg_val = round(stats['avg'], 1);
    print(f"Stats: avg={avg_val}, total={stats['total']}, count={stats['count']}");
}

with entry {
    print("\n✓ Inline Python complete!");
}
from __future__ import annotations
from jaclang.runtimelib.builtin import *
from jaclang import JacMachineInterface as _jl

def python_hello():
    return 'Hello from Python!'

def python_add(a, b):
    return a + b
print('=== 1. Basic Python Block ===')
print(python_hello())
print(f'Python add: {python_add(10, 20)}')

class DataProcessor(_jl.Obj):
    data: list = _jl.field(factory=lambda: [])

    def process(self):
        """Process data using Python libraries."""
        if not self.data:
            return []
        return [x * 2 for x in self.data if x > 0]

    def analyze(self):
        """Statistical analysis with Python."""
        if not self.data:
            return {'mean': 0, 'sum': 0}
        return {'mean': sum(self.data) / len(self.data), 'sum': sum(self.data), 'max': max(self.data), 'min': min(self.data)}
print('\n=== 2. Python Methods in Object ===')
processor = DataProcessor(data=[1, -2, 3, 4, -5, 6])
processed = processor.process()
print(f'Processed data: {processed}')
stats = processor.analyze()
mean_val = round(stats['mean'], 2)
print(f'Statistics: mean={mean_val}, sum={stats['sum']}')

def python_process_list(jac_list):
    """Python can work with Jac data structures."""
    return [x ** 2 for x in jac_list]

def python_process_dict(jac_dict):
    """Process Jac dictionaries in Python."""
    return {k: v * 2 for k, v in jac_dict.items()}
print('\n=== 3. Python Processing Jac Data ===')
jac_list = [1, 2, 3, 4, 5]
squared = python_process_list(jac_list)
print(f'Squared: {squared}')
jac_dict = {'a': 10, 'b': 20, 'c': 30}
doubled = python_process_dict(jac_dict)
print(f'Doubled dict: {doubled}')
import json
import math

def format_json(data):
    """Use Python's json library."""
    return json.dumps(data, indent=2)

def math_operations(x):
    """Use Python's math library."""
    return {'sqrt': math.sqrt(x), 'log': math.log(x), 'sin': math.sin(x)}
print('\n=== 4. Python Libraries ===')
data = {'name': 'Jac', 'version': 1.0, 'features': ['OSP', 'Python interop']}
json_str = format_json(data)
print(f'JSON output:\\n{json_str}')
math_result = math_operations(16)
log_val = round(math_result['log'], 2)
print(f'Math: sqrt={math_result['sqrt']}, log={log_val}')

class MathNode(_jl.Node):
    value: float = 0.0
    computed: dict = _jl.field(factory=lambda: {})

    def compute_all(self):
        """Compute various mathematical properties."""
        import math
        v = self.value
        self.computed = {'square': v ** 2, 'cube': v ** 3, 'sqrt': math.sqrt(abs(v)), 'is_even': v % 2 == 0}
        return self.computed
print('\n=== 5. Python in Nodes ===')
math_node = MathNode(value=9.0)
results = math_node.compute_all()
print(f'Node value: {math_node.value}')
print(f'Computed: square={results['square']}, cube={results['cube']}')

class Counter(_jl.Obj):
    count: int = 0

    def increment(self, by=1):
        """Increment counter (Python method)."""
        self.count += by
        return self.count

    def reset(self):
        """Reset counter."""
        self.count = 0
print('\n=== 6. Python Methods Accessing Jac State ===')
counter = Counter()
counter.increment(5)
counter.increment(3)
print(f'Counter after increments: {counter.count}')
counter.reset()
print(f'Counter after reset: {counter.count}')

class Calculator(_jl.Obj):
    history: list = _jl.field(factory=lambda: [])

    def add_jac(self, value: int) -> None:
        self.history.append(value)
        return sum(self.history)

    def add_python(self, value):
        """Python version of add."""
        self.history.append(value)
        return sum(self.history)

    def get_stats(self):
        """Python statistics on history."""
        if not self.history:
            return {'avg': 0, 'total': 0}
        return {'avg': sum(self.history) / len(self.history), 'total': sum(self.history), 'count': len(self.history)}
print('\n=== 7. Mixed Jac/Python Methods ===')
calc = Calculator()
calc.add_jac(10)
calc.add_python(20)
calc.add_jac(30)
stats = calc.get_stats()
avg_val = round(stats['avg'], 1)
print(f'Stats: avg={avg_val}, total={stats['total']}, count={stats['count']}')
print('\n✓ Inline Python complete!')
Jac Grammar Snippet
py_code_block: PYNLINE

Description

Inline Python in Jac

Jac supports embedding Python code blocks using the ::py:: delimiter syntax, enabling seamless integration with Python libraries and existing Python code.

Basic Python Block

Lines 4-10 demonstrate the simplest Python embedding. Python code is delimited by ::py:: markers at the beginning and end. Functions defined in Python blocks are directly callable from Jac code (lines 14-15).

Python Block Syntax

The ::py:: delimiter: - Opens a Python code block (line 4) - Closes the block with a matching ::py:: (line 10) - Contains valid Python code using Python syntax (indentation-based, no semicolons) - Can appear at module level or within archetype bodies

Python Methods in Objects

Lines 19-41 show Python methods within a Jac object. Python methods: - Use self to access Jac member variables (line 25: self.data) - Follow Python syntax and conventions - Can use Python built-ins and comprehensions - Are called like normal methods from Jac (lines 46, 49)

Data Interoperability

Lines 55-76 demonstrate Jac-Python data exchange. Data structures seamlessly pass between Jac and Python: - Jac lists work as Python lists (line 70) - Jac dicts work as Python dicts (line 74) - Return values integrate naturally into Jac code

Python Libraries Integration

Lines 79-106 show importing and using Python libraries. Standard Python libraries and third-party packages are fully available within ::py:: blocks. Import statements work normally (lines 80-81).

Python in Nodes

Lines 109-135 demonstrate Python methods in node definitions. Python methods in nodes can: - Access node state via self.value (line 117) - Modify node attributes (line 118) - Import libraries locally (line 116) - Return computed results (line 124)

Mixed Jac and Python Methods

Lines 166-205 show combining Jac and Python methods in the same object. Objects can have both Jac methods (lines 170-173) and Python methods (lines 177-191). Both types: - Access the same self.history attribute - Are called the same way from Jac code (lines 198-200) - Can modify shared state

Usage Contexts for Python Blocks

Context Example Line Purpose
Global scope 4-10 Define utility functions
Object body 22-40 Add Python methods to objects
Node body 113-125 Add Python methods to nodes
Mixed with Jac 169-191 Combine Jac and Python methods

When to Use Inline Python

Use Python for: - Computationally intensive operations - Leveraging existing Python libraries - Complex numerical/statistical operations - String processing with Python's rich ecosystem - Integration with Python-only APIs

Use Jac for: - Object-Spatial Programming features - Graph operations and traversal - Walker-node interactions - OSP-specific patterns

Data Flow Diagram

flowchart LR
    JacCode[Jac Code] -->|Call| PyFunc[Python Function]
    PyFunc -->|Access| JacData[Jac Data Structures]
    JacData -->|Lists, Dicts| PyProcess[Python Processing]
    PyProcess -->|Return| PyResult[Python Result]
    PyResult -->|Use| JacCode

    PyBlock[::py:: Block] -->|Import| PyLibs[Python Libraries]
    PyLibs -->|Use| PyFunc

Method Access Patterns

flowchart TD
    Object[Jac Object/Node] --> JacMethod["Jac Methods (def)"]
    Object --> PyMethods["Python Methods (::py::)"]
    JacMethod -->|Access| State[Shared State<br/>self.attributes]
    PyMethods -->|Access| State
    State -->|Available| Both[Both Method Types]

Best Practices

Organize imports at the top of Python blocks:

Use Python for library integration:

Mix Python and Jac strategically:

Common Patterns

State modification from Python:

Using Python libraries:

Python comprehensions:

Key Points

  1. ::py:: delimiters mark Python code blocks
  2. Python blocks can appear at global scope or in archetypes
  3. Python code follows Python syntax (indentation, no semicolons)
  4. Jac data structures (lists, dicts) work seamlessly in Python
  5. Python methods access Jac state via self
  6. Standard and third-party Python libraries are fully available
  7. Objects can mix Jac and Python methods
  8. Return values from Python integrate naturally into Jac
  9. Use Python for computation, Jac for OSP features