Source

moin-2.0 / build / lib / MoinMoin / util / paramparser.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
# Copyright: 2010 MoinMoin:ThomasWaldmann
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.

"""
    MoinMoin - parameter parsing and invoking of extension functions
"""


from MoinMoin.i18n import _, L_, N_


class BracketError(Exception):
    pass

class BracketUnexpectedCloseError(BracketError):
    def __init__(self, bracket):
        self.bracket = bracket
        BracketError.__init__(self, "Unexpected closing bracket {0}".format(bracket))

class BracketMissingCloseError(BracketError):
    def __init__(self, bracket):
        self.bracket = bracket
        BracketError.__init__(self, "Missing closing bracket {0}".format(bracket))


class ParserPrefix(object):
    """
    Trivial container-class holding a single character for
    the possible prefixes for parse_quoted_separated_ext
    and implementing rich equal comparison.
    """
    def __init__(self, prefix):
        self.prefix = prefix

    def __eq__(self, other):
        return isinstance(other, ParserPrefix) and other.prefix == self.prefix

    def __repr__(self):
        return '<ParserPrefix({0})>'.format(self.prefix.encode('utf-8'))


def parse_quoted_separated_ext(args, separator=None, name_value_separator=None,
                               brackets=None, seplimit=0, multikey=False,
                               prefixes=None, quotes='"'):
    """
    Parses the given string according to the other parameters.

    Items can be quoted with any character from the quotes parameter
    and each quote can be escaped by doubling it, the separator and
    name_value_separator can both be quoted, when name_value_separator
    is set then the name can also be quoted.

    Values that are not given are returned as None, while the
    empty string as a value can be achieved by quoting it.

    If a name or value does not start with a quote, then the quote
    looses its special meaning for that name or value, unless it
    starts with one of the given prefixes (the parameter is unicode
    containing all allowed prefixes.) The prefixes will be returned
    as ParserPrefix() instances in the first element of the tuple
    for that particular argument.

    If multiple separators follow each other, this is treated as
    having None arguments inbetween, that is also true for when
    space is used as separators (when separator is None), filter
    them out afterwards.

    The function can also do bracketing, i.e. parse expressions
    that contain things like::

        "(a (a b))" to ['(', 'a', ['(', 'a', 'b']],

    in this case, as in this example, the returned list will
    contain sub-lists and the brackets parameter must be a list
    of opening and closing brackets, e.g.::

        brackets = ['()', '<>']

    Each sub-list's first item is the opening bracket used for
    grouping.
    Nesting will be observed between the different types of
    brackets given. If bracketing doesn't match, a BracketError
    instance is raised with a 'bracket' property indicating the
    type of missing or unexpected bracket, the instance will be
    either of the class BracketMissingCloseError or of the class
    BracketUnexpectedCloseError.

    If multikey is True (along with setting name_value_separator),
    then the returned tuples for (key, value) pairs can also have
    multiple keys, e.g.::

        "a=b=c" -> ('a', 'b', 'c')

    :param args: arguments to parse
    :param separator: the argument separator, defaults to None, meaning any
        space separates arguments
    :param name_value_separator: separator for name=value, default '=',
        name=value keywords not parsed if evaluates to False
    :param brackets: a list of two-character strings giving
        opening and closing brackets
    :param seplimit: limits the number of parsed arguments
    :param multikey: multiple keys allowed for a single value
    :rtype: list
    :returns: list of unicode strings and tuples containing
        unicode strings, or lists containing the same for
        bracketing support
    """
    idx = 0
    assert name_value_separator is None or name_value_separator != separator
    assert name_value_separator is None or len(name_value_separator) == 1
    if not isinstance(args, unicode):
        raise TypeError('args must be unicode')
    max = len(args)
    result = []         # result list
    cur = [None]        # current item
    quoted = None       # we're inside quotes, indicates quote character used
    skipquote = 0       # next quote is a quoted quote
    noquote = False     # no quotes expected because word didn't start with one
    seplimit_reached = False # number of separators exhausted
    separator_count = 0 # number of separators encountered
    SPACE = [' ', '\t', ]
    nextitemsep = [separator]   # used for skipping trailing space
    SPACE = [' ', '\t', ]
    if separator is None:
        nextitemsep = SPACE[:]
        separators = SPACE
    else:
        nextitemsep = [separator]   # used for skipping trailing space
        separators = [separator]
    if name_value_separator:
        nextitemsep.append(name_value_separator)

    # bracketing support
    opening = []
    closing = []
    bracketstack = []
    matchingbracket = {}
    if brackets:
        for o, c in brackets:
            assert not o in opening
            opening.append(o)
            assert not c in closing
            closing.append(c)
            matchingbracket[o] = c

    def additem(result, cur, separator_count, nextitemsep):
        if len(cur) == 1:
            result.extend(cur)
        elif cur:
            result.append(tuple(cur))
        cur = [None]
        noquote = False
        separator_count += 1
        seplimit_reached = False
        if seplimit and separator_count >= seplimit:
            seplimit_reached = True
            nextitemsep = [n for n in nextitemsep if n in separators]

        return cur, noquote, separator_count, seplimit_reached, nextitemsep

    while idx < max:
        char = args[idx]
        next = None
        if idx + 1 < max:
            next = args[idx+1]
        if skipquote:
            skipquote -= 1
        if not separator is None and not quoted and char in SPACE:
            spaces = ''
            # accumulate all space
            while char in SPACE and idx < max - 1:
                spaces += char
                idx += 1
                char = args[idx]
            # remove space if args end with it
            if char in SPACE and idx == max - 1:
                break
            # remove space at end of argument
            if char in nextitemsep:
                continue
            idx -= 1
            if len(cur) and cur[-1]:
                cur[-1] = cur[-1] + spaces
        elif not quoted and char == name_value_separator:
            if multikey or len(cur) == 1:
                cur.append(None)
            else:
                if not multikey:
                    if cur[-1] is None:
                        cur[-1] = ''
                    cur[-1] += name_value_separator
                else:
                    cur.append(None)
            noquote = False
        elif not quoted and not seplimit_reached and char in separators:
            (cur, noquote, separator_count, seplimit_reached,
             nextitemsep) = additem(result, cur, separator_count, nextitemsep)
        elif not quoted and not noquote and char in quotes:
            if len(cur) and cur[-1] is None:
                del cur[-1]
            cur.append(u'')
            quoted = char
        elif char == quoted and not skipquote:
            if next == quoted:
                skipquote = 2 # will be decremented right away
            else:
                quoted = None
        elif not quoted and char in opening:
            while len(cur) and cur[-1] is None:
                del cur[-1]
            (cur, noquote, separator_count, seplimit_reached,
             nextitemsep) = additem(result, cur, separator_count, nextitemsep)
            bracketstack.append((matchingbracket[char], result))
            result = [char]
        elif not quoted and char in closing:
            while len(cur) and cur[-1] is None:
                del cur[-1]
            (cur, noquote, separator_count, seplimit_reached,
             nextitemsep) = additem(result, cur, separator_count, nextitemsep)
            cur = []
            if not bracketstack:
                raise BracketUnexpectedCloseError(char)
            expected, oldresult = bracketstack[-1]
            if not expected == char:
                raise BracketUnexpectedCloseError(char)
            del bracketstack[-1]
            oldresult.append(result)
            result = oldresult
        elif not quoted and prefixes and char in prefixes and cur == [None]:
            cur = [ParserPrefix(char)]
            cur.append(None)
        else:
            if len(cur):
                if cur[-1] is None:
                    cur[-1] = char
                else:
                    cur[-1] += char
            else:
                cur.append(char)
            noquote = True

        idx += 1

    if bracketstack:
        raise BracketMissingCloseError(bracketstack[-1][0])

    if quoted:
        if len(cur):
            if cur[-1] is None:
                cur[-1] = quoted
            else:
                cur[-1] = quoted + cur[-1]
        else:
            cur.append(quoted)

    additem(result, cur, separator_count, nextitemsep)

    return result


def parse_quoted_separated(args, separator=',', name_value=True, seplimit=0):
    result = []
    positional = result
    if name_value:
        name_value_separator = '='
        trailing = []
        keywords = {}
    else:
        name_value_separator = None

    l = parse_quoted_separated_ext(args, separator=separator,
                                   name_value_separator=name_value_separator,
                                   seplimit=seplimit)
    for item in l:
        if isinstance(item, tuple):
            key, value = item
            if key is None:
                key = u''
            keywords[key] = value
            positional = trailing
        else:
            positional.append(item)

    if name_value:
        return result, keywords, trailing
    return result


def get_bool(arg, name=None, default=None):
    """
    For use with values returned from parse_quoted_separated or given
    as macro parameters, return a boolean from a unicode string.
    Valid input is 'true'/'false', 'yes'/'no' and '1'/'0' or None for
    the default value.

    :param arg: The argument, may be None or a unicode string
    :param name: Name of the argument, for error messages
    :param default: default value if arg is None
    :rtype: boolean or None
    :returns: the boolean value of the string according to above rules
              (or default value)
    """
    assert default is None or isinstance(default, bool)
    if arg is None:
        return default
    elif not isinstance(arg, unicode):
        raise TypeError('Argument must be None or unicode')
    arg = arg.lower()
    if arg in [u'0', u'false', u'no']:
        return False
    elif arg in [u'1', u'true', u'yes']:
        return True
    else:
        if name:
            raise ValueError(
                _('Argument "%(name)s" must be a boolean value, not "%(value)s"', name=name, value=arg))
        else:
            raise ValueError(
                _('Argument must be a boolean value, not "%(value)s"', value=arg))


