Using an HTML button to run an python script on a Raspberry - python

I'm trying to make the following. I want to make a remote control for powering on/off my NAS. See: https://www.youtube.com/watch?v=bDtPBzR6FF0 I didn't want to control it by knobs, but by a HTML button. I want to use a Raspberry Pi to host a website with the buttons.
I tried https://raspberrypi.stackexchange.com/questions/66908/how-to-call-and-run-python-file-on-raspberry-pi-using-web-based-button.
#!/usr/bin/python
# coding: utf-8
import cherrypy
import subprocess
text = """
<html><body>
<form method='get' action='do_it'>
<input type='submit' value='Submit' />
</form></body>
{}
</html>
"""
class PiButton(object):
#cherrypy.expose
def index(self):
return text.format("")
#cherrypy.expose
def do_it(self, *vargs, **kwargs):
#command = "ls /"
command = "python my_other_python.script.py"
result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8').replace('\n', '<br>')
result2 = "command: {!r}<br>result:<br>{}".format(command, result)
return text.format(result2)
if __name__ == "__main__":
cherrypy.engine.autoreload.unsubscribe()
cherrypy.config.update({'server.socket_host': "0.0.0.0", 'server.socket_port': 8181})
cherrypy.quickstart(PiButton(), '/', {'':{}})
It gave an error:
Traceback (most recent call last):
File "button.py", line 17, in
class PiButton(object):
File "button.py", line 34, in PiButton
cherrypy.quickstart(PiButton(), '/', {'':{}})
NameError: name 'PiButton' is not defined
I'm quit new to Python?Raspberry and I don't quite understand what goes wrong. Anyone can tell me what the right solution can be?

Related

Basic CGI Example Help in Python

I'm working through Programming Python and am trying to work through the basic CGI example. I so far have the following files saved in the same directory (titled: cgi101.html and webserver.py respectively):
<html>
<title>Interactive Page</title>
<body>
<form method=POST action="cgi-bin/cgi101.py">
<p><b>Enter your name:</b>
<p><input type=text name=user>
<p><input type=submit>
</form>
</body>
import os, sys
from http.server import HTTPServer, CGIHTTPRequestHandler
webdir = '.'
port = 8080
os.chdir(webdir)
srvraddr = ("", port)
srvrobj = HTTPServer(srvraddr, CGIHTTPRequestHandler)
srvrobj.serve_forever()
Then in a sub directory titled cgi-bin I have the following script called cgi101.py saved:
#!/usr/bin/python
import cgi
form = cgi.FieldStorage()
print('Content-type: text/html\n')
print('<title>Reply Page</title>')
if not 'user' in form:
print('<h1>Who are you?</h1>')
else:
print('<h1>Hello <i>%s</i>!</h1>' % cgi.escape(form['user'].value))
I then follow these steps:
Run webserver.py from the directory where webserver.py and cgi101.html are located
Navigate to http://localhost:8080/cgi101.html this opens up as expected
When I enter a name and submit, initially I get an error saying the cgi script is not executable. If I go to cgi101.py and then run chmod 744 to make it executable, when I repeat the above steps the following URL renders: http://localhost:8080/cgi-bin/cgi101.py but no content renders whatsoever and the command line where I ran the server gives a FileNotFound error.
What am I doing wrong?
Any help appreciated.
Richard

Issue with Popen running as another user (macOS)

