I have some global variables in a Python script. Some functions in that script call into C - is it possible to set one of those variables while in C and if so, how?
I appreciate that this isn't a very nice design in the first place, but I need to make a small change to existing code, I don't want to embark on major refactoring of existing scripts.
I'm not a python guru, but I found this question interesting so I googled around. This was the first hit on "python embedding API" - does it help?
If the attributes belong to the global
scope of a module, then you can use
"PyImport_AddModule" to get a handle
to the module object. For example, if
you wanted to get the value of an
integer in the main module named
"foobar", you would do the following:
PyObject *m = PyImport_AddModule("__main__");
PyObject *v = PyObject_GetAttrString(m,"foobar");
int foobar = PyInt_AsLong(v);
Py_DECREF(v);
For anyone coming here from Google, here's the direct method:
PyObject* PyEval_GetGlobals()
https://docs.python.org/2/c-api/reflection.html
https://docs.python.org/3/c-api/reflection.html
The return value is accessed as a dictionary.
I recommend using pyrex to make an extension module you can store the values in in python, and cdef a bunch of functions which can be called from C to return the values there.
Otherwise, much depends on the type of values you're trying to transmit.
Are you willing to modify the API a little bit?
You can make the C function return the new value for the global, and then call it like this:
my_global = my_c_func(...)
If you're using Robin or the Python C API directly, you can pass the globals dictionary as an additional parameter and modify it
If your global is always in the same module, Sherm's solution looks great
Related
I've been learning about about C++ in college and one thing that interests me is the ability to create a shared header file so that all the cpp files can access the objects within. I was wondering if there is some way to do the same thing in python with variables and constants? I only know how to import and use the functions or classes in other py files.
First, if you've ever used sys.argv or os.sep, you've already used another module's variables and constants.
Because the way you share variables and constants is exactly the same way you share functions and classes.
In fact, functions, classes, variables, constants—they're all just module-global variables as far as Python is concerned. They may have values of different types, but they're the same kind of variable.
So, let's say you write this module:
# spam.py
cheese = ['Gouda', 'Edam']
def breakfast():
print(cheese[-1])
If you import spam, you can use cheese, exactly the same way you use eggs:
import spam
# call a function
spam.eggs()
# access a variable
print(spam.cheese)
# mutate a variable's value
spam.cheese.append('Leyden')
spam.eggs() # now it prints Leyden instead of Edam
# even rebind a variable
spam.cheese = (1, 2, 3, 4)
spam.eggs() # now it prints 4
# even rebind a function
spam.eggs = lambda: print('monkeypatched')
spam.eggs()
C++ header files are really just a poor man's modules. Not every language is as flexible as Python, but almost every language from Ruby to Rust has some kind of real module system; only C++ (and C) requires you to fake it by having code that gets included into a bunch of different files at compile time.
If you are just looking to make function definitions, then this post may answer your question:
Python: How to import other Python files
Then you can define a function as per here:
https://www.tutorialspoint.com/python/python_functions.htm
Or if you are looking to make a class:
https://docs.python.org/3/tutorial/classes.html
You can look at example 3.9.5 in the previous link in order to understand how to create a shared variable among different object instances.
I already use this function to change some string to class object.
But now I have defined a new module. How can I implement the same functionality?
def str2class(str):
return getattr(sys.modules[__name__], str)
I want to think some example, but it is hard to think. Anyway, the main problem is maybe the file path problem.
If you really need an example, the GitHub code is here.
The Chain.py file needs to perform an auto action mechanism. Now it fails.
New approach:
Now I put all files under one filefold, and it works, but if I use the modules concept, it fails. So if the problem is in a module file, how can I change the string object to relative class object?
Thanks for your help.
You can do this by accessing the namespace of the module directly:
import module
f = module.__dict__["func_name"]
# f is now a function and can be called:
f()
One of the greatest things about Python is that the internals are accessible to you, and that they fit the language paradigm. A name (of a variable, class, function, whatever) in a namespace is actually just a key in a dictionary that maps to that name's value.
If you're interested in what other language internals you can play with, try running dir() on things. You'd be surprised by the number of hidden methods available on most of the objects.
You probably should write this function like this:
def str2class(s):
return globals()[s]
It's really clearer and works even if __name__ is set to __main__.
I have a small python module I have created with the Python C API which I call mycore.
I have also created some utility scripts in Python which are related.
How can I put both in the same module namespace? I know I can call Python code from my C code but surely there is an easier way to do that.
Thanks
The obvious way is by making mycore a package. Create a mycore/__init__.py that imports both the C part, typically named something like _mycore, and the Python part:
from _mycore import *
from _mycorepy import *
In the same directory you'd have a _mycore.so and _mycorepy.py.
Another way to mix Python and C code is by invoking PyRun_String on the embedded Python. This might be what you mean by I know I can call Python code from my C code..., but just in case, here is a simple example with the potentially tricky refcounting details:
PyObject *get_factory()
{
PyObject *g, *runret, *factory;
// prepare a dictionary for the module to run in
g = Py_BuildValue("{s:O}", "__builtins__", PyEval_GetBuiltins());
if (!g)
return NULL;
// run Python code in the dictionary -- the code may import modules, etc.
runret = PyRun_String("\
def factory():\n\
return 42\n", Py_file_input, g, NULL);
Py_XDECREF(runret);
if (!runret) {
Py_DECREF(g);
return NULL;
}
Py_DECREF(runret);
// pick the stuff you care about from the dictionary and return it
factory = PyDict_GetItemString(g, "factory");
Py_INCREF(factory);
Py_DECREF(g);
return factory;
}
OK, this worked for me. My circumstances may be different. My C module was built internally and provided to my embedded Python with a call to PyImport_AppendInittab().
But I still had some accompanying Python code that I wanted associated with the same namespace.
So I got it working by naming my C code _mygadget and the Python file mygadget.py. For the first line of mygadget.py I wrote:
from _mygadget import *
This seems to work. I don't know what would happen if I had a Python method and C call with the same name. I am avoiding that at present.
I've initialized the Python environment by
Py_Initialize();
I have no external Python module imported into the environment and everything works well. But when I need to pass a C string into this environment, I am lost...
I was thinking to add a function in the environment to assign the variable like the following code do.
char *str;
str="\
def assign_var(str):\
global string\
string = str";
PyRun_SimpleString(str);
And then call this function in C and pass the converted C string as the arguments.
I don't think all I mentioned above is a good way solve the problem...
How can I make this work?
Solution:
Finally, here's the solution with Peter Mortensen's help. (Thanks Peter Mortensen!)
As the python environment I've initialized is a pure empty environment(without any imported modules). I use
py_main = PyImport_AddModule("__main__");
to get a hook to the main environment. and then call
PyModule_AddStringConstant(py_main, "string_name", str);
to bring the C string into the python environment.
To verify everything is done, just try:
PyRun_SimpleString("print dir()");
PyRun_SimpleString("print string_name");
and you'll see you "string_name" string appears in the dir() list and make it print by python!
This should do what you want:
char *cStr = "Some text here.";
PyObject *pyStr = Py_BuildValue("s", cStr);
http://docs.python.org/c-api/arg.html#Py_BuildValue
Of course if you're using Python 3 (or use it in the future), there may be situations where you'd want to use "y" instead of "s" and get a bytes object rather than a str.
UPDATE: Woops, I forgot the even easier way of doing it.
PyObject *pyStr = PyString_FromString(cStr);
http://docs.python.org/c-api/string.html#PyString_FromString
(It'd be PyBytes_FromString() in Python 3.)
You might want to take a look at http://docs.python.org/extending/embedding.html for some more information.
Here's something else you might want to try. See
http://docs.python.org/c-api/module.html#PyModule_AddObject
Or possibly
http://docs.python.org/c-api/module.html#PyModule_AddStringConstant
With the former it'd be something like
errorcheck = PyModule_AddObject(embmodule, "str", pyStr);
And with the latter, something like
errorcheck = PyModule_AddStringConstant(embmodule, "str", cStr);
I have an embedded python interpreter in my program. I'd like to export a module with values defined in my program and be able to change them from a python script. e.g.
in c:
int x = 1;
in python:
import embedded
embedded.x = 2
in c:
printf("%d",x);
output:
2
Is this possible or do I have to export functions to change anything in c?
There's no need to export functions, but the easiest way to do this would be to use PyModule_GetDict() with PyDict_GetItemString() to get the value assigned to the x attribute.
If you don't want to actively check the value of a PyObject in your C code, I think you need to export functions to modify the representation in C. I'm no expert, but I don't think there's an automatic mapping.