or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-management.mdengine-language-management.mdindex.mdjava-python-interop.mdsecurity-access-control.mdsource-management.mdvalue-operations.md

java-python-interop.mddocs/

0

# Java-Python Interoperability

1

2

Java-Python interoperability provides seamless bidirectional communication between Java and Python through the proxy system. This enables Java objects to be used naturally within Python code and Python objects to be manipulated from Java with full type safety and comprehensive data type support.

3

4

## Capabilities

5

6

### Object Proxies

7

8

Create Java objects that behave like Python objects with member access capabilities.

9

10

```java { .api }

11

/**

12

* Base interface for all proxy implementations

13

*/

14

public interface Proxy {

15

}

16

17

/**

18

* Proxy interface for objects with named members (like Python objects/dicts)

19

*/

20

public interface ProxyObject extends Proxy {

21

/**

22

* Gets the value of a member by key

23

* @param key the member name

24

* @return the member value

25

*/

26

Object getMember(String key);

27

28

/**

29

* Gets all member keys as an array or iterable

30

* @return object representing the available keys

31

*/

32

Object getMemberKeys();

33

34

/**

35

* Checks if a member with the given key exists

36

* @param key the member name to check

37

* @return true if the member exists

38

*/

39

boolean hasMember(String key);

40

41

/**

42

* Sets the value of a member

43

* @param key the member name

44

* @param value the value to set

45

*/

46

void putMember(String key, Value value);

47

48

/**

49

* Removes a member

50

* @param key the member name to remove

51

* @return true if the member was removed

52

*/

53

boolean removeMember(String key);

54

55

/**

56

* Creates a ProxyObject from a Java Map

57

* @param values the map to wrap

58

* @return ProxyObject representing the map

59

*/

60

static ProxyObject fromMap(Map<String, Object> values);

61

}

62

```

63

64

**Usage Examples:**

65

66

```java

67

// Create a Java object that acts like a Python dict

68

Map<String, Object> dataMap = new HashMap<>();

69

dataMap.put("name", "Alice");

70

dataMap.put("age", 30);

71

dataMap.put("city", "New York");

72

73

ProxyObject dictProxy = ProxyObject.fromMap(dataMap);

74

context.getPolyglotBindings().putMember("person", dictProxy);

75

76

// Access from Python like a normal dict

77

Value result = context.eval("python", """

78

print(person['name']) # Alice

79

print(person['age']) # 30

80

person['email'] = 'alice@example.com'

81

print(len(person)) # 4

82

list(person.keys()) # ['name', 'age', 'city', 'email']

83

""");

84

85

// Custom ProxyObject implementation

86

public class ConfigObject implements ProxyObject {

87

private final Map<String, Object> config = new HashMap<>();

88

89

@Override

90

public Object getMember(String key) {

91

return config.get(key);

92

}

93

94

@Override

95

public Object getMemberKeys() {

96

return config.keySet().toArray(new String[0]);

97

}

98

99

@Override

100

public boolean hasMember(String key) {

101

return config.containsKey(key);

102

}

103

104

@Override

105

public void putMember(String key, Value value) {

106

config.put(key, value.isString() ? value.asString() : value.as(Object.class));

107

}

108

109

@Override

110

public boolean removeMember(String key) {

111

return config.remove(key) != null;

112

}

113

114

// Custom method for validation

115

public void validate() {

116

System.out.println("Config validated with " + config.size() + " entries");

117

}

118

}

119

120

ConfigObject config = new ConfigObject();

121

context.getPolyglotBindings().putMember("config", config);

122

123

context.eval("python", """

124

config['database_url'] = 'postgresql://localhost:5432/db'

125

config['max_connections'] = 100

126

config['debug'] = True

127

128

# Access like Python dict

129

for key in config:

130

print(f"{key}: {config[key]}")

131

""");

132

133

config.validate(); // Call Java method

134

```

135

136

### Array Proxies

137

138