I have a python script that is being run on some Macs by my MDM tool. This means that the script is being run as root. There is a part of the script I need to run as the currently logged in local user on the account. I found this article below for using Popen to do this:
Run child processes as a different user from a long-running process
However, I am getting an error when I attempt to use this method on any pre macOS 10.13 computers. These are still modern OS versions such as 10.12 and 10.11. I have not been able to track this error down. Please see the code below.
Note: There are likely some extra import statements as this is pulled from a larger script. This snippet should work as-is.
#!/usr/bin/python
import subprocess
import platform
import os
import pwd
import sys
import hashlib
import plistlib
import time
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
from distutils.version import StrictVersion as SV
def getLoggedInUserUID():
userUID = SCDynamicStoreCopyConsoleUser(None, None, None)[1]
return userUID
def getLoggedInUsername():
username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]
return username
def getLoggedInUserGID():
username = getLoggedInUsername()
pwRecord = pwd.getpwnam(username)
userGID = pwRecord.pw_gid
return userGID
def getLoggedInUserHomeDir():
username = getLoggedInUsername()
pwRecord = pwd.getpwnam(username)
homeDir = pwRecord.pw_dir
return homeDir
def demote():
def result():
os.setgid(getLoggedInUserGID())
os.setuid(getLoggedInUserUID())
return result
def setupEnvironment():
environment = os.environ.copy()
environment['HOME'] = str(getLoggedInUserHomeDir())
environment['LOGNAME'] = str(getLoggedInUsername())
environment['PWD'] = str(getLoggedInUserHomeDir())
environment['USER'] = str(getLoggedInUsername())
return environment
def launchCommand():
command = ['echo', 'whoami']
process = subprocess.Popen(command,
stdout=subprocess.PIPE,
preexec_fn=demote(),
cwd=str(getLoggedInUserHomeDir()),
env=setupEnvironment())
def main():
launchCommand()
if __name__== "__main__":
main()
The error that I get is:
Traceback (most recent call last):
File "testScript.py", line 60, in <module>
main()
File "testScript.py", line 57, in main
launchCommand()
File "testScript.py", line 54, in launchCommand
env=setupEnvironment())
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 710, in __init__
errread, errwrite)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1335, in _execute_child
raise child_exception
KeyError: 'getpwnam(): name not found: '
It looks like it is missing some key value but I cannot for the like of me figure out what it is. Any help in tracking this down so I can run the command as the logged in user would help greatly.
Thanks in Advance,
Ed
The way that I did in my small mdm managed environment is like this:
I developed small windowless LoginItem helperApp that starts for every open user session on the mac and listens for custom distributed system notification. It also has a function for executing terminal commands without showing terminal window (You can find examples for this on stackowerflow).
I transmit to all apps currently running on the system two params in the notification: an username and a terminal command string. All of the users running instances get the notification, than they check the username for if they run in that user and the one that does - executes the command in that users name.
Try this if it fits your requirement.

Default the root view in cherrypy

In some source code I am
writing, I am able to make a request such as:
http://proxy.metaperl.org/index/bitgold-rw1
And have it redirect successfully.
However, I want to remove index from the URL and have it still
redirect by using the index() method. I tried renaming index() to
default() after reading about
Dispatching,
but it still does not allow me to have a URL like this:
http://proxy.metaperl.org/bitgold-rw1
It tries to find a method named bitgold-rw1 instead of using the
default method to resolve the request, gving me the error:
NotFound: (404, "The path '/bitgold-rw1' was not found.")
The WSGI startup file looks like this:
# -*- python -*-
# core
import os
import sys
# 3rd party
import cherrypy
# local
def full_path(*extra):
return os.path.join(os.path.dirname(__file__), *extra)
sys.path.insert(0, full_path())
import config
import myapp
application = cherrypy.Application(
myapp.Root(),
"/",
config.config)
As mentioned by #ralhei #saaj default method is the key if you do not want to deal with dispatchers in cherrypy. I tried the code below and working as you want
class Root(object):
#cherrypy.expose
def index(self, tag):
redirect_url = db.urls[tag]
ip = cherrypy.request.headers['Remote-Addr']
request_url = 'http://ipinfo.io/{0}/country'.format(ip)
r = requests.get(request_url)
country = r.text.strip()
raise cherrypy.HTTPRedirect(redirect_url)
#cherrypy.expose
def default(self,tag):
return self.index(tag)
Renaming to default is not enough. It needs to be callable at least with variadic arguments, *args, to receive path segments. Like this:
#!/usr/bin/env python3
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
}
}
class Root:
#cherrypy.expose
def default(self, *args, **kwargs):
cherrypy.log('{0}, {1}'.format(args, kwargs))
return 'OK'
if __name__ == '__main__':
cherrypy.quickstart(Root(), '/', config)
Then it will catch things like http://127.0.0.1:8080/bitgold-rw1/ and also like http://127.0.0.1:8080/bitgold-rw1/foo/bar.
And btw, if it's about MVC it's a controller, not a view.
if you rename your index method to 'default' in your Root class this should work.
Add the line
cherrypy.quickstart(Root())
at the bottom of myapp.py and run it with 'python myapp.py' your server should startup and listen on port 8080.
Making a request to http://localhost:8080/bitgold-rw1 works for me, it complains that I'm not a US citizen which I guess is fine ;-)

Python multiprocessing / Queue with cherrypy

