Spawn Long Running Background Task In Python - python

I am trying to setup a Flask app that will run a shell script when it receives a request. The purpose of this is to re-deploy another app when I push to GitLab. I have everything connected, but for some reason the subprocess spawned by the Flask app gets terminated shortly after it starts. I've been logging into my server over SSH to start the Flask app, and everything works fine as long as I stay logged in, but once I kill the session it stops working.
Flask app
from flask import Flask, request, abort, Response
import os
import subprocess
from waitress import serve
process:subprocess.Popen = None
app = Flask(__name__)
#app.route('/', methods = ['POST'])
def deploy():
req_secret = request.headers.get("X-Gitlab-Token")
if req_secret is not None:
if 'DEPLOY_SECRET' in os.environ:
if os.environ['DEPLOY_SECRET'] == req_secret:
global process
if process is not None:
process.terminate()
process = subprocess.Popen("./deploy.sh", stdout=subprocess.PIPE, shell=True)
return 'Success'
abort(401)
if __name__ == "__main__":
serve(app, port=5000)
deploy.sh
#!/bin/sh
killall server
cd ..
git pull origin master
diesel migration run
cargo run
run.sh
#!/bin/sh
DEPLOY_SECRET=$1 pipenv run python autodeploy.py
cargo run should run forever, and it does as long as I stay logged into my SSH session. I start the flask app by running ./run &

Maybe your waitress server has no privileges to run your scripts. Try to change onwership to your scripts to 'waitress_user' like:
chown your_waitress_user deploy.sh
I am not sure for the username, probably would be 'waitress'.

Turns out the solution was to use nohup in the python script
from flask import Flask, request, abort, Response
import os
import subprocess
from waitress import serve
process:subprocess.Popen = None
app = Flask(__name__)
#app.route('/', methods = ['POST'])
def deploy():
req_secret = request.headers.get("X-Gitlab-Token")
if req_secret is not None:
if 'DEPLOY_SECRET' in os.environ:
if os.environ['DEPLOY_SECRET'] == req_secret:
global process
if process is not None:
process.terminate()
with open("autodeploy.log", "a+") as log_file:
process = subprocess.Popen(["nohup", "./deploy.sh"], stdout=log_file, stderr=log_file)
return 'Success'
abort(401)
if __name__ == "__main__":
serve(app, port=5000)

Related

How to use Python thread with Flask in Apache

I'm trying to deploy my Flask app to Apache webserver.
In my main file, before running the Flask app, I start a daemon thread that runs in background:
# ... here there are all the imports and the Flaks routes
def main():
x = threading.Thread(target=my_thread_function)
x.daemon = True
x.start()
# app.run() # I moved this line below
x.join()
if __name__ == '__main__':
main()
app.run()
This is my wsgi file:
import sys
sys.path.insert(0, 'C:\\myapp\\')
from myapp import app as application
If I run Apache server, Flask works fine and I can see the web app interface when I connect to the IP address. However, the background thread doesn't work, because I don't see its "effect" in the app.
If I run the same code with the Flask development server, the background thread works fine.
How can it work with Apache?
remove the join() because it will block the app.run()
# ... here there are all the imports and the Flaks routes
def main():
x = threading.Thread(target=my_thread_function)
x.daemon = True
x.start()
if __name__ == '__main__':
main()
app.run()

Python script with flask - how to enable setup on windows server

