bendudson / py4cl

Call python from Common Lisp
Other
234 stars 31 forks source link

Doing "from _ import _" in py4cl #50

Open brandflake11 opened 3 years ago

brandflake11 commented 3 years ago

Hello, I am not a python expert at all, but I was using this lisp package to convert some python code into a lisp project I am working on. How would one go about using py4cl to do this kind of python code:

from SigMFUtils import SigMFUtil

I have attempted to do this with PYTHON-EVAL like this: (py4cl:python-eval "from SigMFUtils import SigMFUtil")

but this doesn't work and results in a python syntax error. Is there a way to from import with IMPORT-MODULE?

Thank you very much for your time and any help you can offer.

digikar99 commented 3 years ago

Python like most non-lisp languages makes a distinction between expressions and non-expression-statements; so you'd rather need to use (py4cl:python-exec "from SigMFUtils import SigMFUtil") instead of python-eval.

brandflake11 commented 3 years ago

Thank you very much for your help @digikar99. I wanted to also ask you, which is related to this: How do I also change the directory where the python process for py4cl loads? The SigMFUtils I'm trying to load is in a specific folder, and I need to be in that directory in order for the python process to find SigMFUtils.

I know how to do this if I am just using python in a shell (cd directory && python .), but when it comes to py4cl, I'm not quite sure how that works.

digikar99 commented 3 years ago

There's also another way to change the process directory once the process has started: in the case of python, it is import os; os.chdir("directory/you/want"); so you could just call (python-exec "import os; os.chdir('directory/you/want')")

brandflake11 commented 3 years ago

@digikar99 Thanks so much for your help. As you can tell, I am not very good with python. :D I'll go ahead and close this.

brandflake11 commented 3 years ago

I apologize for reopening this issue. I'm actually having trouble with reading the .py file using the above code we talked about. Here is an example I'm having trouble with:

(ql:quickload :py4cl)

(py4cl:import-module "sigmf")
(py4cl:import-module "types")
(py4cl:import-module "json")
(py4cl:import-module "matplotlib.pyplot" :as "plt")
(py4cl:import-module "os")

(os:chdir "/home/brandon/my-dir/")
(os:getcwd)

(py4cl:python-exec "from SigMFUtils import SigMFUtil")

The above gives an error, even though the output of OS:GETCWD is the correct directory:


Python error: "No module named 'SigMFUtils'"
   [Condition of type py4cl:python-error]

Restarts:
 0: [retry] Retry SLIME interactive evaluation request.
 1: [*abort] Return to SLIME's top level.
 2: [abort] abort thread (#<thread "worker" running {10169097D3}>)

Backtrace:
  0: (py4cl::dispatch-messages #<uiop/launch-program::process-info {1009DEE2D3}>)
  1: (sb-int:simple-eval-in-lexenv (py4cl:python-exec "from SigMFUtils import SigMFUtil") #<NULL-LEXENV>)
  2: (eval (py4cl:python-exec "from SigMFUtils import SigMFUtil"))
  3: ((lambda nil :in swank:interactive-eval))
 --more--

However, if I run the same code using python 3.9.6 in my shell:

import sigmf
import types
import json
import matplotlib.pyplot as plt
import os

os.chdir("/home/brandon/my-dir")
from SigMFUtils import SigMFUtil

I don't get any error and SigMFUtil gets imported correctly. SigMFUtils is a .py file (named SigMFUtils.py) in the directory that I chdir to. Is this a bug, or am I missing something?

Thank you very much for your time!

digikar99 commented 3 years ago

Erm... Check for:

(python-exec "import sys")
(python-eval "sys.path") ; EDIT: corrected, thanks brandflake11!

Okay, I wasn't exactly clear about this, but the first item of this list, sys.path[0], is the directory containing the script that was used to invoke the Python interpreter; I had assumed it already contains the current-directory of the program, but turns out that isn't exactly the case. So, the current working directory would need to be added manually; hopefully that does it for you:

(python-exec "sys.path.append('./')") ; EDIT: corrected
brandflake11 commented 3 years ago

Okay, I looked at this and tried some of it. A couple of things:

  1. I was able to import sys. However, in order to see the sys.path I had to change your code to this: (python-eval "sys.path")

This needed to be a string in order for lisp to print out the paths from python. It just returned a symbol :sys.path otherwise.

  1. Then, to run the sys.append, I looked it up and the command should actually be: (python-exec "sys.path.append('./')")

It seems that sys.append is not a command you can run, but after a quick search sys.path.append was the right answer.

I sys.path.appended the directory that contains the SigMFUtils.py file I'm trying to import.

After appending the path to the directory I am working in, I am getting this error now:

Python error: "cannot import name 'SigMFFile' from 'sigmf' (/home/brandon/.local/lib/python3.9/site-packages/sigmf/__init__.py)"
   [Condition of type py4cl:python-error]

Restarts:
 0: [retry] Retry SLIME interactive evaluation request.
 1: [*abort] Return to SLIME's top level.
 2: [abort] abort thread (#<thread "worker" running {101A739C33}>)

Backtrace:
  0: (py4cl::dispatch-messages #<uiop/launch-program::process-info {101447E383}>)
  1: (sb-int:simple-eval-in-lexenv (py4cl:python-exec "from SigMFUtils import SigMFUtil") #<NULL-LEXENV>)
  2: (eval (py4cl:python-exec "from SigMFUtils import SigMFUtil"))
  3: ((lambda nil :in swank:interactive-eval))
 --more--

Everything looks normal as if I was running python from the shell. I just can't seem to import this. Let me know if you have any more ideas, and if not, that's fine too! Thanks again for your help!

digikar99 commented 3 years ago

My bad at several places! Apologies, and corrected.

cannot import name 'SigMFFile' from 'sigmf'

Okay... the value in sys.path while running as a script includes the absolute path to the directory-containing-script instead of the relative; does appending the absolute path instead of the relative path './' work?

May be the value of sys.path could be set from the py4cl/2 end in the future.

brandflake11 commented 3 years ago

No problem @digikar99! I actually have tried to add the ./ and absolute path to the sys.path. Does the order of sys.path matter? Do I need to start sbcl in the same directory as the .py file I'm trying to load? I run sbcl from slime if that matters.

digikar99 commented 3 years ago

I don't think I know if the order matters; so your best bet (as usual in programming) would be to try and see, and/or consult stackoverflow. For a simple sample file I created on my system to test this, it worked as expected; but for anything more complex, I don't know python enough or have a module to test this with.

You could also try changing the path of the lisp program to the relevant directory using uiop:chdir (or starting sbcl in the relevant directory itself) before starting the python process; but I'm unsure if that'd work.

brandflake11 commented 3 years ago

Thanks so much @digikar99 for all of your help. I'll keep experimenting and see if I can find a solution. I appreciate your help.