Nieuws:

Welkom, Gast. Alsjeblieft inloggen of registreren.
Heb je de activerings-mail niet ontvangen?

Auteur Topic: Python webprogrammeren time-outs  (gelezen 3200 keer)

Offline MKe

  • Lid
Python webprogrammeren time-outs
« Gepost op: 2010/08/19, 15:09:08 »
Hoi,

Ik ben bezig met het maken van een web-applicatie in python. In het formulier van de pagina kan de gebruiker data invoeren. Om dit te bewerken wordt deze data vervolgens door het python-script doorgegeven aan een extern programma, die gestart wordt met os.system. De bewerking kan soms erg lang duren. Al die tijd staat het python script te wachten tot de bewerking voltooid is. Dit betekent dat de gebruiker in tussentijd geen input krijgt en denkt dat er niets gebeurt. Wat nog veel erger is is dat als het lang duurt er een time-out error komt en het script helemaal niet meer voltooid wordt. Ik kan de time-out tijd oprekken, maar dat heeft natuurlijk een grens.
Wat is de beste manier om dit op te lossen en hoe doe ik dat?

Offline Ronnie

  • Lid
    • ronnie.vd.c
Re: Python webprogrammeren time-outs
« Reactie #1 Gepost op: 2010/08/19, 15:35:04 »
Voor dit soort problemen zou ik eens naar technieken als Websockets en Comet kijken.

Beide technieken zijn bedoeld om vanuit de server data naar de browser te sturen, zonder dat de browser om data vraagt. Comet zal op alle browsers werken (maar is lastiger om te implementeren en is soort van hack i.p.v. een techniek), terwijl websockets hiervoor speciaal bedoeld is, maar alleen in de allernieuwste browsers zal werken (HTML5 draft - nog niet af dus)

Zelf ben ik een tijd bezig geweest met de webserver tornado, welke gemaakt is in python en een module heeft voor websockets. Hiermee kom ik een simpel chatprogramma maken, waar de server zodra deze een nieuw bericht ontvangt, meteen naar de browser door kan zenden.

Wat je verder in python moet regelen zodat het script niet blocked zolang het process bezig is, weet ik niet. Maar dat zal wel iets standaard zijn in os.system().
« Laatst bewerkt op: 2010/08/19, 15:37:31 door Ronnie »
Ben je ook blij dat Ubuntu zo toegankelijk en gratis is, en wil je graag net als ik iets terugdoen, kijk dan eens rond bij mwanzo, dé poort naar het bijdragen aan Ubuntu en haar gemeenschap!

Documentatie Terminal

Offline Rachid

  • Lid
    • rachidbm
    • Mijn blog
Re: Python webprogrammeren time-outs
« Reactie #2 Gepost op: 2010/08/19, 15:57:44 »
Niet geheel ontopic, maar ik wil dit wel even kwijt.
Als je invoer van een website doorgeeft aan commando's op je OS pas dan aub HEEEL goed op voor kwaadaardige invoer. Invoervalidatie is sowieso belangrijk, maar ik vind hier extra voorzichtigheid wel op zijn plaats. Dit is nummer 9 van de meest gemaakte programmeerfouten: http://cwe.mitre.org/data/definitions/78.html Kijk even bij "Example"

Let trouwens ook op met welke rechten je dat python script uitvoert, want die rechten heeft een eventuele aanvaller dan ook. Ik mag aannemen dat het logisch is dat je zo'n script nooit als root wilt uitvoeren.
Ben je ook blij dat Ubuntu zo toegankelijk en gratis is, en wil je graag net als ik iets terugdoen, kijk dan eens rond bij mwanzo, dé poort naar het bijdragen aan Ubuntu en haar gemeenschap!

Offline MKe

  • Lid