Enable Java objects to behave like Python lists and sequences.

139

140

```java { .api }

141

/**

142

* Proxy interface for array-like objects (like Python lists/tuples)

143

*/

144

public interface ProxyArray extends Proxy {

145

/**

146

* Gets an element at the specified index

147

* @param index the element index

148

* @return the element at the index

149

*/

150

Object get(long index);

151

152

/**

153

* Sets an element at the specified index

154

* @param index the element index

155

* @param value the value to set

156

*/

157

void set(long index, Value value);

158

159

/**

160

* Removes an element at the specified index

161

* @param index the element index to remove

162

* @return true if the element was removed

163

*/

164

boolean remove(long index);

165

166

/**

167

* Gets the size of the array

168

* @return number of elements

169

*/

170

long getSize();

171

172

/**

173

* Creates a ProxyArray from a Java array

174

* @param values the array to wrap

175

* @return ProxyArray representing the array

176

*/

177

static ProxyArray fromArray(Object... values);

178

179

/**

180

* Creates a ProxyArray from a Java List

181

* @param values the list to wrap

182

* @return ProxyArray representing the list

183

*/

184

static ProxyArray fromList(List<Object> values);

185

}

186

```

187

188

**Usage Examples:**

189

190

```java

191

// Create array proxy from Java array

192

ProxyArray arrayProxy = ProxyArray.fromArray("apple", "banana", "cherry", "date");

193

context.getPolyglotBindings().putMember("fruits", arrayProxy);

194

195

// Access from Python like a normal list

196

context.eval("python", """

197

print(fruits[0]) # apple

198

print(len(fruits)) # 4

199

fruits[1] = 'blueberry' # Modify element

200

201

# Iterate like Python list

202

for i, fruit in enumerate(fruits):

203

print(f"{i}: {fruit}")

204

205

# List comprehension

206

upper_fruits = [fruit.upper() for fruit in fruits]

207

print(upper_fruits)

208

""");

209

210

// Dynamic list proxy

211

public class DynamicList implements ProxyArray {

212

private final List<Object> items = new ArrayList<>();

213

214

@Override

215

public Object get(long index) {

216

if (index < 0 || index >= items.size()) {

217

return null; // Python None

218

}

219

return items.get((int) index);

220

}

221

222

@Override

223

public void set(long index, Value value) {

224

while (items.size() <= index) {

225

items.add(null); // Extend list if needed

226

}

227

items.set((int) index, value.as(Object.class));

228

}

229

230

@Override

231

public boolean remove(long index) {

232

if (index >= 0 && index < items.size()) {

233

items.remove((int) index);

234

return true;

235

}

236

return false;

237

}

238

239

@Override

240

public long getSize() {

241

return items.size();

242

}

243

244

// Java-specific methods

245

public void clear() {

246

items.clear();

247

}

248

249

public List<Object> getJavaList() {

250

return new ArrayList<>(items);

251

}

252

}

253

254

DynamicList dynamicList = new DynamicList();

255

context.getPolyglotBindings().putMember("dynamic_list", dynamicList);

256

257

context.eval("python", """

258

# Use like Python list

259

dynamic_list[0] = "first"

260

dynamic_list[1] = "second"

261

dynamic_list[5] = "sixth" # Auto-extends with None values

262

263

print(f"Length: {len(dynamic_list)}") # 6

264

print(f"Item 2: {dynamic_list[2]}") # None

265

print(f"Item 5: {dynamic_list[5]}") # sixth

266

""");

267

268

// Get Java list back

269

List<Object> javaList = dynamicList.getJavaList();

270

System.out.println("Java list: " + javaList);

271

```

272

273

### Executable Proxies

274

275

Make Java objects callable from Python like functions.

276

277

