Set - 8

Question 1 :

How do I use Python for CGI?

Answer :

On the Microsoft IIS server or on the Win95 MS Personal Web Server you set up Python in the same way that you would set up any other scripting engine. 
Run regedt32 and go to:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\ScriptMap 

and enter the following line (making any specific changes that your system may need):

.py :REG_SZ: c:\\python.exe -u %s %s

This line will allow you to call your script with a simple reference like: http://yourserver/scripts/yourscript.py provided "scripts" is an "executable" directory for your server (which it usually is by default). The "-u" flag specifies unbuffered and binary mode for stdin - needed when working with binary data. 
In addition, it is recommended that using ".py" may not be a good idea for the file extensions when used in this context (you might want to reserve *.py for support modules and use *.cgi or *.cgp for "main program" scripts). 
In order to set up Internet Information Services 5 to use Python for CGI processing, please see the following links: 
http://www.e-coli.net/pyiis_server.html (for Win2k Server) http://www.e-coli.net/pyiis.html (for Win2k pro) 
Configuring Apache is much simpler. In the Apache configuration file httpd.conf, add the following line at the end of the file:

ScriptInterpreterSource Registry

Then, give your Python CGI-scripts the extension .py and put them in the cgi-bin directory.


Question 2 :

How do I emulate os.kill() in Windows? 

Answer :

Use win32api: 
def kill(pid):
"""kill function for Win32"""
import win32api
handle = win32api.OpenProcess(1, 0, pid)
return (0 != win32api.TerminateProcess(handle, 0))

 


Question 3 :

Why does os.path.isdir() fail on NT shared directories?

Answer :

The solution appears to be always append the "\" on the end of shared drives.

>>> import os
>>>os.path.isdir( '\\\\rorschach\\public')
0
>>>os.path.isdir( '\\\\rorschach\\public\\')
1

It helps to think of share points as being like drive letters. Example:

k: is not a directory
k:\ is a directory
k:\media is a directory
k:\media\ is not a directory

The same rules apply if you substitute "k:" with "\conkyfoo":

\\conky\foo is not a directory
\\conky\foo\ is a directory
\\conky\foo\media is a directory
\\conky\foo\media\ is not a directory


Question 4 :

Web Python ?

Answer :

Some host providers only let you run CGI scripts in a certain directory, often named cgi-bin. In this case all you have to do to run the script is to call it like this: 
http://my_server.tld/cgi-bin/my_script.py 
The script will have to be made executable by "others". Give it a 755 permission or check the executable boxes if there is a graphical FTP interface. 
Some hosts let you run CGI scripts in any directory. In some of these hosts you don't have to do anything do configure the directories. In others you will have to add these lines to a file named .htaccess in the directory you want to run CGI scripts from:

Options +ExecCGI
AddHandler cgi-script .py

If the file does not exist create it. All directories below a directory with a .htaccess file will inherit the configurations. So if you want to be able to run CGI scripts from all directories create this file in the document root. 

To run a script saved at the root: 
http://my_server.tld/my_script.py 
If it was saved in some directory: 
http://my_server.tld/some_dir/some_subdir/my_script.py 
Make sure all text files you upload to the server are uploaded as text (not binary), specially if you are in Windows, otherwise you will have problems.


Question 5 :

The classical "Hello World" in python CGI fashion?

Answer :

#!/usr/bin/env python
print "Content-Type: text/html"
print
print """\
<html>
<body>
<h2>Hello World!
</body>
</html>
"""

To test your setup save it with the .py extension, upload it to your server as text and make it executable before trying to run it. 
The first line of a python CGI script sets the path where the python interpreter will be found in the server. Ask your provider what is the correct one. If it is wrong the script will fail. Some examples: 

#!/usr/bin/python
#!/usr/bin/python2.3
#!/usr/bin/python2.4

It is necessary that the script outputs the HTTP header. The HTTP header consists of one or more messages followed by a blank line. If the output of the script is to be interpreted as HTML then the content type will be text/html. The blank line signals the end of the header and is required.

print "Content-Type: text/html"
print

If you change the content type to text/plain the browser will not interpret the script's output as HTML but as pure text and you will only see the HTML source. Try it now to never forget. A page refresh may be necessary for it to work.

Client versus Server 
All python code will be executed at the server only. The client's agent (for example the browser) will never see a single line of python. Instead it will only get the script's output. This is something realy important to understand. 

When programming for the Web you are in a client-server environment, that is, do not make things like trying to open a file in the client's computer as if the script were running there. It isn't.

 


Question 6 :

How to Debugging in python?

Answer :

Syntax and header errors are hard to catch unless you have access to the server logs. Syntax error messages can be seen if the script is run in a local shell before uploading to the server. 
For a nice exceptions report there is the cgitb module. It will show a traceback inside a context. The default output is sent to standard output as HTML:

