Work with Python
IPython also comes with its own You can start the Qt console with:
Qt-based console $ ipython qtconsole
If you get errors related to missing modules,
make sure that you have installed the dependent
packages, such as PyQt, pygments, pyexpect
and ZeroMQ.
Python.framework/Versions/2.7/lib/ %pdef %pdoc %pfile %pinfo %pinfo2
python2.7/posixpath.py %popd %pprint %precision %profile
Docstring: %prun %psearch %psource %pushd %pwd
Common operations on POSIX pathnames. %pycat %pylab %quickref %recall
%rehashx %reload_ext %rep %rerun
Instead of importing this module directly, import %reset %reset_selective %run %save
os and refer to this module as os.path. The %sc %sx %tb %time %timeit %unalias
‘os.path’ name is an alias for this module on %unload_ext %who %who_ls %whos
POSIX systems; on other systems (eg Mac, %xdel %xmode
Windows), os.path provides the same operations
in a manner specific to that platform, and is an Automagic is OFF, % prefix IS needed
alias to another module (eg macpath, ntpath). for magic functions
Some of this can actually be useful on non- To view help on any Magic Function, call
POSIX systems too, eg for manipulation of the ‘%somemagic?’ to read its docstring.
pathname component of URLs.
Python script execution and runtime code
You can also use double question marks (??) to editing: You can use %run to run any Python
view the source code for the relevant object. script. You can also control-run the Python
script with pdb debugger using -d, or pdn
Magic functions: IPython comes with a set of profiler using -p. You can also edit a Python
predefined ‘magic functions’ that you can call script using the %edit command. %edit will
with a command-line-style syntax. IPython open the given Python script in the editor
‘magic’ commands are conventionally prefaced defined by the $EDITOR environment variable.
by %, but if the flag %automagic is set to on,
then you can call magic commands without the Shell command support: If you are in the mood Q IPython Qt console with GUI capabilities
preceding %. to just run a shell command, you can do it very
easily by prefixing the command with ! . As you can see, it’s easy to tailor Python
To view a list of available magic functions, for all your shell environment needs.
you can use ‘magic function %lsmagic’. Magic Example : TIME CMD Python modules like os, subprocess
functions include functions that work with code In [5]: !ps 0:00.07 -bash and shutil are available at your
such as %run, %edit, %macro, %recall etc; 0:00.03 -bash disposal to do just about everything
functions that affect shell such as %colors, PID TTY 0:00.18 -bash you need using Python. IPython turns
%xmode, %autoindent etc; and other functions 4508 ttys000 this whole experience into an even
such as %reset, %timeit, %paste etc. Most of 84275 ttys001 more complete package. You get to do
the cool features of IPython are powered using 17958 ttys002 everything a standard Python shell
magic functions. does and with much more convenient
In [8]: !clang prog.c -o prog features. IPython’s magic functions
Example: prog.c:2:1: warning: type specifier really do provide a magical Python shell
In [45]: %lsmagic missing, defaults to ‘int’ [-Wimplicit- experience. So next time you open a
Available magic functions: int] Bash session, think again: why settle for
%alias %autocall %autoindent main() gold when platinum is a step away?
%automagic %bookmark %cd %colors ^~~~
%cpaste %debug %dhist %dirs 1 warning generated.
%doctest_mode %ed %edit %env %gui
%hist %history %install_default_ Qt console : IPython also comes with its own
config %install_profiles %load_ext Qt-based console. This provides a number of
%loadpy %logoff %logon %logstart features that are only available in a GUI, such
%logstate %logstop %lsmagic %macro as inline figures, multiline editing with syntax
%magic %page %paste %pastebin %pdb highlighting, and graphical calltips .
The Python Book 101
Work with Python
Python for system
administrators
Learn how Python can help in system administration as it dares to
replace the usual shell scripting…
Resources Parsing configuration files RawConfigParser()
Python-devel Python development Configuration files provide a way for applications config.add_section(‘MySQL’)
to store various settings. In order to write a config.set(‘MySQL’,’mysql.trace_
libraries, required for compiling script that allows you to modify settings of a mode’,’Off’)
third-party Python module particular application, you should be able to config.set(‘MySQL’,’mysql.connect_
parse the configuration file of the application. timeout’,’60’)
setuptools setuptools allows you to In this section we learn how to parse INI-style config.set(‘MySQL’,’mysql.default_
configuration files. Although old, the INI file host’,’localhost’)
download, build, install, upgrade, format is very popular with much modern open config.set(‘MySQL’,’mysql.default_
and uninstall Python packages source software, such as PHP and MySQL. port’,’3306’)
with ease Excerpt for php.ini configuration file: config.set(‘MySQL’,’mysql.allow_
[PHP] persistent’, ‘On’ )
System administration is an important part of engine = On config.set(‘MySQL’,’mysql.max_
our computing environment. It does not matter zend.ze1_compatibility_mode = Off persistent’,’20’)
whether you are managing systems at your work short_open_tag = On
our home. Linux, being a UNIX-based operating asp_tags = Off with open(‘php.ini’, ‘ap’) as
system, already has everything a system precision = 14 configfile:
administrator needs, such as the world-class y2k_compliance = On
shells (not just one but many, including Bash, csh, output_buffering = 4096 config.write(configfile)
zsh etc), handy tools, and many other features ;output_handler =
which make the Linux system an administrator’s zlib.output_compression = Off Output:php.ini
dream. So why do we need Python when Linux [MySQL]
already has everything built-in? Being a dynamic [MySQL] mysql.max_persistent = 20
scripting language, Python is very easy to read ; Allow or prevent persistent links. mysql.allow_persistent = On
and learn. That’s just not us saying that, but mysql.allow_persistent = On mysql.default_port = 3306
many Linux distributions actually use Python mysql.max_persistent = 20 mysql.default_host = localhost
in core administrative parts. For example, Red mysql.max_links = -1 mysql.trace_mode = Off
Hat (and Fedora) system setup tool Anaconda mysql.default_port = 3306 mysql.connect_timeout = 60
is written in Python (read this line again, got the mysql.default_socket =
snake connection?). Also, tools like GNU Mailman, mysql.default_host = localhost @code: parseconfig.py
CompizConfig Settings Manager (CCSM) and mysql.connect_timeout = 60 @description: Parsing and updating the config
hundreds of tiny GUI and non-GUI configuration mysql.trace_mode = Off file
tools are written using Python. Python does not Python provides a built-in module called import ConfigParser
limit you on the choice of user interface to follow ConfigParser (known as configparser in Python config = ConfigParser.ConfigParser()
– you can build command-line, GUI and web apps 3.0). You can use this module to parse and create config.read(‘php.ini’)
using Python. This way, it has got covered almost configuration files. # Print config values
all the possible interfaces. @code: writeconfig.py print config.get(‘MySQL’,’mysql.
@description: The following demonstrates
Here we will look into executing sysadmin- adding MySQL section to the php.ini file. Note
related tasks using Python. @warning: Do not use this script with the
actual php.ini file, as it’s not designed to This is written for the Python 2.X series,
handle all aspects of a complete php.ini file. as it is still the most popular and default
import ConfigParser Python distribution across all the
config = ConfigParser. platforms (including all Linux distros,
BSDs and Mac OS X).
102 The Python Book
Work with Python
default_host’) JSON PYTHON DATA MAPPING >>> results[‘totalResultsReturned’]
print config.get(‘MySQL’,’mysql. 20
default_port’) JSON Python >>> items = results[‘Result’]
config.remove_option(‘MySQL’,’mysql. >>> for Result in items:
trace_mode’) object dict ... print
with open(‘php.ini’, ‘wb’) as Result[‘Title’],Result[‘Url’]
configfile: array list ...
Linux User http://www.linuxuser.
config.write(configfile) string unicode co.uk/
Linux User and Developer -
Parsing JSON data number (int) int, long Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Linux_
JSON (also known as JavaScript Object number (real) float User_and_Developer
Notation) is a lightweight modern data- Linux User & Developer |
interchange format. JSON is an open standard TRUE TRUE Linux User http://www.linuxuser.
under ECMA-262. It is a text format and is co.uk/tag/linux-user-developer/
completely language-independent. JSON FALSE FALSE
is also used as the configuration file format Gathering system
for modern applications such as Mozilla null None information
Firefox and Google Chrome. JSON is also
very popular with modern web services such For this section we will use the simplejson. One of the important jobs of a system
as Facebook, Twitter, Amazon EC2 etc. In load function, which allows us to deserialise a administrator is gathering system information.
this section we will use the Python module JSON object into a Python object. In this section we will use the SIGAR (System
‘simplejson’ to access Yahoo Search (using @code: LUDSearch.py Information Gatherer And Reporter) API to
the Yahoo Web Services API), which outputs import simplejson, urllib demonstrate how we can gather system
JSON data. APP_ID = ‘xxxxxxxx’ # Change this to information using Python. SIGAR is a very
your APP ID complete API and it can provide lot of
To use this section, you should have the SEARCH_BASE = ‘http://search. information, including the following:
following: yahooapis.com/WebSearchService/V1/ 1. System memory, swap, CPU, load average,
1. Python module: simplejson. webSearch’ uptime, logins.
Note: You can install Python modules using the 2. Per-process memory, CPU, credential info,
command ‘easy_install <module name>’. This class YahooSearchError(Exception): state, arguments, environment, open files.
command assumes that you have a working pass 3. File system detection and metrics.
internet connection. 4. Network interface detection, configuration
2. Yahoo App ID: The Yahoo App ID can be def search(query, results=20, info and metrics.
created from https://developer.apps.yahoo. start=1, **kwargs): 5. TCP and UDP connection tables.
com/dashboard/createKey.html. The Yahoo 6. Network route table.
App ID will be generated on the next page. See kwargs.update({
the screenshot below for details. ‘appid’: APP_ID, Installing SIGAR
‘query’: query,
Q Generating the Yahoo App ID ‘results’: results, The first step is to build and install SIGAR. SIGAR
‘start’: start, is hosted at GitHub, so make sure that you have
simplejson is very easy to use. In the following ‘output’: ‘json’ Git installed in your system. Then perform
example we will use the capability of mapping the following steps to install SIGAR and its
JSON data structures directly to Python data }) Python bindings:
types. This gives us direct access to the JSON url = SEARCH_BASE + ‘?’ + $ git clone git://github.com/
data without developing any XML parsing code. urllib.urlencode(kwargs) hyperic/sigar.git sigar.git
result = simplejson.load(urllib. $ cd sigar.git/bindings/python
urlopen(url)) $ sudo python setup.py install
if ‘Error’ in result:
Python doesn’t
# An error occurred; raise limit your choice
an exception of interface
raise YahooSearchError,
result[‘Error’]
return result[‘ResultSet’]
Let’s use the above code from the Python shell
to see how it works. Change to the directory
where you have saved the LUDYSearch.py and
open a Python shell.
@code: Python Shell Output. Lines starting
with ‘>>>’ indicate input
>>> execfile(“LUDYSearch.py”)
>>> results = search(‘Linux User and
Developer’)
>>> results[‘totalResultsAvailable’]
123000000
The Python Book 103
Work with Python
Swap: 131072 16048 115024 split(‘@’)
else:
RAM: 8192 MB
hostname = raw_input(‘Hostname: ‘)
==========File System if len(hostname) == 0:
At the end you should see a output similar to Information=============== print ‘*** Hostname required.’
sys.exit(1)
the following : Filesystem Size Used Avail port = 22
if hostname.find(‘:’) >= 0:
Writing /usr/local/lib/python2.6/ Use% Mounted on Type hostname, portstr = hostname.
split(‘:’)
dist-packages/pysigar-0.1.egg-info /dev/disk0s2 300G 175G 124G 59.0 / hfs port = int(portstr)
# get username
SIGAR is a very easy-to-use library and can be / local if username == ‘’:
default_username = getpass.
used to get information on almost every aspect of devfs 191K 191K 0 - /dev devfs / getuser()
username = raw_input(‘Username
a system. The next example shows you how. none [%s]: ‘ % default_username)
if len(username) == 0:
The following code shows the memory and the Accessing Secure Shell
(SSH) services username = default_username
file system information password = getpass.getpass(‘Password
SSH (Secure Shell) is a modern replacement for an for %s@%s: ‘ % (username, hostname))
@code: PySysInfo.py old remote shell system called Telnet. It allows data # now, connect and use paramiko
to be exchanged using a secure channel between Client to negotiate SSH2 across the
import os two networked devices. System administrators connection
frequently use SSH to administrate networked try:
import sigar systems. In addition to providing remote shell, SSH
is also used for secure file transfer (using SSH File client = paramiko.SSHClient()
sg = sigar.open() Transfer Protocol, or SFTP) and remote X server client.load_system_host_keys()
forwarding (allows you to use SSH clients as X client.set_missing_host_key_
mem = sg.mem() server). In this section we will learn how to use the policy(paramiko.WarningPolicy)
SSH protocol from Python using a Python module print ‘*** Connecting...’
swap = sg.swap() called paramiko, which implements the SSH2 client.connect(hostname, port,
protocol for Python. username, password)
fslist = sg.file_system_list() paramiko can be installed using the following chan = client.invoke_shell()
steps: print repr(client.get_transport())
print “==========Memory $ git clone https://github.com/robey/ print ‘*** SSH Server Connected!
paramiko.git ***’
Information==============” $ cd paramiko print
$ sudo python setup.py install interactive.interactive_
print “\tTotal\tUsed\tFree” To the core of paramiko is the shell(chan)
SSHClient class. This class chan.close()
print “Mem:\t”,\ wraps L{Transport}, L{Channel}, and L{SFTPClient} client.close()
to handle most of the aspects of SSH. You can use except Exception, e:
(mem.total() / 1024), \ SSHClient as: print ‘*** Caught exception: %s:
%s’ % (e.__class__, e)
(mem.used() / 1024), \ client = SSHClient() traceback.print_exc()
client.load_system_host_keys() try:
(mem.free() / 1024) client.connect(‘some.host.com’)
stdin, stdout, stderr = client.exec_ client.close()
print “Swap:\t”, \ command(‘dir’) except:
The following example demonstrates a full SSH
(swap.total() / 1024), \ client written using the paramiko module. pass
@code: PySSHClient.py sys.exit(1)
(swap.used() / 1024), \ import base64, getpass, os, socket, sys, To run this code you will also need a custom
socket, traceback Python class interactive.py which implements
(swap.free() / 1024) import paramiko
import interactive Note
print “RAM:\t”, mem.ram(), “MB” # setup logging
paramiko.util.log_to_file(‘demo_simple. If you are confused with the tab spacing of
print “==========File System log’) the code, look for the code files on FileSilo.
# get hostname
Information===============” username = ‘’
if len(sys.argv) > 1:
def format_size(size):
hostname = sys.argv[1]
return sigar.format_size(size * if hostname.find(‘@’) >= 0:
1024) username, hostname = hostname.
print ‘Filesystem\tSize\tUsed\
tAvail\tUse%\tMounted on\tType\n’
for fs in fslist:
dir_name = fs.dir_name()
usage = sg.file_system_
usage(dir_name)
total = usage.total()
used = total - usage.free()
avail = usage.avail()
pct = usage.use_percent() * 100
if pct == 0.0:
pct = ‘-’
print fs.dev_name(), format_
size(total), format_size(used),
format_size(avail),\
pct, dir_name, fs.sys_type_
name(), ‘/’, fs.type_name()
@Output
==========Memory
Information==============
Total Used Free
Mem: 8388608 6061884 2326724
104 The Python Book
Work with Python
the interactive shell for the SSH session. Look Writing a user interface using Python
for this file on FileSilo and copy it into the same
folder where you have created PySSHClient.py . Learn how to create a user-friendly interface using Python
@code_Output
kunal@ubuntu-vm-kdeo:~/src/paramiko/ Administrators are comfortable with running raw (((“Ok”), “ok”), ((“Cancel”),
demos$ python demo_simple.py scripts by hand, but end-users are not. So if you “cancel”)))
Hostname: 192.168.1.2 are writing a script that is supposed to be used by
Username [kunal]: luduser common users, it is a good idea to create a user- e=Entry(3, str(entry_
Password for [email protected]: friendly interface on top of the script. This way value))
*** Connecting... end-users can run the scripts just like any other
<paramiko.Transport at 0xb76201acL application. To demonstrate this, we will create l=Label((“Timeout (in
(cipher aes128-ctr, 128 bits) a simple GRUB configuration tool which allows seconds):”))
(active; 1 open channel(s))> users to select default boot entry and the timeout.
*** SSH Server Connected! *** We will be creating a TUI (text user interface) gg=Grid(2,1)
Last login: Thu Jan 13 02:01:06 2011 application and will use the Python module gg.setField(l,0,0)
from 192.168.1.9 ‘snack’ to facilitate this (not to be confused with gg.setField(e,1,0)
[~ $:] the Python audio library, tksnack).
If the host key for the SSH server is not added
to your $HOME/.ssh/known_hosts file, the This app consists of two files…
client will throw the following error:
*** Caught exception: <type grub.py: GRUB Config File (grub.conf) Parser
‘exceptions.TypeError’>: unbound (available on FileSilo). It implements two main
method missing_host_key() must be functions, readBootDB() and writeBootFile(),
called with WarningPolicy instance which are responsible for reading and writing the
as first argument (got SSHClient GRUB configuration file.
instance instead)
This means that the client cannot verify the grub_tui.py: Text user interface file for
authenticity of the server you are connected manipulating the GRUB configuration file using
to. To add the host key to known_hosts, you the functions available in grub.py.
can use the ssh command. It is important
to remember that this is not the ideal way to @code:grub_tui.py
add the host key; instead you should use ssh-
keygen. But for simplicity’s sake we are using import sys
the ssh client.
kunal@ubuntu-vm-kdeo:~/.ssh$ ssh from snack import *
[email protected]
The authenticity of host from grub import (readBootDB, g.add(Label(‘’),0,1)
‘192.168.1.2 (192.168.1.2)’ can’t be writeBootFile) g.add(gg,0,2)
established. g.add(Label(‘’),0,3)
RSA key fingerprint is be:01:76:6a:b
9:bb:69:64:e3:dc:37:00:a4:36:33:d1. def main(entry_ g.add(bb,0,4,growx=1)
Are you sure you want to continue value=’1’,kernels=[]): result = g.runOnce()
connecting (yes/no)? yes if
Warning: Permanently added try: bb.buttonPressed(result) ==
‘192.168.1.2’ (RSA) to the list of (default_value, entry_ ‘cancel’:
known hosts.
value, kernels)=readBootDB() screen.finish()
So now you’ve seen how easy it can be to except: sys.exit(0)
carry out the complex sysadmin tasks using print >> sys.stderr, else:
Python’s versatile language. entry_value =
(“Error reading /boot/grub/grub. e.value()
As is the case with all Python coding, the conf.”) try :
code that is presented here can easily be
adopted into your GUI application (with software sys.exit(10)
such as PyGTK or PyQt) or a web application
(using a framework such as Django or Grok). c = int(entry_
screen=SnackScreen() value)
while True: break
g=GridForm(screen, (“Boot except ValueError:
configuration”),1,5) continue
if len(kernels)>0 : writeBootFile(c,
li=Listbox(height=len(kernels), li.current())
width=20, returnExit=1) screen.finish()
for i, x in if __name__== ‘__main__’:
enumerate(kernels): main()
li.append(x,i)
g.add(li, 0, 0) Start the tool using the sudo
li.setCurrent(default_value) command (as it reads the grub.
conf file)
bb = ButtonBar(screen, $ sudo grub_tui.py
The Python Book 105
Create
Pywiththon
108 Build tic-tac-toe with Kivy 124
Program noughts and crosses “You’ll be surprised by the
diversity of what you can
112 Create two-step
authentication make with Python”
Use Twilio for safe authentication 112
116 Program a Space
Invaders clone
Make the basic Pivaders game
120 Add animation and sound
Enhance your Pivaders game
124 Make a visual novel game
Use Python to make a storytelling game
128 Pygame Zero
Turn your ideas into games
108
106 The Python Book
116
The Python Book 107
Create with Python
Build tic-tac-toe with Kivy
Ease into the workings of Kivy by creating the pen-and-paper classic
in just over 100 lines of Python...
Kivy is a highly cross-platform graphical information on Kivy’s website. A kivy application is Q The classic ‘Hello World!’ in Kivy GUI form,
framework for Python, designed for the started by instantiating and running an ‘App’ class. using the built-in Label widget
creation of innovative user interfaces like This is what initialises our pp’s window, interfaces
multitouch apps. Its applications can run not with the OS, and provides an entry point for the color=0, 1, 0, 1)) # (r, g, b, a)
only on the traditional desktop platforms of Linux, creation of our GUI. We can start by making the
OS X and Windows, but also Android and iOS, plus simplest Kivy app possible: The ‘build’ method is called when the ‘App’ is run,
devices like the Raspberry Pi. and whatever widget is returned automatically
from kivy.app import App becomes the root widget of that App’. In our case
That means you can develop cross-platform that’s a Label, and we’ve set several properties -
apps using Python libraries such as Requests, class TicTacToeApp(App): the ‘text’, ‘font_size’ and ‘color’. All widgets have
SQLAlchemy or even NumPy. You can even pass different properties controlling aspects of their
access native mobile APIs straight from Python behaviour, which can be dynamically updated to
using some of Kivy’s sister projects. Another if __name__ == “__main__”: alter their appearance later, though here we set
great feature is the Cython-optimised OpenGL TicTacToeApp().run() them just once upon instantiation.
graphics pipeline, allowing advanced GPU effects
even though the basic Python API is very simple. You can already run this, your app will start up and Note that these properties are not just Python
you’ll get a plain black window. Exciting! attributes but instead Kivy properties. These are
Kivy is a set of Python/Cython modules that accessed like normal attributes but provide extra
can easily be installed via pip, but you’ll need a We can build our own GUI out of Kivy widgets. functionality by hooking into Kivy’s event system.
few dependencies. It uses Pygame as a rendering Each is a simple graphics element with some We’ll see examples of creating properties shortly,
backend (though its API is not exposed), Cython specific behaviour of its own ranging from and you should do the same if you want to use your
for compilation of the speedy graphics compiler standard GUI functionality (eg the Button, Label variableswith Kivy’seventorbindingfunctionality.
internals, and GStreamer for multimedia. These or TextInput), to those that impose positioning on
should all be available through your distro’s their child widgets (eg the BoxLayout, FloatLayout That’s all you need to show some simple text,
repositories, or via pip where applicable. or GridLayout), to those abstracting a more so run the program again to check that this does
involved task like interacting with hardware (eg work. You can experiment with the parameters if it’s
With these dependencies satisfied, you the FileChooser, Camera or VideoPlayer). Most unclear what any of them are doing.
should be able install Kivy with the normal pip importantly, Kivy’s widgets are designed to be
incantation. The current version is 1.8.0, and the easily combined - rather than including a widget Our own widget: tic-tac-toe
same codebase supports both python2 and for every need imaginable, widgets are kept simple
python3. The code in this tutorial is also version- but are easy to join to invent new interfaces. We’ll Since Kivy doesn’t have a tic-tac-toe widget, we’ll
agnostic, running in python2.7 and python3.3. see some of that in this tutorial. have to make our own! It’s natural to create a new
widget class to contain this behaviour:
pip install kivy Since ‘Hello World!’ is basically compulsory in
If you have any problems with pip, you can use any programming tutorial, let’s get it over with by from kivy.uix.gridlayout import GridLayout
easy_instalvia easy_install kivy. using a simple ‘Label’ widget to display the text: class TicTacToeGrid(GridLayout):
There are also packages or repositories available from kivy.uix.label import Label pass
for several popular distros. You can find more
We’ll display the ‘Label’ by returning it as our app’s Now this obviously doesn’t do anything yet,
You can develop root widget. Every app has a single root widget, the except that it inherits all the behaviour of the
cross-platform top level of its widget tree, and it will automatically Kivy GridLayout widget - that is, we’ll need to
apps using various be sized to fill the window. We’ll see later how to tell it how many columns to have, but then it will
Python libraries construct a full GUI by adding more widgets for this
one, but for now it’s enough to set the root widget
by adding a new method to the ‘App’:
def build(self):
return Label(text=’Hello World!’,
font_size=100,
108 The Python Book
Create with Python
Kivy comes
with all the tools
needed to use kv
language
Q A tic-tac-toe grid now accepting input, adding a O or X alternately As before, this syntax defines a rule for how a
‘GridEntry’ widget should be constructed, this
automatically arrange any child widgets to fit the former, creating a rule for the ‘TicTacToeGrid’ time setting the ‘font_size’ property that controls
nicely with as many rows as necessary. Tic-tac-toe widget by declaring that every ‘TicTacToeGrid’ the size of the text in the button’s label. The extra
requires three columns and nine children. instantiated should have its ‘cols’ property set to 3. magic is that kv language automatically detects
that we’ve referenced the Button’s own height and
Here we introduce the Kivy language (kv), a We’ll use some more kv language features later, will create a binding to update this relationship
special domain-specific language for making but for now let’s go back to Python to create the – when a ‘GridEntry’ widget’s height changes, its
rules describing Kivy widget trees. It’s very simple buttons that will be the entries in our tic-tac-toe grid. ‘font_size’ will change so the text fits perfectly.
but removes a lot of necessary boilerplate for We could have made these bindings straight
manipulating the GUI with Python code - as a loose from kivy.uix.button import Button from Python (another usage of the ‘bind’ method
analogy you might think of it as the HTML/CSS to from kivy.properties import ListProperty used later on), but that’s rarely as convenient as
Python’s JavaScript. Python gives us the dynamic referencing the property we want to bind to.
power to do anything, but all that power gets in the class GridEntry(Button):
way if we just want to declare the basic structure coords = ListProperty([0, 0]) Let’s now populate our ‘TicTacToeGrid’ with
of our GUI. Note that you never need kv language, ‘GridEntry’ widgets (Fig.01). This introduces a
you can always do the same thing in Python alone, This inherits from Kivy’s ‘Button’ widget, which few new concepts: When we instantiated our
but the rest of the example may show why Kivy interacts with mouse or touch input, dispatching ‘GridEntry’ widgets, we were able to set their
programmers usually like to use kv. events when interactions toggle it. We can hook ‘coords’ property by simply passing it in as a
into these events to call our own functions when a kwarg. This is a minor feature that is automatically
Kivy comes with all the tools needed to use kv user presses the button, and can set the button’s handled by Kivy properties.
language; the simplest way is to write it in a file with ‘text’ property to display the ‘X’ or ‘O’. We also
a name based on our App class. That is, we should created a new Kivy property for our widget, ‘coords’ We used the ‘bind’ method to call the
place the following in a file named ‘tictactoe.kv’: – we’ll show how this is useful later on. It’s almost grid’s ‘button_pressed’ method whenever
identical to making a normal Python attribute by the `GridEntry` widget dispatches an
<TicTacToeGrid>: writing ‘self.coords = [0, 0]’ in ‘GridEntry.__init__’. ‘on_release’ event. This is automatically
cols: 3 # Number of columns handled by its ‘Button’ superclass, and
As with the ‘TicTacToeGrid’, we’ll style our new will occur whenever a user presses, then
This is the basic syntax of kv language; for each class with kv language, but this time we get to see releases a ‘GridEntry’ button. We could also
widget type we may write a rule defining its a more interesting feature. bind to ‘on_press’, which is dispatched when the
behaviour, including setting its properties and <GridEntry>: button is first clicked, or to any Kivy property of
adding child widgets. This example demonstrates the button, which is dispatched dynamically
font_size: self.height whenever the property is modified.
We added each ‘GridEntry’ widget to our ‘Grid’
via the ‘add_widget’ method. That means each
one is a child widget of the ‘TicTacToeGrid’, and
so it will display them and knows it should
automatically arrange them into a grid with the
number of columns we set earlier.
Now all we have to do is replace our root widget
(returned from ‘App.build’) with a ‘TicTacToeGrid’
and we can see what our app looks like.
The Python Book 109
Create with Python
def build(self):
return TicTacToeGrid()
# Replaces the previous label
With this complete you can run your main Python
file again and enjoy your new program. All being
well, the single Label is replaced by a grid of
nine buttons, each of which you can click (it will
automatically change colour) and release (you’ll
see the printed output information from our
binding). We could customise the appearance by
modifying other properties of the Button, but for
now we’ll leave them as they are.
Has anyone won yet?
We’ll want to keep track of the state of the board to
check if anyone has won, which we can do with a
couple more Kivy properties:
from kivy.properties import
(ListProperty, NumericProperty)
class TicTacToeGrid(GridLayout): Q The game with final additions, making the grid square and extending the interface
status = ListProperty([0, 0, 0,
0, 0, 0, it is updated, doing something special if a player This covers the basic detection of a won or drawn
0, 0, 0]) has filled a column, row or diagonal. board, but it only prints the result to stdout. At this
current_player = NumericProperty(1) stage we probably want to reset the board so that
def on_status(self, instance, new_value): the players can try again, along with displaying a
This adds an internal status list representing who status = new_value graphicalindicatoroftheresult(Fig. 03).
has played where, and a number to represent the
current player (1 for ‘O’, -1 for ‘X’). By placing these # Sum each row, column and diagonal. Finally, we can modify the `on_status` method
numbers in our status list, we’ll know if somebody # Could be shorter, but let’s be extra to both reset the board and display the winner
wins because the sum of a row, column or diagonal # clear what’s going on in a ‘ModalView’ widget.
will be +-3. Now we can update our graphical grid sums = [sum(status[0:3]), # rows
whenamoveisplayed(Fig. 02). from kivy.uix.modalview import ModalView
sum(status[3:6]),
You can run your app again to see exactly what sum(status[6:9]), This is a pop-up widget that draws itself on top of
this did, and you’ll find that clicking each button sum(status[0::3]), # columns everything else rather than as part of the normal
now places an ‘O’ or ‘X’ as well as a coloured sum(status[1::3]), widget tree. It also automatically closes when the
background depending on whose turn it is to sum(status[2::3]), user clicks or taps outside it.
play. Not only that, but you can only play one sum(status[::4]), # diagonals
move in each button thanks to our status sum(status[2:-2:2])] winner = None
array keeping track of existing moves. if -3 in sums:
# Sums can only be +-3 if one player
This is enough to play the game but there’s one # filled the whole line winner = ‘Xs win!’
vital element missing... a big pop-up telling you if 3 in sums: elif 3 in sums:
when you’ve won! Before we can do that, we need
to add some code to check if the game is over. print(‘Os win!’) winner = ‘Os win!’
elif -3 in sums: elif 0 not in self.status:
Kivy properties have another useful feature
here, whenever they change they automatically print(‘Xs win!’) winner = ‘Draw...nobody wins!’
call an ‘on_propertyname’ method if it exists elif 0 not in self.status: # Grid full
and dispatch a corresponding event in Kivy’s if winner:
event system. That makes it very easy to write print(‘Draw!’) popup = ModalView(size_hint=0.75, 0.5))
code that will run when a property changes, victory_label = Label(text=winner,
both in Python and kv language. In our case
we can use it to check the status list every time
110 The Python Book
Create with Python
font_size=50) Try swapping out the different widget
popup.add_widget(victory_label) types to see how other widgets behave
popup.bind(on_dismiss=self.reset)
popup.open() class TicTacToeGrid(GridLayout): Fig 01
def __init__(self, *args, **kwargs):
This mostly uses the same ideas we already super(TicTacToeGrid, self).__init__(*args, **kwargs) Code on
covered, adding the ‘Label’ widget to the for row in range(3): FileSilo
‘ModalView’ then letting the ‘ModalView’ take for column in range(3):
care of drawing itself and its children on top of grid_entry = GridEntry(
everything else. We also use another binding; this coords=(row, column))
time to ‘on_dismiss’, which is an event dispatched grid_entry.bind(on_release=self.button_pressed)
by the ‘ModalView’ when it is closed. Finally, we self.add_widget(grid_entry)
made use of the ‘size_hint’ property common
to all widgets, which in this case is used to set def button_pressed(self, instance):
the ‘ModalView’ size proportional to the window # Print output just to see what’s going on
– while a ‘ModalView’ is open you can resize print(‘{} button clicked!’.format(instance.coords))
the window to see it dynamically resize, always
maintaining these proportions. This is another trick def button_pressed(self, button): Fig 02
made possible by a binding with the ‘size_hint’ Kivy # Create player symbol and colour lookups
property, this time managed internally by Kivy. player = {1: ‘O’, -1: ‘X’}
colours = {1: (1, 0, 0, 1), -1: (0, 1, 0, 1)} # (r, g, b, a)
That’s it, a finished program! We can now
not only play tic-tac-toe, but our program row, column = button.coords # The pressed button is automatically
automatically tells us when somebody has won, # passed as an argument
and resets the board so we can play again. Simply
run your program and enjoy hours of fun! # Convert 2D grid coordinates to 1D status index
status_index = 3*row + column
Time to experiment already_played = self.status[status_index]
This has been a quick tour through some of Kivy’s # If nobody has played here yet, make a new move
features, but hopefully it demonstrates how if not already_played:
to think about building a Kivy application. Our
programs are built from individual Kivy widgets, self.status[status_index] = self.current_player
interacting by having Python code run when their button.text = {1: ‘O’, -1: ‘X’}[self.current_player]
properties change (eg our ‘on_status’ method) button.background_color = colours[self.current_player]
or when they dispatch events (eg ‘Button’ ‘on_ self.current_player *= -1 # Switch current player
release’). We also briefly saw kv language and
experienced how it can automatically create # Note the *args parameter! It’s important later when we make a binding Fig 03
bindings between properties. # to reset, which automatically passes an argument that we don’t care about
def reset(self, *args):
You can find a copy of the full program on
FileSilo, which you can reference to check you’ve self.status = [0 for _ in range(9)]
followed everything correctly. We’ve also added
an extra widget, the ‘Interface’, with a structure # self.children is a list containing all child widgets
coded entirely in kv language that demonstrates for child in self.children:
how to add child widgets this way. You can test
it by uncommenting the ‘return Interface()’ line child.text = ‘’
in ‘TicTacToeGrid.build’. It doesn’t do anything child.background_color = (1, 1, 1, 1)
fundamentally different to what we already
covered, but it does make extensive use of kv
language’s binding ability to automatically update
a label showing the current player, and to resize
the TicTacToeGrid so that it is always square to
fit within its parent. You can play with all these
settings to see exactly how it fits together, or try
things like swapping out the different widget types
to see how other widgets behave.
self.current_player = 1
The Python Book 111
Create with Python
Create a two-step
authentication with Twilio
Increase security in access to your web services
by building a simple two-step authentication
with Twilio’s SMS APIs to help you
Resources Telephony is one of the most versatile 01 Get a Twilio account and
technologies in our households. Despite being phone number
Python 2.7+ invented over 100 years ago, we still use the Signing up to Twilio is pretty easy. First, head
same basic infrastructure that once only carried over to http://twilio.com and click the ‘Signup’
Flask 0.10.0: the voices of people to deliver a rich multitude of button. At this point, the sign-up process
media content at incredible speeds. As is often doesn’t really differ from any other service,
flask.pocoo.org/ the case with wonderful things, they can often be but after you’ve entered an email address and
complex too – and yet phones are more important password you’ll be asked for a phone number.
Flask Github: now to our daily lives than ever. So, what can we Given the nature of Twilio’s API, it makes sense
github.com/mitsuhiko/flask do to leverage some of that versatile technology? for them to ask whether we’re human, and
having them text us is a good way to confirm
A Twilio account: Well, for starters we can use an API. Twilio that. Hey, it’s a two-step authentication, which
twilio.com has created a RESTful API that removes a great is exactly what we’re working towards.
deal of that complexity of telephony so that we
Twilio’s Python REST can write apps and services that are able to You can enter any number you have access
API Helper Library: deliver and receive both phone calls and SMS to, be it a landline or mobile, to confirm who
using various endpoints and services. Neat! In you are, but at this point we suggest you
github.com/twilio/twilio-python/zipball/master this tutorial, we’re going to look at using Twilio authenticate using a phone that can accept
to help us create the basic flow for a two-step SMS (instead of a landline). Having entered your
MySQLDB: authentication system for logging into a service. number, you’ll receive a text to authenticate
mysql-python.sourceforge.net We’re also going to be using Flask to help us your phone – enter it and you’ll be presented
create our routes and generate our pages, but with a Twilio phone number. This is your Twilio
little of Flask’s detail will be covered here. phone number and you’ll be using it to send and
receive our authentication texts.
Q The Twilio interface is kept nice and simple – no unnecessary complications here 02 Add credit
Just like a mobile phone operator,
Twilio is not a free service – although it is very
inexpensive. In order to continue, we’ll need
to add a card and some funds to our newly
created Twilio account. On the main page of the
dashboard, you’ll see a big blue dialog asking
to upgrade your trial account; click through
and follow the instructions to add a card and
the amount of credit you would like to use. The
minimum amount of $20 (around £10 GBP) will
be more than plenty for this and other projects.
Once that’s done, you’re almost ready to start
sending text messages – but first head back
over to the Twilio dashboard and copy your
account SID and auth token down somewhere,
you’ll need those a little later.
112 The Python Book
Create with Python
03 Install the Twilio Helper Library import MySQLdb Fig 01
and MySQLDB
The Twilio helper library is a fantastic piece of from flask import Flask, redirect, request, session, render_template
code that lets you jump straight into sending and
handling text messages in no time at all. There from twilio.rest import TwilioRestClient as twilio
are a couple of ways to install the library: you can
use either PIP or Easy_Install, like so import string, random, time
$ pip install twilio db = MySQLdb.connect(host="127.0.0.1", user="SQLUSER",
$ easy_install twilio passwd="SQLPASS", db="two-step", port=3306)
Or you can download the source code for the expirationLength = 300
helper library and run the ‘setup.py’ file. It
really is as simple as that. Now, for storing the account_sid = "YOUR ACCOUNT SID"
verification tokens we’re going to use a MySQL auth_token = "YOUR ACCOUNT AUTH TOKEN"
database. To get Python talking to our SQL client = twilio(account_sid, auth_token)
server, we’ll use the Python module MySQLDB,
the package for which you can grab like so… @app.route('/')
def index():
apt-get install python-mysqldb
return "index page"
In the tutorial resources we have included an
SQL dump with the table structure. Import it @app.route('/login', methods=['GET'])
into a database of your choosing. Assuming def login():
everything so far has gone swimmingly, you can
create a new project folder/environment and return "login page"
add a new file ‘server.py’.
@app.route('/check-user', methods=['POST'])
04 Server setup def checkUser():
Open the ‘server.py’ file for editing. The
first thing we're going to do is import the libraries return "check user page"
we need for our authentication flow, create the
endpoints for our server and assign some of the @app.route('/logout')
variables needed to run our Flask server. (Fig 01) def logout():
You may have noticed the account_sid and return "logout page"
auth_token variable we’ve set after the import
statements. We’ll use these with our Twilio @app.route('/verify', methods=['GET'])
client so we can interact with Twilio and our def twoStep():
mobile phones. These settings can be found
on the Twilio account dashboard, right below return "verify page"
the header. We’ve also connected to our SQL
database, so make sure your SQL server is @app.route('/check-code', methods=['POST'])
running before you fire up the app, otherwise def checkCode():
you’ll have an error thrown. Save, now if you run
your ‘server.py’ file, you should be able to access return "check code page"
the index page of your server at 127.0.0.1:5000/.
if __name__ == '__main__':
05 Server logic
If you’ve hit all of your server endpoints app.secret_key = 'R4nDOMCRypt0gr4ph1cK3yf0R5355i0N'
already, so far all you will see are the strings we app.run(host='0.0.0.0', debug=True)
returned at the end of endpoint declarations.
These are not all that good-looking, so let’s your endpoints as in Fig 02. If you revisit the its particular variables set, otherwise we’ll end
add some Flask templates to pretty things pages again, things might seem a little out of up getting KeyErrors.
up a little. The focus of this tutorial is not on whack at the moment, but don’t worry about
the intricacies of Flask and as such, included that for the time being. It’s mostly because @app.route(‘/’)
on the DVD is a folder called ‘templates’ and we’ve not put together the server logic to help def index():
another called ‘static’; copy them both to the the templates figure out what to do.
root of your current project folder and amend checkSessionState()
Let’s deal with the ‘/’ path first. All we’re
doing here is checking the state of our session return render_template(‘index.
cookies and effecting how the index.html html’)
page renders according to that state. If the
user isn’t logged in, we’ll give them a link to the def checkSessionState():
login page, if the user is logged in but hasn’t
verified, then we’ll give them a link to the
code verification page. Before we deliver the
template we need to check that our session has
The Python Book 113
Create with Python
try: result = cur.fetchone() that will make up the body of our message,
session[‘verified’] == True returnedPassword = result[2] the number that we want to send it to and the
returnedPhoneNumber = result[3] number that we want to send it from. When
except KeyError: inputting the number that we want to send it
session[‘verified’] = ’’ We can then build an SQL statement using cur. to, it’s best to use the +CountryCode type of
execute().Noticethe %s; this will bereplacedwith phone number to avoid any ambiguity about
try: the value passed through in the next variable. We the intended recipient. The number that we’re
session[‘loggedin’] == True execute the statement with cur.fetchone(), which sending from is our Twilio number; you can
will get us the first row that the query returns – if use any Twilio number you have assigned to
except KeyError: there is no result we’ll get None and we can then your account, so long as it has credit. As soon
session[‘loggedin’] = ’’ return the user to the login page with an error as we execute that code, the message will be
message. Let’s assume we’ve requested a valid sent and your SMS will go straight through to
try: user–we’llnextcheckthatthepasswordassigned the telephone. The SID is the unique identifier
session[‘user’] == True to that user is the same as the one submitted. If so, for the message/call sent; receiving it means
we’ll generate the validation code to send to the the message has been executed successfully.
except KeyError: user, which we’ll store in the verification table of After that, we can redirect our user to the
session[‘user’] = ’’ our database until it’s used or expires. We’ll need to verification page with return redirect(‘/
create a new cursor to insert the verification code verify’) at the end of /check-user.
06 Loggingin into our table. After we’ve executed the statement
The first step in two-step authentication we need to commit the changes to the database, 08 Check verification code
is logging in with a traditional username/email we do this with db.commit() – we’ll then add the At this point the user will have received
and password. Access your database and create results of the query to our session so we can check a text message with something along the lines
a new user with the following query: against them later. (Fig 03) of ‘Your verification code is: 12cd56’ and will
be presented with the verification page. If, at
INSERT INTO users (username, password, 07 Send the verification code this point, they choose to browse around our
phonenumber) VALUES (‘A USERNAME', ‘A Now that we believe we’re dealing with site, they won’t be able to access anything that
PASSWORD', ‘+44YOURUSERSPHONENUMBER') a valid user, it’s time for the second step of our we don't want them to. Still, we’ll know that
two-step process. On the line after where we they’ve logged in, so if they head back to the
For the purposes of this tutorial, the password stored a variable in our session, we make a call verification page, we can just let them input
is plain text – but we implore you, when you’re to sendVerificationCode (VERIFICATION their code. Once they submit their code, it will
implementing passwords in a live environment, CODE, USER PHONE NUMBER) and pass be sent to the /check-code endpoint.
make sure that you hash them. Still, for now we’re through the code we want to send to our user
going to use a plain text password. Our login.html and the user’s phone number. So what does Just like before when we checked for our
template has a form that’s going to POST itself that function actually look like? It must be user’s validity, we’re going to attempt to retrieve
to check-user; here we'll check the validity of big, long and complicated because it deals the verification code and check it. (Fig 04)
the credentials and then trigger the verification with the telecommunications network, right?
if needed. So we’re going to use the MySQLDB Wrong. It’s actually incredibly simple to send First we’re simply going to retrieve the code
module to get details from our database. an SMS with Twilio. In fact, part of the inherent and check the user it has assigned to it. If that
beauty of Twilio lies in its simplicity. To send a user assigned to the code matches the user
In order to query our database we need to text, all we have to do is: in our session, then we can be certain that
create a cursor from which to execute our MySQL the right person is logging in with the right
statements. We do this with cur = db.cursor(): def sendVerificationCode(code, code – if not we can redirect them accordingly.
number): Assuming the code is correct, we need to
@app.route(‘/check-user', check it’s still valid. Back in Step 6, we created
methods=[‘POST']) text = client.messages.create( an expiration time that was five minutes in the
def checkUser(): body=“Your verification code future from when the code was generated. If it’s
been more than five minutes (or whatever time
#session.clear() is:" + code, you’ve set on it) then we’re going to consider it
to=number, invalid, delete the code from our table and then
if request.method == ‘POST': from_=“+YOURTWILIONUMBER" log out our user so they can start over, like so.
#print request.form['username']
) elif time.time() > expirationTime:
cur = db.cursor()
return text.sid expirySQL = db.cursor()
cur.execute("""SELECT * FROM
users WHERE username = %s""", Using the client variable we used to instantiate expirySQL.execute("""DELETE FROM
(request.form[‘username'],)) the Twilio REST module, we can access the
messages class and execute the create verification WHERE code=%s""",
method. All we need to pass through is the text (codeToCheck,))
114 The Python Book
Create with Python
expirySQL.close() @app.route('/') Fig 02
session['loggedin'] == False def index():
return redirect('/logout') return render_template('index.html')
If we manage to pass the tests so far, then @app.route('/login', methods=['GET'])
we’ve two-step verified our user – hooray! def login():
Surprisingly easy, eh? Before we give our user
free reign around our service, we still want return render_template('login.html')
to get rid of that token – we don’t need it any
more and we don’t want to risk someone else @app.route('/check-user', methods=['POST'])
using it maliciously in the future. def checkUser():
else: return "check user page"
delSql = db.cursor()
@app.route('/logout')
delSql.execute("""DELETE FROM def logout():
verification WHERE code=%s""",
(codeToCheck,)) return "logout page"
delSql.close() @app.route('/verify', methods=['GET'])
def twoStep():
db.commit()
return render_template('verify.html')
session['verified'] = True
@app.route('/check-code', methods=['POST'])
return redirect('/') def checkCode():
else: return "check code page"
return redirect('/
verficationCode = generateVerificationCode(size=6) Fig 03
verify?error=true')
ins = db.cursor()
And that’s it! Now we redirect our user to
wherever we want them to be at the end of the expiration = int(time.time() + expirationLength)
process. In this instance we’re sending them
back to our index page, which will render a sql = "INSERT INTO verification (code, expiration, username) VALUES ('%s',
success message and give the user a link to '%s', '%s')" % (verficationCode, expiration, request.form['username'])
log out whenever they like – but they could be
redirected to their user page, and so on. ins.execute(sql)
09 Conclusion ins.close()
In every web-based service, security
is king. Users entrust more and more personal db.commit()
data and trends to services every day and it’s
the responsibility of those services to maintain session['user'] = request.form['username']
the privacy of that data as best they can. It’s no session['loggedin'] = True
wonder that services such as Amazon, Google
and Facebook have all implemented two- @app.route('/check-code', methods=['POST']) Fig 04
step verification across their services. With def checkCode():
two-step authentication, a user can tie their
account to one of the most personal things they if request.method == 'POST':
own: their phone. With services like Twilio and codeToCheck = request.form['code']
some simple code, they contain people’s keys –
or at least a part of them. if not 'user' in session:
return redirect('/login')
else:
cur = db.cursor()
cur.execute("""SELECT * FROM verification WHERE code = %s""", (codeToCheck,))
result = cur.fetchone()
cur.close()
if result != None:
returnedUser = result[3]
expirationTime = int(result[2])
if returnedUser != session['user']:
return redirect('/verify?error=true')
The Python Book 115
Create with Python
Part one: Program a
Space Invaders clone
Write your own RasPi shooter in 300 lines of Python
Resources meander their way down the screen 01 Setting up dependencies
towards you, it’s your job to pick them If you’re looking to get a better understanding of
Raspbian: www.raspberrypi.org/ off while dodging their random fire. programming games with Python and Pygame, we strongly
When one wave is conquered, another recommend you copy the Pivaders code in this tutorial into your
downloads faster, more aggressive wave appears. own program. It’s great practice and gives you a chance to tweak
We’ve tried to use many features of elements of the game to suit you, be it a different ship image,
Python: www.python.org/doc Pygame, which is designed to make changing the difficulty or the ways the alien waves behave. If you
Pygame: www.pygame.org/docs the creation of games and interactive just want to play the game, that’s easily achieved too, though.
applications easier. We’ve extensively Either way, the game’s only dependency is Pygame, which (if it
When you’re learning to program in used the Sprite class, which saves isn’t already) can be installed from the terminal by typing:
a new language or trying to master dozens of lines of extra code in making
a new module, experimenting with a collision detection simple and updating sudo apt-get install python-pygame
familiar and relatively simply project the screen and its many actors a
is a very useful exercise to help single-line command. 02 Downloading the project
expand your understanding of the For Pivaders we’ve used Git, a brilliant form of version
tools you’re using. Our Space Invaders We hope you agree that this is an control used to safely store the game files and retain historical
clone is one such example that lends exciting game to play and a great versions of your code. Git should already be installed on your Pi; if
itself perfectly to Python and the tool to learn more about Python and not, you can acquire it by typing:
Pygame module – it’s a simple game Pygame, but our sensory system is far
with almost universally understood from overloaded here. Don’t worry, as sudo apt-get install git
rules and logic. While the Invaders that will be covered in the next tutorial, As well as acting as caretaker for your code, Git enables you
adding animation and sound effects to to clone copies of other people’s projects so you can work on
our game to give it the spit and polish them, or just use them. To clone Pivaders, go to your home
any self-respecting Space Invaders- folder in the terminal (cd ~), make a directory for the project
inspired shooter demands… (mkdir pivaders), enter the directory (cd pivaders) and type:
git pull https://github.com/russb78/pivaders.git
116 The Python Book
Create with Python
#!/usr/bin/env python2 class GameState: Get
pass the code:
import pygame, random bit.ly/
11k5f2x
class Game(object):
BLACK = (0, 0, 0) Clean clode def __init__(self):
BLUE = (0, 0, 255)
WHITE = (255, 255, 255) Having all the most pygame.init()
RED = (255, 0, 0) regularly used
ALIEN_SIZE = (30, 40) global variables pygame.font.init()
ALIEN_SPACER = 20 clearly labelled
BARRIER_ROW = 10 here makes our self.clock = pygame.time.Clock()
BARRIER_COLUMN = 4 code later on easier
BULLET_SIZE = (5, 10) to read. Also, if we self.game_font = pygame.font.Font(
MISSILE_SIZE = (5, 5) want to change the
BLOCK_SIZE = (10, 10) size of something, ‘data/Orbitracer.ttf’, 28)
RES = (800, 600) we only need to do
it here and it will self.intro_font = pygame.font.Font(
‘data/Orbitracer.ttf’, 72)
self.screen = pygame.display.set_mode([RES[0], RES[1]])
self.time = pygame.time.get_ticks()
self.refresh_rate = 20
work everywhere. self.rounds_won = 0 Groups This
self.level_up = 50
class Player(pygame.sprite.Sprite): self.score = 0 long list of groups
def __init__(self): self.lives = 2 we’re creating are
pygame.sprite.Sprite.__init__(self) self.player_group = pygame.sprite.Group() essentially sets.Each
self.size = (60, 55) self.alien_group = pygame.sprite.Group() time we create one
self.rect = self.image.get_rect() self.bullet_group = pygame.sprite.Group() of these items, it’s
self.rect.x = (RES[0] / 2) - (self.size[0] / 2) self.missile_group = pygame.sprite.Group() added to the set so
self.rect.y = 520 self.barrier_group = pygame.sprite.Group() it can be tested for
self.travel = 7 self.all_sprite_list = pygame.sprite.Group() collisions and drawn
self.speed = 350 self.intro_screen = pygame.image.load( with ease.
self.time = pygame.time.get_ticks() ‘data/start_screen.jpg’).convert()
self.background = pygame.image.load(
def update(self): ‘data/Space-Background.jpg’).convert()
self.rect.x += GameState.vector * self.travel
if self.rect.x < 0: pygame.display.set_caption(‘Pivaders - ESC to exit’)
self.rect.x = 0
elif self.rect.x > RES[0] - self.size[0]: pygame.mouse.set_visible(False)
self.rect.x = RES[0] - self.size[0]
Player.image = pygame.image.load(
‘data/ship.png’).convert()
Player.image.set_colorkey(BLACK)
Alien.image = pygame.image.load(
class Alien(pygame.sprite.Sprite): ‘data/Spaceship16.png’).convert()
def __init__(self):
pygame.sprite.Sprite.__init__(self) Alien.image.set_colorkey(WHITE)
self.size = (ALIEN_SIZE)
self.rect = self.image.get_rect() GameState.end_game = False
self.has_moved = [0, 0]
self.vector = [1, 1] GameState.start_screen = True
self.travel = [(ALIEN_SIZE[0] - 7), ALIEN_SPACER]
self.speed = 700 GameState.vector = 0
self.time = pygame.time.get_ticks()
GameState.shoot_bullet = False
def update(self): def control(self):
if GameState.alien_time - self.time > self.speed: for event in pygame.event.get():
if self.has_moved[0] < 12: if event.type == pygame.QUIT:
self.rect.x += self.vector[0] * self.travel[0] GameState.start_screen = False
self.has_moved[0] +=1 GameState.end_game = True
else: if event.type == pygame.KEYDOWN \
if not self.has_moved[1]: and event.key == pygame.K_ESCAPE:
self.rect.y += self.vector[1] * self.travel[1] if GameState.start_screen:
self.vector[0] *= -1 GameState.start_screen = False
self.has_moved = [0, 0] GameState.end_game = True
self.speed -= 20 self.kill_all()
if self.speed <= 100: else:
self.speed = 100 GameState.start_screen = True
self.time = GameState.alien_time self.keys = pygame.key.get_pressed()
if self.keys[pygame.K_LEFT]:
class Ammo(pygame.sprite.Sprite): GameState.vector = -1 Control Taking
elif self.keys[pygame.K_RIGHT]:
def __init__(self, color, (width, height)): GameState.vector = 1 care of keyboard
else: input is the control
pygame.sprite.Sprite.__init__(self) GameState.vector = 0 method. It checks
if self.keys[pygame.K_SPACE]: for key events and
self.image = pygame.Surface([width, height]) if GameState.start_screen: acts accordingly
GameState.start_screen = False depending whether
self.image.fill(color) self.lives = 2 we’re on the start
self.score = 0 screen or playing
self.rect = self.image.get_rect() self.make_player() the game.
self.make_defenses()
self.speed = 0 Rain bullets self.alien_wave(0)
self.vector = 0 else:
The Ammo class is GameState.shoot_bullet = True
def update(self): short and sweet. def splash_screen(self):
self.rect.y += self.vector * self.speed We only need a few while GameState.start_screen:
if self.rect.y < 0 or self.rect.y > RES[1]: initialising attributes self.kill_all()
self.kill() and the update self.screen.blit(self.intro_screen, [0, 0])
method checks if it’s self.screen.blit(self.intro_font.render(
“PIVADERS”, 1, WHITE), (265, 120))
class Block(pygame.sprite.Sprite): still on the screen. If self.screen.blit(self.game_font.render(
def __init__(self, color, (width, height)): not, it’s destroyed. “PRESS SPACE TO PLAY”, 1, WHITE), (274, 191))
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
The Python Book 117
Create with Python
03 Testing Pivaders you can’t reassign individual items within You could, for example, have a pet class. From that class you
With Pygame installed and the them. Perfect for constants, which aren’t could create a cat (that meows) and a dog (that barks). They’re
project cloned to your machine (you can designed to change anyway. different in many ways, but they’re both furry and have four
also find the .zip on FileSilo – simply legs, so can be created from the same parent class. We’ve
unpack it and copy it to your home 06 Classes – part 1 done exactly that with our Ammo class, using it to create both
directory to use it), you can take it for a A class is essentially a blueprint the player bullets and the alien missiles. They’re different
quick test drive to make sure everything’s for an object you’d like to make. In the colours and they shoot in opposite directions, but they’re
set up properly. All you need to do is type case of our player, it contains all the fundamentally one and the same. This saves us creating extra
python pivaders.py from within the required info, from which you can make unnecessary code and ensures consistent behaviour between
pivaders directory in the terminal to get multiple copies (we create a player objects we create.
started. You can start the game with the instance in the make_player() method
space bar, shoot with the same button halfway through the project). The great 09 Thegame
and simply use the left and right arrows thing about the classes in Pivaders is Our final class is called Game. This is where all the main
on your keyboard to move your ship left that they inherit lots of capabilities and functionality of the game itself comes in, but remember, so far
and right. shortcuts from Pygame’s Sprite class, this is still just a list of ingredients – nothing can actually happen
as denoted by the pygame.sprite.Sprite until a ‘Game’ object is created (right at the bottom of the code).
04 Creating your own clone found within the braces of the first line The Game class is where the central mass of the game resides,
Once you’ve racked up a good of the class. You can read the docs to so we initialise Pygame, set the imagery for our protagonist
high score (anything over 2,000 points is learn more about the Sprite class via and extraterrestrial antagonist and create some GameState
respectable) and got to know our simple www.pygame.org/docs/ref/sprite.html. attributes that we use to control key aspects of external classes,
implementation, you’ll get more from like changing the player’s vector (direction) and deciding if we
following along with and exploring the 07 Classes – part 2 need to return to the start screen, among other things.
code and our brief explanations of what’s In Pivader’s classes, besides
going on. For those who want to make creating the required attributes – these 10 The main loop
their own project, create a new project are simply variables in classes – for the There are a lot of methods (class functions) in the Game
folder and use either IDLE or Leafpad (or object (be it a player, an alien, some class, and each is designed to control a particular aspect of
perhaps install Geany) to create and save ammo or a block), you’ll also notice all either setting up the game or the gameplay itself. The actual logic
a .py file of your own. the classes have an update() method that dictates what happens within any one round of the game is
apart from the Block class (a method is actually contained in the main_loop() method right at the bottom
05 Global variables & tuples a function within a class). The update() of the pivaders.py script and is the key to unlocking exactly what
Once we’ve imported the method is called in every loop through variables and functions you need for your game. Starting at
modules we need for the project, the main game (we’ve called ours the top of main_loop() and working line-by-line down to its last
there’s quite a long list of variables in main_loop()) and simply asks the line, you can see exactly what’s being evaluated 20 times every
block capitals. The capitals denote that iteration of the class we’ve created to second when you’re playing the game.
these variables are constants (or global move. In the case of a bullet from the
variables). These are important numbers Ammo class, we’re asking it to move 11 Main loop key logic – part 1
that never change – they represent things down the screen. If it goes off either the Firstly the game checks that the end_game attribute is
referred to regularly in the code, like top or bottom of the screen, we destroy false – if it’s true, the entire loop in main_loop() is skipped and
colours, block sizes and resolution. You’ll it (since we don’t need it any more). we go straight to pygame.quit(), exiting the game. This flag is set
also notice that colours and sizes hold to true only if the player closes the game window or presses the
multiple numbers in braces – these are 08 Ammo Esc key when on the start_screen. Assuming end_game and
tuples. You could use square brackets (to What’s most interesting about start_screen are false, the main loop can start proper, with the
make them lists), but we use tuples here classes, though, is that you can use one control() method, which checks to see if the location of the player
since they’re immutable, which means class to create lots of different things. needs to change. Next we attempt to make an enemy missile and
we use the random module to limit the number of missiles that
can be created. Next we call the update() method for each and
every actor on the screen using a simple for loop. This makes
sure everyone’s up to date and moved before we check collisions
in calc_collisions().
We used widely available open source 12 Main loop key logic – part 2
art and fonts to make the game Once collisions have been calculated, we need to see if
the game is still meant to continue. We do so with is_dead() and
defenses_breached() – if either of these methods returns true,
we know we need to return to the start screen. On the other
hand, we also need to check to see if we’ve killed all the aliens,
from within win_round(). Assuming we’re not dead, but the
aliens are, we know we can call the next_round() method, which
creates a fresh batch of aliens and increases their speed around
the screen. Finally, we refresh the screen so everything that’s
been moved, shot or killed can be updated or removed from the
screen. Remember, the main loop happens 20 times a second –
so the fact we don’t call for the screen to update right at the end
of the loop is of no consequence.
118 The Python Book
Create with Python
A class is essentially a blueprint Dead or alive Probably two of the most
important questions are answered here – is
the player dead or did you win the round?
pygame.display.flip() Refreshing def is_dead(self): Get
self.control() the screen You if self.lives < 0: the code:
self.screen.blit(self.game_font.render(
def make_player(self): need to carefully “The war is lost! You scored: “ + str( bit.ly/
self.player = Player() consider the way in self.score), 1, RED), (250, 15)) 11k5f2x
self.player_group.add(self.player) which you update self.rounds_won = 0
self.all_sprite_list.add(self.player) the screen. Blitting self.refresh_screen()
the background pygame.time.delay(3000)
def refresh_screen(self): between actor return True
self.all_sprite_list.draw(self.screen) movements is vital
self.refresh_scores() for clean animation. def win_round(self):
pygame.display.flip() if len(self.alien_group) < 1:
self.screen.blit(self.background, [0, 0]) self.rounds_won += 1
self.clock.tick(self.refresh_rate) self.screen.blit(self.game_font.render(
“You won round “ + str(self.rounds_won) +
def refresh_scores(self): “ but the battle rages on”, 1, RED), (200, 15))
self.screen.blit(self.game_font.render( self.refresh_screen()
“SCORE “ + str(self.score), 1, WHITE), (10, 8)) pygame.time.delay(3000)
self.screen.blit(self.game_font.render( return True
“LIVES “ + str(self.lives + 1), 1, RED), (355, 575))
def alien_wave(self, speed): def defenses_breached(self):
for column in range(BARRIER_COLUMN): for alien in self.alien_group:
for row in range(BARRIER_ROW): if alien.rect.y > 410:
alien = Alien() self.screen.blit(self.game_font.render(
alien.rect.y = 65 + (column * ( “The aliens have breached Earth defenses!”,
ALIEN_SIZE[1] + ALIEN_SPACER)) 1, RED), (180, 15))
alien.rect.x = ALIEN_SPACER + ( self.refresh_screen()
row * (ALIEN_SIZE[0] + ALIEN_SPACER)) pygame.time.delay(3000)
self.alien_group.add(alien) return True
self.all_sprite_list.add(alien)
alien.speed -= speed def calc_collisions(self):
pygame.sprite.groupcollide(
def make_bullet(self): self.missile_group, self.barrier_group, True, True)
pygame.sprite.groupcollide(
if GameState.game_time - self.player.time > self.player.speed: self.bullet_group, self.barrier_group, True, True)
if pygame.sprite.groupcollide(
bullet = Ammo(BLUE, BULLET_SIZE) self.bullet_group, self.alien_group, True, True):
self.score += 10
bullet.vector = -1 if pygame.sprite.groupcollide(
self.player_group, self.missile_group, False, True):
bullet.speed = 26 self.lives -= 1
bullet.rect.x = self.player.rect.x + 28
bullet.rect.y = self.player.rect.y Guns ’n’ ammo def next_round(self):
self.bullet_group.add(bullet)
self.all_sprite_list.add(bullet) Bullets and missiles for actor in [self.missile_group, Main loop This
self.player.time = GameState.game_time self.barrier_group, self.bullet_group]:
GameState.shoot_bullet = False use the same parent is the business end
for i in actor: of our application.
class. We change a i.kill() This loop executes
20 times a second. It
few key attributes self.alien_wave(self.level_up) needs to be logical
self.make_defenses() and easy for another
def make_missile(self): originally initialised self.level_up += 50
if len(self.alien_group): to create the
shoot = random.random() behaviour we need;
if shoot <= 0.05: eg the vector for each
shooter = random.choice([ is opposite.
alien for alien in self.alien_group]) coder to understand.
missile = Ammo(RED, MISSILE_SIZE) def main_loop(self):
missile.vector = 1 while not GameState.end_game:
missile.rect.x = shooter.rect.x + 15 while not GameState.start_screen:
missile.rect.y = shooter.rect.y + 40 GameState.game_time = pygame.time.get_ticks()
missile.speed = 10 GameState.alien_time = pygame.time.get_ticks()
self.missile_group.add(missile) self.control()
self.all_sprite_list.add(missile) self.make_missile()
for actor in [self.player_group, self.bullet_group,
def make_barrier(self, columns, rows, spacer): self.alien_group, self.missile_group]:
for column in range(columns):
for row in range(rows): for i in actor:
barrier = Block(WHITE, (BLOCK_SIZE))
barrier.rect.x = 55 + (200 * spacer) + (row * 10) i.update()
barrier.rect.y = 450 + (column * 10)
self.barrier_group.add(barrier) if GameState.shoot_bullet:
self.all_sprite_list.add(barrier)
self.make_bullet()
self.calc_collisions()
if self.is_dead() or self.defenses_breached():
GameState.start_screen = True
if self.win_round():
def make_defenses(self): self.next_round()
for spacing, spacing in enumerate(xrange(4)):
self.make_barrier(3, 9, spacing) self.refresh_screen()
self.splash_screen()
pygame.quit() Start the game The very last thing
def kill_all(self): if __name__ == ‘__main__’: we do is create a Game object and call the
for items in [self.bullet_group, self.player_group, pv = Game() main loop. Besides our constants, this is
self.alien_group, self.missile_group, self.barrier_group]: pv.main_loop() the only code that sits outside a class.
for i in items:
i.kill()
The Python Book 119
Create with Python
Part two: Add animation and
sound to Pivaders
After writing a Space Invaders clone in just 300 lines of Python,
now we expand it to include animation and sound
Resources that goal would likely have been overshot at least twofold. 01 Setting up dependencies
Pygame’s ability to group, manage and detect collisions As we recommended with
Raspbian: www.raspberrypi.org thanks to the Sprite class really made a great difference the last tutorial, you’ll get much more
to our project, not just in terms of length but in simplicity. from the exercise if you download
/downloads If you missed the first part of the project, you can find the the code (git.io/8QsK-w) and use it
v0.1 code listing on GitHub via git.io/cBVTBg, while you can for reference as you create your own
Python: www.python.org/doc find version v0.2, including all the images, music and sound animations and sound for your Pygame
Pygame: www.pygame.org/docs effects we used, over at git.io/8QsK-w. projects. Regardless of whether you
Art assets: opengameart.org just want to simply preview and play or
Even working within the clearly defined framework walk-through the code to get a better
We had great fun creating our basic Pygame offers, there are still a thousand ways we could understanding of basic game creation,
Space Invaders clone, Pivaders, have approached adding animation and sound. We could you’re still going to need to satisfy
for the previous tutorial. One of the have created any one of a dozen classes to create and some basic dependencies. The two key
key challenges with the project was manage containers of individual images, or read in a sprite requirements here are Pygame and Git,
keeping it to a manageable size – just sheet (a single image full of smaller, separate images) both of which are installed by default
300 lines of Python. Without the use which we could then draw (or blit) to the screen. For the on up-to-date Raspbian installations.
of Pygame’s strong set of features, sake of simplicity and performance, we integrated a few If you’re unsure if you have them,
animation methods into our Game class and opted to use a though, type the following at the
sprite sheet. Not only does it make it very easy to draw to the command line:
screen, but it also keeps the asset count under control and
keeps performance levels up, which is especially important sudo apt-get install python-
for the Raspberry Pi. pygame git
120 The Python Book
Create with Python
Pivaders.py listing from line 86 (continued on next page) Get
the code:
bit.ly/
class Game(object): self.animate_right = True
def __init__(self): self.animate_left = False 1xPvY1F
pygame.init() else:
pygame.font.init() GameState.vector = 0
self.clock = pygame.time.Clock() self.animate_right = False
self.game_font = pygame.font.Font( self.animate_left = False
‘data/Orbitracer.ttf’, 28)
self.intro_font = pygame.font.Font( if self.keys[pygame.K_SPACE]:
‘data/Orbitracer.ttf’, 72) if GameState.start_screen:
self.screen = pygame.display.set_mode([RES[0], RES[1]]) GameState.start_screen = False
self.time = pygame.time.get_ticks() self.lives = 2
self.refresh_rate = 20; self.rounds_won = 0 ship_sheet self.score = 0 fx.play()
self.level_up = 50; self.score = 0 self.make_player()
self.lives = 2 We set the player self.make_defenses() Having already
self.player_group = pygame.sprite.Group() image to be equal to self.alien_wave(0) loaded the sound
self.alien_group = pygame.sprite.Group() one small segment else: effect we want when
self.bullet_group = pygame.sprite.Group() of the sprite sheet by GameState.shoot_bullet = True we shoot, we now
self.missile_group = pygame.sprite.Group() using the ‘ani_pos’ self.bullet_fx.play() just need to call it
self.barrier_group = pygame.sprite.Group() variable. Change the when we press the
self.all_sprite_list = pygame.sprite.Group() variable to change def animate_player(self): space bar
self.intro_screen = pygame.image.load( the picture if self.animate_right:
‘data/graphics/start_screen.jpg’).convert() if self.ani_pos < 10:
self.background = pygame.image.load( Player.image = self.ship_sheet.subsurface(
‘data/graphics/Space-Background.jpg’).convert() self.ani_pos*64, 0, 64, 61)
pygame.display.set_caption(‘Pivaders - ESC to exit’) self.ani_pos += 1
pygame.mouse.set_visible(False) else:
Alien.image = pygame.image.load( if self.ani_pos > 5:
‘data/graphics/Spaceship16.png’).convert() self.ani_pos -= 1
Alien.image.set_colorkey(WHITE) Player.image = self.ship_sheet.subsurface(
self.ani_pos = 5 # 11 images of ship self.ani_pos*64, 0, 64, 61)
self.ship_sheet = pygame.image.load(
‘data/graphics/ship_sheet_final.png’).convert_alpha() if self.animate_left:
Player.image = self.ship_sheet.subsurface( if self.ani_pos > 0:
self.ani_pos*64, 0, 64, 61) self.ani_pos -= 1
self.animate_right = False Player.image = self.ship_sheet.subsurface(
self.animate_left = False self.ani_pos*64, 0, 64, 61)
self.explosion_sheet = pygame.image.load( else:
‘data/graphics/explosion_new1.png’).convert_alpha() if self.ani_pos < 5:
self.explosion_image = self.explosion_sheet.subsurface(0, 0, Player.image = self.ship_sheet.subsurface(
79, 96) self.ani_pos*64, 0, 64, 61)
self.alien_explosion_sheet = pygame.image.load( self.ani_pos += 1
‘data/graphics/alien_explosion.png’)
self.alien_explode_graphics = self.alien_explosion_sheet. def player_explosion(self):
subsurface(0, 0, 94, 96) if self.explode:
self.explode = False if self.explode_pos < 8:
self.explode_pos = 0; self.alien_explode = False self.explosion_image = self.explosion_sheet.
self.alien_explode_pos = 0 subsurface(0, self.explode_pos*96, 79, 96)
pygame.mixer.music.load(‘data/sound/10_Arpanauts.ogg’) self.explode_pos += 1
pygame.mixer.music.play(-1) self.screen.blit(self.explosion_image, [self.player.
pygame.mixer.music.set_volume(0.7) rect.x -10, self.player.rect.y - 30])
self.bullet_fx = pygame.mixer.Sound( else:
‘data/sound/medetix__pc-bitcrushed-lazer-beam.ogg’) self.explode = False
self.explosion_fx = pygame.mixer.Sound( self.explode_pos = 0
‘data/sound/timgormly__8-bit-explosion.ogg’)
self.explosion_fx.set_volume(0.5) def alien_explosion(self):
self.explodey_alien = [] if self.alien_explode:
GameState.end_game = False if self.alien_explode_pos < 9:
GameState.start_screen = True self.alien_explode_graphics = self.alien_explosion_
GameState.vector = 0 sheet.subsurface(0, self.alien_explode_pos*96, 94, 96)
GameState.shoot_bullet = False self.alien_explode_pos += 1
self.screen.blit(self.alien_explode_graphics,
def control(self): [int(self. explodey_alien[0]) - 50 , int(self.explodey_alien[1]) - 60])
for event in pygame.event.get(): else:
if event.type == pygame.QUIT: self.alien_explode = False
GameState.start_screen = False self.alien_explode_pos = 0
GameState.end_game = True self.explodey_alien = []
if event.type == pygame.KEYDOWN \
and event.key == pygame.K_ESCAPE: def splash_screen(self):
if GameState.start_screen: while GameState.start_screen:
GameState.start_screen = False self.kill_all()
GameState.end_game = True Set flags We’ve self.screen.blit(self.intro_screen, [0, 0])
self.kill_all() self.screen.blit(self.intro_font.render(
else: added ‘animate_left’ “PIVADERS”, 1, WHITE), (265, 120))
GameState.start_screen = True and ‘animate_right’ self.screen.blit(self.game_font.render(
self.keys = pygame.key.get_pressed() Boolean flags to “PRESS SPACE TO PLAY”, 1, WHITE), (274, 191))
if self.keys[pygame.K_LEFT]: the control method. pygame.display.flip()
GameState.vector = -1 When they’re true, self.control()
self.animate_left = True the actual animation self.clock.tick(self.refresh_rate / 2)
self.animate_right = False code is called via a
elif self.keys[pygame.K_RIGHT]: separate method def make_player(self):
GameState.vector = 1 self.player = Player()
The Python Book 121
Create with Python
02 Downloading pivaders with several frames of animation and end of the line so the ship frames render correctly (without any
Git is a superb version control should you take fire, a smaller explosion background). We then use subsurface to set the initial Player.
solution that helps programmers safely occurs on your ship. Music, lasers and image to the middle ship sprite on the sheet. This is set by self.
store their code and associated files. explosion sound effects also accompany ani_pos, which has an initial value of 5. Changing this value will
Not only does it help you retain a full the animations as they happen. alter the ship image drawn to the screen: ‘0’ would draw it leaning
history of changes, it means you can fully left, ‘11’ fully to the right.
‘clone’ entire projects to use and work 05 Finding images to animate
on from places like github.com. To clone Before we can program anything, 08 Animation flags
the version of the project we created for it’s wise to have assets set up in a way we Down the list in the initialising code for the Game class,
this tutorial, go to your home folder from can use them. As mentioned, we’ve opted we set two flags for player animation: self.animate_left and
the command line (cd ~) and type: to use sprite sheets; these can be found self.animate_right. In the Control method of our Game class,
online or created with GIMP with a little we use these to ‘flag’ when we want animations to work using
git pull https://github.com/ practice. Essentially they’re a mosaic Boolean values. It allows us to ‘automatically’ animate the
russb78/pivaders.git made up of individual ‘frames’ of equally player sprite back to its resting state (otherwise the ship will
sized and spaced images representing continue to look as if it’s flying left when it has stopped).
This will create a folder called each frame. Find ready-made examples
pivaders – go inside (cd pivaders) and at opengameart.org, as used here. 09 The animation method
take a look around. These flags pop up again in the core animation code for
06 Tweaking assets the player: animate_player() within the Game class. Here we
03 Navigating the project While many of the assets on use nested if statements to control the animation and physically
The project is laid out quite sites like opengameart.org can be used set the player image accordingly. Essentially it states that if the
simply across a few subfolders. Within as is, you may want to import them into animate_right flag is True and if the current animation position
pivaders sits a licence, readme and a an image-editing application like GIMP is different to what we want, we incrementally increase the
second pivaders folder. This contains to configure them to suit your needs – as ani_pos variable and set the player’s image accordingly. The Else
the main game file, pivaders.py, which we did with our ship sheet asset to help statement then animates the ship sprite back to its resting state
launches the application. Within the us keep the code simple. We started with and the same logic is then applied in the opposite direction.
data folder you’ll find subfolders for both the central ship sprite and centred it into a
graphics and sound assets, as well as the new window. We set the size and width of 10 Animating explosions
font we’ve used for the title screen and the frame and then copy-pasted the other The player_explosion() and alien_explosion() methods
scores. To take pivaders for a test-drive, frames either side of it. We ended up with that come after the player animation block in the Game class are
simply enter the pivaders subdirectory 11 frames of exactly the same size and similar but simpler executions of essentially the same thing. As
(cd pivaders/pivaders) and type: width in a single document. Pixel-perfect we only need to run through the same predefined set of frames
precision on size and width is key, so we (this time vertically), we only need to see if the self.explode and
python pivaders.py can just multiply it to find the next frame. self.alien_explode flags are True before we increment the
Use the arrow keys to steer left and right variables that change the image displayed. As the sprite sheet is
and the space bar to shoot. You can quit 07 Loadingthespritesheet vertical, the variables alien_explode_pos and explosion_image
to the main screen with the Esc key and Since we’re inheriting from the are set to a different part of subsurface than before.
press it again to exit the game completely. Sprite class to create our Player class, we
can easily alter how the player looks on 11 Adding music to your project
04 Animation & sound screen by changing Player.image. First, Pygame makes it easy to add a musical score to a project.
Compared with the game from we need to load our ship sprite sheet with Just obtain a suitable piece of music in your preferred format (we
last month’s tutorial, you’ll see it’s now pygame.image.load(). Since we made our found ours via freemusicarchive.org) and load it using the Mixer
a much more dynamic project. The sheet with a transparent background, Pygame class. As it’s already been initialised via pygame.init(),
protagonist ship now leans into the turns we can append .convert_alpha() to the we can go ahead and load the music with this code:
as you change direction and corrects
itself when you either press the opposite pygame.mixer.music.load(‘data/sound/10_Arpanauts.ogg’)
direction or lift your finger off the button. pygame.mixer.music.play(-1)
When you shoot an alien ship, it explodes pygame.mixer.music.set_volume(0.7)
The music.play(-1) requests that the music should start with the
app and continue to loop until it quits. If we replaced -1 with 5, the
music would loop five times before ending. Learn more about the
Mixer class via www.pygame.org/docs/ref/mixer.html.
Above The Freesound site is a good place to find free and open sound effects for projects 12 Using sound effects
Loading and using sounds is similar to how we do so for
images in Pygame. First we load the sound effect using a simple
assignment. For the laser beam, the initialisation looks like this:
self.bullet_fx = pygame.mixer.Sound(
‘data/sound/medetix__pc-bitcrushed-lazer-beam.ogg’)
Then we simply trigger the sound effect at the appropriate time.
In the case of the laser, we want it to play whenever we press the
space bar to shoot, so we place it in the Game class’s Control
method, straight after we raise the shoot_bullet flag.
If you’re struggling to find free and open sound effects, we
recommend www.freesound.org.
122 The Python Book
Create with Python
self.player_group.add(self.player) self.explode = False
self.all_sprite_list.add(self.player)
self.alien_explode = False Get
def refresh_screen(self): pygame.time.delay(3000) the code:
self.all_sprite_list.draw(self.screen) return True
self.animate_player() bit.ly/
self.player_explosion()
self.alien_explosion() def defenses_breached(self): 1xPvY1F
self.refresh_scores()
pygame.display.flip() for alien in self.alien_group:
self.screen.blit(self.background, [0, 0])
self.clock.tick(self.refresh_rate) if alien.rect.y > 410:
def refresh_scores(self): self.screen.blit(self.game_font.render(
self.screen.blit(self.game_font.render(
“SCORE “ + str(self.score), 1, WHITE), (10, 8)) “The aliens have breached Earth defenses!”,
self.screen.blit(self.game_font.render(
“LIVES “ + str(self.lives + 1), 1, RED), (355, 575)) 1, RED), (180, 15))
def alien_wave(self, speed): self.refresh_screen()
for column in range(BARRIER_COLUMN):
for row in range(BARRIER_ROW): self.level_up = 50
alien = Alien()
alien.rect.y = 65 + (column * ( self.explode = False
ALIEN_SIZE[1] + ALIEN_SPACER))
alien.rect.x = ALIEN_SPACER + ( self.alien_explode = False
row * (ALIEN_SIZE[0] + ALIEN_SPACER))
self.alien_group.add(alien) pygame.time.delay(3000)
self.all_sprite_list.add(alien)
alien.speed -= speed return True
def make_bullet(self): def win_round(self):
if GameState.game_time - self.player.time > self.player.speed: if len(self.alien_group) < 1:
bullet = Ammo(BLUE, BULLET_SIZE) self.rounds_won += 1
bullet.vector = -1 self.screen.blit(self.game_font.render(
bullet.speed = 26 “You won round “ + str(self.rounds_won) +
bullet.rect.x = self.player.rect.x + 28 “ but the battle rages on”, 1, RED), (200, 15))
bullet.rect.y = self.player.rect.y self.refresh_screen()
self.bullet_group.add(bullet) pygame.time.delay(3000)
self.all_sprite_list.add(bullet) return True
self.player.time = GameState.game_time
GameState.shoot_bullet = False def next_round(self):
self.explode = False
def make_missile(self): self.alien_explode = False
if len(self.alien_group): for actor in [self.missile_group,
shoot = random.random() self.barrier_group, self.bullet_group]:
if shoot <= 0.05: for i in actor:
shooter = random.choice([ i.kill()
alien for alien in self.alien_group]) self.alien_wave(self.level_up)
missile = Ammo(RED, MISSILE_SIZE) self.make_defenses()
missile.vector = 1 self.level_up += 50
missile.rect.x = shooter.rect.x + 15
missile.rect.y = shooter.rect.y + 40 def calc_collisions(self):
missile.speed = 10 pygame.sprite.groupcollide(
self.missile_group.add(missile) self.missile_group, self.barrier_group, True, True)
self.all_sprite_list.add(missile) pygame.sprite.groupcollide(
self.bullet_group, self.barrier_group, True, True)
def make_barrier(self, columns, rows, spacer):
for column in range(columns): for z in pygame.sprite.groupcollide(
for row in range(rows): self.bullet_group, self.alien_group, True, True):
barrier = Block(WHITE, (BLOCK_SIZE))
barrier.rect.x = 55 + (200 * spacer) + (row * 10) self.alien_explode = True
barrier.rect.y = 450 + (column * 10) self.explodey_alien.append(z.rect.x)
self.barrier_group.add(barrier) self.explodey_alien.append(z.rect.y)
self.all_sprite_list.add(barrier) self.score += 10
self.explosion_fx.play()
def make_defenses(self):
for spacing, spacing in enumerate(xrange(4)): if pygame.sprite.groupcollide(
self.make_barrier(3, 9, spacing) self.player_group, self.missile_group, False, True):
def kill_all(self): self.lives -= 1
for items in [self.bullet_group, self.player_group, self.explode = True
self.alien_group, self.missile_group, self.barrier_group]: self.explosion_fx.play()
for i in items:
i.kill() def main_loop(self):
while not GameState.end_game:
def is_dead(self): while not GameState.start_screen:
if self.lives < 0: GameState.game_time = pygame.time.get_ticks()
self.screen.blit(self.game_font.render( GameState.alien_time = pygame.time.get_ticks()
“The war is lost! You scored: “ + str( self.control()
self.score), 1, RED), (250, 15)) self.make_missile()
self.rounds_won = 0 self.calc_collisions()
self.refresh_screen() self.refresh_screen()
self.level_up = 50 if self.is_dead() or self.defenses_breached():
GameState.start_screen = True
for actor in [self.player_group, self.bullet_group,
self.alien_group, self.missile_group]:
for i in actor:
i.update()
if GameState.shoot_bullet:
self.make_bullet()
if self.win_round():
self.next_round()
self.splash_screen()
pygame.quit()
if __name__ == ‘__main__’:
pv = Game()
pv.main_loop()
The Python Book 123
Create with Python
Make a visual novel
game with Python
Bridge the gap between books and videogames by creating an
interactive novel or choose-your-own-adventure with Python
and Pygame
Q Change scenes to add more depth to the story, and allow the game to have decisions and routes
Resources Most people look for a compelling story in In Python, this is a relatively simple project
modern videogames, and those that don’t to create, but with the addition of the Pygame
Python 2: have one are appearing less and less. A great module we can make it easier still, and even
way to tell a pure story is through the genre more expandable for the future. Pygame adds
www.python.org/ of visual novels, and you can make one fairly better support for positioning the images and
simply in Python. These interactive novels are text, creating display windows and using mouse
Pygame: an extremely popular form of entertainment in and keyboard inputs, thereby simplifying the
Japan, and usually work by having the player coding process.
pygame.org/download.shtml click through a story and make decisions as they
go along in order to experience different plot We’ll be coding this in standard Python 2, so
IDLE Python IDE points and endings. make sure to run it in IDLE 2 and not IDLE 3 while
you are writing, testing and coding.
Game assets
Code from FileSilo (optional)
124 The Python Book
Create with Python
01 Get Pygame dependencies
The best way to install Pygame for your
system is to compile it. To do this you need to
first install the right dependencies. Open up
the terminal and install the following packages,
which in Ubuntu looks like:
$ sudo apt-get install mercurial
python-dev python-numpy libav-tools
libsdl-image1.2-dev libsdl-mixer1.2-
dev libsdl-ttf2.0-dev libsmpeg-
dev libsdl1.2-dev libportmidi-dev
libswscale-dev libavformat-dev
libavcodec-dev
02 Get the Pygame code 03 Build the Pygame module 04 Install in other ways
Next we need to download the code To install it, we need to do it in two If the above doesn’t work (or is a bit
for Pygame direct from the source. Still in the steps. First we need to prepare the code to daunting) you can check the website for binary
terminal, you can do this by typing in: install using the terminal with: and executable files that will work on other
operating systems and Linux distros. Head to
$ hg clone https://bitbucket.org/pygame/ $ python setup.py build http://pygame.org/download.shtml to get the
pygame files you need for your specific system, including
Once that’s finished you can then actually install Windows and OS X. The rest of the tutorial will
it with: work in any OS.
Which will download it to the folder ‘pygame’. $ sudo python setup.py install 05 Get the visual novel files
Move to that using CD pygame in the terminal so This won’t take too long. We’ve uploaded the code to FileSilo,
we can continue building it. and here we’re going to walk you through what
we’ve done to make it work. Download the files
for the visual novel and unzip them. The two
files we care about for the moment are the
visualnovel.py and script.py python files – this
is where all the important code is.
06 Understand the script file
For the moment the script file is small
and literally just holds the script for the game.
It’s made up of events for the visual novel to
move between, line by line, by splitting it up into
scenes. This includes the location of each line, the
character, the actual line itself and information on
how the game flows. These are matrices with the
information in, and are completely customisable.
The Python Book 125
Create with Python
07 How the script relates 10 Start the game we have to use event.get() to see the mouse
In our game, the code pulls in Create a display for the game. Pygame in general, then we look for the position with
elements from the script file as it goes. We'll works by constantly updating the display with get_pos(). After that, we wait for it to click, see
explain how that works later, but this also new information. To show how this works, the where it clicked (using the co-ordinates of the
allows us to implement decisions later on to menu function adds elements to the display rectangle) and make a decision after that.
change which direction the game might take (which we’ve titled screen), such as filling it
you in. with colour, adding shapes and using blit to add 12 Start the story
images or in this case text. Once you’ve created Our start_game function is called
08 Starting the main game a buffer of changes to the screen, you update it when the mouse clicks the right position and
We don’t need many modules for the with the flip() function. we prepare the game, getting the characters,
current state of the visual novel. Here we’ve locations and progression through the game
imported the new Pygame module, our script 11 See the mouse script. The rest of this function uses this info to
as a module and the time module for aesthetic As we’ve created the button as a pull in data from the script to make the game
reasons – we’re going to have the code pause in rectangle and now an image on the menu, we flow properly.
bits rather than just instantly change scenes to need to recognise when the mouse is hovering
the next line. We also initialise Pygame with a over it to know when the button is clicked. First 13 First screen
simple pygame.init() The first screen is handled differently,
and acts to get every element up on the
interface before we continue – it makes the
code take a little less time to process as we
09 Add variables and assets The code pulls in elements from
We add a mixture of information we the script file as it goes, allowing us to
need to run the novel. We define the size of the implement decisions later on
display screen to use (1000 pixels wide and 563
high), along with some RGB colours for the code
to use. We’re also telling Pygame what font to
use and how large for certain sections and also
loading images for the game.
126 The Python Book
Create with Python
begin. The getattr allows us to use the string/ The code here is very expandable,
integer associated with our place in the story allowing you to add decisions that take
and call upon the relevant scene function from you to different scenes
the script file. We then use an if statement
with an iterative function to successively add
screen element to give the illusion that it’s
building up the first screen. We finish it by
advancing the progression value.
14 Add variables and assets 15 The starting function feel more interactive. This would not require
Similarly to the way that our original We finish our code bit with a simple much more code than the if statements, and
startup code works, our next if statement and function that starts off the entire game. This is it would also be a good way for you to look into
iteration checks to see what is different on the just to encapsulate the entire code and allows us adding graphical buttons to click and use the
next line, and if it moves to a different scene to add different ways of turning it off in the future. collide function.
function. It will also change anything that is IDLE when running the file will load everything
different without filling up the buffer more up and then run the game() function at the end 17 Move the assets
than needed. Where we’ve made no change is – this is similar to how you can add a __main__ Currently the code has the script-
labelled with a 0 in the scripts. function at the end which will start the code in the specific assets in the main visualnovel file.
command line. These can be moved to the script, allowing
you to make the visualnovel file much more
16 Expand your code modular so that can you have multiple scripts
The code written is very expandable, with different assets to load at startup.
allowing you to add decisions that are logged
to take you to different scenes (or routes in
visual novel terminology) and make your game
The Python Book 127
Create with Python
LIVES BREAKOUT SCORE
0001200
Pygame Zero
Pygame Zero cuts out the boilerplate to turn your ideas into games
instantly, and we’ll show you how
Resources Games are a great way of understanding a Pg0’s creator, Daniel Pope, told us that the
language: you have a goal to work towards, and library “grew out of talking to teachers at Pycon
Pygame Zero: each feature you add brings more fun. However, UK’s education track, and trying to understand
games need libraries and modules for graphics that they need to get immediate results and
pygame-zero.readthedocs.org and other essential games features. While the break lessons into bite-size fragments, in order
Pygame library made it relatively easy to make to keep a whole class up to speed.”
Pygame: games in Python, it still brings in boilerplate code
that you need before you get started – barriers to To give you an idea of what’s involved here,
pygame.org/download.shtml you or your kids getting started in coding. we’ll build up a simple game from a Pong-
type bat and ball through to smashing blocks
Pip Pygame Zero deals with all of this boilerplate Breakout-style. The project will illustrate what
code for you, aiming to get you coding games can be done with very little effort. Pg0 is in
pip-installer.org instantly. Pg0 (as we’ll abbreviate it) makes early development but still offers a great start
sensible assumptions about what you’ll need for – and is now included on the Pi in the Raspbian
Python 3.2 or later a game – from the size of the window to importing Jessie image.
the game library – so that you can get straight
www.python.org/ down to coding your ideas. We’ll look at installation on other platforms,
but first let’s see what magic it can perform.
Code from FileSilo (optional)
128 The Python Book
Right Breakout is Create with Python
a classic arcade
CFoiledSeiolon
game that can
be reimagined in
Pygame Zero
04 NoPi?
You don’t even need a Raspberry Pi to install Pygame
Zero – just install the Pygame library, then use pip to install
Pygame Zero. Instructions vary by distro, but a good place to
start is the documentation: bit.ly/1GYznUB.
Young
and old
In situations where 01 Zero effort
Pygame is used Although game writing is not easy, getting started
boilerplate and all certainly is. If you’ve got Raspbian Jessie installed on your Pi,
with young people, you’re ready to go. Open a terminal and type:
great results can
also be achieved touch example.py
(see Bryson pgzrun example.py
Payne’s book),
but Pygame and And you’ll see an empty game window open (Ctrl+Q will close the 05 Intro.py
Pg0, despite their window). Yes, it’s that easy to get started! That default black square of 800 by 600 pixels we
use as powerful saw in Step 1 can now be overridden manually. For example,
educational tools, 02 Python3 we can use the following code to replace it with an oversized
are also good for If you haven’t got Raspbian Jessie, chances are you’ll gold brick, in a sly nod to Breakout:
creating games for have neither Pg0 nor Pygame installed. The Python’s pip package
coders no matter installer will take care of grabbing Pg0 for you, but the preceding WIDTH = 1000
what stage of steps vary by distro. One thing you will need is Python 3.2 (or HEIGHT = 100
learning they are at. newer). If you’ve been sticking with Python 2.x in your coding def draw():
(perhaps because it’s used in a tutorial you’re following), make
Great games are all Pg0 your chance for a gentle upgrade to Python 3. screen.fill((205, 130, 0))
about the gameplay,
driven by powerful 03 Older Raspbian That colour tuple takes RGB values, so you can quickly
imaginations If you’re still running Raspbian Wheezy, you’ll need to run get colours off a cheatsheet; screen is built into Pg0 for
generating images, the following steps to install Pygame Zero: the window display, with methods available for all sorts of
animations, sounds different sprites…
and journeys
through game sudo apt-get update
worlds. Good sudo apt-get install python3-setuptools python3-pip
frameworks open sudo pip-3.2 install pgzero
up this creative
activity to people
who are not
traditional learners
of programming,
which is an area
where Python has
long excelled.
The Python Book 129
Create with Python
Right The bat
and ball come
first – they’re the
cornerstones of
Pong and Breakout
Program 06 Sprite 08 Batty
objects The intro example from the Pg0 docs expands on that You can think of Breakout as essentially being a
with the Actor class, which will automatically load the named moving bat – that is, you’re hitting a moving ball in order to
David Ames, who sprite (Pg0 will hunt around for a .jpg or .png in a subdirectory knock out blocks. The bat is a rectangle, and Pygame’s Rect
uses Pg0 to teach called images). objects store and manipulate rectangular areas – we use
younger children alien = Actor(‘alien’) Rect((left, top), (width, height)), before which we define the bat
to code at events alien.pos = 100, 56 colour and then call upon the draw function to put the bat on
across the UK, WIDTH = 500 the screen, using the screen function.
told us: “One thing HEIGHT = alien.height + 20 W = 800
to avoid when it def draw(): H = 600
comes to teaching RED = 200, 0, 0
kids is Object screen.clear() bat = Rect((W/2, 0.96 * H), (150, 15))
Orientation.” OOP alien.draw() def draw():
(object-oriented You can download the alien from the Pg0 documentation (bit.
programming) is ly/1Sm5lM7) and try out the animation shown there, but we’re screen.clear()
partly abstracted taking a different approach in our game. screen.draw.filled_rect(bat, RED)
away by Pg0, but it
can’t be ignored. 07 BreakoutviaPong 09 Mousemove
While the Pi is something of a tribute to 1980s 8-bit We want to move the bat, and the mouse is closer to an
Perhaps the best computers, Breakout comes from the 1970s and is a direct arcade paddle than the arrow keys. Add the following:
approach is using descendant of the early arcade classic Pong. We’ll follow the def on_mouse_move(pos):
Pg0 and some route from Pong to Breakout (which historically involved Apple
simple code to founders Steve Wozniak and Steve Jobs) in the steps to creating x, y = pos
start, then dropping our code, leaving you with the option of developing the Pong bat.center = (x, bat.center[1])
in a piece of OO elements into a proper game, as well as refining the finished Use pgzrun to test that you have a screen, bat and movement.
when it’s needed to Breakout clone.
solve a particular
problem.
With the Code Club
age group – about
eight to eleven –
feeding information
to solve practical
problems works
well. It can work
with adults, too –
but there’s always
someone who’s
read ahead and
has a few tricky
questions.
130 The Python Book
Create with Python
To get the ball to move we need 10 Square ball
to define move(ball) for each case In properly retro graphics-style, we define a square
where the ball meets a wall ball too – another rectangle, essentially, with the (30, 30) size
making it that subset of rectangles that we call a square.
Full code listing
We’re doing this because Rect is another built-in in Pg0. If we
## Breakout type game to demonstrate Pygame Zero library wanted a circular ball, we’d have to define a class and then use
## Based originally upon Tim Viner’s London Python Dojo Pygame’s draw.filled_circle(pos, radius, (r, g, b)) - but Rect we
## demonstration can call directly. Simply add:
## Licensed under MIT License - see file COPYING
WHITE = 200,200,200
from collections import namedtuple ball = Rect((W/2, H/2), (30, 30))
import pygame
import sys … to the initial variable assignments, and:
import time
screen.draw.filled_rect(ball, WHITE)
W = 804
H = 600 … to the def draw() block.
RED = 200, 0, 0
WHITE = 200,200,200 11 Action!
GOLD = 205,145,0 Now let’s make the ball move. Download the tutorial
resources in FileSilo.co.uk and then add the code inside the
ball = Rect((W/2, H/2), (30, 30)) ‘move.py’ file to assign movement and velocity. Change the 5 in
Direction = namedtuple(‘Direction’, ‘x y’) ball_dir = Direction(5, -5) if you want the ball slower or faster,
ball_dir = Direction(5, -5) as your processor (and dexterity) demands – but it’s hard to
tell now because the ball goes straight off the screen! Pg0 will
bat = Rect((W/2, 0.96 * H), (120, 15)) call the update() function you define once per frame, giving the
illusion of smooth(ish) scrolling if you’re not running much else.
class Block(Rect):
12 def move(ball)
def __init__(self, colour, rect): To get the ball to move within the screen we need to
Rect.__init__(self, rect) define move(ball) for each case where the ball meets a wall.
self.colour = colour For this we use if statements to reverse the ball’s direction at
each of the boundaries. Refer to the full code listing on page 67.
blocks = []
for n_block in range(24): Note the hardcoded value of 781 for the width of screen,
minus the width of ball – it’s okay to hardcode values in
block = Block(GOLD, ((((n_block % 8)* 100) + 2, ((n_block // early versions of code, but it’s the kind of thing that will need
8) * 25) + 2), (96, 23))) changing if your project expands. For example, a resizable
screen would need a value of W - 30.
blocks.append(block)
13 Absolute values
def draw_blocks(): You might expect multiplying y by minus one to work for
for block in blocks: reversing the direction of the ball when it hits the bat:
screen.draw.filled_rect(block, block.colour) ball_dir = Direction(ball_dir.x, -1 * ball_dir.y)
… but you actually need to use abs, which removes any minus
def draw(): signs, then minus:
screen.clear() ball_dir = Direction(ball_dir.x, - abs(ball_dir.y))
screen.draw.filled_rect(ball, WHITE)
screen.draw.filled_rect(bat, RED) Try it without in the finished code and see if you get some
draw_blocks() strange behaviour. Your homework is to work out why.
def on_mouse_move(pos):
x, y = pos
bat.center = (x, bat.center[1])
def on_mouse_down():
global ball_dir
ball_dir = Direction(ball_dir.x * 1.5, ball_dir.y * 1.5)
The Python Book 131
Create with Python
Right Tom Viner’s
array of blocks
negates the need for
bordered rectangles
Pg0 +1 14 Sounds 17 Going for gold
Also upon bat collision, sounds.blip.play() looks in Create a Block class:
There’s a new the sounds subdirectory for a sound file called blip. You can
version of Pg0 in download the sounds (and finished code) from FileSilo.co.uk. class Block(Rect):
development – it def __init__(self, colour, rect):
may even be out Actually, now we think about it, ignore the previous Rect.__init__(self, rect)
as you read this. comment about homework – your real homework is to turn self.colour = colour
Pg0 creator Daniel what we’ve written so far into a proper game of Pong! But first
Pope tells us “a tone let’s finish turning it into Breakout! … and pick a nice colour for your blocks:
generation API is in
the works,” and that 15 Blockhead! GOLD = 205,145,0
at the Pg0 PyConUK If you’re not very familiar with the ancient computer
sprint, “we finished game Breakout, then: 18 Line up the blocks
Actor rotation.” This builds an array of 24 blocks, three rows of eight:
apt-get install lbreakout2
Contributions are … and have a play. Now, we haven’t set our sights on building blocks = []
welcome – not only something quite so ambitious in just these six pages, but we for n_block in range(24):
to the Pg0 code, but do need blocks.
more examples are block = Block(GOLD, ((((n_block % 8)* 100) + 2,
needed not just to 16 Building blocks ((n_block // 8) * 25) + 2), (96, 23)))
show what can be There are many ways of defining blocks and distributing
done, but to give them onto the screen. In Tom Viner’s team’s version, from blocks.append(block)
teachers tools to the London Python Dojo – which was the code that originally
enthuse children inspired this author to give this a go – the blocks are sized in 19 Drawing blocks
about the creative relation to number across the screen, thus: Draw_blocks() is added to def draw() after defining:
act of programming.
N_BLOCKS = 8 def draw_blocks():
Pg0 has also BLOCK_W = W / N_BLOCKS for block in blocks:
inspired GPIO BLOCK_H = BLOCK_W / 4 screen.draw.filled_rect(block, block.colour)
Zero, to make BLOCK_COLOURS = RED, GREEN, BLUE
GPIO programming 20 Block bashing
easier on the Using multicoloured blocks which are then built into an All that remains with the blocks is to expand def
Raspberry Pi, with array means that blocks can join without needing a border. move(ball) – to destroy a block when the ball hits it.
rapid development With its defining variables in terms of screen width, it’s good to_kill = ball.collidelist(blocks)
occurring on this sustainable code, which will be easy to amend for different if to_kill >= 0:
new library as we go screen sizes – see github.com/tomviner/breakout.
to press. sounds.block.play()
However, the array of colour bricks in a single row is not ball_dir = Direction(ball_dir.x, abs(ball_dir.y))
enough for a full game screen, so we’re going to build our array blocks.pop(to_kill)
from hard-coded values…
132 The Python Book
Create with Python
Left Test your game
once it’s finished
– then test other
people’s Breakout
games to see how
the code differs
Full code listing (cont.) 21 Gameover
Lastly, we need to allow for the possibility of successfully
def move(ball): destroying all blocks.
global ball_dir if not blocks:
ball.move_ip(ball_dir)
sounds.win.play()
if ball.x > 781 or ball.x <= 0: sounds.win.play()
ball_dir = Direction(-1 * ball_dir.x, ball_dir.y) print(“Winner!”)
time.sleep(1)
if ball.y <= 0: sys.exit()
ball_dir = Direction(ball_dir.x, abs(ball_dir.y))
22 Scoredraw
if ball.colliderect(bat): Taking advantage of some of Pygame Zero’s quickstart
sounds.blip.play() features, we’ve a working game in around 60 lines of code.
ball_dir = Direction(ball_dir.x, - abs(ball_dir.y)) From here, there’s more Pg0 to explore, but a look into Pygame
unmediated by the Pg0 wrapper is your next step but one.
to_kill = ball.collidelist(blocks)
if to_kill >= 0: First refactor the code; there’s plenty of room for improvement
– see the example ‘breakout-refactored.py’ which is included in
sounds.block.play() your tutorial resources. Try adding scoring, the most significant
ball_dir = Direction(ball_dir.x, abs(ball_dir.y)) absence in the game. You could try using a global variable and
blocks.pop(to_kill) writing the score to the terminal with print(), or instead use
screen.blit to put it on the game screen. Future versions of Pg0
if not blocks: might do more for easy score keeping.
sounds.win.play()
sounds.win.play() 23 Class of nine lives
print(“Winner!”) For adding lives, more layers, and an easier life-keeping
time.sleep(1) score, you may be better defining the class GameClass and
sys.exit() enclosing much of the changes you wish to persist within it,
such as self.score and self.level. You’ll find a lot of Pygame code
if ball.y > H: online doing this, but you can also find Pg0 examples, such as the
sounds.die.play() excellent pi_lander example by Tim Martin: github.com/timboe/
print(“Loser!”) pi_lander.
time.sleep(1)
sys.exit() 24 Don’t stop here
This piece is aimed at beginners, so don’t expect to
def update(): understand everything! Change the code and see what works,
move(ball) borrow code from elsewhere to add in, and read even more code.
Keep doing that, then try a project of your own – and let us know
how you get on.
The Python Book 133
dWeveeblopment
136 Develop with Python 142 136
Why Python is perfect for the web
142 Creating dynamic templates
Use Flask and Jinja2 to their full potential
146 Build your own blog
Begin developing your blog
150 Deliver content to your blog
Add content to your site
154 Enhance your blog
Complete your blog with add-ons
154
“Python is a versatile language,
perfect for making websites”
134 The Python Book
The Python Book 135
Web development
Don’t be fooled into thinking Python is a restrictive language or
incompatible with the modern web. Explore options for building
Python web apps and experience rapid application development
136 The Python Book
Web development
Why? Thanks to the introduction of the Web Server Apple’s implementation of the library differs
Gateway Interface (WSGI) in 2003, developing greatly from the official release. Installing
First released in 1991, companies Python web apps for general web servers using Homebrew helps you to keep up to date
like Google and NASA have been became a viable solution as opposed to and also means you get the Python package
using Python for years restricting them to custom solutions. manager pip included.
Python executables and installers are Once Python is installed the first package to
widely available from the official Python site at download should be virtualenv using ‘pip install
www.python.org. virtualenv’, which enables you to create project-
specific shell environments. You can run
Mac OS X users can also benefit greatly projects on separate versions of Python with
from using Homebrew to install and manage separate project-specific packages installed.
their Python versions. Whilst OS X comes
bundled with a version of Python, it has some Check out the detailed Hitchhiker’s Guide
potential drawbacks. Updating your OS may to Python for more information: docs.python-
clear out any downloaded packages, and guide.org/en/latest.
Frameworks Let’s take a look at some of the frameworks
available when developing Python web applications
Django djangoproject.com Tornado tornadoweb.org
GOOD FOR: Large database-driven web apps with multiuser support GOOD FOR: Web socket interaction and long polling due to its
and sites that need to have heavily customisable admin interfaces ability to scale to manage vast numbers of connections
Django contains a lot of impressive features, all in the name of interfaces Tornado is a networking library that works as a nonblocking web server
and modules. These include autowiring, admin interfaces and database and web application framework. It’s known for its high performance and
migration management tools by default for all of your projects and scalability and was initially developed for friendfeed, which was a real-
applications. Django will help to enable rapid application development for time chat system that aggregated several social media sites. It closed
enterprise-level projects, whilst also enabling a clear modular reuseable down in April 2015 as its user numbers had declined steadily, but Tornado
approach to code using subapplications. remains as active and useful as ever.
Flask flask.pocoo.org PyramiD pylonsproject.org
GOOD FOR: Creating full-featured RESTful APIs. Its ability to manage GOOD FOR: Highly extensible and adaptable to any project
multiple routes and methods is very impressive requirement. Not a lightweight system either
Flask’s aim is to provide a set of commonly used components such as Heavily focused on documentation, Pyramid brings all the much needed
URL routing and templates. Flask will also work on controlling the request basic support for most regular tasks. Pyramid is open source and
and response objects, all-in-all this means it is lightweight but is still a also provides a great deal of extensibility – it comes with the powerful
powerful microframework. Werkzeug Debugger too.
Werkzeug
werkzeug.pocoo.org
GOOD FOR: API creation, interacting with
databases and following strict URL routes
whilst managing HTTP utilitie
Werkzeug is the underlying framework for
Flask and other Python frameworks. It provides
a unique set of tools that will enable you to
perform URL routing processes as well as
request and response objects, and it also
includes a powerful debugger.
The Python Book 137
Web development
Create an API Let us explore the Flask microframework and build a
simple yet powerful RESTful API with minimal code
01 Install Flask 04 Connect to Database from contextlib import closing
Create a new directory inside of With the database path defined, def init_db():
which your project will live. Open a Terminal we need a way to create connection to the
window and navigate to be inside your new database for the application to obtain data. with closing(connect_db()) as db:
directory. Create a new virtual environment Create a new method called ‘connet_db’ to with app.open_resource(‘schema.sql’,
for this project, placed inside a new directory manage this for us. As a method we can call it
called ‘venv’, and activate it. Once inside when we set up a prerequest hook shortly. This mode=’r’) as f:
the new virtual shell, proceed to installing will return a new open connection using the db.cursor().executescript(f.read())
Flask using the ‘pip install Flask’ command. database details set in the configuration object.
db.commit()
virtualenv venv def connect_db():
. venv/bin/activate return sqlite3.connect(app. 07 Populate the Database
pip install Flask config[‘DATABASE’]) To populate the database you
can now run the init_db inside an active
02 Create Index 05 Database Schema python shell. To do so enter a shell by typing
Create a new file in the root of the Our SQLite database will only contain ‘python’ inside your environment, and then
project location called ‘index.py’. The sample API one table. Create a new file called ‘schema.sql’ running the command below. Alternatively,
will use a SQLite database, so we need to import in the root of the project directory. This fill will you can use the sqlite3 command and
that module for use in the application. We’ll contain the SQL commands required to create pipe the schema.sql file into the database.
also import some core components from the the table and populate it with some sample
Flask module to handle request management bootstrapped data. # Importing the database using the
and response formatting as well as some other init_db method
functions. The minimum import for a Flask drop table if exists posts; python
application is Flask itself. create table posts ( >>> from index import init_db
>>> init_db()
import sqlite3 id integer primary key autoincrement, # Piping the schema using SQLite3
from flask import Flask, request, g, title text not null, sqlite3 /tmp/api.db < schema.sql
redirect, url_for, render_template, text text not null
abort, jsonify ); 08 Request DB Connection
insert into posts (title, text) values With the database created and
03 Declare Config (‘First Entry’, ‘This is some text’); populated we need to be able to ensure
For a small application we can declare insert into posts (title, text) values we have an open connection and close it
configuration options as upper-case name value (‘Second Entry’, ‘This is some more text’); accordingly when finished. Flask has some
pairs inside the main module, which we’ll do now. decorator methods to help us achieve this.
Here we can define the path and name of the insert into posts (title, text) values The before_request() method will establish
SQLite database and also set the Flask debug (‘Third Entry’, ‘This is some more text the connection and stores it in the g object
output to True for development work. Initialise (again)’); for use throughout the request cycle. We
the Flask application to a namespace and then can then close the connection after the
import the config values set directly above it. 06 Instantiate the Database cycle using the teardown_request() method.
We then run the application. All routes must be To populate the database with the new
placed above these last two lines. table and any associated data, we will need to @app.before_request
import and apply the schema to the database. def before_request():
Add a new module import at the top of the
project file to obtain the ‘contextlib.closing()’ g.db = connect_db();
method. What we will do next is create a method
that will initialise the database by reading the
contents of schema.sql and executing it against
the open database.
# Config “World-renowned image sharing service
DATABASE = ‘/tmp/api.db’ Instagram and social pin board Pinterest
DEBUG = True have also implemented Python as part of
app = Flask(__name__) their web stack, opting for Django”
app.config.from_object(__name__)
# Add methods and routes here
if __name__ == ‘__main__’:
app.run()
138 The Python Book
@app.teardown_request posts = [dict(title=row[0], Python in
def teardown_request(exception): text=row[1]) for row in ur.fetchall()] the wild
db = getattr(g, ‘db’, None) return jsonify({‘count’: len(posts), Interested in Python development?
if db is not None: ‘posts’: posts}) You’d be in good company with big
names currently using it
db.close() 12 Get a specific Post
CTo obtain a specific post from the Disqus, the popular social interaction
09 Display Posts API we need to create a new route, which will comment service provider, has
Create your first route so that we can accept a dynamic value as part of the URI. We been implementing their production
return and display all available posts. To query can also choose to use this route for multiple applications in Python for a very long time.
the database we execute a SQL statement request methods, which are in this case GET Python’s benefit for the development
against the stored db connection. The results and DELETE. We can determine the method team was its ability to scale effectively
are then mapped to values using Python’s by checking the request.method value and and cater for a large number of consumers
dict method and saved as the posts variable. run it against a conditional if/else statement. whilst also providing an effective
To render a template we then call render_ underlying API for internal and external
template() and pass in the file name and the @app.route(‘/api/v1/posts/<int:post_id>’, use. The company are now starting to
variable to display as the second argument. methods=[‘GET’, ‘DELETE’]) run some production apps in Go, but the
Multiple variables can be passed through as a def single_post(post_id): majority of code still runs on Python.
comma-separated list.
method = request.method World-renowned image sharing service
@app.route(‘/’) if method == ‘GET’: Instagram and social pin board Pinterest
def get_posts(): have also implemented Python as part of
cur = g.db.execute(‘select title, their web stack, opting for Django to assist
cur = g.db.execute(‘select title, text text from posts where id =?’, [post_id]) with the functionality and ability to cater
from posts order by id desc’) for the many thousands of content views
posts = [dict(title=row[0], and requests made to their services.
posts = [dict(title=row[0], text=row[1]) text=row[1]) for row in cur.fetchall()]
for row in cur.fetchall()] Mozilla, Atlassian’s Bitbucket
return jsonify({‘count’: len(posts), repository service, and popular satire site
return render_template(‘show_posts. ‘posts’: posts}) The Onion have all been noted as using
html’, posts=posts) Django for their products.
elif method == ‘DELETE’:
10 Template Output g.db.execute(‘delete from posts
Flask expects templates to be available
within the templates directory in the root of where id = ?’, [post_id])
the project, so make sure that you create that return jsonify({‘status’: ‘Post
directory now. Next, add a new file called
‘show_posts.html’. The dynamic values are deleted’})
managed using Jinja2 template syntax, the
default templating engine for Flask applications. 13 Run the application
Save this file in the templates directory. To run your Flask application, navigate
using the active Terminal window into the
<ul class=posts> root of the project. Ensuring you are in an
{% for post in posts %} active virtual environment Python shell,
<li><h2>{{ post.title }}</h2>{{ post. enter the command to run the main index
file. The built-in server will start and the site
text|safe }} will be accessible in the browser on default
{% else %} port local address http://127.0.0.1:5000.
<li>Sorry, no post matches your
python index.py
request.
{% endfor %}
</ul>
11 Make an API Response 14 API JSON Output
To create an API response we can The root of the application will render
define a new route with a specific API endpoint. the template we previously created. Multiple
Once again, we query the database for all routes can be generated to create a rich web
posts. The data is then returned as JSON, application. Visiting an API-specific URL in
using the JSONify method to do so. We can the browser will return the requested data as
add specific values such as post count and cleanly formatted JSON. The ability to define
a custom message if you wish, as well as the custom routes like a versioned RESTful endpoint
actual posts variable, formatted as JSON. is incredibly powerful.
@app.route(‘/api/v1/posts/’,
methods=[‘GET’])
def show_entries():
cur = g.db.execute(‘select title, text
from posts order by id desc’)
The Python Book 139
Web development
Django application
development Django is a full Python web-app framework
with impressive command-line tools
Installing Database
Django models
& migration
The installation of Django is
relatively easy once you have 03 Create Core Project Django’s ability to manage the
python installed. See for yourself The Django install contains some migration and maintenance of
as we build a simple app here incredibly useful command-line tools, which database schema and project
will help you to run a number of repetitive and models is very impressive
difficult tasks. Let’s use one of them to create a
fresh project structure for us. Run the django- 01 Generate the model
admin.py script with the name of the project that Open blog/models.py and create the
you want created. first model class, providing the property names
and types for each. You can dig deeper into
django-admin.py startproject myblog field types via the docs here: bit.ly/1yln1kn.
Once complete, open myblog/settings.py and
add the blog app to the list of allowed installed
applications so that the project will load it.
01 Create Virtual Environment 04 Initial Migration # blog/models.py
Create a new directory for your project Navigate into the project directory via class Post(models.Model):
and navigate inside it using a new Terminal the Terminal window. Some of the installed
window. Create a new virtual environment for apps included in the project generation require title = models.CharField(max_
this project, opting to use the latest Python 3. database tables. length=200)
Your Python 3 location may vary, so be sure to
set the correct path for the binary package. Using the helper. run a migration command to text = models.TextField()
create all of these automatically. The Terminal # myblog/settings.py
virtualenv -p /usr/local/bin/python3 venv window will keep you informed of all of your INSTALLED_APPS = (‘django.contrib.admin’,
progress and what has been applied from the ..., ‘django.contrib.staticfiles’, ‘blog’)
02 Activate and Install migration.
Using your Terminal window, 02 Data Migration
activate the virtual environment to start the cd myblog Any creation of models or changes to
project-specific shell. VirtualEnv has a local python manage.py migrate data need to be migrated. To do so we need
version of the Python package manager to make migration files from the model data,
pip installed, so it’s fairly straight forward 05 CreateApp which generate sequentially numbered files.
to run the command to install Django. Each Django project is made up of Then we run a specific migration to generate
at least one application or module. Run the the required SQL and the final migrate
. venv/bin.activate startapp command to create a new blog app command performs the database execution.
pip install Django module, which will generate the required
code adjacent to the main project structure. python manage.py makemigrations blog
python manage.py sqlmigrate blog 0001
python manage.py startapp blog python manage.py migrate
140 The Python Book
Web development
Autowiring the Hosting
admin interface Python
apps
Admin sections can be 03 Enable Admin Management
problematic in their own right. To enable our module and associated Admin sections can be
Django provides an extensible models to be managed through the admin problematic in their own right.
admin interface for you interface, we need to register them with the Django provides an extensible
admin module. Open blog/admin.py and then go admin interface for you
on to import and register the models in turn (we
only have one of these currently though). Save Heroku heroku.com
the file and refresh the admin site to see the
posts that are now available to manage. This app is perhaps one of the most well-
known cloud hosting providers. Their stack
from django.contrib import admin server environments support a number
of core web app languages including
# Register your models here. Python as standard. Their unique Toolbelt
command-line features and integration
01 Create Admin User 04 Create a View with Git repositories, as well as being
Django makes content administration With the admin interface accepting incredibly quick and easy to scale and
incredibly easy and has an admin section new submissions for our post class we’ll create improve performance, makes them an
available in a default project as standard a view page to display them. Open blog/views. obvious choice. A free account will let you
at http://127.0.0.1:8000/admin. To log in you py and import the Post class from the models. run a Python web app on one dyno instance
need to create a superuser account. Run Create a method to obtain all posts from the without any cost.
the associated command and specify user database and output them as a string.
details as required to then proceed and log in. Python Anywhere
from django.http import HttpResponse
python manage.py createsuperuser from blog.models import Post www.pythonanywhere.com
def index(request):
Another hosted option, and one created
post_list = Post.objects.order_by(‘-id’) specifically for Python applications in
[:5] general is Python Anywhere. The free
basic option plan has enough weight and
output = ‘<br />’.join([p.title for p power behind it to get you up and running
in post_list]) with a Python web app without having to
scale, but as soon as your project gains
return HttpResponse(output) traction, you can switch plans and boost
your plans performance.
02 Switch on blog management 05 Manage the URLs
Having logged in to the administration Create ‘blog/urls.py’ and add the code It offers an incredibly impressive range
interface you will be greeted with features to to import the views that were just made in the of modules as standard, available to import
manage users and group roles and privileges, module and the accompanying URL patterns. into your application immediately to get you
which alone are very powerful and provided for Open myblog/urls.py and add the URL function started, including Django and Flask should
you by Django. There is not yet, however, any call to implement a new URL for the app to you need them.
access to manage our blog posts so let’s turn display the view. Visit http://127.0.0.1:5000/
that on. blog in your browser to render the new view.
Using the # blog/urls.py
dev server
from django.conf.urls import patterns,url
Django ships with a very helpful built-in from blog import views
development server, which will help you urlpatterns = patterns(‘’,
out by autocompiling and reloading url(r’^$’, views.index, name=’index’),
after you have completed all of your file )
changes. All you have to do to start # myblog/urls.py
the server is to run the ‘python urlpatterns = patterns(‘’,
manage.py runserver’ command from url(r’^blog/’, include(‘blog.urls’)),
your Terminal window within url(r’^admin/’, include(admin.site.urls)),
the project directory. )
The Python Book 141
Web development
Creating dynamic templates
with Flask, Jinja2 and Twitter
Create a dynamic webpage with Twitter and Flask’s rendering
engine, Jinja2
Resources
Python 2.7+
Flask 0.10.0:flask.pocoo.org
Flask GitHub:
github.com/mitsuhiko/flask
A Twitter account
Your favourite text editor
Code downloaded from FileSilo
Q The template uses a loop to generate a list of Twitter tweets
Python and Flask are a great combination 01 Rearranging our code helpers. Because Python is smart, It will look
when you’re looking to handle the Twitter Server code can get messy and in the current directory for a helpers.py file
OAuth process and build requests to obtain unmaintainable quickly, so the first thing we’re before it looks for a system module. Now every
tokens. We’ve used Twitter here because of going to do is move our helper functions to function included in helpers.py is accessible
the large amount of easily digestible data another file and import them into our project, to our project. All we need to do to call them is
available on it. However, since Twitter adheres much like you would a module. This way, it will prepend our the methods we called before with
to the standards set out by OAuth 1.0, the code be clear which functions are our server logic helper.function_name and it will execute. For
we’ve used to sign and build requests can and endpoints and which are generic Python sign_request, we’ll need to pass our
be modified to work with any third-party API functions. Open the TwitterAuthentication file oauth_secret and consumer_secret for each
using the same standard without a great deal downloaded from FileSilo (stored under Twitter request rather than accessing it from the
of work. For years PHP has been a mainstay OAuth files) and locate the getParameters, session. Adjust the function declaration like so:
of template generation, but now with well- sign_request and create_oauth_headers
documented frameworks such as Flask, functions. Cut and paste them into a new file def sign_request(parameters, method,
Sinatra and Handlebars, the ability to use called helpers.py in the root of your project baseURL, consumer_secret, oauth_secret):
powerful scripting languages greatly improves folder. At the top of this file we want to import
our ability to make great web services. Here, some libraries. 02 server.py modules
we’re going to use Python, Flask and its With a lot of the modules needed in this
templating engine to display tweets. Flask import urllib, collections, hmac, project having been moved to helpers.py, we
comes with the super-nifty Jinja2 templating binascii, time, random, string can now remove most of them from server.py.
engine, If you’re familiar with Node.js or front- If we amend our first import statement to be…
end JavaScript, the syntax will look very from hashlib import sha1
similar to the Handlebars rendering engine. import urllib2, time, random, json
But, before we dive into that, we need to Now we can head back over to server.py and
organise some of the example code that we’re import the helper functions back into our …our project will continue to function as it did
using for this. project. We do this by simply calling import before. Note the addition of the json module:
142 The Python Book
Web development
we’ll be using that later as we start handling {% if session[‘oauth_token’] != “” %} Fig 01
Twitter data.
<h1>Already Authorised</h1>
Having Flask use a rendering engine is
super-simple. Flask comes packaged with <div class=”dialog”>
the Jinja2 template rendering engine, so we’ve
nothing to install – we just need to import the <p>Hello, You’ve authenticated!<br>Let’s <a href=”/get-tweets”>get some tweets</a></p>
package into the project. We can do this by
adding render_template to the end of our from </div>
flask import […] statement.
{% else %}
03 Our first template
Now that we have a rendering engine, <h1>Authorisation required</h1>
we need to create some templates for it to
use. In the root of our project’s folder, create <div class=”dialog”>
a new folder called templates. Whenever
we try to render a template, Flask will look in <p>We need to <a href=”/authenticate”>authenticate</a></p>
this folder for the template specified. To get
to grips with templating, we’ll rewrite some </div>
of our authentication logic to use a template,
rather than manually requesting endpoints. In {% endif %} Code on
templates, create an index.html file. You can FileSilo
treat this HTML file like any other – included in Q The BSD-licensed Flask is easy to set up
the resources for this tutorial is an index.html and use – check out the website for more info
that includes all of the necessary head tags and
<!DOCTYPE> declarations for this file.
04 Rendering our template
In server.py, let’s create a route for ‘/’ to
handle the authorisation process.
@app.route(‘/’)
def home():
if not ‘oauth_token’ in session:
session.clear()
session[‘oauth_secret’] = ‘’
session[‘oauth_token’] = ‘’
return render_template(‘index.html’)
It’s a simple function: all we want to do is check
whether or not we have an oauth_token already
and create those properties in the Flask session
so we don’t throw an error if we try to access
it erroneously. In order to send our generated
template in response to the request, we return
render_template(‘index.html’).
05 Template variables
We can choose to send variables to our
template with render_template(‘index.htm’,
variableOne=value, variableTwo=Value) but
in this instance we don’t need to as each template
has access to the request and session variables.
The Python Book 143
Web development
Open index.html. All code executed in a Flask Now we know how to build templates,
template is contained within {% %}. As this is our let’s grab some tweets to display
homepage, we want to direct users accordingly,
So let’s check if we’ve got an access token (Fig 01). intercept and handle the path get-tweets. The 10 Signing and sending our request
second lets us define a parameter that we can We’ve built our parameters, So let’s
Between the ifs and else of the template is use as a value in our getTweets function. By sign our request and then add the signature to
standard HTML. If we want to include some data including count=0 in our function declaration, the parameters (Fig 03).
– for example, the access token – we can just add we ensure that there will always be a default
{{ session[‘oauth_token’] }} in the HTML and it value when the function is executed; this way we Before we create the authorisation headers,
will be rendered in the page. Previously, in our / don’t have to check the value is present before we need to remove the count and user_id
authorised endpoint, we would display the OAuth we access it. If a value is included in the URL, it values from the tweetRequestParams
token that we received from Twitter; however, now will override the value in the function. The string dictionary, otherwise the signature we just
that we have a template, we can redirect our users inside the <variable name> determines the created won’t be valid for the request. We can
back our root URL and have a page rendered for us name of the variable. If you want the variable achieve this with the del keyword. Unlike our
that explains the progress we’ve made. passed to the function to have a specific type, token requests, this request is a GET request,
you can include a converter with the variable so instead of including the parameters
06 Getting lost name. For example, if we wanted to make sure in the request body, we define them as
(and then found again) that <count> was always an integer instead of a query parameters.
With every server, some things get misplaced or float or string, we’d define our route like so:
people get lost. So how do we handle this? Rather ?count=tweetRequestParams[‘count’]
than defining a route, we can define a handler @app.route(‘/get-tweets/<int:count>’) &user_id=tweetRequestParams[‘user_id’]
that deals with getting lost.
09 Checking our session 11 Handling Twitter’s response
@app.errorhandler(404) and building our request Now we’re ready to fire off the request
def fourOhFour(error): Before we start grabbing tweets, we want to and we should get a JSON response back
run a quick check to make sure we have the from Twitter. This is where we’ll use the json
return render_template(‘fourohfour.html’) necessary credentials and if not, redirect the module we imported earlier. By using the
user back the authorisation flow. We can do json.loads function, we can parse the JSON
If a page or endpoint is requested and triggers a this by having Flask respond to the request into a dictionary that we can access and we’ll
404, then the fourOhFour function will be fired. In with a redirection header, like so: pass through to our tweets.html template.
this case, we’ll generate a template that tells the
user, but we could also redirect to another page or if session[‘oauth_token’] == “” or tweetResponse = json.
dump the error message. session[‘oauth_secret’] == “”: loads(httpResponse.read())
return render_template(‘tweets.html’,
07 Static files return redirect(rootURL) data=tweetResponse)
Pretty much every webpage uses
JavaScript, CSS and images, but where do we Assuming we have all we need, we can start to Whereas before, we accessed the session
keep them? With Flask we can define a folder build the parameters for our request (Fig 02). to get data into our template, this time
for use with static content. For Flask, we create we’re explicitly passing a value through to
a static folder in the root of our project and You’ll notice that the nonce value is different our template.
access files by calling /static/css/styles.css or from that in our previous requests. Where the
/static/js/core.js. The tutorial resources include a nonce value in our authenticate and authorise 12 Displaying our tweets
CSS file for styling this project. requests can be any random arrangement of Let’s create that template now, exactly
characters that uniquely identify the request, the same as index.html but this time, instead of
08 Let’s get some tweets for all subsequent requests the nonce needs using a conditional, we’re going to create a loop
So now we know how to build templates, to be a 32-character hexadecimal string using to generate a list of tweets we’ve received.
let’s grab some tweets to display. In server.py only the characters a-f. If we add the following
define a new route, get-tweets,like so: function to our helpers.py file, we can quickly First, we check that we actually received
build one for each request. some data from our request to Twitter. If we’ve
@app.route(‘/get-tweets’) got something to render, we’re ready to work
@app.route(‘/get-tweets/<count>’) def nonce(size=32, chars=”abcdef” + through it, otherwise we’ll just print that we
def getTweets(count=0): string.digits): didn’t get anything.
You’ll notice that unlike our other authentication return ‘’.join(random.choice Once again, any template logic that we want
endpoints, we’ve made two declarations. (chars) for x in range(size)) to use to generate our page is included between
The first is a standard route definition: it will
144 The Python Book
Web development
{% %}. This time we’re creating a loop; inside the tweetRequestParams = { Fig 02
loop we’ll be able to access any property we have “oauth_consumer_key” : consumer_key, Fig 03
of that object and print it out. In this template “oauth_nonce” : helpers.nonce(32),
we’re going to create an <li> element for each “oauth_signature_method” : “HMAC-SHA1”, Fig 04
tweet we received and display the user’s profile “oauth_timestamp” : int(time.time()),
picture and text of the tweet (Fig 04). “oauth_version” : “1.0”,
“oauth_token” : session[‚Äòoauth_token’],
In our template we can access properties “user_id” : session[‘user_id’],
using either dot notation (.) or with square “count” : str(count)
brackets ([]). They behave largely the same;
the [] notation will check for an attribute on }
the dictionary or object defined whereas the
. notation will look for an item with the same tweetRequest = helpers.sign_request(tweetRequestParams, “GET”,
name. If either cannot find the parameter “https://api.twitter.com/1.1/statuses/user_timeline.json”, consumer_secret,
specified, it will return undefined. If this occurs, session[‘oauth_secret’])
the template will not throw an error, it will simply
print an empty string. Keep this in mind if your tweetRequestParams[“oauth_signature”] = tweetRequest
template does not render the expected data:
you’ve probably just mis-defined the property makeRequest=urllib2.Request(“https://api.twitter.com/1.1/statuses/
you’re trying to access. user_timeline.json?count=” + tweetRequestParams[‘count’] + “&user_id=”
+ tweetRequestParams[‘user_id’])
Unlike traditional Python, we need to
tell the template where the for loop and if/ del tweetRequestParams[‘user_id’], tweetRequestParams[‘count’]
else statements end, so we do that with
{% endfor %} and {% endif %}. makeRequest.add_header(“Authorization”, helpers.create_oauth_
headers(tweetRequestParams))
13 Flask filters
Sometimes, when parsing from JSON, try:
Python can generate erroneous characters httpResponse = urllib2.urlopen(makeRequest)
that don’t render particularly well in HTML.
You may notice that after tweet[‘text’] there except urllib2.HTTPError, e:
is |forceescape, This is an example of a Flask return e.read()
filter; it allows us to effect the input before we
render – in this case it’s escaping our values {% if data %}
for us. There are many, many different built-
in filters that come included with Flask. Your <ul id=”tweets”>
advisor recommends a full reading of all the {% for tweet in data %}
potential options. <li>
<div class=”image”>
14 Wrapping up <img src=”{{ tweet[‘user’][‘profile_image_url_https’]
That’s pretty much it for templating
with Flask. As we’ve seen, it’s insanely quick }}” alt=”User Profile Picture”>
and easy to build and deploy dynamic sites. </div>
Flask is great tool for any Python developer <div class=”text”>
looking to run a web service. Although we’ve <a>{{ tweet[‘text’]|forceescape }}</a>
used Twitter to demonstrate Flask’s power, </div>
all of the techniques described can be used
with any third-party service or database </li>
resource. Flask can work with other rendering {% endfor %}
engines, such as Handlebars (which is </ul>
superb), but Jinja2 still needs to be present
to run Flask and conflicts can occur between {% else %}
the two engines. With such great integration <p>We didn’t get any tweets :(</p>
between Flask and Jinja2, it makes little
sense to use another engine outside of very {% endif %}
specific circumstances.
The Python Book 145
Web development
Django comes with a
lightweight development
server so you can test all
your work locally
Django is of course able
to read and write to SQL
databases, but it needs
very little prior knowledge
to succeed in doing so
Using HTML and CSS in
conjunction with Django is
clear and straightforward;
it’s much easier to bug-fix
than PHP
Django comes with
a generic back-end
site that is set up in
seconds, and easily
customisable after that
Resources Build your own blog
with Django
Python Source Code
Learn how to use this extremely powerful
www.python.org/download/releases/2.7.2 Python-based web framework to create a
complete blog from scratch in record time
Django Source Code
www.djangoproject.com/download
Creating your own blog always feels like a orientated programming language designed to
great accomplishment. Sure, you could use the have clearly readable syntax. Due to its Python
fantastic WordPress if you need a complete blog base, it’s an incredibly powerful and simple-to-
with every feature you’d ever need right now. And use language for web development with a vast
Tumblr exists for people who just want to write array of applications.
something, or post pictures of corgis in space.
So let’s use it to make a blog. In this first
You don’t have full control from start to finish section of the process we will explore how to set
with a prefabricated blog, though, and neither up Django, writing and reading to a database,
of these is written in the fantastic Django. creating a front- and back-end, and some
Django is of course based on Python, the object- interactions with HTML.
146 The Python Book
Web development
05 Start the development server
Django comes with a lightweight
development server to test out work locally. We
can also use it to check our work, so cd to the
myblog folder and then use:
python manage.py runserver
If all goes well, it should return zero errors. Use
Ctrl+C to exit the server.
01 Install Python 03 Verify your Django
Django is based on Python, and requires To make sure Django installed properly,
it to be installed to develop on. Python 2.7 is the and that you have the right version, enter the
recommended version, and this is installed with Python shell by typing ‘python’ and enter
the python package. If you want to check your the following:
version, start the Python shell by typing ‘python’ import django
into the terminal. print django.get_version()
It will return a version number if it has installed
correctly, which should be 1.3.
02 Install Django 06 Configure the database
Most operating systems will have a The database settings are kept in the
Django package available in the repository, like settings.py file. Open it up with your favourite
python-django in Debian. The Django website editor and go to the Databases section. Change
has a list if you have trouble finding it, or you ENGINE to:
could build it from source. Make sure you install ‘ENGINE’: ‘django.db.backends.sqlite3’,
version 1.3. And in NAME, put the absolute path – for
example:
04 Start a new project ‘NAME’: ‘/home/user/projects/myblog/
In the terminal, cd to the folder you sqlite.db’,
want to develop the blog in, and then run the Save and exit.
next command:
django-admin startproject myblog 07 Create the database
The database file will be generated by
Here, ‘myblog’ can be replaced by whatever you using the command:
wish to name the project, but we’ll use it for the python manage.py syncdb
upcoming examples.
During the creation, it will ask you to set up a
superuser, which you can do now.
The SQLite database file will be created in
your myblog folder.
The Python Book 147
Web development
You don’t have full control from start
to finish with a prefabricated blog – but
you will with Django
08 Create your blog
Now it’s time to create a blog app in your
project. Type:
python manage.py startapp blog
This creates the models file which is where all
your data lives. You can change ‘blog’ to another
name, but we’ll use it in our examples.
09 Start your blog model 11 Install your app 13 Let’sblog
We can now take the first steps in Your app needs to be installed to your Create the post. For this example, we
creating our blog model. Open models.py and project, which is very simple. Open the settings. will call it test_post:
change it so it says the following: py file again, go to the INSTALLED_APPS section test_post = Post()
from django.db import models and add:
class Post(models.Model): ‘blog’, Now let’s add the blog content:
test_post.post = ‘Hello World!’
post = models.TextField() Then run the following to create the database test_post.title = ‘First Post’
tables: test_post.author = ‘Me’
This creates the Post class, which has a python manage.py sql blog test_post.pub_date = datetime.
subclass that contains your blog text. datetime.now()
And finally:
python manage.py syncdb And then save it with:
test_post.save()
10 Customise your blog 12 Set up to post 14 Start the site back-end
Let’s now expand the blog model a bit so Now we can create a post and test out To create the admin site, edit urls.py
it resembles a more classic blog: our code. First though, enter the Python shell: from the myblog directory, and uncomment or
class Post(models.Model): python manage.py shell add the following lines:
from django.contrib import admin
post = models.TextField() Then execute these commands to add all the admin.autodiscover()
title = models.TextField() necessary fields and data: url(r’^admin/’, include(admin.site.
author = models.CharField(max_ from blog.models import Post urls)),
length=50) import datetime
pub_date = models.DateTimeField() Save and exit, then edit settings.py and
uncomment this line from INSTALLED_APPS:
A CharField needs to have a character ‘django.contrib.admin’,
limit defined, and DateTimeField holds the
time values. The admin site is now at 127.0.0.1:8000/admin/.
148 The Python Book
Web development
Django is an incredibly powerful
and simple-to-uselanguage
15 Setup the admin page 21 Format the front page
The admin page has a generic, usable Go back into the template file,
template, but you need to configure it to view, index.html, and add the following html tags:
edit, create and delete posts. First, create a new {% for post in post_list %}
file admin.py in the blog directory and enter:
from blog.models import Post <h2>{{ post.title }}</h2>
from django.contrib import admin {{ post.author }} on {{ post.pub_
date }}
admin.site.register(Post) 18 Start the template <p>{{ post.post }}</p>
The code we’ve just written looks for a {% endfor %}
To have the posts display nicely on the site, edit template that currently doesn’t exist. We first
models.py and add: need to tell Django where templates are to be This is just an example – the post can be in any
class Post (models.Model): looked for in settings.py: order with any tags.
TEMPLATE_DIRS = (
… 22 Spruce up the admin list
def __unicode__(self): ‘/home/user/projects/templates’, We’ll do this in the admin.py file in our
) blog directory; open it in your editor and make
return self.title the following changes:
from blog.models import Post
Save, and run: You can put the template directory wherever you from django.contrib import admin
python manage.py syncdb want, as long as it’s referenced here. class Admin(admin.ModelAdmin):
The admin page is now usable! You should be 19 Write a template list_display = [‘title’, ‘author’,
able to see the other posts, and it’s now a lot Now to write the site template. In our ‘pub_date’]
easier to add more. example, we’re using index.html: admin.site.register(Post, Admin)
{% for post in post_list %}
16 Activate the front-end In this case ‘list_display’ is a fixed variable name.
Open up urls.py from the myblog {{ post.title }}
directory in your editor and add the following to {{ post.author }} 23 A logical post page
the urlpatterns section: {{ post.pub_date }} The new post page on the site might
url(r’^myblog/’, ‘blog.urls.index’)), {{ post.post }} not be in an order you’re comfortable with.
One of the examples in the file can be {% endfor %} We’ll change that now in admin.py with the
uncommented and edited to this as well. It following additions:
points to a model we will now create. This needs to be located in a folder with class Admin(admin.ModelAdmin):
the same name as your app within the
17 Create another urls file template directory. list_display = [‘title’, ‘author’,
You need to create another urls file in the ‘pub_date’]
app directory, in our case blog/urls.py. Create it 20 View your handiwork
and add the following: Let’s make sure this worked. Start the fields = [‘title’, ‘pub_date’,
from django.template import Context, developer server with: ‘author’, ‘post’]
loader python manage.py runserver admin.site.register(Post, Admin)
from blog.models import Post Remember to save!
from django.http import HttpResponse And navigate to 127.0.0.1:8000/myblog/.
def index(request): It’s not pretty, but you should have 24 A functional blog
So there you have it! Navigating to
post_list = Post.objects.all() successfully called upon your stored data. We’ll 127.0.0.1:8000/admin/ or 127.0.0.1:8000/myblog/
t = loader.get_template(‘blog/ spend the next steps tidying it up a bit. will show off the fine work you’ve created.
index.html’) Django is dead easy to use once you know how,
c = Context({ and there are plenty of tweaks you should be
able to make after this tutorial.
‘post_list’: poll_list,
})
return HttpResponse(t.render(c))
The Python Book 149
Web development
With Django we can make Django has built-in code to Allow your readers to With minimal extra code,
simple sidebars that list deal with pagination very give you feedback, and our template can display
archives by month cleanly and effectively moderate them in the the month archive from
admin panel the sidebar
Deliver content to your blog
We continue building an awesome blog using the powerful
Django framework, and this tutorial is all about the front-end
content delivery
Resources to read and write to the database. All simple
stuff, but of course it’s core to building websites
Python base: where Django might be called upon.
http://www.python.org/download/ Here we will give the front end of the site
an overhaul, making it more of the standard
Django source: https://www. you would expect from a modern blog. This
will include a sidebar, pages, post pages and
djangoproject.com/download/ the ability to add and moderate comments.
In the process we will learn some more of
In the last tutorial we began to build the most the benefits that come with using Django to
basic of blogs, and learned how to use a bit of develop websites.
Django in the process. We can now set up a new
project, create a database and write basic code You should keep using Django 1.3 for this
tutorial, as we did before.
150 The Python Book