-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add documentation. A tiny bit of cleanup.
- Loading branch information
Showing
1 changed file
with
137 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
############################################################################## | ||
# | ||
# Copyright (c) 2005 Zope Corporation. All Rights Reserved. | ||
# | ||
# This software is subject to the provisions of the Zope Visible Source | ||
# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this | ||
# distribution. | ||
# | ||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | ||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | ||
# FOR A PARTICULAR PURPOSE | ||
# | ||
############################################################################## | ||
"""Publisher Hook to ``mechanize`` | ||
$Id$ | ||
""" | ||
import httplib | ||
import urllib2 | ||
from cStringIO import StringIO | ||
|
||
import mechanize | ||
import ClientCookie | ||
|
||
from zope.app.testing.functional import HTTPCaller | ||
from zope.app.testing.testbrowser import browser | ||
|
||
|
||
class PublisherConnection(object): | ||
"""A ``urllib2`` compatible connection obejct.""" | ||
|
||
def __init__(self, host): | ||
self.host = host | ||
self.caller = HTTPCaller() | ||
|
||
def set_debuglevel(self, level): | ||
pass | ||
|
||
def request(self, method, url, body=None, headers=None): | ||
"""Send a request to the publisher. | ||
The response will be stored in ``self.response``. | ||
""" | ||
if body is None: | ||
body = '' | ||
|
||
# Construct the headers. | ||
header_chunks = [] | ||
if headers is not None: | ||
for header in headers.items(): | ||
header_chunks.append('%s: %s' % header) | ||
headers = '\n'.join(header_chunks) + '\n' | ||
else: | ||
headers = '' | ||
|
||
# Construct the full HTTP request string, since that is what the | ||
# ``HTTPCaller`` wants. | ||
request_string = (method + ' ' + url + ' HTTP/1.1\n' | ||
+ headers + '\n' + body) | ||
|
||
self.response = self.caller(request_string) | ||
|
||
def getresponse(self): | ||
"""Return a ``urllib2`` compatible response. | ||
The goal of ths method is to convert the Zope Publisher's reseponse to | ||
a ``urllib2`` compatible response, which is also understood by | ||
mechanize. | ||
""" | ||
headers = self.response.header_output.headers | ||
real_response = self.response._response | ||
status = real_response.getStatus() | ||
reason = real_response._reason # XXX should add a getReason method | ||
output = (real_response.getHeaderText(real_response.getHeaders()) + | ||
self.response.getBody()) | ||
return PublisherResponse(output, status, reason) | ||
|
||
|
||
class PublisherResponse(object): | ||
"""``urllib2`` compatible response object.""" | ||
|
||
def __init__(self, content, status, reason): | ||
self.content = content | ||
self.status = status | ||
self.reason = reason | ||
self.msg = httplib.HTTPMessage(StringIO(content), 0) | ||
self.content_as_file = StringIO(content) | ||
|
||
def read(self, amt=None): | ||
return self.content_as_file.read(amt) | ||
|
||
|
||
class PublisherHTTPHandler(urllib2.HTTPHandler): | ||
"""Special HTTP handler to use the Zope Publisher.""" | ||
|
||
http_request = urllib2.AbstractHTTPHandler.do_request_ | ||
|
||
def http_open(self, req): | ||
"""Open an HTTP connection having a ``urllib2`` request.""" | ||
# Here we connect to the publisher. | ||
return self.do_open(PublisherConnection, req) | ||
|
||
|
||
class PublisherMechanizeBrowser(mechanize.Browser): | ||
"""Special ``mechanize`` browser using the Zope Publisher HTTP handler.""" | ||
|
||
handler_classes = { | ||
# scheme handlers | ||
"http": PublisherHTTPHandler, | ||
|
||
"_http_error": ClientCookie.HTTPErrorProcessor, | ||
"_http_request_upgrade": ClientCookie.HTTPRequestUpgradeProcessor, | ||
"_http_default_error": urllib2.HTTPDefaultErrorHandler, | ||
|
||
# feature handlers | ||
"_authen": urllib2.HTTPBasicAuthHandler, | ||
"_redirect": ClientCookie.HTTPRedirectHandler, | ||
"_cookies": ClientCookie.HTTPCookieProcessor, | ||
"_refresh": ClientCookie.HTTPRefreshProcessor, | ||
"_referer": mechanize.Browser.handler_classes['_referer'], | ||
"_equiv": ClientCookie.HTTPEquivProcessor, | ||
"_seek": ClientCookie.SeekableProcessor, | ||
} | ||
|
||
default_schemes = ["http"] | ||
default_others = ["_http_error", "_http_request_upgrade", | ||
"_http_default_error"] | ||
default_features = ["_authen", "_redirect", "_cookies", "_seek"] | ||
|
||
|
||
class Browser(browser.Browser): | ||
"""A Zope ``testbrowser` Browser that uses the Zope Publisher.""" | ||
|
||
def __init__(self, url=None): | ||
mech_browser = PublisherMechanizeBrowser() | ||
super(Browser, self).__init__(url=url, mech_browser=mech_browser) |