From 0f66f8e6d8f878505806d751df450a71fbe7b8ab Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Thu, 4 May 2023 18:26:11 +0200 Subject: [PATCH] Dont try to initialize frame for python 3.11 and later --- .github/workflows/test.yml | 4 +--- nimpy.nim | 10 ++++++++-- nimpy/py_lib.nim | 8 ++++++-- tests/tpyfromnim.nim | 13 ++++++++----- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e438bf2b1..a303643b2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,12 +10,10 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] nim-channel: [stable, devel] - python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10"] + python-version: ["2.7", "3.7", "3.8", "3.9", "3.10", "3.11"] exclude: - os: windows-latest python-version: "2.7" # DLL not found, specific to CI? - - os: windows-latest - python-version: "3.10" # FIXME: eval doesn't work name: ${{ matrix.os }}-${{ matrix.python-version }}-${{ matrix.nim-channel }} runs-on: ${{ matrix.os }} diff --git a/nimpy.nim b/nimpy.nim index 2fc889dc1..ba5b9dc7f 100644 --- a/nimpy.nim +++ b/nimpy.nim @@ -941,11 +941,15 @@ proc pyBuiltins*(): PyObject = proc pyGlobals*(): PyObject = initPyLibIfNeeded() - newPyObject(pyLib.PyEval_GetGlobals()) + let r = pyLib.PyEval_GetGlobals() + if not r.isNil: + result = newPyObject(r) proc pyLocals*(): PyObject = initPyLibIfNeeded() - newPyObject(pyLib.PyEval_GetLocals()) + let r = pyLib.PyEval_GetLocals() + if not r.isNil: + result = newPyObject(r) proc dir*(v: PyObject): seq[string] = let lst = pyLib.PyObject_Dir(v.rawPyObj) @@ -1002,6 +1006,8 @@ macro toPyDictRaw(a: untyped): PPyObject = template toPyDict*(a: untyped): PyObject = newPyObjectConsumingRef(toPyDictRaw(a)) +proc pyDict*(): PyObject = + newPyObjectConsumingRef(toPyDictRaw(())) ################################################################################ ################################################################################ diff --git a/nimpy/py_lib.nim b/nimpy/py_lib.nim index fb22a531c..423960249 100644 --- a/nimpy/py_lib.nim +++ b/nimpy/py_lib.nim @@ -490,6 +490,8 @@ proc initPyThreadFrame() = # https://stackoverflow.com/questions/42974139/valueerror-call-stack-is-not-deep-enough-when-calling-ipython-embed-method # needed for eval and stuff like pandas.query() otherwise crash (call stack is not deep enough) + # + # XXX Unfortunately this doesn't work with python 3.11 and later. if unlikely pyLib.isNil: initPyLib(pythonLibHandleFromExternalLib()) pyThreadFrameInited = true @@ -502,7 +504,8 @@ proc initPyThreadFrame() = of 2: if not cast[ptr PyThreadState2](pyThread).frame.isNil: return of 3: - if not cast[ptr PyThreadState3](pyThread).frame.isNil: return + if pyLib.pythonVersion < (3, 11, 0): + if not cast[ptr PyThreadState3](pyThread).frame.isNil: return else: doAssert(false, "unreachable") @@ -523,7 +526,8 @@ proc initPyThreadFrame() = of 2: cast[ptr PyThreadState2](pyThread).frame = root_frame of 3: - cast[ptr PyThreadState3](pyThread).frame = root_frame + if pyLib.pythonVersion < (3, 11, 0): + cast[ptr PyThreadState3](pyThread).frame = root_frame else: doAssert(false, "unreachable") diff --git a/tests/tpyfromnim.nim b/tests/tpyfromnim.nim index 6a18052db..a6784198d 100644 --- a/tests/tpyfromnim.nim +++ b/tests/tpyfromnim.nim @@ -48,8 +48,8 @@ proc test*() {.gcsafe.} = block: # eval let py = pyBuiltinsModule() - doAssert(py.eval("3+3").to(int) == 6) - doAssert(py.eval(""" "hello" * 2 """).to(string) == "hellohello") + doAssert(py.eval("3+3", pyDict(), pyDict()).to(int) == 6) + doAssert(py.eval(""" "hello" * 2 """, pyDict(), pyDict()).to(string) == "hellohello") block: var ints = newSeq[int]() @@ -246,6 +246,7 @@ proc test*() {.gcsafe.} = block: # Kinda subclassing python objects in nim and calling super if pyImport("sys").version_info.major.to(int) >= 3: # Only test with python 3 let py = pyBuiltinsModule() + let locals = toPyDict(()) # Create empty dict # Let's say there's this python code: discard py.exec(""" @@ -255,7 +256,9 @@ proc test*() {.gcsafe.} = def useFoo(foo): return foo.overrideMe() - """.dedent()) + """.dedent(), pyDict(), locals) + + let fooClass = locals["Foo"] # Create a subclass of Foo in Nim: proc createFooSubclassInstance(): PyObject = @@ -268,7 +271,7 @@ proc test*() {.gcsafe.} = proc overrideMe(): int = self.super.overrideMe().to(int) + 123 # Call super - self = py.`type`("_", (pyGlobals()["Foo"], ), toPyDict({ + self = py.`type`("_", (fooClass, ), toPyDict({ "overrideMe": overrideMe })).to(proc(): PyObject {.gcsafe.})() return self @@ -277,7 +280,7 @@ proc test*() {.gcsafe.} = let b = createFooSubclassInstance() # Get `useFoo` proc - let useFoo = pyGlobals()["useFoo"].to(proc(self: PyObject): int {.gcsafe.}) + let useFoo = locals["useFoo"].to(proc(self: PyObject): int {.gcsafe.}) # Pass b to `useFoo` doAssert(useFoo(b) == 125)