Deterministically generates names for unnamed variables. The names are based on the body of the function. Args: signature: The signature which may contain null argument names. variables: A list of variables, some of which may be nameless. These will be updated to incl
(signature: Any, variables: Any, body: Any)
| 128 | |
| 129 | @staticmethod |
| 130 | def _resolveNamelessArgs(signature: Any, variables: Any, body: Any) -> Any: |
| 131 | """Deterministically generates names for unnamed variables. |
| 132 | |
| 133 | The names are based on the body of the function. |
| 134 | |
| 135 | Args: |
| 136 | signature: The signature which may contain null argument names. |
| 137 | variables: A list of variables, some of which may be nameless. |
| 138 | These will be updated to include names when this method returns. |
| 139 | body: The Python function to evaluate. |
| 140 | |
| 141 | Returns: |
| 142 | The signature with null arg names resolved. |
| 143 | """ |
| 144 | nameless_arg_indices = [] |
| 145 | for i, variable in enumerate(variables): |
| 146 | if variable.varName is None: |
| 147 | nameless_arg_indices.append(i) |
| 148 | |
| 149 | # Do we have any nameless arguments at all? |
| 150 | if not nameless_arg_indices: |
| 151 | return signature |
| 152 | |
| 153 | # Generate the name base by counting the number of custom functions |
| 154 | # within the body. |
| 155 | def CountFunctions(expression: Any) -> int: |
| 156 | """Counts the number of custom functions in a serialized expression.""" |
| 157 | def CountNodes(nodes: Any) -> int: |
| 158 | return sum([CountNode(node) for node in nodes]) |
| 159 | |
| 160 | def CountNode(node: Any) -> int: |
| 161 | if 'functionDefinitionValue' in node: |
| 162 | return 1 |
| 163 | elif 'arrayValue' in node: |
| 164 | return CountNodes(node['arrayValue']['values']) |
| 165 | elif 'dictionaryValue' in node: |
| 166 | return CountNodes(node['dictionaryValue']['values'].values()) |
| 167 | elif 'functionInvocationValue' in node: |
| 168 | fn = node['functionInvocationValue'] |
| 169 | return CountNodes(fn['arguments'].values()) |
| 170 | return 0 |
| 171 | |
| 172 | return CountNodes(expression['values'].values()) |
| 173 | |
| 174 | # There are three function building phases, which each call body(): |
| 175 | # 1 - Check Return. The constructor verifies that body() returns a result, |
| 176 | # but does not try to serialize the result. If the function tries to use |
| 177 | # unbound variables (e.g., using .getInfo() or print()), ComputedObject will |
| 178 | # throw an exception when these calls try to serialize themselves, so that |
| 179 | # unbound variables are not passed in server calls. |
| 180 | # 2 - Count Functions. We serialize the result here. At this point all |
| 181 | # variables must have names for serialization to succeed, but we don't yet |
| 182 | # know the correct function depth. So we serialize with unbound_name set to |
| 183 | # '<unbound>', which should silently succeed. If this does end up in server |
| 184 | # calls, the function is very unusual: the first call doesn't use unbound |
| 185 | # variables but the second call does. In this rare case we will return |
| 186 | # server errors complaining about <unbound>. |
| 187 | # 3 - Final Serialize. Finally, the constructor calls body() with the |