blob: 8f3f539f933bc7c79125f6d3860135d27d0fa299 [file] [log] [blame]
Michael Walsh0ff2eed2019-03-12 16:21:47 -05001#!/usr/bin/env python
2
3r"""
4This module provides argument manipulation functions like pop_arg.
5"""
6
7import gen_print as gp
Michael Walshc28deec2019-05-17 15:35:51 -05008import collections
Michael Walsh0ff2eed2019-03-12 16:21:47 -05009
10
Michael Walsh3af60872019-08-01 11:13:18 -050011def pop_arg(default=None, *args, **kwargs):
Michael Walsh0ff2eed2019-03-12 16:21:47 -050012 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
Michael Walshc28deec2019-05-17 15:35:51 -050073
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
Michael Walsh3af60872019-08-01 11:13:18 -0500149 # Try special case of collections.OrderedDict which accepts a list of
150 # tuple pairs.
151 if value.startswith("[("):
Michael Walshc28deec2019-05-17 15:35:51 -0500152 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)