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