<no title> — WebOb 1.4 documentation

### Navigation

- index
- modules |
- WebOb 1.4 documentation »

This demonstrates how the Request object works, and tests it.

You can instantiate a request using `Request.blank()`, to create a fresh environment dictionary with all the basic keys such a dictionary should have.

    >>> import sys
    >>> if sys.version >= '2.7':
    ...     from io import BytesIO as InputType
    ... else:
    ...     from cStringIO import InputType
    >>> from doctest import ELLIPSIS, NORMALIZE_WHITESPACE
    >>> from webob import Request, UTC
    >>> req = Request.blank('/')
    >>> req
    <Request at ... GET http://localhost/>
    >>> print req
    GET / HTTP/1.0
    Host: localhost:80
    >>> req.environ
    {...}
    >>> isinstance(req.body_file, InputType)
    True
    >>> req.scheme
    'http'
    >>> req.method
    'GET'
    >>> req.script_name
    ''
    >>> req.path_info
    '/'
    >>> req.upath_info
    u'/'
    >>> req.content_type
    ''
    >>> print req.remote_user
    None
    >>> req.host_url
    'http://localhost'
    >>> req.script_name = '/foo'
    >>> req.path_info = '/bar/'
    >>> req.environ['QUERY_STRING'] = 'a=b'
    >>> req.application_url
    'http://localhost/foo'
    >>> req.path_url
    'http://localhost/foo/bar/'
    >>> req.url
    'http://localhost/foo/bar/?a=b'
    >>> req.relative_url('baz')
    'http://localhost/foo/bar/baz'
    >>> req.relative_url('baz', to_application=True)
    'http://localhost/foo/baz'
    >>> req.relative_url('http://example.org')
    'http://example.org'
    >>> req.path_info_peek()
    'bar'
    >>> req.path_info_pop()
    'bar'
    >>> req.script_name, req.path_info
    ('/foo/bar', '/')
    >>> print req.environ.get('wsgiorg.routing_args')
    None
    >>> req.urlvars
    {}
    >>> req.environ['wsgiorg.routing_args']
    ((), {})
    >>> req.urlvars = dict(x='y')
    >>> req.environ['wsgiorg.routing_args']
    ((), {'x': 'y'})
    >>> req.urlargs
    ()
    >>> req.urlargs = (1, 2, 3)
    >>> req.environ['wsgiorg.routing_args']
    ((1, 2, 3), {'x': 'y'})
    >>> del req.urlvars
    >>> req.environ['wsgiorg.routing_args']
    ((1, 2, 3), {})
    >>> req.urlvars = {'test': 'value'}
    >>> del req.urlargs
    >>> req.environ['wsgiorg.routing_args']
    ((), {'test': 'value'})
    >>> req.is_xhr
    False
    >>> req.environ['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
    >>> req.is_xhr
    True
    >>> req.host
    'localhost:80'

There are also variables to access the variables and body:

    >>> from cStringIO import StringIO
    >>> body = 'var1=value1&var2=value2&rep=1&rep=2'
    >>> req = Request.blank('/')
    >>> req.method = 'POST'
    >>> req.body_file = StringIO(body)
    >>> req.environ['CONTENT_LENGTH'] = str(len(body))
    >>> req.POST
    MultiDict([(u'var1', u'value1'), (u'var2', u'value2'), (u'rep', u'1'), (u'rep', u'2')])

Note that the variables are there for GET requests and non-form requests, but they are empty and read-only:

    >>> req = Request.blank('/')
    >>> req.POST
    <NoVars: Not a form request>
    >>> req.POST.items()
    []
    >>> req.POST['x'] = 'y'
    Traceback (most recent call last):
        ...
    KeyError: 'Cannot add variables: Not a form request'
    >>> req.method = 'POST'
    >>> req.POST
    MultiDict([])
    >>> req.content_type = 'text/xml'
    >>> req.body_file = StringIO('<xml></xml>')
    >>> req.POST
    <NoVars: Not an HTML form submission (Content-Type: text/xml)>
    >>> req.body
    '<xml></xml>'

You can also get access to the query string variables, of course:

    >>> req = Request.blank('/?a=b&d=e&d=f')
    >>> req.GET
    MultiDict([(u'a', u'b'), (u'd', u'e'), (u'd', u'f')])
    >>> req.GET['d']
    u'f'
    >>> req.GET.getall('d')
    [u'e', u'f']
    >>> req.method = 'POST'
    >>> req.body = 'x=y&d=g'
    >>> req.environ['CONTENT_LENGTH']
    '7'
    >>> req.params
    NestedMultiDict([(u'a', u'b'), (u'd', u'e'), (u'd', u'f'), (u'x', u'y'), (u'd', u'g')])
    >>> req.params['d']
    u'f'
    >>> req.params.getall('d')
    [u'e', u'f', u'g']

Cookies are viewed as a dictionary (view only):

    >>> req = Request.blank('/')
    >>> req.environ['HTTP_COOKIE'] = 'var1=value1; var2=value2'
    >>> sorted(req.cookies.items())
    [(u'var1', u'value1'), (u'var2', u'value2')]

Sometimes conditional headers are problematic. You can remove them:

    >>> from datetime import datetime
    >>> req = Request.blank('/')
    >>> req.if_none_match = 'some-etag'
    >>> req.if_modified_since = datetime(2005, 1, 1, 12, 0)
    >>> req.environ['HTTP_ACCEPT_ENCODING'] = 'gzip'
    >>> print sorted(req.headers.items())
    [('Accept-Encoding', 'gzip'), ('Host', 'localhost:80'), ('If-Modified-Since', 'Sat, 01 Jan 2005 12:00:00 GMT'), ('If-None-Match', 'some-etag')]
    >>> req.remove_conditional_headers()
    >>> print req.headers
    {'Host': 'localhost:80'}

Some headers are handled specifically (more should be added):

    >>> req = Request.blank('/')
    >>> req.if_none_match = 'xxx'
    >>> 'xxx' in req.if_none_match
    True
    >>> 'yyy' in req.if_none_match
    False
    >>> req.if_modified_since = datetime(2005, 1, 1, 12, 0)
    >>> req.if_modified_since < datetime(2006, 1, 1, 12, 0, tzinfo=UTC)
    True
    >>> req.user_agent is None
    True
    >>> req.user_agent = 'MSIE-Win'
    >>> req.user_agent
    'MSIE-Win'

    >>> req.cache_control
    <CacheControl ''>
    >>> req.cache_control.no_cache = True
    >>> req.cache_control.max_age = 0
    >>> req.cache_control
    <CacheControl 'max-age=0, no-cache'>

.cache_control is a view:

    >>> 'cache-control' in req.headers
    True
    >>> req.headers['cache-control']
    'max-age=0, no-cache'
    >>> req.cache_control = {'no-transform': None, 'max-age': 100}
    >>> req.headers['cache-control']
    'max-age=100, no-transform'

Accept-* headers are parsed into read-only objects that support containment tests, and some useful methods. Note that parameters on mime types are not supported.

    >>> req = Request.blank('/')
    >>> req.environ['HTTP_ACCEPT'] = "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.1"
    >>> req.accept
    <MIMEAccept('text/*;q=0.3, text/html;q=0.7, text/html, text/html;q=0.4, */*;q=0.1')>
    >>> for item, quality in req.accept._parsed:
    ...     print '%s: %0.1f' % (item, quality)
    text/*: 0.3
    text/html: 0.7
    text/html: 1.0
    text/html: 0.4
    */*: 0.1
    >>> '%0.1f' % req.accept.quality('text/plain')
    '0.3'
    >>> '%0.1f' % req.accept.quality('text/html')
    '1.0'
    >>> 'image/png' in req.accept
    True
    >>> req.environ['HTTP_ACCEPT'] = "text/html, application/xml; q=0.7, text/*; q=0.5, */*; q=0.1"
    >>> req.accept
    <MIMEAccept('text/html, application/xml;q=0.7, text/*;q=0.5, */*;q=0.1')>
    >>> req.accept.best_match(['text/plain', 'application/xml'])
    'application/xml'
    >>> req.accept = "text/html, application/xml, text/*; q=0.5"
    >>> 'image/png' in req.accept
    False
    >>> 'text/plain' in req.accept
    True
    >>> req.accept_charset = 'utf8'
    >>> 'UTF8' in req.accept_charset
    True
    >>> 'gzip' in req.accept_encoding
    False
    >>> req.accept_encoding = 'gzip'
    >>> 'GZIP' in req.accept_encoding
    True
    >>> req.accept_language = {'en-US': 0.5, 'es': 0.7}
    >>> str(req.accept_language)
    'es;q=0.7, en-US;q=0.5'
    >>> req.headers['Accept-Language']
    'es;q=0.7, en-US;q=0.5'
    >>> req.accept_language.best_matches('en-GB')
    ['es', 'en-US', 'en-GB']
    >>> req.accept_language.best_matches('es')
    ['es']
    >>> req.accept_language.best_matches('ES')
    ['ES']

    >>> req = Request.blank('/', accept_language='en;q=0.5')
    >>> req.accept_language.best_match(['en-gb'])
    'en-gb'

    >>> req = Request.blank('/', accept_charset='utf-8;q=0.5')
    >>> req.accept_charset.best_match(['iso-8859-1', 'utf-8'])
    'iso-8859-1'

The If-Range header is a combination of a possible conditional date or etag match:

    >>> req = Request.blank('/')
    >>> req.if_range = 'asdf'
    >>> req.if_range
    <IfRange etag="asdf", date=*>
    >>> from webob import Response
    >>> res = Response()
    >>> res.etag = 'asdf'
    >>> req.if_range.match_response(res)
    True
    >>> res.etag = None
    >>> req.if_range.match_response(res)
    False
    >>> res.last_modified = datetime(2005, 1, 1, 12, 0, tzinfo=UTC)
    >>> req.if_range = datetime(2006, 1, 1, 12, 0, tzinfo=UTC)
    >>> req.if_range
    <IfRange etag=*, date=Sun, 01 Jan 2006 12:00:00 GMT>
    >>> req.if_range.match_response(res)
    True
    >>> res.last_modified = datetime(2007, 1, 1, 12, 0, tzinfo=UTC)
    >>> req.if_range.match_response(res)
    False
    >>> req = Request.blank('/')
    >>> req.if_range
    <Empty If-Range>
    >>> req.if_range.match_response(res)
    True

Ranges work like so:

    >>> req = Request.blank('/')
    >>> req.range = (0, 100)
    >>> req.range
    <Range ranges=(0, 100)>
    >>> str(req.range)
    'bytes=0-99'

You can use them with responses:

    >>> res = Response()
    >>> res.content_range = req.range.content_range(1000)
    >>> res.content_range
    <ContentRange bytes 0-99/1000>
    >>> str(res.content_range)
    'bytes 0-99/1000'
    >>> start, end, length = res.content_range
    >>> start, end, length
    (0, 100, 1000)

A quick test of caching the request body:

    >>> from cStringIO import StringIO
    >>> length = Request.request_body_tempfile_limit+10
    >>> data = StringIO('x'*length)
    >>> req = Request.blank('/')
    >>> req.content_length = length
    >>> req.method = 'PUT'
    >>> req.body_file = data
    >>> req.body_file_raw
    <...IO... object at ...>
    >>> len(req.body)
    10250
    >>> req.body_file
    <open file ..., mode 'w+b' at ...>
    >>> int(req.body_file.tell())
    0
    >>> req.POST
    <NoVars: Not an HTML form submission (Content-Type: )>
    >>> int(req.body_file.tell())
    0

Some query tests:

    >>> req = Request.blank('/')
    >>> req.GET.get('unknown')
    >>> req.GET.get('unknown', '?')
    '?'
    >>> req.POST.get('unknown')
    >>> req.POST.get('unknown', '?')
    '?'
    >>> req.params.get('unknown')
    >>> req.params.get('unknown', '?')
    '?'

Some updating of the query string:

    >>> req = Request.blank('http://localhost/foo?a=b')
    >>> req.GET
    MultiDict([(u'a', u'b')])
    >>> req.GET['c'] = 'd'
    >>> req.query_string
    'a=b&c=d'

And for dealing with file uploads:

    >>> req = Request.blank('/posty')
    >>> req.method = 'POST'
    >>> req.content_type = 'multipart/form-data; boundary="foobar"'
    >>> req.body = '''\
    ... --foobar
    ... Content-Disposition: form-data; name="a"
    ...
    ... b
    ... --foobar
    ... Content-Disposition: form-data; name="upload"; filename="test.html"
    ... Content-Type: text/html
    ...
    ... <html>Some text...</html>
    ... --foobar--
    ... '''
    >>> req.POST
    MultiDict([(u'a', u'b'), (u'upload', FieldStorage(u'upload', u'test.html'))])
    >>> print req.body.replace('\r', '')
    --foobar
    Content-Disposition: form-data; name="a"

    b
    --foobar
    Content-Disposition: form-data; name="upload"; filename="test.html"
    Content-type: text/html

    <html>Some text...</html>
    --foobar--
    >>> req.POST['c'] = 'd'
    >>> req.POST
    MultiDict([('a', 'b'), ('upload', FieldStorage('upload', 'test.html')), ('c', 'd')])
    >>> req.body_file_raw
    <....BufferedReader>
    >>> req.body_file_raw.raw
    <FakeCGIBody at ... viewing MultiDict([('a'...d')])>
    >>> sorted(req.POST.keys())
    ['a', 'c', 'upload']
    >>> print req.body.replace('\r', '') # doctestx: +REPORT_UDIFF
    --foobar
    Content-Disposition: form-data; name="a"

    b
    --foobar
    Content-Disposition: form-data; name="upload"; filename="test.html"
    Content-type: text/html

    <html>Some text...</html>
    --foobar
    Content-Disposition: form-data; name="c"

    d
    --foobar--

FakeCGIBody have both readline and readlines methods:

    >>> req_ = Request.blank('/posty')
    >>> req_.method = 'POST'
    >>> req_.content_type = 'multipart/form-data; boundary="foobar"'
    >>> req_.body = '''\
    ... --foobar
    ... Content-Disposition: form-data; name="a"
    ...
    ... b
    ... --foobar
    ... Content-Disposition: form-data; name="upload"; filename="test.html"
    ... Content-Type: text/html
    ...
    ... <html>Some text...</html>
    ... --foobar--
    ... '''
    >>> req_.POST
    MultiDict([('a', 'b'), ('upload', FieldStorage('upload', 'test.html'))])
    >>> print req_.body.replace('\r', '')
    --foobar
    Content-Disposition: form-data; name="a"

    b
    --foobar
    Content-Disposition: form-data; name="upload"; filename="test.html"
    Content-type: text/html

    <html>Some text...</html>
    --foobar--
    >>> req_.POST['c'] = 'd'
    >>> req_.POST
    MultiDict([('a', 'b'), ('upload', FieldStorage('upload', 'test.html')), ('c', 'd')])
    >>> req_.body_file_raw.readline()
    '--foobar\r\n'
    >>> [n.replace('\r', '') for n in req_.body_file.readlines()]
    ['Content-Disposition: form-data; name="a"\n', '\n', 'b\n', '--foobar\n', 'Content-Disposition: form-data; name="upload"; filename="test.html"\n', 'Content-type: text/html\n', '\n', '<html>Some text...</html>\n', '--foobar\n', 'Content-Disposition: form-data; name="c"\n', '\n', 'd\n', '--foobar--']

Also reparsing works through the fake body:

    >>> del req.environ['webob._parsed_post_vars']
    >>> req.POST
    MultiDict([('a', 'b'), ('upload', FieldStorage('upload', 'test.html')), ('c', 'd')])

A `BaseRequest` class exists for the purpose of usage by web frameworks that want a less featureful `Request`.

For example, the `Request` class mutates the `environ['webob.adhoc_attrs']` attribute when its `__getattr__`, `__setattr__`, and `__delattr__` are invoked.

The `BaseRequest` class omits the mutation annotation behavior provided by the default `Request` implementation. Instead, the of the `BaseRequest` class actually mutates the `__dict__` of the request instance itself.

    >>> from webob import BaseRequest
    >>> req = BaseRequest.blank('/')
    >>> req.foo = 1
    >>> req.environ['webob.adhoc_attrs']
    Traceback (most recent call last):
        ...
    KeyError: 'webob.adhoc_attrs'
    >>> req.foo
    1
    >>> del req.foo
    >>> req.foo
    Traceback (most recent call last):
        ...
    AttributeError: 'BaseRequest' object has no attribute 'foo'

    >>> req = BaseRequest.blank('//foo')
    >>> print req.path_info_pop('x')
    None
    >>> req.script_name
    ''
    >>> print BaseRequest.blank('/foo').path_info_pop('/')
    None
    >>> BaseRequest.blank('/foo').path_info_pop('foo')
    'foo'
    >>> BaseRequest.blank('/foo').path_info_pop('fo+')
    'foo'
    >>> BaseRequest.blank('//1000').path_info_pop('\d+')
    '1000'
    >>> BaseRequest.blank('/1000/x').path_info_pop('\d+')
    '1000'

    >>> req = Request.blank('/', method='PUT', body='x'*10)

str(request) returns the request as HTTP request string

    >>> print req
    PUT / HTTP/1.0
    Content-Length: 10
    Host: localhost:80

    xxxxxxxxxx

req.as_text() does the same thing but also can take additional argument skip_body skip_body=True excludes the body from the result

    >>> print req.as_text(skip_body=True)
    PUT / HTTP/1.0
    Content-Length: 10
    Host: localhost:80

req.as_bytes() returns the body as bytes; it does the same thing as as_text except the result is bytes.

    >>> print req.as_bytes(skip_body=True)
    PUT / HTTP/1.0
    Content-Length: 10
    Host: localhost:80

skip_body=<int> excludes the body from the result if it’s longer than that number

    >>> print req.as_bytes(skip_body=5)
    PUT / HTTP/1.0
    Content-Length: 10
    Host: localhost:80

    <body skipped (len=10)>

but not if it’s shorter

    >>> print req.as_bytes(skip_body=100)
    PUT / HTTP/1.0
    Content-Length: 10
    Host: localhost:80

    xxxxxxxxxx

### Navigation

- index
- modules |
- WebOb 1.4 documentation »