Sep 202014
 

The good news is that many inexpensive shared hosts support Python.

The great news is that following this article you should be able to get a pure Python CGI script running and then install the custom packages required to use one of the nice Python web frameworks.

A hosting provider that at least supports Python is required for these instructions to be of
immediate use. However it may be possible to get Python onto a host. If ssh access is available
with a compiler then Python can be installed from source. Otherwise you may be able to extract
and copy a Python binary to your hosting environment but you’ll have to search for information on
doing that, for example egenix-pyrun. You may need to consider your hosts acceptable use policy.
Also placing package directories under the web server directory can be made to work in this case.
As a Python script will import modules from package directories.

A shared hosting environment may only supply Python and CGI. So this will be our starting point as
it is not safe to assume that FastCGI or mod_wsgi etc will be available without checking.

Discover

Unless your shared host provides the exact nature of their Python support you will need to do a
little discovery, even if they do follow along since server configurations change.

Let’s confirm that the basics can be achieved on your host of choice.

Using a control panel or ssh create and place the following files under a web server directory e.g:

~/public_html/dev.example.com/

(note that I’m using a properly configured sub domain here, you don’t have to)

Remember: to set execute permissions for each of the Python (.py) scripts.

cgi_check.py:

#!/usr/bin/env python
 
def main():
    print 'Content-type: text/html\n'
    print 'Pure Python CGI Script'
 
if __name__ == "__main__":
    # Run main when commands read either from standard input,
    # from a script file, or from an interactive prompt.
    main()

.htaccess:

Options +ExecCGI
AddHandler cgi-script .py

Test with a web browser:

http://dev.example.com/cgi_check.py

Once the above check passes we can continue.

If your host provides a ssh jail to login to then you can test for Python simply with:

[~]# python
Python 2.6.6 (r266:84292, Jan 22 2014, 06:01:05) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 
Ctrl+D

A fun little bit of code that may tell you something about the CGI environment.
(Optional step, requires Python cgi to be available)

cgi_test.py:

#!/usr/bin/env python
import cgi
cgi.test()

Python Virtual Environments

The good stuff!
Python virtualenv is actually fairly simple to use and solves dependency and custom package installation challenges.

Get:

cd $HOME
curl https://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.11.6.tar.gz | tar xz

# OR

cd $HOME
wget http://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.11.6.tar.gz
tar xzf virtualenv-1.11.6.tar.gz
rm -R virtualenv-1.11.6.tar.gz

Create:

python virtualenv-1.11.6/virtualenv.py python-virtualenv-myapp

Here we create a virtual environment that will be specific to our app `python-virtualenv-myapp`.
Then below we link it to a directory called `env` inside our web server directories.

Replace `dev.example.com` below.

Link:

ln -s ~/python-virtualenv-myapp ~/public_html/dev.example.com/env

Clean up (optional, suggested on web servers):

rm -R virtualenv-1.11.6

Install any required custom packages to our virtualenv:

cd python-virtualenv-myapp

bin/pip install web.py
bin/pip install flup

# OR

# Using a supplied requirements.txt
wget http://example.com/requirements.txt
bin/pip install -r requirements.txt

Test 1:

[~]# bin/python
Python 2.6.6 (r266:84292, Jan 22 2014, 06:01:05) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import flup
>>> import web
>>> 
Ctrl+D

Yay we can import custom packages!

Test 2:

[~]# cd ~/public_html/dev.example.com/env
[~]# bin/python
>>> 
Ctrl+D

Yay we can access Python from our linked directory!

(See: http://www.dabapps.com/blog/introduction-to-pip-and-virtualenv-python/)
(See: https://virtualenv.pypa.io/en/latest/)

Note: I found the virtualenv shell `activate` annoying so stopped using it.

virtualenv and CGI

Let’s check that we can access our virtualenv from a CGI script.
Replace the contents of the cgi_check.py file that we created earlier.
Note that the shebang (#!) now points to the `env` directory that we linked to our virtualenv.

cgi_check:

#!env/bin/python
 
import sys 
 
print 'Content-type: text/html\n'
print 'Pure Python CGI Script. </br></br>'
print 'Interpreter: %s' % sys.executable

Test with a web browser:

http://dev.example.com/cgi_check.py

You should see the path to the virtualenv interpreter in the web browser.

virtualenv and CGI with web.py

Let’s try a web.py application.
Create code.py under the web server directory.
Remember: to set execute permissions.

code.py:

#!env/bin/python
 
import web
 
urls = (
    '/', 'Index',
    '/test', 'Test'
)
 
 
class Index(object):
    """Respond to requests against application root."""
 
    @staticmethod
    def GET():
        return 'Welcome to Web.py'
 
 
class Test(object):
    """Respond to requests against /test."""
 
    @staticmethod
    def GET():
        return '{"error":0}'
 
 
def main():
    """Called when this module is the primary one."""
 
    app = web.application(urls, globals())
    app.run()
 
if __name__ == "__main__":
    # Run main when commands read either from standard input,
    # from a script file, or from an interactive prompt.
    main()

Test with a web browser:

http://dev.example.com/code.py/

http://dev.example.com/code.py/test

Note: the tailing slash ‘/’ on code.py

That’s it! You now have Python, CGI and a web framework on a shared host!

(See: http://code.google.com/p/modwsgi/wiki/VirtualEnvironments)
(See: http://stackoverflow.com/questions/5800608/django-apache-mod-wsgi-not-using-virtualenvs-python-binary)

Security

A note on security. I take the layered approach to security.

I didn’t find much specific to Python web application security and that’s because the same security requirements apply to all web applications.

Even if you are just testing in your development sub domain. Don’t leave insecure scripts laying around, clean up. Just like any other construction site this keeps you from tripping over yourself. Anything on a server is a possible security hole so remove anything unused.

Set file system permissions to the minimum possible without causing your self undue pain. In a shared host environment a CGI script will usually run as your user. The primary CGI script only has to be readable and executable by the user running the process so chmod 0700 or even 0500 to really lock it down. This prevents your source code being served as text if someone changes the sever configuration.

All library files only need to be readable by the user running the process so chmod 0600 or even 0400 to really lock them down for long stable production periods.

Remove world read and execute rights for our virtualenv:
chmod -R o-r,o-x python-virtualenv-myapp

We created the virtualenv outside of the web server directories and linked to it for a reason. Again this helps prevent serving source code as text. Also helps to keep an easily re-creatable environment out of backups.

Htaccess files need to be readable by the user running the web server, in my case 0644.

A few simple server rules improve security further.

.htaccess:

Options +ExecCGI
AddHandler cgi-script .py
 
# Disable directory index.
Options -Indexes 
 
# Forbid access to /env sub directory.
Redirect 403 /env
 
# Set DirectoryIndex or use a full Rewrite rule.
DirectoryIndex code.py

FastCGI

Since we have flup available we can try FastCGI.

Note that this doesn’t seem to prove that FastCGI is actually being used, it runs either way if I have set `AddHandler cgi-script .py`.
On my shared host it failed if I set `AddHandler fcgid-script .py` or `AddHandler fastcgi-script .py`

How to check which Apache modules are loaded.

fastcgi.py:

#!env/bin/python
 
def myapp(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['Python, flup and FastCGI!\n']
 
if __name__ == '__main__':
    from flup.server.fcgi import WSGIServer
    WSGIServer(myapp).run()

Test with a web browser:

http://dev.example.com/fastcgi.py

Distribution & Deployment

Some notes on distribution and deployment of Python web applications.

A web application can be packaged with the appropriate setup.py then distributed via the Python
Package Index (PyPI).

A possible work flow might be:

get virtualenv
create virtualenv
pip install web-app

A lighter distribution option might be to install from github since pip can install from a requirements.txt file.
The requirements.txt can specify the web-app and dependencies.

A possible work flow might be:

get virtualenv
create virtualenv
get http://example.com/requirements.txt
pip install -r requirements.txt

See: https://www.digitalocean.com/community/tutorials/how-to-package-and-distribute-python-applications

See: https://wiki.python.org/moin/CheeseShopTutorial

See: http://www.buildout.org/

See: Fabric, Carton and bootstrapper.

Pip can install from VCS and URLs specified on the command line or in requirements.txt:

See: http://pip.readthedocs.org/en/latest/reference/pip_install.html#pip-install-examples

pip install http://my.package.repo/SomePackage-1.0.4.zip

pip freeze -l > requirements.txt

pip install -r requirements.txt
pip install -r requirements.txt –allow-all-external

Gavin Kromhout:


Thank you for visiting.
Do look around.
Do leave a comment.

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)