<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>examples/media/default.css</filename>
    </added>
    <added>
      <filename>examples/media/itty.png</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -40,7 +40,7 @@ Example
   
   run_itty()
 
-See ``test/test_itty.py`` for more usage.
+See ``examples/`` for more usages.
 
 
 Other Sources</diff>
      <filename>README.rst</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,8 @@
 from itty import *
 
 @error(500)
-def my_great_500(exception, env, start_response):
-    start_response('500 APPLICATION ERROR', [('Content-Type', 'text/html')])
+def my_great_500(request, exception):
+    request._start_response('500 APPLICATION ERROR', [('Content-Type', 'text/html')])
     html_output = &quot;&quot;&quot;
     &lt;html&gt;
         &lt;head&gt;
@@ -20,6 +20,10 @@ def my_great_500(exception, env, start_response):
     &quot;&quot;&quot; % exception[0]
     return [html_output]
 
+@get('/hello')
+def hello(request):
+    return 'Hello errors!'
+
 @get('/test_404')
 def test_404(request):
     raise NotFound('Not here, sorry.')</diff>
      <filename>examples/error_handling.py</filename>
    </modified>
    <modified>
      <diff>@@ -1,20 +1,18 @@
 from itty import *
 
-# By default, itty can serve static media out of the ``media`` folder for all
-# requests coming from ``/media``.
-
-# So in your browser, you should be able to hit ``/media/default.css`` or
-# ``/media/itty.png``.
-
-# If you uncomment the below, you can manipulate both the path and the url into
-# whatever you choose.
-
-# import os
-# Media.path = '/public'
-# Media.root = os.path.join(os.path.dirname(__file__), 'html')
-
 @get('/')
 def index(request):
     return 'Hello World!'
 
+# To serve static files, simply setup a standard @get method. You should
+# capture the filename/path and get the content-type. If your media root is
+# different than where your ``itty.py`` lives, manually setup your root
+# directory as well. Finally, use the ``static_file`` handler to serve up the
+# file.
+@get('/media/(?P&lt;filename&gt;.+)')
+def my_media(request, filename):
+    my_media.content_type = content_type(filename)
+    my_root = os.path.join(os.path.dirname(__file__), 'media')
+    return static_file(request, filename=filename, root=my_root)
+
 run_itty()</diff>
      <filename>examples/static_files.py</filename>
    </modified>
    <modified>
      <diff>@@ -32,7 +32,7 @@ except ImportError:
     from cgi import parse_qs
 
 __author__ = 'Daniel Lindsley'
-__version__ = ('0', '4', '0')
+__version__ = ('0', '5', '0')
 __license__ = 'MIT'
 
 
