hileez / node-pyrunner

Nodejs call python native addon.
MIT License
36 stars 2 forks source link

How should I execute an python async method? #14

Closed WoJiaoFuXiaoYun closed 5 months ago

WoJiaoFuXiaoYun commented 5 months ago
async def sum(num1, num2):
return num1 + num2

The appModule.call method will not be able to retrieve the return value.

hileez commented 5 months ago

appModule.call() method call python functions are all asynchronous, and you don't need to special declaration in the python script. Asynchrony implemented by JavaScript engines using libuv thread pools, not python.

WoJiaoFuXiaoYun commented 5 months ago

appModule.call() method call python functions are all asynchronous, and you don't need to special declaration in the python script. Asynchrony implemented by JavaScript engines using libuv thread pools, not python.

This is my code, and I want to get word_boundaries

async def tts(content, output_file_url="test.mp3", voice="zh-CN-YunxiNeural"):
    communicate = edge_tts.Communicate(content, voice)
    word_boundaries = [] 
    with open(output_file_url, "wb") as file:
        async for chunk in communicate.stream():
            if chunk["type"] == "audio":
                file.write(chunk["data"])
            elif chunk["type"] == "WordBoundary":
                word_boundaries.append(chunk)
    return word_boundaries
WoJiaoFuXiaoYun commented 5 months ago

@supercoderlee

hileez commented 5 months ago

@WoJiaoFuXiaoYun Python 的 async 函数在 asyncio.run() 方法调用时就能直接返回结果,下面是简单的案例。

app.py

import asyncio

async def sum(a, b):
    return a + b

def speak():
    result = asyncio.run(sum(1, 2))
    return result    # return sum function result

main.js

const pyrunner = require('node-pyrunner')
pyrunner.config['module_search_paths'].push('./pyscript');
pyrunner.init();
let appModule = pyrunner.import('app');

// 同步调用speak函数
let data = appModule.callSync('speak', []);
console.log(data);       // 3

// 异步调用speak函数
appModule.call('speak', [], (data)=>{
  console.log(data);    // 3
}, (error)=>{
  console.log(error);
});

你可以把以上案例改为使用 edge_tts 模块。请注意,因为 Node-PyRunner 使用的是 python3.10 版本,所以请用 python3.10 创建 venv 虚拟环境,并在该环境下安装 edge_tts 模块,否则该模块无法使用。

app.py

import asyncio
import edge_tts
import json

async def tts(content, output_file_url="test.mp3", voice="zh-CN-YunxiNeural"):
    communicate = edge_tts.Communicate(content, voice)
    word_boundaries = [] 
    with open(output_file_url, "wb") as file:
        async for chunk in communicate.stream():
            if chunk["type"] == "audio":
                file.write(chunk["data"])
            elif chunk["type"] == "WordBoundary":
                word_boundaries.append(chunk)
    return word_boundaries

def speak():
    result = asyncio.run(tts('Hello Node-PyRunner'))
    return json.dumps(result)

除了 return 返回值,你还可以用回调的方式返回值,在 python 脚本中用 nodepyrunner.callJs 方法回调 JavaScript 函数。nodepyrunner 是 内置的Python模块,不需要安装且只能在 Node-PyRunner 中使用。

app.py

import nodepyrunner
import asyncio
import edge_tts
import json

async def tts(content, output_file_url="test.mp3", voice="zh-CN-YunxiNeural"):
    communicate = edge_tts.Communicate(content, voice)
    word_boundaries = [] 
    with open(output_file_url, "wb") as file:
        async for chunk in communicate.stream():
            if chunk["type"] == "audio":
                file.write(chunk["data"])
            elif chunk["type"] == "WordBoundary":
                word_boundaries.append(chunk)
    # 回调JS函数
    nodepyrunner.callJs(target='speakCallback', args=[json.dumps(word_boundaries)])

def speak():
    result = asyncio.run(tts('Hello Node-PyRunner'))

main.js

const pyrunner = require('node-pyrunner')
pyrunner.config['module_search_paths'].push('./pyscript');
pyrunner.init();
let appModule = pyrunner.import('app');

// 被Python回调的JS函数
speakCallback = function (data) {
    console.log(`speakCallback:${data}`);
}

// 同步调用speak函数
let data = appModule.callSync('speak', []);
console.log(data);

// 异步调用speak函数
appModule.call('speak', []);
WoJiaoFuXiaoYun commented 5 months ago

Thank you so much