def get_int(arg, name=None, default=None):
    """
    For use with values returned from parse_quoted_separated or given
    as macro parameters, return an integer from a unicode string
    containing the decimal representation of a number.
    None is a valid input and yields the default value.

    :param arg: The argument, may be None or a unicode string
    :param name: Name of the argument, for error messages
    :param default: default value if arg is None
    :rtype: int or None
    :returns: the integer value of the string (or default value)
    """
    assert default is None or isinstance(default, (int, long))
    if arg is None:
        return default
    elif not isinstance(arg, unicode):
        raise TypeError('Argument must be None or unicode')
    try:
        return int(arg)
    except ValueError:
        if name:
            raise ValueError(
                _('Argument "%(name)s" must be an integer value, not "%(value)s"', name=name, value=arg))
        else:
            raise ValueError(
                _('Argument must be an integer value, not "%(value)s"', value=arg))


def get_float(arg, name=None, default=None):
    """
    For use with values returned from parse_quoted_separated or given
    as macro parameters, return a float from a unicode string.
    None is a valid input and yields the default value.

    :param arg: The argument, may be None or a unicode string
    :param name: Name of the argument, for error messages
    :param default: default return value if arg is None
    :rtype: float or None
    :returns: the float value of the string (or default value)
    """
    assert default is None or isinstance(default, (int, long, float))
    if arg is None:
        return default
    elif not isinstance(arg, unicode):
        raise TypeError('Argument must be None or unicode')
    try:
        return float(arg)
    except ValueError:
        if name:
            raise ValueError(
                _('Argument "%(name)s" must be a floating point value, not "%(value)s"', name=name, value=arg))
        else:
            raise ValueError(
                _('Argument must be a floating point value, not "%(value)s"', value=arg))


def get_complex(arg, name=None, default=None):
    """
    For use with values returned from parse_quoted_separated or given
    as macro parameters, return a complex from a unicode string.
    None is a valid input and yields the default value.

    :param arg: The argument, may be None or a unicode string
    :param name: Name of the argument, for error messages
    :param default: default return value if arg is None
    :rtype: complex or None
    :returns: the complex value of the string (or default value)
    """
    assert default is None or isinstance(default, (int, long, float, complex))
    if arg is None:
        return default
    elif not isinstance(arg, unicode):
        raise TypeError('Argument must be None or unicode')
    try:
        # allow writing 'i' instead of 'j'
        arg = arg.replace('i', 'j').replace('I', 'j')
        return complex(arg)
    except ValueError:
        if name:
            raise ValueError(
                _('Argument "%(name)s" must be a complex value, not "%(value)s"', name=name, value=arg))
        else:
            raise ValueError(
                _('Argument must be a complex value, not "%(value)s"', value=arg))


def get_unicode(arg, name=None, default=None):
    """
    For use with values returned from parse_quoted_separated or given
    as macro parameters, return a unicode string from a unicode string.
    None is a valid input and yields the default value.

    :param arg: The argument, may be None or a unicode string
    :param name: Name of the argument, for error messages
    :param default: default return value if arg is None;
    :rtype: unicode or None
    :returns: the unicode string (or default value)
    """
    assert default is None or isinstance(default, unicode)
    if arg is None:
        return default
    elif not isinstance(arg, unicode):
        raise TypeError('Argument must be None or unicode')

    return arg


