facehugger
facehugger

Reputation: 408

SystemError is raised after attempt to create function with positional-only args in runtime inside other one

There is some test code:

some_type = int

def func0():
    def func1(arg: some_type, /):
        pass

func0()

And I get the following error:

Traceback (most recent call last): 
...
SystemError: no locals when loading 'some_type'

However the code below works as expected:

some_type = int

def func0():
    def func1(arg: some_type):
        pass

func0()

And this one is also valid:

some_type = int
exec('''
def func1(arg: some_type, /):
    pass
''')

I know that annotations will no longer be evaluated at definition time in future versions; also it is possible to activate such behaviour in 3.7+ versions. Something like

from __future__ import annotations
some_type = int

def func0():
    def func1(arg: some_type, /):
        pass

func0()

has no problems as well. However, the question is about the current strange behaviour at function definition time. some_type is in no way a local variable of func0, though python thinks so. One more fine version:

def func0():
    some_type = int
    def func1(arg: some_type, /):
        pass

func0()

I've read PEP 570 but not found anything about annotations declarations there.

My python version:

sys.version_info(major=3, minor=8, micro=0, releaselevel='final', serial=0)

Upvotes: 1

Views: 594

Answers (1)

anthony sottile
anthony sottile

Reputation: 70175

This is a bug in cpython -- I've opened an issue for it here: https://bugs.python.org/issue39215

looking at the disassembly of the two functions, it appears as though it incorrectly uses LOAD_NAME instead of LOAD_GLOBAL when building the annotation type -- here's the diff between one with and without positional only arguments:

$ diff -u <(python3.9 -m dis t2.py | sed 's/0x[a-f0-9]*/0xdeadbeef/g;s/t2\.py/FILENAME/g') <(python3.9 -m dis t3.py | sed 's/0x[a-f0-9]*/0xdeadbeef/g;s/t3\.py/FILENAME/g')
--- /dev/fd/63  2020-01-04 16:34:27.372004436 -0800
+++ /dev/fd/62  2020-01-04 16:34:27.372004436 -0800
@@ -10,7 +10,7 @@
              16 RETURN_VALUE

 Disassembly of <code object f at 0xdeadbeef, file "FILENAME", line 1>:
-  2           0 LOAD_NAME                0 (int)
+  2           0 LOAD_GLOBAL              0 (int)
               2 LOAD_CONST               1 (('arg',))
               4 BUILD_CONST_KEY_MAP      1
               6 LOAD_CONST               2 (<code object g at 0xdeadbeef, file "FILENAME", line 2>)

EDIT: and here's a pull request to fix this (should land in 3.8.2 and 3.9.0a3 if my guess about timing is correct): https://github.com/python/cpython/pull/17826

Upvotes: 2

Related Questions