JuliaPy / PythonCall.jl

Python and Julia in harmony.
https://juliapy.github.io/PythonCall.jl/stable/
MIT License
776 stars 63 forks source link

AttributeError: 'NoneType' object has no attribute 'f_locals' #490

Open albertomercurio opened 5 months ago

albertomercurio commented 5 months ago

Affects: PythonCall

Describe the bug Minimal working example:

using PythonCall

sym = pyimport("sympy")

a = sym.Symbol("a", positive=true)
sym.lambdify(a, a)
Python: AttributeError: 'NoneType' object has no attribute 'f_locals'

Stacktrace:
 [1] pythrow()
   @ PythonCall.Core [~/.julia/packages/PythonCall/bb3ax/src/Core/err.jl:92](https://file+.vscode-resource.vscode-cdn.net/home/alberto/GitHub/PhD-Thesis-Notebooks/~/.julia/packages/PythonCall/bb3ax/src/Core/err.jl:92)
 [2] errcheck
   @ [~/.julia/packages/PythonCall/bb3ax/src/Core/err.jl:10](https://file+.vscode-resource.vscode-cdn.net/home/alberto/GitHub/PhD-Thesis-Notebooks/~/.julia/packages/PythonCall/bb3ax/src/Core/err.jl:10) [inlined]
 [3] pycallargs(f::Py, args::Py)
   @ PythonCall.Core [~/.julia/packages/PythonCall/bb3ax/src/Core/builtins.jl:212](https://file+.vscode-resource.vscode-cdn.net/home/alberto/GitHub/PhD-Thesis-Notebooks/~/.julia/packages/PythonCall/bb3ax/src/Core/builtins.jl:212)
 [4] pycall(::Py, ::Py, ::Vararg{Py}; kwargs::@Kwargs{})
   @ PythonCall.Core [~/.julia/packages/PythonCall/bb3ax/src/Core/builtins.jl:230](https://file+.vscode-resource.vscode-cdn.net/home/alberto/GitHub/PhD-Thesis-Notebooks/~/.julia/packages/PythonCall/bb3ax/src/Core/builtins.jl:230)
 [5] pycall(::Py, ::Py, ::Vararg{Py})
   @ PythonCall.Core [~/.julia/packages/PythonCall/bb3ax/src/Core/builtins.jl:220](https://file+.vscode-resource.vscode-cdn.net/home/alberto/GitHub/PhD-Thesis-Notebooks/~/.julia/packages/PythonCall/bb3ax/src/Core/builtins.jl:220)
 [6] (::Py)(::Py, ::Vararg{Py}; kwargs::@Kwargs{})
   @ PythonCall.Core [~/.julia/packages/PythonCall/bb3ax/src/Core/Py.jl:339](https://file+.vscode-resource.vscode-cdn.net/home/alberto/GitHub/PhD-Thesis-Notebooks/~/.julia/packages/PythonCall/bb3ax/src/Core/Py.jl:339)
 [7] top-level scope
   @ In[8]:5

Your system

Status `~/GitHub/PhD-Thesis-Notebooks/Project.toml`
  [992eb4ea] CondaPkg v0.2.22
  [429524aa] Optim v1.9.4
  [6099a3de] PythonCall v0.9.19
  [6c2fb7c5] QuantumToolbox v0.7.2
  [a25cea48] SpecialPolynomials v0.4.9
julia> versioninfo()
Julia Version 1.10.2
Commit bd47eca2c8a (2024-03-01 10:14 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 32 × 13th Gen Intel(R) Core(TM) i9-13900KF
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, goldmont)
Threads: 32 default, 0 interactive, 16 GC (on 32 virtual cores)
Environment:
  JULIA_EDITOR = code
  JULIA_NUM_THREADS = 32
CondaPkg Status /home/alberto/GitHub/PhD-Thesis-Notebooks/CondaPkg.toml
Environment
  /home/alberto/GitHub/PhD-Thesis-Notebooks/.CondaPkg/env
Packages
  numpy v1.26.4
  sympy v1.12

Additional context Add any other context about the problem here.

cjdoris commented 5 months ago

That error happens here in sympy: https://github.com/sympy/sympy/blob/af0c8559a845df49a6d0f8855659cebd0ddc9bc8/sympy/utilities/lambdify.py#L849

What's happening is that lambdify is assuming it is being called from Python, whereas it's actually being called from Julia (and therefore there are no higher stack frames, which is what that line is trying to access). I'm not familiar with sympy but it looks like it's trying to access the variables in the scope where lambdify is being called from. Which is pretty magical and sketchy TBH.

AFAIK this isn't fixable from PythonCall. You've got a couple of options:

  1. Raise an issue (or make a PR) with sympy changing it so that callers_local_vars only gets computed if it is actually needed. In your case it is not needed. This would just be a mild reordering of the logic of that block. Or add a flag to skip that bit of logic - which looks optional anyway.
  2. In your own code, only call lambdify from within a proper Python function.

For option 2 I mean you can do something like

@pyexec """
def lambdify(*args):
    import sympy
    return sympy.lambdify(*args)
""" => lambdify

then use this lambdify function instead.