# From PyCall to PythonCall

**URL:** <https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874>\
**Category:** General Usage\
**Tags:** question, package, sympy, pythoncall\
**Created:** [October 11, 2023, 7:35pm UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874 "2023-10-11T19:35:15Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [October 11, 2023, 7:35pm UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/1 "2023-10-11T19:35:15Z")

</div>

I am trying to convert SymbolicControlSystems.jl from using PyCall to PythonCall.  
How can I translate the following line:

```julia
const sp = SymPy.PyCall.PyNULL()

```

I tried, but this does not work:

```julia
const sp = SymPyPythonCall.PythonCall.PyNULL()

```

Any idea?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [October 11, 2023, 7:45pm UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/2 "2023-10-11T19:45:22Z")

</div>

> [@ufechner7](#):
>
> I tried, but this does not work:
> 
> ```julia
> const sp = SymPyPythonCall.PythonCall.PyNULL()
> 
> ```

Use `SymPyPythonCall.PythonCall.pynew()`. For example, compare [PyPlot.jl/src/init.jl](https://github.com/JuliaPy/PyPlot.jl/blob/6c91a61dd7e6c73784b360427384e6527cea1d09/src/init.jl) to [PythonPlot.jl/src/init.jl](https://github.com/JuliaPy/PythonPlot.jl/blob/fe588f4e3b084b9d0ada8641939c6331def77036/src/init.jl).

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [October 11, 2023, 7:51pm UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/3 "2023-10-11T19:51:00Z")

</div>

Thanks! Next question: How to convert the init method:

```julia
function __init__ ()
    # global const s = Sym("s")
    # global const z = Sym("z")
    copy!(sp, SymPy.sympy)
end

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [October 11, 2023, 7:54pm UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/4 "2023-10-11T19:54:29Z")

</div>

> [@ufechner7](#):
>
> `copy!(sp, SymPy.sympy)`

Replace `copy!` with `PythonCall.pycopy!`, as in the PyPlot → PythonPlot example I linked.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [October 11, 2023, 8:45pm UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/5 "2023-10-11T20:45:33Z")

</div>

> [@stevengj](#):
>
> `PythonCall.pycopy!`

OK, I can load the package now, but still a lot of test failures:

```julia
Test Summary: | Pass Fail Error Total Time
SymbolicControlSystems.jl | 12 1 54 67 22.0s
  To symbolic | 7 7 2.7s
  SymPy: Sym -> tf and vice versa | 24 24 5.1s
  sym2num | 1 12 13 0.8s
  Tustin and C-code | 1 1 0.1s
  Symbolics: Sym -> tf and vice versa | 12 8 20 10.7s
  conversion between SymPy and Symbolics | 2 2 2.6s
ERROR: LoadError: Some tests did not pass: 12 passed, 1 failed, 54 errored, 0 broken.
in expression starting at /home/ufechner/repos/SymbolicControlSystems.jl/test/runtests.jl:37
ERROR: Package SymbolicControlSystems errored during testing

```

Will look into them tomorrow…

Feel free to have a look at my current code: [GitHub - ufechner7/SymbolicControlSystems.jl: C-code generation and an interface between ControlSystems.jl and SymPy.jl](https://github.com/ufechner7/SymbolicControlSystems.jl)

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [October 12, 2023, 6:27am UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/6 "2023-10-12T06:27:05Z")

</div>

Next question: How can I convert a Python number to a Float64?  
The following works with PyCall, but not with PythonCall:

```julia
julia> pol = 2s^2+3s+4
2*s^2 + 3*s + 4

julia> n = sp.Poly(pol, s)
Python: Poly(2*s**2 + 3*s + 4, s, domain='ZZ')

julia> n.degree()
Python: 2

julia> deg = n.degree() |> Float64
ERROR: MethodError: no method matching Float64(::PythonCall.Py)

```

UPDATE:  
This works:

```julia
pyconvert(Float64, n.degree())

```

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [October 12, 2023, 6:49am UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/7 "2023-10-12T06:49:29Z")

</div>

OK, I managed to convert my first function:

```julia
function expand_coeffs(n, var; numeric = false)
    n = sp.Poly(n, var)
    # deg = n.degree() |> Float64 |> Int
    deg = pc.pyconvert(Float64, n.degree()) |> Int
    c = n.all_coeffs() # This fails if the coeffs are symbolic
    # numeric && (c = Float64.(c))
    numeric && (c = pc.pyconvert(Vector{Float64}, c))
    [c; zeros(eltype(c), deg - length(c) + 1)]
end

```

But is this a good way of doing it? It looks a bit verbose…

---

<div class="post-metadata">

**Author:** ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)\
**Post date:** [October 12, 2023, 7:47am UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/8 "2023-10-12T07:47:57Z")

</div>

Looks good to me. The verbosity is because PythonCall intentionally requires explicitly converting things back to Julia with `pyconvert` (unlike PyCall) - but ultimately it makes your code easier to reason about and more type stable.

The only improvements I can suggest are to do

```julia
using SymPyPythonCall.PythonCall

```

then you don’t need the `pc.` prefix.

Aside: you should really just have your project depend on PythonCall explicitly and do

```julia
using PythonCall

```

Also you can replace

```julia
pc.pyconvert(Float64, n.degree()) |> Int

```

with simply

```julia
pc.pyconvert(Int, n.degree())

```

---

<div class="post-metadata">

**Author:** ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)\
**Post date:** [October 12, 2023, 7:55am UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/9 "2023-10-12T07:55:41Z")

</div>

BTW while the above advice about `pynew` and `pycopy!` is correct, I advise using one of the methods documented here instead: [Guide · PythonCall & JuliaCall](https://juliapy.github.io/PythonCall.jl/stable/pythoncall/#Precompilation) (my recommendation is the first version, with `Ref`).

These methods are safe (whereas `pycopy!` is unsafe in that it can cause memory corruption if you get it wrong), and I’m considering deprecating `pycopy!` from the API in PythonCall v1.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [October 12, 2023, 8:04am UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/10 "2023-10-12T08:04:07Z")

</div>

Thank you!

I now run in the next, perhaps more complicated issue.  
After installing my fork

```julia
]
add https://github.com/ufechner7/SymbolicControlSystems.jl

```

and executing:

```julia
using SymbolicControlSystems
using ControlSystemsBase
@syms a b c
sys=(tf([a], [1, c, b]))
sp.Poly(denvec(sys)[], s)

```

I get the error:

```julia
julia> sp.poly(denvec(sys)[], s)
ERROR: MethodError: no method matching getptr(::Vector{PythonCall.Py})

Closest candidates are:
  getptr(::Any)
   @ PythonCall ~/.julia/packages/PythonCall/qTEA1/src/Py.jl:26
  getptr(::PythonCall.Py)
   @ PythonCall ~/.julia/packages/PythonCall/qTEA1/src/Py.jl:49

```

where

```julia
julia> denvec(sys)[]
3-element Vector{Sym}:
 1
 c
 b

```

Any idea?  
Perhaps I have to convert this vector to a different type before passing it to the SymPyPythonCall function poly?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [October 12, 2023, 12:52pm UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/11 "2023-10-12T12:52:19Z")

</div>

> [@cjdoris](#):
>
> These methods are safe (whereas `pycopy!` is unsafe in that it can cause memory corruption if you get it wrong), and I’m considering deprecating `pycopy!` from the API in PythonCall v1.

If anything, rename it to `unsafe_pycopy!`. It’s quite useful for module initialization because it allows a module like PythonPlot to initialize a variable like `PythonPlot.matplotlib` to directly access a Python object, without forcing the user to do `PythonPlot.matplotlib[]`.

(But it can be made safe if you only use it with valid `Py` objects or `NULL` objects, no? Whereas with an invalid `Py` object, i.e. a corrupted pointer, everything is unsafe. It’s really `PythonCall.pynew()` that’s unsafe, because a lot of PythonCall functions don’t check for NULL pointers.)

---

<div class="post-metadata">

**Author:** ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)\
**Post date:** [October 12, 2023, 3:33pm UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/12 "2023-10-12T15:33:16Z")

</div>

> [@stevengj](#):
>
> If anything, rename it to `unsafe_pycopy!`.

Absolutely that’s one option. I’ll make a GitHub issue about it to not derail this thread.

---

<div class="post-metadata">

**Author:** ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)\
**Post date:** [October 12, 2023, 3:42pm UTC](https://discourse.julialang.org/t/from-pycall-to-pythoncall/104874/13 "2023-10-12T15:42:27Z")

</div>

> [@ufechner7](#):
>
> Any idea?

What’s the stacktrace for that error?
