Get Current Working Directory With Python C Extension - python

I am building a Python C Extension that can load images.
To load a certain image, the user has to write the file path for that image.
The problem is that the user has to type the whole file path, even if the image is in the same directory as their module.
Python itself has some useful tools like os.path and pathlib that can help you to find the path of the current module.
I have searched the Python C API and I cannot seem to find any tools that relate to finding the current working directory in C.
What can I do - in C - to get the path to the directory of the user's module?
Are there any Python.h functions?
Am I taking the wrong approach?

The python c api can interact with lots of things you can do in Python.
How would you do this in Python? You would import os and call os.getcwd(), right?
Turns out you can do the same in the c api.
PyObject *os_module = PyImport_ImportModule("os");
if (os_module == NULL) { //error handling
return NULL;
}
PyObject* cwd = PyObject_CallMethod(os_module, "getcwd", NULL);
if (cwd == NULL) { //error handling
return NULL;
}
char* result = PyUnicode_AsUTF8(cwd);
And then when you're done with the module, or any pyobject in general, remember to call Py_DECREF() on it.

Related

Python ctypes breaks on include iostream

I am using python ctypes to call a function in c++ from python. Currently, I have the following c++ file:
five.cpp
extern "C" {
int get_five(){
return 5;
}
}
And python file:
five.py
import ctypes
from pathlib import Path
lib = ctypes.CDLL(Path(Path.cwd(),'five.dll').as_posix())
print(lib.get_five())
Which works and prints the number 5 when i run it.
However, as soon as I include any headers in the c++ file, it breaks down. So if I change the file to:
#include <iostream>
extern "C" {
int get_five(){
return 5;
}
}
It breaks, and I get the following error:
FileNotFoundError: Could not find module '...\five.dll' (or one of its dependencies). Try using the full path with constructor syntax.
I am compiling on Windows, with the following command:
g++ -shared five.cpp -o five.dll
I am probably missing something obvious, since I am very new to programming in c++. However, I can't seem to find what it is.

Executing Python Script from Windows Forms .NET