#!/usr/bin/env python
print "Content-Type: text/html"
print
import cgitb; cgitb.enable()
print 1/0

The handler() method can be used to handle only the catched exceptions:

#!/usr/bin/env python
print "Content-Type: text/html"
print
import cgitb
try:
f = open('non-existent-file.txt', 'r')
except:
cgitb.handler()

There is also the option for a crude approach making the header "text/plain" and setting the standard error to standard out:

#!/usr/bin/env python
print "Content-Type: text/plain"
print
import sys
sys.stderr = sys.stdout
f = open('non-existent-file.txt', 'r')

Will output this: 

Traceback (most recent call last):
File "/var/www/html/teste/cgi-bin/text_error.py", line 6, in ?
f = open('non-existent-file.txt', 'r')
IOError: [Errno 2] No such file or directory: 'non-existent-file.txt'

Warning: These techniques expose information that can be used by an attacker. Use it only while developing/debugging. Once in production disable it.


Question 7 :

How to make Forms in python?

Answer :

The FieldStorage class of the cgi module has all that is needed to handle submited forms.

import cgi
form = cgi.FieldStorage() # instantiate only once!

It is transparent to the programmer if the data was submited by GET or by POST. The interface is exactly the same. 
* Unique field names : 
Suppose we have this HTML form which submits a field named name to a python CGI script named process_form.py: 

<html><body>
<form method="get" action="process_form.py">
Name: <input type="text" name="name">
<input type="submit" value="Submit">
</form>
</body></html>

This is the process_form.py script:

#!/usr/bin/env python
import cgi
form = cgi.FieldStorage() # instantiate only once!
name = form.getfirst('name', 'empty')
# Avoid script injection escaping the user input
name = cgi.escape(name)

print """\
Content-Type: text/html\n
<html><body>
<p>The submited name was "%s"</p>
</body></html>
""" % name

The getfirst() method returns the first value of the named field or a default or None if no field with that name was submited or if it is empty. If there is more than one field with the same name only the first will be returned. 
If you change the HTML form method from get to post the process_form.py script will be the same. 
* Multiple field names:
If there is more than one field with the same name like in HTML input check boxes then the method to be used is getlist(). It will return a list containing as many items (the values) as checked boxes. If no check box was checked the list will be empty. 
Sample HTML with check boxes:

<html><body>
<form method="post" action="process_check.py">
Red<input type="checkbox" name="color" value="red">
Green<input type="checkbox" name="color" value="green">
<input type="submit" value="Submit">
</form>
</body></html>

And the corresponding process_check.py script: 

#!/usr/bin/env python
import cgi
form = cgi.FieldStorage()
# getlist() returns a list containing the
# values of the fields with the given name
colors = form.getlist('color')
print "Content-Type: text/html\n"
print '<html><body>'
print 'The colors list:', colors
for color in colors:
print '<p>', cgi.escape(color), '</p>'
print '</body></html>'

* File Upload;

To upload a file the HTML form must have the enctype attribute set to multipart/form-data. The input tag with the file type will create a "Browse" button. 

<html><body>
<form enctype="multipart/form-data" action="save_file.py" method="post">
<p>File: <input type="file" name="file"></p>
<p><input type="submit" value="Upload"></p>
</form>
</body></html>

The getfirst() and getlist() methods will only return the file(s) content. To also get the filename it is necessary to access a nested FieldStorage instance by its index in the top FieldStorage instance.

#!/usr/bin/env python
import cgi
form = cgi.FieldStorage()
# A nested FieldStorage instance holds the file
fileitem = form['file']
# Test if the file was uploaded
if fileitem.filename:
open('files/' + fileitem.filename, 'w').write(fileitem.file.read())
message = 'The file "' + fileitem.filename + '" was uploaded successfully'
else:
message = 'No file was uploaded'

print """\
Content-Type: text/html\n
<html><body>
<p>%s</p>
</body></html>
""" % (message,)

The Apache user must have write permission on the directory where the file will be saved. 

* Big File Upload 

To handle big files without using all the available memory a generator can be used. The generator will return the file in small chunks:

#!/usr/bin/env python
import cgi
form = cgi.FieldStorage()
# Generator to buffer file chunks
def fbuffer(f, chunk_size=10000):
while True:
chunk = f.read(chunk_size)
if not chunk: break
yield chunk
# A nested FieldStorage instance holds the file
fileitem = form['file']
# Test if the file was uploaded
if fileitem.filename:
f = open('files/' + fileitem.filename, 'w')
# Read the file in chunks
for chunk in fbuffer(fileitem.file):
f.write(chunk)
f.close()
message = 'The file "' + fileitem.filename + '" was uploaded successfully'
else:
message = 'No file was uploaded'

print """\
Content-Type: text/html\n
<html><body>
<p>%s</p>
</body></html>
""" % (message,)