I am making an interactive data manipulation with bokeh (0.12.6) utility that I will deploy within a package. The idea is that a user can run a some routine module.utility() that will start the bokeh server, launch the application in a browser, and when the tab or browser are closed, the server will be stopped.
My application launches fine if I run bokeh serve --show myapp, but it hangs when connecting to the localhost using my method described below. I've inspected the handlers, everything looks as it should.
Is this a reasonable thing to do, and am I going about it correctly?
Directory format
<installed module path>/myapp
└── main.py
Where ./myapp will reside in venv/lib/python3.5/site-packages/mymodule etc.
main.py
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.plotting import figure
from bokeh.models.sources import ColumnDataSource
source = ColumnDataSource(dict(x=list(range(5)), y=list(range(5))))
p = figure(width=300, height=300, tools=[], toolbar_location=None)
p.line(x='x', y='y', source=source)
curdoc().add_root(column(p, sizing_mode='scale_width'))
Run script
def run_single_server(abs_app_path, port=5000):
'''Run bokeh application for single session from server`'''
from bokeh.application import Application
from bokeh.application.handlers import DirectoryHandler
from bokeh.server.server import Server
import os
app_name = os.path.split(abs_app_path)[1]
url = '/{}'.format(app_name)
# Start a bokeh server
apps = {url:Application(DirectoryHandler(filename=abs_app_path))}
server = Server(apps, port=port)
server.start()
server.show(url)
# somehow wait for session to end, perhaps using `server_lifecycle.py`
server.stop()
return
def utility():
import mymodule
module_path = os.path.split(mymodule.__file__)[0]
abs_app_path = os.path.join(module_path, 'myapp')
run_single_server(abs_app_path, port=5000)
return
Perhaps have that routine in the main __init__.py, and have it work like this:
import mymodule
mymodule.utility()
# 1. Browser launches
# 2. user does stuff
# 3. user closes window
# 4. bokeh server is shutdown
Update
I found the build_single_handler_application routine and tried that, but it also appears to hang.
from bokeh.command.util import build_single_handler_application
import os
app_name = os.path.split(abs_app_path)[1]
url = '/{}'.format(app_name)
# Start a bokeh server
apps = build_single_handler_application(abs_app_path)
It looks like I had a couple of problems. I ended up finding and adapting some code that I found on the mail group here for my use-case.
I managed to get everything to work by using separate process for 1) starting the server, 2) launching the app urls with webbrowser, and 3) checking for closed connections and shutting down.
I think I could perhaps do away with initiating the tornado server instance as was done in the flask example I adapted, but I'm happy here.
Note: this example uses single file apps, but you can pass the paths of directory formatted apps as well.
def create_bokeh_server(io_loop, files, argvs, host, port):
'''Start bokeh server with applications paths'''
from bokeh.server.server import Server
from bokeh.command.util import build_single_handler_applications
# Turn file paths into bokeh apps
apps = build_single_handler_applications(files, argvs)
# kwargs lifted from bokeh serve call to Server, with created io_loop
kwargs = {
'io_loop':io_loop,
'generate_session_ids':True,
'redirect_root':True,
'use_x_headers':False,
'secret_key':None,
'num_procs':1,
'host': host,
'sign_sessions':False,
'develop':False,
'port':port,
'use_index':True
}
server = Server(apps,**kwargs)
return server
def run_single_app(files, port=5000, new='tab'):
def start_bokeh(io_loop):
'''Start the `io_loop`'''
io_loop.start()
return None
def launch_app(host, app_name, new):
'''Lauch app in browser
Ideally this would `bokeh.util.browser.view()`, but it doesn't work
'''
import webbrowser
# Map method strings to webbrowser method
options = {'current':0, 'window':1, 'tab':2}
# Concatenate url and open in browser, creating a session
app_url = 'http://{}/{}'.format(host, app_name)
print('Opening `{}` in browser'.format(app_url))
webbrowser.open(app_url, new=options[new])
return None
def server_loop(server, io_loop):
'''Check connections once session created and close on disconnect'''
import time
connected = [True,]
session_loaded = False
while any(connected):
# Check if no session started on server
sessions = server.get_sessions()
if not session_loaded:
if sessions:
session_loaded = True
# Once 1+ sessions started, check for no connections
else:
# List of bools for each session
connected = [True,]*len(sessions)
# Set `connected` item false no connections on session
for i in range(len(sessions)):
if sessions[i].connection_count == 0:
connected[i] = False
# Keep the pace down
time.sleep(2)
# Stop server once opened session connections closed
io_loop.stop()
return None
import os
import threading
import tornado.ioloop
import tornado.autoreload
import time
# Initialize some values, sanatize the paths to the bokeh plots
argvs = {}
app_names = []
for path in files:
argvs[path] = None
app_names.append(os.path.splitext(os.path.split(path)[1])[0])
# Concate hostname/port for creating handlers, launching apps
host = 'localhost:{}'.format(port)
# Initialize the tornado server
io_loop = tornado.ioloop.IOLoop.instance()
tornado.autoreload.start(io_loop)
# Add the io_loop to the bokeh server
server = run_bokeh_server(io_loop, files, argvs, host, port)
print('Starting the server on {}'.format(host))
args = (io_loop,)
th_startup = threading.Thread(target=start_bokeh, args=args)
th_startup.start()
# Launch each application in own tab or window
th_launch = [None,]*len(app_names)
for i in range(len(app_names)):
args = (host, app_names[i], new)
th_launch[i] = threading.Thread(target=launch_app, args=args)
th_launch[i].start()
# Delay to allow tabs to open in same browser window
time.sleep(2)
# Run session connection test, then stop `io_loop`
args = (server, io_loop)
th_shutdown = threading.Thread(target=server_loop, args=args)
th_shutdown.start()
return None
if __name__ == "__main__":
import os
files = [os.path.join('bokeh', fname) for fname in ['ex1.py','ex2.py']]
run_single_app(files, port=5006)
Related
Getting the specifics out of the way, I'm writing an open source P2P social network over IPFS and Flask -- I know, it's been done. I'm choosing Flask because pyinstaller can put it in an exe file.
I am attempting to update my IPNS every 10 minutes to publish all status updates I've added to the network during said 10 minutes. The cron function from setup class (from library.py) is where that updater function is stored. At first, I threaded the cron function from init of setup. The server hung. Then I moved the threading process over to app.before_first_request. The server still hangs.
https://pastebin.com/bXHTuH83 (main.py)
from flask import Flask, jsonify
from library import *
#=========================TO BE DELETED=========================================
def pretty(json):
json = dumps(loads(json), indent=4, sort_keys=True)
return json
#===============================================================================
app = Flask(__name__)
GANN = setup()
#app.before_first_request
def cron_job():
Thread(target=GANN.cron())
#app.route("/")
def home():
return "Hello World!!!"
if __name__ == "__main__":
app.run(port="80", debug=True, threaded=True)
https://pastebin.com/W5P8Tpvd (library.py)
from threading import Thread
from time import time, sleep
import urllib.request
from json import loads, dumps
def api(*argv, **kwargs):
url = "http://127.0.0.1:5001/api/v0/"
for arg in argv:
arg = arg.replace(" ", "/")
if arg[:-1] != "/":
arg += "/"
url += arg
url = url[0:-1]
if kwargs:
url+="?"
for val in kwargs:
url = url + val + "=" + kwargs[val] + "&"
url = url[0:-1]
print(url)
try:
with urllib.request.urlopen(url, timeout=300) as response:
return response.read()
except:
return b"""{"ERROR": "CANNOT CONNECT TO IPFS!"}"""
class setup():
def __init__(self):
api("files", "mkdir", arg="/GANN", parents="True")
self.root_hash = ""
def update_root(self):
try:
for entry in loads(api("files", "ls", l="True").decode())["Entries"]:
if entry["Name"] == "GANN":
self.root_hash = entry["Hash"]
except:
return """{"ERROR": "CANNOT FIND ROOT DIRECTORY"}"""
def publish_root(self):
api("name", "publish", arg=self.root_hash)
def cron(self):
while True:
print("CRON Thread Started!")
self.update_root()
self.publish_root()
sleep(600)
I have searched the web for a couple days and have yet to find a threading technique that will split from the main process and not hang the server from taking other requests. I believe I'm on a single stream connection, as IPFS blocks connections to every other device in my home when it's started. It takes a couple minutes for the CLI IPNS update to go through, so I set urllib's timeout to 300 seconds.
Well what I think the threading code is not correct.
#app.before_first_request
def cron_job():
Thread(target=GANN.cron())
Here you created a Thread object. The argument must be callable, but you called your method already here. so the right way would be
Thread(target=GANN.cron)
So the thread can call the cron function later. having said that, the Thread must be started, so it will call the function target you gave. So it must be ike
thread_cron = Thread(target=GANN.cron)
thread_cron.start()
Since you called the GANN.cron() , the method starts executing and your app hung!
I have a python document running in bokeh server with bokeh serve view_server_so.py --show. The webpage is immediately opened in a browser tab.
For testing purposes I am using Pyro to allow remote access to some of the objects / methods running in the bokeh server.
My server testscript creates just one button and one method (exposed to remote control), bokeh serves the webpage on http://localhost:5006/view_server_so.
The button is a Toggle button, each time the button is pressed its active state alternates between True and False, and its color changes.
# view_server_so.py
# This script shall be started from the command line with
# bokeh serve view_server_so.py --show
from bokeh.layouts import layout, row
bokeh.models import Toggle
from bokeh.io import curdoc
import os
import Pyro4
import socket
import subprocess
import threading
# The layout used in the html-page
class View():
def __init__(self):
self.document = curdoc()
self.btn_task = Toggle(label="Press me",
button_type="success",
css_classes=["btn_task"])
self.layout = layout(row(self.btn_task))
#Pyro4.expose
def get_btn_task_active_state(self):
# This method is enabled for remote control
# The btn_task changes its active state each time it is pressed (bool)
print('active state: {}'.format(self.btn_task.active))
return self.btn_task.active
# As the script is started with 'bokeh serve',
# bokeh has control of the main event loop.
# To enable Pyro to listen for client requests for the object view,
# and its exposed method,
# the Pyro event loop needs to run in its own thread.
class PyroDaemon(threading.Thread):
def __init__(self, obj):
threading.Thread.__init__(self)
self.obj=obj
self.started=threading.Event()
print('PyroDaemon.__init__()')
def run(self):
daemon=Pyro4.Daemon()
obj = self.obj
self.uri=daemon.register(obj,"test")
self.started.set()
print('PyroDaemon.run()')
daemon.requestLoop()
view = View()
print('view created')
# starting a Pyro name server
hostname = socket.gethostname()
print('hostname: {}'.format(hostname))
try:
print('try Pyro4.locateNS()')
name_server=Pyro4.locateNS()
except Pyro4.errors.NamingError:
print('except Pyro4.errorsNamingError')
args = ['python', '-m', 'Pyro4.naming', '-n', hostname]
p = subprocess.Popen(args)
name_server=Pyro4.locateNS()
print('Nameserver is started')
# create a pyro daemon with object view, running in its own worker thread
pyro_thread=PyroDaemon(view)
pyro_thread.setDaemon(True)
pyro_thread.start()
pyro_thread.started.wait()
print('Daemon is started')
# The object view is registered at the Pyro name server
name_server.register('view', pyro_thread.uri, metadata = {'View'})
print('view is registered')
view.document.add_root(view.layout)
Now I have a second testscript. After some setup for the remote object, it simply calls several times the method get_btn_task_active_state() and prints the result. When between the calls the button is clicked in the browser, the active state switches and is correctly printed.
import Pyro4
import time
# looking for the Pyro name server created in view_server_so.py
nameserver=Pyro4.locateNS(broadcast = True)
# get the URI (universal resource identifier)
uris = nameserver.list(metadata_all = {'View'})
# create a proxy object that interacts (remote control)
# with the remote object 'view'
view_rc = Pyro4.Proxy(uris['view'])
for i in range(3):
time.sleep(3)
# change the state manually: click on the button
print('state: {}'.format(view_rc.get_btn_task_active_state()))
# prints
# state: False
# state: True
# state: False
As manual testing gets tedious, I want to automate the manual clicks to the button. So I am adding webdriver support, look up the button in the webpage, and have some automated clicks and the calls to get_btn_task_active_state() as before.
import Pyro4
from selenium import webdriver
import socket
import time
# looking for the Pyro name server created in view_server_so.py
nameserver=Pyro4.locateNS(broadcast = True)
# get the URI (universal resource identifier)
uris = nameserver.list(metadata_all = {'View'})
# create a proxy object that interacts (remote control)
# with the remote object 'view'
view_rc = Pyro4.Proxy(uris['view'])
# use the Chrome webdriver
driver = webdriver.Chrome()
# open a Chrome Browser and the given webpage
driver.get("http://localhost:5006/view_server_so")
time.sleep(1)
# Find the button
btn_task = driver.find_element_by_class_name("btn_task")
for i in range(3):
time.sleep(1)
print('state: {}'.format(view_rc.get_btn_task_active_state()))
btn_task.click()
# prints
# state: False
# state: False
# state: False
#
# but should alternate between False and True
An webdriver controlled browser opens, I can see the color of the button change visually as it is automatically clicked, but the active state of the button does not seems to change any more.
What changes would be necessary, so that the automated script gives the same results as the manual testing?
i'm having some trouble about this WallBoard webapp (Python) that i have to fix, i hope i can be very clear of what the problem is so its easy for you to understand and help me.
So, the app has 3 pages, Main Page, Calls Waiting, Tickets. It is used by support team in this company to track calls of the day and tickets too.
So when i run this app with VS Code, its behavior is absolutely normal. App logs to another website to retrieve calls information, and Database to retrieve Tickets information. After some time session expires (retrieving calls information) but app detects and logs in again.
When i start the app at IIS(8.5) EVERY 17min the app gets restarted for no reason, and after a few hours of this i have 40 to 50 Google Chrome Threads taking all my memory from the machine it is hosted in...
I don't believe it is a problem with IIS, i think it is responding like this for some reason and i can't find out.
Here is the code for the WallBoard App
from datetime import datetime
from flask import Flask, g, render_template
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import pypyodbc as pyodbc
import time
import sys, socket
from funcs import *
from config import *
from querys import *
from cachetools import LRUCache
from apscheduler.schedulers.background import BackgroundScheduler
# Init
app = Flask(__name__)
app.debug = True
# Globals
CALLS_WAITING_CACHE = LRUCache(maxsize=20)
TICKETS_CACHE = LRUCache(maxsize=20)
op = Options()
op.binary_location = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"
op.add_argument("--headless")
op.add_argument("--disable-gpu")
browser = webdriver.Chrome(chrome_options=op, executable_path=r'C:\Python34\Scripts\chromedriver.exe')
with open('log.txt', 'a') as f:
print('Walboard Starting! - ' + str(datetime.now()), file=f)
def login():
# Here i log in to the website where i get calls information
###################################################################
#app.before_request
def before_request():
"""Before Request"""
g.request_start_time = time.time()
g.request_time = lambda: "%.5f" % (time.time() - g.request_start_time)
####################################################################
#app.route('/')
def index():
"""Landing Page"""
return render_template("landing.html",
dashboard_name='TSU WallBoard',
css_folder='/static/css/',
CallsWaiting_Page='/callswaiting',
Tickets_Page='/tickets'
)
####################################################################
def calls_waiting():
# Here i take the information and store in in cache thanks to cachetools
############################################################################
#app.route('/callswaiting')
def calls_wainting_render():
# Here i take information from cache and show it in the webpage of the app
####################################################################
def tickets():
# Here i do the same with tickets, load from DB and store in cache
#app.route('/tickets')
def tickets_render():
# Here i load tickets from cache and show them in Web Page
##########################################################
# Login to avaya website
login()
time.sleep(3)
# Get calls and store in cache
calls_waiting()
# Get tickets and store in cache
tickets()
# Create scheduler obj
sched = BackgroundScheduler(daemon=True)
# Create Jobs
sched.add_job(calls_waiting, 'interval', seconds=refresh_time_calls,
max_instances=1, id='calls_waiting')
sched.add_job(tickets, 'interval', seconds=refresh_time_tickets,
max_instances=1, id='tickets')
# Start scheduler
sched.start()
with open('log.txt', 'a') as f:
print('WallBoard Started! - ' + str(datetime.now()), file=f)
if __name__ == "__main__":
app.run()
So at this point i'm completely lost, i have done hours of research trying to find what the problem is but still no luck.
I took out the code inside because it was too big but if necessary i can provide it.
I was using PhantomJS but i was always getting the Warning that is deprecated and i decided to change do ChromeDrive but the problem still remains.
Can it be with BackgroundScheduler? because i'm starting to think that each time IIS force the app to restart, it creates more Jobs and doesn't kill the others.
I appreciate all the help and tips, and i am really sorry for this long post!
I took this screenShot after 10min of running, i will update with new one in the morning
This question already has answers here:
Why does running the Flask dev server run itself twice?
(7 answers)
Closed 5 years ago.
I am running into a seemingly bizarre problem that I can't figure out - I am trying to create a single object, however for some reason, two are always made.
The two files control a robot i am building.
App.py
A flask server which receives in coming commands through a socket.io connection with clients
Hypervisor.py
Robot Controller, takes in user commands, sensor data, current agenda, and feeds commands to the MotionController.
I am trying to create a single instance of a the Hypervisor class from within the Flask server app.py, however two are consistently created.
Below is my code and console output showing the double object creation. Why is this happeneing!!?!?!?
App.py
#!/usr/bin/env python
from flask import Flask, render_template, session, request, send_from_directory, send_file
from flask_socketio import SocketIO, emit, join_room, leave_room, close_room, rooms, disconnect
import time
import json
import datetime
import logging
import platform
from bColors import bcolors
from RobotSystem.Hypervisor import Hypervisor
from RobotSystem.Services.Utilities.RobotUtils import RobotUtils
async_mode = None
app = Flask(__name__, static_url_path='/static')
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode=async_mode)
log = logging.getLogger("werkzeug")
log.setLevel(logging.ERROR)
thread = None
connections = 0
#app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html', async_mode=socketio.async_mode)
def background_thread():
while True:
socketio.sleep(1)
#socketio.on('valueUpdate')
def valueUpdateHandler(message):
RobotUtils.ColorPrinter("app.py",'Value update fired ', 'OKBLUE')
quadbot.inputData(message)
data = {}
data['Recieved'] = True
return json.dumps(data)
#socketio.on('connect')
def test_connect():
global connections
connections+=1
print_str = "Client connected. "+ str(connections)+ " current connections"
RobotUtils.ColorPrinter("app.py",print_str, 'OKBLUE')
global thread, quadbotThread
if thread is None:
print "init"
thread = socketio.start_background_task(target=background_thread)
#socketio.on('disconnect')
def test_disconnect():
global connections
connections -= 1
RobotUtils.ColorPrinter("app.py",str( 'Client disconnected. ' +str(connections)+ " current connections" ), 'OKBLUE')
if __name__ == '__main__':
global quadbot
quadbot = Hypervisor()
socketio.run(app, debug=True)
Hypervisor.py
#!/usr/bin/python
from Services import *
import time
import math
import json
import sys
import threading
import os
from Queue import Queue,Empty
class Hypervisor():
def __init__(self):
if RobotUtils.LIVE_TESTING:
self.pwm = PWM()
self.pwm.setPWMFreq(RobotUtils.FREQUENCY)
else:
self.pwm = None
self.inputQueue = Queue()
self.agendaThread = threading.Thread(group=None,target=self.updateAgendaLoop,name="agendaThread")
self.agendaThread.start()
self.data_file_name = RobotUtils.DATA_FILE
self.front_left = None
self.front_right = None
self.back_left = None
self.back_right = None
self.TURN_LEFT = RobotUtils.TURN_LEFT
self.TURN_RIGHT = RobotUtils.TURN_RIGHT
self.FORWARD = RobotUtils.FORWARD
self.BACKWARD = RobotUtils.BACKWARD
self.STOP = RobotUtils.STOP
self.AUTONOMOUS = RobotUtils.AUTONOMOUS
self.INVALID_DATA_ERROR = RobotUtils.INVALID_DATA_ERROR
self.horizVidMotor = Motor(50, RobotUtils.HORIZONTAL_VID_PIN, RobotUtils.HORIZONTAL_VID_MIN_VAL, RobotUtils.HORIZONTAL_VID_MAX_VAL, 0, "horizontal video motor", self.pwm)
self.vertVidMotor = Motor( 50, RobotUtils.VERTICAL_VID_PIN, RobotUtils.VERTICAL_VID_MIN_VAL, RobotUtils.VERTICAL_VID_MAX_VAL, 0, "vertical video motor", self.pwm)
self.setup()
self.motors = [self.front_left, self.front_right,self.back_left,self.back_right, self.horizVidMotor, self.vertVidMotor ]
self.MotionController = MotionController(self.TURN_LEFT, self.TURN_RIGHT, self.FORWARD, self.BACKWARD, self.STOP,self.AUTONOMOUS,self.INVALID_DATA_ERROR,
self.motors, RobotUtils
)
self.stand()
RobotUtils.ColorPrinter(self.__class__.__name__, '__init__() finished. Robot Created with id ' +str(id(self)), 'OKBLUE')
# loads json data and creates Leg objects with add_leg()
def setup(self):
with open(self.data_file_name) as data_file:
data = json.load(data_file)
constants = data["constants"]
for i in range(len(data["legs"])):
self.add_leg(data["legs"][i],constants)
# reads dictuanary values from input, creates a Leg object, and adds it to leg variables
def add_leg(self,legData,constants):
leg_name = legData["name"]
body_pin = legData["motors"]["body"]["pinValue"]
body_offset = legData["motors"]["body"]["offset"]
body_center = constants["bodyCenterValue"] + body_offset
body_min = constants["bodyRange"]["min"]
body_max = constants["bodyRange"]["max"]
mid_horiz_value = legData["motors"]["middle"]["horizValue"]
middle_pin = legData["motors"]["middle"]["pinValue"]
middle_min = constants["middleRange"]["min"]
middle_max = constants["middleRange"]["max"]
middle_offset_to_center = constants["midOffsetFromHoriz"]
leg_horiz_value = legData["motors"]["leg"]["horizValue"]
leg_pin = legData["motors"]["leg"]["pinValue"]
leg_min = constants["legRange"]["min"]
leg_max = constants["legRange"]["max"]
leg_offset_to_center = constants["legOffsetFromHoriz"]
leg = Leg( self.pwm, leg_name, body_pin, body_min, body_max, body_center, mid_horiz_value, middle_pin, middle_min, middle_max, middle_offset_to_center, leg_horiz_value, leg_pin, leg_min, leg_max, leg_offset_to_center)
if leg_name == "FR":
self.front_right = leg
elif leg_name == "FL":
self.front_left = leg
elif leg_name == "BL":
self.back_left = leg
elif leg_name == "BR":
self.back_right = leg
else:
print "ERROR: LEG CANNOT BE IDENTIFIED"
# Called by server when a change in user data is detected
def inputData(self,data):
self.inputQueue.put(data)
def updateAgendaLoop(self):
while True:
try:
data = self.inputQueue.get_nowait()
self.updateAgenda(data)
except Empty:
pass
time.sleep(RobotUtils.AGENDA_UPDATE_SPEED)
print '\033[94m' + "Robot: QUEUE READING FINISHED" + '\033[0m'
sys.exit()
# acts as central coordinator for the robot - raeads incoming data + state of the bot and calls methods accordingly
def updateAgenda(self,data):
self.MotionController.updateCameras(data)
nextMove = self.MotionController.NextMove(data)
if nextMove == self.INVALID_DATA_ERROR:
print "Fix this"
else:
self.MotionController.MakeMove(nextMove)
Console Output
First of all a little soapboxing:
If you provide a SSCCE (Short, Self-Contained, Correct (Compilable), Example) then you are more likely to get a response. Also by trimming the example to the minimum required to reproduce you may very well identify the source of the problem yourself. For example the following would be a SSCCE for your issue:
Required dependancies:
pip install flask
pip install flask-socketio
Code:
import logging
from flask import Flask
from flask_socketio import SocketIO
logging.basicConfig(level=logging.ERROR)
app = Flask(__name__)
socketio = SocketIO(app)
class Hypervisor():
def __init__(self):
print('Hypervisor initialized')
if __name__ == '__main__':
quadbot = Hypervisor()
socketio.run(app, debug=True)
Output:
Hypervisor initialized
Hypervisor initialized
Explanation
If you use a debugger the answer presents itself fairly easily. You can use the debugger tools included in your IDE, or you can always use The Python Debugger from the python standard library (a.k.a. pdb).
While it's beyond the scope of this answer to provide a complete tutorial of pdb, the method we will use to debug is to break into the debugger by importing pdb and inserting the following pdb.set_trace() at the point you want to begin debugging.
Since the issue is with Hypervisor creation the logical point to insert the debugger is just before the initialization of Hypervisor like so:
if __name__ == '__main__':
import pdb
global quadbot
pdb.set_trace() # debugging begins here
quadbot = Hypervisor()
socketio.run(app, debug=True)
From this point when you run your app, it will drop into pdb right before the initialization of Hypervisor. Before you do so though, you will need to know two commands for using pdb.
The first is n for next which continues execution until to the next line of code in the current function.
The second is s for step which will step into the current code stopping at first opportunity.
Or as the docs explain it:
The difference between next and step is that step stops inside a called function, while next executes called functions at (nearly) full speed, only stopping at the next line in the current function.
So armed with this knowledge run your app, and you will be presented with the pdb prompt which should look something like this:
-> quadbot = Hypervisor()
(Pdb)
The -> shows the current line of code about to be executed. (Pdb) is the prompt awaiting input. At this point let's initialize the Hypervisor by proceeding to the next line of code by typing n and enter. At this point you should see a single Hypervisor has been created.
[4/3/2017 20:02:46 ] Hypervisor: __init__() finished. Robot Created with id 4218654299
And be returned to the pdb prompt for the next line:
-> socketio.run(app, debug=True)
(Pdb)
So since there's only one more line of code left to run, the issue is somewhere in socketio.run. So this time we will step into the current line of code by typing s and enter which will bring you to:
-> def run(self, app, host=None, port=None, **kwargs):
(Pdb)
From this point, keep stepping to the next line of code until you see the second Hypervisor initialization. Looking at the trace you should see something like the following:
-> app.run(host=host, port=port, threaded=True,
(Pdb) n
-> use_reloader=use_reloader, **kwargs)
(Pdb) n
-> quadbot = Hypervisor()
(Pdb) n
[4/3/2017 20:03:52 ] Hypervisor: __init__() finished. Robot Created with id 4367452293
This shows you that right after the app.run executes (it takes two n's to execute since the command spans two lines of code), it returns to the quadbot = Hypervisor() line of code we started at. So a closer look at app.run shows an argument called use_reloader. If you haven't guessed already, looking at the Flask-SocketIO docs tells us:
use_reloader - True to enable the Flask reloader, False to disable it.
With a little more digging we can find the following two nuggets of wisdom in the Flask docs:
debug
The debug flag. Set this to True to enable debugging of the application. In debug mode the debugger will kick in when an unhandled exception occurs and the integrated server will automatically reload the application if changes in the code are detected.
and
run...
Flask will suppress any server error with a generic error page unless it is in debug mode. As such to enable just the interactive debugger without the code reloading, you have to invoke run() with debug=True and use_reloader=False. Setting use_debugger to True without being in debug mode won’t catch any exceptions because there won’t be any to catch.
So armed with this information you can choose to either disable debug, or pass the option use_reloader=False to socketio.run.
TL;DR
You most likely want to disable the Flask reloader by passing use_reloader=False to socketio.run
if __name__ == '__main__':
global quadbot
quadbot = Hypervisor()
socketio.run(app, debug=True, use_reloader=False)
I currently have a Python file that when run using python file_name.py installs a Windows service that is viewable in Event Viewer under application logs and stoppable using sc stop service_name. However, when converted into an executable using cx_Freeze, the executable runs with no errors but the service no longer installs. This happens if I run just the executable by itself, if I run service_name.exe --install service_name, or if I run sc create service_name binPath=service_path
My setup.py file looks something like:
from cx_Freeze import setup, Executable
options = {
'build_exe': {
'packages': ['packagename'],
'includes': ['ServiceHandler', 'cx_Logging']}
}
setup(name='cx_FreezeSampleService',
version='0.1',
description='Sample cx_Freeze Windows serice',
executables=Executable('Config.py', base='Win32Service',
targetName='cx_FreezeSampleService.exe'),
options=options
)
My Config.py looks something like:
NAME = 'cx_FreezeSampleService%s'
DISPLAY_NAME = 'cx_Freeze Sample Service - %s'
MODULE_NAME = 'ServiceHandler'
CLASS_NAME = 'Handler'
DESCRIPTION = 'Sample service description'
AUTO_START = True
SESSION_CHANGES = False
And finally, my ServiceHandler.py looks something like:
class Handler(object):
def Initialize(self, Config):
pass
def Run(self):
#code to run service
def Stop(self):
#code to stop service
This code follows the example at the cx_Freeze source code here (https://bitbucket.org/anthony_tuininga/cx_freeze/src/1282b6b6ee637738210113dd88c3c198d475340f/cx_Freeze/samples/service/?at=default) almost exactly, but neither this nor the example seem to work in actually installing a service.
Thank you in advance!
This is an old question, but I manage to get it working as a window service for a simple flask application with the help of the developers.
[https://github.com/marcelotduarte/cx_Freeze/tree/master/cx_Freeze/samples/service]
You have to set up all the windows service actions you want performance.
this is how the ServiceHandler.py should look like as a template you still need to adapt to run your application.
"""
Implements a simple service using cx_Freeze.
See below for more information on what methods must be implemented and how they
are called.
"""
import threading
import os
import sys
import cx_Logging
class Handler:
# no parameters are permitted; all configuration should be placed in the
# configuration file and handled in the Initialize() method
def __init__(self):
self.stopEvent = threading.Event()
self.stopRequestedEvent = threading.Event()
# called when the service is starting
def initialize(self, configFileName):
self.directory = os.path.dirname(sys.executable)
cx_Logging.StartLogging(os.path.join(self.directory, "teste.log"), cx_Logging.DEBUG)
#pass
# called when the service is starting immediately after Initialize()
# use this to perform the work of the service; don't forget to set or check
# for the stop event or the service GUI will not respond to requests to
# stop the service
def run(self):
cx_Logging.Debug("stdout=%r", sys.stdout)
sys.stdout = open(os.path.join(self.directory, "stdout.log"), "a")
sys.stderr = open(os.path.join(self.directory, "stderr.log"), "a")
self.stopRequestedEvent.wait()
self.stopEvent.set()
# called when the service is being stopped by the service manager GUI
def stop(self):
self.stopRequestedEvent.set()
self.stopEvent.wait()