Marius Gedminas  committed 73009e2 Merge

Merge from upstream

  • Participants
  • Parent commits a757ec8, 75b1557
  • Branches trunk

Comments (0)

Files changed (14)

 a189d0d06b9378ee9fb5b2a00d252428df31db65 1.0
 6c4638153cbc54da9c10ada1c1eb7bbe74a28ffc 1.0
 beb7fc70212f307928193b02edc73e8c51c09dbd 1.1
+f5867a319abfe77bcb712f799736ca74821285a1 1.2
 WebHelpers ChangeLog
-1.2 (unreleased)
+1.3 (unreleased)
+* webhelpers.paginate:
+  - Support page parameter in the route path (e.g., "/help/page5") when using
+    Routes explicit mode without a URL generator.
+1.2 (2010-08-18)
 * WebHelpers now depends on MarkupSafe. ``literal`` and ``escape`` now use it.
     ``literal``. Compatibility should not be a problem; but see the docs if
     you encounter any edge cases.
+* webhelpers.html.tags:
+  - For new HTML 5 input fields, the ``text`` helper has a "type" argument.
+  - Don't put an "id" attribute on a hidden fields generated by the ``form()``
+    helper, including the magic ``_method`` field. The IDs will clash if there
+    are multiple forms on the page.
+  - Preserve case of "method" arg in ``button_to()`` for XHTML compatibility. 
+    Patch by transducer.
+* webhelpers.text:
+  - Urlencode ``urlify`` return value in case it contains special
+    characters like "?". Reported by
+* webhelpers.util:
+  - Fix bug in ``update_params`` in handling existing query strings. Support
+    multiple values per parameter.
 1.1 (2010-08-09)

File docs/_templates/index.html

 <p>WebHelpers is a wide variety of utility functions for web applications and
 other applications. It can be used with any web framework.  <strong>Version
-1.2</strong> was released 2010-08-XX.  See <a href="{{ pathto('whats_new') }}">What's
+1.2</strong> was released 2010-08-18.  See <a href="{{ pathto('whats_new') }}">What's
 New</a> for a list of changes and upgrading hints.  (The helpers
 deprecated in 0.6 are removed in 1.0, including the entire rails suite.)</p>
 <p><strong>Security update in version 1.2:</strong> addresses a potential XSS
-attack; all users are recommended to upgrade. More in
+attack. Users who use ' around HTML attributes (rather than ") are recommended
+to upgrade.  More in
 <a href="{{ pathto('whats_new') }}">What's New</a>.</p>
 <p>WebHelpers includes the widely-used HTML tag builder with smart escaping and

File docs/

 # other places throughout the built documents.
 # The short X.Y version.
-version = '1.2'
+version = '1.3'
 # The full version, including alpha/beta/rc tags.
-release = '1.2'
+release = '1.3'
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:

File docs/whats_new.rst

 Version 1.2
-*webhelpers.html*: The HTML builder now uses Armin Ronacher's
+*webhelpers.html:* The HTML builder now uses Armin Ronacher's
 "MarkupSafe" package, which Mako and Pylons have also switched to.  MarkupSafe
 has a C speedup for escaping, escapes single-quotes for greater security (to
 close a potential XSS attack route), and adds new methods to ``literal``.
+**literal** is now a subclass of ``markupsafe.Markup``;
+**escape** is a wrapper for ``markupsafe.escape_silent``.
-* **literal** is now a subclass of ``markupsafe.Markup``
+*webhelpers.html.tags:* The ``text()`` helper has a "type" argument for new
+HTML 5 input types.
-* **escape** is ``markupsafe.escape_silent``
+*webhelpers.html.tags:* **No longer adds an "id" attribute to hidden fields
+generated by the ``form()`` helper**, to prevent IDs from clashing if the page
+contains multiple forms. To create a hidden field with an ID, call ``hidden()``
-*Note*: ``escape_silent`` does not exist yet in MarkupSafe 0.9.3, but
-WebHelpers has a fallback. 
+*webhelpers.util:* ``update_params`` now supports query parameters with
+multiple values.
 Version 1.1
     from setuptools import setup, find_packages