```java { .api }

278

/**

279

* Proxy interface for executable/callable objects (like Python functions)

280

*/

281

public interface ProxyExecutable extends Proxy {

282

/**

283

* Executes this object with the given arguments

284

* @param arguments the arguments to pass to execution

285

* @return the execution result

286

*/

287

Object execute(Value... arguments);

288

}

289

290

/**

291

* Proxy interface for instantiable objects (like Python classes/constructors)

292

*/

293

public interface ProxyInstantiable extends Proxy {

294

/**

295

* Creates a new instance using this object as constructor

296

* @param arguments the constructor arguments

297

* @return the new instance

298

*/

299

Object newInstance(Value... arguments);

300

}

301

```

302

303

**Usage Examples:**

304

305

```java

306

// Simple function proxy

307

public class MathFunctions implements ProxyExecutable {

308

@Override

309

public Object execute(Value... arguments) {

310

if (arguments.length != 2) {

311

throw new IllegalArgumentException("Expected 2 arguments");

312

}

313

314

double a = arguments[0].asDouble();

315

double b = arguments[1].asDouble();

316

return a + b; // Simple addition

317

}

318

}

319

320

context.getPolyglotBindings().putMember("add", new MathFunctions());

321

322

Value result = context.eval("python", """

323

result = add(5.5, 3.2) # Call like Python function

324

print(f"Result: {result}") # 8.7

325

result

326

""");

327

328

// Multi-function proxy with operation selection

329

public class Calculator implements ProxyExecutable {

330

@Override

331

public Object execute(Value... arguments) {

332

if (arguments.length < 3) {

333

throw new IllegalArgumentException("Expected: operation, a, b");

334

}

335

336

String operation = arguments[0].asString();

337

double a = arguments[1].asDouble();

338

double b = arguments[2].asDouble();

339

340

return switch (operation) {

341

case "add" -> a + b;

342

case "subtract" -> a - b;

343

case "multiply" -> a * b;

344

case "divide" -> b != 0 ? a / b : Double.NaN;

345

default -> throw new IllegalArgumentException("Unknown operation: " + operation);

346

};

347

}

348

}

349

350

context.getPolyglotBindings().putMember("calc", new Calculator());

351

352

context.eval("python", """

353

print(calc("add", 10, 5)) # 15.0

354

print(calc("multiply", 3, 4)) # 12.0

355

print(calc("divide", 15, 3)) # 5.0

356

""");

357

358

// Class-like instantiable proxy

359

public class PersonFactory implements ProxyInstantiable {

360

@Override

361

public Object newInstance(Value... arguments) {

362

String name = arguments.length > 0 ? arguments[0].asString() : "Unknown";

363

int age = arguments.length > 1 ? arguments[1].asInt() : 0;

364

365

Map<String, Object> person = new HashMap<>();

366

person.put("name", name);

367

person.put("age", age);

368

person.put("greet", (ProxyExecutable) args -> "Hello, I'm " + name);

369

370

return ProxyObject.fromMap(person);

371

}

372

}

373

374

context.getPolyglotBindings().putMember("Person", new PersonFactory());

375

376

context.eval("python", """

377

# Create instances like Python classes

378

alice = Person("Alice", 30)

379

bob = Person("Bob", 25)

380

381

print(alice['name']) # Alice

382

print(alice['age']) # 30

383

greeting = alice['greet']() # Call method

384

print(greeting) # Hello, I'm Alice

385

""");

386

```

387

388

### Iterator Proxies

389

390

Support Python iteration protocols with Java objects.

391

392

```java { .api }

393

/**

394

* Proxy interface for iterable objects (like Python iterables)

395

*/

396

public interface ProxyIterable extends Proxy {

397

/**

398

* Gets an iterator for this iterable

399

* @return the iterator object

400

*/

401

Object getIterator();

402

}

403

404

/**

405

* Proxy interface for iterator objects (like Python iterators)

406

*/

407

public interface ProxyIterator extends Proxy {

408

/**

409

* Checks if there are more elements

410

* @return true if more elements are available

411

*/

412

boolean hasNext();

413

414

/**

415

* Gets the next element from the iterator

416

* @return the next element

417

* @throws NoSuchElementException if no more elements

418

*/

419

Object getNext();

420

}

421

```

