├── 9781484200292.jpg
├── Chapter02
├── listing2-1.py
├── listing2-2.py
├── listing2-3.py
└── listing2-4.py
├── Chapter03
└── listing3-1.py
├── Chapter04
├── listing4-1.py
└── listing4-2.py
├── Chapter10
├── listing10-1.py
├── listing10-10.py
├── listing10-11.py
├── listing10-12.txt
├── listing10-13.txt
├── listing10-14.txt
├── listing10-2.py
├── listing10-3.py
├── listing10-4.py
├── listing10-5.py
├── listing10-6.py
└── listing10-8.py
├── Chapter11
├── listing11-1.py
├── listing11-10.py
├── listing11-11.py
├── listing11-12.py
├── listing11-13.py
├── listing11-2.txt
├── listing11-3.txt
├── listing11-4.txt
├── listing11-5.txt
├── listing11-6.py
├── listing11-7.py
├── listing11-8.py
└── listing11-9.py
├── Chapter12
└── listing12-1.py
├── Chapter13
├── listing13-1.py
└── listing13-2.py
├── Chapter14
├── listing14-1.py
├── listing14-2.py
├── listing14-3.py
├── listing14-4.py
├── listing14-5.py
├── listing14-6.py
├── listing14-7.py
├── listing14-8.py
└── listing14-9.py
├── Chapter15
├── listing15-1.py
├── listing15-2.py
├── listing15-3.py
├── listing15-4.py
├── listing15-5.py
├── listing15-6.py
└── listing15-7.py
├── Chapter16
├── listing16-1.py
├── listing16-2.py
└── listing16-3.py
├── Chapter17
├── listing17-1.java
├── listing17-2.cs
├── listing17-3.c
├── listing17-4.py
├── listing17-5.i
└── listing17-6.c
├── Chapter18
└── listing18-1.py
├── Chapter19
├── listing19-1.cfg
├── listing19-2.py
└── listing19-3.py
├── Chapter20
├── listing20-1.txt
├── listing20-2.py
├── listing20-3.py
├── listing20-4.py
├── listing20-5.py
└── listing20-6.py
├── Chapter21
├── listing21-1.py
├── listing21-2.py
└── listing21-3.py
├── Chapter22
├── listing22-1.xml
├── listing22-2.py
└── listing22-3.py
├── Chapter23
├── listing23-1.py
└── listing23-2.py
├── Chapter24
├── listing24-1.py
├── listing24-2.py
├── listing24-3.py
├── listing24-4.py
├── listing24-5.py
└── listing24-6.py
├── Chapter25
├── listing25-1.py
├── listing25-2.py
└── listing25-3.py
├── Chapter26
├── listing26-1.sql
├── listing26-2.sql
├── listing26-3.sql
├── listing26-4.py
├── listing26-5.py
├── listing26-6.py
├── listing26-7.py
└── listing26-8.py
├── Chapter27
├── listing27-1.py
├── listing27-2.py
└── listing27-3.py
├── Chapter28
├── listing28-1.py
└── listing28-2.py
├── Chapter29
├── listing29-1.py
├── listing29-2.py
├── listing29-3.py
└── listing29-4.py
├── LICENSE.txt
├── README.md
├── README.txt
├── contributing.md
└── errata.md
/9781484200292.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/beginning-python-3ed/bcff4b8f3ad2345eff8494cf955f656112e3c4cc/9781484200292.jpg
--------------------------------------------------------------------------------
/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 = input('Year: ')
24 | month = input('Month (1-12): ')
25 | day = 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)
--------------------------------------------------------------------------------
/Chapter02/listing2-2.py:
--------------------------------------------------------------------------------
1 | # Split up a URL of the form http://www.something.com
2 |
3 | url = input('Please enter the URL:')
4 | domain = url[11:-4]
5 |
6 | print("Domain name: " + domain)
--------------------------------------------------------------------------------
/Chapter02/listing2-3.py:
--------------------------------------------------------------------------------
1 | # Prints a sentence in a centered "box" of correct width
2 |
3 | sentence = input("Sentence: ")
4 |
5 | screen_width = 80
6 | text_width = len(sentence)
7 | box_width = text_width + 6
8 | left_margin = (screen_width - box_width) // 2
9 |
10 | print()
11 | print(' ' * left_margin + '+' + '-' * (box_width-2) + '+')
12 | print(' ' * left_margin + '| ' + ' ' * text_width + ' |')
13 | print(' ' * left_margin + '| ' + sentence + ' |')
14 | print(' ' * left_margin + '| ' + ' ' * text_width + ' |')
15 | print(' ' * left_margin + '+' + '-' * (box_width-2) + '+')
16 | print()
--------------------------------------------------------------------------------
/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 = input('User name: ')
11 | pin = input('PIN code: ')
12 |
13 | if [username, pin] in database: print('Access granted')
--------------------------------------------------------------------------------
/Chapter03/listing3-1.py:
--------------------------------------------------------------------------------
1 | # Print a formatted price list with a given width
2 |
3 | width = int(input('Please enter width: '))
4 |
5 | price_width = 10
6 | item_width = width - price_width
7 |
8 | header_fmt = '{{:{}}}{{:>{}}}'.format(item_width, price_width)
9 | fmt = '{{:{}}}{{:>{}.2f}}'.format(item_width, price_width)
10 |
11 | print('=' * width)
12 |
13 | print(header_fmt.format('Item', 'Price'))
14 |
15 | print('-' * width)
16 |
17 | print(fmt.format('Apples', 0.4))
18 | print(fmt.format('Pears', 0.5))
19 | print(fmt.format('Cantaloupes', 1.92))
20 | print(fmt.format('Dried Apricots (16 oz.)', 8))
21 | print(fmt.format('Prunes (4 lbs.)', 12))
22 |
23 | print('=' * width)
--------------------------------------------------------------------------------
/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 | people = {
7 |
8 | 'Alice': {
9 | 'phone': '2341',
10 | 'addr': 'Foo drive 23'
11 | },
12 |
13 | 'Beth': {
14 | 'phone': '9102',
15 | 'addr': 'Bar street 42'
16 | },
17 |
18 | 'Cecil': {
19 | 'phone': '3158',
20 | 'addr': 'Baz avenue 90'
21 | }
22 |
23 | }
24 |
25 | # Descriptive labels for the phone number and address. These will be used
26 | # when printing the output.
27 | labels = {
28 | 'phone': 'phone number',
29 | 'addr': 'address'
30 | }
31 |
32 | name = input('Name: ')
33 |
34 | # Are we looking for a phone number or an address?
35 | request = input('Phone number (p) or address (a)? ')
36 |
37 | # Use the correct key:
38 | if request == 'p': key = 'phone'
39 | if request == 'a': key = 'addr'
40 |
41 | # Only try to print information if the name is a valid key in
42 | # our dictionary:
43 | if name in people: print("{}'s {} is {}.".format(name, labels[key], people[name][key]))
--------------------------------------------------------------------------------
/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 = input('Name: ')
11 |
12 | # Are we looking for a phone number or an address?
13 | request = 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 {} is {}.".format(name, label, result))
--------------------------------------------------------------------------------
/Chapter10/listing10-1.py:
--------------------------------------------------------------------------------
1 | # hello.py
2 | print("Hello, world!")
--------------------------------------------------------------------------------
/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))
--------------------------------------------------------------------------------
/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 | # We'll collect variables in this:
9 | scope = {}
10 |
11 | # This is used in re.sub:
12 | def replacement(match):
13 | code = match.group(1)
14 | try:
15 | # If the field can be evaluated, return it:
16 | return str(eval(code, scope))
17 | except SyntaxError:
18 | # Otherwise, execute the assignment in the same scope ...
19 | exec code in scope
20 | # ... and return an empty string:
21 | return ''
22 |
23 | # Get all the text as a single string:
24 |
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))
--------------------------------------------------------------------------------
/Chapter10/listing10-12.txt:
--------------------------------------------------------------------------------
1 | [x = 2]
2 | [y = 3]
3 | The sum of [x] and [y] is [x + y].
--------------------------------------------------------------------------------
/Chapter10/listing10-13.txt:
--------------------------------------------------------------------------------
1 | [name = 'Magnus Lie Hetland' ]
2 | [email = 'magnus@foo.bar' ]
3 | [language = 'python' ]
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/Chapter10/listing10-2.py:
--------------------------------------------------------------------------------
1 | # hello2.py
2 | def hello():
3 | print("Hello, world!")
--------------------------------------------------------------------------------
/Chapter10/listing10-3.py:
--------------------------------------------------------------------------------
1 | # hello3.py
2 |
3 | def hello():
4 | print("Hello, world!")
5 |
6 | # A test:
7 | hello()
--------------------------------------------------------------------------------
/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()
--------------------------------------------------------------------------------
/Chapter10/listing10-5.py:
--------------------------------------------------------------------------------
1 | # reverseargs.py
2 | import sys
3 | args = sys.argv[1:]
4 | args.reverse()
5 | print(' '.join(args))
--------------------------------------------------------------------------------
/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('{:<50} # {:2d}'.format(line, num))
--------------------------------------------------------------------------------
/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 = input('Enter unique ID number: ')
9 | person = {}
10 | person['name'] = input('Enter name: ')
11 | person['age'] = input('Enter age: ')
12 | person['phone'] = input('Enter phone number: ')
13 | db[pid] = person
14 |
15 | def lookup_person(db):
16 | """
17 | Query user for ID and desired field, and fetch the corresponding data from
18 | the shelf object
19 | """
20 | pid = input('Enter ID number: ')
21 | field = input('What would you like to know? (name, age, phone) ')
22 | field = field.strip().lower()
23 |
24 | print(field.capitalize() + ':', db[pid][field])
25 |
26 | def print_help():
27 | print('The available commands are:')
28 | print('store : Stores information about a person')
29 | print('lookup : Looks up a person from ID number')
30 | print('quit : Save changes and exit')
31 | print('? : Prints this message')
32 |
33 | def enter_command():
34 | cmd = input('Enter command (? for help): ')
35 | cmd = cmd.strip().lower()
36 | return cmd
37 |
38 | def main():
39 | database = shelve.open('C:\\database.dat') # You may want to change this name
40 | try:
41 | while True:
42 | cmd = enter_command()
43 | if cmd == 'store':
44 | store_person(database)
45 | elif cmd == 'lookup':
46 | lookup_person(database)
47 | elif cmd == '?':
48 | print_help()
49 | elif cmd == 'quit':
50 | return
51 | finally:
52 | database.close()
53 |
54 | if name == '__main__': main()
--------------------------------------------------------------------------------
/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)
--------------------------------------------------------------------------------
/Chapter11/listing11-10.py:
--------------------------------------------------------------------------------
1 | with open(filename) as f:
2 | for line in f.readlines():
3 | process(line)
--------------------------------------------------------------------------------
/Chapter11/listing11-11.py:
--------------------------------------------------------------------------------
1 | import fileinput
2 | for line in fileinput.input(filename):
3 | process(line)
--------------------------------------------------------------------------------
/Chapter11/listing11-12.py:
--------------------------------------------------------------------------------
1 | with open(filename) as f:
2 | for line in f:
3 | process(line)
--------------------------------------------------------------------------------
/Chapter11/listing11-13.py:
--------------------------------------------------------------------------------
1 | for line in open(filename):
2 | process(line)
--------------------------------------------------------------------------------
/Chapter11/listing11-2.txt:
--------------------------------------------------------------------------------
1 | Your mother was a hamster and your
2 | father smelled of elderberries.
--------------------------------------------------------------------------------
/Chapter11/listing11-3.txt:
--------------------------------------------------------------------------------
1 | Welcome to this file
2 | There is nothing here except
3 | This stupid haiku
--------------------------------------------------------------------------------
/Chapter11/listing11-4.txt:
--------------------------------------------------------------------------------
1 | this
2 | is no
3 | haiku
--------------------------------------------------------------------------------
/Chapter11/listing11-5.txt:
--------------------------------------------------------------------------------
1 | this
2 | isn't a
3 | haiku
--------------------------------------------------------------------------------
/Chapter11/listing11-6.py:
--------------------------------------------------------------------------------
1 | with open(filename) as f:
2 | char = f.read(1)
3 | while char:
4 | process(char)
5 | char = f.read(1)
--------------------------------------------------------------------------------
/Chapter11/listing11-7.py:
--------------------------------------------------------------------------------
1 | with open(filename) as f:
2 | while True:
3 | char = f.read(1)
4 | if not char: break
5 | process(char)
--------------------------------------------------------------------------------
/Chapter11/listing11-8.py:
--------------------------------------------------------------------------------
1 | with open(filename) as f:
2 | while True:
3 | line = f.readline()
4 | if not line: break
5 | process(line)
--------------------------------------------------------------------------------
/Chapter11/listing11-9.py:
--------------------------------------------------------------------------------
1 | with open(filename) as f:
2 | for char in f.read():
3 | process(char)
--------------------------------------------------------------------------------
/Chapter12/listing12-1.py:
--------------------------------------------------------------------------------
1 | from tkinter import *
2 | from tkinter.scrolledtext import ScrolledText
3 |
4 | def load():
5 | with open(filename.get()) as file:
6 | contents.delete('1.0', END)
7 | contents.insert(INSERT, file.read())
8 |
9 | def save():
10 | with open(filename.get(), 'w') as file:
11 | file.write(contents.get('1.0', END))
12 |
13 | top = Tk()
14 | top.title("Simple Editor")
15 |
16 | contents = ScrolledText()
17 | contents.pack(side=BOTTOM, expand=True, fill=BOTH)
18 |
19 | filename = Entry()
20 | filename.pack(side=LEFT, expand=True, fill=X)
21 |
22 | Button(text='Open', command=load).pack(side=LEFT)
23 | Button(text='Save', command=save).pack(side=LEFT)
24 |
25 | mainloop()
--------------------------------------------------------------------------------
/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 | field_count = 10
30 |
31 | for line in open('ABBREV.txt'):
32 | fields = line.split('^')
33 | vals = [convert(f) for f in fields[:field_count]]
34 | curs.execute(query, vals)
35 |
36 | conn.commit()
37 | conn.close()
--------------------------------------------------------------------------------
/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 ' + 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('{}: {}'.format(*pair))
13 | print()
--------------------------------------------------------------------------------
/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()
--------------------------------------------------------------------------------
/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))
--------------------------------------------------------------------------------
/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()
--------------------------------------------------------------------------------
/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()
--------------------------------------------------------------------------------
/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()
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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()
--------------------------------------------------------------------------------
/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()
--------------------------------------------------------------------------------
/Chapter15/listing15-1.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | import re
3 | p = re.compile('(.*?)')
4 | text = urlopen('http://python.org/jobs').read().decode()
5 | for url, name in p.findall(text):
6 | print('{} ({})'.format(name, url))
--------------------------------------------------------------------------------
/Chapter15/listing15-2.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from html.parser import HTMLParser
3 |
4 | def isjob(url):
5 | try:
6 | a, b, c, d = url.split('/')
7 | except ValueError:
8 | return False
9 | return a == d == '' and b == 'jobs' and c.isdigit()
10 |
11 | class Scraper(HTMLParser):
12 |
13 | in_link = False
14 |
15 | def handle_starttag(self, tag, attrs):
16 | attrs = dict(attrs)
17 | url = attrs.get('href', '')
18 | if tag == 'a' and isjob(url):
19 | self.url = url
20 | self.in_link = True
21 | self.chunks = []
22 |
23 | def handle_data(self, data):
24 | if self.in_link:
25 | self.chunks.append(data)
26 |
27 | def handle_endtag(self, tag):
28 | if tag == 'a' and self.in_link:
29 | print('{} ({})'.format(''.join(self.chunks), self.url))
30 | self.in_link = False
31 |
32 | text = urlopen('http://python.org/jobs').read().decode()
33 | parser = Scraper()
34 | parser.feed(text)
35 | parser.close()
--------------------------------------------------------------------------------
/Chapter15/listing15-3.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from bs4 import BeautifulSoup
3 |
4 | text = urlopen('http://python.org/jobs').read()
5 | soup = BeautifulSoup(text, 'html.parser')
6 |
7 | jobs = set()
8 | for job in soup.body.section('h2'):
9 | jobs.add('{} ({})'.format(job.a.string, job.a['href']))
10 |
11 | print('\n'.join(sorted(jobs, key=str.lower)))
--------------------------------------------------------------------------------
/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!')
--------------------------------------------------------------------------------
/Chapter15/listing15-5.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import cgitb; cgitb.enable()
4 |
5 | print('Content-type: text/html\n')
6 |
7 | print(1/0)
8 |
9 | print('Hello, world!')
--------------------------------------------------------------------------------
/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\n')
9 |
10 | print('Hello, {}!'.format(name))
--------------------------------------------------------------------------------
/Chapter15/listing15-7.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import cgi
4 | form = cgi.FieldStorage()
5 |
6 | name = form.getvalue('name', 'world')
7 |
8 | print("""Content-type: text/html
9 |
10 |
11 |
12 | Greeting Page
13 |
14 |
15 |
Hello, %s!
16 |
17 |
18 |
22 |
23 |
24 | """.format(name))
--------------------------------------------------------------------------------
/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 ')
--------------------------------------------------------------------------------
/Chapter16/listing16-2.py:
--------------------------------------------------------------------------------
1 | import unittest, my_math
2 |
3 | class ProductTestCase(unittest.TestCase):
4 |
5 | def test_integers(self):
6 | for x in range(-10, 10):
7 | for y in range(-10, 10):
8 | p = my_math.product(x, y)
9 | self.assertEqual(p, x * y, 'Integer multiplication failed')
10 |
11 | def test_floats(self):
12 | for x in range(-10, 10):
13 | for y in range(-10, 10):
14 | x = x / 10
15 | y = y / 10
16 | p = my_math.product(x, y)
17 | self.assertEqual(p, x * y, 'Float multiplication failed')
18 |
19 | if __name__ == '__main__': unittest.main()
--------------------------------------------------------------------------------
/Chapter16/listing16-3.py:
--------------------------------------------------------------------------------
1 | import unittest, my_math
2 | from subprocess import Popen, PIPE
3 |
4 | class ProductTestCase(unittest.TestCase):
5 |
6 | # Insert previous tests here
7 |
8 | def test_with_PyChecker(self):
9 | cmd = 'pychecker', '-Q', my_math.__file__.rstrip('c')
10 | pychecker = Popen(cmd, stdout=PIPE, stderr=PIPE)
11 | self.assertEqual(pychecker.stdout.read(), '')
12 |
13 | def test_with_PyLint(self):
14 | cmd = 'pylint', '-rn', 'my_math'
15 | pylint = Popen(cmd, stdout=PIPE, stderr=PIPE)
16 | self.assertEqual(pylint.stdout.read(), '')
17 |
18 | if __name__ == '__main__': unittest.main()
--------------------------------------------------------------------------------
/Chapter17/listing17-1.java:
--------------------------------------------------------------------------------
1 | public class JythonTest {
2 |
3 | public void greeting() {
4 | System.out.println("Hello, world!");
5 | }
6 |
7 | }
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/Chapter17/listing17-5.i:
--------------------------------------------------------------------------------
1 | %module palindrome
2 |
3 | %{
4 | #include
5 | %}
6 |
7 | extern int is_palindrome(char *text);
--------------------------------------------------------------------------------
/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 |
27 | /* name, function, argument type, docstring */
28 | {"is_palindrome", is_palindrome, METH_VARARGS, "Detect palindromes"},
29 | /* An end-of-listing sentinel: */
30 | {NULL, NULL, 0, NULL}
31 |
32 | };
33 |
34 | static struct PyModuleDef palindrome =
35 | {
36 | PyModuleDef_HEAD_INIT,
37 | "palindrome", /* module name */
38 | "", /* docstring */
39 | -1, /* signals state kept in global variables */
40 | PalindromeMethods
41 | };
42 |
43 |
44 | /* An initialization function for the module: */
45 | PyMODINIT_FUNC PyInit_palindrome(void)
46 | {
47 | return PyModule_Create(&palindrome);
48 | }
--------------------------------------------------------------------------------
/Chapter18/listing18-1.py:
--------------------------------------------------------------------------------
1 | from setuptools 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'])
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/Chapter19/listing19-2.py:
--------------------------------------------------------------------------------
1 | from configparser import ConfigParser
2 |
3 | CONFIGFILE = "area.ini"
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['messages'].get('greeting'))
12 |
13 | # Read in the radius, using a question from the config file:
14 | radius = float(input(config['messages'].get('question') + ' '))
15 |
16 | # Print a result message from the config file;
17 | # end with a space to stay on same line:
18 | print(config['messages'].get('result_message'), end=' ')
19 |
20 | # getfloat() converts the config value to a float:
21 | print(config['numbers'].getfloat('pi') * radius**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')
--------------------------------------------------------------------------------
/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).
--------------------------------------------------------------------------------
/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 = []
--------------------------------------------------------------------------------
/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('')
--------------------------------------------------------------------------------
/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 '{}'.format(match.group(1))
59 | def sub_url(self, match):
60 | return '{}'.format(match.group(1), match.group(1))
61 | def sub_mail(self, match):
62 | return '{}'.format(match.group(1), match.group(1))
63 | def feed(self, data):
64 | print(data)
--------------------------------------------------------------------------------
/Chapter20/listing20-5.py:
--------------------------------------------------------------------------------
1 | class Rule:
2 | """
3 | Base class for all rules.
4 | """
5 |
6 | def action(self, block, handler):
7 | handler.start(self.type)
8 | handler.feed(block)
9 | handler.end(self.type)
10 | return True
11 |
12 | class HeadingRule(Rule):
13 | """
14 | A heading is a single line that is at most 70 characters and
15 | that doesn't end with a colon.
16 | """
17 | type = 'heading'
18 | def condition(self, block):
19 | return not '\n' in block and len(block) <= 70 and not block[-1] == ':'
20 |
21 | class TitleRule(HeadingRule):
22 | """
23 | The title is the first block in the document, provided that
24 | it is a heading.
25 | """
26 | type = 'title'
27 | first = True
28 |
29 | def condition(self, block):
30 | if not self.first: return False
31 | self.first = False
32 | return HeadingRule.condition(self, block)
33 |
34 | class ListItemRule(Rule):
35 | """
36 | A list item is a paragraph that begins with a hyphen. As part of the
37 | formatting, the hyphen is removed.
38 | """
39 | type = 'listitem'
40 | def condition(self, block):
41 | return block[0] == '-'
42 | def action(self, block, handler):
43 | handler.start(self.type)
44 | handler.feed(block[1:].strip())
45 | handler.end(self.type)
46 | return True
47 |
48 | class ListRule(ListItemRule):
49 | """
50 | A list begins between a block that is not a list item and a
51 | subsequent list item. It ends after the last consecutive list 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 other rules.
69 | """
70 | type = 'paragraph'
71 | def condition(self, block):
72 | return True
--------------------------------------------------------------------------------
/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 handler.
9 | """
10 | def __init__(self, handler):
11 | self.handler = handler
12 | self.rules = []
13 | self.filters = []
14 | def addRule(self, rule):
15 | self.rules.append(rule)
16 | def addFilter(self, pattern, name):
17 | def filter(block, handler):
18 | return re.sub(pattern, handler.sub(name), block)
19 | self.filters.append(filter)
20 |
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,
29 | self.handler)
30 | if last: break
31 | self.handler.end('document')
32 |
33 | class BasicTextParser(Parser):
34 | """
35 | A specific Parser that adds rules and filters in its 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)
--------------------------------------------------------------------------------
/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')
--------------------------------------------------------------------------------
/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(list(zip(times, pred)), strokeColor=colors.blue))
27 | drawing.add(PolyLine(list(zip(times, high)), strokeColor=colors.red))
28 | drawing.add(PolyLine(list(zip(times, low)), strokeColor=colors.green))
29 |
30 | drawing.add(String(65, 115, 'Sunspots', fontSize=18, fillColor=colors.red))
31 | renderPDF.drawToFile(drawing, 'report1.pdf', 'Sunspots')
--------------------------------------------------------------------------------
/Chapter21/listing21-3.py:
--------------------------------------------------------------------------------
1 | from urllib.request 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 = 'ftp://ftp.swpc.noaa.gov/pub/weekly/Predict.txt'
8 | COMMENT_CHARS = '#:'
9 |
10 |
11 | drawing = Drawing(400, 200)
12 | data = []
13 | for line in urlopen(URL).readlines():
14 | line = line.decode()
15 | if not line.isspace() and line[0] not in COMMENT_CHARS:
16 | data.append([float(n) for n in line.split()])
17 |
18 | pred = [row[2] for row in data]
19 | high = [row[3] for row in data]
20 | low = [row[4] for row in data]
21 | times = [row[0] + row[1]/12.0 for row in data]
22 |
23 | lp = LinePlot()
24 | lp.x = 50
25 | lp.y = 50
26 | lp.height = 125
27 | lp.width = 300
28 | lp.data = [list(zip(times, pred)),
29 | list(zip(times, high)),
30 | list(zip(times, low))]
31 | lp.lines[0].strokeColor = colors.blue
32 | lp.lines[1].strokeColor = colors.red
33 | lp.lines[2].strokeColor = colors.green
34 |
35 | drawing.add(lp)
36 |
37 | drawing.add(String(250, 150, 'Sunspots',
38 | fontSize=14, fillColor=colors.red))
39 |
40 |
41 | renderPDF.drawToFile(drawing, 'report2.pdf', 'Sunspots')
--------------------------------------------------------------------------------
/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:
43 |
44 | Back to the main page
45 |
46 | s
47 | """)
--------------------------------------------------------------------------------
/Chapter27/listing27-1.py:
--------------------------------------------------------------------------------
1 | from xmlrpc.client import ServerProxy
2 | from os.path import join, isfile
3 | from xmlrpc.server import SimpleXMLRPCServer
4 | from urllib.parse import urlparse
5 | import sys
6 |
7 | MAX_HISTORY_LENGTH = 6
8 |
9 | OK = 1
10 | FAIL = 2
11 | EMPTY = ''
12 |
13 | def get_port(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 | def _start(self):
66 | """
67 | Used internally to start the XML-RPC server.
68 | """
69 | s = SimpleXMLRPCServer(("", get_port(self.url)), logRequests=False)
70 | s.register_instance(self)
71 | s.serve_forever()
72 |
73 | def _handle(self, query):
74 | """
75 | Used internally to handle queries.
76 | """
77 | dir = self.dirname
78 | name = join(dir, query)
79 | if not isfile(name): return FAIL, EMPTY
80 | return OK, open(name).read()
81 |
82 | def _broadcast(self, query, history):
83 | """
84 | Used internally to broadcast a query to all known Nodes.
85 | """
86 | for other in self.known.copy():
87 | if other in history: continue
88 | try:
89 | s = ServerProxy(other)
90 | code, data = s.query(query, history)
91 | if code == OK:
92 | return code, data
93 | except:
94 | self.known.remove(other)
95 | return FAIL, EMPTY
96 |
97 | def main():
98 | url, directory, secret = sys.argv[1:]
99 | n = Node(url, directory, secret)
100 | n._start()
101 |
102 | if __name__ == '__main__': main()
--------------------------------------------------------------------------------
/Chapter27/listing27-2.py:
--------------------------------------------------------------------------------
1 | from xmlrpc.client import ServerProxy, Fault
2 | from os.path import join, abspath, isfile
3 | from xmlrpc.server import SimpleXMLRPCServer
4 | from urllib.parse 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 | super().__init__(UNHANDLED, message)
20 |
21 | class AccessDenied(Fault):
22 | """
23 | An exception that is raised if a user tries to access a
24 | resource for which he or she is not authorized.
25 | """
26 | def __init__(self, message="Access denied"):
27 | super().__init__(ACCESS_DENIED, message)
28 |
29 | def inside(dir, name):
30 | """
31 | Checks whether a given file name lies within a given directory.
32 | """
33 | dir = abspath(dir)
34 | name = abspath(name)
35 | return name.startswith(join(dir, ''))
36 |
37 | def get_port(url):
38 | """
39 | Extracts the port number from a URL.
40 | """
41 | name = urlparse(url)[1]
42 | parts = name.split(':')
43 | return int(parts[-1])
44 |
45 | class Node:
46 | """
47 | A node in a peer-to-peer network.
48 | """
49 | def __init__(self, url, dirname, secret):
50 | self.url = url
51 | self.dirname = dirname
52 | self.secret = secret
53 | self.known = set()
54 |
55 | def query(self, query, history=[]):
56 | """
57 | Performs a query for a file, possibly asking other known Nodes for
58 | help. Returns the file as a string.
59 | """
60 | try:
61 | return self._handle(query)
62 | except UnhandledQuery:
63 | history = history + [self.url]
64 | if len(history) >= MAX_HISTORY_LENGTH: raise
65 | return self._broadcast(query, history)
66 |
67 | def hello(self, other):
68 | """
69 | Used to introduce the Node to other Nodes.
70 | """
71 | self.known.add(other)
72 | return 0
73 |
74 | def fetch(self, query, secret):
75 | """
76 | Used to make the Node find a file and download it.
77 | """
78 | if secret != self.secret: raise AccessDenied
79 | result = self.query(query)
80 | f = open(join(self.dirname, query), 'w')
81 | f.write(result)
82 | f.close()
83 | return 0
84 |
85 | def _start(self):
86 | """
87 | Used internally to start the XML-RPC server.
88 | """
89 | s = SimpleXMLRPCServer(("", get_port(self.url)), logRequests=False)
90 | s.register_instance(self)
91 | s.serve_forever()
92 |
93 | def _handle(self, query):
94 | """
95 | Used internally to handle queries.
96 | """
97 | dir = self.dirname
98 | name = join(dir, query)
99 | if not isfile(name): raise UnhandledQuery
100 | if not inside(dir, name): raise AccessDenied
101 | return open(name).read()
102 |
103 | def _broadcast(self, query, history):
104 | """
105 | Used internally to broadcast a query to all known Nodes.
106 | """
107 | for other in self.known.copy():
108 | if other in history: continue
109 | try:
110 | s = ServerProxy(other)
111 | return s.query(query, history)
112 | except Fault as f:
113 | if f.faultCode == UNHANDLED: pass
114 | else: self.known.remove(other)
115 | except:
116 | self.known.remove(other)
117 | raise UnhandledQuery
118 |
119 | def main():
120 | url, directory, secret = sys.argv[1:]
121 | n = Node(url, directory, secret)
122 | n._start()
123 |
124 | if __name__ == '__main__': main()
--------------------------------------------------------------------------------
/Chapter27/listing27-3.py:
--------------------------------------------------------------------------------
1 | from xmlrpc.client import ServerProxy, Fault
2 | from cmd import Cmd
3 | from random import choice
4 | from string import ascii_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 random_string(length):
14 | """
15 | Returns a random string of letters with the given length.
16 | """
17 | chars = []
18 | letters = ascii_lowercase[:26]
19 |
20 | while length > 0:
21 | length -= 1
22 | chars.append(choice(letters))
23 | return ''.join(chars)
24 |
25 | class Client(Cmd):
26 | """
27 | A simple text-based interface to the Node class.
28 | """
29 |
30 | prompt = '> '
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 = random_string(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 as 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()
--------------------------------------------------------------------------------
/Chapter28/listing28-1.py:
--------------------------------------------------------------------------------
1 | from xmlrpc.client import ServerProxy, Fault
2 | from server import Node, UNHANDLED
3 | from client import random_string
4 | from threading import Thread
5 | from time import sleep
6 | from os import listdir
7 | import sys
8 | import tkinter as tk
9 |
10 | HEAD_START = 0.1 # Seconds
11 | SECRET_LENGTH = 100
12 |
13 | class Client(tk.Frame):
14 |
15 | def __init__(self, master, url, dirname, urlfile):
16 | super().__init__(master)
17 | self.node_setup(url, dirname, urlfile)
18 | self.pack()
19 | self.create_widgets()
20 |
21 | def node_setup(self, url, dirname, urlfile):
22 | self.secret = random_string(SECRET_LENGTH)
23 | n = Node(url, dirname, self.secret)
24 | t = Thread(target=n._start)
25 | t.setDaemon(1)
26 | t.start()
27 | # Give the server a head start:
28 | sleep(HEAD_START)
29 | self.server = ServerProxy(url)
30 | for line in open(urlfile):
31 | line = line.strip()
32 | self.server.hello(line)
33 |
34 | def create_widgets(self):
35 | self.input = input = tk.Entry(self)
36 | input.pack(side='left')
37 |
38 | self.submit = submit = tk.Button(self)
39 | submit['text'] = "Fetch"
40 | submit['command'] = self.fetch_handler
41 | submit.pack()
42 |
43 | def fetch_handler(self):
44 | query = self.input.get()
45 | try:
46 | self.server.fetch(query, self.secret)
47 | except Fault as f:
48 | if f.faultCode != UNHANDLED: raise
49 | print("Couldn't find the file", query)
50 |
51 | def main():
52 | urlfile, directory, url = sys.argv[1:]
53 |
54 | root = tk.Tk()
55 | root.title("File Sharing Client")
56 |
57 | client = Client(root, url, directory, urlfile)
58 | client.mainloop()
59 |
60 | if __name__ == "__main__": main()
--------------------------------------------------------------------------------
/Chapter28/listing28-2.py:
--------------------------------------------------------------------------------
1 | from xmlrpc.client import ServerProxy, Fault
2 | from server import Node, UNHANDLED
3 | from client import random_string
4 | from threading import Thread
5 | from time import sleep
6 | from os import listdir
7 | import sys
8 | import tkinter as tk
9 |
10 | HEAD_START = 0.1 # Seconds
11 | SECRET_LENGTH = 100
12 |
13 | class ListableNode(Node):
14 |
15 | def list(self):
16 | return listdir(self.dirname)
17 |
18 | class Client(tk.Frame):
19 |
20 | def __init__(self, master, url, dirname, urlfile):
21 | super().__init__(master)
22 | self.node_setup(url, dirname, urlfile)
23 | self.pack()
24 | self.create_widgets()
25 |
26 | def node_setup(self, url, dirname, urlfile):
27 | self.secret = random_string(SECRET_LENGTH)
28 | n = ListableNode(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 | def create_widgets(self):
40 | self.input = input = tk.Entry(self)
41 | input.pack(side='left')
42 |
43 | self.submit = submit = tk.Button(self)
44 | submit['text'] = "Fetch"
45 | submit['command'] = self.fetch_handler
46 | submit.pack()
47 |
48 | self.files = files = tk.Listbox()
49 | files.pack(side='bottom', expand=True, fill=tk.BOTH)
50 | self.update_list()
51 |
52 | def fetch_handler(self):
53 | query = self.input.get()
54 | try:
55 | self.server.fetch(query, self.secret)
56 | self.update_list()
57 | except Fault as f:
58 | if f.faultCode != UNHANDLED: raise
59 | print("Couldn't find the file", query)
60 |
61 | def update_list(self):
62 | self.files.delete(0, tk.END)
63 | self.files.insert(tk.END, self.server.list())
64 |
65 | def main():
66 | urlfile, directory, url = sys.argv[1:]
67 |
68 | root = tk.Tk()
69 | root.title("File Sharing Client")
70 |
71 | client = Client(root, url, directory, urlfile)
72 | client.mainloop()
73 |
74 | if __name__ == '__main__': main()
--------------------------------------------------------------------------------
/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, speed):
8 | pygame.sprite.Sprite.__init__(self)
9 | self.speed = speed
10 | # image and rect used when drawing sprite:
11 | self.image = weight_image
12 | self.rect = self.image.get_rect()
13 | self.reset()
14 |
15 | def reset(self):
16 | """
17 | Move the weight to a random position at the top of the screen.
18 | """
19 | self.rect.top = -self.rect.height
20 | self.rect.centerx = randrange(screen_size[0])
21 |
22 | def update(self):
23 | """
24 | Update the weight for display in the next frame.
25 | """
26 | self.rect.top += self.speed
27 |
28 | if self.rect.top > screen_size[1]:
29 | self.reset()
30 |
31 | # Initialize things
32 | pygame.init()
33 | screen_size = 800, 600
34 | pygame.display.set_mode(screen_size, FULLSCREEN)
35 | pygame.mouse.set_visible(0)
36 |
37 | # Load the weight image
38 | weight_image = pygame.image.load('weight.png')
39 | weight_image = weight_image.convert() # ... to match the display
40 |
41 | # You might want a different speed, of courase
42 | speed = 5
43 |
44 | # Create a sprite group and add a Weight
45 | sprites = pygame.sprite.RenderUpdates()
46 | sprites.add(Weight(speed))
47 |
48 | # Get the screen surface and fill it
49 | screen = pygame.display.get_surface()
50 | bg = (255, 255, 255) # White
51 | screen.fill(bg)
52 | pygame.display.flip()
53 |
54 | # Used to erase the sprites:
55 | def clear_callback(surf, rect):
56 | surf.fill(bg, rect)
57 |
58 | while True:
59 | # Check for quit events:
60 | for event in pygame.event.get():
61 | if event.type == QUIT:
62 | sys.exit()
63 | if event.type == KEYDOWN and event.key == K_ESCAPE:
64 | sys.exit()
65 | # Erase previous positions:
66 | sprites.clear(screen, clear_callback)
67 | # Update all sprites:
68 | sprites.update()
69 | # Draw all sprites:
70 | updates = sprites.draw(screen)
71 | # Update the necessary parts of the display:
72 | pygame.display.update(updates)
--------------------------------------------------------------------------------
/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 = 1
22 | banana_speed = 10
23 | speed_increase = 1
24 | weights_per_level = 10
25 | banana_pad_top = 40
26 | banana_pad_side = 20
--------------------------------------------------------------------------------
/Chapter29/listing29-3.py:
--------------------------------------------------------------------------------
1 | import pygame, config, os
2 | from random import randrange
3 |
4 | "This module contains the game objects of the Squish game."
5 |
6 | class SquishSprite(pygame.sprite.Sprite):
7 |
8 | """
9 | Generic superclass for all sprites in Squish. The constructor
10 | takes care of loading an image, setting up the sprite rect, and
11 | the area within which it is allowed to move. That area is governed
12 | by the screen size and the margin.
13 | """
14 |
15 | def __init__(self, image):
16 | super().__init__()
17 | self.image = pygame.image.load(image).convert()
18 | self.rect = self.image.get_rect()
19 | screen = pygame.display.get_surface()
20 | shrink = -config.margin * 2
21 | self.area = screen.get_rect().inflate(shrink, shrink)
22 |
23 | class Weight(SquishSprite):
24 |
25 | """
26 | A falling weight. It uses the SquishSprite constructor to set up
27 | its weight image, and will fall with a speed given as a parameter
28 | to its constructor.
29 | """
30 |
31 | def __init__(self, speed):
32 | super().__init__(config.weight_image)
33 | self.speed = speed
34 | self.reset()
35 |
36 | def reset(self):
37 | """
38 | Move the weight to the top of the screen (just out of sight)
39 | and place it at a random horizontal position.
40 | """
41 | x = randrange(self.area.left, self.area.right)
42 | self.rect.midbottom = x, 0
43 |
44 | def update(self):
45 | """
46 | Move the weight vertically (downwards) a distance
47 | corresponding to its speed. Also set the landed attribute
48 | according to whether it has reached the bottom of the screen.
49 | """
50 | self.rect.top += self.speed
51 | self.landed = self.rect.top >= self.area.bottom
52 |
53 | class Banana(SquishSprite):
54 |
55 | """
56 | A desperate banana. It uses the SquishSprite constructor to set up
57 | its banana image, and will stay near the bottom of the screen,
58 | with its horizontal position governed by the current mouse
59 | position (within certain limits).
60 | """
61 |
62 | def __init__(self):
63 | super().__init__(config.banana_image)
64 | self.rect.bottom = self.area.bottom
65 | # These paddings represent parts of the image where there is
66 | # no banana. If a weight moves into these areas, it doesn't
67 | # constitute a hit (or, rather, a squish):
68 | self.pad_top = config.banana_pad_top
69 | self.pad_side = config.banana_pad_side
70 |
71 | def update(self):
72 | """
73 | Set the Banana's center x-coordinate to the current mouse
74 | x-coordinate, and then use the rect method clamp to ensure
75 | that the Banana stays within its allowed range of motion.
76 | """
77 | self.rect.centerx = pygame.mouse.get_pos()[0]
78 | self.rect = self.rect.clamp(self.area)
79 |
80 | def touches(self, other):
81 | """
82 | Determines whether the banana touches another sprite (e.g., a
83 | Weight). Instead of just using the rect method colliderect, a
84 | new rectangle is first calculated (using the rect method
85 | inflate with the side and top paddings) that does not include
86 | the 'empty' areas on the top and sides of the banana.
87 | """
88 | # Deflate the bounds with the proper padding:
89 | bounds = self.rect.inflate(-self.pad_side, -self.pad_top)
90 | # Move the bounds so they are placed at the bottom of the Banana:
91 | bounds.bottom = self.rect.bottom
92 | # Check whether the bounds intersect with the other object's rect:
93 | return bounds.colliderect(other.rect)
--------------------------------------------------------------------------------
/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 first_display(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 | A game level. Takes care of counting how many weights have been
43 | dropped, moving the sprites around, and other tasks relating to
44 | game logic.
45 | """
46 |
47 | def __init__(self, number=1):
48 | self.number = number
49 | # How many weights remain to dodge in this level?
50 | self.remaining = config.weights_per_level
51 |
52 | speed = config.drop_speed
53 | # One speed_increase added for each level above 1:
54 | speed += (self.number-1) * config.speed_increase
55 | # Create the weight and banana:
56 | self.weight = objects.Weight(speed)
57 | self.banana = objects.Banana()
58 | both = self.weight, self.banana # This could contain more sprites...
59 | self.sprites = pygame.sprite.RenderUpdates(both)
60 |
61 | def update(self, game):
62 | "Updates the game state from the previous frame."
63 | # Update all sprites:
64 | self.sprites.update()
65 | # If the banana touches the weight, tell the game to switch to
66 | # a GameOver state:
67 | if self.banana.touches(self.weight):
68 | game.next_state = GameOver()
69 | # Otherwise, if the weight has landed, reset it. If all the
70 | # weights of this level have been dodged, tell the game to
71 | # switch to a LevelCleared state:
72 | elif self.weight.landed:
73 | self.weight.reset()
74 | self.remaining -= 1
75 | if self.remaining == 0:
76 | game.next_state = LevelCleared(self.number)
77 |
78 | def display(self, screen):
79 | """
80 | Displays the state after the first display (which simply wipes
81 | the screen). As opposed to firstDisplay, this method uses
82 | pygame.display.update with a list of rectangles that need to
83 | be updated, supplied from self.sprites.draw.
84 | """
85 | screen.fill(config.background_color)
86 | updates = self.sprites.draw(screen)
87 | pygame.display.update(updates)
88 |
89 |
90 | class Paused(State):
91 | """
92 | A simple, paused game state, which may be broken out of by pressing
93 | either a keyboard key or the mouse button.
94 | """
95 |
96 | finished = 0 # Has the user ended the pause?
97 | image = None # Set this to a file name if you want an image
98 | text = '' # Set this to some informative text
99 |
100 | def handle(self, event):
101 | """
102 | Handles events by delegating to State (which handles quitting
103 | in general) and by reacting to key presses and mouse
104 | clicks. If a key is pressed or the mouse is clicked,
105 | self.finished is set to true.
106 | """
107 | State.handle(self, event)
108 | if event.type in [MOUSEBUTTONDOWN, KEYDOWN]:
109 | self.finished = 1
110 |
111 | def update(self, game):
112 | """
113 | Update the level. If a key has been pressed or the mouse has
114 | been clicked (i.e., self.finished is true), tell the game to
115 | move to the state represented by self.next_state() (should be
116 | implemented by subclasses).
117 | """
118 | if self.finished:
119 | game.next_state = self.next_state()
120 |
121 | def first_display(self, screen):
122 | """
123 | The first time the Paused state is displayed, draw the image
124 | (if any) and render the text.
125 | """
126 | # First, clear the screen by filling it with the background color:
127 | screen.fill(config.background_color)
128 |
129 | # Create a Font object with the default appearance, and specified size:
130 | font = pygame.font.Font(None, config.font_size)
131 |
132 | # Get the lines of text in self.text, ignoring empty lines at
133 | # the top or bottom:
134 | lines = self.text.strip().splitlines()
135 |
136 | # Calculate the height of the text (using font.get_linesize()
137 | # to get the height of each line of text):
138 | height = len(lines) * font.get_linesize()
139 |
140 | # Calculate the placement of the text (centered on the screen):
141 | center, top = screen.get_rect().center
142 | top -= height // 2
143 |
144 | # If there is an image to display...
145 | if self.image:
146 | # load it:
147 | image = pygame.image.load(self.image).convert()
148 | # get its rect:
149 | r = image.get_rect()
150 | # move the text down by half the image height:
151 | top += r.height // 2
152 | # place the image 20 pixels above the text:
153 | r.midbottom = center, top - 20
154 | # blit the image to the screen:
155 | screen.blit(image, r)
156 |
157 | antialias = 1 # Smooth the text
158 | black = 0, 0, 0 # Render it as black
159 |
160 | # Render all the lines, starting at the calculated top, and
161 | # move down font.get_linesize() pixels for each line:
162 | for line in lines:
163 | text = font.render(line.strip(), antialias, black)
164 | r = text.get_rect()
165 | r.midtop = center, top
166 | screen.blit(text, r)
167 | top += font.get_linesize()
168 |
169 | # Display all the changes:
170 | pygame.display.flip()
171 |
172 |
173 | class Info(Paused):
174 |
175 | """
176 | A simple paused state that displays some information about the
177 | game. It is followed by a Level state (the first level).
178 | """
179 |
180 | next_state = Level
181 | text = '''
182 | In this game you are a banana,
183 | trying to survive a course in
184 | self-defense against fruit, where the
185 | participants will "defend" themselves
186 | against you with a 16 ton weight.'''
187 |
188 | class StartUp(Paused):
189 |
190 | """
191 | A paused state that displays a splash image and a welcome
192 | message. It is followed by an Info state.
193 | """
194 |
195 | next_state = Info
196 | image = config.splash_image
197 | text = '''
198 | Welcome to Squish,
199 | the game of Fruit Self-Defense'''
200 |
201 |
202 | class LevelCleared(Paused):
203 | """
204 | A paused state that informs the user that he or she has cleared a
205 | given level. It is followed by the next level state.
206 | """
207 |
208 | def __init__(self, number):
209 | self.number = number
210 | self.text = '''Level {} cleared
211 | Click to start next level'''.format(self.number)
212 |
213 | def next_state(self):
214 | return Level(self.number + 1)
215 |
216 | class GameOver(Paused):
217 |
218 | """
219 | A state that informs the user that he or she has lost the
220 | game. It is followed by the first level.
221 | """
222 |
223 | next_state = Level
224 | text = '''
225 | Game Over
226 | Click to Restart, Esc to Quit'''
227 |
228 | class Game:
229 |
230 | """
231 | A game object that takes care of the main event loop, including
232 | changing between the different game states.
233 | """
234 |
235 | def __init__(self, *args):
236 | # Get the directory where the game and the images are located:
237 | path = os.path.abspath(args[0])
238 | dir = os.path.split(path)[0]
239 | # Move to that directory (so that the image files may be
240 | # opened later on):
241 | os.chdir(dir)
242 | # Start with no state:
243 | self.state = None
244 | # Move to StartUp in the first event loop iteration:
245 | self.next_state = StartUp()
246 |
247 | def run(self):
248 | """
249 | This method sets things in motion. It performs some vital
250 | initialization tasks, and enters the main event loop.
251 | """
252 | pygame.init() # This is needed to initialize all the pygame modules
253 |
254 | # Decide whether to display the game in a window or to use the
255 | # full screen:
256 | flag = 0 # Default (window) mode
257 |
258 | if config.full_screen:
259 | flag = FULLSCREEN # Full screen mode
260 | screen_size = config.screen_size
261 | screen = pygame.display.set_mode(screen_size, flag)
262 |
263 | pygame.display.set_caption('Fruit Self Defense')
264 | pygame.mouse.set_visible(False)
265 |
266 | # The main loop:
267 | while True:
268 | # (1) If nextState has been changed, move to the new state, and
269 | # display it (for the first time):
270 | if self.state != self.next_state:
271 | self.state = self.next_state
272 | self.state.first_display(screen)
273 | # (2) Delegate the event handling to the current state:
274 | for event in pygame.event.get():
275 | self.state.handle(event)
276 | # (3) Update the current state:
277 | self.state.update(self)
278 | # (4) Display the current state:
279 | self.state.display(screen)
280 |
281 | if __name__ == '__main__':
282 | game = Game(*sys.argv)
283 | game.run()
284 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/beginning-python-3ed/bcff4b8f3ad2345eff8494cf955f656112e3c4cc/LICENSE.txt
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #Apress Source Code
2 |
3 | This repository accompanies [*Beginning Python*](http://www.apress.com/9781484200292) by Magnus Lie Hetland (Apress, 2017).
4 |
5 | 
6 |
7 | Download the files as a zip using the green button, or clone the repository to your machine using Git.
8 |
9 | ##Releases
10 |
11 | Release v1.0 corresponds to the code in the published book, without corrections or updates.
12 |
13 | ##Contributions
14 |
15 | See the file Contributing.md for more information on how you can contribute to this repository.
16 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | This is the source code for Beginning Python : From Novice to Professional,
2 | third 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 may
4 | 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-8.py: A Simple Database Application
20 | Chapter10/listing10-10.py: A Program for Finding the Sender of an E-mail
21 | Chapter10/listing10-11.py: A Template System
22 | Chapter10/listing10-12.txt: A Simple Template Example
23 | Chapter10/listing10-13.txt: Some Template Definitions
24 | Chapter10/listing10-14.txt: A Template
25 | Chapter11/listing11-1.py: Simple Script That Counts the Words in sys.stdin
26 | Chapter11/listing11-2.txt: A File Containing Some Nonsensical Text
27 | Chapter11/listing11-3.txt: A Simple Text File
28 | Chapter11/listing11-4.txt: The Modified Text File
29 | Chapter11/listing11-5.txt: The Text File, Modified Again
30 | Chapter11/listing11-6.py: Looping over Characters with read
31 | Chapter11/listing11-7.py: Writing the Loop Differently
32 | Chapter11/listing11-8.py: Using readline in a while Loop
33 | Chapter11/listing11-9.py: Iterating over Characters with read
34 | Chapter11/listing11-10.py: Iterating over Lines with readlines
35 | Chapter11/listing11-11.py: Iterating over Lines with fileinput
36 | Chapter11/listing11-12.py: Iterating over a File
37 | Chapter11/listing11-13.py: Iterating over a File Without Storing the File Object in a Variable
38 | Chapter12/listing12-1.py: Simple GUI Text Editor
39 | Chapter13/listing13-1.py: Importing Data into the Database (importdata.py)
40 | Chapter13/listing13-2.py: Food Database Query Program (food_query.py)
41 | Chapter14/listing14-1.py: A Minimal Server
42 | Chapter14/listing14-2.py: A Minimal Client
43 | Chapter14/listing14-3.py: A SocketServer-Based Minimal Server
44 | Chapter14/listing14-4.py: A Forking Server
45 | Chapter14/listing14-5.py: A Threading Server
46 | Chapter14/listing14-6.py: A Simple Server Using select
47 | Chapter14/listing14-7.py: A Simple Server Using poll
48 | Chapter14/listing14-8.py: A Simple Server Using Twisted
49 | Chapter14/listing14-9.py: An Improved Logging Server, Using the LineReceiver Protocol
50 | Chapter15/listing15-1.py: A Simple Screen Scraping Program
51 | Chapter15/listing15-2.py: A Screen Scraping Program Using the html.parser Module
52 | Chapter15/listing15-3.py: A Screen Scraping Program Using Beautiful Soup
53 | Chapter15/listing15-4.py: A Simple CGI Script
54 | Chapter15/listing15-5.py: A CGI Script That Invokes a Traceback (faulty.cgi)
55 | Chapter15/listing15-6.py: A CGI Script That Retrieves a Single Value from a FieldStorage (simple2.cgi)
56 | Chapter15/listing15-7.py: A Greeting Script with an HTML Form (simple3.cgi)
57 | Chapter16/listing16-1.py: A Simple Test Program
58 | Chapter16/listing16-2.py: A Simple Test Using the unittest Framework
59 | Chapter16/listing16-3.py: Calling External Checkers Using the subprocess Module
60 | Chapter17/listing17-1.java: A Simple Java Class (JythonTest.java)
61 | Chapter17/listing17-2.cs: A Simple C# Class (IronPythonTest.cs)
62 | Chapter17/listing17-3.c: A Simple C Function for Detecting a Palindrome (palindrome.c)
63 | Chapter17/listing17-4.py: Detecting Palindromes in Python
64 | Chapter17/listing17-5.i: Interface to the Palindrome Library (palindrome.i)
65 | Chapter17/listing17-6.c: Palindrome Checking Again (palindrome2.c)
66 | Chapter18/listing18-1.py: Simple Distutils Setup Script (setup.py)
67 | Chapter19/listing19-1.cfg: A Simple Configuration File
68 | Chapter19/listing19-2.py: A Program Using ConfigParser
69 | Chapter19/listing19-3.py: A Program Using the logging Module
70 | Chapter20/listing20-1.txt: A Sample Plain-Text Document (test_input.txt)
71 | Chapter20/listing20-2.py: A Text Block Generator (util.py)
72 | Chapter20/listing20-3.py: A Simple Markup Program (simple_markup.py)
73 | Chapter20/listing20-4.py: The Handlers (handlers.py)
74 | Chapter20/listing20-5.py: The Rules (rules.py)
75 | Chapter20/listing20-6.py: The Main Program (markup.py)
76 | Chapter21/listing21-1.py: A Simple ReportLab Program (hello_report.py)
77 | Chapter21/listing21-2.py: The First Prototype for the Sunspot Graph Program (sunspots_proto.py)
78 | Chapter21/listing21-3.py: The Final Sunspot Program (sunspots.py)
79 | Chapter22/listing22-1.xml: A Simple Web Site Represented As an XML File (website.xml)
80 | Chapter22/listing22-2.py: A Simple Page Maker Script (pagemaker.py)
81 | Chapter22/listing22-3.py: The Web Site Constructor (website.py)
82 | Chapter23/listing23-1.py: A Simple News-Gathering Agent (newsagent1.py)
83 | Chapter23/listing23-2.py: A More Flexible News-Gathering Agent (newsagent2.py)
84 | Chapter24/listing24-1.py: A Minimal Server Program
85 | Chapter24/listing24-2.py: A Server That Accepts Connections
86 | Chapter24/listing24-3.py: The Basic Server with Some Cleanups
87 | Chapter24/listing24-4.py: Server Program with ChatSession Class
88 | Chapter24/listing24-5.py: A Simple Chat Server (simple_chat.py)
89 | Chapter24/listing24-6.py: A Slightly More Complicated Chat Server (chatserver.py)
90 | Chapter25/listing25-1.py: A Simple Web Editor (simple_edit.cgi)
91 | Chapter25/listing25-2.py: The Editor Script (edit.cgi)
92 | Chapter25/listing25-3.py: The Saving Script (save.cgi)
93 | Chapter26/listing26-1.sql: Creating the Database in PostgreSQL
94 | Chapter26/listing26-2.sql: Creating the Database in MySQL
95 | Chapter26/listing26-3.sql: Creating the Database in SQLite
96 | Chapter26/listing26-4.py: The Main Bulletin Board (simple_main.cgi)
97 | Chapter26/listing26-5.py: The Main Bulletin Board (main.cgi)
98 | Chapter26/listing26-6.py: The Message Viewer (view.cgi)
99 | Chapter26/listing26-7.py: The Message Editor (edit.cgi)
100 | Chapter26/listing26-8.py: The Save Script (save.cgi)
101 | Chapter27/listing27-1.py: A Simple Node Implementation (simple_node.py)
102 | Chapter27/listing27-2.py: A New Node Implementation (server.py)
103 | Chapter27/listing27-3.py: A Node Controller Interface (client.py)
104 | Chapter28/listing28-1.py: A Simple GUI Client (simple_guiclient.py)
105 | Chapter28/listing28-2.py: The Finished GUI Client (guiclient.py)
106 | Chapter29/listing29-1.py: A Simple “Falling Weights” Animation (weights.py)
107 | Chapter29/listing29-2.py: The Squish Configuration File (config.py)
108 | Chapter29/listing29-3.py: The Squish Game Objects (objects.py)
109 | Chapter29/listing29-4.py: The Main Game Module (squish.py)
110 |
--------------------------------------------------------------------------------
/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!
--------------------------------------------------------------------------------
/errata.md:
--------------------------------------------------------------------------------
1 | # Errata for *Beginning Python, 3rd Edition*
2 |
3 | On **page 369**:
4 |
5 |
6 | The error is in the definition of function parse.
7 |
8 | The original source code:
9 | def parse(self, file):
10 | self.handler.start('document')
11 | for block in blocks(file):
12 | for filter in self.filters:
13 | block = filter(block, self.handler)
14 | for rule in self.rules:
15 | if rule.condition(block):
16 | last = rule.action(block,
17 | self.handler)
18 | if last: break
19 | self.handler.end('document')
20 | 'rule' has been looped 3 times in the 'for filter in self.filters:' statement, this is incorrect.
21 | I think it should be:
22 | def parse(self, file):
23 | self.handler.start('document')
24 | for block in blocks(file):
25 | for filter in self.filters:
26 | block = filter(block, self.handler)
27 | for rule in self.rules:
28 | if rule.condition(block):
29 | last = rule.action(block, self.handler)
30 | if last: break
31 | self.handler.end('document')
32 | ]
33 |
--------------------------------------------------------------------------------