I am tying to do a python code with cherrypy. The objective is to call a script from UI and while the script generates the logs on console print it on the UI (Iframe / div).
I am using CherryPy-3.2.4 and Python 2.7.
Following is the sample code I have written which is refusing to work.
TestProcess.py
from multiprocessing import Process, Queue
import subprocess
import os, os.path
from string import Template
import cherrypy
from multiprocessing import Process, Queue
import HTML
import TestScript
class TestProcess(object):
PID=0
jquery_url = 'C:\CherryPy\TestScripts\jquery\jquery-1.11.1.js'
q=Queue()
#cherrypy.expose
def index(self):
html = """\
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE> Test Doc </TITLE>
<script type="text/javascript" src="/static/jq/jquery-1.11.1.js"></script>
</HEAD>
<BODY>
<BR/>
<h3>Test utility</h3>
<form id="ping_form" target="console_iframe" method="post" action="/f">
<button id="ping" type="submit">Upgrade Installed Releases</button>
</form>
<BR/>
<iframe name="console_iframe" frameborder="1" width="1200"/>
</BODY>
</HTML>
"""
t = Template(html)
page = t.substitute(jquery_url=self.jquery_url)
return page
def f1():
print "*********** TEST **********************"
q.put([42, None, 'hello'])
#cherrypy.expose
def f(self, **kw):
q=Queue()
ts = TestScript.TestScript()
p = Process(target=ts.testCallFunction, args=(q,))
p.start()
#print q.get() # prints "[42, None, 'hello']"
p.join()
print "Test F"
def run_command():
# The yeilds here are the key to keeping things streaming
yield '<style>body {font-family: monospace;}</style>'
while(p.is_alive()):
while(not q.empty()):
yield q.get_nowait()
while(not q.empty()):
yield q.get_nowait()
return run_command()
if __name__ == '__main__':
conf = {
'/': {
'tools.sessions.on': True,
'tools.staticdir.root': os.path.abspath(os.getcwd())
},
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.dir': './public'
}
}
q = Queue()
cherrypy.server.socket_host = '10.49.69.103'
cherrypy.config.update({
'log.screen':True,
'tools.sessions.on': True,
'checker.on':False
})
cherrypy.tree.mount(TestProcess(), config=None)
cherrypy.engine.start()
cherrypy.engine.block()
#cherrypy.quickstart(TestProcess(), '/', conf)
Class TestScript
class TestScript(object):
q=Queue()
def write_queue(self,data):
print "*********** TEST **********************"
scroll_to_bottom = '<script type="text/javascript">window.scrollBy(0,50);</script>'
if data == '\n':
self.q.put("\n<br />%s" % scroll_to_bottom) # include the iframe scroll fix
else:
self.q.put(data)
def testCallFunction(self, q):
#proc = subprocess.Popen(['python','CreateLog.py'],stdout=subprocess.PIPE)
cmd = subprocess.Popen(['python','CreateLog.py'], shell=True, stdout=subprocess.PIPE)
while True:
data = cmd.stdout.read(1) # Alternatively proc.stdout.read(1024)
if len(data) == 0:
break
self.write_queue(data) # sys.stdout.buffer.write(data) on Python 3.x
#print "*********** TEST **********************"
script CreateLog.py
#filters output
import time
i = 0
while (i<=10):
print hex(i)*512
i += 1
time.sleep(0.5)
Error That I get while running it
[24/Jul/2014:16:59:10] ENGINE Bus STARTING
[24/Jul/2014:16:59:10] ENGINE Started monitor thread 'Autoreloader'.
[24/Jul/2014:16:59:10] ENGINE Started monitor thread '_TimeoutMonitor'.
[24/Jul/2014:16:59:15] ENGINE Error in 'start' listener <bound method Server.sta
rt of <cherrypy._cpserver.Server object at 0x01583390>>
Traceback (most recent call last):
File "c:\Python27\lib\site-packages\cherrypy\process\wspbus.py", line 197, in
publish
output.append(listener(*args, **kwargs))
File "c:\Python27\lib\site-packages\cherrypy\_cpserver.py", line 151, in start
ServerAdapter.start(self)
File "c:\Python27\lib\site-packages\cherrypy\process\servers.py", line 168, in
start
wait_for_free_port(*self.bind_addr)
File "c:\Python27\lib\site-packages\cherrypy\process\servers.py", line 412, in
wait_for_free_port
raise IOError("Port %r not free on %r" % (port, host))
IOError: Port 8080 not free on '10.49.69.103'
[24/Jul/2014:16:59:15] ENGINE Shutting down due to error in start listener:
Traceback (most recent call last):
File "c:\Python27\lib\site-packages\cherrypy\process\wspbus.py", line 235, in
start
self.publish('start')
File "c:\Python27\lib\site-packages\cherrypy\process\wspbus.py", line 215, in
publish
raise exc
ChannelFailures: IOError("Port 8080 not free on '10.49.69.103'",)
What am I doing wrong ?
See 16.6.3.2. Windows:
Make sure that the main module can be safely imported by a new Python interpreter without causing unintended side effects (such a starting a new process).
You did not protect the last lines of TestProcess.py with if __name__ == '__main__':, so the Process attempts to run another server.
Port 8080 is not free, so the HTTP server can't listen on it. Try netstat -anpt to see what's using that port, or try "server.socket_port: 8081" or some other port that is free.

