I have a C# piece that generates a DLL which I'm trying to invoke from Python. The C# prototype is as follow:
public Main(NationalInstruments.TestStand.Interop.API.SequenceContext sequenceContext){
public void ShowFullOptions(out bool errorOccurred, out int errorCode, out String errorMsg) {
[...]
}
}
I followed this post which explains how to achieve to pass "out" parameters from python, without success since clr.Reference does not exist and throw me the error:
AttributeError: module 'clr' has no attribute 'Reference'
I have pythonnet installed, version 2.3.
My python code:
import clr
dll_path = R"thatDllPath.dll"
clr.AddReference(dll_path)
from Flamenco_Calibration_Interface import Main
import System
my_instance = Main(None)
v1 = clr.Reference[System.Boolean](False)
v2 = clr.Reference[System.Int64](1)
v3 = clr.Reference[System.String]("")
a = my_instance.ShowFullOptions(v1,v2,v3)
I have checked that the DLL path is good and I am able to successfully call the C# method if I overload "ShowFullOptions" with a version without any arguments.
The main problem resides in that I have the "out" keyword in the C# method prototype.
How do you properly generate argument in python so they are accepted as "out" within C# ?
Any help much appreciated.
Related
I have this part of a C# DLL from a larger project. It has no dependencies, runs in either .NET 5.0 or .NET 6.0 C# without anything else in it. It looks something like this:
namespace Some.Namespace {
public static class Rename{
public static string RenameString(string stringToRename){
//Do some stuff to it
return stringToRename;
}
}
}
However, no matter what way we attempt to add the reference to Python 3.6 (ArcGIS Pro Distribution) using Python.NET...
sys.path.append(os.path.join(os.getcwd(),"Project_Folder","bin"))
clr.AddReference("SomeDLL")
from Some.Namespace import Rename
It throws this error on the 3rd line.
Exception has occurred: ModuleNotFoundError No module named Some
We've tried just about every possible means to load the DLL at this point and none of them have worked (Yes I know that 2.5.x Python.NET doesn't support .NET 6.0 - we switched to 5.0 and it didn't work either) Exposing the function through Robert's DllExport using ctypes throws a CLR Exception that we can't debug because it's cross-environment whenever the function is run.
Here's that attempt.
//The DLL With Robert's DLLExport
namespace Some.Namespace {
public static class Rename{
//We've also tried CallingConvention.Cdecl
[DllExport("Rename", System.Runtime.InteropServices.CallingConvention.StdCall)]
public static string RenameString(string stringToRename){
//Do some stuff to it
return stringToRename;
}
}
}
#In Python
#We've tried WINDLL
dll_utils = CDLL(os.path.join(os.getcwd(),"project_folder","bin","SomeDLL.dll"))
#We've tried using CFUNCTTYPE here too
encrypt_proto = ctypes.WINFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
encrypt_args = (1, "p1", 0),(1, "p2", 0),
#It fails here when we add the encrypt_args flags to it, saying they don't match up,
#but there's no way to figure out how many it wants, we've tried many variations of
#the above
encrypt_call = encrypt_proto(("Rename",dll_utils),encrypt_args)'
#So because of that on one attempt we removed these two lines
p2 = ctypes.c_char_p(text_to_obfuscate)
p1 = ctypes.c_char_p(text_to_obfuscate)
#And the second argument here
return_value = encrypt_call(p1,p2)
#And...
OSError: [WinError -532462766] Windows Error 0xe0434352
Attempting to write a second function in the DLL to convert the C# string to a byte array and back and using a ctypes pointer doesn't work - it throws the same error just posted when it's called. Someone suggested in another question here IronPython next - which we want to avoid, or to try to use comtypes which I've never used before. At one point we even tried to do a COM decoration and it claimed something I've seen on an answer here is obsolete. I've researched this for 2-3 days and haven't been able to solve this.
At this point I'm at a loss. Is there any effective, simple way, to get an external .NET C# function in a DLL to call a function and pass a string in Python without installing any major dependencies? Just out of the box? There's gotta be something simple I'm missing.
I am trying to use a .NET Core library inside a Jupyter Notebook python script by using PythonNet. Support for .NET Core was added recently (see https://github.com/pythonnet/pythonnet/issues/984#issuecomment-778786164) but I am still getting a No module named 'TestAppCore' error.
I don't have an issue using a .NET Framework library with PythonNet, only .NET Core. Any help with diagnosing and fixing the issue would be greatly appreciated.
The C# library I'm trying to get working is a simple class library project with no dependencies at all. Below is the entirety of the code:
namespace TestAppCore
{
public class Foo
{
public int ID { get; set; }
public Foo(int id)
{
ID = id;
}
public int Add(int a, int b)
{
return a + b;
}
}
}
Here is the python script:
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("D:\src\Test.runtimeconfig.json")
set_runtime(rt)
import clr
import sys
sys.path.append(r"D:\src\TestAppCore")
clr.AddReference(r"TestAppCore")
from TestAppCore import Foo
foo = Foo(5)
print(foo.ID)
res = foo.Add(1, 2)
print(res)
Here is the output:
Finally, here is the runtime config I am using:
{
"runtimeOptions": {
"tfm": "netcoreapp3.1",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "3.1.0"
}
}
}
.NET Core: 3.1
python version: 3.7
pythonnet: 3.0.0.dev1
clr-loader: 0.1.6
I suspect that you are getting the DLL path wrong.
This worked for me:
from clr_loader import get_coreclr
from pythonnet import set_runtime
set_runtime(get_coreclr("pythonnetconfig.json"))
import clr
clr.AddReference("C:/Path/To/Interface.dll")
from Interface import Foo
foo = Foo()
Using
Python 3.8.10
pythonnet 3.0.0a1
clr-loader 0.1.7
C# DLL (Class Library) targeting .NET Core 3.1
pythonnetconfig.json exactly as you posted.
I never got it to work with .NET Core 3.1. For me it worked with .NET Framework 4.8 and pythonnet 2.5.2. See my other answer for more details.
I have a similar problem and found out that if the DLL and the namespace are the same, then it fails. In my case, following pythonnet tutorial:
Calculate.sln contains Calc.cs, with
namespace Calculate; // recommended by VS
class Calc { ... }
Then in Python
clr.AddReference("Calculate") # Assuming sys.path correct setting
from Calculate import Calc
Then:
ImportError: cannot import name 'Calc' from 'Calculate' (unknown location)
But with:
namespace CalculateNS; // different name than Calculate.dll
class Calc { ... }
Then in Python
clr.AddReference("Calculate") # Assuming sys.path correct setting
from CalculateNS import Calc
it works... side effects, pylance known Calculate module but not CalculateNS :-(
Anyone experimented this? I saw lot of answers that have never been tested I guess, only thoughts
Using .NET 6.0 Framework.
I'm trying to use some Python Code in my Swift project with PythonKit. In order to do that, I've downloaded the new Xcode 11 to add PythonKit as Swift Package.
After adding PythonKit with Swift Package, I have these project dependencies right here.
I want to use my Python code at the start of my app, so I put the call for Python code in my App Delegate, in the application function.
import UIKit
import PythonKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let sys = Python.import("sys")
print("Python Version: \(sys.version)")
sys.path.append("/Users/me/Documents/MyProject/MyPackage/my_python_main_file")
let pythonAPI = Python.import("my_python_main_file")
pythonAPI.main_function.call()
return true
}
After running my project, I get the following error :
Fatal error: Python library not found. Set the PYTHON_LIBRARY environment variable with the path to a Python library.
I tried to follow the different steps with Breakpoints to know where the code actually crashes. So here is the different stages the PythonKit goes through :
It goes inside PythonLibrary init() and produces the error :
guard let pythonLibraryHandle = PythonLibrary.loadPythonLibrary() else {
fatalError("""
Python library not found. Set the \(Environment.library.key) \
environment variable with the path to a Python library.
""")
}
After investigation, it is because of the call of dlopen inside of loadPythonLibrary function
static func loadPythonLibrary(at path: String) -> UnsafeMutableRawPointer? {
log("Trying to load library at '\(path)'...")
#if canImport(Darwin) || canImport(Glibc)
// Must be RTLD_GLOBAL because subsequent .so files from the imported python
// modules may depend on this .so file.
let pythonLibraryHandle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL)
#elseif os(Windows)
let pythonLibraryHandle = UnsafeMutableRawPointer(LoadLibraryA(path))
#endif
if pythonLibraryHandle != nil {
log("Library at '\(path)' was sucessfully loaded.")
}
return pythonLibraryHandle
}
This functions returns nil, which is due to dlopen function returning nil.
However, I checked the path given as parameter to loadPythonLibrary(at : path) and it appeared to be correct (in Terminal I tried to do cd to the following path and it worked) :
/usr/local/Frameworks/Python.framework/Versions/2.7/Python
I am using Python 2.7 in my code.
Do you know why dlopen would return nil in my case ?
You can do it now using my fork of PythonKit at https://github.com/kewlbear/PythonKit. This package depends on my other package https://github.com/kewlbear/Python-iOS. Ultimately Python will be embedded in the app. Currently Python 3.8 is used.
We are building a project using active RFID, and this RFID needs to be coded on Python to use it on Raspberry PI3
SAAT RFID already has DLL file, RFIDAPI.lib and RFIDAPIEXPORT.h and various API calling function
For instance and the basic code I need to execute
bool SAAT_TCPInit (void** pHandle,char *pHostName,int nsocketPort)
HANDLE hp; if(!SAAT_TCPInit(&hp,”192.168.0.238”,7086) )
{
printf("reader initialization failed!\n"); return false;
}
How can I convert this code into Python to get into RFID?
Untested, but illustrates what you need to do. I assumed Python 3, but Python 2 is similar.
#!python3
import ctypes
# import the DLL
dll = ctypes.CDLL('RFIDAPI')
# declare the argument and return value types
dll.SAAT_TCPInit.argtypes = ctypes.POINTER(ctypes.c_void_p),ctypes.c_char_p,ctypes.c_int)
dll.SAAT_TCPInit.restype = ctypes.c_bool
# For the output parameter, create an instance to be passed by reference.
hp = ctypes.c_void_p()
if not dll.SAAT_TCPInit(ctypes.byref(hp),b'192.168.0.238',7086):
print('reader initialization failed!')
Note the byte string for the IP. In Python 3 byte strings are the correct input for c_char_p.
I'm looking to try and run Jasper reports (that have been written in iReports and exported to xml) from within a python application, without having to communicate with a JasperServer instance. Is this possible?
I've done some googling and only come across a 2 year old SO question (where the suggested answer actually requires JasperServer):
Run jasper report (created with iReport) from within python without jasperserver?
And something that looks kind of promising, except for the "It is obsolete" in the title:
http://code.activestate.com/recipes/576969-python-jasperreport-integration-it-is-obsolete/
I'm hoping it's obsolete because this is now an officially supported thing (dream on, Dave), but I can't find anything about it, if it is.
Actually Jasper Reports are not implemented in Python, so the only way to have it serving to your Python code is to have Jasper Server running and awaiting Python requests over REST or other remote way of communication.
Simply - no way to have Jasper without Jasper (server) in Python
I used py4j. I had to write a small program in java.
Using this as an example, it was simple.
It was more difficult to configure the build environment and put all the dependencies for printing qr-codes.
Python example:
from py4j.java_gateway import JavaGateway
gateway = JavaGateway()
gateway.entry_point.pdf_from_json('e:/test.jasper', 'e:/test.json', 'e:/test.pdf')
Java example:
package jasper4py;
import py4j.GatewayServer;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JsonDataSource;
import net.sf.jasperreports.engine.util.JRLoader;
public class JasperEntryPoint {
public static void main(String[] args) {
GatewayServer gatewayServer = new GatewayServer(new JasperEntryPoint());
gatewayServer.start();
System.out.println("Gateway Server Started");
}
public void pdf_from_json(String report, String data, String result) throws JRException, IOException {
Map<String, Object> parameters = new HashMap<String, Object>();
JsonDataSource dataSource = new JsonDataSource(JRLoader.getLocationInputStream(data));
JasperPrint jasperPrint = JasperFillManager.fillReport(report, parameters, dataSource);
JasperExportManager.exportReportToPdfFile(jasperPrint, result);
}
}