Crear Extensiones para Chrome con Python
Creación de una extensión para Google Chrome paso a paso con Brython

Logo Python-Chrome

INTRODUCCIÓN

Vamos a crear una extensión para Google Chrome utilizando Python en lugar de JavaScript. Concretamente la extensión abrirá una consola interactiva de python (REPL).

Por si no lo conoces, Brython es una implementación del intérprete de Python escrita en JavaScript, y gracias a este proyecto podemos crear extensiones para Chrome sin escribir nada (o casi nada) de JavaScript.

Si estás leyendo estas líneas supongo que disfrutas programando en Python, y por el contrario odias (o desconoces) JavaScript. Continúa leyendo y verás lo fácil que es.

Creación del proyecto

Lo primero que debes hacer es crear una carpeta, llamémosla "extension". En su interior debes crear dos archivos: index.html y manifest.json.

A continuación crear 4 subcarpetas: css, icons, js y py.

Esta es la estructura del proyecto:

-- extension
      |
      |-- css
      |    |
      |    |-- styles.css
      |
      |-- icons
      |    |
      |    |-- 32.png
      |    |-- 128.png
      |
      |-- js
      |    |
      |    |-- brython.js
      |    |-- brython_stdlib.js
      |    |-- loader.js
      |
      |-- py
      |    |
      |    |-- main.py
      |
      |-- index.html
      |
      |-- manifest.json

Ahora vamos a editar los archivos del proyecto. Lo primero será configurar el archivo manifest.json.


Archivo manifest.json

Este es el archivo de configuración de la aplicación:

{

 "manifest_version": 2,

 "name": "Brython Console (REPL)",
 "description": "Consola interactiva Brython para probar código Python en cualquier momento y lugar",

 "content_security_policy":"script-src 'self' 'unsafe-eval' https://apis.google.com; object-src 'self'",

 "version": "1.0",

 "icons": {
   "32": "icons/32.png",
   "128": "icons/128.png"
 },

 "browser_action": {
   "default_icon": "icons/32.png",
   "default_popup": "index.html"
 }

}

Como puedes observar, indicamos un nombre y descripción para la aplicación. Además especificamos un número de versión. En cuanto a "manifest_version": 2, simplemente decir que si pones 1 el navegador te avisará de que debe ser 2 al intentar instalar la extensión.

Por otra parte hemos indicado cuales son los iconos de la aplicación. En el campo "browser_actions" hemos seleccionado el que será el icono por defecto y el archivo html que se ejecutará al hacer click en el icono de la extensión una vez instalada. El archivo que se abrirá al hacer click es index.html, que configuraremos a continuación.

Lo mas importante para nosotros en el archivo manifest.json es el campo "content_security_policy": puesto que Brython va a interpretar nuestros scripts python y los va a traducir a código javascript, posteriormente ejecutará ese código generado mediante la función eval de javascript. Es por ello que es importante poner 'unsafe-eval' aquí.


Archivo index.html

En el archivo index.html debemos enlazar la hoja de estilos (css/styles.css), así como cargar brython y su librería estándar. Además vamos a cargar el script js/loader.js y vamos a definir un textarea:

<!DOCTYPE html>

<html lang='es'>

<head>

<meta charset='utf-8'/>

<!-- ENLAZAMOS LA HOJA DE ESTILOS -->
<link rel="stylesheet" href="css/styles.css"/>

<!-- CARGAMOS BRYTHON Y SU LIBRERÍA ESTÁNDAR -->
<script src="js/brython.js"></script> 
<script src="js/brython_stdlib.js"></script>

<!-- CARGAMOS EL SCRIPT LOADER -->
<script src="js/loader.js"></script>

</head>

<body>

<!-- DEFINIMOS UN TEXTAREA CON ID #code -->
<textarea id="code" rows="20" spellcheck="false"></textarea>

</body>

</html>

En cuanto al textarea, le asignamos un id para referirnos a él en styles.css y en main.py. El atributo spellcheck="false" es para evitar que lo que escribamos en el textarea sea detectado por el corrector ortográfico del navegador (y que así no se subraye de rojo el texto del REPL).

Si ya conocías Brython quizás te hayas preguntado por que no hemos ejecutado la función brython al cargarse body, que es lo que habría sido habitual al crear una página web con Brython:

<body onload="brython();">

Esto se debe a que no está permitida la ejecución de código javascript en línea (escrito directamente en el archivo html). Al igual que hice en la sección Cordova-Brython, intenté utilizar 'unsafe-inline' en content security policy, pero para las extensiones de chrome esto parece no estar permitido.

Solamente está permitido enlazar scripts en el documento html, y es por ello que necesitamos el archivo loader.js que configuraremos mas adelante.


Archivo styles.css

No estamos aquí para aprender CSS así que no me voy a explayar.