unittest - Importerror

I'm following this tutorial on web2py where you get to make a testdriven environment. However when I try to run the test with unittest, selenium I get this error:
$ python functional_tests.py
running tests
Traceback (most recent call last):
File "functional_tests.py", line 56, in <module>
run_functional_tests()
File "functional_tests.py", line 46, in run_functional_tests
tests = unittest.defaultTestLoader.discover('fts')
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/loader.py", line 202, in discover
raise ImportError('Start directory is not importable: %r' % start_dir)
ImportError: Start directory is not importable: 'fts'
This is how the functional_tests.py looks like:
#!/usr/bin/env python
try: import unittest2 as unittest #for Python <= 2.6
except: import unittest
import sys, urllib2
sys.path.append('./fts/lib')
from selenium import webdriver
import subprocess
import sys
import os.path
ROOT = 'http://localhost:8001'
class FunctionalTest(unittest.TestCase):
#classmethod
def setUpClass(self):
self.web2py = start_web2py_server()
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(1)
#classmethod
def tearDownClass(self):
self.browser.close()
self.web2py.kill()
def get_response_code(self, url):
"""Returns the response code of the given url
url the url to check for
return the response code of the given url
"""
handler = urllib2.urlopen(url)
return handler.getcode()
def start_web2py_server():
#noreload ensures single process
print os.path.curdir
return subprocess.Popen([
'python', '../../web2py.py', 'runserver', '-a "passwd"', '-p 8001'
])
def run_functional_tests(pattern=None):
print 'running tests'
if pattern is None:
tests = unittest.defaultTestLoader.discover('fts')
else:
pattern_with_globs = '*%s*' % (pattern,)
tests = unittest.defaultTestLoader.discover('fts', pattern=pattern_with_globs)
runner = unittest.TextTestRunner()
runner.run(tests)
if __name__ == '__main__':
if len(sys.argv) == 1:
run_functional_tests()
else:
run_functional_tests(pattern=sys.argv[1])
I solved this problem by replacing fts with the full path i.e. /home/simon/web2py/applications/testapp/fts
Hope this helps
I had the same problem and based on an excellent article unit testing with web2py,
I got this to work by doing the following:
Create a tests folder in the tukker directory
Copy/save the amended code(below) into tests folder as alt_functional_tests.py
Alter the web2py path in the start_web2py_server function to your own path
To run, enter the command: python web2py.py -S tukker -M -R applications/tukker/tests/alt_functional_tests.py
I am no expert but hopefully this will work for you also.
import unittest
from selenium import webdriver
import subprocess
import urllib2
execfile("applications/tukker/controllers/default.py", globals())
ROOT = 'http://localhost:8001'
def start_web2py_server():
return subprocess.Popen([
'python', '/home/alan/web2py/web2py/web2py.py', 'runserver',
'-a "passwd"', '-p 8001' ])
class FunctionalTest(unittest.TestCase):
#classmethod
def setUpClass(self):
self.web2py = start_web2py_server()
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(1)
#classmethod
def tearDownClass(self):
self.browser.close()
self.web2py.kill()
def get_response_code(self, url):
"""Returns the response code of the given url
url the url to check for
return the response code of the given url
"""
handler = urllib2.urlopen(url)
return handler.getcode()
def test_can_view_home_page(self):
# John opens his browser and goes to the home-page of the tukker app
self.browser.get(ROOT + '/tukker/')
# He's looking at homepage and sees Heading "Messages With 300 Chars"
body = self.browser.find_element_by_tag_name('body')
self.assertIn('Messages With 300 Chars', body.text)
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(FunctionalTest))
unittest.TextTestRunner(verbosity=2).run(suite)
First you have to do some changes in wrong paths in fts/functional_tests.py
search for
'python', '../../web2py.py', 'runserver', '-a "passwd"', '-p 8001'
and change it to
'python', '../../../web2py.py', 'runserver', '-a "passwd"', '-p 8001'
then
tests = unittest.defaultTestLoader.discover('fts')
to
tests = unittest.defaultTestLoader.discover('.')
then
tests = unittest.defaultTestLoader.discover('fts', pattern=pattern_with_globs)
to
tests = unittest.defaultTestLoader.discover('.', pattern=pattern_with_globs)
and
sys.path.append('fts/lib')
to
sys.path.append('./lib')

Categories

Resources