1#!/usr/bin/env python
2
3r"""
4This module provides argument manipulation functions like pop_arg.
5"""
6
7import gen_print as gp
8import collections
9
10
11def pop_arg(default=None, *args, **kwargs):
12    r"""
13    Pop a named argument from the args/kwargs and return a tuple consisting of
14    the argument value, the modified args and the modified kwargs.
15
16    The name of the argument is determined automatically by this function by
17    examining the source code which calls it (see examples below).  If no
18    suitable argument can be found, the default value passed to this function
19    will be returned as the argument value.  This function is useful for
20    wrapper functions that wish to process arguments in some way before
21    calling subordinate function.
22
23    Examples:
24
25    Given this code:
26
27    def func1(*args, **kwargs):
28
29        last_name, args, kwargs = pop_arg('Doe', *args, **kwargs)
30        some_function(last_name.capitalize(), *args, **kwargs)
31
32    Consider this call to func1:
33
34    func1('Johnson', ssn='111-11-1111')
35
36    The pop_arg in func1 would return the following:
37
38        'Johnson', [], {'ssn': "111-11-1111"}
39
40    Notice that the 'args' value returned is an empty list. Since last_name
41    was assumed to be the first positional argument, it was popped from args.
42
43    Now consider this call to func1:
44
45    func1(last_name='Johnson', ssn='111-11-1111')
46
47    The pop_arg in func1 would return the same last_name value as in the
48    previous example.  The only difference being that the last_name value was
49    popped from kwargs rather than from args.
50
51    Description of argument(s):
52    default                         The value to return if the named argument
53                                    is not present in args/kwargs.
54    args                            The positional arguments passed to the
55                                    calling function.
56    kwargs                          The keyword arguments passed to the
57                                    calling function.
58    """
59
60    # Retrieve the argument name by examining the source code.
61    arg_name = gp.get_arg_name(None, arg_num=-3, stack_frame_ix=2)
62    if arg_name in kwargs:
63        arg_value = kwargs.pop(arg_name)
64    else:
65        # Convert args from a tuple to a list.
66        args = list(args)
67        if args:
68            arg_value = args.pop(0)
69        else:
70            arg_value = default
71
72    return arg_value, args, kwargs
73
74
75def source_to_object(value):
76    r"""
77    Evaluate string value as python source code and return the resulting
78    object.
79
80    If value is NOT a string or can not be interpreted as a python source
81    object definition, simply return value.
82
83    The idea is to convert python object definition source code (e.g. for
84    lists, dictionaries, tuples, etc.) into an object.
85
86    Example:
87
88    Note that this first example is a special case in that it is a short-cut
89    for specifying a collections.OrderedDict.
90
91    result = source_to_object("[('one', 1), ('two', 2), ('three', 3)]")
92
93    The result is a collections.OrderedDict object:
94
95    result:
96      [one]:                     1
97      [two]:                     2
98      [three]:                   3
99
100    This is a short-cut for the long form shown here:
101
102    result = source_to_object("collections.OrderedDict([
103        ('one', 1),
104        ('two', 2),
105        ('three', 3)])")
106
107    Also note that support for this special-case short-cut precludes the
108    possibility of interpreting such a string as a list of tuples.
109
110    Example:
111
112    In this example, the result will be a list:
113
114    result = source_to_object("[1, 2, 3]")
115
116    result:
117      result[0]:                 1
118      result[1]:                 2
119      result[2]:                 3
120
121    Example:
122
123    In this example, the value passed to this function is not a string, so it
124    is simply returned.
125
126    result = source_to_object(1)
127
128    More examples:
129    result = source_to_object("dict(one=1, two=2, three=3)")
130    result = source_to_object("{'one':1, 'two':2, 'three':3}")
131    result = source_to_object(True)
132    etc.
133
134    Description of argument(s):
135    value                           If value is a string, it will be evaluated
136                                    as a python statement.  If the statement
137                                    is valid, the resulting object will be
138                                    returned.  In all other cases, the value
139                                    will simply be returned.
140    """
141
142    if type(value) not in gp.get_string_types():
143        return value
144
145    # Strip white space prior to attempting to interpret the string as python
146    # code.
147    value = value.strip()
148
149    # Try special case of collections.OrderedDict which accepts a list of
150    # tuple pairs.
151    if value.startswith("[("):
152        try:
153            return eval("collections.OrderedDict(" + value + ")")
154        except (TypeError, NameError, ValueError):
155            pass
156
157    try:
158        return eval(value)
159    except (NameError, SyntaxError):
160        pass
161
162    return value
163
164
165def args_to_objects(args):
166    r"""
167    Run source_to_object() on each element in args and return the result.
168
169    Description of argument(s):
170    args                            A type of dictionary, list, set, tuple or
171                                    simple object whose elements are to be
172                                    converted via a call to source_to_object().
173    """
174
175    type_of_dict = gp.is_dict(args)
176    if type_of_dict:
177        if type_of_dict == gp.dict_type():
178            return {k: source_to_object(v) for (k, v) in args.items()}
179        elif type_of_dict == gp.ordered_dict_type():
180            return collections.OrderedDict((k, v) for (k, v) in args.items())
181        elif type_of_dict == gp.dot_dict_type():
182            return DotDict((k, v) for (k, v) in args.items())
183        elif type_of_dict == gp.normalized_dict_type():
184            return NormalizedDict((k, v) for (k, v) in args.items())
185    # Assume args is list, tuple or set.
186    if type(args) in (list, set):
187        return [source_to_object(arg) for arg in args]
188    elif type(args) is tuple:
189        return tuple([source_to_object(arg) for arg in args])
190
191    return source_to_object(args)
192