├── 978-1-59059-982-2
├── Chapter11
│ ├── listing11-4.txt
│ ├── listing11-5.txt
│ ├── listing11-13.py
│ ├── listing11-2.txt
│ ├── listing11-12.py
│ ├── listing11-3.txt
│ ├── listing11-9.py
│ ├── listing11-10.py
│ ├── listing11-11.py
│ ├── listing11-6.py
│ ├── listing11-7.py
│ ├── listing11-8.py
│ └── listing11-1.py
├── Chapter10
│ ├── listing10-1.py
│ ├── listing10-12.txt
│ ├── listing10-2.py
│ ├── listing10-3.py
│ ├── listing10-5.py
│ ├── listing10-13.txt
│ ├── listing10-4.py
│ ├── listing10-10.py
│ ├── listing10-6.py
│ ├── listing10-14.txt
│ ├── listing10-7.py
│ ├── listing10-11.py
│ └── listing10-8.py
├── Chapter12
│ ├── listing12-1.py
│ ├── listing12-2.py
│ ├── listing12-3.py
│ ├── listing12-4.py
│ ├── listing12-5.py
│ └── listing12-6.py
├── Chapter17
│ ├── listing17-5.i
│ ├── listing17-1.java
│ ├── listing17-4.py
│ ├── listing17-3.c
│ ├── listing17-2.cs
│ └── listing17-6.c
├── Chapter24
│ ├── listing24-1.py
│ ├── listing24-2.py
│ ├── listing24-3.py
│ ├── listing24-4.py
│ ├── listing24-5.py
│ └── listing24-6.py
├── Chapter14
│ ├── listing14-2.py
│ ├── listing14-1.py
│ ├── listing14-3.py
│ ├── listing14-4.py
│ ├── listing14-5.py
│ ├── listing14-8.py
│ ├── listing14-9.py
│ ├── listing14-6.py
│ └── listing14-7.py
├── Chapter15
│ ├── listing15-4.py
│ ├── listing15-5.py
│ ├── listing15-6.py
│ ├── listing15-1.py
│ ├── listing15-8.psp
│ ├── listing15-9.py
│ ├── listing15-3.py
│ ├── listing15-7.py
│ └── listing15-2.py
├── Chapter02
│ ├── listing2-2.py
│ ├── listing2-4.py
│ ├── listing2-3.py
│ └── listing2-1.py
├── Chapter19
│ ├── listing19-1.cfg
│ ├── listing19-3.py
│ └── listing19-2.py
├── Chapter18
│ └── listing18-1.py
├── Chapter26
│ ├── listing26-3.sql
│ ├── listing26-1.sql
│ ├── listing26-2.sql
│ ├── listing26-6.py
│ ├── listing26-4.py
│ ├── listing26-5.py
│ ├── listing26-7.py
│ └── listing26-8.py
├── Chapter16
│ ├── listing16-1.py
│ ├── listing16-2.py
│ └── listing16-3.py
├── Chapter21
│ ├── listing21-1.py
│ ├── listing21-3.py
│ └── listing21-2.py
├── Chapter20
│ ├── listing20-2.py
│ ├── listing20-3.py
│ ├── listing20-1.txt
│ ├── listing20-6.py
│ ├── listing20-5.py
│ └── listing20-4.py
├── Chapter13
│ ├── listing13-2.py
│ └── listing13-1.py
├── Chapter25
│ ├── listing25-1.py
│ ├── listing25-3.py
│ └── listing25-2.py
├── Chapter03
│ └── listing3-1.py
├── Chapter04
│ ├── listing4-2.py
│ └── listing4-1.py
├── Chapter23
│ ├── listing23-1.py
│ └── listing23-2.py
├── Chapter22
│ ├── listing22-1.xml
│ ├── listing22-2.py
│ └── listing22-3.py
├── Chapter27
│ ├── listing27-3.py
│ ├── listing27-1.py
│ └── listing27-2.py
├── Chapter29
│ ├── listing29-1.py
│ ├── listing29-2.py
│ └── listing29-4.py
├── Chapter28
│ ├── listing28-1.py
│ └── listing28-2.py
└── README.txt
├── 4118.pdf
├── 4119.pdf
├── LICENSE.txt
├── 9781590599822.jpg
├── README.md
└── contributing.md
/978-1-59059-982-2/Chapter11/listing11-4.txt:
--------------------------------------------------------------------------------
1 | this
2 | is no
3 | haiku
--------------------------------------------------------------------------------
/4118.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/beg-python/HEAD/4118.pdf
--------------------------------------------------------------------------------
/4119.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/beg-python/HEAD/4119.pdf
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-5.txt:
--------------------------------------------------------------------------------
1 | this
2 | isn't a
3 | haiku
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-1.py:
--------------------------------------------------------------------------------
1 | # hello.py
2 | print "Hello, world!"
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/beg-python/HEAD/LICENSE.txt
--------------------------------------------------------------------------------
/9781590599822.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/beg-python/HEAD/9781590599822.jpg
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-13.py:
--------------------------------------------------------------------------------
1 | for line in open(filename):
2 | process(line)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-12.txt:
--------------------------------------------------------------------------------
1 | [x = 2]
2 | [y = 3]
3 | The sum of [x] and [y] is [x + y].
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-2.py:
--------------------------------------------------------------------------------
1 | # hello2.py
2 | def hello():
3 | print "Hello, world!"
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-2.txt:
--------------------------------------------------------------------------------
1 | Your mother was a hamster and your
2 | father smelled of elderberries.
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-12.py:
--------------------------------------------------------------------------------
1 | f = open(filename)
2 | for line in f:
3 | process(line)
4 | f.close()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-3.txt:
--------------------------------------------------------------------------------
1 | Welcome to this file
2 | There is nothing here except
3 | This stupid haiku
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-9.py:
--------------------------------------------------------------------------------
1 | f = open(filename)
2 | for char in f.read():
3 | process(char)
4 | f.close()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-10.py:
--------------------------------------------------------------------------------
1 | f = open(filename)
2 | for line in f.readlines():
3 | process(line)
4 | f.close()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-11.py:
--------------------------------------------------------------------------------
1 | import fileinput
2 | for line in fileinput.input(filename):
3 | process(line)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter12/listing12-1.py:
--------------------------------------------------------------------------------
1 | import wx
2 | app = wx.App()
3 | win = wx.Frame(None)
4 | win.Show()
5 | app.MainLoop()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-3.py:
--------------------------------------------------------------------------------
1 | # hello3.py
2 |
3 | def hello():
4 | print "Hello, world!"
5 |
6 | # A test:
7 | hello()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-5.py:
--------------------------------------------------------------------------------
1 | # reverseargs.py
2 | import sys
3 | args = sys.argv[1:]
4 | args.reverse()
5 | print ' '.join(args)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter17/listing17-5.i:
--------------------------------------------------------------------------------
1 | %module palindrome
2 |
3 | %{
4 | #include
5 | %}
6 |
7 | extern int is_palindrome(char *text);
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-13.txt:
--------------------------------------------------------------------------------
1 | [name = 'Magnus Lie Hetland' ]
2 | [email = 'magnus@foo.bar' ]
3 | [language = 'python' ]
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-6.py:
--------------------------------------------------------------------------------
1 | f = open(filename)
2 | char = f.read(1)
3 | while char:
4 | process(char)
5 | char = f.read(1)
6 | f.close()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter12/listing12-2.py:
--------------------------------------------------------------------------------
1 | import wx
2 | app = wx.App()
3 | win = wx.Frame(None)
4 | btn = wx.Button(win)
5 | win.Show()
6 | app.MainLoop()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-7.py:
--------------------------------------------------------------------------------
1 | f = open(filename)
2 | while True:
3 | char = f.read(1)
4 | if not char: break
5 | process(char)
6 | f.close()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-8.py:
--------------------------------------------------------------------------------
1 | f = open(filename)
2 | while True:
3 | line = f.readline()
4 | if not line: break
5 | process(line)
6 | f.close()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter17/listing17-1.java:
--------------------------------------------------------------------------------
1 | public class JythonTest {
2 |
3 | public void greeting() {
4 | System.out.println("Hello, world!");
5 | }
6 |
7 | }
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-4.py:
--------------------------------------------------------------------------------
1 | # hello4.py
2 | def hello():
3 | print "Hello, world!"
4 |
5 | def test():
6 | hello()
7 |
8 | if __name__ == '__main__': test()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter24/listing24-1.py:
--------------------------------------------------------------------------------
1 | from asyncore import dispatcher
2 | import asyncore
3 |
4 | class ChatServer(dispatcher): pass
5 |
6 | s = ChatServer()
7 | asyncore.loop()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter11/listing11-1.py:
--------------------------------------------------------------------------------
1 | # somescript.py
2 | import sys
3 | text = sys.stdin.read()
4 | words = text.split()
5 | wordcount = len(words)
6 | print 'Wordcount:', wordcount
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter14/listing14-2.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | s = socket.socket()
4 |
5 | host = socket.gethostname()
6 | port = 1234
7 |
8 | s.connect((host, port))
9 | print s.recv(1024)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter15/listing15-4.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | print 'Content-type: text/plain'
4 | print # Prints an empty line, to end the headers
5 |
6 | print 'Hello, world!'
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter02/listing2-2.py:
--------------------------------------------------------------------------------
1 | # Split up a URL of the form http://www.something.com
2 |
3 | url = raw_input('Please enter the URL: ')
4 | domain = url[11:-4]
5 |
6 | print "Domain name: " + domain
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter15/listing15-5.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import cgitb; cgitb.enable()
4 |
5 | print 'Content-type: text/html'
6 |
7 | print
8 |
9 | print 1/0
10 |
11 | print 'Hello, world!'
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter17/listing17-4.py:
--------------------------------------------------------------------------------
1 | def is_palindrome(text):
2 | n = len(text)
3 | for i in range(len(text)//2):
4 | if text[i] != text[n-i-1]:
5 | return False
6 | return True
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-10.py:
--------------------------------------------------------------------------------
1 | # find_sender.py
2 | import fileinput, re
3 | pat = re.compile('From: (.*) <.*?>$')
4 | for line in fileinput.input():
5 | m = pat.match(line)
6 | if m: print m.group(1)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter17/listing17-3.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | int is_palindrome(char *text) {
4 | int i, n=strlen(text);
5 | for (i=0; i<=n/2; ++i) {
6 | if (text[i] != text[n-i-1]) return 0;
7 | }
8 | return 1;
9 | }
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter19/listing19-1.cfg:
--------------------------------------------------------------------------------
1 | [numbers]
2 |
3 | pi: 3.1415926535897931
4 |
5 | [messages]
6 |
7 | greeting: Welcome to the area calculation program!
8 | question: Please enter the radius:
9 | result_message: The area is
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-6.py:
--------------------------------------------------------------------------------
1 | # numberlines.py
2 |
3 | import fileinput
4 |
5 | for line in fileinput.input(inplace=True):
6 | line = line.rstrip()
7 | num = fileinput.lineno()
8 | print '%-40s # %2i' % (line, num)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter18/listing18-1.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup
2 |
3 | setup(name='Hello',
4 | version='1.0',
5 | description='A simple example',
6 | author='Magnus Lie Hetland',
7 | py_modules=['hello'])
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter17/listing17-2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace FePyTest {
3 | public class IronPythonTest {
4 |
5 | public void greeting() {
6 | Console.WriteLine("Hello, world!");
7 | }
8 |
9 | }
10 | }
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter15/listing15-6.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import cgi
4 | form = cgi.FieldStorage()
5 |
6 | name = form.getvalue('name', 'world')
7 |
8 | print 'Content-type: text/plain'
9 | print
10 |
11 | print 'Hello, %s!' % name
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter26/listing26-3.sql:
--------------------------------------------------------------------------------
1 | create table messages (
2 | id integer primary key autoincrement,
3 | subject text not null,
4 | sender text not null,
5 | reply_to int,
6 | text text not null
7 | );
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter16/listing16-1.py:
--------------------------------------------------------------------------------
1 | from area import rect_area
2 | height = 3
3 | width = 4
4 | correct_answer = 12
5 | answer = rect_area(height, width)
6 | if answer == correct_answer:
7 | print 'Test passed '
8 | else:
9 | print 'Test failed '
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter26/listing26-1.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE messages (
2 | id SERIAL PRIMARY KEY,
3 | subject TEXT NOT NULL,
4 | sender TEXT NOT NULL,
5 | reply_to INTEGER REFERENCES messages,
6 | text TEXT NOT NULL
7 | );
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter12/listing12-3.py:
--------------------------------------------------------------------------------
1 | import wx
2 |
3 | app = wx.App()
4 | win = wx.Frame(None, title="Simple Editor")
5 |
6 | loadButton = wx.Button(win, label='Open')
7 |
8 | saveButton = wx.Button(win, label='Save')
9 |
10 | win.Show()
11 |
12 | app.MainLoop()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter15/listing15-1.py:
--------------------------------------------------------------------------------
1 | from urllib import urlopen
2 | import re
3 | p = re.compile('(.*?) ')
4 | text = urlopen('http://python.org/community/jobs').read()
5 | for url, name in p.findall(text):
6 | print '%s (%s)' % (name, url)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter26/listing26-2.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE messages (
2 | id INT NOT NULL AUTO_INCREMENT,
3 | subject VARCHAR(100) NOT NULL,
4 | sender VARCHAR(15) NOT NULL,
5 | reply_to INT,
6 | text MEDIUMTEXT NOT NULL,
7 | PRIMARY KEY(id)
8 | );
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter15/listing15-8.psp:
--------------------------------------------------------------------------------
1 | <%
2 | from random import choice
3 | adjectives = ['beautiful', 'cruel']
4 | %>
5 |
6 |
7 | Hello
8 |
9 |
10 | Hello, <%=choice(adjectives)%> world. My name is Mr. Gumby.
11 |
12 |
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter21/listing21-1.py:
--------------------------------------------------------------------------------
1 | from reportlab.graphics.shapes import Drawing, String
2 | from reportlab.graphics import renderPDF
3 |
4 | d = Drawing(100, 100)
5 | s = String(50, 50, 'Hello, world!', textAnchor='middle')
6 |
7 | d.add(s)
8 |
9 | renderPDF.drawToFile(d, 'hello.pdf', 'A simple PDF file')
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter19/listing19-3.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.INFO, filename='mylog.log')
4 |
5 | logging.info('Starting program')
6 |
7 | logging.info('Trying to divide 1 by 0')
8 |
9 | print 1 / 0
10 |
11 | logging.info('The division succeeded')
12 |
13 | logging.info('Ending program')
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter14/listing14-1.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | s = socket.socket()
4 |
5 | host = socket.gethostname()
6 | port = 1234
7 | s.bind((host, port))
8 |
9 | s.listen(5)
10 | while True:
11 | c, addr = s.accept()
12 | print 'Got connection from', addr
13 | c.send('Thank you for connecting')
14 | c.close()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-14.txt:
--------------------------------------------------------------------------------
1 | [import time]
2 | Dear [name],
3 |
4 | I would like to learn how to program. I hear you use
5 | the [language] language a lot -- is it something I
6 | should consider?
7 |
8 | And, by the way, is [email] your correct email address?
9 |
10 |
11 | Fooville, [time.asctime()]
12 |
13 | Oscar Frozzbozz
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter20/listing20-2.py:
--------------------------------------------------------------------------------
1 | def lines(file):
2 | for line in file: yield line
3 | yield '\n'
4 |
5 | def blocks(file):
6 | block = []
7 | for line in lines(file):
8 | if line.strip():
9 | block.append(line)
10 | elif block:
11 | yield ''.join(block).strip()
12 | block = []
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter02/listing2-4.py:
--------------------------------------------------------------------------------
1 | # Check a user name and PIN code
2 |
3 | database = [
4 | ['albert', '1234'],
5 | ['dilbert', '4242'],
6 | ['smith', '7524'],
7 | ['jones', '9843']
8 | ]
9 |
10 | username = raw_input('User name: ')
11 | pin = raw_input('PIN code: ')
12 |
13 | if [username, pin] in database: print 'Access granted'
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter13/listing13-2.py:
--------------------------------------------------------------------------------
1 | import sqlite3, sys
2 |
3 | conn = sqlite3.connect('food.db')
4 | curs = conn.cursor()
5 |
6 | query = 'SELECT * FROM food WHERE %s' % sys.argv[1]
7 | print query
8 | curs.execute(query)
9 | names = [f[0] for f in curs.description]
10 | for row in curs.fetchall():
11 | for pair in zip(names, row):
12 | print '%s: %s' % pair
13 | print
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter14/listing14-3.py:
--------------------------------------------------------------------------------
1 | from SocketServer import TCPServer, StreamRequestHandler
2 |
3 | class Handler(StreamRequestHandler):
4 |
5 | def handle(self):
6 | addr = self.request.getpeername()
7 | print 'Got connection from', addr
8 | self.wfile.write('Thank you for connecting')
9 |
10 | server = TCPServer(('', 1234), Handler)
11 | server.serve_forever()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter15/listing15-9.py:
--------------------------------------------------------------------------------
1 | from sha import sha
2 |
3 | __auth_realm__ = "A simple test"
4 |
5 | def __auth__(req, user, pswd):
6 | return user == "gumby" and sha(pswd).hexdigest() == \
7 | '17a15a277d43d3d9514ff731a7b5fa92dfd37aff'
8 |
9 | def __access__(req, user):
10 | return True
11 |
12 | def index(req, name="world"):
13 | return "Hello, %s!" % name
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-7.py:
--------------------------------------------------------------------------------
1 | # numberlines.py # 1
2 | # 2
3 | import fileinput # 3
4 | # 4
5 | for line in fileinput.input(inplace=1): # 5
6 | line = line.rstrip() # 6
7 | num = fileinput.lineno() # 7
8 | print '%-40s # %2i' % (line, num) # 8
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter24/listing24-2.py:
--------------------------------------------------------------------------------
1 | from asyncore import dispatcher
2 | import socket, asyncore
3 |
4 | class ChatServer(dispatcher):
5 |
6 | def handle_accept(self):
7 | conn, addr = self.accept()
8 | print 'Connection attempt from', addr[0]
9 |
10 | s = ChatServer()
11 | s.create_socket(socket.AF_INET, socket.SOCK_STREAM)
12 | s.bind(('', 5005))
13 | s.listen(5)
14 | asyncore.loop()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter14/listing14-4.py:
--------------------------------------------------------------------------------
1 | from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler
2 |
3 | class Server(ForkingMixIn, TCPServer): pass
4 |
5 | class Handler(StreamRequestHandler):
6 |
7 | def handle(self):
8 | addr = self.request.getpeername()
9 | print 'Got connection from', addr
10 | self.wfile.write('Thank you for connecting')
11 |
12 | server = Server(('', 1234), Handler)
13 | server.serve_forever()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter14/listing14-5.py:
--------------------------------------------------------------------------------
1 | from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler
2 |
3 | class Server(ThreadingMixIn, TCPServer): pass
4 |
5 | class Handler(StreamRequestHandler):
6 |
7 | def handle(self):
8 | addr = self.request.getpeername()
9 | print 'Got connection from', addr
10 | self.wfile.write('Thank you for connecting')
11 |
12 | server = Server(('', 1234), Handler)
13 | server.serve_forever()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter15/listing15-3.py:
--------------------------------------------------------------------------------
1 | from urllib import urlopen
2 | from BeautifulSoup import BeautifulSoup
3 |
4 | text = urlopen('http://python.org/community/jobs').read()
5 | soup = BeautifulSoup(text)
6 |
7 | jobs = set()
8 | for header in soup('h3'):
9 | links = header('a', 'reference')
10 | if not links: continue
11 | link = links[0]
12 | jobs.add('%s (%s)' % (link.string, link['href']))
13 |
14 | print '\n'.join(sorted(jobs, key=lambda s: s.lower()))
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter20/listing20-3.py:
--------------------------------------------------------------------------------
1 | import sys, re
2 | from util import *
3 |
4 | print '... '
5 |
6 | title = True
7 | for block in blocks(sys.stdin):
8 | block = re.sub(r'\*(.+?)\*', r'\1 ', block)
9 | if title:
10 | print ''
11 | print block
12 | print ' '
13 | title = False
14 | else:
15 | print ''
16 | print block
17 | print '
'
18 |
19 | print ''
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter15/listing15-7.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import cgi
4 | form = cgi.FieldStorage()
5 |
6 | name = form.getvalue('name', 'world')
7 |
8 | print """Content-type: text/html
9 |
10 |
11 |
12 | Greeting Page
13 |
14 |
15 | Hello, %s!
16 |
17 |
18 |
22 |
23 |
24 | """ % name
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter14/listing14-8.py:
--------------------------------------------------------------------------------
1 | from twisted.internet import reactor
2 | from twisted.internet.protocol import Protocol, Factory
3 |
4 | class SimpleLogger(Protocol):
5 |
6 | def connectionMade(self):
7 | print 'Got connection from', self.transport.client
8 |
9 | def connectionLost(self, reason):
10 | print self.transport.client, 'disconnected'
11 |
12 | def dataReceived(self, data):
13 | print data
14 |
15 | factory = Factory()
16 | factory.protocol = SimpleLogger
17 |
18 | reactor.listenTCP(1234, factory)
19 | reactor.run()
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Apress Source Code
2 |
3 | This repository accompanies [*Beginning Python*](http://www.apress.com/9781590599822) by Magnus Lie Hetland (Apress, 2008).
4 |
5 | 
6 |
7 | Download the files as a zip using the green button, or clone the repository to your machine using Git.
8 |
9 | ## Releases
10 |
11 | Release v1.0 corresponds to the code in the published book, without corrections or updates.
12 |
13 | ## Contributions
14 |
15 | See the file Contributing.md for more information on how you can contribute to this repository.
16 |
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter12/listing12-4.py:
--------------------------------------------------------------------------------
1 | import wx
2 |
3 | app = wx.App()
4 | win = wx.Frame(None, title="Simple Editor", size=(410, 335))
5 | win.Show()
6 |
7 | loadButton = wx.Button(win, label='Open',
8 | pos=(225, 5), size=(80, 25))
9 |
10 | saveButton = wx.Button(win, label='Save',
11 | pos=(315, 5), size=(80, 25))
12 |
13 | filename = wx.TextCtrl(win, pos=(5, 5), size=(210, 25))
14 |
15 | contents = wx.TextCtrl(win, pos=(5, 35), size=(390, 260),
16 | style=wx.TE_MULTILINE | wx.HSCROLL)
17 |
18 | app.MainLoop()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter25/listing25-1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import cgi
4 | form = cgi.FieldStorage()
5 |
6 |
7 | text = form.getvalue('text', open('simple_edit.dat').read())
8 | f = open('simple_edit.dat', 'w')
9 | f.write(text)
10 | f.close()
11 |
12 | print """Content-type: text/html
13 |
14 |
15 |
16 | A Simple Editor
17 |
18 |
19 |
23 |
24 |
25 | """ % text
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter14/listing14-9.py:
--------------------------------------------------------------------------------
1 | from twisted.internet import reactor
2 | from twisted.internet.protocol import Factory
3 | from twisted.protocols.basic import LineReceiver
4 |
5 |
6 | class SimpleLogger(LineReceiver):
7 |
8 | def connectionMade(self):
9 | print 'Got connection from', self.transport.client
10 |
11 | def connectionLost(self, reason):
12 | print self.transport.client, 'disconnected'
13 |
14 | def lineReceived(self, line):
15 | print line
16 |
17 | factory = Factory()
18 | factory.protocol = SimpleLogger
19 |
20 | reactor.listenTCP(1234, factory)
21 | reactor.run()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter24/listing24-3.py:
--------------------------------------------------------------------------------
1 | from asyncore import dispatcher
2 | import socket, asyncore
3 |
4 | PORT = 5005
5 |
6 | class ChatServer(dispatcher):
7 |
8 | def __init__(self, port):
9 | dispatcher.__init__(self)
10 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
11 | self.set_reuse_addr()
12 | self.bind(('', port))
13 | self.listen(5)
14 |
15 | def handle_accept(self):
16 | conn, addr = self.accept()
17 | print 'Connection attempt from', addr[0]
18 |
19 | if __name__ == '__main__':
20 | s = ChatServer(PORT)
21 | try: asyncore.loop()
22 | except KeyboardInterrupt: pass
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter16/listing16-2.py:
--------------------------------------------------------------------------------
1 | import unittest, my_math
2 |
3 | class ProductTestCase(unittest.TestCase):
4 |
5 | def testIntegers(self):
6 | for x in xrange(-10, 10):
7 | for y in xrange(-10, 10):
8 | p = my_math.product(x, y)
9 | self.failUnless(p == x*y, 'Integer multiplication failed')
10 |
11 | def testFloats(self):
12 | for x in xrange(-10, 10):
13 | for y in xrange(-10, 10):
14 | x = x/10.0
15 | y = y/10.0
16 | p = my_math.product(x, y)
17 | self.failUnless(p == x*y, 'Float multiplication failed')
18 |
19 | if __name__ == '__main__': unittest.main()
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing to Apress Source Code
2 |
3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers.
4 |
5 | ## How to Contribute
6 |
7 | 1. Make sure you have a GitHub account.
8 | 2. Fork the repository for the relevant book.
9 | 3. Create a new branch on which to make your change, e.g.
10 | `git checkout -b my_code_contribution`
11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted.
12 | 5. Submit a pull request.
13 |
14 | Thank you for your contribution!
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter19/listing19-2.py:
--------------------------------------------------------------------------------
1 | from ConfigParser import ConfigParser
2 |
3 | CONFIGFILE = "python.txt"
4 |
5 | config = ConfigParser()
6 | # Read the configuration file:
7 | config.read(CONFIGFILE)
8 |
9 | # Print out an initial greeting;
10 | # 'messages' is the section to look in:
11 | print config.get('messages', 'greeting')
12 |
13 | # Read in the radius, using a question from the config file:
14 | radius = input(config.get('messages', 'question') + ' ')
15 |
16 | # Print a result message from the config file;
17 | # end with a comma to stay on same line:
18 | print config.get('messages', 'result_message'),
19 |
20 | # getfloat() converts the config value to a float:
21 | print config.getfloat('numbers', 'pi') * radius**2
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter25/listing25-3.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | print 'Content-type: text/html\n'
4 |
5 | from os.path import join, abspath
6 | import cgi, sha, sys
7 |
8 | BASE_DIR = abspath('data')
9 |
10 | form = cgi.FieldStorage()
11 |
12 | text = form.getvalue('text')
13 | filename = form.getvalue('filename')
14 | password = form.getvalue('password')
15 |
16 | if not (filename and text and password):
17 | print 'Invalid parameters.'
18 | sys.exit()
19 |
20 | if sha.sha(password).hexdigest() != '8843d7f92416211de9ebb963ff4ce28125932878':
21 | print 'Invalid password'
22 | sys.exit()
23 |
24 | f = open(join(BASE_DIR,filename), 'w')
25 | f.write(text)
26 | f.close()
27 |
28 | print 'The file has been saved.'
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter03/listing3-1.py:
--------------------------------------------------------------------------------
1 | # Print a formatted price list with a given width
2 |
3 | width = input('Please enter width: ')
4 |
5 | price_width = 10
6 | item_width = width - price_width
7 |
8 | header_format = '%-*s%*s'
9 | format = '%-*s%*.2f'
10 |
11 | print '=' * width
12 |
13 | print header_format % (item_width, 'Item', price_width, 'Price')
14 |
15 | print '-' * width
16 |
17 | print format % (item_width, 'Apples', price_width, 0.4)
18 | print format % (item_width, 'Pears', price_width, 0.5)
19 | print format % (item_width, 'Cantaloupes', price_width, 1.92)
20 | print format % (item_width, 'Dried Apricots (16 oz.)', price_width, 8)
21 | print format % (item_width, 'Prunes (4 lbs.)', price_width, 12)
22 |
23 | print '=' * width
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter04/listing4-2.py:
--------------------------------------------------------------------------------
1 | # A simple database using get()
2 |
3 | # Insert database (people) from Listing 4-1 here.
4 |
5 | labels = {
6 | 'phone': 'phone number',
7 | 'addr': 'address'
8 | }
9 |
10 | name = raw_input('Name: ')
11 |
12 | # Are we looking for a phone number or an address?
13 | request = raw_input('Phone number (p) or address (a)? ')
14 |
15 | # Use the correct key:
16 | key = request # In case the request is neither 'p' nor 'a'
17 | if request == 'p': key = 'phone'
18 | if request == 'a': key = 'addr'
19 |
20 | # Use get to provide default values:
21 | person = people.get(name, {})
22 | label = labels.get(key, key)
23 | result = person.get(key, 'not available')
24 |
25 | print "%s's %s is %s." % (name, label, result)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter02/listing2-3.py:
--------------------------------------------------------------------------------
1 | # Prints a sentence in a centered "box" of correct width
2 |
3 | # Note that the integer division operator (//) only works in Python
4 | # 2.2 and newer. In earlier versions, simply use plain division (/)
5 |
6 |
7 | sentence = raw_input("Sentence: ")
8 |
9 | screen_width = 80
10 | text_width = len(sentence)
11 | box_width = text_width + 6
12 | left_margin = (screen_width - box_width) // 2
13 |
14 | print
15 | print ' ' * left_margin + '+' + '-' * (box_width-2) + '+'
16 | print ' ' * left_margin + '| ' + ' ' * text_width + ' |'
17 | print ' ' * left_margin + '| ' + sentence + ' |'
18 | print ' ' * left_margin + '| ' + ' ' * text_width + ' |'
19 | print ' ' * left_margin + '+' + '-' * (box_width-2) + '+'
20 | print
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter23/listing23-1.py:
--------------------------------------------------------------------------------
1 | from nntplib import NNTP
2 | from time import strftime, time, localtime
3 |
4 | day = 24 * 60 * 60 # Number of seconds in one day
5 |
6 | yesterday = localtime(time() - day)
7 | date = strftime('%y%m%d', yesterday)
8 | hour = strftime('%H%M%S', yesterday)
9 |
10 | servername = 'news.foo.bar'
11 | group = 'comp.lang.python.announce'
12 | server = NNTP(servername)
13 |
14 | ids = server.newnews(group, date, hour)[1]
15 |
16 |
17 | for id in ids:
18 | head = server.head(id)[3]
19 | for line in head:
20 | if line.lower().startswith('subject:'):
21 | subject = line[9:]
22 | break
23 |
24 | body = server.body(id)[3]
25 |
26 | print subject
27 | print '-'*len(subject)
28 | print '\n'.join(body)
29 |
30 | server.quit()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter14/listing14-6.py:
--------------------------------------------------------------------------------
1 | import socket, select
2 |
3 | s = socket.socket()
4 |
5 | host = socket.gethostname()
6 | port = 1234
7 | s.bind((host, port))
8 |
9 | s.listen(5)
10 | inputs = [s]
11 | while True:
12 | rs, ws, es = select.select(inputs, [], [])
13 | for r in rs:
14 | if r is s:
15 | c, addr = s.accept()
16 | print 'Got connection from', addr
17 | inputs.append(c)
18 | else:
19 | try:
20 | data = r.recv(1024)
21 | disconnected = not data
22 | except socket.error:
23 | disconnected = True
24 |
25 | if disconnected:
26 | print r.getpeername(), 'disconnected'
27 | inputs.remove(r)
28 | else:
29 | print data
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter12/listing12-5.py:
--------------------------------------------------------------------------------
1 | import wx
2 |
3 | app = wx.App()
4 | win = wx.Frame(None, title="Simple Editor", size=(410, 335))
5 |
6 |
7 | bkg = wx.Panel(win)
8 |
9 | loadButton = wx.Button(bkg, label='Open')
10 | saveButton = wx.Button(bkg, label='Save')
11 | filename = wx.TextCtrl(bkg)
12 | contents = wx.TextCtrl(bkg, style=wx.TE_MULTILINE | wx.HSCROLL)
13 |
14 | hbox = wx.BoxSizer()
15 | hbox.Add(filename, proportion=1, flag=wx.EXPAND)
16 | hbox.Add(loadButton, proportion=0, flag=wx.LEFT, border=5)
17 | hbox.Add(saveButton, proportion=0, flag=wx.LEFT, border=5)
18 |
19 | vbox = wx.BoxSizer(wx.VERTICAL)
20 | vbox.Add(hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
21 | vbox.Add(contents, proportion=1,
22 | flag=wx.EXPAND | wx.LEFT | wx.BOTTOM | wx.RIGHT, border=5)
23 |
24 | bkg.SetSizer(vbox)
25 | win.Show()
26 |
27 | app.MainLoop()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter14/listing14-7.py:
--------------------------------------------------------------------------------
1 | import socket, select
2 |
3 | s = socket.socket()
4 |
5 | host = socket.gethostname()
6 | port = 1234
7 | s.bind((host, port))
8 |
9 | fdmap = {s.fileno(): s}
10 |
11 |
12 | s.listen(5)
13 | p = select.poll()
14 | p.register(s)
15 | while True:
16 | events = p.poll()
17 | for fd, event in events:
18 | if fd in fdmap:
19 | c, addr = s.accept()
20 | print 'Got connection from', addr
21 | p.register(c)
22 | fdmap[c.fileno()] = c
23 | elif event & select.POLLIN:
24 | data = fdmap[fd].recv(1024)
25 | if not data: # No data -- connection closed
26 | print fdmap[fd].getpeername(), 'disconnected'
27 | p.unregister(fd)
28 | del fdmap[fd]
29 | else:
30 | print data
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter13/listing13-1.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | def convert(value):
4 | if value.startswith('~'):
5 | return value.strip('~')
6 | if not value:
7 | value = '0'
8 | return float(value)
9 |
10 | conn = sqlite3.connect('food.db')
11 | curs = conn.cursor()
12 |
13 | curs.execute('''
14 | CREATE TABLE food (
15 | id TEXT PRIMARY KEY,
16 | desc TEXT,
17 | water FLOAT,
18 | kcal FLOAT,
19 | protein FLOAT,
20 | fat FLOAT,
21 | ash FLOAT,
22 | carbs FLOAT,
23 | fiber FLOAT,
24 | sugar FLOAT
25 | )
26 | ''')
27 |
28 | query = 'INSERT INTO food VALUES (?,?,?,?,?,?,?,?,?,?)'
29 |
30 | for line in open('ABBREV.txt'):
31 | fields = line.split('^')
32 | vals = [convert(f) for f in fields[:field_count]]
33 | curs.execute(query, vals)
34 |
35 | conn.commit()
36 | conn.close()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter02/listing2-1.py:
--------------------------------------------------------------------------------
1 | # Print out a date, given year, month, and day as numbers
2 |
3 | months = [
4 | 'January',
5 | 'February',
6 | 'March',
7 | 'April',
8 | 'May',
9 | 'June',
10 | 'July',
11 | 'August',
12 | 'September',
13 | 'October',
14 | 'November',
15 | 'December'
16 | ]
17 |
18 | # A list with one ending for each number from 1 to 31
19 | endings = ['st', 'nd', 'rd'] + 17 * ['th'] \
20 | + ['st', 'nd', 'rd'] + 7 * ['th'] \
21 | + ['st']
22 |
23 | year = raw_input('Year: ')
24 | month = raw_input('Month (1-12): ')
25 | day = raw_input('Day (1-31): ')
26 |
27 | month_number = int(month)
28 | day_number = int(day)
29 |
30 | # Remember to subtract 1 from month and day to get a correct index
31 | month_name = months[month_number-1]
32 | ordinal = day + endings[day_number-1]
33 |
34 | print month_name + ' ' + ordinal + ', ' + year
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-11.py:
--------------------------------------------------------------------------------
1 | # templates.py
2 |
3 | import fileinput, re
4 |
5 | # Matches fields enclosed in square brackets:
6 | field_pat = re.compile(r'\[(.+?)\]')
7 |
8 |
9 | # We'll collect variables in this:
10 | scope = {}
11 |
12 | # This is used in re.sub:
13 | def replacement(match):
14 | code = match.group(1)
15 | try:
16 | # If the field can be evaluated, return it:
17 | return str(eval(code, scope))
18 | except SyntaxError:
19 | # Otherwise, execute the assignment in the same scope...
20 | exec code in scope
21 | # ...and return an empty string:
22 | return ''
23 |
24 | # Get all the text as a single string:
25 | # (There are other ways of doing this; see Chapter 11)
26 | lines = []
27 | for line in fileinput.input():
28 | lines.append(line)
29 | text = ''.join(lines)
30 |
31 | # Substitute all the occurrences of the field pattern:
32 | print field_pat.sub(replacement, text)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter22/listing22-1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Welcome to My Home Page
4 |
5 | Hi, there. My name is Mr. Gumby, and this is my home page. Here
6 | are some of my interests:
7 |
8 |
13 |
14 |
15 |
16 | Mr. Gumby's Shouting Page
17 |
18 | ...
19 |
20 |
21 | Mr. Gumby's Sleeping Page
22 |
23 | ...
24 |
25 |
26 | Mr. Gumby's Eating Page
27 |
28 | ...
29 |
30 |
31 |
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter25/listing25-2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | print 'Content-type: text/html\n'
4 |
5 | from os.path import join, abspath
6 | import cgi, sys
7 |
8 | BASE_DIR = abspath('data')
9 |
10 | form = cgi.FieldStorage()
11 | filename = form.getvalue('filename')
12 | if not filename:
13 | print 'Please enter a file name'
14 | sys.exit()
15 | text = open(join(BASE_DIR, filename)).read()
16 |
17 | print """
18 |
19 |
20 | Editing...
21 |
22 |
23 |
24 | File: %s
25 |
26 | Password:
27 |
28 | Text:
29 | %s
30 |
31 |
32 |
33 |
34 | """ % (filename, filename, text)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter15/listing15-2.py:
--------------------------------------------------------------------------------
1 | from urllib import urlopen
2 | from HTMLParser import HTMLParser
3 |
4 | class Scraper(HTMLParser):
5 |
6 | in_h3 = False
7 | in_link = False
8 |
9 | def handle_starttag(self, tag, attrs):
10 | attrs = dict(attrs)
11 | if tag == 'h3':
12 | self.in_h3 = True
13 |
14 | if tag == 'a' and 'href' in attrs:
15 | self.in_link = True
16 | self.chunks = []
17 | self.url = attrs['href']
18 |
19 | def handle_data(self, data):
20 | if self.in_link:
21 | self.chunks.append(data)
22 |
23 | def handle_endtag(self, tag):
24 | if tag == 'h3':
25 | self.in_h3 = False
26 | if tag == 'a':
27 | if self.in_h3 and self.in_link:
28 | print '%s (%s)' % (''.join(self.chunks), self.url)
29 | self.in_link = False
30 |
31 | text = urlopen('http://python.org/community/jobs').read()
32 | parser = Scraper()
33 | parser.feed(text)
34 | parser.close()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter26/listing26-6.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | print 'Content-type: text/html\n'
4 |
5 | import cgitb; cgitb.enable()
6 |
7 | import psycopg
8 | conn = psycopg.connect('dbname=foo user=bar')
9 | curs = conn.cursor()
10 |
11 | import cgi, sys
12 | form = cgi.FieldStorage()
13 | id = form.getvalue('id')
14 |
15 | print """
16 |
17 |
18 | View Message
19 |
20 |
21 | View Message
22 | """
23 |
24 | try: id = int(id)
25 | except:
26 | print 'Invalid message ID'
27 | sys.exit()
28 |
29 | curs.execute('SELECT * FROM messages WHERE id = %i' % id)
30 | rows = curs.dictfetchall()
31 |
32 | if not rows:
33 | print 'Unknown message ID'
34 | sys.exit()
35 |
36 | row = rows[0]
37 |
38 | print """
39 | Subject: %(subject)s
40 | Sender: %(sender)s
41 |
%(text)s
42 |
43 |
44 | Back to the main page
45 | | Reply
46 |
47 |
48 | """ % row
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter26/listing26-4.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | print 'Content-type: text/html\n'
4 |
5 | import cgitb; cgitb.enable()
6 |
7 | import psycopg
8 | conn = psycopg.connect('dbname=foo user=bar')
9 | curs = conn.cursor()
10 |
11 | print """
12 |
13 |
14 | The FooBar Bulletin Board
15 |
16 |
17 | The FooBar Bulletin Board
18 | """
19 |
20 |
21 | curs.execute('SELECT * FROM messages')
22 | rows = curs.dictfetchall()
23 |
24 | toplevel = []
25 | children = {}
26 |
27 | for row in rows:
28 | parent_id = row['reply_to']
29 | if parent_id is None:
30 | toplevel.append(row)
31 | else:
32 | children.setdefault(parent_id,[]).append(row)
33 |
34 | def format(row):
35 | print row['subject']
36 | try: kids = children[row['id']]
37 | except KeyError: pass
38 | else:
39 | print ''
40 | for kid in kids:
41 | format(kid)
42 | print ' '
43 |
44 | print ''
45 |
46 | for row in toplevel:
47 | format(row)
48 |
49 | print """
50 |
51 |
52 |
53 | """
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter17/listing17-6.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | static PyObject *is_palindrome(PyObject *self, PyObject *args) {
4 | int i, n;
5 | const char *text;
6 | int result;
7 | /* "s" means a single string: */
8 | if (!PyArg_ParseTuple(args, "s", &text)) {
9 | return NULL;
10 | }
11 | /* The old code, more or less: */
12 | n=strlen(text);
13 | result = 1;
14 | for (i=0; i<=n/2; ++i) {
15 | if (text[i] != text[n-i-1]) {
16 | result = 0;
17 | break;
18 | }
19 | }
20 | /* "i" means a single integer: */
21 | return Py_BuildValue("i", result);
22 | }
23 |
24 | /* A listing of our methods/functions: */
25 | static PyMethodDef PalindromeMethods[] = {
26 | /* name, function, argument type, docstring */
27 | {"is_palindrome", is_palindrome, METH_VARARGS, "Detect palindromes"},
28 | /* An end-of-listing sentinel: */
29 | {NULL, NULL, 0, NULL}
30 | };
31 |
32 |
33 | /* An initialization function for the module (the name is
34 | significant): */
35 | PyMODINIT_FUNC initpalindrome() {
36 | Py_InitModule("palindrome", PalindromeMethods);
37 | }
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter16/listing16-3.py:
--------------------------------------------------------------------------------
1 | import unittest, my_math
2 | from subprocess import Popen, PIPE
3 |
4 | class ProductTestCase(unittest.TestCase):
5 |
6 | def testIntegers(self):
7 | for x in xrange(-10, 10):
8 | for y in xrange(-10, 10):
9 | p = my_math.product(x, y)
10 | self.failUnless(p == x*y, 'Integer multiplication failed')
11 |
12 | def testFloats(self):
13 | for x in xrange(-10, 10):
14 | for y in xrange(-10, 10):
15 | x = x/10.0
16 | y = y/10.0
17 | p = my_math.product(x, y)
18 | self.failUnless(p == x*y, 'Float multiplication failed')
19 |
20 | def testWithPyChecker(self):
21 | cmd = 'pychecker', '-Q', my_math.__file__.rstrip('c')
22 | pychecker = Popen(cmd, stdout=PIPE, stderr=PIPE)
23 | self.assertEqual(pychecker.stdout.read(), '')
24 |
25 | def testWithPyLint(self):
26 | cmd = 'pylint', '-rn', 'my_math'
27 | pylint = Popen(cmd, stdout=PIPE, stderr=PIPE)
28 | self.assertEqual(pylint.stdout.read(), '')
29 |
30 | if __name__ == '__main__': unittest.main()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter22/listing22-2.py:
--------------------------------------------------------------------------------
1 | from xml.sax.handler import ContentHandler
2 | from xml.sax import parse
3 |
4 | class PageMaker(ContentHandler):
5 | passthrough = False
6 | def startElement(self, name, attrs):
7 | if name == 'page':
8 | self.passthrough = True
9 | self.out = open(attrs['name'] + '.html', 'w')
10 | self.out.write('\n')
11 | self.out.write('%s \n' % attrs['title'])
12 | self.out.write('\n')
13 | elif self.passthrough:
14 | self.out.write('<' + name)
15 | for key, val in attrs.items():
16 | self.out.write(' %s="%s"' % (key, val))
17 | self.out.write('>')
18 |
19 |
20 | def endElement(self, name):
21 | if name == 'page':
22 | self.passthrough = False
23 | self.out.write('\n\n')
24 | self.out.close()
25 | elif self.passthrough:
26 | self.out.write('%s>' % name)
27 | def characters(self, chars):
28 | if self.passthrough: self.out.write(chars)
29 |
30 | parse('website.xml', PageMaker ())
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter24/listing24-4.py:
--------------------------------------------------------------------------------
1 | from asyncore import dispatcher
2 | from asynchat import async_chat
3 | import socket, asyncore
4 |
5 | PORT = 5005
6 |
7 | class ChatSession(async_chat):
8 |
9 | def __init__(self, sock):
10 | async_chat.__init__(self, sock)
11 | self.set_terminator("\r\n")
12 | self.data = []
13 |
14 | def collect_incoming_data(self, data):
15 | self.data.append(data)
16 |
17 | def found_terminator(self):
18 | line = ''.join(self.data)
19 | self.data = []
20 | # Do something with the line...
21 | print line
22 |
23 | class ChatServer(dispatcher):
24 |
25 | def __init__(self, port):
26 | dispatcher.__init__(self)
27 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
28 | self.set_reuse_addr()
29 | self.bind(('', port))
30 | self.listen(5)
31 | self.sessions = []
32 |
33 |
34 | def handle_accept(self):
35 | conn, addr = self.accept()
36 | self.sessions.append(ChatSession(conn))
37 |
38 | if __name__ == '__main__':
39 | s = ChatServer(PORT)
40 | try: asyncore.loop()
41 | except KeyboardInterrupt: print
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter12/listing12-6.py:
--------------------------------------------------------------------------------
1 | import wx
2 | def load(event):
3 | file = open(filename.GetValue())
4 | contents.SetValue(file.read())
5 | file.close()
6 |
7 | def save(event):
8 | file = open(filename.GetValue(), 'w')
9 | file.write(contents.GetValue())
10 | file.close()
11 |
12 | app = wx.App()
13 | win = wx.Frame(None, title="Simple Editor", size=(410, 335))
14 |
15 | bkg = wx.Panel(win)
16 |
17 | loadButton = wx.Button(bkg, label='Open')
18 | loadButton.Bind(wx.EVT_BUTTON, load)
19 |
20 | saveButton = wx.Button(bkg, label='Save')
21 | saveButton.Bind(wx.EVT_BUTTON, save)
22 |
23 | filename = wx.TextCtrl(bkg)
24 | contents = wx.TextCtrl(bkg, style=wx.TE_MULTILINE | wx.HSCROLL)
25 |
26 | hbox = wx.BoxSizer()
27 | hbox.Add(filename, proportion=1, flag=wx.EXPAND)
28 | hbox.Add(loadButton, proportion=0, flag=wx.LEFT, border=5)
29 | hbox.Add(saveButton, proportion=0, flag=wx.LEFT, border=5)
30 |
31 | vbox = wx.BoxSizer(wx.VERTICAL)
32 | vbox.Add(hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
33 | vbox.Add(contents, proportion=1,
34 | flag=wx.EXPAND | wx.LEFT | wx.BOTTOM | wx.RIGHT, border=5)
35 |
36 | bkg.SetSizer(vbox)
37 | win.Show()
38 |
39 | app.MainLoop()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter21/listing21-3.py:
--------------------------------------------------------------------------------
1 | from urllib import urlopen
2 | from reportlab.graphics.shapes import *
3 | from reportlab.graphics.charts.lineplots import LinePlot
4 | from reportlab.graphics.charts.textlabels import Label
5 | from reportlab.graphics import renderPDF
6 |
7 | URL = 'http://www.swpc.noaa.gov/ftpdir/weekly/Predict.txt'
8 | COMMENT_CHARS = '#:'
9 |
10 |
11 | drawing = Drawing(400, 200)
12 | data = []
13 | for line in urlopen(URL).readlines():
14 | if not line.isspace() and not line[0] in COMMENT_CHARS:
15 | data.append([float(n) for n in line.split()])
16 |
17 | pred = [row[2] for row in data]
18 | high = [row[3] for row in data]
19 | low = [row[4] for row in data]
20 | times = [row[0] + row[1]/12.0 for row in data]
21 |
22 | lp = LinePlot()
23 | lp.x = 50
24 | lp.y = 50
25 | lp.height = 125
26 | lp.width = 300
27 | lp.data = [zip(times, pred), zip(times, high), zip(times, low)]
28 | lp.lines[0].strokeColor = colors.blue
29 | lp.lines[1].strokeColor = colors.red
30 | lp.lines[2].strokeColor = colors.green
31 |
32 | drawing.add(lp)
33 |
34 | drawing.add(String(250, 150, 'Sunspots',
35 | fontSize=14, fillColor=colors.red))
36 |
37 |
38 | renderPDF.drawToFile(drawing, 'report2.pdf', 'Sunspots')
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter20/listing20-1.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Welcome to World Wide Spam, Inc.
5 |
6 |
7 | These are the corporate web pages of *World Wide Spam*, Inc. We hope
8 | you find your stay enjoyable, and that you will sample many of our
9 | products.
10 |
11 | A short history of the company
12 |
13 | World Wide Spam was started in the summer of 2000. The business
14 | concept was to ride the dot-com wave and to make money both through
15 | bulk email and by selling canned meat online.
16 |
17 | After receiving several complaints from customers who weren't
18 | satisfied by their bulk email, World Wide Spam altered their profile,
19 | and focused 100% on canned goods. Today, they rank as the world's
20 | 13,892nd online supplier of SPAM.
21 |
22 | Destinations
23 |
24 | From this page you may visit several of our interesting web pages:
25 |
26 | - What is SPAM? (http://wwspam.fu/whatisspam)
27 |
28 | - How do they make it? (http://wwspam.fu/howtomakeit)
29 |
30 | - Why should I eat it? (http://wwspam.fu/whyeatit)
31 |
32 | How to get in touch with us
33 |
34 | You can get in touch with us in *many* ways: By phone (555-1234), by
35 | email (wwspam@wwspam.fu) or by visiting our customer feedback page
36 | (http://wwspam.fu/feedback).
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter04/listing4-1.py:
--------------------------------------------------------------------------------
1 | # A simple database
2 |
3 | # A dictionary with person names as keys. Each person is represented as
4 | # another dictionary with the keys 'phone' and 'addr' referring to their phone
5 | # number and address, respectively.
6 |
7 | people = {
8 |
9 | 'Alice': {
10 | 'phone': '2341',
11 | 'addr': 'Foo drive 23'
12 | },
13 |
14 | 'Beth': {
15 | 'phone': '9102',
16 | 'addr': 'Bar street 42'
17 | },
18 |
19 | 'Cecil': {
20 | 'phone': '3158',
21 | 'addr': 'Baz avenue 90'
22 | }
23 |
24 | }
25 |
26 | # Descriptive labels for the phone number and address. These will be used
27 | # when printing the output.
28 | labels = {
29 | 'phone': 'phone number',
30 | 'addr': 'address'
31 | }
32 |
33 | name = raw_input('Name: ')
34 |
35 | # Are we looking for a phone number or an address?
36 | request = raw_input('Phone number (p) or address (a)? ')
37 |
38 | # Use the correct key:
39 | if request == 'p': key = 'phone'
40 | if request == 'a': key = 'addr'
41 |
42 | # Only try to print information if the name is a valid key in
43 | # our dictionary:
44 | if name in people: print "%s's %s is %s." % \
45 | (name, labels[key], people[name][key])
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter26/listing26-5.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | print 'Content-type: text/html\n'
4 |
5 | import cgitb; cgitb.enable()
6 |
7 | import psycopg
8 | conn = psycopg.connect('dbname=foo user=bar')
9 | curs = conn.cursor()
10 |
11 | print """
12 |
13 |
14 | The FooBar Bulletin Board
15 |
16 |
17 | The FooBar Bulletin Board
18 | """
19 |
20 | curs.execute('SELECT * FROM messages')
21 | rows = curs.dictfetchall()
22 |
23 | toplevel = []
24 | children = {}
25 |
26 | for row in rows:
27 | parent_id = row['reply_to']
28 | if parent_id is None:
29 | toplevel.append(row)
30 | else:
31 | children.setdefault(parent_id,[]).append(row)
32 |
33 | def format(row):
34 | print '%(subject)s
' % row
35 | try: kids = children[row['id']]
36 | except KeyError: pass
37 | else:
38 | print ''
39 | for kid in kids:
40 | format(kid)
41 | print ' '
42 |
43 | print ''
44 |
45 | for row in toplevel:
46 | format(row)
47 |
48 | print """
49 |
50 |
51 | Post message
52 |
53 |
54 | """
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter21/listing21-2.py:
--------------------------------------------------------------------------------
1 | from reportlab.lib import colors
2 | from reportlab.graphics.shapes import *
3 | from reportlab.graphics import renderPDF
4 |
5 | data = [
6 | # Year Month Predicted High Low
7 | (2007, 8, 113.2, 114.2, 112.2),
8 | (2007, 9, 112.8, 115.8, 109.8),
9 | (2007, 10, 111.0, 116.0, 106.0),
10 | (2007, 11, 109.8, 116.8, 102.8),
11 | (2007, 12, 107.3, 115.3, 99.3),
12 | (2008, 1, 105.2, 114.2, 96.2),
13 | (2008, 2, 104.1, 114.1, 94.1),
14 | (2008, 3, 99.9, 110.9, 88.9),
15 | (2008, 4, 94.8, 106.8, 82.8),
16 | (2008, 5, 91.2, 104.2, 78.2),
17 | ]
18 |
19 | drawing = Drawing(200, 150)
20 |
21 | pred = [row[2]-40 for row in data]
22 | high = [row[3]-40 for row in data]
23 | low = [row[4]-40 for row in data]
24 | times = [200*((row[0] + row[1]/12.0) - 2007)-110 for row in data]
25 |
26 | drawing.add(PolyLine(zip(times, pred), strokeColor=colors.blue))
27 | drawing.add(PolyLine(zip(times, high), strokeColor=colors.red))
28 | drawing.add(PolyLine(zip(times, low), strokeColor=colors.green))
29 |
30 | drawing.add(String(65, 115, 'Sunspots', fontSize=18, fillColor=colors.red))
31 |
32 | renderPDF.drawToFile(drawing, 'report1.pdf', 'Sunspots')
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter26/listing26-7.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | print 'Content-type: text/html\n'
4 |
5 | import cgitb; cgitb.enable()
6 |
7 |
8 | import psycopg
9 | conn = psycopg.connect('dbname=foo user=bar')
10 | curs = conn.cursor()
11 |
12 | import cgi, sys
13 | form = cgi.FieldStorage()
14 | reply_to = form.getvalue('reply_to')
15 |
16 | print """
17 |
18 |
19 | Compose Message
20 |
21 |
22 | Compose Message
23 |
24 |
25 | """
26 |
27 | subject = ''
28 | if reply_to is not None:
29 | print ' ' % reply_to
30 | curs.execute('SELECT subject FROM messages WHERE id = %s' % reply_to)
31 | subject = curs.fetchone()[0]
32 | if not subject.startswith('Re: '):
33 | subject = 'Re: ' + subject
34 |
35 | print """
36 | Subject:
37 |
38 | Sender:
39 |
40 | Message:
41 |
42 |
43 |
44 |
45 | Back to the main page '
46 |
47 |
48 | """ % subject
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter26/listing26-8.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | print 'Content-type: text/html\n'
4 |
5 | import cgitb; cgitb.enable()
6 |
7 | def quote(string):
8 | if string:
9 | return string.replace("'", "\\'")
10 | else:
11 | return string
12 |
13 | import psycopg
14 | conn = psycopg.connect('dbname=foo user=bar')
15 | curs = conn.cursor()
16 |
17 | import cgi, sys
18 | form = cgi.FieldStorage()
19 |
20 | sender = quote(form.getvalue('sender'))
21 | subject = quote(form.getvalue('subject'))
22 | text = quote(form.getvalue('text'))
23 | reply_to = form.getvalue('reply_to')
24 |
25 | if not (sender and subject and text):
26 | print 'Please supply sender, subject, and text'
27 | sys.exit()
28 |
29 | if reply_to is not None:
30 | query = """
31 | INSERT INTO messages(reply_to, sender, subject, text)
32 | VALUES(%i, '%s', '%s', '%s')""" % (int(reply_to), sender, subject, text)
33 | else:
34 | query = """
35 | INSERT INTO messages(sender, subject, text)
36 | VALUES('%s', '%s', '%s')""" % (sender, subject, text)
37 |
38 | curs.execute(query)
39 | conn.commit()
40 |
41 |
42 | print """
43 |
44 |
45 | Message Saved
46 |
47 |
48 | Message Saved
49 |
50 | Back to the main page
51 |
52 | s
53 | """
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter20/listing20-6.py:
--------------------------------------------------------------------------------
1 | import sys, re
2 | from handlers import *
3 | from util import *
4 | from rules import *
5 |
6 | class Parser:
7 | """
8 | A Parser reads a text file, applying rules and controlling a
9 | handler.
10 | """
11 | def __init__(self, handler):
12 | self.handler = handler
13 | self.rules = []
14 | self.filters = []
15 | def addRule(self, rule):
16 | self.rules.append(rule)
17 | def addFilter(self, pattern, name):
18 | def filter(block, handler):
19 | return re.sub(pattern, handler.sub(name), block)
20 | self.filters.append(filter)
21 | def parse(self, file):
22 | self.handler.start('document')
23 | for block in blocks(file):
24 | for filter in self.filters:
25 | block = filter(block, self.handler)
26 | for rule in self.rules:
27 | if rule.condition(block):
28 | last = rule.action(block, self.handler)
29 | if last: break
30 | self.handler.end('document')
31 |
32 | class BasicTextParser(Parser):
33 | """
34 | A specific Parser that adds rules and filters in its
35 | constructor.
36 | """
37 | def __init__(self, handler):
38 | Parser.__init__(self, handler)
39 | self.addRule(ListRule())
40 | self.addRule(ListItemRule())
41 | self.addRule(TitleRule())
42 | self.addRule(HeadingRule())
43 | self.addRule(ParagraphRule())
44 |
45 | self.addFilter(r'\*(.+?)\*', 'emphasis')
46 | self.addFilter(r'(http://[\.a-zA-Z/]+)', 'url')
47 | self.addFilter(r'([\.a-zA-Z]+@[\.a-zA-Z]+[a-zA-Z]+)', 'mail')
48 |
49 | handler = HTMLRenderer()
50 | parser = BasicTextParser(handler)
51 |
52 | parser.parse(sys.stdin)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter10/listing10-8.py:
--------------------------------------------------------------------------------
1 | # database.py
2 | import sys, shelve
3 |
4 | def store_person(db):
5 | """
6 | Query user for data and store it in the shelf object
7 | """
8 | pid = raw_input('Enter unique ID number: ')
9 | person = {}
10 | person['name'] = raw_input('Enter name: ')
11 | person['age'] = raw_input('Enter age: ')
12 | person['phone'] = raw_input('Enter phone number: ')
13 |
14 | db[pid] = person
15 |
16 | def lookup_person(db):
17 | """
18 | Query user for ID and desired field, and fetch the corresponding data from
19 | the shelf object
20 | """
21 | pid = raw_input('Enter ID number: ')
22 | field = raw_input('What would you like to know? (name, age, phone) ')
23 | field = field.strip().lower()
24 | print field.capitalize() + ':', \
25 | db[pid][field]
26 |
27 | def print_help():
28 | print 'The available commands are:'
29 | print 'store : Stores information about a person'
30 | print 'lookup : Looks up a person from ID number'
31 | print 'quit : Save changes and exit'
32 | print '? : Prints this message'
33 |
34 | def enter_command():
35 | cmd = raw_input('Enter command (? for help): ')
36 | cmd = cmd.strip().lower()
37 | return cmd
38 |
39 | def main():
40 | database = shelve.open('C:\\database.dat') # You may want to change this name
41 | try:
42 | while True:
43 | cmd = enter_command()
44 | if cmd == 'store':
45 | store_person(database)
46 | elif cmd == 'lookup':
47 | lookup_person(database)
48 | elif cmd == '?':
49 | print_help()
50 | elif cmd == 'quit':
51 | return
52 | finally:
53 | database.close()
54 |
55 | if __name__ == '__main__': main()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter27/listing27-3.py:
--------------------------------------------------------------------------------
1 | from xmlrpclib import ServerProxy, Fault
2 | from cmd import Cmd
3 | from random import choice
4 | from string import lowercase
5 | from server import Node, UNHANDLED
6 | from threading import Thread
7 | from time import sleep
8 | import sys
9 |
10 | HEAD_START = 0.1 # Seconds
11 | SECRET_LENGTH = 100
12 |
13 | def randomString(length):
14 | """
15 | Returns a random string of letters with the given length.
16 | """
17 | chars = []
18 | letters = lowercase[:26]
19 | while length > 0:
20 | length -= 1
21 | chars.append(choice(letters))
22 | return ''.join(chars)
23 |
24 | class Client(Cmd):
25 | """
26 | A simple text-based interface to the Node class.
27 | """
28 |
29 | prompt = '> '
30 |
31 |
32 | def __init__(self, url, dirname, urlfile):
33 | """
34 | Sets the url, dirname, and urlfile, and starts the Node
35 | Server in a separate thread.
36 | """
37 | Cmd.__init__(self)
38 | self.secret = randomString(SECRET_LENGTH)
39 | n = Node(url, dirname, self.secret)
40 | t = Thread(target=n._start)
41 | t.setDaemon(1)
42 | t.start()
43 | # Give the server a head start:
44 | sleep(HEAD_START)
45 | self.server = ServerProxy(url)
46 | for line in open(urlfile):
47 | line = line.strip()
48 | self.server.hello(line)
49 |
50 | def do_fetch(self, arg):
51 | "Call the fetch method of the Server."
52 | try:
53 | self.server.fetch(arg, self.secret)
54 | except Fault, f:
55 | if f.faultCode != UNHANDLED: raise
56 | print "Couldn't find the file", arg
57 |
58 | def do_exit(self, arg):
59 | "Exit the program."
60 | print
61 | sys.exit()
62 |
63 | do_EOF = do_exit # End-Of-File is synonymous with 'exit'
64 |
65 | def main():
66 | urlfile, directory, url = sys.argv[1:]
67 | client = Client(url, directory, urlfile)
68 | client.cmdloop()
69 |
70 | if __name__ == '__main__': main()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter29/listing29-1.py:
--------------------------------------------------------------------------------
1 | import sys, pygame
2 | from pygame.locals import *
3 | from random import randrange
4 |
5 | class Weight(pygame.sprite.Sprite):
6 |
7 | def __init__(self):
8 | pygame.sprite.Sprite.__init__(self)
9 | # image and rect used when drawing sprite:
10 | self.image = weight_image
11 | self.rect = self.image.get_rect()
12 | self.reset()
13 |
14 | def reset(self):
15 | """
16 | Move the weight to a random position at the top of the screen.
17 | """
18 | self.rect.top = -self.rect.height
19 | self.rect.centerx = randrange(screen_size[0])
20 |
21 | def update(self):
22 | """
23 | Update the weight for display in the next frame.
24 | """
25 | self.rect.top += 1
26 |
27 | if self.rect.top > screen_size[1]:
28 | self.reset()
29 |
30 | # Initialize things
31 | pygame.init()
32 | screen_size = 800, 600
33 | pygame.display.set_mode(screen_size, FULLSCREEN)
34 | pygame.mouse.set_visible(0)
35 |
36 | # Load the weight image
37 | weight_image = pygame.image.load('weight.png')
38 | weight_image = weight_image.convert() # ...to match the display
39 |
40 | # Create a sprite group and add a Weight
41 | sprites = pygame.sprite.RenderUpdates()
42 | sprites.add(Weight())
43 |
44 | # Get the screen surface and fill it
45 | screen = pygame.display.get_surface()
46 | bg = (255, 255, 255) # White
47 | screen.fill(bg)
48 | pygame.display.flip()
49 |
50 | # Used to erase the sprites:
51 | def clear_callback(surf, rect):
52 | surf.fill(bg, rect)
53 |
54 | while True:
55 | # Check for quit events:
56 | for event in pygame.event.get():
57 | if event.type == QUIT:
58 | sys.exit()
59 | if event.type == KEYDOWN and event.key == K_ESCAPE:
60 | sys.exit()
61 | # Erase previous positions:
62 | sprites.clear(screen, clear_callback)
63 | # Update all sprites:
64 | sprites.update()
65 | # Draw all sprites:
66 | updates = sprites.draw(screen)
67 | # Update the necessary parts of the display:
68 | pygame.display.update(updates)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter24/listing24-5.py:
--------------------------------------------------------------------------------
1 | from asyncore import dispatcher
2 | from asynchat import async_chat
3 | import socket, asyncore
4 |
5 | PORT = 5005
6 | NAME = 'TestChat'
7 |
8 | class ChatSession(async_chat):
9 | """
10 | A class that takes care of a connection between the server
11 | and a single user.
12 | """
13 | def __init__(self, server, sock):
14 | # Standard setup tasks:
15 | async_chat.__init__(self, sock)
16 | self.server = server
17 | self.set_terminator("\r\n")
18 | self.data = []
19 | # Greet the user:
20 | self.push('Welcome to %s\r\n' % self.server.name)
21 |
22 | def collect_incoming_data(self, data):
23 | self.data.append(data)
24 |
25 | def found_terminator(self):
26 | """
27 | If a terminator is found, that means that a full
28 | line has been read. Broadcast it to everyone.
29 | """
30 | line = ''.join(self.data)
31 | self.data = []
32 | self.server.broadcast(line)
33 |
34 | def handle_close(self):
35 | async_chat.handle_close(self)
36 | self.server.disconnect(self)
37 |
38 | class ChatServer(dispatcher):
39 | """
40 | A class that receives connections and spawns individual
41 | sessions. It also handles broadcasts to these sessions.
42 | """
43 | def __init__(self, port, name):
44 | # Standard setup tasks
45 | dispatcher.__init__(self)
46 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
47 | self.set_reuse_addr()
48 | self.bind(('', port))
49 | self.listen(5)
50 | self.name = name
51 | self.sessions = []
52 |
53 | def disconnect(self, session):
54 | self.sessions.remove(session)
55 |
56 | def broadcast(self, line):
57 | for session in self.sessions:
58 | session.push(line + '\r\n')
59 |
60 | def handle_accept(self):
61 | conn, addr = self.accept()
62 | self.sessions.append(ChatSession(self, conn))
63 |
64 | if __name__ == '__main__':
65 | s = ChatServer(PORT, NAME)
66 | try: asyncore.loop()
67 | except KeyboardInterrupt: print
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter20/listing20-5.py:
--------------------------------------------------------------------------------
1 | class Rule:
2 | """
3 | Base class for all rules.
4 | """
5 | def action(self, block, handler):
6 | handler.start(self.type)
7 | handler.feed(block)
8 | handler.end(self.type)
9 | return True
10 |
11 | class HeadingRule(Rule):
12 | """
13 | A heading is a single line that is at most 70 characters and
14 | that doesn't end with a colon.
15 | """
16 | type = 'heading'
17 | def condition(self, block):
18 | return not '\n' in block and len(block) <= 70 and not block[-1] == ':'
19 |
20 | class TitleRule(HeadingRule):
21 | """
22 | The title is the first block in the document, provided that it is
23 | a heading.
24 | """
25 | type = 'title'
26 | first = True
27 |
28 | def condition(self, block):
29 | if not self.first: return False
30 | self.first = False
31 | return HeadingRule.condition(self, block)
32 |
33 | class ListItemRule(Rule):
34 | """
35 | A list item is a paragraph that begins with a hyphen. As part of
36 | the formatting, the hyphen is removed.
37 | """
38 | type = 'listitem'
39 | def condition(self, block):
40 | return block[0] == '-'
41 | def action(self, block, handler):
42 | handler.start(self.type)
43 | handler.feed(block[1:].strip())
44 | handler.end(self.type)
45 | return True
46 |
47 | class ListRule(ListItemRule):
48 | """
49 | A list begins between a block that is not a list item and a
50 | subsequent list item. It ends after the last consecutive list
51 | item.
52 | """
53 | type = 'list'
54 | inside = False
55 | def condition(self, block):
56 | return True
57 | def action(self, block, handler):
58 | if not self.inside and ListItemRule.condition(self, block):
59 | handler.start(self.type)
60 | self.inside = True
61 | elif self.inside and not ListItemRule.condition(self, block):
62 | handler.end(self.type)
63 | self.inside = False
64 | return False
65 |
66 | class ParagraphRule(Rule):
67 | """
68 | A paragraph is simply a block that isn't covered by any of the
69 | other rules.
70 | """
71 | type = 'paragraph'
72 | def condition(self, block):
73 | return True
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter20/listing20-4.py:
--------------------------------------------------------------------------------
1 | class Handler:
2 | """
3 | An object that handles method calls from the Parser.
4 |
5 | The Parser will call the start() and end() methods at the
6 | beginning of each block, with the proper block name as a
7 | parameter. The sub() method will be used in regular expression
8 | substitution. When called with a name such as 'emphasis', it will
9 | return a proper substitution function.
10 | """
11 | def callback(self, prefix, name, *args):
12 | method = getattr(self, prefix+name, None)
13 | if callable(method): return method(*args)
14 | def start(self, name):
15 | self.callback('start_', name)
16 | def end(self, name):
17 | self.callback('end_', name)
18 | def sub(self, name):
19 | def substitution(match):
20 | result = self.callback('sub_', name, match)
21 | if result is None: match.group(0)
22 | return result
23 | return substitution
24 |
25 | class HTMLRenderer(Handler):
26 | """
27 | A specific handler used for rendering HTML.
28 |
29 | The methods in HTMLRenderer are accessed from the superclass
30 | Handler's start(), end(), and sub() methods. They implement basic
31 | markup as used in HTML documents.
32 | """
33 | def start_document(self):
34 | print '... '
35 | def end_document(self):
36 | print ''
37 | def start_paragraph(self):
38 | print ''
39 | def end_paragraph(self):
40 | print '
'
41 | def start_heading(self):
42 | print ''
43 | def end_heading(self):
44 | print ' '
45 | def start_list(self):
46 | print ''
47 | def end_list(self):
48 | print ' '
49 | def start_listitem(self):
50 | print ''
51 | def end_listitem(self):
52 | print ' '
53 | def start_title(self):
54 | print ''
55 | def end_title(self):
56 | print ' '
57 | def sub_emphasis(self, match):
58 | return '%s ' % match.group(1)
59 | def sub_url(self, match):
60 | return '%s ' % (match.group(1), match.group(1))
61 | def sub_mail(self, match):
62 | return '%s ' % (match.group(1), match.group(1))
63 | def feed(self, data):
64 | print data
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter22/listing22-3.py:
--------------------------------------------------------------------------------
1 | from xml.sax.handler import ContentHandler
2 | from xml.sax import parse
3 | import os
4 |
5 | class Dispatcher:
6 |
7 | def dispatch(self, prefix, name, attrs=None):
8 | mname = prefix + name.capitalize()
9 | dname = 'default' + prefix.capitalize()
10 | method = getattr(self, mname, None)
11 | if callable(method): args = ()
12 | else:
13 | method = getattr(self, dname, None)
14 | args = name,
15 | if prefix == 'start': args += attrs,
16 | if callable(method): method(*args)
17 |
18 | def startElement(self, name, attrs):
19 | self.dispatch('start', name, attrs)
20 |
21 | def endElement(self, name):
22 | self.dispatch('end', name)
23 |
24 | class WebsiteConstructor(Dispatcher, ContentHandler):
25 |
26 | passthrough = False
27 |
28 | def __init__(self, directory):
29 | self.directory = [directory]
30 | self.ensureDirectory()
31 |
32 | def ensureDirectory(self):
33 | path = os.path.join(*self.directory)
34 | if not os.path.isdir(path): os.makedirs(path)
35 |
36 |
37 | def characters(self, chars):
38 | if self.passthrough: self.out.write(chars)
39 |
40 | def defaultStart(self, name, attrs):
41 | if self.passthrough:
42 | self.out.write('<' + name)
43 | for key, val in attrs.items():
44 | self.out.write(' %s="%s"' % (key, val))
45 | self.out.write('>')
46 |
47 | def defaultEnd(self, name):
48 | if self.passthrough:
49 | self.out.write('%s>' % name)
50 |
51 | def startDirectory(self, attrs):
52 | self.directory.append(attrs['name'])
53 | self.ensureDirectory()
54 |
55 | def endDirectory(self):
56 | self.directory.pop()
57 |
58 | def startPage(self, attrs):
59 | filename = os.path.join(*self.directory+[attrs['name']+'.html'])
60 | self.out = open(filename, 'w')
61 | self.writeHeader(attrs['title'])
62 | self.passthrough = True
63 |
64 | def endPage(self):
65 | self.passthrough = False
66 | self.writeFooter()
67 | self.out.close()
68 |
69 | def writeHeader(self, title):
70 | self.out.write('\n \n ')
71 | self.out.write(title)
72 | self.out.write(' \n \n \n')
73 |
74 | def writeFooter(self):
75 | self.out.write('\n \n\n')
76 |
77 | parse('website.xml', WebsiteConstructor('public_html'))
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter28/listing28-1.py:
--------------------------------------------------------------------------------
1 | from xmlrpclib import ServerProxy, Fault
2 | from server import Node, UNHANDLED
3 | from client import randomString
4 | from threading import Thread
5 | from time import sleep
6 | from os import listdir
7 | import sys
8 | import wx
9 |
10 | HEAD_START = 0.1 # Seconds
11 | SECRET_LENGTH = 100
12 |
13 | class Client(wx.App):
14 | """
15 | The main client class, which takes care of setting up the GUI and
16 | starts a Node for serving files.
17 | """
18 | def __init__(self, url, dirname, urlfile):
19 | """
20 | Creates a random secret, instantiates a Node with that secret,
21 | starts a Thread with the Node's _start method (making sure the
22 | Thread is a daemon so it will quit when the application quits),
23 | reads all the URLs from the URL file and introduces the Node to
24 | them.
25 | """
26 | super(Client, self).__init__()
27 | self.secret = randomString(SECRET_LENGTH)
28 | n = Node(url, dirname, self.secret)
29 | t = Thread(target=n._start)
30 | t.setDaemon(1)
31 | t.start()
32 | # Give the server a head start:
33 | sleep(HEAD_START)
34 | self.server = ServerProxy(url)
35 | for line in open(urlfile):
36 | line = line.strip()
37 | self.server.hello(line)
38 |
39 |
40 | def OnInit(self):
41 | """
42 | Sets up the GUI. Creates a window, a text field, and a button, and
43 | lays them out. Binds the submit button to self.fetchHandler.
44 | """
45 |
46 | win = wx.Frame(None, title="File Sharing Client", size=(400, 45))
47 |
48 | bkg = wx.Panel(win)
49 |
50 | self.input = input = wx.TextCtrl(bkg);
51 |
52 | submit = wx.Button(bkg, label="Fetch", size=(80, 25))
53 | submit.Bind(wx.EVT_BUTTON, self.fetchHandler)
54 |
55 | hbox = wx.BoxSizer()
56 |
57 | hbox.Add(input, proportion=1, flag=wx.ALL | wx.EXPAND, border=10)
58 | hbox.Add(submit, flag=wx.TOP | wx.BOTTOM | wx.RIGHT, border=10)
59 |
60 | vbox = wx.BoxSizer(wx.VERTICAL)
61 | vbox.Add(hbox, proportion=0, flag=wx.EXPAND)
62 |
63 | bkg.SetSizer(vbox)
64 |
65 | win.Show()
66 |
67 | return True
68 |
69 | def fetchHandler(self, event):
70 | """
71 | Called when the user clicks the 'Fetch' button. Reads the
72 | query from the text field, and calls the fetch method of the
73 | server Node. If the query is not handled, an error message is
74 | printed.
75 | """
76 |
77 | query = self.input.GetValue()
78 | try:
79 | self.server.fetch(query, self.secret)
80 | except Fault, f:
81 | if f.faultCode != UNHANDLED: raise
82 | print "Couldn't find the file", query
83 |
84 |
85 | def main():
86 | urlfile, directory, url = sys.argv[1:]
87 | client = Client(url, directory, urlfile)
88 | client.MainLoop()
89 |
90 | if __name__ == "__main__": main()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter27/listing27-1.py:
--------------------------------------------------------------------------------
1 | from xmlrpclib import ServerProxy
2 | from os.path import join, isfile
3 | from SimpleXMLRPCServer import SimpleXMLRPCServer
4 | from urlparse import urlparse
5 | import sys
6 |
7 | MAX_HISTORY_LENGTH = 6
8 |
9 | OK = 1
10 | FAIL = 2
11 | EMPTY = ''
12 |
13 | def getPort(url):
14 | 'Extracts the port from a URL'
15 | name = urlparse(url)[1]
16 | parts = name.split(':')
17 | return int(parts[-1])
18 |
19 |
20 | class Node:
21 | """
22 | A node in a peer-to-peer network.
23 | """
24 | def __init__(self, url, dirname, secret):
25 | self.url = url
26 | self.dirname = dirname
27 | self.secret = secret
28 | self.known = set()
29 |
30 | def query(self, query, history=[]):
31 | """
32 | Performs a query for a file, possibly asking other known Nodes for
33 | help. Returns the file as a string.
34 | """
35 | code, data = self._handle(query)
36 | if code == OK:
37 | return code, data
38 | else:
39 | history = history + [self.url]
40 | if len(history) >= MAX_HISTORY_LENGTH:
41 | return FAIL, EMPTY
42 | return self._broadcast(query, history)
43 |
44 | def hello(self, other):
45 | """
46 | Used to introduce the Node to other Nodes.
47 | """
48 | self.known.add(other)
49 | return OK
50 |
51 | def fetch(self, query, secret):
52 | """
53 | Used to make the Node find a file and download it.
54 | """
55 | if secret != self.secret: return FAIL
56 | code, data = self.query(query)
57 | if code == OK:
58 | f = open(join(self.dirname, query), 'w')
59 | f.write(data)
60 | f.close()
61 | return OK
62 | else:
63 | return FAIL
64 |
65 |
66 | def _start(self):
67 | """
68 | Used internally to start the XML-RPC server.
69 | """
70 | s = SimpleXMLRPCServer(("", getPort(self.url)), logRequests=False)
71 | s.register_instance(self)
72 | s.serve_forever()
73 |
74 | def _handle(self, query):
75 | """
76 | Used internally to handle queries.
77 | """
78 | dir = self.dirname
79 | name = join(dir, query)
80 | if not isfile(name): return FAIL, EMPTY
81 | return OK, open(name).read()
82 |
83 | def _broadcast(self, query, history):
84 | """
85 | Used internally to broadcast a query to all known Nodes.
86 | """
87 | for other in self.known.copy():
88 | if other in history: continue
89 | try:
90 | s = ServerProxy(other)
91 | code, data = s.query(query, history)
92 | if code == OK:
93 | return code, data
94 | except:
95 | self.known.remove(other)
96 | return FAIL, EMPTY
97 |
98 | def main():
99 | url, directory, secret = sys.argv[1:]
100 | n = Node(url, directory, secret)
101 | n._start()
102 |
103 | if __name__ == '__main__': main()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter28/listing28-2.py:
--------------------------------------------------------------------------------
1 | from xmlrpclib import ServerProxy, Fault
2 | from server import Node, UNHANDLED
3 | from client import randomString
4 | from threading import Thread
5 | from time import sleep
6 | from os import listdir
7 | import sys
8 | import wx
9 |
10 | HEAD_START = 0.1 # Seconds
11 | SECRET_LENGTH = 100
12 |
13 |
14 | class ListableNode(Node):
15 | """
16 | An extended version of Node, which can list the files
17 | in its file directory.
18 | """
19 | def list(self):
20 | return listdir(self.dirname)
21 |
22 | class Client(wx.App):
23 | """
24 | The main client class, which takes care of setting up the GUI and
25 | starts a Node for serving files.
26 | """
27 | def __init__(self, url, dirname, urlfile):
28 | """
29 | Creates a random secret, instantiates a ListableNode with that secret,
30 | starts a Thread with the ListableNode's _start method (making sure the
31 | Thread is a daemon so it will quit when the application quits),
32 | reads all the URLs from the URL file and introduces the Node to
33 | them. Finally, sets up the GUI.
34 | """
35 | self.secret = randomString(SECRET_LENGTH)
36 | n = ListableNode(url, dirname, self.secret)
37 | t = Thread(target=n._start)
38 | t.setDaemon(1)
39 | t.start()
40 | # Give the server a head start:
41 | sleep(HEAD_START)
42 | self.server = ServerProxy(url)
43 | for line in open(urlfile):
44 | line = line.strip()
45 | self.server.hello(line)
46 | # Get the GUI going:
47 | super(Client, self).__init__()
48 |
49 | def updateList(self):
50 | """
51 | Updates the list box with the names of the files available
52 | from the server Node.
53 | """
54 | self.files.Set(self.server.list())
55 |
56 |
57 | def OnInit(self):
58 | """
59 | Sets up the GUI. Creates a window, a text field, a button, and
60 | a list box, and lays them out. Binds the submit button to
61 | self.fetchHandler.
62 | """
63 |
64 | win = wx.Frame(None, title="File Sharing Client", size=(400, 300))
65 |
66 | bkg = wx.Panel(win)
67 |
68 | self.input = input = wx.TextCtrl(bkg);
69 |
70 | submit = wx.Button(bkg, label="Fetch", size=(80, 25))
71 | submit.Bind(wx.EVT_BUTTON, self.fetchHandler)
72 |
73 | hbox = wx.BoxSizer()
74 |
75 | hbox.Add(input, proportion=1, flag=wx.ALL | wx.EXPAND, border=10)
76 | hbox.Add(submit, flag=wx.TOP | wx.BOTTOM | wx.RIGHT, border=10)
77 |
78 | self.files = files = wx.ListBox(bkg)
79 | self.updateList()
80 |
81 | vbox = wx.BoxSizer(wx.VERTICAL)
82 | vbox.Add(hbox, proportion=0, flag=wx.EXPAND)
83 | vbox.Add(files, proportion=1,
84 | flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=10)
85 |
86 | bkg.SetSizer(vbox)
87 |
88 | win.Show()
89 |
90 | return True
91 |
92 | def fetchHandler(self, event):
93 | """
94 | Called when the user clicks the 'Fetch' button. Reads the
95 | query from the text field, and calls the fetch method of the
96 | server Node. After handling the query, updateList is called.
97 | If the query is not handled, an error message is printed.
98 | """
99 | query = self.input.GetValue()
100 | try:
101 | self.server.fetch(query, self.secret)
102 | self.updateList()
103 |
104 | except Fault, f:
105 | if f.faultCode != UNHANDLED: raise
106 | print "Couldn't find the file", query
107 |
108 | def main():
109 | urlfile, directory, url = sys.argv[1:]
110 | client = Client(url, directory, urlfile)
111 | client.MainLoop()
112 |
113 | if __name__ == '__main__': main()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter27/listing27-2.py:
--------------------------------------------------------------------------------
1 | from xmlrpclib import ServerProxy, Fault
2 | from os.path import join, abspath, isfile
3 | from SimpleXMLRPCServer import SimpleXMLRPCServer
4 | from urlparse import urlparse
5 | import sys
6 |
7 | SimpleXMLRPCServer.allow_reuse_address = 1
8 |
9 | MAX_HISTORY_LENGTH = 6
10 |
11 | UNHANDLED = 100
12 | ACCESS_DENIED = 200
13 |
14 | class UnhandledQuery(Fault):
15 | """
16 | An exception that represents an unhandled query.
17 | """
18 | def __init__(self, message="Couldn't handle the query"):
19 | Fault.__init__(self, UNHANDLED, message)
20 |
21 |
22 | class AccessDenied(Fault):
23 | """
24 | An exception that is raised if a user tries to access a
25 | resource for which he or she is not authorized.
26 | """
27 | def __init__(self, message="Access denied"):
28 | Fault.__init__(self, ACCESS_DENIED, message)
29 |
30 | def inside(dir, name):
31 | """
32 | Checks whether a given file name lies within a given directory.
33 | """
34 | dir = abspath(dir)
35 | name = abspath(name)
36 | return name.startswith(join(dir, ''))
37 |
38 | def getPort(url):
39 | """
40 | Extracts the port number from a URL.
41 | """
42 | name = urlparse(url)[1]
43 | parts = name.split(':')
44 | return int(parts[-1])
45 |
46 | class Node:
47 | """
48 | A node in a peer-to-peer network.
49 | """
50 | def __init__(self, url, dirname, secret):
51 | self.url = url
52 | self.dirname = dirname
53 | self.secret = secret
54 | self.known = set()
55 |
56 | def query(self, query, history=[]):
57 | """
58 | Performs a query for a file, possibly asking other known Nodes for
59 | help. Returns the file as a string.
60 | """
61 | try:
62 | return self._handle(query)
63 | except UnhandledQuery:
64 | history = history + [self.url]
65 | if len(history) >= MAX_HISTORY_LENGTH: raise
66 | return self._broadcast(query, history)
67 |
68 |
69 | def hello(self, other):
70 | """
71 | Used to introduce the Node to other Nodes.
72 | """
73 | self.known.add(other)
74 | return 0
75 |
76 | def fetch(self, query, secret):
77 | """
78 | Used to make the Node find a file and download it.
79 | """
80 | if secret != self.secret: raise AccessDenied
81 | result = self.query(query)
82 | f = open(join(self.dirname, query), 'w')
83 | f.write(result)
84 | f.close()
85 | return 0
86 |
87 | def _start(self):
88 | """
89 | Used internally to start the XML-RPC server.
90 | """
91 | s = SimpleXMLRPCServer(("", getPort(self.url)), logRequests=False)
92 | s.register_instance(self)
93 | s.serve_forever()
94 |
95 | def _handle(self, query):
96 | """
97 | Used internally to handle queries.
98 | """
99 | dir = self.dirname
100 | name = join(dir, query)
101 | if not isfile(name): raise UnhandledQuery
102 | if not inside(dir, name): raise AccessDenied
103 | return open(name).read()
104 |
105 | def _broadcast(self, query, history):
106 | """
107 | Used internally to broadcast a query to all known Nodes.
108 | """
109 | for other in self.known.copy():
110 | if other in history: continue
111 | try:
112 | s = ServerProxy(other)
113 | return s.query(query, history)
114 |
115 | except Fault, f:
116 | if f.faultCode == UNHANDLED: pass
117 | else: self.known.remove(other)
118 | except:
119 | self.known.remove(other)
120 | raise UnhandledQuery
121 |
122 | def main():
123 | url, directory, secret = sys.argv[1:]
124 | n = Node(url, directory, secret)
125 | n._start()
126 |
127 | if __name__ == '__main__': main()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter29/listing29-2.py:
--------------------------------------------------------------------------------
1 | # Configuration file for Squish
2 | # -----------------------------
3 |
4 | # Feel free to modify the configuration variables below to taste.
5 | # If the game is too fast or too slow, try to modify the speed
6 | # variables.
7 |
8 | # Change these to use other images in the game:
9 | banana_image = 'banana.png'
10 | weight_image = 'weight.png'
11 | splash_image = 'weight.png'
12 |
13 | # Change these to affect the general appearance:
14 | screen_size = 800, 600
15 | background_color = 255, 255, 255
16 | margin = 30
17 | full_screen = 1
18 | font_size = 48
19 |
20 | # These affect the behavior of the game:
21 | drop_speed = 5
22 | banana_speed = 10
23 | speed_increase = 1
24 | weights_per_level = 10
25 | banana_pad_top = 40
26 | banana_pad_side = 20
27 | Listing 29-3. The Squish Game Objects (objects.py)
28 | import pygame, config, os
29 | from random import randrange
30 |
31 | "This module contains the game objects of the Squish game."
32 |
33 | class SquishSprite(pygame.sprite.Sprite):
34 |
35 | """
36 | Generic superclass for all sprites in Squish. The constructor
37 | takes care of loading an image, setting up the sprite rect, and
38 | the area within which it is allowed to move. That area is governed
39 | by the screen size and the margin.
40 | """
41 |
42 | def __init__(self, image):
43 | pygame.sprite.Sprite.__init__(self)
44 | self.image = pygame.image.load(image).convert()
45 | self.rect = self.image.get_rect()
46 | screen = pygame.display.get_surface()
47 | shrink = -config.margin * 2
48 | self.area = screen.get_rect().inflate(shrink, shrink)
49 |
50 |
51 | class Weight(SquishSprite):
52 |
53 | """
54 | A falling weight. It uses the SquishSprite constructor to set up
55 | its weight image, and will fall with a speed given as a parameter
56 | to its constructor.
57 | """
58 |
59 | def __init__(self, speed):
60 | SquishSprite.__init__(self, config.weight_image)
61 | self.speed = speed
62 | self.reset()
63 |
64 | def reset(self):
65 | """
66 | Move the weight to the top of the screen (just out of sight)
67 | and place it at a random horizontal position.
68 | """
69 | x = randrange(self.area.left, self.area.right)
70 | self.rect.midbottom = x, 0
71 |
72 | def update(self):
73 | """
74 | Move the weight vertically (downwards) a distance
75 | corresponding to its speed. Also set the landed attribute
76 | according to whether it has reached the bottom of the screen.
77 | """
78 | self.rect.top += self.speed
79 | self.landed = self.rect.top >= self.area.bottom
80 |
81 |
82 | class Banana(SquishSprite):
83 |
84 | """
85 | A desperate banana. It uses the SquishSprite constructor to set up
86 | its banana image, and will stay near the bottom of the screen,
87 | with its horizontal position governed by the current mouse
88 | position (within certain limits).
89 | """
90 |
91 | def __init__(self):
92 | SquishSprite.__init__(self, config.banana_image)
93 | self.rect.bottom = self.area.bottom
94 |
95 | # These paddings represent parts of the image where there is
96 | # no banana. If a weight moves into these areas, it doesn't
97 | # constitute a hit (or, rather, a squish):
98 | self.pad_top = config.banana_pad_top
99 | self.pad_side = config.banana_pad_side
100 |
101 | def update(self):
102 | """
103 | Set the Banana's center x-coordinate to the current mouse
104 | x-coordinate, and then use the rect method clamp to ensure
105 | that the Banana stays within its allowed range of motion.
106 | """
107 | self.rect.centerx = pygame.mouse.get_pos()[0]
108 | self.rect = self.rect.clamp(self.area)
109 |
110 | def touches(self, other):
111 | """
112 | Determines whether the banana touches another sprite (e.g., a
113 | Weight). Instead of just using the rect method colliderect, a
114 | new rectangle is first calculated (using the rect method
115 | inflate with the side and top paddings) that does not include
116 | the 'empty' areas on the top and sides of the banana.
117 | """
118 | # Deflate the bounds with the proper padding:
119 | bounds = self.rect.inflate(-self.pad_side, -self.pad_top)
120 | # Move the bounds so they are placed at the bottom of the Banana:
121 | bounds.bottom = self.rect.bottom
122 | # Check whether the bounds intersect with the other object's rect:
123 | return bounds.colliderect(other.rect)
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter23/listing23-2.py:
--------------------------------------------------------------------------------
1 | from nntplib import NNTP
2 | from time import strftime, time, localtime
3 | from email import message_from_string
4 | from urllib import urlopen
5 | import textwrap
6 | import re
7 |
8 | day = 24 * 60 * 60 # Number of seconds in one day
9 |
10 | def wrap(string, max=70):
11 | """
12 | Wraps a string to a maximum line width.
13 | """
14 |
15 | return '\n'.join(textwrap.wrap(string)) + '\n'
16 |
17 | class NewsAgent:
18 | """
19 | An object that can distribute news items from news
20 | sources to news destinations.
21 | """
22 | def __init__(self):
23 | self.sources = []
24 | self.destinations = []
25 |
26 | def addSource(self, source):
27 | self.sources.append(source)
28 |
29 | def addDestination(self, dest):
30 | self.destinations.append(dest)
31 |
32 | def distribute(self):
33 | """
34 | Retrieve all news items from all sources, and
35 | Distribute them to all destinations.
36 | """
37 | items = []
38 | for source in self.sources:
39 | items.extend(source.getItems())
40 | for dest in self.destinations:
41 | dest.receiveItems(items)
42 |
43 | class NewsItem:
44 | """
45 | A simple news item consisting of a title and a body text.
46 | """
47 | def __init__(self, title, body):
48 | self.title = title
49 | self.body = body
50 |
51 | class NNTPSource:
52 | """
53 | A news source that retrieves news items from an NNTP group.
54 | """
55 | def __init__(self, servername, group, window):
56 | self.servername = servername
57 | self.group = group
58 | self.window = window
59 |
60 | def getItems(self):
61 |
62 | start = localtime(time() - self.window*day)
63 | date = strftime('%y%m%d', start)
64 | hour = strftime('%H%M%S', start)
65 |
66 | server = NNTP(self.servername)
67 |
68 |
69 | ids = server.newnews(self.group, date, hour)[1]
70 |
71 | for id in ids:
72 | lines = server.article(id)[3]
73 | message = message_from_string('\n'.join(lines))
74 |
75 | title = message['subject']
76 | body = message.get_payload()
77 | if message.is_multipart():
78 | body = body[0]
79 |
80 | yield NewsItem(title, body)
81 |
82 | server.quit()
83 |
84 | class SimpleWebSource:
85 | """
86 | A news source that extracts news items from a Web page using
87 | regular expressions.
88 | """
89 | def __init__(self, url, titlePattern, bodyPattern):
90 | self.url = url
91 | self.titlePattern = re.compile(titlePattern)
92 | self.bodyPattern = re.compile(bodyPattern)
93 |
94 | def getItems(self):
95 | text = urlopen(self.url).read()
96 | titles = self.titlePattern.findall(text)
97 | bodies = self.bodyPattern.findall(text)
98 | for title, body in zip(titles, bodies):
99 | yield NewsItem(title, wrap(body))
100 |
101 | class PlainDestination:
102 | """
103 | A news destination that formats all its news items as
104 | plain text.
105 | """
106 | def receiveItems(self, items):
107 | for item in items:
108 | print item.title
109 | print '-'*len(item.title)
110 | print item.body
111 |
112 |
113 | class HTMLDestination:
114 | """
115 | A news destination that formats all its news items
116 | as HTML.
117 | """
118 | def __init__(self, filename):
119 | self.filename = filename
120 |
121 | def receiveItems(self, items):
122 |
123 | out = open(self.filename, 'w')
124 | print >> out, """
125 |
126 |
127 | Today's News
128 |
129 |
130 | Today's News
131 | """
132 |
133 | print >> out, ''
134 | id = 0
135 | for item in items:
136 | id += 1
137 | print >> out, ' %s ' % (id, item.title)
138 | print >> out, ' '
139 |
140 | id = 0
141 | for item in items:
142 | id += 1
143 | print >> out, '' % (id, item.title)
144 | print >> out, '%s ' % item.body
145 |
146 | print >> out, """
147 |
148 |
149 | """
150 |
151 | def runDefaultSetup():
152 | """
153 | A default setup of sources and destination. Modify to taste.
154 | """
155 | agent = NewsAgent()
156 |
157 |
158 | # A SimpleWebSource that retrieves news from the
159 | # BBC news site:
160 | bbc_url = 'http://news.bbc.co.uk/text_only.stm'
161 | bbc_title = r'(?s)a href="[^"]*">\s*\s*(.*?)\s* '
162 | bbc_body = r'(?s)\s* \s*(.*?)\s*<'
163 | bbc = SimpleWebSource(bbc_url, bbc_title, bbc_body)
164 |
165 | agent.addSource(bbc)
166 |
167 | # An NNTPSource that retrieves news from comp.lang.python.announce:
168 | clpa_server = 'news.foo.bar' # Insert real server name
169 | clpa_group = 'comp.lang.python.announce'
170 | clpa_window = 1
171 | clpa = NNTPSource(clpa_server, clpa_group, clpa_window)
172 |
173 | agent.addSource(clpa)
174 |
175 | # Add plain text destination and an HTML destination:
176 | agent.addDestination(PlainDestination())
177 | agent.addDestination(HTMLDestination('news.html'))
178 |
179 | # Distribute the news items:
180 | agent.distribute()
181 |
182 | if __name__ == '__main__': runDefaultSetup()
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter24/listing24-6.py:
--------------------------------------------------------------------------------
1 | from asyncore import dispatcher
2 | from asynchat import async_chat
3 | import socket, asyncore
4 |
5 | PORT = 5005
6 | NAME = 'TestChat'
7 |
8 | class EndSession(Exception): pass
9 |
10 | class CommandHandler:
11 | """
12 | Simple command handler similar to cmd.Cmd from the standard
13 | library.
14 | """
15 |
16 |
17 | def unknown(self, session, cmd):
18 | 'Respond to an unknown command'
19 | session.push('Unknown command: %s\r\n' % cmd)
20 |
21 | def handle(self, session, line):
22 | 'Handle a received line from a given session'
23 | if not line.strip(): return
24 | # Split off the command:
25 | parts = line.split(' ', 1)
26 | cmd = parts[0]
27 | try: line = parts[1].strip()
28 | except IndexError: line = ''
29 | # Try to find a handler:
30 | meth = getattr(self, 'do_'+cmd, None)
31 | try:
32 | # Assume it's callable:
33 | meth(session, line)
34 | except TypeError:
35 | # If it isn't, respond to the unknown command:
36 | self.unknown(session, cmd)
37 |
38 | class Room(CommandHandler):
39 | """
40 | A generic environment that may contain one or more users
41 | (sessions). It takes care of basic command handling and
42 | broadcasting.
43 | """
44 |
45 | def __init__(self, server):
46 | self.server = server
47 | self.sessions = []
48 |
49 | def add(self, session):
50 | 'A session (user) has entered the room'
51 | self.sessions.append(session)
52 |
53 | def remove(self, session):
54 | 'A session (user) has left the room'
55 | self.sessions.remove(session)
56 |
57 | def broadcast(self, line):
58 | 'Send a line to all sessions in the room'
59 | for session in self.sessions:
60 | session.push(line)
61 |
62 |
63 | def do_logout(self, session, line):
64 | 'Respond to the logout command'
65 | raise EndSession
66 |
67 | class LoginRoom(Room):
68 | """
69 | A room meant for a single person who has just connected.
70 | """
71 |
72 | def add(self, session):
73 | Room.add(self, session)
74 | # When a user enters, greet him/her:
75 | self.broadcast('Welcome to %s\r\n' % self.server.name)
76 |
77 | def unknown(self, session, cmd):
78 | # All unknown commands (anything except login or logout)
79 | # results in a prodding:
80 | session.push('Please log in\nUse "login "\r\n')
81 |
82 | def do_login(self, session, line):
83 | name = line.strip()
84 | # Make sure the user has entered a name:
85 | if not name:
86 | session.push('Please enter a name\r\n')
87 | # Make sure that the name isn't in use:
88 | elif name in self.server.users:
89 | session.push('The name "%s" is taken.\r\n' % name)
90 | session.push('Please try again.\r\n')
91 | else:
92 | # The name is OK, so it is stored in the session, and
93 | # the user is moved into the main room.
94 | session.name = name
95 | session.enter(self.server.main_room)
96 |
97 | class ChatRoom(Room):
98 | """
99 | A room meant for multiple users who can chat with the others in
100 | the room.
101 | """
102 |
103 | def add(self, session):
104 | # Notify everyone that a new user has entered:
105 | self.broadcast(session.name + ' has entered the room.\r\n')
106 | self.server.users[session.name] = session
107 | Room.add(self, session)
108 |
109 |
110 | def remove(self, session):
111 | Room.remove(self, session)
112 | # Notify everyone that a user has left:
113 | self.broadcast(session.name + ' has left the room.\r\n')
114 |
115 | def do_say(self, session, line):
116 | self.broadcast(session.name+': '+line+'\r\n')
117 |
118 | def do_look(self, session, line):
119 | 'Handles the look command, used to see who is in a room'
120 | session.push('The following are in this room:\r\n')
121 | for other in self.sessions:
122 | session.push(other.name + '\r\n')
123 |
124 | def do_who(self, session, line):
125 | 'Handles the who command, used to see who is logged in'
126 | session.push('The following are logged in:\r\n')
127 | for name in self.server.users:
128 | session.push(name + '\r\n')
129 |
130 | class LogoutRoom(Room):
131 | """
132 | A simple room for a single user. Its sole purpose is to remove
133 | the user's name from the server.
134 | """
135 |
136 | def add(self, session):
137 | # When a session (user) enters the LogoutRoom it is deleted
138 | try: del self.server.users[session.name]
139 | except KeyError: pass
140 |
141 | class ChatSession(async_chat):
142 | """
143 | A single session, which takes care of the communication with a
144 | single user.
145 | """
146 |
147 | def __init__(self, server, sock):
148 | async_chat.__init__(self, sock)
149 | self.server = server
150 | self.set_terminator("\r\n")
151 | self.data = []
152 | self.name = None
153 | # All sessions begin in a separate LoginRoom:
154 | self.enter(LoginRoom(server))
155 |
156 |
157 | def enter(self, room):
158 | # Remove self from current room and add self to
159 | # next room...
160 | try: cur = self.room
161 | except AttributeError: pass
162 | else: cur.remove(self)
163 | self.room = room
164 | room.add(self)
165 |
166 | def collect_incoming_data(self, data):
167 | self.data.append(data)
168 |
169 | def found_terminator(self):
170 | line = ''.join(self.data)
171 | self.data = []
172 | try: self.room.handle(self, line)
173 | except EndSession:
174 | self.handle_close()
175 |
176 | def handle_close(self):
177 | async_chat.handle_close(self)
178 | self.enter(LogoutRoom(self.server))
179 |
180 | class ChatServer(dispatcher):
181 | """
182 | A chat server with a single room.
183 | """
184 |
185 | def __init__(self, port, name):
186 | dispatcher.__init__(self)
187 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
188 | self.set_reuse_addr()
189 | self.bind(('', port))
190 | self.listen(5)
191 | self.name = name
192 | self.users = {}
193 | self.main_room = ChatRoom(self)
194 |
195 | def handle_accept(self):
196 | conn, addr = self.accept()
197 | ChatSession(self, conn)
198 |
199 | if __name__ == '__main__':
200 | s = ChatServer(PORT, NAME)
201 | try: asyncore.loop()
202 | except KeyboardInterrupt: print
--------------------------------------------------------------------------------
/978-1-59059-982-2/README.txt:
--------------------------------------------------------------------------------
1 | This is the source code for Beginning Python : From Novice to Professional,
2 | second edition. Each code listing in the book can be found in a correspondingly
3 | numbered directory/file. In order to work properly together, some of these
4 | may need to be renamed (as indicated by the book).
5 |
6 | Chapter02/listing2-1.py: Indexing Example
7 | Chapter02/listing2-2.py: Slicing Example
8 | Chapter02/listing2-3.py: Sequence (String) Multiplication Example
9 | Chapter02/listing2-4.py: Sequence Membership Example
10 | Chapter03/listing3-1.py: String Formatting Example
11 | Chapter04/listing4-1.py: Dictionary Example
12 | Chapter04/listing4-2.py: Dictionary Method Example
13 | Chapter10/listing10-1.py: A Simple Module
14 | Chapter10/listing10-2.py: A Simple Module Containing a Function
15 | Chapter10/listing10-3.py: A Simple Module with Some Problematic Test Code
16 | Chapter10/listing10-4.py: A Module with Conditional Test Code
17 | Chapter10/listing10-5.py: Reversing and Printing Command-Line Arguments
18 | Chapter10/listing10-6.py: Adding Line Numbers to a Python Script
19 | Chapter10/listing10-7.py: The Line Numbering Program with Line Numbers Added
20 | Chapter10/listing10-8.py: A Simple Database Application
21 | Chapter10/listing10-10.py: A Program for Finding the Sender of an E-mail
22 | Chapter10/listing10-11.py: A Template System
23 | Chapter10/listing10-12.txt: A Simple Template Example
24 | Chapter10/listing10-13.txt: Some Template Definitions
25 | Chapter10/listing10-14.txt: A Template
26 | Chapter11/listing11-1.py: Simple Script That Counts the Words in sys.stdin
27 | Chapter11/listing11-2.txt: A File Containing Some Nonsensical Text
28 | Chapter11/listing11-3.txt: A Simple Text File
29 | Chapter11/listing11-4.txt: The Modified Text File
30 | Chapter11/listing11-5.txt: The Text File, Modified Again
31 | Chapter11/listing11-6.py: Looping over Characters with read
32 | Chapter11/listing11-7.py: Writing the Loop Differently
33 | Chapter11/listing11-8.py: Using readline in a while Loop
34 | Chapter11/listing11-9.py: Iterating over Characters with read
35 | Chapter11/listing11-10.py: Iterating over Lines with readlines
36 | Chapter11/listing11-11.py: Iterating over Lines with fileinput
37 | Chapter11/listing11-12.py: Iterating over a File
38 | Chapter11/listing11-13.py: Iterating over a File Without Storing the File Object in a Variable
39 | Chapter12/listing12-1.py: Creating and Showing a Frame
40 | Chapter12/listing12-2.py: Adding a Button to a Frame
41 | Chapter12/listing12-3.py: Adding Labels and Titles with Keyword Arguments
42 | Chapter12/listing12-4.py: Setting Button Positions
43 | Chapter12/listing12-5.py: Using a Sizer
44 | Chapter12/listing12-6.py: The Final GUI Program
45 | Chapter13/listing13-1.py: Importing Data into the Database (importdata.py)
46 | Chapter13/listing13-2.py: Food Database Query Program (food_query.py)
47 | Chapter14/listing14-1.py: A Minimal Server
48 | Chapter14/listing14-2.py: A Minimal Client
49 | Chapter14/listing14-3.py: A SocketServer-Based Minimal Server
50 | Chapter14/listing14-4.py: A Forking Server
51 | Chapter14/listing14-5.py: A Threading Server
52 | Chapter14/listing14-6.py: A Simple Server Using select
53 | Chapter14/listing14-7.py: A Simple Server Using poll
54 | Chapter14/listing14-8.py: A Simple Server Using Twisted
55 | Chapter14/listing14-9.py: An Improved Logging Server, Using the LineReceiver Protocol
56 | Chapter15/listing15-1.py: A Simple Screen Scraping Program
57 | Chapter15/listing15-2.py: A Screen Scraping Program Using the HTMLParser Module
58 | Chapter15/listing15-3.py: A Screen Scraping Program Using Beautiful Soup
59 | Chapter15/listing15-4.py: A Simple CGI Script
60 | Chapter15/listing15-5.py: A CGI Script That Invokes a Traceback (faulty.cgi)
61 | Chapter15/listing15-6.py: A CGI Script That Retrieves a Single Value from a FieldStorage (simple2.cgi)
62 | Chapter15/listing15-7.py: A Greeting Script with an HTML Form (simple3.cgi)
63 | Chapter15/listing15-8.psp: A Slightly Stochastic PSP Example
64 | Chapter15/listing15-9.py: Simple Authentication with the mod_python Publisher
65 | Chapter15/listing15-1.py: A Simple Screen Scraping Program
66 | Chapter16/listing16-1.py: A Simple Test Program
67 | Chapter16/listing16-2.py: A Simple Test Using the unittest Framework
68 | Chapter16/listing16-3.py: Calling External Checkers Using the subprocess Module
69 | Chapter17/listing17-1.java: A Simple Java Class (JythonTest.java)
70 | Chapter17/listing17-2.cs: A Simple C# Class (IronPythonTest.cs)
71 | Chapter17/listing17-3.c: A Simple C Function for Detecting a Palindrome (palindrome.c)
72 | Chapter17/listing17-4.py: Detecting Palindromes in Python
73 | Chapter17/listing17-5.i: Interface to the Palindrome Library (palindrome.i)
74 | Chapter17/listing17-6.c: Palindrome Checking Again (palindrome2.c)
75 | Chapter18/listing18-1.py: Simple Distutils Setup Script (setup.py)
76 | Chapter19/listing19-1.cfg: A Simple Configuration File
77 | Chapter19/listing19-2.py: A Program Using ConfigParser
78 | Chapter19/listing19-3.py: A Program Using the logging Module
79 | Chapter20/listing20-1.txt: A Sample Plain-Text Document (test_input.txt)
80 | Chapter20/listing20-2.py: A Text Block Generator (util.py)
81 | Chapter20/listing20-3.py: A Simple Markup Program (simple_markup.py)
82 | Chapter20/listing20-4.py: The Handlers (handlers.py)
83 | Chapter20/listing20-5.py: The Rules (rules.py)
84 | Chapter20/listing20-6.py: The Main Program (markup.py)
85 | Chapter21/listing21-1.py: A Simple ReportLab Program (hello_report.py)
86 | Chapter21/listing21-2.py: The First Prototype for the Sunspot Graph Program (sunspots_proto.py)
87 | Chapter21/listing21-3.py: The Final Sunspot Program (sunspots.py)
88 | Chapter22/listing22-1.xml: A Simple Web Site Represented As an XML File (website.xml)
89 | Chapter22/listing22-2.py: A Simple Page Maker Script (pagemaker.py)
90 | Chapter22/listing22-3.py: The Web Site Constructor (website.py)
91 | Chapter23/listing23-1.py: A Simple News-Gathering Agent (newsagent1.py)
92 | Chapter23/listing23-2.py: A More Flexible News-Gathering Agent (newsagent2.py)
93 | Chapter24/listing24-1.py: A Minimal Server Program
94 | Chapter24/listing24-2.py: A Server That Accepts Connections
95 | Chapter24/listing24-3.py: The Basic Server with Some Cleanups
96 | Chapter24/listing24-4.py: Server Program with ChatSession Class
97 | Chapter24/listing24-5.py: A Simple Chat Server (simple_chat.py)
98 | Chapter24/listing24-6.py: A Slightly More Complicated Chat Server (chatserver.py)
99 | Chapter25/listing25-1.py: A Simple Web Editor (simple_edit.cgi)
100 | Chapter25/listing25-2.py: The Editor Script (edit.cgi)
101 | Chapter25/listing25-3.py: The Saving Script (save.cgi)
102 | Chapter26/listing26-1.sql: Creating the Database in PostgreSQL
103 | Chapter26/listing26-2.sql: Creating the Database in MySQL
104 | Chapter26/listing26-3.sql: Creating the Database in SQLite
105 | Chapter26/listing26-4.py: The Main Bulletin Board (simple_main.cgi)
106 | Chapter26/listing26-5.py: The Main Bulletin Board (main.cgi)
107 | Chapter26/listing26-6.py: The Message Viewer (view.cgi)
108 | Chapter26/listing26-7.py: The Message Editor (edit.cgi)
109 | Chapter26/listing26-8.py: The Save Script (save.cgi)
110 | Chapter27/listing27-1.py: A Simple Node Implementation (simple_node.py)
111 | Chapter27/listing27-2.py: A New Node Implementation (server.py)
112 | Chapter27/listing27-3.py: A Node Controller Interface (client.py)
113 | Chapter28/listing28-1.py: A Simple GUI Client (simple_guiclient.py)
114 | Chapter28/listing28-2.py: The Finished GUI Client (guiclient.py)
115 | Chapter29/listing29-1.py: A Simple “Falling Weights” Animation (weights.py)
116 | Chapter29/listing29-2.py: The Squish Configuration File (config.py)
117 | Chapter29/listing29-4.py: The Main Game Module (squish.py)
118 |
--------------------------------------------------------------------------------
/978-1-59059-982-2/Chapter29/listing29-4.py:
--------------------------------------------------------------------------------
1 | import os, sys, pygame
2 | from pygame.locals import *
3 | import objects, config
4 |
5 | "This module contains the main game logic of the Squish game."
6 |
7 | class State:
8 |
9 | """
10 | A generic game state class that can handle events and display
11 | itself on a given surface.
12 | """
13 |
14 | def handle(self, event):
15 | """
16 | Default event handling only deals with quitting.
17 | """
18 | if event.type == QUIT:
19 | sys.exit()
20 | if event.type == KEYDOWN and event.key == K_ESCAPE:
21 | sys.exit()
22 |
23 | def firstDisplay(self, screen):
24 | """
25 | Used to display the State for the first time. Fills the screen
26 | with the background color.
27 | """
28 | screen.fill(config.background_color)
29 | # Remember to call flip, to make the changes visible:
30 | pygame.display.flip()
31 |
32 | def display(self, screen):
33 | """
34 | Used to display the State after it has already been displayed
35 | once. The default behavior is to do nothing.
36 | """
37 | pass
38 |
39 |
40 | class Level(State):
41 |
42 | """
43 | A game level. Takes care of counting how many weights have been
44 | dropped, moving the sprites around, and other tasks relating to
45 | game logic.
46 | """
47 |
48 | def __init__(self, number=1):
49 | self.number = number
50 | # How many weights remain to dodge in this level?
51 | self.remaining = config.weights_per_level
52 |
53 | speed = config.drop_speed
54 | # One speed_increase added for each level above 1:
55 | speed += (self.number-1) * config.speed_increase
56 |
57 | # Create the weight and banana:
58 | self.weight = objects.Weight(speed)
59 | self.banana = objects.Banana()
60 | both = self.weight, self.banana # This could contain more sprites...
61 | self.sprites = pygame.sprite.RenderUpdates(both)
62 |
63 | def update(self, game):
64 | "Updates the game state from the previous frame."
65 | # Update all sprites:
66 | self.sprites.update()
67 | # If the banana touches the weight, tell the game to switch to
68 | # a GameOver state:
69 | if self.banana.touches(self.weight):
70 | game.nextState = GameOver()
71 | # Otherwise, if the weight has landed, reset it. If all the
72 | # weights of this level have been dodged, tell the game to
73 | # switch to a LevelCleared state:
74 | elif self.weight.landed:
75 | self.weight.reset()
76 | self.remaining -= 1
77 | if self.remaining == 0:
78 | game.nextState = LevelCleared(self.number)
79 |
80 | def display(self, screen):
81 | """
82 | Displays the state after the first display (which simply wipes
83 | the screen). As opposed to firstDisplay, this method uses
84 | pygame.display.update with a list of rectangles that need to
85 | be updated, supplied from self.sprites.draw.
86 | """
87 | screen.fill(config.background_color)
88 | updates = self.sprites.draw(screen)
89 | pygame.display.update(updates)
90 |
91 |
92 | class Paused(State):
93 |
94 | """
95 | A simple, paused game state, which may be broken out of by pressing
96 | either a keyboard key or the mouse button.
97 | """
98 |
99 | finished = 0 # Has the user ended the pause?
100 | image = None # Set this to a file name if you want an image
101 | text = '' # Set this to some informative text
102 |
103 | def handle(self, event):
104 | """
105 | Handles events by delegating to State (which handles quitting
106 | in general) and by reacting to key presses and mouse
107 | clicks. If a key is pressed or the mouse is clicked,
108 | self.finished is set to true.
109 | """
110 | State.handle(self, event)
111 | if event.type in [MOUSEBUTTONDOWN, KEYDOWN]:
112 | self.finished = 1
113 |
114 | def update(self, game):
115 | """
116 | Update the level. If a key has been pressed or the mouse has
117 | been clicked (i.e., self.finished is true), tell the game to
118 | move to the state represented by self.nextState() (should be
119 | implemented by subclasses).
120 | """
121 | if self.finished:
122 | game.nextState = self.nextState()
123 |
124 | def firstDisplay(self, screen):
125 | """
126 | The first time the Paused state is displayed, draw the image
127 | (if any) and render the text.
128 | """
129 | # First, clear the screen by filling it with the background color:
130 | screen.fill(config.background_color)
131 |
132 | # Create a Font object with the default appearance, and specified size:
133 | font = pygame.font.Font(None, config.font_size)
134 |
135 | # Get the lines of text in self.text, ignoring empty lines at
136 | # the top or bottom:
137 | lines = self.text.strip().splitlines()
138 |
139 | # Calculate the height of the text (using font.get_linesize()
140 | # to get the height of each line of text):
141 | height = len(lines) * font.get_linesize()
142 |
143 | # Calculate the placement of the text (centered on the screen):
144 | center, top = screen.get_rect().center
145 | top -= height // 2
146 |
147 | # If there is an image to display...
148 | if self.image:
149 | # load it:
150 | image = pygame.image.load(self.image).convert()
151 | # get its rect:
152 | r = image.get_rect()
153 | # move the text down by half the image height:
154 | top += r.height // 2
155 | # place the image 20 pixels above the text:
156 | r.midbottom = center, top - 20
157 | # blit the image to the screen:
158 | screen.blit(image, r)
159 |
160 | antialias = 1 # Smooth the text
161 | black = 0, 0, 0 # Render it as black
162 |
163 | # Render all the lines, starting at the calculated top, and
164 | # move down font.get_linesize() pixels for each line:
165 | for line in lines:
166 | text = font.render(line.strip(), antialias, black)
167 | r = text.get_rect()
168 | r.midtop = center, top
169 | screen.blit(text, r)
170 | top += font.get_linesize()
171 |
172 | # Display all the changes:
173 | pygame.display.flip()
174 |
175 |
176 | class Info(Paused):
177 |
178 | """
179 | A simple paused state that displays some information about the
180 | game. It is followed by a Level state (the first level).
181 | """
182 |
183 | nextState = Level
184 | text = '''
185 | In this game you are a banana,
186 | trying to survive a course in
187 | self-defense against fruit, where the
188 | participants will "defend" themselves
189 | against you with a 16 ton weight.'''
190 |
191 |
192 | class StartUp(Paused):
193 |
194 | """
195 | A paused state that displays a splash image and a welcome
196 | message. It is followed by an Info state.
197 | """
198 |
199 | nextState = Info
200 | image = config.splash_image
201 | text = '''
202 | Welcome to Squish,
203 | the game of Fruit Self-Defense'''
204 |
205 |
206 | class LevelCleared(Paused):
207 |
208 | """
209 | A paused state that informs the user that he or she has cleared a
210 | given level. It is followed by the next level state.
211 | """
212 |
213 | def __init__(self, number):
214 | self.number = number
215 | self.text = '''Level %i cleared
216 | Click to start next level''' % self.number
217 |
218 |
219 | def nextState(self):
220 | return Level(self.number+1)
221 |
222 |
223 | class GameOver(Paused):
224 |
225 | """
226 | A state that informs the user that he or she has lost the
227 | game. It is followed by the first level.
228 | """
229 |
230 | nextState = Level
231 | text = '''
232 | Game Over
233 | Click to Restart, Esc to Quit'''
234 |
235 |
236 | class Game:
237 |
238 | """
239 | A game object that takes care of the main event loop, including
240 | changing between the different game states.
241 | """
242 |
243 | def __init__(self, *args):
244 | # Get the directory where the game and the images are located:
245 | path = os.path.abspath(args[0])
246 | dir = os.path.split(path)[0]
247 | # Move to that directory (so that the images files may be
248 | # opened later on):
249 | os.chdir(dir)
250 | # Start with no state:
251 | self.state = None
252 | # Move to StartUp in the first event loop iteration:
253 | self.nextState = StartUp()
254 |
255 | def run(self):
256 | """
257 | This method sets things in motion. It performs some vital
258 | initialization tasks, and enters the main event loop.
259 | """
260 | pygame.init() # This is needed to initialize all the pygame modules
261 |
262 | # Decide whether to display the game in a window or to use the
263 | # full screen:
264 | flag = 0 # Default (window) mode
265 |
266 | if config.full_screen:
267 | flag = FULLSCREEN # Full screen mode
268 | screen_size = config.screen_size
269 | screen = pygame.display.set_mode(screen_size, flag)
270 |
271 | pygame.display.set_caption('Fruit Self Defense')
272 | pygame.mouse.set_visible(False)
273 |
274 | # The main loop:
275 | while True:
276 | # (1) If nextState has been changed, move to the new state, and
277 | # display it (for the first time):
278 | if self.state != self.nextState:
279 | self.state = self.nextState
280 | self.state.firstDisplay(screen)
281 | # (2) Delegate the event handling to the current state:
282 | for event in pygame.event.get():
283 | self.state.handle(event)
284 | # (3) Update the current state:
285 | self.state.update(self)
286 | # (4) Display the current state:
287 | self.state.display(screen)
288 |
289 |
290 | if __name__ == '__main__':
291 | game = Game(*sys.argv)
292 | game.run()
293 |
--------------------------------------------------------------------------------