From dabf74b9aff6c2be1663e971b4222213e5385292 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Jun 2009 15:05:04 -0400 Subject: [PATCH] Bumped to version 0.3, adding translated functionality. Now you can rename kwargs in parent functions. This is useful if some kwarg name is perfectly clear for the child function, but needs a bit more context in the parent. For example if vibrate(time) is a child of calibrate(vibration_time) you'd want to translate "time"->"vibration_time" somewehere in between. See the doc strings for explicit examples. --- .../values | 2 +- splittable_kwargs.py | 90 +++++++++++-------- 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/.be/bugs/05b1f110-85b5-4373-9378-07f54c7d316a/values b/.be/bugs/05b1f110-85b5-4373-9378-07f54c7d316a/values index 1ce2b49..5eff05c 100644 --- a/.be/bugs/05b1f110-85b5-4373-9378-07f54c7d316a/values +++ b/.be/bugs/05b1f110-85b5-4373-9378-07f54c7d316a/values @@ -7,7 +7,7 @@ reporter: W. Trevor King severity: minor -status: open +status: closed summary: Should have a map to translate kwarg names diff --git a/splittable_kwargs.py b/splittable_kwargs.py index 0d87095..04408a2 100755 --- a/splittable_kwargs.py +++ b/splittable_kwargs.py @@ -9,7 +9,6 @@ This code is released to the public domain. Example usage (adapted from the unittests) >>> from splittable_kwargs import * - >>> >>> @splittableKwargsFunction() ... def foo(x, y, z=2): ... return "foo: x "+str(x)+", y "+str(y)+", z "+str(z)+"\\n" @@ -17,20 +16,18 @@ Example usage (adapted from the unittests) >>> @splittableKwargsFunction() ... def bar(a, b, c=2): ... return "bar: a "+str(a)+", b "+str(b)+", c "+str(c)+"\\n" - >>> - >>> @splittableKwargsFunction((bar, 'a')) + >>> @splittableKwargsFunction({'fn':bar, 'mask':'a', 'translate':{'b':'b_bar'}}) ... def baz(d, **kwargs): - ... string = bar(a=5, c=4, **kwargs) + ... barkw, = baz._splitargs(baz, kwargs) # for translation/masking + ... string = bar(a=5, c=4, **barkw) ... return string + "baz: d "+str(d)+"\\n" - >>> >>> @splittableKwargsFunction(foo, baz) ... def simple_joint(**kwargs): ... fookw,bazkw = simple_joint._splitargs(simple_joint, kwargs) ... string = foo(**fookw) ... string += baz(**bazkw) ... return string.strip() - >>> - >>> print simple_joint(x=1,y=3,b=6,d=7) + >>> print simple_joint(x=1,y=3,b_bar=6,d=7) foo: x 1, y 3, z 2 bar: a 5, b 6, c 4 baz: d 7 @@ -44,8 +41,10 @@ define baz) with You can also get a list of the available named arguments with + >>> print baz._kwargs(baz) + ['d', 'b_bar', 'c'] >>> simple_joint._kwargs(simple_joint) - ['x', 'y', 'z', 'd', 'b', 'c'] + ['x', 'y', 'z', 'd', 'b_bar', 'c'] It may seem redundant to need to pass the function (here simple_joint) to a method of simple_joint, but remember that simple_joint is a @@ -62,19 +61,19 @@ something like ... def _kwargs(self): ... return self.__call__._kwargs(self.__call__) >>> cj = ClassJoint() - >>> print cj(x=1,y=3,b=6,d=7) + >>> cj._kwargs() + ['self', 'x', 'y', 'z', 'd', 'b_bar', 'c'] + >>> print cj(x=1,y=3,b_bar=6,d=7) foo: x 1, y 3, z 2 bar: a 5, b 6, c 4 baz: d 7 - >>> cj._kwargs() - ['self', 'x', 'y', 'z', 'd', 'b', 'c'] """ import inspect import doctest import unittest -VERSION = "0.2" +VERSION = "0.3" class UnknownKwarg (KeyError): def __init__(self, fn, kwarg, value): @@ -90,49 +89,54 @@ class UnknownKwarg (KeyError): def _parse_splittable(splittable): """ - splittable -> (splittable_fn, masked_args) + splittable -> (splittable_fn, masked_args, translated_args) """ if hasattr(splittable, "_kwargs"): # bare splittableKwargsFunction - return (splittable, []) - else: # function followed by masked args - return (splittable[0], splittable[1:]) + return (splittable, [], {}) + else: # masked/translated dict + return (splittable['fn'], + splittable.get('mask', []), + splittable.get('translate', {})) def splitargs(kwargs, *internal_splittables): """ where - *internal_splittables : a list of splittableKwargsFunctions items - that this function uses internally. - the items can be either - a bare splittableKwargsFunction - or a tuple where the additional elements are arguments to mask - a (bare splittableKwargsFunction, masked argument, ...) + *internal_splittables : a list of splittableKwargsFunctions + items that this function uses internally. The items must be + parsable by _parse_splittable(). """ # sort the kwargs according to the appropriate function fn_kwargs = [{} for splittable in internal_splittables] for key,value in kwargs.items(): sorted = False for i,splittable in enumerate(internal_splittables): - fn,masked = _parse_splittable(splittable) - if key in fn._kwargs(fn) and key not in masked: - fn_kwargs[i][key] = value + fn,masked,translated = _parse_splittable(splittable) + inv_translation = dict([(v,k) for k,v in translated.items()]) + if key in inv_translation: + child_key = inv_translation[key] sorted = True + elif key in fn._kwargs(fn) and key not in masked: + child_key = key + sorted = True + if sorted == True: + fn_kwargs[i][child_key] = value break - if sorted != True: + if sorted != True: # couldn't find anywhere for that key. fns = [] for splittable in internal_splittables: - fn,masked = _parse_splittable(splittable) + fn,masked,translated = _parse_splittable(splittable) fns.append(fn) raise UnknownKwarg(fns, key, value) return fn_kwargs def _kwargs(self): - "return a list of acceptable kwargs" + "Return a list of acceptable kwargs" args,varargs,varkw,defaults = inspect.getargspec(self) if varargs != None: raise NotImplementedError, "\n %s" % varargs if varkw != None: for child in self._childSplittables: - child,masked = _parse_splittable(child) + child,masked,translated = _parse_splittable(child) child_args = child._kwargs(child) for arg in masked: try: @@ -140,18 +144,27 @@ def _kwargs(self): except ValueError, e: msg = "%s not in %s" % (arg, child_args) raise ValueError(msg) + for original,new in translated.items(): + try: + child_args[child_args.index(original)] = new + except ValueError, e: + msg = "%s not in %s" % (arg, child_args) + raise ValueError(msg) args.extend(child_args) return args def _declareInternalSplittableKwargsFunction(self, function): """ - FUNCTION can be either a bare splittableKwargsFuntion or one - such function followed by a sequence of masked arguments. + FUNCTION can be either a bare splittableKwargsFuntion or a dict of + the form: + {'fn': splittableKwargsFuntion, + 'mask':["argumentA",...], + 'translate':{"argumentB":"argB_new_name", ...}} Example values for FUNCTION: - bar - (bar, "a") - (bar, "a", "b") + bar + {'fn':bar, mask:["a"]} + {'fn':bar, mask:["a", "b"], translate:{"c":"bar_c"}}} """ self._childSplittables.append(function) @@ -184,10 +197,11 @@ def _bar(a, b, c=2): "bar function" return "bar: a "+str(a)+", b "+str(b)+", c "+str(c)+"\n" -@splittableKwargsFunction((_bar, 'a')) +@splittableKwargsFunction({'fn':_bar, 'mask':'a', 'translate':{'b':'b_bar'}}) def _baz(d, **kwargs): "baz function" - string = _bar(a=6, c=4, **kwargs) + barkw, = _baz._splitargs(_baz, kwargs) # for translation/masking + string = _bar(a=6, c=4, **barkw) return string + "baz: d "+str(d)+"\n" @splittableKwargsFunction(_foo, _bar) @@ -258,7 +272,7 @@ bar: a 4, b 5, c 2 def testDoubleLevel(self): deeper_joint = SplittableKwargsTestCase.deeper_joint expected = """joining - b : 5 + b_bar : 5 d : 6 x : 1 y : 2 @@ -267,7 +281,7 @@ foo: x 1, y 2, z 3 bar: a 6, b 5, c 4 baz: d 6 """ - output = deeper_joint(x=1, y=2, z=3, b=5, d=6) + output = deeper_joint(x=1, y=2, z=3, b_bar=5, d=6) self.failUnless(output == expected, "GOT\n%s\nEXPECTED\n%s" % (output, expected)) def testDoubleLevelOverrideRequired(self): -- 2.26.2