Re: Python webprogrammeren time-outs
« Reactie #3 Gepost op: 2010/08/19, 16:24:10 »
Dat niet blocken is te regelen met multithreading. Dat is op zich geen probleem, maar ik loop tegen het probleem op dat de browser pas wat weergeeft op het moment dat het scriptje klaar is. Dus ik kan zoveel naar de browser printen wat ik wil tijdens het wachten m.b.v. een aparte thread, maar het enige wat ik dan krijg is dat de browser keurig wacht tot het totale script is afgelopen, waarna ik alle meldingen die de thread heeft gegenereerd in een keer onder elkaar in beeld krijg.
Ik zal eens kijken naar Comet en websockets. Het is me nog niet helemaal duidelijk wat het doet, maar ik zal eens kijken of daar een oplossing in zit.

Niet geheel ontopic, maar ik wil dit wel even kwijt.
Als je invoer van een website doorgeeft aan commando's op je OS pas dan aub HEEEL goed op voor kwaadaardige invoer. Invoervalidatie is sowieso belangrijk, maar ik vind hier extra voorzichtigheid wel op zijn plaats. Dit is nummer 9 van de meest gemaakte programmeerfouten: http://cwe.mitre.org/data/definitions/78.html Kijk even bij "Example"

Let trouwens ook op met welke rechten je dat python script uitvoert, want die rechten heeft een eventuele aanvaller dan ook. Ik mag aannemen dat het logisch is dat je zo'n script nooit als root wilt uitvoeren.
Opmerking genoteerd, al denk ik niet dat dat hier een probleem is. Ik ben namelijk bezig met iets wat blast heet, een programma dat gebruikt wordt in de bio-informatica. Om deze aan te sturen maak ik gebruik van Biopython en de data die de gebruiker invoerd wordt als parameter doorgegeven aan een module in dit pakket. Deze doet de vallidatie en Biopython zorgt vervolgens voor de goede syntax die naar het os wordt gestuurd. In principe kan de gebruiker dus niet het commando wijzigen. Verder gaat het hier om een intern netwerk, afgeschermd van de buitenwereld.

Offline Ronnie

  • Lid
    • ronnie.vd.c
Re: Python webprogrammeren time-outs
« Reactie #4 Gepost op: 2010/08/19, 16:42:19 »
Websockets gaat ongeveer op deze manier:

Browser->Server: Open websocket
Server->Browser: Websocket is open
Browser: Ok, stuur '<data>' naar de server
Server: Open proces os.open()
Server: krijgt tussendata van proces
Server->Browser: Hier heb je alvast wat data
Server: krijgt nog meer data vanuit proces
Server->Browser: Hier heb je nog meer data
...
Server: proces is klaar
Server->Browser: en hier heb je nog wat data
Browser: Kan de websocket afsluiten of weer een nieuw commando versturen (kan zelfs tussentijds als het andere commando nog verwerkt wordt)

In dit concept opent de browser dus één enkele maal de websocket. Daarna kan de server een oneindig aantal delen data naar de browser sturen, zonder timeout of dat de browser elke keer opnieuw om data moet vragen.


Comet:
Browser->Server: Hier heb je data, en laat de connectie voor x tijd open
Server: open os.system() en doe dan een while loop (die open blijft voor een x tijd en response geeft als het proces klaar is). In de while loop zit een sleep(korte tijd) ingebouwd, zodat de server niet overbelast wordt.
Server->Browser: Timeout
Browser->Server: Nieuwe request met vraag naar het antwoord (geen nieuwe data (berekening) wordt verzonden)
Server: Weer while loop
.......
Server: krijgt data van het proces, en geeft het aan de browser, omdat er nog een connectie tussen de server en browser actief is.
Browser: Zend geen nieuwe request omdat de data ontvangen is

Hierbij is er dus constant een verbinding tussen browser en server, waarvan op de server constant een while loop draait. Bij een timeout stuurt de browser gewoon weer een nieuwe request.
« Laatst bewerkt op: 2010/08/19, 16:49:52 door Ronnie »
Ben je ook blij dat Ubuntu zo toegankelijk en gratis is, en wil je graag net als ik iets terugdoen, kijk dan eens rond bij mwanzo, dé poort naar het bijdragen aan Ubuntu en haar gemeenschap!

