Get enum definition from shared library - python

I am using ctypes to access a shared library written in C. The C source of the shared library contains an enum like
enum {
invalid = 0,
type1 = 1,
type2 = 2
} type_enum;
On the Python side I was intending to just define integer constants for the various enum values, like:
INVALID = 0
TYPE1 = 1
TYPE2 = 2
And then use these numerical "constants" in the Python code calling the C functions. This seems to work OK, however I would strongly prefer to get the numerical values for the enums directly from the shared library (introspection?); however using e.g. nm on the shared library it does not seem to contain any of the symbols 'invalid', 'type1' or 'type2'. So my question is:
Is it possible to extract the numerical values from enum definitions from a shared library - or is the whole enum concept 'dropped on the floor' when the compiler is done?
If the enum values exist in the shared library - how can I access them from Python/ctypes?

Enum definitions are not exported so your current solution is the only one available.
In any case, C enum values are nothing much more than integer constants. There's no type safety on the C side, you can pass any integer values to an enum parameter. So it's not like the C compiler is doing much anyway.

See MSDN on the benefits of enums: "alternative to the #define preprocessor directive with the advantages that the values can be generated for you and obey normal scoping rules" - notably missing is type safety. This strongly suggests that, as you suggest, enums are dropped on the floor once compiled.

Related

Accessing Fortran module PARAMETER from python using ctypes [duplicate]

I have a Fortran module that contains some variables that have the attribute parameter and some have the attribute save. The parameter ones are not included in the compiled object, which becomes a problem when trying to assemble a library. For example, consider a file testModule.f90:
module testMOD
integer, save :: thisIsSaved = 1
integer, parameter :: thisIsParametered = 2
end module testMOD
I compile this with: ifort -c testModule.f90. When I check what's inside it:
>$ nm testModule.o
0000000000000000 T testmod._
0000000000000000 D testmod_mp_thisissaved_
only the thisIsSaved variable is there. I know that I can just change thisIsParametered to save rather than parameter but, ideally, I'd like to prevent a linking user from changing this value. Is there a way to do this?
Edit: I'd like this library to be accessible to C codes, as well, not just Fortran.
That should actually be stored in the .mod file. All of the data types and function prototypes are stored there which is why you need to include it when you send someone a .lib file. Try linking in the module after using it in something else and it should work just fine.
Essentially the .mod file serves the same purpose as the .h file in c, so of course you are going to have to include it with your library.
[update:]
If you are attempting to use this in C, then as you said, there is no means for you to easily maintain the named constant. As an alternative, you can use the protected attribute on the entity. At least with Fortran, anything outside of the module is restricted from writing to the variable. I don't know if the C compiler and the linker will respect this behavior, but I think this is probably your best shot.
module testMOD
INTEGER, PROTECTED, BIND(C) :: globalvar = 1
end module testMOD
Unfortunately I don't really do much with interoperability with C, so I can't really guarantee that C will respect the protected attribute and not allow the variable to be changed.
As others have noted, a parameter is a named constant and implementations may not set aside storage for that constant in the object code (particularly for scalars).
Your library should provide a header file for your C clients. You can define the value of the Fortran parameter in that header file, either by #define or const.
This requires maintenance of the value of the parameter in two places, but you already have that maintenance burden with other aspects of the library's interface.

Constant integer Attributes in Python Module written in C

I implemented a python extension module in C according to https://docs.python.org/3.3/extending/extending.html
Now I want to have integer constants in that module, so I did:
module= PyModule_Create(&myModuleDef);
...
PyModule_AddIntConstant(module, "VAR1",1);
PyModule_AddIntConstant(module, "VAR2",2);
...
return module;
This works. But I can modify the "constants" from python, like
import myModule
myModule.VAR1 = 10
I tried to overload __setattr__, but this function is not called upon assignment.
Is there a solution?
You can't define module level "constants" in Python as you would in C(++). The Python way is to expect everyone to behave like responsible adults. If something is in all caps with underscores (like PEP 8 dictates), you shouldn't change it.

Using a Python 2.7 enum from C

I have an enum in Python (backported enum package to 2.7) that is meant to be of only integers:
import enum
class MyEnum(enum.Enum):
val = 0
Let's say I receive a PyObject * in a C extension pointing to MyEnum.val. I want the integer value associated with the PyObject *. How do I get it most succinctly?
Looking at the source to the enum34 backport, just like the enum module in 3.4+, it's pure Python, and does nothing to expose a custom C API.
So, you just use PyObject_GetAttr and friends to access class attributes. In particular, if you have a MyEnum.val, you need to get its value attribute, which will be an int, which you can then PyInt_AsLong.
This is the same way things work in Python. If you try to use MyEnum.val where an int is expected, you should get a TypeError; if you try to explicitly call int(MyEnum.val), you will definitely get a TypeError. So, although I haven't tested it, PyInt_AsLong directly on the constant instead of its value should raise a TypeError and return -1.
If you want enumeration constants that act like subtypes of int, then, as the enum docs explain, you want IntEnum. Usually, that isn't really what you want (as the docs explain), but if it is, of course it works. And you should be able to PyInt_Check and PyInt_AsLong an IntEnum value (although, again, I haven't tested).

What is GetSetDescriptorType in Python?

I was looking at types.py to understand the built-in types and I came across this GetSetDescriptorType. From the Python documentation:
types.GetSetDescriptorType
The type of objects defined in extension modules with PyGetSetDef,
such as FrameType.f_locals or array.array.typecode. This type is used
as descriptor for object attributes; it has the same purpose as the
property type, but for classes defined in extension modules
I do understand the property type, but could not wrap my mind around this. Can some one who understands this throw some light ?
When you write a Python module using C, you define new types using a C API. This API has a lot of functions and structs to specify all the behavior of the new type.
One way to specify properties of a type using the C API is to define an array of PyGetSetDef structs:
static PyGetSetDef my_props[] = { /*... */ }
And then use the array in the initialization of the type (see this example for details).
Then, in Python, when you use MyType.my_property you have a value of types.GetSetDescriptorType, that is used to resolve the actual value of the property when you write my_obj.my_property.
As such, this type is an implementation detail, unlikely to be very useful.

Python and C coupling

I tried loading C shared library .so in Python using ctypes.CDLL class (Linux). Here is the link to which tells what I did. As I see the documentation it says CDLL class assumes that function returns int types. I've a doubt here what if I need to return variable of type other than the int type from a function in C?.
And to what extent we can use C functions in Python i mean what are the limits/restrictions on using C shared libraries and functions
Thanks in Advance
By default, it assumes int, but you can set restype to any of the supported types to override that. E.g., from the docs:
strchr.restype = c_char_p
This means that strchr returns a pointer to a char, which corresponds to a Python string (or None, for a NULL pointer).

Categories

Resources