Lo que he pretendido con el siguiente código es que el textarea (#code) ocupe todo el ancho y alto de la ventana. Lo mas importante es asignar un ancho y alto mínimo para body, ya que si no lo hacemos, al abrir la aplicación esta será muy muy pequeña. Este es el código del archivo styles.css:

* {
    margin:0px;
    padding:0px;
    border:0px;
}

body {
    display:flex;
    justify-content:flex-end;
    min-width:600px; /*IMPORTANTE: ASIGNAR UN ANCHO MÍNIMO */
    min-height:500px; /* IMPORTANTE: ASIGNAR UN ALTO MÍNIMO */
}

#code {
    background-color:#000;
    color:#fff;
    font-family:'Oxygen Mono', Consolas, 'Liberation Mono', 'DejaVu Sans Mono', monospace;
    font-size:14px;
    overflow:auto;
    height:100vh;
    width:100vw;
    resize:none;
}

Ahora vamos a configurar el archivo loader.js:


Archivo loader.js

En el archivo loader.js tenemos que inicializar el intérprete brython y ejecutar el script main.py. La función brython debe ejecutarse una vez que el cuerpo del documento se haya cargado, de modo que empezamos a configurar loader.js del siguiente modo:

window.addEventListener('load', function(){

    // Aquí tenemos que obtener el código del script main.py

});

Tenemos que crear un objeto XMLHttpRequest que haga una petición GET para obtener el script main.py:

window.addEventListener('load', function(){

    req = new XMLHttpRequest();
    req.open('GET', 'py/main.py', true);
    req.onload = function(){

    	// Aquí tenemos que iniciar brython y que este
    	// interprete a main.py (this.responseText)

    }

    req.send();

});

Iniciamos Brython simplemente con brython() y a continuación ejecutamos a main.py:

window.addEventListener('load', function(){

    req = new XMLHttpRequest();
    req.open('GET', 'py/main.py', true);
    req.onload = function(){

    	brython();
    	src = __BRYTHON__.python_to_js(this.responseText);
    	eval(src);

    }

    req.send();

});

El código fuente de main.py se encuentra almacenado en this.responseText. Mediante el método __BRYTHON__.python_to_js traducimos el código python a javascript y lo almacenamos en la variable src. Finalmente ejecutamos el código traducido mediante eval(src).

Ahora viene lo bueno. Es momento de editar el script main.py:


Archivo main.py

El script de este ejemplo es muy sencillo. Simplemente debemos importar a browser.document y a interpreter.Interpreter. Finalmente iniciamos un intérprete indicando que se renderice en el textarea (#code):

from browser import document
from interpreter import Interpreter

Interpreter(document["code"])

Es importante que estés ejecutando Brython 3.8.9 o superior, ya que en versiones anteriores no existe el módulo interpreter.

Con todo lo que hemos tenido que configurar quizás te decepcione el código de main.py. Es solamente un ejemplo, aquí puedes escribir tu código python, y puedes crear aplicaciones tan grandes como quieras. Lo mas importante es saber como ejecutar el código mediante loader.js.

Si lo deseas borra el textarea de index.html y diseña una aplicación diferente en main.py (también modifica styles.css a tu gusto). Puedes probar a implementar la Calculadora Cordova-Brython como una extensión de Chrome en lugar de una aplicación para Android ;)

Ahora vamos a terminar instalando y ejecutando la extensión que acabamos de crear:


Instalar y Ejecutar la Extensión

Abre tu navegador Google Chrome (si es que no estás ahora mismo en él) y ve a la página de extensiones.

Lo mas fácil es escribir chrome://extensions/ en la barra de direcciones, pero también puedes acceder a través del menú de configuración.

Una vez allí, haz click en el botón [Cargar descomprimida] de la esquina superior izquierda y selecciona la carpeta extension donde creaste la aplicación.

¡Ya está!

La aplicación ha sigo instalada y puedes ejecutarla haciendo click en su icono (en la esquina superior derecha). Si no ves el icono haz click en el icono de extensiones y se deslizará hacia abajo el menú con todas las extensiones instaladas.


NOTAS FINALES: Trabajando con módulos

El script main.py del ejemplo se encuentra intencionadamente en la carpeta "py" para que te fijes en una cosa:

Si, por ejemplo, tuvieses un módulo llamado mimodulo.py en el directorio raíz, podrías importarlo desde main.py sin problema mediante:

import mimodulo

Pero si mimodulo.py está en la carpeta "py" junto a main.py se produciría un error, cosa que podría sorprenderte ya que en Python un módulo siempre tiene acceso a otro que se encuentra en el mismo directorio de trabajo, y sin embargo si que pudimos importar a mimodulo.py cuando este se encontraba en el direcorio raíz de la aplicación.

Esto se debe a que el directorio de trabajo es el directorio raíz de la aplicación, ya que no hemos ejecutado al script main.py directamente, sino a través de loader.js, que a su vez fue cargado por index.html, que se encuentra en el direcorio raíz.

Además, al ejecutar main.py este desconoce su ubicación real, ya que lo que hicimos en loader.js fue leer el código fuente de main.py y ejecutarlo.

En fin, si deseases crear mimodulo.py en la carpeta "py", junto a main.py, e importarlo para usarlo, tendrías que añadir la carpeta "py" a sys.path:

import sys

sys.path.append('py')

import mimodulo

Así de simple. Si has leido hasta aquí ya sabes como crear extensiones para Chrome con Python!! :D

Espero que hayas disfrutado aprendiendo! Si tienes alguna duda puedes escribirme haciendo click en el pie de página