Documentatie Terminal

Re: Python webprogrammeren time-outs
« Reactie #5 Gepost op: 2010/08/19, 16:43:36 »
Dat niet blocken is te regelen met multithreading. Dat is op zich geen probleem, maar ik loop tegen het probleem op dat de browser pas wat weergeeft op het moment dat het scriptje klaar is. Dus ik kan zoveel naar de browser printen wat ik wil tijdens het wachten m.b.v. een aparte thread, maar het enige wat ik dan krijg is dat de browser keurig wacht tot het totale script is afgelopen, waarna ik alle meldingen die de thread heeft gegenereerd in een keer onder elkaar in beeld krijg.

Dan is er iets mis met uw code lijkt me. Post eens de threading stukken als ge wilt.

Ook moet ik even zeggen dat os.system() en aanverwanten zoals os.popen() en de commands module vervangen worden/zijn door de subprocess module.

Offline MKe

  • Lid
Re: Python webprogrammeren time-outs
« Reactie #6 Gepost op: 2010/08/19, 20:09:23 »
Dat niet blocken is te regelen met multithreading. Dat is op zich geen probleem, maar ik loop tegen het probleem op dat de browser pas wat weergeeft op het moment dat het scriptje klaar is. Dus ik kan zoveel naar de browser printen wat ik wil tijdens het wachten m.b.v. een aparte thread, maar het enige wat ik dan krijg is dat de browser keurig wacht tot het totale script is afgelopen, waarna ik alle meldingen die de thread heeft gegenereerd in een keer onder elkaar in beeld krijg.

Dan is er iets mis met uw code lijkt me. Post eens de threading stukken als ge wilt.

Ook moet ik even zeggen dat os.system() en aanverwanten zoals os.popen() en de commands module vervangen worden/zijn door de subprocess module.

Hieronder een vereenvoudigde versie
#!/usr/bin/python
import threading
import time

class wachten(threading.Thread):
    #test voor wait report
    teller=0
    status=''
    def run(self):
        while True:
            if self.status=='klaar':break
            self.teller+=1
            print 'Content-Type: text/html\n'
            print
            print 'Wachten '+str(self.teller)
            time.sleep(2)
#thread starten
pauze=wachten()
pauze.start()

#om proces te emuleren is hier een wachtstap geplaatst.
#in het echt wordt hier de code uitgevoerd die lang duurt
time.sleep(10)
pauze.status='klaar'
#eind bericht:
print 'Content-Type: text/html\n'
print
print 'Programma afgelopen'
In de terminal werkt dit prima. Blijkbaar werkt het niet als web applicatie met een browser als output. De 'dump' naar de browser gebeurt pas als het script afgelopen is, dus in bovenstaande script na 10 seconden. In de browser staat dan:
Wachten 1 Content-Type: text/html Wachten 2 Content-Type: text/html Wachten 3 Content-Type: text/html Programma afgelopen
Websocket of Comet klinken veelbelovend, dus ik denk dat ik daar maar eens naar ga kijken. Klopt het dat websocket pas wordt ondersteund vanaf Firefox 4?
« Laatst bewerkt op: 2010/08/19, 20:15:50 door MKe »
Mijn blokkendoos blog: http://mke21.wordpress.com/

Offline Rachid

  • Lid
    • rachidbm
    • Mijn blog
Re: Python webprogrammeren time-outs
« Reactie #7 Gepost op: 2010/08/19, 20:15:12 »

In de terminal werkt dit prima. Blijkbaar werkt het niet als web applicatie met een browser als output. De 'dump' naar de browser gebeurt pas als het script afgelopen is, dus in bovenstaande script na 10 seconden. Het is dan een lijst van 5x de wacht boodschap en aan het eind de eind boodschap.
In php heb je zoiets als flush()
Als je dat aanroept wordt de huidige output naar de browser gestuurd, en gaat het script weer verder. Misschien heb je ook zoiets voor python?
Dat Websockets of Comet is wel idealer...
Ben je ook blij dat Ubuntu zo toegankelijk en gratis is, en wil je graag net als ik iets terugdoen, kijk dan eens rond bij mwanzo, dé poort naar het bijdragen aan Ubuntu en haar gemeenschap!

