Commits

Anonymous committed b5d2fab

``get_many()`` helper.

Comments (0)

Files changed (3)

 tip (development version)
 -------------------------
 
+* webhelpers.containers:
+
+ - ``get_many()``: Extract several values from a dict to unpack into simple
+   variables, allowing optional keys while still returning values in a 
+   predictable order. Contributed by Shazow.
+
 * webhelpers.feedgenerator:
 
  - Allow ``RssFeed`` base class to be instantiated. However, it sets the RSS

tests/test_containers.py

 import copy
 import tempfile
 
-from nose.tools import eq_, raises
+from nose.tools import eq_
+
+from util import raises
 
 from webhelpers.containers import DumbObject
 from webhelpers.containers import defaultdict as webhelpers_containers_defaultdict
-from webhelpers.containers import distribute
+from webhelpers.containers import distribute, get_many
 
 # Tests from Python 2.5 test_defaultdict_defaultdict.py, as this is just a 2.4 backport
 # anyway
         eq_(distribute(food, 3, "V", ""), [['apple', 'daikon', 'gelato'], ['banana', 'egg', 'honey'], ['carrot', 'fish', 'EXTRA']])
         eq_(distribute(food, 2, "H", ""), [['apple', 'banana'], ['carrot', 'daikon'], ['egg', 'fish'], ['gelato', 'honey'], ['EXTRA', '']])
         eq_(distribute(food, 2, "V", ""), [['apple', 'fish'], ['banana', 'gelato'], ['carrot', 'honey'], ['daikon', 'EXTRA'], ['egg', '']])
+
+def test_get_many():
+    # Dict with keys and values of 0, 1, 2, 3, 4
+    params = dict((n,n) for n in range(5))
+
+    eq_(get_many(params), [])
+    eq_(get_many(params, required=[1, 2]), [1, 2])
+    eq_(get_many(params, optional=[1, 2]), [1, 2])
+    eq_(get_many(params, optional=[6, 7, 8]), [None, None, None])
+    eq_(get_many(params, one_of=[1, 2]), [1])
+    eq_(get_many(params, one_of=[6, 1]), [1])
+
+    eq_(get_many(params, optional=[6, 1]), [None, 1])
+    eq_(get_many(params, required=[1, 2], optional=[6, 1]), [1, 2, None, 1])
+    eq_(get_many(params, required=[1, 2], optional=[6, 1], one_of=[7, 8, 3]), [1, 2, None, 1, 3])
+
+    raises(KeyError, get_many, params, required=[1, 6])
+    raises(KeyError, get_many, params, one_of=[7, 6])

webhelpers/containers.py

         for key, value in d.iteritems():
             yield key, value
 
+def get_many(d, required=None, optional=None, one_of=None):
+    """Return a predictable number of dict values to handle optional keys.
+
+    ``d`` is a dict.
+
+    ``required`` is a list of keys that must be in the dict. The corresponding
+    values will be the first elements in the return list. Raise KeyError if any
+    of the keys are missing.
+
+    ``optional`` is a list of optional keys. The corresponding values will be
+    appended to the return list, substititing None for missing keys.
+
+    ``one_of`` is a list of alternative keys. Take the first key that exists 
+    and append its value to the list. Raise KeyError if none of the keys exist.
+    This argument will append exactly one value if specified, or will do
+    nothing if not specified.
+
+    Example::
+
+        uid, action, limit, offset = get_many(request.params, 
+            required=['uid', 'action'], optional=['limit', 'offset'])
+
+    Contributed by Shazow.
+
+    """
+    r = []
+    if required:
+        for k in required:
+            r.append(d[k])
+    if optional:
+        for k in optional:
+            r.append(d.get(k))
+    if one_of:
+        for k in one_of:
+            if k in d:
+                r.append(d[k])
+                break
+        else:
+            raise KeyError("none of these keys found: %s" % one_of)
+    return r
+
 def del_quiet(dic, keys):
     """Delete several keys from a dict, ignoring those that don't exist.
     
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.