I am trying to get the output from a python script and put it into a table in the html of my cherrypy app.
Example app:
import string, os
import cherrypy
file_path = os.getcwd()
html = """<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>CCMF</title>
<link rel='shortcut icon' type='image/x-icon' href='img/favicon.ico' />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
function b1() {
var request = $.ajax({
url: "b1.py",
type: "POST",
dataType: "text"
});
request.done(function(msg) {
$("#output").html(msg);
});
request.fail(function(jqXHR, textStatus) {
alert( "Request failed: " + textStatus );
});
}
</script>
</head>
<button onclick="b1()">call b1.py</button>
...
<td id = "output"; style="vertical-align: top; height: 90%; width: 100%;">
<--output goes here -->
</td>
...
</html>
"""
class ccmf(object):
#cherrypy.expose
def index(self):
return html
if __name__ == '__main__':
cherrypy.server.socket_host = "127.0.0.1"
cherrypy.server.socket_port = 8084
config = {
"/img": {
"tools.staticdir.on": True,
"tools.staticdir.dir": os.path.join(file_path, "img"),
}
}
cherrypy.tree.mount(ccmf(), "/", config=config)
cherrypy.engine.start()
cherrypy.engine.block()
and here's the example python script b1.py:
def b1():
op = "ajax b1 pushed"
print op
return op
b1()
The ajax get's called but returns the failure alert. I have tried GET, POST, "text", "html", b1.py is in the same directory, no joy. All currently running on my local box.
Any hints greatly appreciated!
You are completely misunderstanding how modern, CherryPy's for instance, routing works. Unlike outdated approaches that were commonly employed with CGI and Apache's mod_* (mod_php, mod_python, etc.), where you directly point to the file containing the script with URL, modern routing is an application level activity.
Your application receives all requests and dispatches them according to the established method. CherryPy in that sense has two major approaches: built-in object tree dispatcher and Routes adapter. For most simple and mid-level cases built-in dispatcher is fair enough.
Basically it can look like this.
app.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import cherrypy
from cherrypy.lib.static import serve_file
path = os.path.abspath(os.path.dirname(__file__))
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
}
}
class App:
#cherrypy.expose
def index(self):
return serve_file(os.path.join(path, 'index.html'))
#cherrypy.expose
#cherrypy.tools.json_out()
def getData(self):
return {
'foo' : 'bar',
'baz' : 'another one'
}
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='content-type' content='text/html; charset=utf-8'>
<title>CCMF</title>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
<script type='text/javascript'>
$(document).ready(function()
{
$('button').on('click', function()
{
var request = $.ajax({'url': '/getData'});
request.done(function(response)
{
$('#foo').text(response.foo);
$('#baz').text(response.baz);
});
request.fail(function(jqXHR, textStatus)
{
alert('Request failed: ' + textStatus);
});
})
});
</script>
</head>
<body>
<button>make ajax call</button>
<h1>Foo</h1>
<div id='foo'></div>
<h1>Baz</h1>
<div id='baz'></div>
</body>
</html>
Related
for my first stack overflow question, I have been working on a socketio flask chat app tutorial found here codeburst chatapp tutorial
While trying to include the logging headers to debug this 504 error I began running into another Attribute error, this time around the socketio.run line of the main.py file- but the error starts at line 564 of the init.py file with the debug, however this 'eio' object appears in more places than that getting rid of the flask debug mode doesn't fix it.
I have also tried reinstalling flask-socketio and python-engineio. Then I deleted my virtual environment and created a fresh one- still the same eio attribute error.
After spending about 7 hours on this, I think the title of the tutorial should be revised;) Any help would be appreciated.
here is my main.py (with the added logging line commented out)
`app = Flask(__name__)
app.config['SECRET_KEY'] = 'vnkdjnfjknfl1232#'
socketio = SocketIO(app)
# socketio = SocketIO(logger=True, engineio_logger=True)
#app.route('/')
def sessions():
return render_template('session.html')
def messageReceived(methods=['GET', 'POST']):
print('message was received!!!')
#socketio.on('my event')
def handle_my_custom_event(json, methods=['GET', 'POST']):
print('received my event: ' + str(json))
socketio.emit('my response', json, callback=messageReceived)
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', debug=True)`
hopefully that entered properly, and now for the html part (called session.html here)
<!DOCTYPE html>
<html lang="en">
<head>
<title>Flask_Chat_App</title>
<p>bChat not weChat</p>
</head>
<body>
<h3 style='color: rgb(134, 0, 0);font-size: 30px;'>No message yet..</h3>
<div class="message_holder"></div>
<form action="" method="POST">
<input type="text" class="username" placeholder="User Name"/>
<input type="text" class="message" placeholder="Messages"/>
<input type="submit"/>
</form>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"></script>
<script src="https://cdn.socket.io/3.1.3/socket.io.min.js" integrity="sha384-cPwlPLvBTa3sKAgddT6krw0cJat7egBga3DJepJyrLl4Q9/5WLra3rrnMcyTyOnh" crossorigin="anonymous"></script>
<script type="text/javascript">
var socket = io.connect('http://' + 192.168.1.138 + ':' + location.port);
socket.on( 'connect', function() {
socket.emit( 'my event', {
data: 'User Connected'
} )
var form = $( 'form' ).on( 'submit', function( e ) {
e.preventDefault()
let user_name = $( 'input.username' ).val()
let user_input = $( 'input.message' ).val()
socket.emit( 'my event', {
user_name : user_name,
message : user_input
} )
$( 'input.message' ).val( '' ).focus()
} )
} )
socket.on( 'my response', function( msg ) {
console.log( msg )
if( typeof msg.user_name !== 'undefined' ) {
$( 'h3' ).remove()
$( 'div.message_holder' ).append( '<div><b style="color: #000">'+msg.user_name+'</b> '+msg.message+'</div>' )
}
})
</script>
</body>
</html>
And here is the freeze of my pip for my virtual environment
(bChat) pi#raspberrypi:~/bChat $ python -m pip freeze
bidict==0.21.4
click==8.0.4
dnspython==2.2.0
eventlet==0.33.0
Flask==2.0.3
Flask-SocketIO==5.1.1
greenlet==1.1.2
itsdangerous==2.1.0
Jinja2==3.0.3
MarkupSafe==2.1.0
python-engineio==4.3.1
python-socketio==5.5.2
RPi.GPIO==0.7.1
six==1.16.0
Werkzeug==2.0.3
(bChat) pi#raspberrypi:~/bChat $
thanks again for your time. The ultimate goal is to make a near realtime checkbox updater where each checkbox turns a neon light on (checked) and off (unchecked). A friend recommended the socketio chat app. Baby steps.
I'm quite a beginner and I've been looking for an answer for days now..
I am building a webapp using nodejs.
Here is the app.js I node to start (my server-side)
http = require("http"),
path = require("path"),
url = require("url"),
fs = require("fs");
function sendError(errCode, errString, response)
{
response.writeHead(errCode, {"Content-Type": "text/plain"});
response.write(errString + "\n");
response.end();
return;
}
function sendFile(err, file, response)
{
if(err) return sendError(500, err, response);
response.writeHead(200);
response.write(file, "binary");
response.end();
}
function getFile(exists, response, localpath)
{
if(!exists) return sendError(404, '404 Not Found', response);
fs.readFile(localpath, "binary",
function(err, file){ sendFile(err, file, response);});
}
function getFilename(request, response)
{
var urlpath = url.parse(request.url).pathname; // following domain or IP and port
var localpath = path.join(process.cwd(), urlpath); // if we are at root
fs.exists(localpath, function(result) { getFile(result, response, localpath)});
}
var server = http.createServer(getFilename);
server.listen(1000);
console.log("Server available...");
I can therefore access my app using localhost:1000/index.html
My index.html (client-side) is a html page containing buttons i want to use to start a python script.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>Lights</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
</head>
<body class="text-white bg-dark">
<center>
<input type="submit" value="OFF" name="OFF" onclick="turnOff()">
<input type="submit" value="ON" name="ON" onclick="turnOn()">
</center>
<div id="texttest"> </div>
<div id="fil"> </div>
<script src="funcs.js"></script>
</body>
</html>
from there you can see i use a script to run the "onclick()"
this script is calling a python run.. But obviously I can't do that 'cause I am trying to run python client side.. ERROR "require not definded"
my funcs.js
const { exec } = require('child_process');
function turnOff() {
console.log("enter Off");
exec('python3 turnOff.py', (err, stdout, stderr) => {
if (err) {
document.getElementById("fil").innerHTML = "error..."+err;
} else {
document.getElementById("fil").innerHTML = "running python...";
}
});
document.getElementById("texttest").innerHTML = "trying off sucess :<br> " ;
}
function turnOn() {
console.log("enter On");
exec('python3 turnOn.py', (err, stdout, stderr) => {
if (err) {
document.getElementById("fil").innerHTML = "error..."+err;
} else {
document.getElementById("fil").innerHTML = "running python...";
}
});
document.getElementById("texttest").innerHTML = "trying on sucess :<br> " ;
}
I should be using AJAX request, but i can't seem to find how to run python on server side using it.. because the ajax request is sent from html inside nodejs..
What can I do ?
Thanks for your help
You're right about your errors
require is defined on server side only.
funcs.js is executed on client side, so you can't use require.
And you'll need to make an AJAX call in order to reach your goal.
// server.js
const { exec } = require('child_process');
const http = require('http');
const server = http.createServer((req, res) => {
/* When a request will call the http://localhost:5000/run url
it will execute this code block */
if (req.url === '/run') {
exec('python ...', () => {
// ...
res.write('Python script executed');
res.end();
});
}
});
server.listen(5000);
// funcs.js
function turnOff() {
console.log("enter Off");
// Turn off will send a request on the http://localhost:5000/run endpoint
fetch("/run", {
method: "POST"
})
.then(() => {
document.getElementById("fil").innerHTML = "running python...";
})
.catch(() => {
document.getElementById("fil").innerHTML = "error..."+err;
})
}
I have a simple Flask app like so:
import msgpack
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/test')
def test():
return msgpack.packb([1, 2, 3])
#app.route('/ping')
def ping():
return "Pong"
if __name__ == '__main__':
app.run(debug=True, port=5000)
In the Flask route /test, I'm using msgpack to pack an array which is returned as a response. When this response is printed to the console, it looks something like this: b'\x93\x01\x02\x03'
I'm using AngularJS 1.7 in my front end which looks like:
<!doctype html>
<html ng-app="MyApp">
<body ng-cloak>
<div class="container" ng-controller="MyController">
<h1>Hello, {{text}}</h1>
<button class="btn btn-primary" ng-click="ping()">Ping</button>
<button class="btn btn-primary" ng-click="getMsgpackData()">Get Msgpack Data</button>
</div>
<script src="jquery-3.3.1.slim.min.js"></script>
<script src="/angular.js/1.7.2/angular.min.js"></script>
<script src="https://rawgithub.com/kawanet/msgpack-lite/master/dist/msgpack.min.js"></script>
<script>
var myApp = angular.module("MyApp", []);
myApp.controller("MyController", ["$scope", "$http", function($scope, $http) {
$scope.text = "World";
$scope.ping = function() {
$http.get("/ping").then(function success(response) {
$scope.text = response.data;
}, function error(response) {
console.log(response);
});
}
$scope.getMsgpackData = function() {
$http.get("/test").then(function success(response) {
console.log(response);
$scope.text = msgpack.decode(response.data);
}, function error(response) {
console.log(response);
});
}
}]);
</script>
</body>
</html>
When the getMsgpackData function is invoked in MyController, I get the following error in the browser's console:
Error: Invalid type: 0x�
at n.r [as decode] (msgpack.min.js:1)
at n.fetch (msgpack.min.js:1)
at n.s [as read] (msgpack.min.js:1)
at Object.n [as decode] (msgpack.min.js:1)
at success ((index):46)
at angular.js:17396
at m.$digest (angular.js:18557)
at m.$apply (angular.js:18945)
at k (angular.js:12799)
at V (angular.js:13056) "Possibly unhandled rejection: {}"
It seems like the first HEX value \x93 is not being decoded.
I'm using kawanet/msgpack-lite in the front end as I found that it can be used in a browser.
Please help!
The error message tells you you sent the wrong type of object to the decode() method. The buffer decoding documentation shows that only buffers, arrays and Uint8Array objects are supported.
Change the response type to arraybuffer (from the default text), then feed the response data to msgpack.decode() as a Uint8Array typed array:
$http.get("/test", {responseType: "arraybuffer"}).then(function success(response) {
var arr = new Uint8Array(response.data);
console.log(arr);
$scope.text = msgpack.decode(arr);
}, function error(response) {
console.log(response);
});
While not necessary here, you do want to set a different content type on your Flask response. Currently the /test route is advertising the content as text/html, while you really should be labelling as application/msgpack:
#app.route('/test')
def test():
return msgpack.packb([1, 2, 3]), {'content-type': 'application/msgpack'}
I am writing a small web app to test my understanding of integrating Angular 1 and Flask. When I try to send output from my Python function to the URL Localhost/synthesize_data, I see a Flask error: 'NoneType Object is not Subscriptable." It is trying to run my Python script again, but I only want it to show the output that the function should already have generated.
HTML with Angular:
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<script src="http://code.angularjs.org/1.5.3/angular-route.min.js"></script>
<script>
var myApp = angular.module('myApp', [
'ngRoute',
]);
myApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: '/static/partials/index.html',
}).
otherwise({
redirectTo: '/'
});
}]);
myApp.controller('formController', ['$scope', '$http', function($scope, $http) {
console.log('HA');
$scope.formData = {};
$scope.processForm=function() {
console.log("righthere")
$http({
method: 'POST',
url : '/synthesize_data',
data : $scope.formData,
headers: {'Content-Type': 'application/json'}
})
.success(function(data){
console.log(data);
})
};
}])
</script>
<!--<link rel="stylesheet" href="/static/css/style.css" />-->
</head>
<body ng-app="myApp" ng-controller="formController">
<h1>Data Synthesizer Startup Page</h1>
<div>
<form ng-submit = "processForm()">
<div id = "name-group" class = "form-group">
<label>Number of Rows to Create </label>
<input type = "text" name = "name" class = "form-control" placeholder = "Enter valid number input" ng-model = "formData.name">
<span class = "help-block"></span>
</div>
<button type = "submit" class = "btn btn-success btn-lg btn-block">
<span class = "glyphicon glyphicon-flash"></span> Submit
</button>
</form>
<!--<div ng-view></div>-->
</body>
</html>
Python code (initialize program with hello()):
from flask import Flask, send_file, request
from data_synthesis import *
app = Flask(__name__)
#app.route("/synthesize_data", methods=['GET', 'POST'])
def datasynthesize():
print ("Calling synthesizer for ", request.json['name'], " rows.")
main(int(request.json['name']))
return "TEST"
#app.route("/")
def hello():
print ("Python up and running.")
return send_file("templates/start.html")
if __name__ == "__main__":
app.run(debug=True)
What I want is to see "TEST" on the screen when I run this program and navigate to Localhost/synthesize_data. Instead, it attempts to run my Python program again without proper input, which throws the error.
To only execute the program when it has submitted form data you need to add a conditional into the view function. The code the way you have it is dependent on having the name value as part of the request.
Request.json is None if the mimetype is not application/json. This is what is throwing the 'NoneType' Object is not Subscriptable error when you're issue a GET request (e.g. when you navigate to localhost/synthesize_data). A better way of accomplishing this, per the Docs, is to use Request.get_json():
#app.route("/synthesize_data", methods=['GET', 'POST'])
def datasynthesize():
data = request.get_json()
if data:
print ("Calling synthesizer for ", data['name'], " rows.")
main(int(data['name']))
return "TEST"
here is my simple html, when I open the file directly, there is no issue
<html>
<head>
<meta charset="utf-8">a
<title>Demo</title>
</head>
<body>
jQuery
<script src="jquery.js"></script>
<script>
$( document ).ready(function() {
$( "a" ).click(function( event ) {
alert( "The link will no longer take you to jquery.com" );
event.preventDefault();
});
});
</script>
</body>
</html>
However, if i input the http://localhost:8000/ tornaod gives me the error that WARNING:tornado.access:404 GET /jquery.js (::1) 3.00ms
following is my simple tornado code...I am not sure what wrong with my code...
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render("./pages/index.html")
app = tornado.web.Application([(r'/test1', Test1Handler),
(r'/test2', Test2Handler),
(r'/test3', Test3Handler),
(r'/', IndexHandler)],
debug=True)
app.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
From what I see, you have only 4 paths specified to be accessible via Tornado: /test1, /test2, test3 and /. There is no path specified to access /jquery.js.
Theck this question to see how to serve static files:
Using Tornado, how do I serve static files and serve a favicon.ico from a different directory than the static path?