* refactor: remove main window dependency from PythonService and utilize WindowService for window management * format code
100 lines
2.7 KiB
TypeScript
100 lines
2.7 KiB
TypeScript
import { randomUUID } from 'node:crypto'
|
|
|
|
import { ipcMain } from 'electron'
|
|
|
|
import { windowService } from './WindowService'
|
|
|
|
interface PythonExecutionRequest {
|
|
id: string
|
|
script: string
|
|
context: Record<string, any>
|
|
timeout: number
|
|
}
|
|
|
|
interface PythonExecutionResponse {
|
|
id: string
|
|
result?: string
|
|
error?: string
|
|
}
|
|
|
|
/**
|
|
* Service for executing Python code by communicating with the PyodideService in the renderer process
|
|
*/
|
|
export class PythonService {
|
|
private static instance: PythonService | null = null
|
|
private pendingRequests = new Map<string, { resolve: (value: string) => void; reject: (error: Error) => void }>()
|
|
|
|
private constructor() {
|
|
// Private constructor for singleton pattern
|
|
this.setupIpcHandlers()
|
|
}
|
|
|
|
public static getInstance(): PythonService {
|
|
if (!PythonService.instance) {
|
|
PythonService.instance = new PythonService()
|
|
}
|
|
return PythonService.instance
|
|
}
|
|
|
|
private setupIpcHandlers() {
|
|
// Handle responses from renderer
|
|
ipcMain.on('python-execution-response', (_, response: PythonExecutionResponse) => {
|
|
const request = this.pendingRequests.get(response.id)
|
|
if (request) {
|
|
this.pendingRequests.delete(response.id)
|
|
if (response.error) {
|
|
request.reject(new Error(response.error))
|
|
} else {
|
|
request.resolve(response.result || '')
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Execute Python code by sending request to renderer PyodideService
|
|
*/
|
|
public async executeScript(
|
|
script: string,
|
|
context: Record<string, any> = {},
|
|
timeout: number = 60000
|
|
): Promise<string> {
|
|
if (!windowService.getMainWindow()) {
|
|
throw new Error('Main window not found')
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const requestId = randomUUID()
|
|
|
|
// Store the request
|
|
this.pendingRequests.set(requestId, { resolve, reject })
|
|
|
|
// Set up timeout
|
|
const timeoutId = setTimeout(() => {
|
|
this.pendingRequests.delete(requestId)
|
|
reject(new Error('Python execution timed out'))
|
|
}, timeout + 5000) // Add 5s buffer for IPC communication
|
|
|
|
// Update resolve/reject to clear timeout
|
|
const originalResolve = resolve
|
|
const originalReject = reject
|
|
this.pendingRequests.set(requestId, {
|
|
resolve: (value: string) => {
|
|
clearTimeout(timeoutId)
|
|
originalResolve(value)
|
|
},
|
|
reject: (error: Error) => {
|
|
clearTimeout(timeoutId)
|
|
originalReject(error)
|
|
}
|
|
})
|
|
|
|
// Send request to renderer
|
|
const request: PythonExecutionRequest = { id: requestId, script, context, timeout }
|
|
windowService.getMainWindow()?.webContents.send('python-execution-request', request)
|
|
})
|
|
}
|
|
}
|
|
|
|
export const pythonService = PythonService.getInstance()
|