├── 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 |
19 | Change name 20 | 21 |
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 | ![Cover image](9781590599822.jpg) 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 |
20 |
21 | 22 |
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 |
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('' % 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('' % 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, '

    %s

    ' % (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 | --------------------------------------------------------------------------------