Offline MKe

  • Lid
Re: Python webprogrammeren time-outs
« Reactie #8 Gepost op: 2010/08/19, 20:21:09 »

In de terminal werkt dit prima. Blijkbaar werkt het niet als web applicatie met een browser als output. De 'dump' naar de browser gebeurt pas als het script afgelopen is, dus in bovenstaande script na 10 seconden. Het is dan een lijst van 5x de wacht boodschap en aan het eind de eind boodschap.
In php heb je zoiets als flush()
Als je dat aanroept wordt de huidige output naar de browser gestuurd, en gaat het script weer verder. Misschien heb je ook zoiets voor python?
Dat Websockets of Comet is wel idealer...
Ik heb de threading class als volgt aangepast:
class wachten(threading.Thread):
    #test voor wait report
    teller=0
    status=''
    def run(self):
        while True:
            if self.status=='klaar':break
            self.teller+=1
            print 'Content-Type: text/html\n'
            print
            print 'Wachten '+str(self.teller)
            sys.stdout.flush()
            time.sleep(2)
Maar het lijkt niet te werken.

Re: Python webprogrammeren time-outs
« Reactie #9 Gepost op: 2010/08/19, 20:22:06 »
Hier zit uw fout als ik het snel even bekijk:
#om proces te emuleren is hier een wachtstap geplaatst.
#in het echt wordt hier de code uitgevoerd die lang duurt
time.sleep(10)

Dit moet in de thread gebeuren, anders heeft die helemaal geen nut.

Offline MKe

  • Lid
Re: Python webprogrammeren time-outs
« Reactie #10 Gepost op: 2010/08/19, 20:30:37 »
Hier zit uw fout als ik het snel even bekijk:
#om proces te emuleren is hier een wachtstap geplaatst.
#in het echt wordt hier de code uitgevoerd die lang duurt
time.sleep(10)

Dit moet in de thread gebeuren, anders heeft die helemaal geen nut.
Hoezo? Het genereren van de wacht-berichten moet gedaan worden terwijl het script wacht op die sleep(10). Dan moet het toch in een aparte thread, die parallel loopt?

Re: Python webprogrammeren time-outs
« Reactie #11 Gepost op: 2010/08/19, 20:39:17 »
Ik zou eerder het lange proces in een thread zetten zodat deze het Python proces niet blokkeert, en dan deze thread pollen om de x seconden om te kijken of deze klaar is (en eventueel de voortgang melden).

Ik moet zodadelijk door, maar ik kan morgen eventueel wel even iets in elkaar zetten.

Edit: Toch nog even iets in elkaar gestoken.
#!/usr/bin/env python

import threading
import time

class WorkerThread(threading.Thread):
    def run(self):
        # Zet hier de langlopende taak
        time.sleep(10)

worker = WorkerThread()
worker.start()
while worker.is_alive():
    print "Het programma is bezig. Even geduld..."
    time.sleep(2)
« Laatst bewerkt op: 2010/08/19, 20:54:20 door Nunslaughter »

Offline MKe

  • Lid
Re: Python webprogrammeren time-outs
« Reactie #12 Gepost op: 2010/08/19, 21:02:02 »
Hoi,

Bedankt. Dit doet echter zo ongeveer hetzelfde als mijn script, maar wel een beetje netter:). In de terminal werkt dit prima, maar niet als een cgi script in een webomgeving.

Offline Ronnie

  • Lid
    • ronnie.vd.c