def get_choice(arg, name=None, choices=[None], default_none=False):
    """
    For use with values returned from parse_quoted_separated or given
    as macro parameters, return a unicode string that must be in the
    choices given. None is a valid input and yields first of the valid
    choices.

    :param arg: The argument, may be None or a unicode string
    :param name: Name of the argument, for error messages
    :param choices: the possible choices
    :param default_none: If False (default), get_choice returns first available
                         choice if arg is None. If True, get_choice returns
                         None if arg is None. This is useful if some arg value
                         is required (no default choice).
    :rtype: unicode or None
    :returns: the unicode string (or default value)
    """
    assert isinstance(choices, (tuple, list))
    if arg is None:
        if default_none:
            return None
        else:
            return choices[0]
    elif not isinstance(arg, unicode):
        raise TypeError('Argument must be None or unicode')
    elif not arg in choices:
        if name:
            raise ValueError(
                _('Argument "%(name)s" must be one of "%(choices)s", not "%(value)s"',
                    name=name,
                    choices='", "'.join([repr(choice) for choice in choices]),
                    value=arg))
        else:
            raise ValueError(
                _('Argument must be one of "%(choices)s", not "%(value)s"',
                    choices='", "'.join([repr(choice) for choice in choices]),
                    value=arg))

    return arg


class IEFArgument:
    """
    Base class for new argument parsers for
    invoke_extension_function.
    """
    def __init__(self):
        pass

    def parse_argument(self, s):
        """
        Parse the argument given in s (a string) and return
        the argument for the extension function.
        """
        raise NotImplementedError

    def get_default(self):
        """
        Return the default for this argument.
        """
        raise NotImplementedError


class UnitArgument(IEFArgument):
    """
    Argument class for invoke_extension_function that forces
    having any of the specified units given for a value.

    Note that the default unit is "mm".

    Use, for example, "UnitArgument('7mm', float, ['%', 'mm'])".

    If the defaultunit parameter is given, any argument that
    can be converted into the given argtype is assumed to have
    the default unit. NOTE: This doesn't work with a choice
    (tuple or list) argtype.
    """
    def __init__(self, default, argtype, units=['mm'], defaultunit=None):
        """
        Initialise a UnitArgument giving the default,
        argument type and the permitted units.
        """
        IEFArgument.__init__(self)
        self._units = list(units)
        self._units.sort(lambda x, y: len(y) - len(x))
        self._type = argtype
        self._defaultunit = defaultunit
        assert defaultunit is None or defaultunit in units
        if default is not None:
            self._default = self.parse_argument(default)
        else:
            self._default = None

    def parse_argument(self, s):
        for unit in self._units:
            if s.endswith(unit):
                ret = (self._type(s[:len(s) - len(unit)]), unit)
                return ret
        if self._defaultunit is not None:
            try:
                return (self._type(s), self._defaultunit)
            except ValueError:
                pass
        units = ', '.join(self._units)
        ## XXX: how can we translate this?
        raise ValueError("Invalid unit in value {0} (allowed units: {1})".format(s, units))

    def get_default(self):
        return self._default


class required_arg:
    """
    Wrap a type in this class and give it as default argument
    for a function passed to invoke_extension_function() in
    order to get generic checking that the argument is given.
    """
    def __init__(self, argtype):
        """
        Initialise a required_arg
        :param argtype: the type the argument should have
        """
        if not (argtype in (bool, int, long, float, complex, unicode) or
                isinstance(argtype, (IEFArgument, tuple, list))):
            raise TypeError("argtype must be a valid type")
        self.argtype = argtype


