Commits

Ludvig Ericson committed 65f2c26 Merge

Merge upstream

Comments (0)

Files changed (41)

 426d64c7ef3e605eb7551980b3b9de3bb0001dab 0.5
 f972dac514934c6b993c5d5813a03da15bda6a46 0.5.1
 f0716ad49661076e0f129b9ee4e5f7118a25e730 0.6
+a32bf2b4175616347b2493ff58aebbc5e4392901 0.6.1
+3aa163672076c95e0e36e65cfbb28d79bf8368ef 0.6.2
 Werkzeug Changelog
 ==================
 
+Version 1.0
+-----------
+(first 1.0 release, release date to be announced)
+
+- fixed an issue where the SharedDataMiddleware could cause an
+  internal server error on weird paths when loading via pkg_resources.
+- fixed an URL generation bug that caused URLs to be invalid if a
+  generated component contains a colon.
+
+Version 0.6.2
+-------------
+(bugfix release, released on April 23th 2010)
+
+- renamed the attribute `implicit_seqence_conversion` attribute of the
+  request object to `implicit_sequence_conversion`.
+
 Version 0.6.1
 -------------
-(bugfix release, release date to be decided)
+(bugfix release, released on April 13th 2010)
 
+- heavily improved local objects.  Should pick up standalone greenlet
+  builds now and support proxies to free callables as well.  There is
+  also a stacked local now that makes it possible to invoke the same
+  application from within itself by pushing current request/response
+  on top of the stack.
+- routing build method will also build non-default method rules properly
+  if no method is provided.
 - added proper IPv6 support for the builtin server.
 - windows specific filesystem session store fixes.
   (should no be more stable under high concurrency)
 - fixed a bug with empty arguments in the werkzeug.script system.
 - fixed a bug where log lines will be duplicated if an application uses
   :meth:`logging.basicConfig` (#499)
+- added secure password hashing and checking functions.
+- `HEAD` is now implicitly added as method in the routing system if
+  `GET` is present.  Not doing that was considered a bug because often
+  code assumed that this is the case and in web servers that do not
+  normalize `HEAD` to `GET` this could break `HEAD` requests.
+- the script support can start SSL servers now.
 
 Version 0.6
 -----------
 API Changes
 ===========
 
+`0.6.2`
+    -   renamed the attribute `implicit_seqence_conversion` attribute of
+        the request object to `implicit_sequence_conversion`.  Because
+        this is a feature that is typically unused and was only in there
+        for the 0.6 series we consider this a bug that does not require
+        backwards compatibility support which would be impossible to
+        properly implement.
+
 `0.6`
     -   Old deprecations were removed.
     -   `cached_property.writeable` was deprecated.

docs/deployment/cgi.rst

 ===
 
 If all other deployment methods do not work, CGI will work for sure.  CGI
-is supported by all major browsers but usually has a less-than-optimal
+is supported by all major servers but usually has a less-than-optimal
 performance.
 
 This is also the way you can use a Werkzeug application on Google's
 
 .. sourcecode:: apache
 
-    ScriptName /app /path/to/the/application.cgi
+    ScriptAlias /app /path/to/the/application.cgi
 
 For more information consult the documentation of your webserver.

docs/deployment/fastcgi.rst

 
 This configuration binds the application to `/yourapplication`.  If you
 want the application to work in the URL root you have to work around a
-lighttpd bug with the `~werkzeug.contrib.fixers.LighttpdCGIRootFix` middleware.
+lighttpd bug with the :class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix`
+middleware.
 
 Make sure to apply it only if you are mounting the application the URL
 root.

docs/deployment/mod_wsgi.rst

     <VirtualHost *>
         ServerName example.com
 
-        WSGIDaemonProcess yourapplication user=user1 group=group1 processes=1 threads=5
+        WSGIDaemonProcess yourapplication user=user1 group=group1 processes=2 threads=5
         WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
 
         <Directory /var/www/yourapplication>
 This will try to get the request or return `None` if the request is not
 (yet?) available.
 
+Note that local objects cannot manage themselves, for that you need a local
+manager.  You can pass a local manager multiple locals or add additionals
+later by appending them to `manager.locals` and everytime the manager
+cleans up it will clean up all the data left in the locals for this
+context.
+
+.. autofunction:: release_local
+
 .. autoclass:: LocalManager
    :members: cleanup, make_middleware, middleware, get_ident
 
+.. autoclass:: LocalStack
+   :members: push, pop, top
+
 .. autoclass:: LocalProxy
    :members: _get_current_object
 
 .. autoclass:: Map
    :members:
 
+   .. attribute:: converters
+
+      The dictionary of converters.  This can be modified after the class
+      was created, but will only affect rules added after the
+      modification.  If the rules are defined with the list passed to the
+      class, the `converters` parameter to the constructor has to be used
+      instead.
+
 .. autoclass:: MapAdapter
    :members:
 

docs/tutorial.rst

 
     metadata = MetaData()
     session = scoped_session(lambda: create_session(application.database_engine,
-                             transactional=True), local_manager.get_ident)
+                             autocommit=False, autoflush=False),
+                             local_manager.get_ident)
 
     url_map = Map()
     def expose(rule, **kw):
 .. autofunction:: secure_filename
 
 .. autofunction:: bind_arguments
+
+
+Security Helpers
+================
+
+.. versionadded:: 0.6.1
+
+.. autofunction:: generate_password_hash
+
+.. autofunction:: check_password_hash
 
     Requirements:
     -   SQLAlchemy
-    -   Creoleparser >= 0.3.3
+    -   Creoleparser >= 0.7
     -   genshi
 
     You can obtain all packages in the Cheeseshop via easy_install.  You have
-    to have at least version 0.3.3 of Creoleparser.
+    to have at least version 0.7 of Creoleparser.
 
     Usage::
 
     can add more in a python shell by playing with the `Blog` model.
 
 `shorty`
-    A tinyurl clone for the Werkzeug 0.2 tutorial.
+    A tinyurl clone for the Werkzeug tutorial.
 
     Requirements:
     -   SQLAlchemy

examples/cupoftee/application.py

     def update_master(self):
         wait = self.interval
         while 1:
-            time.sleep(wait)
             if self.master.sync():
                 wait = self.interval
             else:
                 wait = self.interval // 2
+            time.sleep(wait)
 
     def dispatch_request(self, request):
         url_adapter = url_map.bind_to_environ(request.environ)

examples/cupoftee/network.py

 from cupoftee.utils import unicodecmp
 
 
-GAMETYPES = dict(enumerate(('dm', 'tdm', 'ctf')))
-
-
 class ServerError(Exception):
     pass
 
         to_delete = set(self.servers)
         for x in xrange(1, 17):
             addr = ('master%d.teeworlds.com' % x, 8300)
+            print addr
             try:
                 self._sync_master(addr, to_delete)
-            except (socket.error, socket.timeout, IOError):
+            except (socket.error, socket.timeout, IOError), e:
                 continue
         for server_id in to_delete:
             self.servers.pop(server_id, None)
         self.version, server_name, map_name = bits[:3]
         self.name = server_name.decode('latin1')
         self.map = map_name.decode('latin1')
-        self.gametype_id, self.flags, self.progression, player_count, \
-            self.max_players = map(int, bits[3:8])
-        self.gametype = GAMETYPES.get(self.gametype_id, 'unknown')
+        self.gametype = bits[3]
+        self.flags, self.progression, player_count, \
+            self.max_players = map(int, bits[4:8])
 
         # sync the player stats
         players = dict((p.name, p) for p in self.players)

examples/manage-plnt.py

 
 def action_initdb():
     """Initialize the database"""
-    from plnt.database import Blog, Session
+    from plnt.database import Blog, session
     make_app().init_database()
     # and now fill in some python blogs everybody should read (shamelessly
     # added my own blog too)
-    Blog('Armin Ronacher', 'http://lucumr.pocoo.org/',
-         'http://lucumr.pocoo.org/cogitations/feed/'),
-    Blog('Georg Brandl', 'http://pyside.blogspot.com/',
-         'http://pyside.blogspot.com/feeds/posts/default')
-    Blog('Ian Bicking', 'http://blog.ianbicking.org/',
-         'http://blog.ianbicking.org/feed/')
-    Blog('Amir Salihefendic', 'http://amix.dk/',
-         'http://feeds.feedburner.com/amixdk')
-    Blog('Christopher Lenz', 'http://www.cmlenz.net/blog/',
-         'http://www.cmlenz.net/blog/atom.xml')
-    Blog('Frederick Lundh', 'http://online.effbot.org/',
-         'http://online.effbot.org/rss.xml')
+    blogs = [
+        Blog('Armin Ronacher', 'http://lucumr.pocoo.org/',
+             'http://lucumr.pocoo.org/cogitations/feed/'),
+        Blog('Georg Brandl', 'http://pyside.blogspot.com/',
+             'http://pyside.blogspot.com/feeds/posts/default'),
+        Blog('Ian Bicking', 'http://blog.ianbicking.org/',
+             'http://blog.ianbicking.org/feed/'),
+        Blog('Amir Salihefendic', 'http://amix.dk/',
+             'http://feeds.feedburner.com/amixdk'),
+        Blog('Christopher Lenz', 'http://www.cmlenz.net/blog/',
+             'http://www.cmlenz.net/blog/atom.xml'),
+        Blog('Frederick Lundh', 'http://online.effbot.org/',
+             'http://online.effbot.org/rss.xml')
+    ]
     # okay. got tired here.  if someone feels that he is missing, drop me
     # a line ;-)
-    Session().commit()
+    for blog in blogs:
+        session.add(blog)
+    session.commit()
     print 'Initialized database, now run manage-plnt.py sync to get the posts'
 
 

examples/plnt/database.py

 """
 from sqlalchemy import MetaData, Table, Column, ForeignKey, Boolean, \
      Integer, String, DateTime
-from sqlalchemy.orm import dynamic_loader, scoped_session, create_session
+from sqlalchemy.orm import dynamic_loader, scoped_session, create_session, \
+     mapper
 from plnt.utils import application, local_manager
 
 
 def new_db_session():
     return create_session(application.database_engine, autoflush=True,
-                          transactional=True)
+                          autocommit=False)
 
 metadata = MetaData()
-Session = scoped_session(new_db_session, local_manager.get_ident)
+session = scoped_session(new_db_session, local_manager.get_ident)
 
 
 blog_table = Table('blogs', metadata,
 
 
 class Blog(object):
+    query = session.query_property()
 
     def __init__(self, name, url, feed_url, description=u''):
         self.name = name
 
 
 class Entry(object):
+    query = session.query_property()
 
     def __repr__(self):
         return '<%s %r>' % (self.__class__.__name__, self.guid)
 
 
-Session.mapper(Entry, entry_table)
-Session.mapper(Blog, blog_table, properties=dict(
+mapper(Entry, entry_table)
+mapper(Blog, blog_table, properties=dict(
     entries=dynamic_loader(Entry, backref='blog')
 ))

examples/plnt/sync.py

 from time import time
 from datetime import datetime
 from werkzeug import escape
-from plnt.database import Blog, Entry, Session
+from plnt.database import Blog, Entry, session
 from plnt.utils import strip_tags, nl2p
 
 
             entry.text = text
             entry.pub_date = pub_date
             entry.last_update = updated
+            session.add(entry)
 
-    Session().commit()
+    session.commit()

examples/plnt/utils.py

 """
 import re
 from os import path
-from jinja import Environment, FileSystemLoader
+from jinja2 import Environment, FileSystemLoader
 from werkzeug import Response, Local, LocalManager, url_encode, \
      url_quote, cached_property
 from werkzeug.routing import Map, Rule

examples/plnt/webapp.py

 from werkzeug import SharedDataMiddleware, ClosingIterator, Request
 from werkzeug.exceptions import HTTPException, NotFound
 from plnt.utils import local, local_manager, url_map, endpoints
-from plnt.database import Session, metadata
+from plnt.database import session, metadata
 
 # import the views module because it contains setup code
 import plnt.views
         except HTTPException, e:
             response = e
         return ClosingIterator(response(environ, start_response),
-                               Session.remove)
+                               session.remove)
 
     def __call__(self, environ, start_response):
         return self._dispatch(environ, start_response)

examples/shorty/utils.py

 url_map = Map([Rule('/static/<file>', endpoint='static', build_only=True)])
 
 session = scoped_session(lambda: create_session(application.database_engine,
-                         transactional=True), local_manager.get_ident)
+                                                autocommit=False,
+                                                autoflush=False))
 jinja_env = Environment(loader=FileSystemLoader(TEMPLATE_PATH))
 
 

examples/simplewiki/actions.py

             change_note = request.form.get('change_note', '')
             if page is None:
                 page = Page(page_name)
-            revision = Revision(page, text, change_note)
+                session.add(page)
+            session.add(Revision(page, text, change_note))
             session.commit()
             return redirect(href(page.name))
 
                     change_note = request.form.get('change_note', '')
                     change_note = 'revert' + (change_note and ': ' +
                                               change_note or '')
-                    revision = Revision(page, old_revision.text,
-                                        change_note)
+                    session.add(Revision(page, old_revision.text,
+                                         change_note))
                     session.commit()
                     return redirect(href(page_name))
 

examples/simplewiki/database.py

 from datetime import datetime
 from sqlalchemy import Table, Column, Integer, String, DateTime, \
      ForeignKey, MetaData, join
-from sqlalchemy.orm import relation, create_session, scoped_session
+from sqlalchemy.orm import relation, create_session, scoped_session, \
+     mapper
 from simplewiki.utils import application, local_manager, parse_creole
 
 
     new revisions.  It's also used for the diff system and the revision
     log.
     """
+    query = session.query_property()
 
     def __init__(self, page, text, change_note='', timestamp=None):
         if isinstance(page, (int, long)):
     Represents a simple page without any revisions.  This is for example
     used in the page index where the page contents are not relevant.
     """
+    query = session.query_property()
 
     def __init__(self, name):
         self.name = name
     and the ability of SQLAlchemy to map to joins we can combine `Page` and
     `Revision` into one class here.
     """
+    query = session.query_property()
 
     def __init__(self):
         raise TypeError('cannot create WikiPage instances, use the Page and '
 
 
 # setup mappers
-session.mapper(Revision, revision_table)
-session.mapper(Page, page_table, properties=dict(
+mapper(Revision, revision_table)
+mapper(Page, page_table, properties=dict(
     revisions=relation(Revision, backref='page',
                        order_by=Revision.revision_id.desc())
 ))
-session.mapper(RevisionedPage, join(page_table, revision_table), properties=dict(
+mapper(RevisionedPage, join(page_table, revision_table), properties=dict(
     page_id=[page_table.c.page_id, revision_table.c.page_id],
 ))

examples/simplewiki/utils.py

     :license: BSD.
 """
 import difflib
+import creoleparser
 from os import path
 from genshi import Stream
 from genshi.template import TemplateLoader
-from creoleparser import Parser, Creole10
 from werkzeug import BaseRequest, BaseResponse, Local, LocalManager, \
      url_encode, url_quote, redirect, cached_property
 
 application = local('application')
 
 # create a new creole parser
-creole_parser = Parser(dialect=Creole10(
-    wiki_links_base_url='',
-    wiki_links_path_func=lambda page_name: href(page_name),
-    wiki_links_space_char='_',
-    no_wiki_monospace=True,
-    use_additions=True
-))
+creole_parser = creoleparser.Parser(
+    dialect=creoleparser.create_dialect(creoleparser.creole10_base,
+        wiki_links_base_url='',
+        wiki_links_path_func=lambda page_name: href(page_name),
+        wiki_links_space_char='_',
+        no_wiki_monospace=True
+    ),
+    method='html'
+)
 
 
 def generate_template(template_name, **context):
 
 setup(
     name='Werkzeug',
-    version='0.6.1',
+    version='1.0',
     url='http://werkzeug.pocoo.org/',
     license='BSD',
     author='Armin Ronacher',

tests/multipart/ie7_full_path_request.txt

Binary file added.

tests/test_formparser.py

                                content_length=len(data))
         assert response.data == repr(text)
 
-
+def test_ie7_unc_path():
+    client = Client(form_data_consumer, Response)
+    data_file = join(dirname(__file__), 'multipart', 'ie7_full_path_request.txt')
+    data = get_contents(data_file)
+    boundary = '---------------------------7da36d1b4a0164'
+    response = client.post('/?object=cb_file_upload_multiple', data=data, content_type=
+                               'multipart/form-data; boundary="%s"' % boundary, content_length=len(data))
+    lines = response.data.split('\n', 3)
+    assert lines[0] == repr(u'Sellersburg Town Council Meeting 02-22-2010doc.doc'), lines[0]
+    
 def test_end_of_file_multipart():
     """Test for multipart files ending unexpectedly"""
     # This test looks innocent but it was actually timeing out in

tests/test_local.py

 
 from nose.tools import assert_raises
 
-from werkzeug import Local, LocalManager
+from werkzeug import Local, LocalManager, LocalStack, LocalProxy, \
+     release_local
+from werkzeug.local import get_ident # for testing purposes only
 
 
 def test_basic_local():
     delfoo()
     assert_raises(AttributeError, lambda: l.foo)
     assert_raises(AttributeError, delfoo)
+
+    release_local(l)
+
+
+def test_local_release():
+    """Locals work without manager"""
+    loc = Local()
+    loc.foo = 42
+    release_local(loc)
+    assert not hasattr(loc, 'foo')
+
+    ls = LocalStack()
+    ls.push(42)
+    release_local(ls)
+    assert ls.top is None
+
+
+def test_local_proxy():
+    """Tests some proxy operations"""
+    foo = []
+    ls = LocalProxy(lambda: foo)
+    ls.append(42)
+    ls.append(23)
+    ls[1:] = [1, 2, 3]
+    assert foo == [42, 1, 2, 3]
+    assert repr(foo) == repr(ls)
+    assert foo[0] == 42
+    foo += [1]
+    assert list(foo) == [42, 1, 2, 3, 1]
+
+
+def test_local_stack():
+    """Test the LocalStack"""
+    ident = get_ident()
+
+    ls = LocalStack()
+    assert ident not in ls._local.__storage__
+    assert ls.top is None
+    ls.push(42)
+    assert ident in ls._local.__storage__
+    assert ls.top == 42
+    ls.push(23)
+    assert ls.top == 23
+    ls.pop()
+    assert ls.top == 42
+    ls.pop()
+    assert ls.top is None
+    assert ls.pop() is None
+    assert ls.pop() is None
+
+    proxy = ls()
+    ls.push([1, 2])
+    assert proxy == [1, 2]
+    ls.push((1, 2))
+    assert proxy == (1, 2)
+    ls.pop()
+    ls.pop()
+    assert repr(proxy) == '<LocalProxy unbound>'
+
+    assert ident not in ls._local.__storage__
+
+
+def test_local_proxies_with_callables():
+    """Use a callable with a local proxy"""
+    foo = 42
+    ls = LocalProxy(lambda: foo)
+    assert ls == 42
+    foo = [23]
+    ls.append(42)
+    assert ls == [23, 42]
+    assert foo == [23, 42]

tests/test_routing.py

 from werkzeug.wrappers import Response
 from werkzeug.datastructures import ImmutableDict
 from werkzeug.routing import Map, Rule, NotFound, BuildError, RequestRedirect, \
-     RuleTemplate, Submount, EndpointPrefix, Subdomain, UnicodeConverter
+     RuleTemplate, Submount, EndpointPrefix, Subdomain, UnicodeConverter, \
+     MethodNotAllowed
 from werkzeug.test import create_environ
 
 
         'http://example.org/bar/0.815?bif=1.0'
     assert adapter.build('barf', {'bazf': 0.815, 'bif' : 1.0},
         append_unknown=False) == 'http://example.org/bar/0.815'
+
+
+def test_method_fallback():
+    """Test that building falls back to different rules"""
+    map = Map([
+        Rule('/', endpoint='index', methods=['GET']),
+        Rule('/<name>', endpoint='hello_name', methods=['GET']),
+        Rule('/select', endpoint='hello_select', methods=['POST']),
+        Rule('/search_get', endpoint='search', methods=['GET']),
+        Rule('/search_post', endpoint='search', methods=['POST'])
+    ])
+    adapter = map.bind('example.com')
+    assert adapter.build('index') == '/'
+    assert adapter.build('index', method='GET') == '/'
+    assert adapter.build('hello_name', {'name': 'foo'}) == '/foo'
+    assert adapter.build('hello_select') == '/select'
+    assert adapter.build('hello_select', method='POST') == '/select'
+    assert adapter.build('search') == '/search_get'
+    assert adapter.build('search', method='GET') == '/search_get'
+    assert adapter.build('search', method='POST') == '/search_post'
+
+
+def test_implicit_head():
+    """Test implicit HEAD in URL rules where GET is present"""
+    url_map = Map([
+        Rule('/get', methods=['GET'], endpoint='a'),
+        Rule('/post', methods=['POST'], endpoint='b')
+    ])
+    adapter = url_map.bind('example.org')
+    assert adapter.match('/get', method='HEAD') == ('a', {})
+    assert_raises(MethodNotAllowed, adapter.match, '/post', method='HEAD')
+
+
+def test_protocol_joining_bug():
+    m = Map([Rule('/<foo>', endpoint='x')])
+    a = m.bind('example.org')
+    assert a.build('x', {'foo': 'x:y'}) == '/x:y'
+    assert a.build('x', {'foo': 'x:y'}, force_external=True) == 'http://example.org/x:y'

tests/test_security.py

+# -*- coding: utf-8 -*-
+"""
+    werkzeug.security test
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD license.
+"""
+from werkzeug import generate_password_hash, check_password_hash
+
+
+
+def test_password_hashing():
+    """Test the password hashing and password hash checking"""
+    hash1 = generate_password_hash('default')
+    hash2 = generate_password_hash(u'default', method='sha1')
+    assert hash1 != hash2
+    assert check_password_hash(hash1, 'default')
+    assert check_password_hash(hash2, 'default')
+    assert hash1.startswith('sha1$')
+    assert hash2.startswith('sha1$')
+
+    fakehash = generate_password_hash('default', method='plain')
+    assert fakehash == 'plain$$default'
+    assert check_password_hash(fakehash, 'default')
+
+    mhash = generate_password_hash(u'default', method='md5')
+    assert mhash.startswith('md5$')
+    assert check_password_hash(mhash, 'default')
+
+    legacy = 'md5$$c21f969b5f03d33d43e04f8f136e7682'
+    assert check_password_hash(legacy, 'default')
+
+    legacy = u'md5$$c21f969b5f03d33d43e04f8f136e7682'
+    assert check_password_hash(legacy, 'default')

tests/test_wrappers.py

 
     # automatic generator sequence conversion
     resp.response = generate_items()
-    resp.implicit_seqence_conversion = False
+    resp.implicit_sequence_conversion = False
     assert resp.is_streamed
     assert not resp.is_sequence
     assert_raises(RuntimeError, lambda: resp.data)
 
     # stream makes it a list no matter how the conversion is set
     for val in True, False:
-        resp.implicit_seqence_conversion = val
+        resp.implicit_sequence_conversion = val
         resp.response = ("foo", "bar")
         assert resp.is_sequence
         resp.stream.write('baz')

werkzeug/__init__.py

 # import mapping to objects in other modules
 all_by_module = {
     'werkzeug.debug':       ['DebuggedApplication'],
-    'werkzeug.local':       ['Local', 'LocalManager', 'LocalProxy'],
+    'werkzeug.local':       ['Local', 'LocalManager', 'LocalProxy',
+                             'LocalStack', 'release_local'],
     'werkzeug.templates':   ['Template'],
     'werkzeug.serving':     ['run_simple'],
     'werkzeug.test':        ['Client', 'EnvironBuilder', 'create_environ',
                              'UserAgentMixin', 'AuthorizationMixin',
                              'WWWAuthenticateMixin',
                              'CommonRequestDescriptorsMixin'],
+    'werkzeug.security':    ['generate_password_hash', 'check_password_hash'],
     # the undocumented easteregg ;-)
     'werkzeug._internal':   ['_easteregg']
 }

werkzeug/contrib/limiter.py

         self.maximum_size = maximum_size
 
     def __call__(self, environ, start_response):
-        limit = min(limit, int(environ.get('CONTENT_LENGTH') or 0))
+        limit = min(self.maximum_size, int(environ.get('CONTENT_LENGTH') or 0))
         environ['wsgi.input'] = LimitedStream(environ['wsgi.input'], limit)
         return self.app(environ, start_response)

werkzeug/contrib/securecookie.py

         from werkzeug.contrib.securecookie import SecureCookie
 
         # don't use this key but a different one; you could just use
-        # os.unrandom(20) to get something random
+        # os.urandom(20) to get something random
         SECRET_KEY = '\xfa\xdd\xb8z\xae\xe0}4\x8b\xea'
 
         class Request(BaseRequest):
     return '"%s"' % value.replace('\\', '\\\\').replace('"', '\\"')
 
 
-def unquote_header_value(value):
+def unquote_header_value(value, is_filename = False):
     r"""Unquotes a header value.  (Reversal of :func:`quote_header_value`).
     This does not use the real unquoting but what browsers are actually
     using for quoting.
         # RFC is met will result in bugs with internet explorer and
         # probably some other browsers as well.  IE for example is
         # uploading files with "C:\foo\bar.txt" as filename
-        value = value[1:-1].replace('\\\\', '\\').replace('\\"', '"')
+        value = value[1:-1]
+        
+        # if this is a filename and the starting characters look like
+        # a UNC path, then just return the value without quotes.  Using the
+        # replace sequence below on a UNC path has the effect of turning
+        # the leading double slash into a single slash and then
+        # _fix_ie_filename() doesn't work correctly.  See #458.
+        if not is_filename or value[:2] != '\\\\':
+            return value.replace('\\\\', '\\').replace('\\"', '"')
     return value
 
 
             key, value = match.groups()
             key = unquote_header_value(key)
             if value is not None:
-                value = unquote_header_value(value)
+                value = unquote_header_value(value, key == 'filename')
             yield key, value
 
     if not value:

werkzeug/local.py

     :license: BSD, see LICENSE for more details.
 """
 try:
-    from py.magic import greenlet
-    get_current_greenlet = greenlet.getcurrent
-    del greenlet
-except: # pragma: no cover
-    # catch all, py.* fails with so many different errors.
-    get_current_greenlet = int
+    from greenlet import getcurrent as get_current_greenlet
+except ImportError: # pragma: no cover
+    try:
+        from py.magic import greenlet
+        get_current_greenlet = greenlet.getcurrent
+        del greenlet
+    except:
+        # catch all, py.* fails with so many different errors.
+        get_current_greenlet = int
 try:
     from thread import get_ident as get_current_thread, allocate_lock
 except ImportError: # pragma: no cover
     get_ident = lambda: (get_current_thread(), get_current_greenlet())
 
 
+def release_local(local):
+    """Releases the contents of the local for the current context.
+    This makes it possible to use locals without a manager.
+
+    Example::
+
+        >>> loc = Local()
+        >>> loc.foo = 42
+        >>> release_local(loc)
+        >>> hasattr(loc, 'foo')
+        False
+
+    With this function one can release :class:`Local` objects as well
+    as :class:`StackLocal` objects.  However it is not possible to
+    release data held by proxies that way, one always has to retain
+    a reference to the underlying local object in order to be able
+    to release it.
+
+    .. versionadded:: 0.6.1
+    """
+    local.__release_local__()
+
+
 class Local(object):
     __slots__ = ('__storage__', '__lock__')
 
         """Create a proxy for a name."""
         return LocalProxy(self, proxy)
 
+    def __release_local__(self):
+        self.__storage__.pop(get_ident(), None)
+
     def __getattr__(self, name):
         self.__lock__.acquire()
         try:
             self.__lock__.release()
 
 
+class LocalStack(object):
+    """This class works similar to a :class:`Local` but keeps a stack
+    of objects instead.  This is best explained with an example::
+
+        >>> ls = LocalStack()
+        >>> ls.push(42)
+        >>> ls.top
+        42
+        >>> ls.push(23)
+        >>> ls.top
+        23
+        >>> ls.pop()
+        23
+        >>> ls.top
+        42
+
+    They can be force released by using a :class:`LocalManager` or with
+    the :func:`release_local` function but the correct way is to pop the
+    item from the stack after using.  When the stack is empty it will
+    no longer be bound to the current context (and as such released).
+
+    By calling the stack without arguments it returns a proxy that resolves to
+    the topmost item on the stack.
+
+    .. versionadded:: 0.6.1
+    """
+
+    def __init__(self):
+        self._local = Local()
+        self._lock = allocate_lock()
+
+    def __release_local__(self):
+        self._local.__release_local__()
+
+    def __call__(self):
+        def _lookup():
+            rv = self.top
+            if rv is None:
+                raise RuntimeError('object unbound')
+            return rv
+        return LocalProxy(_lookup)
+
+    def push(self, obj):
+        """Pushes a new item to the stack"""
+        self._lock.acquire()
+        try:
+            rv = getattr(self._local, 'stack', None)
+            if rv is None:
+                self._local.stack = rv = []
+            rv.append(obj)
+            return rv
+        finally:
+            self._lock.release()
+
+    def pop(self):
+        """Removes the topmost item from the stack, will return the
+        old value or `None` if the stack was already empty.
+        """
+        self._lock.acquire()
+        try:
+            stack = getattr(self._local, 'stack', None)
+            if stack is None:
+                return None
+            elif len(stack) == 1:
+                release_local(self._local)
+                return stack[-1]
+            else:
+                return stack.pop()
+        finally:
+            self._lock.release()
+
+    @property
+    def top(self):
+        """The topmost item on the stack.  If the stack is empty,
+        `None` is returned.
+        """
+        try:
+            return self._local.stack[-1]
+        except (AttributeError, IndexError):
+            return None
+
+
 class LocalManager(object):
     """Local objects cannot manage themselves. For that you need a local
     manager.  You can pass a local manager multiple locals or add them later
     by appending them to `manager.locals`.  Everytime the manager cleans up
     it, will clean up all the data left in the locals for this context.
+
+    .. versionchanged:: 0.6.1
+       Instead of a manager the :func:`release_local` function can be used
+       as well.
     """
 
     def __init__(self, locals=None):
         """
         ident = self.get_ident()
         for local in self.locals:
-            local.__storage__.pop(ident, None)
+            release_local(local)
 
     def make_middleware(self, app):
         """Wrap a WSGI application so that cleaning up happens after
 
         from werkzeug import Local
         l = Local()
+
+        # these are proxies
         request = l('request')
         user = l('user')
 
+
+        from werkzeug import LocalStack
+        _response_local = LocalStack()
+
+        # this is a proxy
+        response = _response_local()
+
     Whenever something is bound to l.user / l.request the proxy objects
-    will forward all operations.  If no object is bound a `RuntimeError`
+    will forward all operations.  If no object is bound a :exc:`RuntimeError`
     will be raised.
+
+    To create proxies to :class:`Local` or :class:`LocalStack` objects,
+    call the object as shown above.  If you want to have a proxy to an
+    object looked up by a function, you can (as of Werkzeug 0.6.1) pass
+    a function to the :class:`LocalProxy` constructor::
+
+        session = LocalProxy(lambda: get_current_request().session)
+
+    .. versionchanged:: 0.6.1
+       The class can be instanciated with a callable as well now.
     """
     __slots__ = ('__local', '__dict__', '__name__')
 
-    def __init__(self, local, name):
+    def __init__(self, local, name=None):
         object.__setattr__(self, '_LocalProxy__local', local)
         object.__setattr__(self, '__name__', name)
 
         object behind the proxy at a time for performance reasons or because
         you want to pass the object into a different context.
         """
+        if not hasattr(self.__local, '__release_local__'):
+            return self.__local()
         try:
             return getattr(self.__local, self.__name__)
         except AttributeError:
             raise RuntimeError('no object bound to %s' % self.__name__)
-    __current_object = property(_get_current_object)
 
+    @property
     def __dict__(self):
         try:
-            return self.__current_object.__dict__
+            return self._get_current_object().__dict__
         except RuntimeError:
-            return AttributeError('__dict__')
-    __dict__ = property(__dict__)
+            raise AttributeError('__dict__')
 
     def __repr__(self):
         try:
-            obj = self.__current_object
+            obj = self._get_current_object()
         except RuntimeError:
             return '<%s unbound>' % self.__class__.__name__
         return repr(obj)
 
     def __nonzero__(self):
         try:
-            return bool(self.__current_object)
+            return bool(self._get_current_object())
         except RuntimeError:
             return False
 
     def __unicode__(self):
         try:
-            return unicode(self.__current_object)
+            return unicode(self._get_current_object())
         except RuntimeError:
             return repr(self)
 
     def __dir__(self):
         try:
-            return dir(self.__current_object)
+            return dir(self._get_current_object())
         except RuntimeError:
             return []
 
     def __getattr__(self, name):
         if name == '__members__':
-            return dir(self.__current_object)
-        return getattr(self.__current_object, name)
+            return dir(self._get_current_object())
+        return getattr(self._get_current_object(), name)
 
     def __setitem__(self, key, value):
-        self.__current_object[key] = value
+        self._get_current_object()[key] = value
 
     def __delitem__(self, key):
-        del self.__current_object[key]
+        del self._get_current_object()[key]
 
     def __setslice__(self, i, j, seq):
-        self.__current_object[i:j] = seq
+        self._get_current_object()[i:j] = seq
 
     def __delslice__(self, i, j):
-        del self.__current_object[i:j]
+        del self._get_current_object()[i:j]
 
-    __setattr__ = lambda x, n, v: setattr(x.__current_object, n, v)
-    __delattr__ = lambda x, n: delattr(x.__current_object, n)
-    __str__ = lambda x: str(x.__current_object)
-    __lt__ = lambda x, o: x.__current_object < o
-    __le__ = lambda x, o: x.__current_object <= o
-    __eq__ = lambda x, o: x.__current_object == o
-    __ne__ = lambda x, o: x.__current_object != o
-    __gt__ = lambda x, o: x.__current_object > o
-    __ge__ = lambda x, o: x.__current_object >= o
-    __cmp__ = lambda x, o: cmp(x.__current_object, o)
-    __hash__ = lambda x: hash(x.__current_object)
-    __call__ = lambda x, *a, **kw: x.__current_object(*a, **kw)
-    __len__ = lambda x: len(x.__current_object)
-    __getitem__ = lambda x, i: x.__current_object[i]
-    __iter__ = lambda x: iter(x.__current_object)
-    __contains__ = lambda x, i: i in x.__current_object
-    __getslice__ = lambda x, i, j: x.__current_object[i:j]
-    __add__ = lambda x, o: x.__current_object + o
-    __sub__ = lambda x, o: x.__current_object - o
-    __mul__ = lambda x, o: x.__current_object * o
-    __floordiv__ = lambda x, o: x.__current_object // o
-    __mod__ = lambda x, o: x.__current_object % o
-    __divmod__ = lambda x, o: x.__current_object.__divmod__(o)
-    __pow__ = lambda x, o: x.__current_object ** o
-    __lshift__ = lambda x, o: x.__current_object << o
-    __rshift__ = lambda x, o: x.__current_object >> o
-    __and__ = lambda x, o: x.__current_object & o
-    __xor__ = lambda x, o: x.__current_object ^ o
-    __or__ = lambda x, o: x.__current_object | o
-    __div__ = lambda x, o: x.__current_object.__div__(o)
-    __truediv__ = lambda x, o: x.__current_object.__truediv__(o)
-    __neg__ = lambda x: -(x.__current_object)
-    __pos__ = lambda x: +(x.__current_object)
-    __abs__ = lambda x: abs(x.__current_object)
-    __invert__ = lambda x: ~(x.__current_object)
-    __complex__ = lambda x: complex(x.__current_object)
-    __int__ = lambda x: int(x.__current_object)
-    __long__ = lambda x: long(x.__current_object)
-    __float__ = lambda x: float(x.__current_object)
-    __oct__ = lambda x: oct(x.__current_object)
-    __hex__ = lambda x: hex(x.__current_object)
-    __index__ = lambda x: x.__current_object.__index__()
+    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
+    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
+    __str__ = lambda x: str(x._get_current_object())
+    __lt__ = lambda x, o: x._get_current_object() < o
+    __le__ = lambda x, o: x._get_current_object() <= o
+    __eq__ = lambda x, o: x._get_current_object() == o
+    __ne__ = lambda x, o: x._get_current_object() != o
+    __gt__ = lambda x, o: x._get_current_object() > o
+    __ge__ = lambda x, o: x._get_current_object() >= o
+    __cmp__ = lambda x, o: cmp(x._get_current_object(), o)
+    __hash__ = lambda x: hash(x._get_current_object())
+    __call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
+    __len__ = lambda x: len(x._get_current_object())
+    __getitem__ = lambda x, i: x._get_current_object()[i]
+    __iter__ = lambda x: iter(x._get_current_object())
+    __contains__ = lambda x, i: i in x._get_current_object()
+    __getslice__ = lambda x, i, j: x._get_current_object()[i:j]
+    __add__ = lambda x, o: x._get_current_object() + o
+    __sub__ = lambda x, o: x._get_current_object() - o
+    __mul__ = lambda x, o: x._get_current_object() * o
+    __floordiv__ = lambda x, o: x._get_current_object() // o
+    __mod__ = lambda x, o: x._get_current_object() % o
+    __divmod__ = lambda x, o: x._get_current_object().__divmod__(o)
+    __pow__ = lambda x, o: x._get_current_object() ** o
+    __lshift__ = lambda x, o: x._get_current_object() << o
+    __rshift__ = lambda x, o: x._get_current_object() >> o
+    __and__ = lambda x, o: x._get_current_object() & o
+    __xor__ = lambda x, o: x._get_current_object() ^ o
+    __or__ = lambda x, o: x._get_current_object() | o
+    __div__ = lambda x, o: x._get_current_object().__div__(o)
+    __truediv__ = lambda x, o: x._get_current_object().__truediv__(o)
+    __neg__ = lambda x: -(x._get_current_object())
+    __pos__ = lambda x: +(x._get_current_object())
+    __abs__ = lambda x: abs(x._get_current_object())
+    __invert__ = lambda x: ~(x._get_current_object())
+    __complex__ = lambda x: complex(x._get_current_object())
+    __int__ = lambda x: int(x._get_current_object())
+    __long__ = lambda x: long(x._get_current_object())
+    __float__ = lambda x: float(x._get_current_object())
+    __oct__ = lambda x: oct(x._get_current_object())
+    __hex__ = lambda x: hex(x._get_current_object())
+    __index__ = lambda x: x._get_current_object().__index__()
     __coerce__ = lambda x, o: x.__coerce__(x, o)
     __enter__ = lambda x: x.__enter__()
     __exit__ = lambda x, *a, **kw: x.__exit__(*a, **kw)

werkzeug/posixemulation.py

     :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
     :license: BSD, see LICENSE for more details.
 """
+import sys
 import os
 import errno
 import time

werkzeug/routing.py

         endpoints for `POST` and `GET`.  If methods are defined and the path
         matches but the method matched against is not in this list or in the
         list of another rule for that path the error raised is of the type
-        `MethodNotAllowed` rather than `NotFound`.
+        `MethodNotAllowed` rather than `NotFound`.  If `GET` is present in the
+        list of methods and `HEAD` is not, `HEAD` is added automatically.
+
+        .. versionchanged:: 0.6.1
+           `HEAD` is now automatically added to the methods if `GET` is
+           present.  The reason for this is that existing code often did not
+           work properly in servers not rewriting `HEAD` to `GET`
+           automatically and it was not documented how `HEAD` should be
+           treated.  This was considered a bug in Werkzeug because of that.
 
     `strict_slashes`
         Override the `Map` setting for `strict_slashes` only for this rule. If
             self.methods = None
         else:
             self.methods = set([x.upper() for x in methods])
+            if 'HEAD' not in self.methods and 'GET' in self.methods:
+                self.methods.add('HEAD')
         self.endpoint = endpoint
         self.greediness = 0
         self.redirect_to = redirect_to
 
-        self._trace = []
         if defaults is not None:
             self.arguments = set(map(str, defaults))
         else:
             self.arguments = set()
-        self._converters = {}
-        self._regex = None
-        self._weights = []
+        self._trace = self._converters = self._regex = self._weights = None
 
     def empty(self):
         """Return an unbound copy of this rule.  This can be useful if you
     def get_rules(self, map):
         yield self
 
-    def bind(self, map):
+    def refresh(self):
+        """Rebinds and refreshes the URL.  Call this if you modified the
+        rule in place.
+
+        :internal:
+        """
+        self.bind(self.map, rebind=True)
+
+    def bind(self, map, rebind=False):
         """Bind the url to a map and create a regular expression based on
         the information from the rule itself and the defaults from the map.
 
         :internal:
         """
-        if self.map is not None:
+        if self.map is not None and not rebind:
             raise RuntimeError('url rule %r already bound to map %r' %
                                (self, self.map))
         self.map = map
         rule = self.subdomain + '|' + (self.is_leaf and self.rule
                                        or self.rule.rstrip('/'))
 
+        self._trace = []
+        self._converters = {}
+        self._weights = []
+
         regex_parts = []
         for converter, arguments, variable in parse_rule(rule):
             if converter is None:
         subdomain, url = (u''.join(tmp)).split('|', 1)
 
         if append_unknown:
-            query_vars = {}
-            for key in set(values) - processed:
-                query_vars[key] = unicode(values[key])
+            query_vars = MultiDict(values)
+            for key in processed:
+                if key in query_vars:
+                    del query_vars[key]
+
             if query_vars:
                 url += '?' + url_encode(query_vars, self.map.charset,
                                         sort=self.map.sort_parameters,
                self.endpoint == rule.endpoint and self != rule and \
                self.arguments == rule.arguments
 
-    def suitable_for(self, values, method):
+    def suitable_for(self, values, method=None):
         """Check if the dict of values has enough data for url generation.
 
         :internal:
         """
-        if self.methods is not None and method not in self.methods:
-            return False
+        if method is not None:
+            if self.methods is not None and method not in self.methods:
+                return False
 
         valueset = set(values)
 
             return False
         return True
 
+    def _partial_build(self, endpoint, values, method, append_unknown):
+        """Helper for :meth:`build`.  Returns subdomain and path for the
+        rule that accepts this endpoint, values and method.
+
+        :internal:
+        """
+        # in case the method is none, try with the default method first
+        if method is None:
+            rv = self._partial_build(endpoint, values, self.default_method,
+                                     append_unknown)
+            if rv is not None:
+                return rv
+
+        # default method did not match or a specific method is passed,
+        # check all and go with first result.
+        for rule in self.map._rules_by_endpoint.get(endpoint, ()):
+            if rule.suitable_for(values, method):
+                rv = rule.build(values, append_unknown)
+                if rv is not None:
+                    return rv
+
     def build(self, endpoint, values=None, method=None, force_external=False,
               append_unknown=True):
         """Building URLs works pretty much the other way round.  Instead of
                                if you want the builder to ignore those.
         """
         self.map.update()
-        method = method or self.default_method
         if values:
-            values = dict([(k, v) for k, v in values.items() if v is not None])
+            if isinstance(values, MultiDict):
+                values = dict((k, v) for k, v in values.iteritems(multi=True)
+                              if v is not None)
+            else:
+                values = dict((k, v) for k, v in values.iteritems()
+                              if v is not None)
         else:
             values = {}
 
-        for rule in self.map._rules_by_endpoint.get(endpoint, ()):
-            if rule.suitable_for(values, method):
-                rv = rule.build(values, append_unknown)
-                if rv is not None:
-                    break
-        else:
+        rv = self._partial_build(endpoint, values, method, append_unknown)
+        if rv is None:
             raise BuildError(endpoint, values, method)
         subdomain, path = rv
+
         if not force_external and subdomain == self.subdomain:
-            return str(urljoin(self.script_name, path.lstrip('/')))
+            return str(urljoin(self.script_name, './' + path.lstrip('/')))
         return str('%s://%s%s%s/%s' % (
             self.url_scheme,
             subdomain and subdomain + '.' or '',
     'float':            FloatConverter
 }
 
-from werkzeug.datastructures import ImmutableDict
+from werkzeug.datastructures import ImmutableDict, MultiDict
 Map.default_converters = ImmutableDict(DEFAULT_CONVERTERS)

werkzeug/script.py

 def make_runserver(app_factory, hostname='localhost', port=5000,
                    use_reloader=False, use_debugger=False, use_evalex=True,
                    threaded=False, processes=1, static_files=None,
-                   extra_files=None):
+                   extra_files=None, ssl_context=None):
     """Returns an action callback that spawns a new development server.
 
     .. versionadded:: 0.5
        `static_files` and `extra_files` was added.
 
+    ..versionadded:: 0.6.1
+       `ssl_context` was added.
+
     :param app_factory: a function that returns a new WSGI application.
     :param hostname: the default hostname the server should listen on.
     :param port: the default port of the server.
     :param use_evalex: the default setting for the evalex flag of the debugger.
     :param threaded: the default threading setting.
     :param processes: the default number of processes to start.
-    :param static_files: optionally a dict of static files.
-    :param extra_files: optionally a list of extra files to track for reloading.
+    :param static_files: optional dict of static files.
+    :param extra_files: optional list of extra files to track for reloading.
+    :param ssl_context: optional SSL context for running server in HTTPS mode.
     """
     def action(hostname=('h', hostname), port=('p', port),
                reloader=use_reloader, debugger=use_debugger,
         app = app_factory()
         run_simple(hostname, port, app, reloader, debugger, evalex,
                    extra_files, 1, threaded, processes,
-                   static_files=static_files)
+                   static_files=static_files, ssl_context=ssl_context)
     return action

werkzeug/security.py

+# -*- coding: utf-8 -*-
+"""
+    werkzeug.security
+    ~~~~~~~~~~~~~~~~~
+
+    Security related helpers such as secure password hashing tools.
+
+    :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import hmac
+import string
+from random import SystemRandom
+
+# because the API of hmac changed with the introduction of the
+# new hashlib module, we have to support both.  This sets up a
+# mapping to the digest factory functions and the digest modules
+# (or factory functions with changed API)
+try:
+    from hashlib import sha1, md5
+    _hash_funcs = _hash_mods = {'sha1': sha1, 'md5': md5}
+    _sha1_mod = sha1
+    _md5_mod = md5
+except ImportError:
+    import sha as _sha1_mod, md5 as _md5_mod
+    _hash_mods = {'sha1': _sha1_mod, 'md5': _md5_mod}
+    _hash_funcs = {'sha1': _sha1_mod.new, 'md5': _md5_mod.new}
+
+
+SALT_CHARS = string.letters + string.digits
+
+
+_sys_rng = SystemRandom()
+
+
+def gen_salt(length):
+    """Generate a random string of SALT_CHARS with specified ``length``."""
+    if length <= 0:
+        raise ValueError('requested salt of length <= 0')
+    return ''.join(_sys_rng.choice(SALT_CHARS) for _ in xrange(length))
+
+
+def _hash_internal(method, salt, password):
+    """Internal password hash helper.  Supports plaintext without salt,
+    unsalted and salted passwords.  In case salted passwords are used
+    hmac is used.
+    """
+    if method == 'plain':
+        return password
+    if salt:
+        if method not in _hash_mods:
+            return None
+        if isinstance(salt, unicode):
+            salt = salt.encode('utf-8')
+        h = hmac.new(salt, None, _hash_mods[method])
+    else:
+        if method not in _hash_funcs:
+            return None
+        h = _hash_funcs[method]()
+    if isinstance(password, unicode):
+        password = password.encode('utf-8')
+    h.update(password)
+    return h.hexdigest()
+
+
+def generate_password_hash(password, method='sha1', salt_length=8):
+    """Hash a password with the given method and salt with with a string of
+    the given length.  The format of the string returned includes the method
+    that was used so that :func:`check_password_hash` can check the hash.
+
+    The format for the hashed string looks like this::
+
+        method$salt$hash
+
+    This method can **not** generate unsalted passwords but it is possible
+    to set the method to plain to enforce plaintext passwords.  If a salt
+    is used, hmac is used internally to salt the password.
+
+    :param password: the password to hash
+    :param method: the hash method to use (``'md5'`` or ``'sha1'``)
+    :param salt_length: the lengt of the salt in letters
+    """
+    salt = method != 'plain' and gen_salt(salt_length) or ''
+    h = _hash_internal(method, salt, password)
+    if h is None:
+        raise TypeError('invalid method %r' % method)
+    return '%s$%s$%s' % (method, salt, h)
+
+
+def check_password_hash(pwhash, password):
+    """check a password against a given salted and hashed password value.
+    In order to support unsalted legacy passwords this method supports
+    plain text passwords, md5 and sha1 hashes (both salted and unsalted).
+
+    Returns `True` if the password matched, `False` otherwise.
+
+    :param pwhash: a hashed string like returned by
+                   :func:`generate_password_hash`
+    :param password: the plaintext password to compare against the hash
+    """
+    if pwhash.count('$') < 2:
+        return False
+    method, salt, hashval = pwhash.split('$', 2)
+    return _hash_internal(method, salt, password) == hashval
             warn(DeprecationWarning('it\'s no longer possible to pass dicts '
                                     'as `data`.  Use tuples or FileStorage '
                                     'objects instead'), stacklevel=2)
-            args = v
             value = dict(value)
             mimetype = value.pop('mimetype', None)
             if mimetype is not None:

werkzeug/wrappers.py

 
     #: if set to `False` accessing properties on the response object will
     #: not try to consume the response iterator and convert it into a list.
-    implicit_seqence_conversion = True
+    #:
+    #: .. versionadded:: 0.6.2
+    #:
+    #:    That attribute was previously called `implicit_seqence_conversion`.
+    #:    (Notice the typo).  If you did use this feature, you have to adapt
+    #:    your code to the name change.
+    implicit_sequence_conversion = True
 
     def __init__(self, response=None, status=None, headers=None,
                  mimetype=None, content_type=None, direct_passthrough=False):
         can lead to unwanted behavior if you stream big data.
 
         This behavior can be disabled by setting
-        :attr:`implicit_seqence_conversion` to `False`.
+        :attr:`implicit_sequence_conversion` to `False`.
         """
         self._ensure_sequence()
         return ''.join(self.iter_encoded())
             if mutable and not isinstance(self.response, list):
                 self.response = list(self.response)
             return
-        if not self.implicit_seqence_conversion:
+        if not self.implicit_sequence_conversion:
             raise RuntimeError('The response object required the iterable '
                                'to be a sequence, but the implicit '
                                'conversion was disabled.  Call '
 
     def make_sequence(self):
         """Converts the response iterator in a list.  By default this happens
-        automatically if required.  If `implicit_seqence_conversion` is
+        automatically if required.  If `implicit_sequence_conversion` is
         disabled, this method is not automatically called and some properties
         might raise exceptions.  This also encodes all the items.
 
         manager = ResourceManager()
         filesystem_bound = isinstance(provider, DefaultProvider)
         def loader(path):
+            if path is None:
+                return None, None
             path = posixpath.join(package_path, path)
-            if path is None or not provider.has_resource(path):
+            if not provider.has_resource(path):
                 return None, None
             basename = posixpath.basename(path)
             if filesystem_bound: