Python perusteet ja PythonAnywhere
- Hello World
- Tietorakenteet
- Tiedostojen käsitteleminen ja JSON
- PythonAnywhere ja WSGI
- Flask
- PythonAnywhere ja Flask
- users.jyu.fi, CGI ja Flask
- Laskuri
- Querystring
- Lisätietoa
Näissä tehtävissä tutustutaan Pythonin perusteisiin.
Asenna omalle koneellesi Pythonin uusin versio.
Opettele käyttämään Pythonin komentotulkin help-komentoa.:
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> help
Type help() for interactive help, or help(object) for help about object.
>>> import cgi
>>> help(cgi)
Help on module cgi:
NAME
cgi - Support module for CGI (Common Gateway Interface) scripts.
...
...
...
Hello World
Käy ensimmäisenä läpi Python 3 tutoriaali.
- Luo haluamallasi editorilla (esim. notepad++ tai visual studio code)
hello.py
-tiedosto ja tallenna se haluamaasi kansioon.Käyttämäsi editorin täytyy osata tallentaa tekemäsi tiedosto käyttäen unix-muotoisia rivinvaihtoja (LF eikä windows-muotoisia CRLF. Notepad++ näyttää ikkunan alalaidassa rivinvaihtojen tyypin ja sen voi vaihtaa valinnalla Edit|EOL Conversion|Unix/OSX Format.
- Kopioi ohjelmasi pohjaksi seuraava koodi:
Ensimmäisenä rivinä on unix-ympäristön edellyttämä shebang-rivi, joka kertoo mistä käytettävä tulkki löytyy. Tätä ei välttämättä windows-ympäristössä tarvita, mutta se on hyvä säilyttää varmuuden vuoksi. Toinen rivi on vasta varsinaisesti python-kieltä ja kertoo millä merkistöllä ohjelmakoodi on kirjoitettu. Käytä aina UTF-8-merkistöä.#!/usr/bin/python # -*- coding: utf-8 -*- print("Hello world")
- Kokeile ohjelman toimintaa komentoriviltä (cmd tai powershell).
Siirry samaan kansioon, jossa
hello.py
-tiedostosi sijaitsee. Jos Pythonin asennus toimii oikein, voit käynnistää ohjelmasi kirjoittamalla suoraan sen nimen:
Toinen vaihtoehto on kirjoittaa:hello.py
python hello.py
Tietorakenteet
HUOM! Pythonissa lohkot merkitään sisentämällä! Sama sisennystaso tarkoittaa siis samaa lohkoa. Varmista, että editorisi korvaa tabulaattorimerkit neljällä välilyönnillä. Käytä sisennyksessä neljää välilyöntiä. Notepad++:ssa tämä asetus löytyy kohdasta Settings|Preferences|Language
Lisää ohjelmasi alkuun seuraava tietorakenne:
data = [
{ 'nimi': 'Kalle',
'ammatti': 'Yliopistonopettaja',
'syntymävuosi': 1980,
'palkka': 2000
},
{ 'nimi': 'Ville',
'ammatti': 'Opiskelija',
'syntymävuosi': 1995,
'palkka': None
},
{ 'nimi': 'Maija',
'ammatti': 'Professori',
'syntymävuosi': 1970,
'palkka': 3000
}
]
Avaa Python tutorial ja toteuta sen avulla seuraavat asiat:
- Tulosta tietorakenteen sisältö seuraavaan tapaan:
nimi Kalle ammatti Yliopistonopettaja palkka 2000 syntymävuosi 1980 ...
Jos saat virheilmoituksen:
TypeError: cannot concatenate 'str' and 'int' objects
Niin muista muuntaa kokonaislukutyyppiset tiedot merkkijonoiksi
str
-funktiolla:luku = 1 print("merkkijono" + str(luku)
Tee tulostaminen siten, että se toimii vaikka lisättäisiin uusia tietoja rakenteeseen. Muuta Villen kohdalta rakennetta seuraavanlaiseksi ja kokeile, että tulostus edelleen toimii:
{ 'nimi': 'Ville', 'ammatti': 'Opiskelija', 'syntymävuosi': 1995, 'kotipaikka': 'Jyväskylä', 'palkka': 1000 }
- Kokeile tehdä tulostaminen kahdella eri tavalla. Käytä ensimmäisessä versiossa uloimmassa silmukassa range()-funktiota ja tee toinen ilman range()-funktiota.
- Tulosta vain niiden henkilöiden tiedot, joiden palkka on suurempi kuin 2500
- Tulosta vain henkilöiden ammatit
- Tulosta henkilöiden tiedot siten, että tiedot tulevat avaimen mukaan aakkosjärjestyksessä. Dicteissä ei ole järjestystä joten normaalisti avaimet saadaan satunnaisessa järjestyksessä. Käytä apunasi joko listan sort()-metodia tai sorted-funktiota.
- Tulosta vain kotipaikat. Varmista, että tulostaminen toimii vaikka kaikille ei ole kotipaikkaa määritelty
Ratkaise ongelma try..except-rakenteella:
Miten voisit ratkaista saman asian ilman try..except-rakennetta? Kts. Why dict.get(key) instead of dict[key]? Pythonissa on yleisenä tapana käyttää try..except-rakennetta lähes kaikkialla. Is it a good practice to use try-except-else in Python?try: # tämä rivi kaatuu joskus, koska kotipaikkaa ei ole olemassa print("tulostetaan jostakin rakenteesta kotipaikka" + foobar["kotipaikka"]) except KeyError: # Tullaan tänne jos edellinen print kaatuu. pass ei tee mitään pass
- Tulosta vain henkilöiden palkat. Jos palkkaa ei ole annettu niin tulosta tyhjä.
- Lisää ohjelmaasi koodirivi, joka lisää tietorakenteeseen omat tietosi. Kts. append(). Varmista, että edellä tehdyt tulosteet toimivat myös omien tietojesi lisäämisen jälkeen.
- Laske kaikki palkat yhteen ja tulosta saatu summa. Laske myös mikä on keskipalkka ja tulosta keskipalkka. Sinun pitää
siis laskea myös montako palkkaa löytyy. Jos jollekin ei ole määritelty palkkaa niin käytä palkkana perustulokokeilun määrää eli 560 euroa. Tässä sinun pitää huomioida, että kaikille ei ole annettu
palkkaa. Käytä taas try..except-rakennetta.
Jos haluat ottaa kiinni minkä tahansa virheen niin se onnistuu seuraavalla rakenteella. Huomaa, että tämä voi piilottaa myös omia ohjelmointivirheitä! Käsittele vain olennaiset poikkeukset. Geneerinen kaiken kaappaava except aiheuttaa vain ongelmia debuggaamisessa.
try: virheellinen koodirivi except: #tänne tullaan jos tuli virhe
Jos haluat kuitenkin tietää tarkemmin mikä virhe oli kyseessä tai tulostaa saadun virheilmoituksen niin se onnistuu seuraavasti:
import sys try: ... virheellinen rivi except: for i in sys.exc_info(): print(i) # tai print(sys.exc_info()[0]) print(sys.exc_info()[1]) print(sys.exc_info()[2]) #tai, kts. https://www.adamsmith.haus/python/answers/how-to-retrieve-the-file,-line-number,-and-type-of-an-exception-in-python try: int("foo") except Exception as e: exception_type, exception_object, exception_traceback = sys.exc_info() filename = exception_traceback.tb_frame.f_code.co_filename line_number = exception_traceback.tb_lineno print("Exception type: ", exception_type) print("File name: ", filename) print("Line number: ", line_number)
- Järjestä henkilöt nimen mukaan aakkosjärjestykseen ja tulosta ne sen jälkeen aakkosjärjestyksessä. Joudut kertomaan sort()/sorted()-funktiolle miten tietorakenteesi dictejä pitää vertailla. Kts. Key Functions.
#järjestää datan palkka-ominaisuuden mukaan: data.sort(key=lambda hlo: hlo["palkka"]) #sama kuin edellä mutta käytetty erillistä funktiota: def palkka(obj): return obj["palkka"] data.sort(key=palkka) # Sama mutta käänteisessä järjestyksessä data.sort(key=palkka, reverse=True)
Tiedostojen käsitteleminen ja JSON
- Kokeile muuttaa tietorakenne json-muotoon. Lataa käyttöösi json-kirjasto:
Nyt voit dumpata tietorakenteen json-muotoon seuraavasti:import json
print(json.dumps(data))
Kopioi json-muotoinen tietorakenne omaan tekstitiedostoon ja tallenna se "tietorakenne.json"-nimellä. Skandinaaviset merkit näkyvät koodatussa muodossa.
Kokeile nyt poistaa ohjelmakoodista alkuperäinen tietorakenne ja lataakin se tiedostosta:
import io tiedosto = io.open("tietorakenne.json", encoding="UTF-8") data = json.load(tiedosto)
Pidä huoli, että käytät koko ajan UTF-8-merkistöä
- Kokeile lisätä ohjelmakoodissa tietorakenteeseen muutama lisähenkilö. Kokeile
saatko json.dump-komennolla
tallennettua uuden tietorakenteen vanhan päälle. Katso muuttuiko tiedoston sisältö
- json.dump tarvitsee kaksi parametria: objektin, joka halutaan tallentaa json-muodossa ja kirjoittamista (mode="w") varten avatun tiedosto-objektin
- Kokeile samaan tapaan ladata tietorakenne webbiosoitteesta kuten täältä:
https://appro.mit.jyu.fi/ties4080/ohjaus/ohjaus1/malli.json
Käytä urllib.request-kirjaston
urlopen
-metodia:
Mallina käytetty json-data on lainattu JSON Data Set Sample-sivulta. Tulosta tästä tietorakenteesta jokaisen donitsin nimi ja sen päällysteet (topping) ja kuorrutteet (batters).import urllib.request with urllib.request.urlopen('tähän sivun osoite') as response: data = json.load(response)
PythonAnywhere ja WSGI
- Luo itsellesi ilmainen tunnus (beginner account) PythonAnywhere-palvelussa. Huomioi, että käytät palvelua, joka sijaitsee osoitteessa
eu.pythonanywhere.com
. - Siirry Web-välilehdelle ja luo siellä itsellesi uusi web app Add a new web app. Valitse tyypiksi Manual configuration. Valitse Pythonin versioksi 3.10 tai uudempi.
-
Web-applikaation luomisen jälkeen pääset sivulle, jossa listataan erilaisia sovellukseesi liittyviä tietoja:
WSGI configuration file
-kohdasta pääset muokkaamaan valmista WSGI-koodia-
Log files/
-kohdasta löytyvät sovelluksen lokitiedostot. Tärkein näistä on error.log, josta näet virheet, jotka kaatavat sovelluksesi.- access.log : kuka on ladannut ja minkä resurssin ja milloin
- error.log : virheilmoitukset eli kaikki se mitä sovelluksessa on kirjoitettu STDERR:iin
- server.log : kaikki muu lokitieto sisältäen myös STDOUT:iin tehdyt tulosteet
#WSGI-sovelluksessa toimivat seuraavat: import sys print("tämä näkyy server.logissa") #CGI-ohjelmassa tämä menisi suoraan selaimelle. print("tämä näkyy error.logissa", file=sys.stderr)
- Kokeile nyt sovellustasi osoitteessa
tunnus.eu.pythonanywhere.com
- Käy muuttamassa
WSGI configuration file
seuraavanlaiseksi. Lue kuitenkin ensin mitä valmiissa tiedostossa kerrotaan.# environ sisältää CGI-ympäristöä vastaavat muuttujat ja niiden arvot #start_reponse on funktio, jolla kerrotaan palvelimelle mikä on HTTP-status ja mitkä ovat HTTP-otsakkeet def application(environ, start_response): #content tulee sisältämään varsinaisen vastauksen sisällön #tässä tapauksessa se on tavallista tekstiä content = "Ympäristömuuttujat\n" for key in environ.keys(): content = content + '%s\t:\t%s\n' % (key, environ[key]) # HTTP-statuskoodi status = '200 OK' # HTTP-otsakkeet # vähintään content-type pitää löytyä response_headers = [ ('Content-Type', 'text/plain;charset=UTF-8'), ('Content-Length', str(len(content))) ] # Asetetaan status ja otsakkeet start_response(status, response_headers) # Käytännössä edellinen lähettää selaimelle seuraavat rivit: # Content-type: text/plain;charset=UTF-8 # Content-length: ... # # viimeisenä on aina tyhjä rivi, joka kertoo selaimelle, että otsakkeet loppuvat # www-palvelin lisää automaattisesti väliin läjän omia tarpeellisia otsakkeitaan # ja seuraavaksi tulee varsinainen sisältö return [content.encode("UTF-8")]
- Kokeile nyt sovellustasi osoitteessa
tunnus.eu.pythonanywhere.com
. Koodimuutosten päivittyminen sivuun voi kestää tovin. Käytä editorin reload-painiketta. Sinun pitäisi saada seuraavaa listausta vastaava tulos:Ympäristömuuttujat QUERY_STRING : REQUEST_METHOD : GET CONTENT_TYPE : CONTENT_LENGTH : REQUEST_URI : / PATH_INFO : / DOCUMENT_ROOT : /usr/local/openresty/nginx/html SERVER_PROTOCOL : HTTP/1.1 REMOTE_ADDR : 10.0.0.61 REMOTE_PORT : 46746 SERVER_PORT : 80 SERVER_NAME : hazor.eu.pythonanywhere.com SCRIPT_NAME : HTTP_HOST : hazor.eu.pythonanywhere.com HTTP_X_REAL_IP : 84.250.80.244 HTTP_X_FORWARDED_FOR : 84.250.80.244 HTTP_CONNECTION : close HTTP_USER_AGENT : Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0 HTTP_ACCEPT : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 HTTP_ACCEPT_LANGUAGE : en-US,en;q=0.5 HTTP_ACCEPT_ENCODING : gzip, deflate HTTP_DNT : 1 HTTP_COOKIE : __stripe_mid=cf10ed5b-20da-44eb-85e3-8e06a8b4144e9c3674 HTTP_UPGRADE_INSECURE_REQUESTS : 1 HTTP_CACHE_CONTROL : max-age=0 wsgi.input : <uwsgi._Input object at 0x7fb599c7ef50> wsgi.file_wrapper : <built-in function uwsgi_sendfile> wsgi.version : (1, 0) wsgi.errors : <uwsgi_file__bin_user_wsgi_wrapper.ErrorLogFile object at 0x7fb599c6da90> wsgi.run_once : False wsgi.multithread : False wsgi.multiprocess : False wsgi.url_scheme : http uwsgi.version : b'2.0.17.1' uwsgi.node : b'blue-euweb1'
- WSGI tai CGI on matalin taso, jolla web-palvelinsovelluksia voi ohjelmoida. Yleensä käytetään hieman helpompia kirjastoja kuten flask.
- Poista tekemäsi WSGI-sovellus pythonanywhere-palvelusta. Luo sen tilalle uusi sovellus, mutta valitse tällä kertaa tyypiksi Flask. Tutki mitä nyt ilmestyi WSGI-konfiguraatiotiedostoon. PythonAnywhere tekee sinulle valmiiksi sopivan tiedoston Flask-sovellusten suorittamista varten. Flask-sovelluskehys toimii WSGI-rajanpinnan päällä.
- Jos haluat jakaa pythonanywheressa sijaitsevia tiedostojasi webissä, siirry Web-välilehdelle ja siellä kohtaan Static Files. Voit tässä määrätä mikä tiedosto näkyy webissä ja millä osoitteella. Vasempaan sarakkeeseen kirjoitat haluamasi URLin ja oikeaan sarakkeeseen annat tiedoston polun.
Flask
Käy läpi Flask Tutorial in Visual Studio Code -ohje väliotsikkoon Use template to render a page -saakka. Ohje kertoo miten saat helposti asennettua Flaskin omalle koneelle ja debuggattua sovellustasi suoraan Visual Studio Codella.
Vaihtoehtoinen ohje Flaskin käyttöönottoon ilman VS codea
Otetaan Flask käyttöön omalla koneella.
- Luo ensimmäiseksi virtuaalinen ympäristö flaskille. Siirry powershellissä
kansioon johon haluat virtuaaliympäristön ja kirjoita:
py -3 -m venv venv
Lähes aina kannattaa asentaa sovellusta varten oma python-virtuaaliympäristö, jonka avulla voit olla varma, että sovellukseen liittyvät kirjastot ja mahdolliset muut python-ympäristön säädöt eivät riko muita sovelluksiasi.
- Seuraavaksi aktivoi virtuaaliympäristö komennolla:
.\venv\Scripts\activate
Jos saat PSSecurityException-virheilmoituksen:
- Kirjoita uudessa powershellissa komento:
set-executionPolicy remoteSigned -scope currentUser
- Jos edellinen ei toimi, niin käynnistä uusi powershell
Run As Administrator
-valinnalla ja kokeile uudessa powershellissa seuraavaa:
. Vastaa kysymykseen Yset-executionpolicy remotesigned
- Yritä nyt uudelleen virtuaaliympäristön aktivointia
- Kirjoita uudessa powershellissa komento:
- Uusia kirjastoja voi nyt asentaa virtuaaliympäristöön pip-komennolla. Asenna Flask:
pip install Flask
- Nyt voit kirjoittaa yksinkertaisimman flask-ohjelman. Tallenna seuraava
app.py-nimellä:
from flask import Flask app = Flask(__name__) @app.route('/') #tämä rivi kertoo osoitteen, josta tämä sovellus löytyy def hello_world(): return 'Hello, World!'
- Jos haluat käyttää sovellustiedostosi nimenä muuta nimeä kuin app.py, täytyy määrätä mikä sovellus halutaan suorittaa. Tämä tehdään ympäristömuuttujan avulla:
Samoin kannattaa asettaa flask kehitysmoodiin eli aktivoida debugger ja sovelluksen automaattinen uudelleen käynnistys seuraavalla:$env:FLASK_APP = "flask_hello.py"
Debug-tilan voi aktivoida myös komentoriviparametrilla. Samalla voi aktivoida sovelluksen automaattisen päivityksen (reload) aina sovellustiedoston (app.py) muuttuessa:$env:FLASK_DEBUG = "true"
flask run --debugger --reload
- Nyt voit käynnistää flaskin komennolla:
flask run
Saat seuraavanlaisia tulostuksia, jos kaikki toimii kuten pitää:
* Serving Flask app "flask_hello.py" * Environment: development * Debug mode: on * Restarting with stat * Debugger is active! * Debugger PIN: 241-637-288 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
- Kokeile avata sovelluksesi osoitteesta http://127.0.0.1:5000/
- Tee muutos sovelluksen tulostamaan tekstiin. Kokeile päivittyykö sivun sisältö eli päivittääkö flask sovelluksesi lennosta
- Oletuksena Flask palauttaa html-dokumentteja. Jos haluat vaihtaa
tyyppiä tekstiksi tai vaikkapa kuvaksi se onnistuu Response-objektin avulla
uudelleenmäärittelemällä käytettävä mediatyyppi:
from flask import Response ... ... return Response('Hello, World!', mimetype="text/plain;charset=UTF-8")
- Virtuaaliympäristö suljetaan komennolla
deactivate
PythonAnywhere ja Flask
- Kopioi edellä tekemäsi flask-sovellus PythonAnywhere-palveluun ja kokeile toimiiko sovellus myös siellä. Pitäisi riittää, että kopioit python-tiedostosi (.py) suoraan PythonAnywheressä olevaan Source code -kansioon. Jos sieltä löytyy jo .py-päätteinen tiedosto, korvaa se omalla tiedostollasi. Älä koske WSGI configuration -tiedostoon.
- Jos sovellus kaatuu PythonAnywhere-palvelussa ja saat
internal server error
-virheilmoituksen, tarkista error.log-tiedostosta mikä meni väärin. Yleensä helpompi ja nopeampi on, jos virheet näkee suoraan selaimessa. Flaskin voi asettaa debug-tilaan ja saada virheet suoraan sivulle tekemällä seuraavat lisäykset PythonAnywheren WSGI-konfiguraatiotiedostoon:
Mikä ihmeen werkzeug? Werkzeug on WSGI-rajapinnan päälle rakennettu kirjasto. Flask on rakennettu suoraan Werkzeugin päälle. Ei kannata ihmetellä, jos Flaskin dokumentaatiosta usein viitataan Werkzeugin dokumentaatioon.from werkzeug.debug import DebuggedApplication application.debug = True application = DebuggedApplication(application)
Jos haluat itse käsitellä virheet
Jos haluat itse käsitellä internal server errorit, voit tehdä myös seuraavan lisäyksen flask-tiedostoosi:
kts. Generic Exception Handlersfrom werkzeug.exceptions import InternalServerError import sys @app.errorhandler(InternalServerError) def handle_500(e): original = getattr(e, "original_exception", None) if original is None: # direct 500 error, such as abort(500) return "no error", 500 # wrapped unhandled error errors = "" for err in sys.exc_info(): errors = errors + str(err) + "\n" return errors, 500
- Tehtyäsi ohjelmakoodiin muutoksia täytyy sovellus käynnistää uudelleen pythonanywheren-editorin oikean yläkulman reload-painikkeella tai web-välilehden reload-painikkeella. kts. Reload web app.
- Saatuasi hello-sovelluksen toimimaan kokeile siirtää myös aiemmin tekemäsi
tietorakennetta käsittelevä sovellus (funktio) PythonAnywhere-palveluun. Tämä
funktio on lisättävä samaan tiedostoon .py-tiedostoon, kuin jo aiempi
sovelluksesi, koska ilmaisella tunnuksella voi olla vain yksi sovellus kerrallaan
PythonAnywhere-palvelussa.
Flask-sovelluksessa et voi
print
-funktiolla tulostaa vaan joudut muuttamaan funktiotasi siten, että se palauttaakin kaiken merkkijonona.Aseta tämä toimimaan osoitteessa
tunnus.eu.pythonanywhere.com/ohjaus1/
muuttamalla routea:@app.route('/ohjaus1/')
- Kokeile ensin tietorakenteella, joka on valmiiksi koodissa
- Yritä seuraavaksi ladata tietorakenne tiedostosta. Siirrä valmis tiedosto PythonAnywhere-palvelussa omaan kotihakemistoosi
- Kokeile myös onnistuuko tiedoston tallentaminen
- Kokeile vasta viimeisenä tietorakenteen lataamista verkosta osoitteesta https://appro.mit.jyu.fi/ties4080/ohjaus/ohjaus1/malli.json. Se ei onnistu vaan sovelluksesi kaatuu. PythonAnywhere-palvelun ilmaisilla tunnuksilla saa ladata verkosta tietoja vain tietyistä osoitteista.
- Kokeile onnistuuko tietorakenteen lataaminen osoitteesta http://hazor.eu.pythonanywhere.com/malli.json
- Tarvittaessa voi myös PythonAnywhere-palveluun luoda oman virtuaaliympäristön Flaskia varten: How to use a virtualenv in your web app (to get newer versions of django, flask etc) ja Setting up Flask applications on PythonAnywhere. Esim. jos jokin tarvittava kirjasto puuttuu.
users.jyu.fi, CGI ja Flask
- Ota Puttylla/Kittyllä tai powershellin ssh-komennolla ssh-yhteys jalava- tai halava-palvelimeen
- Luo W:-asemalle kansio
cgi-bin/ties4080/. Kansion koko polku halava/jalava-koneessa on
HUOM! Kansion täytyy ehdottomasti olla w:\cgi-bin eikä mikään muu. kts. CGI/SSI-tekniikat users.jyu.fi-palvelimella/wwwhome/home/oma_tunnus/public_html/cgi-bin/ties4080/
- Siirry cgi-bin/ties4080/-kansioon
- Kirjoita seuraava komento:
Tämä luo sinulle nyt oman virtuaalisen python-ympäristön venv-alikansioon/opt/rh/rh-python38/root/usr/bin/python3.8 -m venv venv
- Aktivoi virtuaaliympäristö komennolla (Bash-shellissä):
Jos käytätkin esim. csh/tcsh niin aktivointikomento on:. venv/bin/activate
source venv/bin/activate.csh
- Aktivoitumisen merkiksi komentokehotteesi alkuun ilmestyy merkintä (venv)
- Nyt voit asentaa kirjastoja pip-komennolla. Päivitä ensimmäiseksi pip uusimpaan versioon:
pip install --upgrade pip
- Asenna seuraavaksi Flask- ja Flask-WTF-kirjastot:
pip install Flask Flask-WTF
- Sulje virtuaaliympäristö deactivate-komennolla
- Virtuaaliympäristösi on nyt käyttövalmis. Voit suorittaa Python-ohjelmia tässä ympäristössä, kun
asetat shebang-rivin osoittamaan
virtuaaliympäristössä olevaan python-tulkkiin. users.jyu.fi (karahka2)-palvelimessa suoritettavalla ohjelmalle polku on muotoa:
Huomaathan, että polku ei ole sama kuin halava/jalava-koneissa.#!/home/oma_tunnus/public_html/cgi-bin/ties4080/venv/bin/python
- Voit tarvittaessa asentaa lisää kirjastoja virtuaaliympäristöösi. Riittää, että aktivoit edellä tehdyllä tavalla ympäristön ja käytät pip-komentoa.
- Valitettavasti users.jyu.fi-palvelimella ei voida suoraan ajaa Flaskia vaan sen suorittaminen on kikkailtava CGI-ohjelmana.
- Luo haluamallasi editorilla hello.cgi-tiedosto ja tallenna se w:\cgi-bin\ties4080\-kansioon.
Käyttämäsi editorin täytyy osata tallentaa tekemäsi tiedosto käyttäen unix-muotoisia rivinvaihtoja (LF) eikä windows-muotoisia (CRLF). Notepad++ näyttää ikkunan alalaidassa rivinvaihtojen tyypin ja sen voi vaihtaa valinnalla Edit|EOL Conversion|Unix/OSX Format.
- Notepad++:ssa kannattaa myös muistaa valita käytetty kieli (Python) Language-valikosta, koska Notepad++ ei suoraan .cgi-tiedostopäätteestä pysty sitä päättelemään.
- Kopioi ohjelmasi pohjaksi seuraava koodi:
Nyt käytössä ei ole mitään helpottavaa ympäristöä vaan on itse tulostettava HTTP-protokollan edellyttämät tiedot. Huomaa edellisessä tyhjä rivi, joka erottaa HTTP-otsakkeet varsinaisesta sisällöstä.#!/usr/bin/python # -*- coding: utf-8 -*- print("""Content-type: text/plain; charset=UTF-8 Hello world! """)
- Kokeile ohjelman toimintaa komentorivillä
jalava/halava-koneessa.
Toimiakseen ohjelma vaatii suoritusoikeuden (execute) kaikille käyttäjille. Lisäksi toimiakseen CGI-ohjelmana ohjelman ryhmän (group) täytyy olla users
[tjlahton@halava cgi-bin]$ chmod a+x hello.cgi [tjlahton@halava cgi-bin]$ chgrp users hello.cgi [tjlahton@halava cgi-bin]$ ls -al hello.cgi -rwxr-xr-x. 1 tjlahton users 3777 Feb 5 09:59 hello.cgi
Cgi-ohjelmaan sekä samaan kansioon EI SAA olla kirjoitusoikeutta muilla kuin käyttäjällä itsellään.
Jos edellämainitut asiat ovat kunnossa voit kokeilla suorittaa ohjelmasi:
[tjlahton@jalava cgi-bin]$ ./hello.cgi
Rivinvaihdot (CRLF vs LF)
Seuraava virhe tarkoittaa, että rivinvaihdot ovat DOS-tyyppisiä vaikka pitäisi olla Unix-tyyppisiä:
usr/bin/python^M: bad interpreter: No such file or directory
Varmista, että editorisi käyttää unix-tyylisiä rivinvaihtoja. Visual Studio Codessa rivinvaihto näkyy alalaidan statusbarissa joko muodossa CRLF tai LF. Oikea muoto on LF.
Jos saat seuraavan herjauksen, vika voi olla myös rivinvaihdoissa
./hello.cgi: no such file or directory
Jos CGI-tiedostossa on väärät rivinvaihdot, sovelluksesi kaatuu aina Internal Server Error -virheeseen
Rivinvaihtojen oikeellisuuden voi helposti tarkistaa file-komennolla. Seuraavassa näkyy, että tiedostoissa on väärät rivinvaihdot (CRLF):
[tjlahton@halava malli]$ file hello.cgi oma.py hello.cgi: a /home/tjlahton/public_html/cgi-bin/ties4080/venv/bin/python3.8\015 script, ASCII text executable, with CRLF line terminators oma.py: Python script, UTF-8 Unicode text executable, with CRLF line terminators
Rivinvaihdot voi helposti korjata joko dos2unix-komennolla
dos2unix hello.cgi
tai esim. nano-editorilla. Riittää, että avaa tiedoston nanolla -u-parametrin kera ja tallentaa sen jälkeen tiedoston nanolla
nano -u hello.cgi
Oikeat rivinvaihdot (LF) näyttävät file-komennolla seuraavilta:
[tjlahton@halava malli]$ file hello.cgi oma.py hello.cgi: a /home/tjlahton/public_html/cgi-bin/ties4080/venv/bin/python3.8 script, ASCII text executable oma.py: Python script, UTF-8 Unicode text executable
Varmista myös, että tiedostosi alussa ei ole UTF-8:aan liittyvää BOM-merkintää. Seuraava virhe yleensä vihjaa, että tiedoston alussa on BOM:
Exec format error. Binary file not executable.
less-komennolla on helppo tarkistaa onko tiedoston alussa BOM:
Saatuasi ohjelman toimimaan komentoriviltä voit kokeilla sitä myös WWW-selaimella osoitteesta:
http://users.jyu.fi/~omatunnus/cgi-bin/ties4080/hello.cgi
Jos ohjelma antaa selaimessa internal server error-virheilmoituksen, tarkista vielä kaikki edellämainitut asiat.
-
Saatuasi hello-ohjelman toimimaan selaimessa voit jatkaa seuraavalla.
Luo kansioon uusi tiedosto flask.cgi ja kirjoita siihen seuraava ohjelmakoodi. Muista muuttaa ensimmäiselle riville oma käyttäjätunnuksesi.
#!/home/oma_tunnus/public_html/cgi-bin/ties4080/venv/bin/python # -*- coding: utf-8 -*- # suorittaa Flask-sovellukset CGI-ohjelmina users.jyu.fi-palvelimella import sys from wsgiref.handlers import CGIHandler from werkzeug.debug import DebuggedApplication try: from oma import app as application if __name__ == '__main__': handler = CGIHandler() application.debug = True handler.run(DebuggedApplication(application)) except: #koska tänne päädyttäessä ei werkzeug toimi täytyy itse tulostaa http-protokollan #edellyttämä otsake. STDOUT menee tässä tapauksessa suoraan selaimelle print("Content-Type: text/plain;charset=UTF-8\n") print("Syntaksivirhe:\n") for err in sys.exc_info(): print(err)
Tämä koodi vastaa sitä mitä PythonAnywhere loi sinulle WSGI-konfigurointitiedostoon. Oleellisin ero on, että tässä suoritetaan Flask-sovellus CGI-rajapinnan kautta.
- Luo samaan kansioon myös oma.py-tiedosto. Tiedoston nimen on oltava
tämä. Vrt. edellisessä koodissa olevaan riviin
from oma import app as application
. Kirjoita tiedostoon seuraava ohjelmakoodi:#!/wwwhome/home/oma_tunnus/public_html/cgi-bin/ties4080//venv/bin/python # edellisellä shebang-rivillä ei ole merkitystä nyt muuten kuin komentorivillä # suoritettaessa eikä oikeastaan silloinkaan, koska Flask-sovellusta ei voi kunnolla # suorittaa komentoriviltä halava/jalava-koneissa # -*- coding: utf-8 -*- from flask import Flask, request, Response import os app = Flask(__name__) # @app.route määrää mille osoitteille tämä funktio suoritetaan @app.route('/') def hello_world(): return Response("Hello World", content_type="text/plain; charset=UTF-8")
- Varmista, että tiedostojen ja kansioiden oikeudet ovat samat kuin aiemmin tehdyissä cgi-ohjelmissa.
- Kokeile sovellustasi osoitteessa http://users.jyu.fi/~omatunnus/cgi-bin/ties4080/flask.cgi/. Viimeinen /-merkki on olennainen!
- Jos sovelluksesi ei toimi, lue ohjetta eteenpäin ja kokeile omalle koneelle asennetulla Flaskilla
- Yleensä syntaksivirheet löytyvät, kun yrität suorittaa sovelluksesi komentoriviltä:
taipython flask.cgi
python oma.py
Sinun pitää suorittaa ohjelmasi virtualenvillä asentamallasi python-tulkilla. Sinun pitää siis ensin aktivoida virtuaaliympäristö ja sen jälkeen kokeilla ohjelmasi suorittamista.
Komentoriviltä suoritettuna flask-ohjelma ei voi toimia, koska se vaatii www-ympäristön. Komentoriviltä ajettuna saat kuitenkin kiinni koko sovelluksen kaatavat syntaksivirheet. Varsinaista Flask-palvelinta ei voi käynnistää halava/jalava-koneissa.
- Yleensä syntaksivirheet löytyvät, kun yrität suorittaa sovelluksesi komentoriviltä:
- WWW-palvelin suorittaa Flask-kehyksen ja oma.py-tiedostosta löytyy hello_world-funktio, joka on määrätty suoritettavaksi,
kun pyydetään sovelluksen juurikansiota (/). HTTP-protokollaan liittyviä asioita ei tarvitse itse tehdä vaan Flask hoitaa ne. Riittää, että
funktio palauttaa merkkijonon, jossa on sivulle haluttu sisältö.
- WWW-palvelin (users.jyu.fi) suorittaa flask.cgi-sovelluksen tiedoston ensimmäisellä rivillä kerrotulla ohjelmalla eli virtuaaliympäristön python-tulkilla #!/home/oma_tunnus/public_html/cgi-bin/ties4080/venv/bin/python
- Sovellus yrittää ladata oma.py-tiedoston (kirjaston) sisällön
(
from oma import app as application
). Jos ei onnistu eli kyseisessä tiedostossa on esim. syntaksivirheitä niin näytetään virhe sivulla - Käynnistetään
handler = CGIHandler()
. Suoritetaan CGIHandlerilla edellä ladattu oma sovellushandler.run(DebuggedApplication(application))
- oma.py-tiedostossa käynnistetään Flask.
app = Flask(__name__)
- Flask ottaa ohjat käsiinsä ja suorittaa
@app.route('/')
-merkinnän määräämän funktion elihello_world()
hello_world()
asettaa halutun mediatyypin ja palauttaa tekstin "Hello World"
- Kokeile saatko PythonAnywhere-palveluun tekemäsi sovelluksen toimimaan myös
users.jyu.fi-palvelimella. Kokeile ensin ilman tallentamista.
Lisää lopuksi myös tallentaminen. Tämä ei onnistu, miksi?
- PythonAnywhere tarjoaa yhden käyttäjän hiekkalaatikon, jossa sovelluksesi toimii. Tässä hiekkalaatikossa myös www-palvelin-prosessilla on oikeus koskea tiedostoihisi
- users.jyu.fi on monen käyttäjän ympäristö. Samassa palvelimessa pyörivät kaikkien opiskelijoiden sivut ja sovellukset. WWW-palvelin-sovelluksella (Apache) on oma käyttäjätunnuksensa ja oikeutensa. Apache ei pääse käsiksi tiedostoihisi, jos et anna sille erikseen niihin lupaa. Users.jyu.fi:ssä on myös lisärajoitteita CGI-ohjelmille.
Laskuri
Onnistuuko laskurin rakentaminen? Kokeillaan.
- Lisää w:-asemalla olevaasi oma.py-tiedostoon seuraavat
rivit:
count = 0 #globaali muuttuja @app.route('/laskuri') def laskuri(): global count count = count + 1 return str(count)
- Kokeile toimiiko lisäyksesi osoitteessa:
Toimii muuten, mutta laskuri ei kasva. Miksi?http://users.jyu.fi/~omatunnus/cgi-bin/ties4080/flask.cgi/laskuri
- Tee sama lisäys PythonAnywhere-palvelimessa olevaan flask-sovellukseesi.
Kokeile toimiiko tämä paremmin osoitteessa:
Nyt toimii. Miksi? Oikeasti tämäkään versio ei vielä toimi kunnolla. Kokeile ylläpitoliittymästä reloadata sovelluksesi. Mitä laskuri näyttää nyt?tunnus.eu.pythonanywhere.com/laskuri
- Sama sovellus voitaisiin laittaa vielä Googlen App Engine -palveluun. Nyt laskuri antaisi ihan kummallisia lukuja. Miksi? App Engineen tutustumme myöhemmin.
- Globaalit muuttujat? Riippuen pilvipalvelusta sovelluksesi toimii yhdellä
tai useammalla prosessilla. PythonAnywheren ilmainen tunnus saa käyttöönsä vain yhden
prosessin, joka nollautuu aina, kun sovellus käynnistetään uudelleen. Tämä tarkoittaa,
että sovelluksen suorittamista tekevä python-tulkki käynnistyy uudelleen.
Sama voi tapahtua myös milloin tahansa riippuen siitä miten PythonAnywhere-palvelun
omat toiminnot siirtelevät sovelluksia kuormantasaussyistä. Älä käytä
globaaleja muuttujia Flask-sovelluksessa. Erityisesti palveluissa, joissa
sovelluksestasi voi samaan aikaan olla käynnissä useampi instanssi, kuten Googlen App Engine,
globaalit muuttujat toimivat hankalasti.
Globaalit muuttujat ovat aina prosessikohtaisia. kts. Global variables in Flask
- CGI-sovellus : jokaisella latauskerralla python-tulkki käynnistetään uudelleen eli myös globaalit muuttujat nollautuvat. Tämä on myös hidasta, koska tulkin käynnistäminen on raskas operaatio. Joskus voi vastaan tulla FastCGI, joka nopeuttaa CGI-sovelluksia jättämällä tulkin muistiin.
- WSGI-sovellus : python-tulkki jää muistiin odottamaan seuraavia sivulatauksia. Globaalit muuttujat alustetaan tulkin käynnistyessä. Palvelusta riippuen sama tulkki voi olla käynnistää vaikka kuinka pitkään tai vaikka kuinka vähän.
- Kuormantasaus? Samasta sovelluksesta voi olla käynnissä useita instansseja. Jokaisella on oma python-tulkki ja samalla myös omat globaalit muuttujansa. Nämä instanssit voivat olla samalla palvelimella tai kokonaan eri palvelimilla.
- Kunnollisen laskurin rakentaminen edellyttää joko sessioiden ja evästeiden käyttämistä tai sovelluksen tilan tallentamista HTML-dokumentin rakenteeseen. Näihin asioihin palataan myöhemmin.
Querystring
- Sivun osoitteeseen voidaan lisätä parametreja seuraavalla tavalla:
http://osoite.example?parametri=arvo¶metri2=arvo1¶metri2=arvo2 http://osoite.example?nimi=M%C3%A4lli%20Henkil%C3%B6&arvosana=5&arvosana=3&arvosana=2&kurssi=Helppo+kurssi&kurssi=Vaikea+kurssi&kurssi=%C3%84ll%C3%B6+kurssi
Osoitteen perään lisätään
?
-merkki.?
-merkin jälkeen luetellaan avaimia ja arvoja&
-merkillä eroteltuinaOsoitteessa esiintyvät erikoismerkit on koodattava Percent-encoding-tavalla. Pythonissa tämän voi tehdä
urllib.parse.quote_plus
-funktiolla.Jos query_stringin sisältävän osoitteen sijoittaa html-dokumenttiin on muistettava koodata myös &-merkit
html.escape
-funktiolla.Näitä koodauksia tarvitaan vasta tulevissa tehtävissä. Nyt riittää, että osataan lukea QueryStringissä annetut parametrit ohjelman käyttöön.
- Querystringissä annettuihin parametreihin pääsee käsiksi
Flaskin Request-objektin
avulla. WWW-sivuilla olevien lomakkeiden lähettämät tiedot käsitellään samalla
tavalla. Tähän palataan seuraavien viikkojen tehtävissä.
Lisää omalla koneellasi ohjelmasi alkuun seuraava rivi niin saat request-objektin käyttöösi:
from flask import request
Request-objektin tärkeimmät ominaisuudet:
request.environ
WSGI-ympäristörequest.args
URL-parametrit (querystring) MultiDict-muodossarequest.form
lomakeparametrit MultiDict-muodossarequest.method
käytetty metodi (GET tai POST)request.query_string
koko querystring sellaisenaan
request.args
on MultiDict, joka toimii kuten tavanomainen dict, mutta se palauttaa jokaista avainta kohti vain ensimmäisen arvon, jos kyseiselle avaimelle löytyy useampia.MultiDictin tärkeimmät ominaisuudet:
nimi = request.args.get("nimi", "Tuntematon") arvosanat = request.args.getlist("lkm")
- Tee uusi ohjelma, joka osaa tulostaa seuraavan querystringin sisällön
http://127.0.0.1:5000/query?nimi=M%C3%A4lli%20Henkil%C3%B6&arvosana=5&arvosana=3&arvosana=2&kurssi=Helppo+kurssi&kurssi=Vaikea+kurssi&kurssi=%C3%84ll%C3%B6+kurssi
Muuta mediatyypiksi
text/plain
, niin saat tulosteen näyttämään seuraavalta:Henkilön nimi : Mälli Henkilö Helppo kurssi 5 Vaikea kurssi 3 Ällö kurssi 2 Keskiarvo : 3.33
Pyöristä keskiarvo kahteen desimaaliin
Kts. Malliohjelma. Kokeile muuttaa malliohjelman parametreja. Jos saat malliohjelman kaatumaan, ilmoita opettajalle millä tavalla onnistuit. - Muuttele omalle ohjelmallesi annettujen parametrien sisältöä. Varmista, että ohjelmasi toimii vaikka parametreja ei annettaisi tai kursseja ja arvosanoja olisi eri määrä.
- Lue nimi
get
-metodilla - Lue kurssit ja arvosanat
getlist
-funktiolla - Käy silmukassa läpi kaikki kurssit
- Käy silmukassa läpi kaikki arvosanat ja muunna ne kokonaisluvuiksi. Jos muuntaminen kokonaisluvuksi epäonnistuu, käytä kyseisenä arvosanana lukua nolla (0)
- Jos arvosanoja on annettu vähemmän kuin kursseja, käytä tulosteessa puuttuvana arvosanana "-"-merkkiä. Laskea arvosanojen keskiarvo oikeiden annettujen arvosanojen perusteella
- Lue nimi
- Saatuasi ohjelman toimimaan omalla koneellasi siirrä se myös PythonAnywhere-palveluun ja varmista ohjelman toiminta siellä
- Malliratkaisun lähdekoodi. Voit tutkia myös vanhaa mallia (python 2.7)
Käyttäjien kommentit