422

423

**Usage Examples:**

424

425

```java

426

// Custom iterable that generates Fibonacci numbers

427

public class FibonacciSequence implements ProxyIterable {

428

private final int maxCount;

429

430

public FibonacciSequence(int maxCount) {

431

this.maxCount = maxCount;

432

}

433

434

@Override

435

public Object getIterator() {

436

return new FibonacciIterator(maxCount);

437

}

438

439

private static class FibonacciIterator implements ProxyIterator {

440

private final int maxCount;

441

private int count = 0;

442

private long a = 0, b = 1;

443

444

FibonacciIterator(int maxCount) {

445

this.maxCount = maxCount;

446

}

447

448

@Override

449

public boolean hasNext() {

450

return count < maxCount;

451

}

452

453

@Override

454

public Object getNext() {

455

if (!hasNext()) {

456

throw new NoSuchElementException();

457

}

458

459

long result = a;

460

long next = a + b;

461

a = b;

462

b = next;

463

count++;

464

return result;

465

}

466

}

467

}

468

469

context.getPolyglotBindings().putMember("fibonacci", new FibonacciSequence(10));

470

471

context.eval("python", """

472

# Use in for loop like Python iterable

473

for i, fib in enumerate(fibonacci):

474

print(f"Fibonacci {i}: {fib}")

475

476

# Convert to list

477

fib_list = list(fibonacci) # Note: creates new iterator

478

print(f"First 10 Fibonacci numbers: {fib_list}")

479

480

# Use in comprehensions

481

even_fibs = [f for f in fibonacci if f % 2 == 0]

482

print(f"Even Fibonacci numbers: {even_fibs}")

483

""");

484

485

// Range-like iterable

486

public class JavaRange implements ProxyIterable {

487

private final int start, stop, step;

488

489

public JavaRange(int stop) {

490

this(0, stop, 1);

491

}

492

493

public JavaRange(int start, int stop, int step) {

494

this.start = start;

495

this.stop = stop;

496

this.step = step;

497

}

498

499

@Override

500

public Object getIterator() {

501

return new RangeIterator();

502

}

503

504

private class RangeIterator implements ProxyIterator {

505

private int current = start;

506

507

@Override

508

public boolean hasNext() {

509

return step > 0 ? current < stop : current > stop;

510

}

511

512

@Override

513

public Object getNext() {

514

if (!hasNext()) {

515

throw new NoSuchElementException();

516

}

517

int result = current;

518

current += step;

519

return result;

520

}

521

}

522

}

523

524

// Create range function

525

context.getPolyglotBindings().putMember("jrange", (ProxyExecutable) args -> {

526

if (args.length == 1) {

527

return new JavaRange(args[0].asInt());

528

} else if (args.length == 2) {

529

return new JavaRange(args[0].asInt(), args[1].asInt(), 1);

530

} else if (args.length == 3) {

531

return new JavaRange(args[0].asInt(), args[1].asInt(), args[2].asInt());

532

}

533

throw new IllegalArgumentException("Expected 1-3 arguments");

534

});

535

536

context.eval("python", """

537

# Use like Python range

538

for i in jrange(5):

539

print(i) # 0, 1, 2, 3, 4

540

541

for i in jrange(2, 8, 2):

542

print(i) # 2, 4, 6

543

544

# Convert to list

545

numbers = list(jrange(10, 20))

546

print(numbers) # [10, 11, 12, ..., 19]

547

""");

548

```

549

550

### Hash Map Proxies

551

552

Support Python dictionary-like operations with Java objects.

553

554

