Han pasado aproximadamente 4 meses desde el lanzamiento de Pocket Editor para Android . Una de las principales características que faltan en la aplicación es la posibilidad de ejecutar algún código sobre la marcha. Hay un montón de razones por las que la implementación de compiladores o intérpretes se ha retrasado tanto. Una de las principales razones se debe al problemático Storage Access Framework implementado desde Android 11 que hace que sea bastante complejo trabajar con archivos desde el dispositivo del usuario, además de las expectativas del usuario. Por ejemplo, algunos usuarios pueden esperar que ejecutar el código Python directamente en el dispositivo les otorgue automáticamente los derechos para manipular los archivos del dispositivo a su voluntad, algo que es bastante difícil de implementar, al menos por el conocimiento que poseo en este momento.
Entonces, aunque bajaré las expectativas de muchos usuarios, decidí continuar con una implementación muy útil de un intérprete de Python en Pocket Editor gracias a Chaquopy, el SDK de Python para Android . Chaquopy proporciona todo lo que se necesita para incluir componentes de Python en una aplicación de Android, incluyendo:
- Integración completa con el sistema de compilación Gradle estándar de Android Studio.
- API simples para llamar al código Python desde Java/Kotlin y viceversa.
- Una amplia gama de paquetes de Python de terceros, incluidos SciPy, OpenCV, TensorFlow y muchos más.
En este artículo, te explicaré cómo ejecutar fácilmente el código de Python y obtener su salida usando Chaquopy en tu aplicación de Android.
1. Instalación de Chaquopy
En la mayoría de los casos, la instalación de Chaquopy es realmente fácil y sencilla, especialmente con proyectos limpios (recién creados). En mi caso, tuve algunos problemas con la instalación ya que Chaquopy es bastante estricto cuando se trata de la versión de Android Gradle y ya tenía una aplicación antigua que usaba una versión no tan reciente de Gradle. Recuerda, si tienes problemas al sincronizar tu gradle porque la versión de Chaquopy no se puede encontrar en el repositorio maven (pero la versión sí existe) o no se puede acceder al espacio de nombres de com.chaquo.python
, asegúrate de que coincida con la versión requerida de Android Gradle con la lista de versiones compatibles en la documentación oficial de Chaquopy aquí. Por ejemplo, en el caso de mi proyecto, pude tener éxito con la instalación utilizando las siguientes versiones de Gradle:
Y solicitando la instalación de Chaquopy 9.1.0:
classpath 'com.chaquo.python:gradle:9.1.0'
Una vez que sepas la versión de Gradle de tu proyecto y la versión de Chaquopy que puedes instalar, ahora puedes continuar con la instalación. Como se especifica en la documentación oficial, Chaquopy se distribuye como un complemento para el sistema de compilación basado en Gradle de Android. Se puede utilizar en cualquier aplicación que cumpla con los siguientes requisitos:
-
En el archivo de nivel superior
build.gradle
de tu proyecto , la versión del complemento Android Gradle (com.android.tools.build:gradle
) debe estar entre 3.6 y 7.0. Las versiones anteriores, desde la 2.2, son compatibles con las versiones anteriores de Chaquopy . -
minSdkVersion debe tener al menos 16. Las versiones anteriores de hasta 15 son compatibles con las versiones anteriores de Chaquopy .
Cumpliendo con este criterio, ahora necesitas registrar el repositorio maven de Chaquopy e incluirlo como una dependencia en su build.gradle
archivo de nivel superior :
buildscript {
repositories {
maven { url "https://chaquo.com/maven" }
}
dependencies {
classpath "com.chaquo.python:gradle:9.1.0"
}
}
Como mencioné anteriormente, utilicé la versión 9.1.0 de Chaquopy para que funcionara en mi proyecto, el tuyo podría funcionar con la última versión de Chaquopy (10.0.1 hasta la fecha de redacción de este artículo). Después de realizar este cambio en el archivo Gradle, aplique el plugin com.chaquo.python
debajo del plugin de com.android.application
. Si estás utilizando la sintaxis de aplicación:
apply plugin: 'com.android.application'
apply plugin: 'com.chaquo.python'
O si está utilizando la sintaxis del complemento:
plugins {
id 'com.android.application'
id 'com.chaquo.python'
}
Eso es todo lo que necesitas para la instalación por ahora, no sincronices su proyecto con Gradle Files todavía, ya que necesita configurar algunas cosas antes.
2. Configurar Chaquopy
Ahora, en tu aplicación, en el archivo build.gradle
debes configurar los ajustes básicos de Chaquopy. Estos ajustes son los siguientes:
apply plugin: 'com.android.application'
apply plugin: 'com.chaquo.python'
android {
...
defaultConfig {
applicationId "com.yourcompany.yourappid"
...
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
sourceSets{
main{
python.srcDir "src/main/python"
}
}
python {
buildPython "C:/Python38/python.exe"
}
}
}
- ndk (abiFilters): el intérprete de Python es un componente nativo, por lo que debe usar la configuración de abiFilters para especificar qué ABI desea que admita la aplicación. Los ABI disponibles actualmente son:
-
-
armeabi-v7a
, compatible con prácticamente todos los dispositivos Android. -
arm64-v8a
, compatible con los dispositivos Android más recientes. -
x86
, para el emulador de Android. -
x86_64
, para el emulador de Android.
-
- sourceSets: de forma predeterminada, Chaquopy utiliza el subdirectorio de Python predeterminado de cada conjunto de fuentes. Por ejemplo, el código de Python de la fuente principal debe almacenarse en el
app/src/main/python
directorio. - buildPython: algunas funciones requieren que Python esté instalado en la máquina de compilación. De forma predeterminada, Chaquopy intentará encontrar Python en la RUTA con el comando estándar para su sistema operativo, primero con una versión secundaria coincidente y luego con una versión principal coincidente. En mi caso usando Windows, proporcioné la ruta absoluta al ejecutable con la misma versión de Python de Chaquopy 9.1.0 (Python 3.8.6).
Finalmente, sincroniza tu proyecto con Gradle Files y espera a que finalice la compilación. Una vez que finalice, podrás usar Chaquopy en su proyecto.
3. Configurando el interprete
Como se mencionó al principio del artículo, la idea de la aplicación es ejecutar el código python proporcionado por el usuario y obtener el resultado. Por ejemplo, si el usuario proporciona el siguiente código de Python para imprimir la serie de Fibonacci:
def fibonacci_of(n):
if n in {0, 1}: # Base case
return n
return fibonacci_of(n - 1) + fibonacci_of(n - 2)
print([fibonacci_of(n) for n in range(10)])
La aplicación debería poder capturar la salida que sería en este caso:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Después de leer la documentación, no pude encontrar una manera de ejecutar el código directamente sin tanta molestia usando Chaquopy (no existe un método simple como executeCode o algo así). En cambio, seguí un enfoque curioso que ejecutaría el código a través de un script de Python que recibirá el código y lo ejecutará en otro subproceso. El siguiente script de Python nos permitirá ejecutar código arbitrario utilizando la función exec de python. Obtuve este script de esta implementación de Chaquopy en Flutter ( ver repositorio oficial aquí ):
# Source: https://github.com/jaydangar/chaquopy_flutter/blob/5fefecba7c918ce4c0e790a1a6494e70a701059c/example/android/app/src/main/python/script.py
# interpreter.py
import time,threading,ctypes,inspect
def _async_raise(tid, exctype):
tid = ctypes.c_long(tid)
if not inspect.isclass(exctype):
exctype = type(exctype)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
if res == 0:
raise ValueError("Invalid thread id")
elif res != 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
raise SystemError("Timeout Exception")
def stop_thread(thread):
_async_raise(thread.ident, SystemExit)
def text_thread_run(code):
try:
env={}
exec(code, env, env)
except Exception as e:
print(e)
# This is the code to run Text functions...
def mainTextCode(code):
global thread1
thread1 = threading.Thread(target=text_thread_run, args=(code,),daemon=True)
thread1.start()
# Define the
timeout = 15 # change timeout settings in seconds here...
thread1_start_time = time.time()
while thread1.is_alive():
if time.time() - thread1_start_time > timeout:
stop_thread(thread1)
raise TimeoutError
time.sleep(1)
Asegúrate de crear el archivo interpreter.py
dentro de tu directorio de Android app/src/main/python
y guarda el código anterior en él. Teniendo en cuenta la forma en que opera Chaquopy, necesitamos crear un PyObject que usará el interpreter.py
archivo como un módulo y ejecutaremos el código usando el método mainTextCode
del script de python. La siguiente lógica hará el truco en tu aplicación de Android para ejecutar el código de Python desde texto plano:
import com.chaquo.python.PyException;
import com.chaquo.python.PyObject;
import com.chaquo.python.Python;
import com.chaquo.python.android.AndroidPlatform;
// 1. Inicie la instancia de Python si aún no se está ejecutando.
if(!Python.isStarted()){
Python.start(new AndroidPlatform(getContext()));
}
// 2. Obtener la instancia de python
Python py = Python.getInstance();
// 3. Declarar algún código de Python que será interpretado
// En nuestro caso, la sucesión de fibonacci
String code = "def fibonacci_of(n):\n";
code += " if n in {0, 1}: # Base case\n";
code += " return n\n";
code += " return n\n";
code += "\n";
code += "print([fibonacci_of(n) for n in range(10)])\n";
// 4. Obtenga el flujo de entrada del sistema (disponible en Chaquopy)
PyObject sys = py.getModule("sys");
PyObject io = py.getModule("io");
// Obtener el módulo interpreter.py
PyObject console = py.getModule("interpreter");
// 5. Redirigir el flujo de salida del sistema al intérprete de Python
PyObject textOutputStream = io.callAttr("StringIO");
sys.put("stdout", textOutputStream);
// 6. Cree una variable de cadena que contendrá la salida estándar del intérprete de Python
String interpreterOutput = "";
// 7. Ejecutar el código Python
try {
console.callAttrThrows("mainTextCode", code);
interpreterOutput = textOutputStream.callAttr("getvalue").toString();
}catch (PyException e){
// Si hay un error, también puede obtener su salida
// p. ej. si escribes mal el codigo
// Missing parentheses in call to 'print'
// Did you mean print("text")?
// <string>, line 1
interpreterOutput = e.getMessage().toString();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
// Salidas en el caso del script fibonacci:
// "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]"
System.out.println(interpreterOutput);
Y así es como puedes ejecutar el código de Python de forma dinámica en su aplicación de Android usando Chaquopy.
Que te diviertas ❤️!
Conviertete en un programador más sociable