Re: Python webprogrammeren time-outs
« Reactie #13 Gepost op: 2010/08/20, 09:40:22 »
Ik heb even wat voor Tornado/Websocket in elkaar gezet, maar krijg het nog niet werkend. Ik heb nu even geen tijd om die bug te pletten, maar ik zal de code eens hier posten. Volgens mij is Chromium(Google Chrome) de enige webbrowser die websockets ondersteunt op dit moment.

EDIT:
Websockets  works already in the latest Webkit-browsers like Safari 5 and Chrome 5. Firefox 4 Beta 1 knows the Websocket-Object but it can't open the connection.

Ook heb je Tornado nodig, deze webserver doet zelf al het threading werk. En het websocket.py script. Deze moet je in de map: /usr/local/lib/python2.6/dist-packages/tornado/ zetten.

LongScript.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time

if __name__ == '__main__':
    print 'Starting long programm'
    for i in range(5):#300):
        print i
        time.sleep(1)
    print 'Long programm ended'

main.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import subprocess
import os

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.websocket
import tornado.locale

from tornado.options import define, options

define('port', default=8888, help='run on the given port', type=int)


class BiopythonServer(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r'/', MainHandler),
            (r'/websocket', BaseWebSocket),
        ]
        settings = dict(
            template_path=os.path.join(os.path.dirname(__file__), 'templates'),
        )
        tornado.web.Application.__init__(self, handlers, **settings)


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('command.html')


class BaseWebSocket(tornado.websocket.WebSocketHandler):
    def open(self):
        print 'WebSocket opened'

    def on_message(self, message):
        self.write_message(u'Starting Programm: ' + message)
        p = subprocess.Popen(['python', 'LongScript.py'], stdout=subprocess.PIPE)
        for i in p.stdout:
            if i:
                self.write_message(i)

    def on_close(self):
        print 'WebSocket closed'



def main():
    tornado.options.parse_command_line()
    http_server = tornado.httpserver.HTTPServer(BiopythonServer())
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()


if __name__ == "__main__":
    main()

templates/command.html
<!DOCTYPE html>

<html>
<head>
<title>BioPython</title>
<script src="http://www.google.com/jsapi"></script>
<script>google.load("jquery", "1.4.2")</script>
<script>
$(document).ready(function(){
$('#messages').append($('<p>').text('Opening websocket...'));
var ws = new WebSocket("ws://localhost:8888/websocket");
ws.onopen = function() {
$('#messages').append($('<p>').text('Websocket open'));
ws.send("Hello, world");
};
ws.onmessage = function (evt) {
$('#messages').append($('<p>').text(evt.data));
};
ws.onclose = function() {
$('#messages').append($('<p>').text('Websocket closed'));
}

$('form').submit(function(event){
event.preventDefault(); // Prevent normal submitting
var msg = $('input[name=cmd]', this).val();
$('#messages').append($('<p>').text('Sending ' + msg));
ws.send(msg);
});
});
</script>
</head>
<body>
<form method="POST" action="/">
<label>Commando</label>
<input type='text' name='cmd' />
<input type='submit' />
</form>
<div id="messages">
</div>
</body>
</html>

Starten doe je met het commando python main.py

EDIT: Code wat aangepast. Hij werkt nu in chrome 6.0 (beta repo), alleen worden de berichten pas over de websocket verzonden wanneer het hele proces klaar is.
« Laatst bewerkt op: 2010/08/20, 10:22:50 door Ronnie »
Ben je ook blij dat Ubuntu zo toegankelijk en gratis is, en wil je graag net als ik iets terugdoen, kijk dan eens rond bij mwanzo, dé poort naar het bijdragen aan Ubuntu en haar gemeenschap!

Documentatie Terminal

Re: Python webprogrammeren time-outs
« Reactie #14 Gepost op: 2010/08/20, 09:49:18 »
Bedankt. Dit doet echter zo ongeveer hetzelfde als mijn script, maar wel een beetje netter:).

Toch niet hoor. Gij zet gewoon een teller in een thread, maar ge moet de langlopende taak in de thread zetten, zodat deze het Python proces niet blokkeert.