```java { .api }

555

/**

556

* Proxy interface for hash map objects (like Python dicts with arbitrary keys)

557

*/

558

public interface ProxyHashMap extends Proxy {

559

/**

560

* Gets the size of the hash map

561

* @return number of key-value pairs

562

*/

563

long getHashSize();

564

565

/**

566

* Checks if the hash map contains the specified key

567

* @param key the key to check

568

* @return true if the key exists

569

*/

570

boolean hasHashEntry(Value key);

571

572

/**

573

* Gets the value for the specified key

574

* @param key the key to look up

575

* @return the value, or null if key doesn't exist

576

*/

577

Value getHashValue(Value key);

578

579

/**

580

* Sets the value for the specified key

581

* @param key the key

582

* @param value the value to set

583

*/

584

void putHashEntry(Value key, Value value);

585

586

/**

587

* Removes the entry for the specified key

588

* @param key the key to remove

589

* @return true if the entry was removed

590

*/

591

boolean removeHashEntry(Value key);

592

593

/**

594

* Gets an iterator for the hash entries

595

* @return iterator over key-value pairs

596

*/

597

Value getHashEntriesIterator();

598

}

599

```

600

601

**Usage Examples:**

602

603

```java

604

// Custom hash map implementation

605

public class JavaHashMap implements ProxyHashMap {

606

private final Map<Object, Object> map = new HashMap<>();

607

608

@Override

609

public long getHashSize() {

610

return map.size();

611

}

612

613

@Override

614

public boolean hasHashEntry(Value key) {

615

return map.containsKey(keyToJava(key));

616

}

617

618

@Override

619

public Value getHashValue(Value key) {

620

Object value = map.get(keyToJava(key));

621

return value != null ? context.asValue(value) : null;

622

}

623

624

@Override

625

public void putHashEntry(Value key, Value value) {

626

map.put(keyToJava(key), value.as(Object.class));

627

}

628

629

@Override

630

public boolean removeHashEntry(Value key) {

631

return map.remove(keyToJava(key)) != null;

632

}

633

634

@Override

635

public Value getHashEntriesIterator() {

636

List<ProxyArray> entries = new ArrayList<>();

637

for (Map.Entry<Object, Object> entry : map.entrySet()) {

638

entries.add(ProxyArray.fromArray(entry.getKey(), entry.getValue()));

639

}

640

return context.asValue(ProxyArray.fromList(entries));

641

}

642

643

private Object keyToJava(Value key) {

644

if (key.isString()) return key.asString();

645

if (key.isNumber()) return key.asLong();

646

return key.as(Object.class);

647

}

648

}

649

650

context.getPolyglotBindings().putMember("java_dict", new JavaHashMap());

651

652

context.eval("python", """

653

# Use like Python dict with any key types

654

java_dict["string_key"] = "string_value"

655

java_dict[42] = "number_key_value"

656

java_dict[(1, 2)] = "tuple_key_value"

657

658

print(len(java_dict)) # 3

659

print(java_dict["string_key"]) # string_value

660

print(java_dict[42]) # number_key_value

661

662

# Check existence

663

print("string_key" in java_dict) # True

664

print("missing" in java_dict) # False

665

666

# Iterate over items

667

for key, value in java_dict.items():

668

print(f"{key}: {value}")

669

""");

670

```

671

672

### Temporal Proxies

673

674

Support Python date/time objects with Java temporal types.

675

676

```java { .api }

677

/**

678

* Proxy interface for date objects

679

*/

680

public interface ProxyDate extends Proxy {

681

/**

682

* Converts to Java LocalDate

683

* @return LocalDate representation

684

*/

685

LocalDate asDate();

686

}

687

688

/**

689

* Proxy interface for time objects

690

*/

691

public interface ProxyTime extends Proxy {

692

/**

693

* Converts to Java LocalTime

694

* @return LocalTime representation

695

*/

696

LocalTime asTime();

697

}

698

699

/**

700

* Proxy interface for timezone objects

701

*/

702

public interface ProxyTimeZone extends Proxy {

703

/**

704

* Converts to Java ZoneId

705

* @return ZoneId representation

706

*/

707

ZoneId asTimeZone();

708

}

709

710

/**

711

* Proxy interface for duration objects

712

*/

713

public interface ProxyDuration extends Proxy {

714

/**

715

* Converts to Java Duration

716

* @return Duration representation

717

*/

718

Duration asDuration();

719

}

720

721

/**

722

* Proxy interface for instant/timestamp objects

723

*/

724

public interface ProxyInstant extends Proxy {

725

/**

726

* Converts to Java Instant

727

* @return Instant representation

728

*/

729

Instant asInstant();

730

}

731

```