def invoke_extension_function(function, args, fixed_args=[]):
    """
    Parses arguments for an extension call and calls the extension
    function with the arguments.

    If the macro function has a default value that is a bool,
    int, long, float or unicode object, then the given value
    is converted to the type of that default value before passing
    it to the macro function. That way, macros need not call the
    get_* functions for any arguments that have a default.

    :param function: the function to invoke
    :param args: unicode string with arguments (or evaluating to False)
    :param fixed_args: fixed arguments to pass as the first arguments
    :returns: the return value from the function called
    """
    from inspect import getargspec, isfunction, isclass, ismethod

    def _convert_arg(value, default, name=None):
        """
        Using the get_* functions, convert argument to the type of the default
        if that is any of bool, int, long, float or unicode; if the default
        is the type itself then convert to that type (keeps None) or if the
        default is a list require one of the list items.

        In other cases return the value itself.
        """
        # if extending this, extend required_arg as well!
        if isinstance(default, bool):
            return get_bool(value, name, default)
        elif isinstance(default, (int, long)):
            return get_int(value, name, default)
        elif isinstance(default, float):
            return get_float(value, name, default)
        elif isinstance(default, complex):
            return get_complex(value, name, default)
        elif isinstance(default, unicode):
            return get_unicode(value, name, default)
        elif isinstance(default, (tuple, list)):
            return get_choice(value, name, default)
        elif default is bool:
            return get_bool(value, name)
        elif default is int or default is long:
            return get_int(value, name)
        elif default is float:
            return get_float(value, name)
        elif default is complex:
            return get_complex(value, name)
        elif isinstance(default, IEFArgument):
            # defaults handled later
            if value is None:
                return None
            return default.parse_argument(value)
        elif isinstance(default, required_arg):
            if isinstance(default.argtype, (tuple, list)):
                # treat choice specially and return None if no choice
                # is given in the value
                return get_choice(value, name, list(default.argtype),
                       default_none=True)
            else:
                return _convert_arg(value, default.argtype, name)
        return value

    assert isinstance(fixed_args, (list, tuple))

    kwargs = {}
    kwargs_to_pass = {}
    trailing_args = []

    if args:
        assert isinstance(args, unicode)

        positional, keyword, trailing = parse_quoted_separated(args)

        for kw in keyword:
            try:
                kwargs[str(kw)] = keyword[kw]
            except UnicodeEncodeError:
                kwargs_to_pass[kw] = keyword[kw]

        trailing_args.extend(trailing)

    else:
        positional = []

    if isfunction(function) or ismethod(function):
        argnames, varargs, varkw, defaultlist = getargspec(function)
    elif isclass(function):
        (argnames, varargs,
         varkw, defaultlist) = getargspec(function.__init__.im_func)
    else:
        raise TypeError('function must be a function, method or class')

    # self is implicit!
    if ismethod(function) or isclass(function):
        argnames = argnames[1:]

    fixed_argc = len(fixed_args)
    argnames = argnames[fixed_argc:]
    argc = len(argnames)
    if not defaultlist:
        defaultlist = []

    # if the fixed parameters have defaults too...
    if argc < len(defaultlist):
        defaultlist = defaultlist[fixed_argc:]
    defstart = argc - len(defaultlist)

    defaults = {}
    # reverse to be able to pop() things off
    positional.reverse()
    allow_kwargs = False
    allow_trailing = False
    # convert all arguments to keyword arguments,
    # fill all arguments that weren't given with None
    for idx in range(argc):
        argname = argnames[idx]
        if argname == '_kwargs':
            allow_kwargs = True
            continue
        if argname == '_trailing_args':
            allow_trailing = True
            continue
        if positional:
            kwargs[argname] = positional.pop()
        if not argname in kwargs:
            kwargs[argname] = None
        if idx >= defstart:
            defaults[argname] = defaultlist[idx - defstart]

    if positional:
        if not allow_trailing:
            raise ValueError(_('Too many arguments'))
        trailing_args.extend(positional)

    if trailing_args:
        if not allow_trailing:
            raise ValueError(_('Cannot have arguments without name following'
                               ' named arguments'))
        kwargs['_trailing_args'] = trailing_args

    # type-convert all keyword arguments to the type
    # that the default value indicates
    for argname in kwargs.keys()[:]:
        if argname in defaults:
            # the value of 'argname' from kwargs will be put into the
            # macro's 'argname' argument, so convert that giving the
            # name to the converter so the user is told which argument
            # went wrong (if it does)
            kwargs[argname] = _convert_arg(kwargs[argname],
                                           defaults[argname], argname)
            if kwargs[argname] is None:
                if isinstance(defaults[argname], required_arg):
                    raise ValueError(_('Argument "%(name)s" is required', name=argname))
                if isinstance(defaults[argname], IEFArgument):
                    kwargs[argname] = defaults[argname].get_default()

        if not argname in argnames:
            # move argname into _kwargs parameter
            kwargs_to_pass[argname] = kwargs[argname]
            del kwargs[argname]

    if kwargs_to_pass:
        kwargs['_kwargs'] = kwargs_to_pass
        if not allow_kwargs:
            raise ValueError(_(u'No argument named "%(name)s"', name=kwargs_to_pass.keys()[0]))

    return function(*fixed_args, **kwargs)