-version = '1.2'
+version = '1.3'
 contains a large number of functions not specific to the web, including text
 processing, number formatting, date calculations, container objects, etc.
-WebHelpers does not have any dependencies; however, certain helpers depend on
+WebHelpers itself depends only on MarkupSafe, but certain helpers depend on
 third-party packages as described in the docs.
 The development version of WebHelpers is at

File tests/

         eq_(control, strip_leading_whitespace(s))
     # @@MO wrap_paragraphs untested.
+    def test_urlify(self):
+        s = "What is this? It is a car."
+        control = "What%20is%20this%3f%20It%20is%20a%20car."

File tests/

     def test_button_to_with_method_delete(self):
         self.assertEqual(u"<form action=\"\" class=\"button-to\" method=\"POST\"><div><input id=\"_method\" name=\"_method\" type=\"hidden\" value=\"DELETE\" /><input type=\"submit\" value=\"Hello\" /></div></form>", 
             button_to("Hello", "", method='DELETE'))
-        self.assertEqual(u"<form action=\"\" class=\"button-to\" method=\"POST\"><div><input id=\"_method\" name=\"_method\" type=\"hidden\" value=\"delete\" /><input type=\"submit\" value=\"Hello\" /></div></form>", 
+        self.assertEqual(u"<form action=\"\" class=\"button-to\" method=\"post\"><div><input id=\"_method\" name=\"_method\" type=\"hidden\" value=\"delete\" /><input type=\"submit\" value=\"Hello\" /></div></form>", 
             button_to("Hello", "", method='delete'))
     def test_button_to_with_method_get(self):

File webhelpers/

-__version__ = "1.2"
+__version__ = "1.3"

File webhelpers/html/

     >>> form("/submit", method="get")
     literal(u'<form action="/submit" method="get">')
     >>> form("/submit", method="put")
-    literal(u'<form action="/submit" method="post"><div style="display:none">\\n<input id="_method" name="_method" type="hidden" value="put" />\\n</div>\\n')
+    literal(u'<form action="/submit" method="post"><div style="display:none">\\n<input name="_method" type="hidden" value="put" />\\n</div>\\n')
     >>> form("/submit", "post", multipart=True) 
     literal(u'<form action="/submit" enctype="multipart/form-data" method="post">')
-    Changed in WebHelpers 2.0b2: add <div> and ``hidden_fields`` arg.
+    Changed in WebHelpers 1.0b2: add <div> and ``hidden_fields`` arg.
+    Changed in WebHelpers 1.2: don't add an "id" attribute to hidden tags
+    generated by this helper; they clash if there are multiple forms on the
+    page.
     fields = []
     attrs["action"] = url
         attrs['method'] = method
         attrs['method'] = "post"
-        field = hidden("_method", method)
+        field = hidden("_method", method, id=None)
     if hidden_fields is not None:
         except AttributeError:
             it = hidden_fields
         for name, value in it:
-            field = hidden(name, value)
+            field = hidden(name, value, id=None)
     if fields:
         div = HTML.div(style="display:none", _nl=True, *fields)
     return literal("</form>")
-def text(name, value=None, id=NotGiven, **attrs):
+def text(name, value=None, id=NotGiven, type="text", **attrs):
     """Create a standard text field.
     ``value`` is a string, the content of the text field.
     ``_make_safe_id_component()``.  Pass None to suppress the
     ID attribute entirely.
+    ``type`` is the input field type, normally "text". You can override it
+    for HTML 5 input fields that don't have their own helper; e.g.,
+    "search", "email", "date".
         will allow the user to enter.
     The remaining keyword args will be standard HTML attributes for the tag.
+    Example, a text input field::
+        >>> text("address")
+        literal(u'<input id="address" name="address" type="text" />')
+    HTML 5 example, a color picker:
+        >>> text("color", type="color")
+        literal(u'<input id="color" name="color" type="color" />')
-    _set_input_attrs(attrs, "text", name, value)
+    _set_input_attrs(attrs, type, name, value)
     _set_id_attr(attrs, id, name)
     convert_boolean_attrs(attrs, ["disabled"])
     return HTML.input(**attrs)

File webhelpers/html/

         # inside of controller for "feeds"
         >> button_to("Edit", url(action='edit', id=3))
-        <form method="POST" action="/feeds/edit/3" class="button-to">
+        <form method="post" action="/feeds/edit/3" class="button-to">
         <div><input value="Edit" type="submit" /></div>
         not in the middle of a run of text, nor can you place a form
         within another form.
         (Bottom line: Always validate your HTML before going public.)
+    Changed in WebHelpers 1.2: Preserve case of "method" arg for XHTML
+    compatibility. E.g., "POST" or "PUT" causes *method="POST"*; "post" or
+    "put" causes *method="post"*.
     if html_attrs:
     if method.upper() in ['PUT', 'DELETE']:
         method_tag = HTML.input(
             type='hidden', id='_method', name_='_method', value=method)
-    form_method = (method.upper() == 'GET' and method) or 'POST'
+    if method.upper() in ('GET', 'POST'):
+        form_method = method
+    elif method in ('put', 'delete'):
+        # preserve lowercasing of verb
+        form_method = 'post'
+    else:
+        form_method = 'POST'
     url, name = url, name or url

File webhelpers/

     # Example 4: a smarter callback that uses ``update_params``, which converts
     # keyword args to query parameters.
-    from import update_params
+    from webhelpers.util import update_params
     def get_page_url(**kw):
         return update_params("/content", **kw)
     page = Page(MY_COLLECTION, url=get_page_url)
                     if config.mapper.explicit:
                         if hasattr(config, 'mapper_dict'):
                             for k, v in config.mapper_dict.items():
-                                link_params[k] = v
+                                if k != self.page_param:
+                                    link_params[k] = v
         # Create the URL to load a certain page
         link_url = url_generator(**link_params)

File webhelpers/

 import re
 import textwrap
+import urllib
 from import strip_tags
     Based on Ruby's stringex package
+    Changed in WebHelpers 1.2: urlecode the result in case it contains special
+    characters like "?". 
     s = remove_formatting(string).lower()
     s = replace_whitespace(s, '-')
-    return collapse(s, '-')
+    s = collapse(s, '-')
+    return urllib.quote(s)
 def remove_formatting(string):

File webhelpers/

     from cgi import parse_qs
-def update_params(url, **params):
+def update_params(_url, _debug=False, **params):
     """Update query parameters in a URL.
-    ``url`` is any URL.
-    ``params`` are query parameters to add or replace. If any value is None,
-    the corresponding parameter is deleted from the URL if present.
+    ``_url`` is any URL, with or without a query string.
+    ``\*\*params`` are query parameters to add or replace. Each value may be a
+    string, a list of strings, or None. Passing a list generates multiple
+    values for the same parameter. Passing None deletes the corresponding
+    parameter if present.
     Return the new URL.
-    This function does not handle multiple parameters with the same name.
-    It will arbitrarily choose one value and discard the others.
     *Debug mode:* if a pseudo-parameter ``_debug=True`` is passed,
     return a tuple: ``[0]`` is the URL without query string or fragment,
     ``[1]`` is the final query parameters as a dict, and ``[2]`` is the
     >>> update_params("", new1="NEW1", _debug=True)
     ('', {'new1': 'NEW1'}, 'myfrag')
+    >>> update_params("", brrr=3)
+    ''
+    >>> update_params("", foo=["C", "D"])
+    ''
-    debug = params.pop("_debug", False)
-    orig_url = url
-    url, fragment = urlparse.urldefrag(url)
+    url, fragment = urlparse.urldefrag(_url)
     if "?" in url:
         url, qs = url.split("?", 1)
         query = parse_qs(qs)
             query[key] = value
         elif key in query:
             del query[key]
-    if debug:
+    if _debug:
         return url, query, fragment
-    qs = urllib.urlencode(query)
+    qs = urllib.urlencode(query, True)
     if qs:
         qs = "?" + qs
     if fragment:
     Changed in WebHelpers 1.2: escape single-quote as well as double-quote.
-    # Called by webhelpers.html.builder
     if '&' in s:
         s = s.replace("&", "&amp;") # Must be done first!
     if '<' in s: