Software Design Spring 2007 Today: 1) Exam 2 Solutions 2) Networking, part 1 Exam 2 ------ Median score about 29 out of 40. A little low, but Question 2 hurt, and the exam was long. Overall, I think class performance is fine. Question 1: def index_book(book, word_list): index = {} num = 0 for page in book: num += 1 // start on page 1 for word in page: if word in word_list: index.setdefault(word, []).append(num) return index 1) If this wasn't fast enough, how would you make it faster? 2) What's wrong with index[word] = index.get(word, []).append(num) 3) Could also have used for num, page in enumerate(book): Question 2: This question was ugly. Lots of people drew figures that included some elements of Python syntax and some elements of UML class diagrams (as opposed to object diagrams). UML is in some ways a formal language that is as strict as Python. It can be used idiomatically, but that is not a license to ignore the syntax altogether. Question 3: Not the clearest question I ever wrote, but what I was looking for was 1) Can you translate the information in the UML class diagram into code? 2) Can you make reasonable decisions about the parameters of the __init__ methods and the initialization of the attributes. class Book: def __init__(self, title, pages=None): self.name = title if pages == None: self.pages = [] else: self.pages = pages self.index = None class Page: def __init__(self, words=None): if words == None: self.words = [] else: self.words = words class Index: def __init__(self, book=None): if book == None: self.pagenums = None else: self.pagenums = make_index(book) 1) In some cases it would be better to copy lists passed as parameters. Either way, it should be clear which behavior users should expect. 2) Why not inherit from list, dict? a) That's not the design shown in the UML. b) Use this idea with great caution. 3) Don't use modifiable objects as default parameters! def __init__(self, title, pages=[]): 4) What's wrong with def __init__(self, words=None): self.words = words or [] Question 4 1) Packing widgets with side=TOP means top-to-bottom. side=LEFT means left-to-right This GUI has two frames packed top-to-bottom; the bottom frame has an entry and two buttons packed left-to-right. 2) When the user presses "Press me", the text from the entry appears on the canvas. When press is invoked, args is a tuple with one element, a reference to the Entry object created by Gui.en. 3) class YourGui(MyGui): def setup(self): MyGui.setup(self) self.color = StringVar() self.fr(TOP) for color in ['red', 'green', 'blue']: self.rb(LEFT, text=color, variable=self.color, value=color) self.endfr() Common errors: a) When you override a method and you want to call the overridden method, you have to pass self as an argument. b) The StringVar should be an attribute of YourGui. Network programming ------------------- 1) low-level communication: sockets 2) medium-level protocol modules 3) high-level distributed programming: remote objects Sockets ------- A socket is a software object that allows a process to connect to a port. A process is a running program. There might be more than one process running the same program. A port is a mailbox that is visible from other computers. Clients and servers ------------------- A common way to organize a distributed computation is with different processes that act as clients and servers. 1) The Web is a client-server system Web browsers are clients They connect to servers, as specified by URLs Servers wait for connections, receive requests, look up files, send replies. Many servers, many clients. Most servers run all the time. Clients are intermittent. 2) A (relatively) simple example: An echo server waits for connections, receives data, "echos" the data back to the client. One server, one client (at a time). ### echo_server.py from socket import * HOST = '' # empty string means the local host PORT = 50007 # arbitrary non-privileged port lsock = socket(AF_INET, SOCK_STREAM) lsock.bind((HOST, PORT)) lsock.listen(1) csock, addr = lsock.accept() print 'Server connected to:', csock.getpeername() while 1: data = csock.recv(1024) if not data: break print 'Server received', `data` csock.send(data) csock.close() lsock.close() ### echo_client.py from socket import * HOST = 'rocky.olin.edu' # the remote host PORT = 50007 # the same port used by the server csock = socket(AF_INET, SOCK_STREAM) csock.connect((HOST, PORT)) print 'Client connected to: ', csock.getpeername() csock.send('Hello, world') data = csock.recv(1024) csock.close() print 'Client received', `data` 1) port numbers: some "well-known" numbers are reserved for particular functions others are available for transient use 2) bind: associate a socket with a particular port 3) listen: make this socket into a (passive) listening socket servers use this method; clients don't 4) accept: wait for a client to connect, and return a tuple lsock is the listening socket csock is the connected socket addr is the address of the client 5) send/recv: can only send and receive strings! 6) connect: clients invoke connect to specify the HOST and PORT of the server (How do they know???) Lab Exercise ------------ 1) Find a partner. 2) Boot linux and make sure you have a working network. 3) One of you download echo_client.py from the usual place; the other should download echo_server.py 4) If you have the server, run /sbin/ifconfig to get your IP address, and tell it to your partner. 5) If you have the client, replace the value of HOST with your partner's IP address (as a string). 6) Run the server. 6a) Select Desktop->System Settings->Security Level Type root password when asked, then add 50007:tcp to the list of "Other ports" Press OK. 7) Run the client. If things go according to plan, the client should say Client connected to: ('10.8.11.90', 50007) Client received 'Hello, world' And the server should say Server connected to: ('10.8.11.90', 36807) Server received 'Hello, world' Notice that the server uses TWO sockets, one for listening and one for send/recv with clients. Pros and Cons of sockets ------------------------ +) versatile and powerful -) complicated -) can only send and receive strings! +) can implement any protocol -) have to implement any protocol Protocol modules ---------------- There are built-in (or readily-available) modules that work with many common protocols. For example, urllib 1) parses URLs into (protocol, server, file) 2) opens a socket and connects to the server 3) sends a legal HTTP GET request 4) reads and parses the response 5) returns an object that can be used to access the response Documentation at www.python.org/doc/current/lib/module-urllib.html Example: from urllib import * conn = urlopen('http://ece.olin.edu/sd/code/World.py') print conn for line in conn.fp: print line, OR print conn.read() As another example, HTMLParser reads HTML, processes the syntax, and calls your handler functions for different tags. from HTMLParser import HTMLParser from urllib import * class MyHTMLParser(HTMLParser): def handle_starttag(self, tag, attrs): print "Encountered the beginning of a %s tag" % tag def handle_endtag(self, tag): print "Encountered the end of a %s tag" % tag p = MyHTMLParser() conn = urlopen('http://wb/sd/index.html') for line in conn.fp: p.feed(line) Pros and cons of protocol modules --------------------------------- +) easy to work with existing protocols +) useful tools for both the server and client end -) not as versatile as sockets Remote objects -------------- A little more cutting edge, not in standard modules. But I found something pretty good in the Vaults: Pyro (Python Remote Objects). 1) the server a) creates an object that will be available remotely b) advertises the object on a name server 2) the client a) contacts the name server to find a remote object b) creates a local object that acts as a PROXY for the remote object c) invokes methods on the proxy 3) the middleware (Pyro) when the client invokes a method on a proxy, Pyro a) packages the arguments and transfers them to the server b) invokes the method on the remote object c) packages the return value and transfers it to the client This kind of distributed programming is called Remote Procedure Call (RPC) in procedural languages, and Remote Method Invocation (RMI) in object-oriented languages.