$ virtualenv playground
$ cd playground
$ ls
bin/ include/ lib/
$ . bin/activate
(playground)$ pip install requests
(playground)$ python
>>> import requests
>>> r = requests.get('http://google.com')
OO = Object-Oriented Programming
What is an “Object”?
There were variables:
firstname = 'Guido'
lastname = 'van Rossum'
state = 'CA'
age = 55
genre = 'Dutch'
firstnames = ['George', 'John', 'Thomas']
lastnames = ['Washington', 'Adams', 'Jefferson']
states = ['VA', 'MA', 'VA']
terms = [2, 1, 2]
ignorant_function(firstnames[i], lastnames[i],
states[i], terms[i])
aware_function(firstnames, lastnames,
states, terms, i)
presidents = [
Person(firstname='George',
lastname='Washington,
state='VA',
terms=2),
]
p = presidents[0]
print p.firstname, p.lastname
designate_holiday_for(p)
presidents = [
Person(firstname='George',
lastname='Washington,
state='VA',
terms=2),
] ↑
# Why did I insert
# this unnecessary comma?
some_primes = [
6311,
6317,
6323,
6329,
6337,
6343,
]
Terminal commas are always allowed
♥ Python
So, records are great
p.firstname='George'
p.lastname='Washington
p.state='VA'
p.terms=2
def create_person(): …
def delete_person(p): …
def send_paycheck_to(p): …
def designate_retiree(p): …
def record_contribution(p): …
def produce_obituary(p): …
def send_paycheck_to(p):
"Produce a paycheck for person `p`."
if p.role == 'student':
…
elif p.role == 'employee':
if p.status = 'biweekly':
…
elif p.status = 'salary':
if p.rank = 'emeritus':
…
else:
…
myperson.process_payroll()
class Employee(object):
def process_payroll(…): …
def move_to_retirement(…): …
class BiweeklyStaff(Employee):
def process_payroll(…): …
class EmeritusProf(Employee):
def move_to_retirement(…): …
myperson.process_payroll()
↓
Employee.process_payroll(myperson)
def process_payroll(self):
…
But could OO have a dark side?
“Hypertext Transfer Protocol”
(hypertext means text with links)
“fetch http://google.com for me!”
Your browser:
GET / HTTP/1.1
Accept: text/html,application/xhtml+xml,…
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cache-Control: max-age=0
Connection: keep-alive
Cookie: …
Host: www.google.com
User-Agent: Mozilla/5.0 (X11; Linux i686)
AppleWebKit/535.1 (KHTML, like Gecko)
Chrome/13.0.782.41 Safari/535.1
Q:
A:
∵ SimpleHTTPServer
∴ Python MUST have a parser!
$ python -m SimpleHTTPServer
GET / HTTP/1.1
Accept: text/html,text/plain
Host: www.example.com
POST / HTTP/1.1
Accept: text/html,text/plain
Content-Length: 12
Host: www.example.com
query=Python
Hint: this was probably a bad idea!
Why invite another programmer in?
#inheritance
BaseHTTPRequestHandler
↑
HTTPServer → MyHTTPRequestHandler
# API
HTTPServer → HTTPParser → MyHandler
BaseRequestHandler.__init__(self, request, client_address…)
StreamRequestHandler.setup(self)
BaseHTTPRequestHandler.handle_one_request(self)
BaseHTTPRequestHandler.parse_request(self)
Q:
What does parse_request() need?
A:
A:
Freebies:
self.default_request_version = "HTTP/0.9"
self.protocol_version = "HTTP/1.0"
self.MessageClass = mimetools.Message
Needs:
self.raw_requestline # string
self.rfile # used by self.MessageClass
self.send_error(code, message) # HTTP response!
Q:
A:
“Well, you start by opening a socket…”
test/test_httpservers.py
self.server = HTTPServer(('', 0), self.request_handler)
self.test_object.PORT = self.server.socket.getsockname()[1]
I was willing.
from BaseHTTPServer import BaseHTTPRequestHandler
from StringIO import StringIO
class HTTPRequest(BaseHTTPRequestHandler):
def __init__(self, request_text):
self.rfile = StringIO(request_text)
self.raw_requestline = self.rfile.readline()
self.error_code = self.error_message = None
self.parse_request()
def send_error(self, code, message):
self.error_code = code
self.error_message = message
# Simply instantiate with the request text
request = HTTPRequest(request_text)
print request.error_code # None
print request.command # "GET"
print request.path # "/who/ken/trust.html"
print request.request_version # "HTTP/1.1"
print len(request.headers) # 3
print request.headers['host'] # "cm.bell-labs.com"
# Parsing can result in an error code and message
request = HTTPRequest(bad_request_text)
print request.error_code # 400
print request.error_message # "Bad request syntax"
What have we learned?
BaseHTTPServer hides sockets really well!
def parse_http_request(
infile,
request,
version='HTTP/1.0',
default_version='HTTP/0.9'):
…
if failure:
raise HTTPError(400, "Bad request")
…
request.code = …
request.path = …
…
return
Robert Brewer is pretty awesome
From _cperror.py:
class HTTPError(CherryPyException):
⋮
def get_error_page(self, …):
return get_error_page(…)
⋮
def get_error_page(status, …):
⋮
Martin Fowler
http://martinfowler.com/articles/injection.html
Any questions?