2010/10/22 M.-A. Lemburg <span dir="ltr"><<a href="mailto:mal@egenix.com">mal@egenix.com</a>></span><br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;"><div><div class="h5">
Cesare Di Mauro wrote:<br>> I think that having more than 255 arguments for a function call is a very<br>
> rare case for which a workaround (may be passing a tuple/list or a<br>
> dictionary) can be a better solution than having to introduce a brand new<br>
> opcode to handle it.<br>
<br>
</div></div>It's certainly rare when writing applications by hand, but such<br>
limits can be reached with code generators wrapping external resources<br>
such as database query rows, spreadsheet rows, sensor data input, etc.<br>
<br>
We've had such a limit before (number of lines in a module) and that<br>
was raised for the same reason.<br>
<div class="im"><br>
> Changing the current opcode(s) is a very bad idea, since common cases will<br>
> slow down.<br>
<br>
</div>I'm sure there are ways to avoid that, e.g. by using EXTENDED_ARG<br>
for such cases.<br>
<br>
--<br>
<div class="im">Marc-Andre Lemburg<br>
eGenix.com<br>
</div></blockquote><div><br></div><div>I've patched Python 3.2 alpha 3 with a rough solution using EXTENDED_ARG for CALL_FUNCTION* opcodes, raising the arguments and keywords limits to 65535 maximum. I hope it'll be enough. :)</div>
<div><br></div><div><br></div><div>In ast.c:</div><div><br></div><div>ast_for_arguments:<br></div><div>    if (nposargs > 65535 || nkwonlyargs > 65535) {<br>        ast_error(n, "more than 65535 arguments");<br>
        return NULL;<br>    }<br></div><div><br></div><div>ast_for_call:<br>    if (nargs + ngens > 65535 || nkeywords > 65535) {<br>        ast_error(n, "more than 65535 arguments");<br>      return NULL;<br>
    }<br></div><div><br></div><div><br></div><div>In compile.c:<br></div><div><br></div><div>opcode_stack_effect:</div><div>#define NARGS(o) (((o) & 0xff) + ((o) >> 8 & 0xff00) + 2*(((o) >> 8 & 0xff) + ((o) >> 16 & 0xff00)))<br>
                case CALL_FUNCTION:<br>                        return -NARGS(oparg);<br>                case CALL_FUNCTION_VAR:<br>                case CALL_FUNCTION_KW:<br>                        return -NARGS(oparg)-1;<br>
                case CALL_FUNCTION_VAR_KW:<br>                        return -NARGS(oparg)-2;<br>#undef NARGS<br>#define NARGS(o) (((o) % 256) + 2*(((o) / 256) % 256))<br>                case MAKE_FUNCTION:<br>                        return -NARGS(oparg) - ((oparg >> 16) & 0xffff);<br>
                case MAKE_CLOSURE:<br>                        return -1 - NARGS(oparg) - ((oparg >> 16) & 0xffff);<br>#undef NARGS<br><br></div><div>compiler_call_helper:</div><div>    int len;<br>    int code = 0;<br>
<br>    len = asdl_seq_LEN(args) + n;<br>    n = len & 0xff | (len & 0xff00) << 8;<br>    VISIT_SEQ(c, expr, args);<br>    if (keywords) {<br>  VISIT_SEQ(c, keyword, keywords);<br>  len = asdl_seq_LEN(keywords);<br>
  n |= (len & 0xff | (len & 0xff00) << 8) << 8;<br>    }<br><br></div><div><br></div><div>In ceval.c:<br></div><div><br></div><div>PyEval_EvalFrameEx:</div><div>                TARGET_WITH_IMPL(CALL_FUNCTION_VAR, _call_function_var_kw)<br>
                TARGET_WITH_IMPL(CALL_FUNCTION_KW, _call_function_var_kw)<br>                TARGET(CALL_FUNCTION_VAR_KW)<br>                _call_function_var_kw:<br>                {<br>                        int na = oparg & 0xff | oparg >> 8 & 0xff00;<br>
                        int nk = (oparg & 0xff00 | oparg >> 8 & 0xff0000) >> 8;<br><br></div><div><br></div><div>call_function:<br>    int na = oparg & 0xff | oparg >> 8 & 0xff00;<br>    int nk = (oparg & 0xff00 | oparg >> 8 & 0xff0000) >> 8;<br>
<br></div><div><br></div><div>A quick example:</div><div><br></div><div>s = '''def f(*Args, **Keywords):<br>    print('Got', len(Args), 'arguments and', len(Keywords), 'keywords')<br><br>
def g():</div><div>    f(''' + ', '.join(str(i) for i in range(500)) + ', ' + ', '.join('k{} = {}'.format(i, i) for i in range(500)) + ''')<br><br>g()<br>'''<br>
<br>c = compile(s, '<string>', 'exec')<br>eval(c)<br>from dis import dis<br>dis(g)<br></div><div><br></div><div><br>The output is:<br><br>Got 500 arguments and 500 keywords<br><br>  5           0 LOAD_GLOBAL              0 (f)<br>
              3 LOAD_CONST               1 (0)<br>              6 LOAD_CONST               2 (1)<br>[...]<br>           1497 LOAD_CONST             499 (498)<br>           1500 LOAD_CONST             500 (499)<br>           1503 LOAD_CONST             501 ('k0')<br>
           1506 LOAD_CONST               1 (0)<br>           1509 LOAD_CONST             502 ('k1')<br>           1512 LOAD_CONST               2 (1)<br>[...]<br>           4491 LOAD_CONST             999 ('k498')<br>
           4494 LOAD_CONST             499 (498)<br>           4497 LOAD_CONST            1000 ('k499')<br>           4500 LOAD_CONST             500 (499)<br>           4503 EXTENDED_ARG           257<br>           4506 CALL_FUNCTION        16905460<br>
           4509 POP_TOP<br>           4510 LOAD_CONST               0 (None)<br>           4513 RETURN_VALUE<br><br></div><div>The dis module seems to have some problem displaying the correct extended value, but I have no time now to check and fix it.</div>
<div><br></div><div>Anyway, I'm still unconvinced of the need to raise the function def/call limits.</div><div><br></div><div>Cesare</div>