Skip to main content

Pyodide Integration Guide

Pyodide allows you to run Python code directly in the browser. This guide covers the specifics of integrating Python code with JavaScript in NetSuite Suitelets.

Loading Pyodide

Basic Setup

<script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script>

<script>
async function initPyodide() {
// Initialize Pyodide
let pyodide = await loadPyodide();

// Load required packages
await pyodide.loadPackage("micropip");
await pyodide.loadPackage('tzdata');

// Install packages not in the base distribution
const micropip = pyodide.pyimport("micropip");
await micropip.install('pandas');
await micropip.install('xlsxwriter');

return pyodide;
}

let pyodide = await initPyodide();
</script>

Passing Data: JavaScript to Python

// JavaScript side
window.array_aging = JSON.stringify(agingData).replaceAll(null, 'null');
window.asofdate = '15/12/2024';
window.companyName = 'PT. EXAMPLE COMPANY';
# Python side (inside Pyodide)
from js import array_aging, asofdate, companyName
import json

aging_data = json.loads(array_aging)

Method 2: Pyodide Globals

// JavaScript side
pyodide.globals.set('data', JSON.stringify(agingData));
pyodide.globals.set('config', JSON.stringify({
date: '15/12/2024',
company: 'PT. EXAMPLE'
}));
# Python side
import json
aging_data = json.loads(data)
config = json.loads(config)

Handling Null Values

Important

JavaScript null values can cause issues in Python. Always sanitize data.

JavaScript Sanitization

// Replace null with 'null' string before passing to Python
window.array_aging = JSON.stringify(agingData).replaceAll(null, 'null');

Python Handling

import pandas as pd

# After creating DataFrame, fill nulls with appropriate defaults
data_df = pd.DataFrame(aging_data)
data_df = data_df.fillna(0) # or fillna('') for strings

Returning Files to JavaScript

Creating Excel File

import io
import base64
import pandas as pd

# Create Excel in memory
bio = io.BytesIO()

with pd.ExcelWriter(bio, engine='xlsxwriter') as writer:
df.to_excel(writer, sheet_name='Report', index=False)
# Add formatting...

# Encode as base64
bio.seek(0)
excel_base64 = base64.b64encode(bio.getvalue()).decode()

JavaScript Download Handler

// Convert base64 to binary
function base64ToArrayBuffer(base64) {
const binaryString = window.atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}

// Trigger download
function saveByteArray(filename, byteArray) {
const blob = new Blob([byteArray], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}

// Get result from Python and download
const excelBase64 = pyodide.runPython(`excel_base64`);
const arrayBuffer = base64ToArrayBuffer(excelBase64);
saveByteArray('report.xlsx', arrayBuffer);

Console Logging from Python

from js import console

# Log messages to browser console
console.log("Starting Python processing...")
console.log(f"Records loaded: {len(data)}")

# Log objects
console.log(str(df.head()))

# Warnings and errors
console.warn("This is a warning")
console.error("This is an error")

Date Handling

Date Formatting

from datetime import datetime

# Parse date string
asof_date = datetime.strptime(asofdate, '%d/%m/%Y')

# Format for display
formatted_date = asof_date.strftime('%d %B %Y') # "15 December 2024"

Timezone Handling

# Note: Pyodide requires tzdata package for timezone support
from zoneinfo import ZoneInfo

# Convert to specific timezone
jakarta_tz = ZoneInfo('Asia/Jakarta')
local_date = asof_date.replace(tzinfo=jakarta_tz)

Performance Optimization

Process Large Data in Chunks

import pandas as pd

def process_in_chunks(data, chunk_size=1000):
results = []
for i in range(0, len(data), chunk_size):
chunk = data[i:i + chunk_size]
result = process_chunk(chunk)
results.append(result)
return pd.concat(results)

Use Efficient Data Types

# Reduce memory usage
df = df.astype({
'amount_current': 'float32',
'amount_h30': 'float32',
'count': 'int32',
'currency': 'category'
})

Cleanup After Processing

import gc

# Clear large objects when done
del large_df, temp_data
gc.collect()

Error Handling

Try-Catch in Python

from js import console

try:
result = process_data(aging_data)
console.log("Processing successful!")
except Exception as e:
console.error(f"Error processing data: {str(e)}")
raise

Try-Catch in JavaScript

try {
const result = await pyodide.runPythonAsync(`
# Python code here
process_data(data)
`);
} catch (error) {
console.error('Python execution failed:', error);
alert('Error generating report: ' + error.message);
}

Complete Example

<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script>
</head>
<body>
<button onclick="generateReport()">Generate Report</button>

<script>
let pyodide = null;

async function initPyodide() {
pyodide = await loadPyodide();
await pyodide.loadPackage("micropip");
const micropip = pyodide.pyimport("micropip");
await micropip.install('pandas');
await micropip.install('xlsxwriter');
console.log("Pyodide initialized!");
}

async function generateReport() {
if (!pyodide) {
await initPyodide();
}

// Set data for Python
window.reportData = JSON.stringify(agingData).replaceAll(null, 'null');

try {
const result = await pyodide.runPythonAsync(`
import json
import pandas as pd
import io
import base64
from js import reportData, console

console.log("Processing report...")

# Load data
data = json.loads(reportData)
df = pd.DataFrame(data).fillna(0)

# Create Excel
bio = io.BytesIO()
with pd.ExcelWriter(bio, engine='xlsxwriter') as writer:
df.to_excel(writer, sheet_name='Report', index=False)

bio.seek(0)
base64.b64encode(bio.getvalue()).decode()
`);

// Download the file
const arrayBuffer = base64ToArrayBuffer(result);
saveByteArray('report.xlsx', arrayBuffer);

} catch (error) {
console.error('Error:', error);
alert('Error generating report');
}
}

function base64ToArrayBuffer(base64) {
const binaryString = window.atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}

function saveByteArray(filename, byteArray) {
const blob = new Blob([byteArray], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
</script>
</body>
</html>