@@ -914,6 +914,29 @@ def convert(name, locals=locals,
914914 specs .append (formatvarkw (varkw ) + formatvalue (locals [varkw ]))
915915 return '(' + ', ' .join (specs ) + ')'
916916
917+ def _positional_error (f_name , args , kwonly , varargs , defcount , given , values ):
918+ atleast = len (args ) - defcount
919+ if given is None :
920+ given = len ([arg for arg in args if arg in values ])
921+ kwonly_given = len ([arg for arg in kwonly if arg in values ])
922+ if varargs :
923+ plural = atleast != 1
924+ sig = "at least %d" % (atleast ,)
925+ elif defcount :
926+ plural = True
927+ sig = "from %d to %d" % (atleast , len (args ))
928+ else :
929+ plural = len (args ) != 1
930+ sig = str (len (args ))
931+ kwonly_sig = ""
932+ if kwonly_given :
933+ msg = " positional argument%s (and %d keyword-only argument%s)"
934+ kwonly_sig = (msg % ("s" if given != 1 else "" , kwonly_given ,
935+ "s" if kwonly_given != 1 else "" ))
936+ raise TypeError ("%s() takes %s positional argument%s but %d%s %s given" %
937+ (f_name , sig , "s" if plural else "" , given , kwonly_sig ,
938+ "was" if given == 1 and not kwonly_given else "were" ))
939+
917940def getcallargs (func , * positional , ** named ):
918941 """Get the mapping of arguments to values.
919942
@@ -925,64 +948,50 @@ def getcallargs(func, *positional, **named):
925948 f_name = func .__name__
926949 arg2value = {}
927950
951+
928952 if ismethod (func ) and func .__self__ is not None :
929953 # implicit 'self' (or 'cls' for classmethods) argument
930954 positional = (func .__self__ ,) + positional
931955 num_pos = len (positional )
932- num_total = num_pos + len (named )
933956 num_args = len (args )
934957 num_defaults = len (defaults ) if defaults else 0
935- for arg , value in zip (args , positional ):
936- arg2value [arg ] = value
958+
959+ n = min (num_pos , num_args )
960+ for i in range (n ):
961+ arg2value [args [i ]] = positional [i ]
937962 if varargs :
938- if num_pos > num_args :
939- arg2value [varargs ] = positional [- (num_pos - num_args ):]
940- else :
941- arg2value [varargs ] = ()
942- elif 0 < num_args < num_pos :
943- raise TypeError ('%s() takes %s %d positional %s (%d given)' % (
944- f_name , 'at most' if defaults else 'exactly' , num_args ,
945- 'arguments' if num_args > 1 else 'argument' , num_total ))
946- elif num_args == 0 and num_total :
947- if varkw or kwonlyargs :
948- if num_pos :
949- # XXX: We should use num_pos, but Python also uses num_total:
950- raise TypeError ('%s() takes exactly 0 positional arguments '
951- '(%d given)' % (f_name , num_total ))
952- else :
953- raise TypeError ('%s() takes no arguments (%d given)' %
954- (f_name , num_total ))
955-
956- for arg in itertools .chain (args , kwonlyargs ):
957- if arg in named :
958- if arg in arg2value :
959- raise TypeError ("%s() got multiple values for keyword "
960- "argument '%s'" % (f_name , arg ))
961- else :
962- arg2value [arg ] = named .pop (arg )
963- for kwonlyarg in kwonlyargs :
964- if kwonlyarg not in arg2value :
965- try :
966- arg2value [kwonlyarg ] = kwonlydefaults [kwonlyarg ]
967- except KeyError :
968- raise TypeError ("%s() needs keyword-only argument %s" %
969- (f_name , kwonlyarg ))
970- if defaults : # fill in any missing values with the defaults
971- for arg , value in zip (args [- num_defaults :], defaults ):
972- if arg not in arg2value :
973- arg2value [arg ] = value
963+ arg2value [varargs ] = tuple (positional [n :])
964+ possible_kwargs = set (args + kwonlyargs )
974965 if varkw :
975- arg2value [varkw ] = named
976- elif named :
977- unexpected = next (iter (named ))
978- raise TypeError ("%s() got an unexpected keyword argument '%s'" %
979- (f_name , unexpected ))
980- unassigned = num_args - len ([arg for arg in args if arg in arg2value ])
981- if unassigned :
982- num_required = num_args - num_defaults
983- raise TypeError ('%s() takes %s %d %s (%d given)' % (
984- f_name , 'at least' if defaults else 'exactly' , num_required ,
985- 'arguments' if num_required > 1 else 'argument' , num_total ))
966+ arg2value [varkw ] = {}
967+ for kw , value in named .items ():
968+ if kw not in possible_kwargs :
969+ if not varkw :
970+ raise TypeError ("%s() got an unexpected keyword argument %r" %
971+ (f_name , kw ))
972+ arg2value [varkw ][kw ] = value
973+ continue
974+ if kw in arg2value :
975+ raise TypeError ("%s() got multiple values for argument %r" %
976+ (f_name , kw ))
977+ arg2value [kw ] = value
978+ if num_pos > num_args and not varargs :
979+ _positional_error (f_name , args , kwonlyargs , varargs , num_defaults ,
980+ num_pos , arg2value )
981+ if num_pos < num_args :
982+ for arg in args [:num_args - num_defaults ]:
983+ if arg not in arg2value :
984+ _positional_error (f_name , args , kwonlyargs , varargs ,
985+ num_defaults , None , arg2value )
986+ for i , arg in enumerate (args [num_args - num_defaults :]):
987+ if arg not in arg2value :
988+ arg2value [arg ] = defaults [i ]
989+ for kwarg in kwonlyargs :
990+ if kwarg not in arg2value :
991+ if kwarg not in kwonlydefaults :
992+ raise TypeError ("%s() requires keyword-only argument %r" %
993+ (f_name , kwarg ))
994+ arg2value [kwarg ] = kwonlydefaults [kwarg ]
986995 return arg2value
987996
988997# -------------------------------------------------- stack frame extraction
0 commit comments