@@ -45,6 +45,8 @@ REQUEST_MAPPINGS = {
 
 ERROR_HANDLERS = {}
 
+MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'media')
+
 HTTP_MAPPINGS = {
     100: '100 CONTINUE',
     101: '101 SWITCHING PROTOCOLS',
@@ -116,11 +118,7 @@ class Redirect(RequestError):
     
     def __init__(self, url):
         self.url = url
-
-
-class Media(object):
-    path = '/media'
-    root = os.path.join(os.path.dirname(__file__), 'media')
+        self.args = [&quot;Redirecting to '%s'...&quot; % self.url]
 
 
 class Request(object):
@@ -129,8 +127,9 @@ class Request(object):
     POST = {}
     PUT = {}
     
-    def __init__(self, environ):
+    def __init__(self, environ, start_response):
         self._environ = environ
+        self._start_response = start_response
         self.setup_self()
     
     def setup_self(self):
@@ -185,23 +184,17 @@ class Request(object):
                 query_dict[field] = raw_data[field].value
         
         return query_dict
-    
-    def is_static(self):
-        return self.path.rstrip('/').startswith(Media.path)
 
 
 def handle_request(environ, start_response):
     &quot;&quot;&quot;The main handler. Dispatches to the user's code.&quot;&quot;&quot;
     try:
-        request = Request(environ)
-        
-        if request.is_static():
-            return static_file(request, start_response)
+        request = Request(environ, start_response)
         
         (re_url, url, callback), kwargs = find_matching_url(request)
         output = callback(request, **kwargs)
     except Exception, e:
-        return handle_error(e, environ, start_response)
+        return handle_error(e, request)
     
     ct = getattr(callback, 'content_type', 'text/html')
     status = getattr(callback, 'status', 200)
@@ -211,9 +204,9 @@ def handle_request(environ, start_response):
     return output
 
 
-def handle_error(exception, environ, start_response):
+def handle_error(exception, request):
     &quot;&quot;&quot;If an exception is thrown, deal with it and present an error page.&quot;&quot;&quot;
-    environ['wsgi.errors'].write(&quot;Exception occurred on '%s': %s\n&quot; % (environ['PATH_INFO'], exception[0]))
+    request._environ['wsgi.errors'].write(&quot;Exception occurred on '%s': %s\n&quot; % (request._environ['PATH_INFO'], exception[0]))
     
     if isinstance(exception, RequestError):
         status = getattr(exception, 'status', 404)
@@ -221,9 +214,9 @@ def handle_error(exception, environ, start_response):
         status = 500
     
     if status in ERROR_HANDLERS:
-        return ERROR_HANDLERS[status](exception, environ, start_response)
+        return ERROR_HANDLERS[status](request, exception)
     
-    return not_found(exception, environ, start_response)
+    return not_found(request, exception)
 
 
 def find_matching_url(request):
@@ -247,30 +240,39 @@ def add_slash(url):
     return url
 
 
-def check_url(url):
+def content_type(filename):
     &quot;&quot;&quot;
-    Ensures a URL can be registered.
+    Takes a guess at what the desired mime type might be for the requested file.
     
-    For now, this only ensures that it does not have the same path as the
-    MEDIA_ROOT.
+    Mostly only useful for static media files.
     &quot;&quot;&quot;
-    if url.startswith(Media.path):
-        raise RuntimeError(&quot;Url '%s' can not have the same path as the Media.path.&quot; % url)
+    ct = 'text/plain'
+    ct_guess = mimetypes.guess_type(filename)
+
+    if ct_guess[0] is not None:
+        ct = ct_guess[0]
+    
+    return ct
 
 
 # Static file handler
 
-def static_file(request, start_response, media=Media):
-    # Strip the Media.path from beginning.
-    valid_path = request.path.replace(media.path, '', 1)
+def static_file(request, filename, root=MEDIA_ROOT):
+    &quot;&quot;&quot;
+    Basic handler for serving up static media files.
+    
+    Accepts an optional root (filepath string, defaults to MEDIA_ROOT) parameter.
+    &quot;&quot;&quot;
+    if filename is None:
+        raise Forbidden(&quot;You must specify a file you'd like to access.&quot;)
     
     # Strip the '/' from the beginning/end.
-    valid_path = valid_path.strip('/')
+    valid_path = filename.strip('/')
     
     # Kill off any character trying to work their way up the filesystem.
     valid_path = valid_path.replace('//', '/').replace('/./', '/').replace('/../', '/')
     
-    desired_path = os.path.join(media.root, valid_path)
+    desired_path = os.path.join(root, valid_path)
     
     if not os.path.exists(desired_path):
         raise NotFound(&quot;File does not exist.&quot;)
@@ -278,15 +280,7 @@ def static_file(request, start_response, media=Media):
     if not os.access(desired_path, os.R_OK):
         raise Forbidden(&quot;You do not have permission to access this file.&quot;)
     
-    ct = 'text/plain'
-    ct_guess = mimetypes.guess_type(desired_path)
-    
-    if ct_guess[0] is not None:
-        ct = ct_guess[0]
-    
-    file_contents = open(desired_path, 'r').read()
-    start_response(HTTP_MAPPINGS[200], [('Content-Type', ct)])
-    return [file_contents]
+    return open(desired_path, 'r').read()
 
 
 # Decorators
@@ -297,7 +291,6 @@ def get(url):
         def new(*args, **kwargs):
             return method(*args, **kwargs)
         # Register.
-        check_url(url)
         re_url = re.compile(&quot;^%s$&quot; % add_slash(url))
         REQUEST_MAPPINGS['GET'].append((re_url, url, new))
         return new
@@ -310,7 +303,6 @@ def post(url):
         def new(*args, **kwargs):
             return method(*args, **kwargs)
         # Register.
-        check_url(url)
         re_url = re.compile(&quot;^%s$&quot; % add_slash(url))
         REQUEST_MAPPINGS['POST'].append((re_url, url, new))
         return new
@@ -323,7 +315,6 @@ def put(url):
         def new(*args, **kwargs):
             return method(*args, **kwargs)
         # Register.
-        check_url(url)
         re_url = re.compile(&quot;^%s$&quot; % add_slash(url))
         REQUEST_MAPPINGS['PUT'].append((re_url, url, new))
         new.status = 201
@@ -337,7 +328,6 @@ def delete(url):
         def new(*args, **kwargs):
             return method(*args, **kwargs)
         # Register.
-        check_url(url)
         re_url = re.compile(&quot;^%s$&quot; % add_slash(url))
         REQUEST_MAPPINGS['DELETE'].append((re_url, url, new))
         return new
@@ -358,26 +348,26 @@ def error(code):
 # Error handlers
 
 @error(403)
-def forbidden(exception, environ, start_response):
-    start_response(HTTP_MAPPINGS[403], [('Content-Type', 'text/plain')])
+def forbidden(request, exception):
+    request._start_response(HTTP_MAPPINGS[403], [('Content-Type', 'text/plain')])
     return ['Forbidden']
 
 
 @error(404)
-def not_found(exception, environ, start_response):
-    start_response(HTTP_MAPPINGS[404], [('Content-Type', 'text/plain')])
+def not_found(request, exception):
+    request._start_response(HTTP_MAPPINGS[404], [('Content-Type', 'text/plain')])
     return ['Not Found']
 
 
 @error(500)
-def app_error(exception, environ, start_response):
-    start_response(HTTP_MAPPINGS[500], [('Content-Type', 'text/plain')])
+def app_error(request, exception):
+    request._start_response(HTTP_MAPPINGS[500], [('Content-Type', 'text/plain')])
     return ['Application Error']
 
 
 @error(302)
-def redirect(exception, environ, start_response):
-    start_response(HTTP_MAPPINGS[302], [('Content-Type', 'text/plain'), ('Location', exception.url)])
+def redirect(request, exception):
+    request._start_response(HTTP_MAPPINGS[302], [('Content-Type', 'text/plain'), ('Location', exception.url)])
     return ['']
 
 </diff>
      <filename>itty.py</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>media/default.css</filename>
    </removed>
    <removed>
      <filename>media/itty.png</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>fdbcbd4f1a49b0d7d170211f7d898287c659baf9</id>
    </parent>
  </parents>
  <author>
    <name>Daniel Lindsley</name>
    <email>daniel@toastdriven.com</email>
  </author>
  <url>http://github.com/toastdriven/itty/commit/f0b8fa0e7deb3f4dce264d7cc7a38714714097e1</url>
  <id>f0b8fa0e7deb3f4dce264d7cc7a38714714097e1</id>
  <committed-date>2009-03-09T00:17:26-07:00</committed-date>
  <authored-date>2009-03-09T00:17:26-07:00</authored-date>
  <message>Refactored static file serving to be less opinionated.

You now simply set up your own functions to handle media directories just like normal requests. The automagic media serving is gone. See 'examples/static_files.py' for usage.

Thanks to mintchaos for pushing me to do the right thing.</message>
  <tree>9d5a3bdc0e99017420c11fc37a06c01eefd6c426</tree>
  <committer>
    <name>Daniel Lindsley</name>
    <email>daniel@toastdriven.com</email>
  </committer>
</commit>
