I am trying to build a redis queue worker to offload the creation process of an expo app for a simple flask application, with the goal of displaying the qr code for Expo Go in the front end when it is ready, using rq and subprocess but it seems that something is going on with subprocess.Popen() that is getting in the way of processing the task, and I am not the most versed with npm/npx errors.
This is my current code:
In app.py:
from flask import Blueprint, request, jsonify, current_app
import redis
from rq import Queue
r = redis.Redis.from_url(os.environ.get('REDIS_URL'))
queue = Queue(connection=r)
@app.route('/expo/apps/create', methods=["POST"])
def create_expo_app():
data = request.get_json()
print(data)
kwargs = {'app_name': data}
job = enqueue_job_with_dependency(queue, 'create_expo_qr_code', **kwargs)
job_id = job.get_id()
# Return the HTML template with a placeholder for the QR code
return jsonify({'job_id': job_id})
def enqueue_job_with_dependency(queue, job_function_name, **kwargs):
# Check if the last job of the same function type was successful
last_job = queue.fetch_job('expo_app_resources.' + job_function_name)
print("start")
if last_job is not None:
if last_job.is_finished:
print("finished")
job = queue.enqueue('expo_app_resources.' + job_function_name, kwargs=kwargs, job_id = job_function_name, job_timeout=1200)
return job
if last_job.is_failed:
print("failed")
job = queue.enqueue('expo_app_resources.' + job_function_name, kwargs=kwargs, job_id = job_function_name, job_timeout=1200)
return job
else:
print('none')
job = queue.enqueue('expo_app_resources.' + job_function_name, kwargs=kwargs, job_id = job_function_name, job_timeout=1200)
return job
In worker.py:
import os
import sys
import redis
from rq import Worker, Queue, Connection
from dotenv import load_dotenv
from expo_app_resources import create_expo_qr_code
load_dotenv()
r = redis.Redis.from_url(os.environ.get('REDIS_URL'))
queue = Queue(connection=r)
if __name__ == '__main__':
# Create a worker and listen to the queue
with Connection(r):
worker = Worker([queue])
worker.work()
In expo_app_resources.py:
import subprocess
import os
import rq.timeouts
def create_expo_qr_code(app_name):
# Create a new React Native Expo project
try:
process = subprocess.Popen(['npx', 'create-expo-app', app_name, '--template', 'blank'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
for line in process.stdout:
print(line, end='', flush=True)
for line in process.stderr:
print(line, end='', flush=True)
process.wait()
project_folder = os.getcwd() + f'/{app_name}'
print(project_folder)
# Change the current working directory to the project folder
os.chdir(project_folder)
address, port = start_expo_server()
if address and port:
server_address = f"http://{address}:{port}"
print(server_address) # Just print it for now
except rq.timeouts.JobTimeoutException:
process.kill()
print("Subprocess timed out")
def start_expo_server():
command = 'npm start' # Use your desired port
process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, universal_newlines=True)
for line in process.stdout:
if "Metro is running" in line:
address, port = line.strip().split()[-1].split(':')
return address, int(port)
return None, None
From all indication, this should work, and it does, all the way up to the subprocessing of the CLI commands. But when the rq worker does run the create_expo_qr_code() function (as written above), it will print the expected npm install and that is it:
17:13:14 Subscribing to channel rq:pubsub:ac*******************
17:13:15 *** Listening on default...
17:13:32 default: expo_app_resources.create_expo_qr_code(app_name='example') (create_expo_qr_code)
> npm install
It seems that it is hanging there and cannot move past that step for whatever reason and will auto timeout after 20 minutes per the enqueue call args. In a previous version of the same code, I used subprocess.run() for the first subprocess, and that successfully created the app, and printed out the project_folder, but then would not show any life after that (when it hit start_expo_server())
def create_expo_qr_code(app_name):
# Create a new React Native Expo project
subprocess.run(['npx', 'create-expo-app', app_name, '--template', 'blank'], check=True)
project_folder = os.getcwd() + f'/{app_name}
print(project_folder)
# Change the current working directory to the project folder
os.chdir(project_folder)
address, port = start_expo_server()
if address and port:
server_address = f"http://{address}:{port}”
Note: expo login, eas whoami, create-expo-app, npm start, all work fine when doing them manually.