I am fairly new to Python and .NET in general, but decided to ask more competent people, since I have been struggling with the issue of executing python script from Windows Forms.
The basic idea of my project is a desktop applicaton and the overall logic would be to read from a couple of selected check boxes, pass the values of those selections to my python script, from there I generate an excell table based on those results, and display this table back into the Windows Forms application.
Creating the table and managing to display it in the Desktop App is already done, but I am having serious issues with the communication between the two platforms, when it came to executing the script itself.
I have tried using IronPython and it worked perfectly, untill the fact that I found that Iron Python does not support CPython packages, like Pandas, which is build on numpy, and numpy apparantly is one of those packages. I looked over a lot of articles about this issue and the answers did not seem promising and most of the suggestions were to use pythonnet.
I tried to implement pythonnet, following numerous articles and all I managed to do, besides creating a bigger mess, is nothing as a result.
Finally, I decided to use C# Process class, but did not succeed also.
Would appreciate if there are any comments and suggestions on how to remedy this issue.
Python version: 3.7
Windows 10 (64 bit)
.NET Framework 4.7.2
Here is some of my code attempts in Windows Forms:
Implementation with the usage of the Process Class
Issue here is that I am not able to run this script due to the error messages that it cannot find the packages for the python script
var processStartInfo = new ProcessStartInfo
{
Arguments = "C:\\Users\\Dobromir\\PycharmProjects\\pythonProject\\main.py",
FileName = "C:\\Python27\\python.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Process.Start(processStartInfo);
Implementation using IronPython (which was working before the usage of pandas package)
Issue here is the CPython packages limitation and errors.
For IronPython I had to downgrade to Python 2.7 in order to work with it. For the rest of the examples I am using Python 3.7
ScriptEngine pythonEngine = Python.CreateEngine();
var searchPaths = pythonEngine.GetSearchPaths();
searchPaths.Add(#"C:\Python27\Lib");
searchPaths.Add(#"C:\Users\Dobromir\PycharmProjects\pythonProject\venv\Lib\site-packages");
pythonEngine.SetSearchPaths(searchPaths);
List<String> argv = new List<String>();
argv.Add("Some Value1");
argv.Add("Some Value2");
ScriptSource pythonScript = pythonEngine.CreateScriptSourceFromFile("C:\\Users\\Dobromir\\PycharmProjects\\pythonProject\\main.py");
pythonEngine.GetSysModule().SetVariable("argv", argv);
pythonEngine.SetSearchPaths(searchPaths);
ScriptScope scope = pythonEngine.CreateScope();
pythonScript.Execute(scope);
Implementation of pythonnet
The issue that I got here is on the line using Py.GIL(). I believe it is having trouble finding the python files, and also tried giving the python37.dll in the variable pathToPython.
I received the error that Python.Runtime, Version=2.5.2.0, Culture=neutral....missmatch"
string pathToPython = #"C:\Users\Dobromir\AppData\Local\Programs\Python\Python37";
string path = pathToPython + "; " + Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("PATH", path, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONHOME", pathToPython, EnvironmentVariableTarget.Process);
Console.WriteLine(path);
var lib = new[]
{
#"C:\\Users\\Dobromir\\PycharmProjects\\App37\\main.py",
Path.Combine(pathToPython, "Lib"),
Path.Combine(pathToPython, "DLLs")
};
string paths = string.Join("; ", lib);
Environment.SetEnvironmentVariable("PYTHONPATH", paths, EnvironmentVariableTarget.Process);
using (Py.GIL()) //Initialize the Python engine and acquire the interpreter lock
{
try
{
Console.WriteLine("I am working");
}
catch (PythonException error)
{
Console.WriteLine("Error occured: ", error.Message);
}
}
I Also tried creating a bash script to execute the python script and got the no module found error as well
I know that these are not the best implementations out there, but do the job.
My question is if someone has any idea on how to make this simple operation work I would be very grateful, thank you for your time and understanding
P.S - Apologies for the long post, wanted to write what I have tried before asking for help, but if someone is more interested I will provide additional information.
I did a project like this recently; a couple of things I would suggest to make it easy:
Confirm that the instance of python set in your env variables (WIN+R, sysdm.cpl, Advanced, env variables) is that of the instance of python you wish to use (do this for your python search path too!)
Remove any lines attempting to set these in code; and instead handle errors if they are not found
Then, when you call you script from within your program; it only needs to look like this:
var processStartInfo = new ProcessStartInfo
{
Arguments = "main.py",
FileName = "Python",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Process.Start(processStartInfo);
After some struggle, I found a solution to fit my needs.
Firstly, I completely removed python 2.7 and installed back 3.10.
I tried running the script file inside the shell command line and got the same error that the modules could not be found. What I did is try to import these modules and it gave an error, specifically for bs4 that I am using packages for python 2.x instead of 3.x packages.
After futher investigation I discovered that the packages that I have for my script are treated as "local" packages, meaning I installed them from the IDE (PyCharm) and they work for that project only I guess.
I also found that to "globally" access these packages I had to install them through the command line using the pip3 install <package_name>. After doing this the problem was gone and was left with running the script from the Windows Forms.
NOTE: I did not manage to start the script using python.exe, so I used bash script for the job.
Here is my code and I hope it helps someone down the line...
Code in C#
string myApp = string.Format("{0} {1}", #"C:\testing1.sh", "Hello");
var processStartInfo = new ProcessStartInfo
{
Arguments = myApp,
FileName = "C:\\Program Files\\Git\\git-bash.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = false
};
Process.Start(processStartInfo)
Code in Bash Script File
#!/bin/bash
arg1="$1"
python C:/Users/Dobromir/PycharmProjects/testing/main.py "$arg1"
Inside the Python file I am using sys.argv[] and accessing the arguments.
NOTE: Passing arguments from the bash script to the python script, in this case, you will receive 2 arguments - first one is the path to the python file and the second is the variable arg1.
Another important thing to mention is you need to have comas around the $1 - this is the property that is being send from the C# file, else it will show as empty.
Articles that were useful:
Installed BeautifulSoup but still get no module named bs4
Passing arguments to Python from Shell Script
https://unix.stackexchange.com/questions/31414/how-can-i-pass-a-command-line-argument-into-a-shell-script
https://gist.github.com/creativcoder/6df4d349447ff1416e68
Thank you to everyone who contributed and tried to help, I managed to learned new things with your suggestions!

Run java main class in command line/batch file/python script

I have a java class which is to be made into a tool so that I can give the input to the class as parameters and then produce the output in the command line, I have written the code in IntelliJ and want the code to be portable so that it can be used instantly by the click of a button. I have little experience in creating a bat file or python script.
package ase.ATool;
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class AnaliT {
public static void main(String[] args) {
File file = null;
File dir = new File(args[0]);
File[] directoryListing = dir.listFiles();
StringBuffer componentString = new StringBuffer(100);
if (directoryListing != null) {
for (File child : directoryListing) {
float complexity = 0, noOfMethods = 0;
System.out.println(child.getPath() + " ");
String currentLine;
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(child.getPath()));
System.out.println(bufferedReader);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
I want to convert the above code in to a tool so i can invoke this main function from the command line, batch file or python file.
You could compile and run the java file from python by checking out the subprocess module:
https://docs.python.org/3/library/subprocess.html
You can also run java code using the system module: running a java file in python using os.system()
If you want to invoke Java code from python, take a look here:
Calling Java from Python
Here it is recommended you use Py4J.
Here you can find an example of running a Java Class from a .bat file: How to run java application by .bat file
As mentioned in a comment you can also compile the file with java using java target.java and then run it with java target in the same directory
I hope by showing you some of these resources you can guide yourself towards more specific help.

spawn python ENOENT error with node child process

I am trying to call a python script as a child process within a node script. The output of the script is to be used within a callback. The code looks like this:
//myFunction.js
const myFunction = callback => {
let py = process.spawn('python', ['../folder/pyscript.py'], {
cwd: '../folder/'
});
let str = '';
py.stdout.on('data', data => {
str += data.toString();
}
py.stdout.on('end', () => {
callback(str);
}
}
exports.myFunction = myFunction;
This code works as expected when I directly run node myFunction.js (with an instance of myFunction within the script) and it works fine when I require the module in any other files within the same directory as myFunction.js.
But it fails with the following error when the module is required in a different higher level directory:
error: spawn python ENOENT
I'm guessing this has something to do with paths (value of cwd maybe?) but I can't seem to fix this. I've looked up similar questions but the answers aren't helping.
Any help will be appreciated. :)
Apparently, the issue is with the cwd. Everything in the script is relative to the path of the directory from where the script is invoked. So basically, running node myFunction.js from the project root directory (say ~/projects/myProject would set the cwd to ~/projects/myProject/../folder which would evaluate to ~/projects/folder. This is obviously incorrect, since in all probability, no directory named folder exists on the system, and thus this would lead to an ENOENT error.
The solution would be to construct the absolute path of your script in the code, perhaps by using the __dirname__ property in combination with the functionalities provided by the path module.
I struggled with this issue for days, before realizing that my script file was not getting picked up and spawned by nodeJS, because of some filepath issue.
Although I don't guarantee this will work for everyone, depending on their setup, this is what I did in my nodejs file:
let py = process.spawn('python', [__dirname + '../folder/pyscript.py']);
As you can see, I didn't have to use the {cwd: '../folder/'} option.
If your script is in the current directory as your javascript file, just do
let py = process.spawn('python', [__dirname + './pyscript.py']);
I should also point out that:
process.spawn('python', ['./pyscript.py']);
never worked for me and I spent days wondering why. Could find an answer until I tried this technique. Hope someone having this issue find this answer useful.
Using ${process.cwd()} worked for me... you can write it like this
let py = process.spawn('python', [`${process.cwd()}/folder/pyscript.py`]});

How to import Python functions to C in Xcode?

I'm trying to combine C and Python for research reasons but I'm getting problems in my code. I used the code from a tutorial but it seems that the code for getting the python file is not working. Here's the code:
helloClass.py
def sayHello(name):
return "Hello " + name + "!"
main.c
int main(int argc, const char * argv[]) {
char name[50];
printf("What's your name?\n");
fgets(name, 64, stdin);
char *answer = NULL;
PyObject *modname, *mod, *mdict, *func, *stringarg, *args, *rslt;
Py_Initialize();
PySys_SetPath(Py_GetPath());
modname = PyString_FromString("helloClass");
mod = PyImport_Import(modname);// Here mod = null because it can´t find it.
if (mod){
mdict = PyModule_GetDict(mod);
func = PyDict_GetItemString(mdict, "sayHello");
if (func){
if (PyCallable_Check(func)) {
stringarg = PyString_FromString(name);
args = PyTuple_New(1);
PyTuple_SetItem(args, 0, stringarg);
rslt = PyObject_CallObject(func, args);
if (rslt) {
answer = PyString_AsString(rslt);
Py_XDECREF(rslt);
}
Py_XDECREF(stringarg);
Py_XDECREF(args);
}
Py_XDECREF(mdict);
Py_XDECREF(func);
}
Py_XDECREF(modname);
Py_XDECREF(mod);
}
printf("%s", answer);
answer = NULL; }
There's not enough information here to give you the complete answer, but the basic problem is almost certainly that helloClass.py does not end up anywhere on the module search path, so PyImport_Import can't find it.
For example, maybe you're building your main executable into a location like ~/Library/Developer/Xcode/DerivedData/Hello/Build/Products/Release/hello, but not copying helloClass.py there. Or, maybe you are copying it there, but the equivalent of dirname $0 isn't on your module path. Or… well, there are a million possibilities, but the answer is the same in every case:
Figure out where the executable and the Python file are getting installed to.
Build an appropriate (probably relative) path out of that knowledge.
Make sure that path gets added to the module path.
The exact details on how Python searches for modules is different between different versions, but the basic idea is explained in The importstatement (or the 3.3 version, which defers to The import system), and often, it's just a matter of adding something to sys.path.
(Note that if you're building .app bundles instead of just Unix command-line executables, there is a standard answer to this—you stick the .py files in a standard place, use Cocoa or CoreFoundation APIs to get the path, and then use that. But that doesn't seem to be relevant here.)
Looking at the project you uploaded at https://github.com/Eduardof0nt/help.git:
You're not copying helloClass.py anywhere. You have to decide where you want to copy it, and that depends entirely on how you want to deploy things. But for debugging purposes, just to get something testable, you can just put it alongside the Hello Python! executable (in build/Debug or build/Release). To do that:
Select the project in the Project Navigator.
Select the target in the left sidebar of the project view that appears.
Select the Build Phases tab.
Click the Add Build Phase button.
Add a Copy Files phase.
Set its Destination to Products Directory.
Drag helloClass.py from the Project Navigator into the build phase.
Now, when you build, if you look at the output (turn on "All" and "All Messages"), you'll see "Copy build/Debug/helloClass.py" right after the "Link build/Debug/Hello Python!". And, if you look inside that directory, you'll see the two files side by side.
But that doesn't solve your whole problem. Because /wherever/you/started/build/Debug is not going to be on the sys.path that Python uses. The quickest way around this is to call Py_SetProgramName(argv[0]) right before the Py_Initialize(), and PySys_SetArgvEx(argc, argv, 1) right after. That may well not be the right thing to do for your use case, but you're going to have the read the documentation and understand what all of this does, because I can't possibly explain all the details of embedding Python in an SO answer.
While you're trying to learn this stuff, you probably want to do some debugging. For example, you can printf(path=%s\n", Py_GetPath()) to see what the sys.path equivalent is, and PyObject_Print(mod, stdout, 0) to see what's in mod, and so on. But really, you're going to want to use the Xcode debugger—add a breakpoint and try doing this stuff at runtime.
Much of this is explained in Extending and Emnbedding the Python Interpreter, and you really do need to read that; there's no substitute. (You don't need to read the whole Python/C API Reference Manual, but you will end up reading a lot of it, and getting good at searching it, before you're done.) However, because embedding Python is much less common than extending it, the docs really don't include everything you need, so… if whatever tutorial you're using doesn't cover things like PySys_* and Py_GetPath, you may need to find a better tutorial first.

Categories