1.1 --- a/translator.py Tue Mar 21 00:12:25 2017 +0100
1.2 +++ b/translator.py Tue Mar 21 00:15:15 2017 +0100
1.3 @@ -1067,6 +1067,8 @@
1.4 have_access_context = isinstance(expr, AttrResult)
1.5 context_identity = have_access_context and expr.context()
1.6 parameters = None
1.7 + num_parameters = None
1.8 + num_defaults = None
1.9
1.10 # Obtain details of the callable and of its parameters.
1.11
1.12 @@ -1082,6 +1084,8 @@
1.13 elif objpath:
1.14 parameters = self.importer.function_parameters.get(objpath)
1.15 function_defaults = self.importer.function_defaults.get(objpath)
1.16 + num_parameters = parameters and len(parameters) or 0
1.17 + num_defaults = function_defaults and len(function_defaults) or 0
1.18
1.19 # Class invocation involves instantiators.
1.20
1.21 @@ -1129,26 +1133,43 @@
1.22 if attrname:
1.23 all_params = set()
1.24 all_defaults = set()
1.25 + min_params = set()
1.26 + max_params = set()
1.27 refs = set()
1.28
1.29 # Obtain parameters and defaults for each possible target.
1.30
1.31 for ref in self.get_attributes_for_attrname(attrname):
1.32 - refs.add(ref)
1.33 origin = ref.get_origin()
1.34 params = self.importer.function_parameters.get(origin)
1.35 - if params:
1.36 - all_params.add(tuple(params))
1.37 +
1.38 defaults = self.importer.function_defaults.get(origin)
1.39 - if defaults:
1.40 + if defaults is not None:
1.41 all_defaults.add(tuple(defaults))
1.42
1.43 + if params is not None:
1.44 + all_params.add(tuple(params))
1.45 + min_params.add(len(params) - (defaults and len(defaults) or 0))
1.46 + max_params.add(len(params))
1.47 + refs.add(ref)
1.48 + else:
1.49 + refs = set()
1.50 + break
1.51 +
1.52 # Where the parameters and defaults are always the same,
1.53 # permit populating them in advance.
1.54
1.55 - if len(all_params) == 1 and (not all_defaults or len(all_defaults) == 1):
1.56 - parameters = first(all_params)
1.57 - function_defaults = all_defaults and first(all_defaults) or []
1.58 + if refs:
1.59 + if self.uses_keyword_arguments(n):
1.60 + if len(all_params) == 1 and (not all_defaults or len(all_defaults) == 1):
1.61 + parameters = first(all_params)
1.62 + function_defaults = all_defaults and first(all_defaults) or []
1.63 + num_parameters = parameters and len(parameters) or 0
1.64 + num_defaults = function_defaults and len(function_defaults) or 0
1.65 + else:
1.66 + if len(min_params) == 1 and len(max_params) == 1:
1.67 + num_parameters = first(max_params)
1.68 + num_defaults = first(max_params) - first(min_params)
1.69
1.70 # Some information about the target may be available and be used to
1.71 # provide warnings about argument compatibility.
1.72 @@ -1159,11 +1180,11 @@
1.73 if unsuitable:
1.74 for ref in unsuitable:
1.75 _objpath = ref.get_origin()
1.76 - num_parameters = len(self.importer.function_parameters[_objpath])
1.77 print >>sys.stderr, \
1.78 "In %s, at line %d, inappropriate number of " \
1.79 "arguments given. Need %d arguments to call %s." % (
1.80 - self.get_namespace_path(), n.lineno, num_parameters,
1.81 + self.get_namespace_path(), n.lineno,
1.82 + len(self.importer.function_parameters[_objpath]),
1.83 _objpath)
1.84
1.85 # Determine any readily-accessible target identity.
1.86 @@ -1185,6 +1206,8 @@
1.87 # always being the first argument. Where it would be unused, it may be
1.88 # set to null.
1.89
1.90 + known_parameters = num_parameters is not None
1.91 +
1.92 if context_required:
1.93 if have_access_context:
1.94 args = ["__ATTRVALUE(%s)" % context_identity]
1.95 @@ -1196,7 +1219,7 @@
1.96 # Complete the array with null values, permitting tests for a complete
1.97 # set of arguments.
1.98
1.99 - args += [None] * (parameters is None and len(n.args) or parameters is not None and len(parameters) or 0)
1.100 + args += [None] * (num_parameters is None and len(n.args) or num_parameters is not None and num_parameters or 0)
1.101 kwcodes = []
1.102 kwargs = []
1.103
1.104 @@ -1257,17 +1280,29 @@
1.105 # Defaults are added to the frame where arguments are missing.
1.106
1.107 if parameters and function_defaults:
1.108 - target_structure = target_structure or "%s.value" % target_var
1.109 -
1.110 - # Visit each default and set any missing arguments.
1.111 - # Use the target structure to obtain defaults, as opposed to the
1.112 - # actual function involved.
1.113 +
1.114 + # Visit each default and set any missing arguments. Where keyword
1.115 + # arguments have been used, the defaults must be inspected and, if
1.116 + # necessary, inserted into gaps in the argument list.
1.117
1.118 for i, (argname, default) in enumerate(function_defaults):
1.119 argnum = parameters.index(argname)
1.120 if not args[argnum+1]:
1.121 args[argnum+1] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
1.122
1.123 + elif known_parameters:
1.124 +
1.125 + # No specific parameter details are provided, but no keyword
1.126 + # arguments are used. Thus, defaults can be supplied using position
1.127 + # information only.
1.128 +
1.129 + i = len(n.args)
1.130 + pos = i - (num_parameters - num_defaults)
1.131 + while i < num_parameters:
1.132 + args[i+1] = "__GETDEFAULT(%s.value, %d)" % (target_var, pos)
1.133 + i += 1
1.134 + pos += 1
1.135 +
1.136 # Test for missing arguments.
1.137
1.138 if None in args:
1.139 @@ -1329,7 +1364,7 @@
1.140
1.141 # With known parameters, the target can be tested.
1.142
1.143 - elif parameters:
1.144 + elif known_parameters:
1.145 context_arg = context_required and args[0] or "__NULL"
1.146 if self.always_callable(refs):
1.147 if context_var == target_var:
1.148 @@ -1345,7 +1380,7 @@
1.149 # the sequence. Moreover, the parameters become part of the sequence
1.150 # and thereby participate in a guaranteed evaluation order.
1.151
1.152 - if target or function or parameters:
1.153 + if target or function or known_parameters:
1.154 stages[-1] += "(%s)" % argstr
1.155 if instantiation:
1.156 return InstantiationResult(instantiation, stages)
1.157 @@ -1393,6 +1428,16 @@
1.158 parameters = self.importer.function_parameters.get(objpath)
1.159 return nargs < len(parameters)
1.160
1.161 + def uses_keyword_arguments(self, n):
1.162 +
1.163 + "Return whether invocation node 'n' uses keyword arguments."
1.164 +
1.165 + for arg in enumerate(n.args):
1.166 + if isinstance(arg, compiler.ast.Keyword):
1.167 + return True
1.168 +
1.169 + return False
1.170 +
1.171 def get_attributes_for_attrname(self, attrname):
1.172
1.173 "Return a set of all attributes exposed by 'attrname'."