Ik zal vandaag eens kijken of ik iets op mijn webserver in elkaar kan steken.

Offline Ronnie

  • Lid
    • ronnie.vd.c
Re: Python webprogrammeren time-outs
« Reactie #15 Gepost op: 2010/08/20, 16:10:38 »
Geeft de Python opdracht tussentijds antwoorden, of pas op het eind.

Als het alleen op het eind is (wil laten zien aan de gebruiker) kun je toch ook gewoon een tellen bouwen in javascript, die telt zolang de request bezig is.

De rest van de oplossing zit in de code in mijn vorige post
Ben je ook blij dat Ubuntu zo toegankelijk en gratis is, en wil je graag net als ik iets terugdoen, kijk dan eens rond bij mwanzo, dé poort naar het bijdragen aan Ubuntu en haar gemeenschap!

Documentatie Terminal

Offline MKe

  • Lid
Re: Python webprogrammeren time-outs
« Reactie #16 Gepost op: 2010/08/23, 13:18:42 »
Hoi Ronnie,

Bedankt voor je heldere voorbeeld. Het werkt, maar helaas zoals je al zei slechts in bepaalde browsers.

Als ik javascript gebruik, zou dat de time-out ook oplossen?
Een andere mogelijkheid die ik bedacht had is om een onafhankelijk process te openen en het het python scriptje steeds opnieuw aan te laten roepen door een 'META HTTP-EQUIV="refresh"' pagina te laten printen. Dit lijkt te werken! Is dit een goede methode?

mijn script:
#!/usr/bin/env python
import cgi
import os.path
import subprocess
import sys
#debuggen
import cgitb; cgitb.enable()

def wachtpagina(id,stap):
    #print wachtstap met teller om aantal aanroepen te tonen
    #de key ID geeft process ID aan
    print """Content-Type: text/html\n\n\n
    <html>
    <META HTTP-EQUIV="refresh" content="2; URL=fork2.py?ID=%s&step=%d">
    <HEAD>
    <TITLE>Waiting</TITLE>
    </Head>
    <BODY>
    Waiting...%d
    </BODY>
    </html>
    """ % (id,stap,stap)
   

param=cgi.FieldStorage()
#als er een parameter ID is, dan is er een process opgestart.
#ID geeft dan het process nummer (ID) aan, uniek voor het proces
if param.has_key('ID'):
    id=param['ID'].value
    if os.path.exists("/proc/%s" % str(id)):
        #print wachpagina
        wachtpagina(id,int(param['step'].value)+1)
    else:
        #process klaar, dus ga verder.
        print 'Content-Type: text/html\n'
        print
        print 'ready'
else:
    #Subprocess om een process te starten die onafhankelijk van
    #het hoofdprocess door blijft gaan (geen stdout, dus nar PIPE
    #fork1.py simuleert het langlopende process
    pid = subprocess.Popen([sys.executable, "fork1.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
    #laat wachtpagina zien
    wachtpagina(str(pid.pid),1)

fork1.py is hierin en apart python scriptje, in mijn echte code ga ik natuurlijk een ander programma aanroepen:
#!/usr/bin/env python
import time

time.sleep(20)

Offline Ronnie

  • Lid
    • ronnie.vd.c
Re: Python webprogrammeren time-outs
« Reactie #17 Gepost op: 2010/08/23, 14:02:51 »
Voor de browsers die het niet ondersteunen, is er een flash object, die websockets kan emuleren. Dit werkt in elke browser met flash ondersteuning (niet zelf geprobeerd).

Jou oplossing is waarschijnlijk veel eenvoudiger, en lijkt me ook geschikt. Plus het hangt niet af van de nieuwe webstandaarden, wat binnen een bedrijfs/school situatie wellicht beter is.
Ben je ook blij dat Ubuntu zo toegankelijk en gratis is, en wil je graag net als ik iets terugdoen, kijk dan eens rond bij mwanzo, dé poort naar het bijdragen aan Ubuntu en haar gemeenschap!

Documentatie Terminal