I have a python script with flask API. I am running the code in command line python scriptname.py and making an POST call and it works.
But in realtime how to enable this set up on windows server, so that script is running and available anytime for third-party to make HTTP post. Any pointers please.
class Impersonate:
def __init__(self,login,password):
self.domain='<domain>'
self.login=login
self.password=password
def logon(self):
self.handel=win32security.LogonUser(self.login,self.domain,self.password,win32con.LOGON32_LOGON_INTERACTIVE,win32con.LOGON32_PROVIDER_DEFAULT)
win32security.ImpersonateLoggedOnUser(self.handel)
def logoff(self):
win32security.RevertToSelf() #terminates impersonation
self.handel.Close() #guarantees cleanup
a=Impersonate('testuser','password]')
try:
a.logon() #become the user
print(a.login)
a.logoff() #return to normal
except:
pass
app = Flask(__name__)
api = Api(app)
class Hellow(Resource):
def post(self):
path = os.path.join(parentdir, dirname)
try:
os.makedirs(path)
resp = Response('{} successfully created.)
api.add_resource(Hellow, '/test')
if __name__ == "__main__":
app.run(port=5000, host="<hostname>" use_reloader=True)
You either need to deploy the flask backend on server or on windows you can use "start /b python xyz.py". Or can also have a look at https://www.howtogeek.com/50786/using-srvstart-to-run-any-application-as-a-windows-service/

What is the best way for a Python script to communicate with a Python Flask server that transports content to the client?

The following scenario:
I have a Raspberry Pi running as a server. Currently I am using a Python script with Flask and I can also access the Raspberry Pi from my PC. (The flask server runs an react app.)
But the function should be extended. It should look like the following:
2nd Python script is running all the time. This Python script fetches data from an external API every second and processes it. If certain conditions are met, the data should be processed and then the data should be communicated to the Python Flask server. And the Flask server then forwards the data to the website running on the computer.
How or which method is best to program this "interprocess communication". Are there any libraries? I tried Celery, but then it throws up my second Python script whenever I want to access the external API, so I don't know if this is the right choice.
What else would be the best approach? Threading? Direct interprocess communication?
If important, this is how my server application looks so far:
from gevent import monkey
from flask import Flask, render_template
from flask_socketio import SocketIO
monkey.patch_all()
app = Flask(__name__, template_folder='./build', static_folder='./build/static')
socket_io = SocketIO(app)
#app.route('/')
def main():
return render_template('index.html')
#socket_io.on('fromFrontend')
def handleInput(input):
print('Input from Frontend: ' + input)
send_time()
#socket_io.on('time')
def send_time():
socket_io.emit('time', {'returnTime': "some time"})
if __name__ == '__main__':
socket_io.run(app, host='0.0.0.0', port=5000, debug=True)
Well i found a solution for my specific problem i implemented it with a thread as follows:
import gevent.monkey
gevent.monkey.patch_all()
from flask import Flask, render_template
from flask_socketio import SocketIO
import time
import requests
from threading import Thread
app = Flask(__name__, template_folder='./build', static_folder='./build/static')
socket_io = SocketIO(app)
#app.route('/')
def main():
thread = Thread(target=backgroundTask)
thread.daemon = True
thread.start()
return render_template('index.html')
#socket_io.on('fromFrontend')
def handleInput(input):
print('Input from Frontend: ' + input)
#socket_io.on('time')
def send_time():
socket_io.emit('time', {'returnTime': 'hi frontend'})
def backgroundTask():
# do something here
# access socket to push some data
socket_io.emit('time', {'returnTime': "some time"})
if __name__ == '__main__':
socket_io.run(app, host='0.0.0.0', port=5000, debug=True)

How to access localhost (flask app) from within docker?

On my laptop I can start simple flask app:
import os
import io
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
s = """
This is a localhost!
"""
return (s)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=3000, debug=True)
And when do curl localhost:3000 on my laptop - I can get a good response.
But when I start a docker image and put same code and start it with same version of Python - it shows as running but when I do from within a docker curl localhost:3000 - do not get any response (it just hangs and nothing happens).
How to enable localhost (routing) inside docker?
Thanks.
Change your code to
app.run(host='127.0.0.1', port=3000, debug=True)
and check if localhost is defined in /etc/hosts.

Use zerorpc inside Flask app throws error "operation would block forever"

I have a RPC Server using zerorpc in Python, written this way
import zerorpc
from service import Service
print('RPC server - loading')
def main():
print('RPC server - main')
s = zerorpc.Server(Service())
s.bind("tcp://*:4242")
s.run()
if __name__ == "__main__" : main()
It works fine when I create a client
import zerorpc, sys
client_rpc = zerorpc.Client()
client_rpc.connect("tcp://127.0.0.1:4242")
name = sys.argv[1] if len(sys.argv) > 1 else "dude"
print(client_rpc.videos('138cd9e5-3c4c-488a-9b6f-49907b55a040.webm'))
and runs it. The print() outputs what this 'videos' function returns.
But when I try to use it this same code inside route from a Flask app, I receive the following error:
File "src/gevent/__greenlet_primitives.pxd", line 35, in
gevent.__greenlet_primitives._greenlet_switch
gevent.exceptions.LoopExit: This operation would block forever Hub:
The flask method/excerpt
import zerorpc, sys
client_rpc = zerorpc.Client()
client_rpc.connect("tcp://127.0.0.1:4242")
#app.route('/videos', methods=['POST'])
def videos():
global client_rpc
client_rpc.videos('138cd9e5-3c4c-488a-9b6f-49907b55a040.webm')
I can't find out what might be happening. I'm quite new to Python and I understand that this may have some relation with Flask and how it handles the thread, but I can't figure out how to solve it.
zerorpc depends on gevent, which provides async IO with cooperative coroutines. This means your flask application must use gevent for all IO operations.
In your specific case, you are likely starting your application with a standard blocking IO WSGI server.
Here is a snippet using the WSGI server from gevent:
import zerorpc
from gevent.pywsgi import WSGIServer
app = Flask(__name__)
client_rpc = zerorpc.Client()
client_rpc.connect("tcp://127.0.0.1:4242")
#app.route('/videos', methods=['POST'])
def videos():
global client_rpc
client_rpc.videos('138cd9e5-3c4c-488a-9b6f-49907b55a040.webm')
# ...
if __name__ == "__main__":
http = WSGIServer(('', 5000), app)
http.serve_forever()
Excerpt from https://sdiehl.github.io/gevent-tutorial/#chat-server

Categories