732

733

**Usage Examples:**

734

735

```java

736

// Custom date proxy

737

public class JavaDate implements ProxyDate {

738

private final LocalDate date;

739

740

public JavaDate(LocalDate date) {

741

this.date = date;

742

}

743

744

@Override

745

public LocalDate asDate() {

746

return date;

747

}

748

749

// Additional methods accessible from Python

750

public int getYear() { return date.getYear(); }

751

public int getMonth() { return date.getMonthValue(); }

752

public int getDay() { return date.getDayOfMonth(); }

753

public String format(String pattern) {

754

return date.format(DateTimeFormatter.ofPattern(pattern));

755

}

756

}

757

758

// Date factory function

759

context.getPolyglotBindings().putMember("create_date", (ProxyExecutable) args -> {

760

int year = args[0].asInt();

761

int month = args[1].asInt();

762

int day = args[2].asInt();

763

return new JavaDate(LocalDate.of(year, month, day));

764

});

765

766

context.eval("python", """

767

# Create and use date objects

768

date = create_date(2023, 12, 25)

769

print(f"Year: {date.getYear()}") # 2023

770

print(f"Month: {date.getMonth()}") # 12

771

print(f"Day: {date.getDay()}") # 25

772

print(date.format("yyyy-MM-dd")) # 2023-12-25

773

""");

774

775

// Duration proxy for time calculations

776

public class JavaDuration implements ProxyDuration {

777

private final Duration duration;

778

779

public JavaDuration(Duration duration) {

780

this.duration = duration;

781

}

782

783

@Override

784

public Duration asDuration() {

785

return duration;

786

}

787

788

public long getSeconds() { return duration.getSeconds(); }

789

public long getMillis() { return duration.toMillis(); }

790

public String toString() { return duration.toString(); }

791

}

792

793

context.getPolyglotBindings().putMember("duration_from_seconds", (ProxyExecutable) args -> {

794

long seconds = args[0].asLong();

795

return new JavaDuration(Duration.ofSeconds(seconds));

796

});

797

798

context.eval("python", """

799

# Create and use duration objects

800

duration = duration_from_seconds(3661) # 1 hour, 1 minute, 1 second

801

print(f"Seconds: {duration.getSeconds()}") # 3661

802

print(f"Millis: {duration.getMillis()}") # 3661000

803

print(f"String: {duration.toString()}") # PT1H1M1S

804

""");

805

```

806

807

## Types

808

809

```java { .api }

810

/**

811

* Exception thrown when proxy operations fail

812

*/

813

public final class ProxyException extends RuntimeException {

814

public ProxyException(String message);

815

public ProxyException(String message, Throwable cause);

816

}

817

818

/**

819

* Interface for native pointer objects

820

*/

821

public interface ProxyNativeObject extends Proxy {

822

/**

823

* Gets the native pointer address

824

* @return native pointer as long

825

*/

826

long asPointer();

827

}

828

829

/**

830

* Utility class for creating common proxy objects

831

*/

832

public final class ProxyUtils {

833

/**

834

* Creates a simple executable proxy from a lambda

835

* @param function the function to execute

836

* @return ProxyExecutable implementation

837

*/

838

public static ProxyExecutable executable(Function<Value[], Object> function);

839

840

/**

841

* Creates a proxy object from method references

842

* @param object the target object

843

* @return ProxyObject with methods exposed

844

*/

845

public static ProxyObject fromObject(Object object);

846

}

847

```